LLVM 24.0.0git
InstrumentorUtils.cpp
Go to the documentation of this file.
1//===-- InstrumentorUtils.cpp - Highly configurable instrumentation pass --===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//===----------------------------------------------------------------------===//
10
13
14#include "llvm/ADT/DenseMap.h"
16
17using namespace llvm;
18using namespace llvm::instrumentor;
19
20namespace {
21enum PropertyType { INT, STRING, POINTER, UNKNOWN };
22
23/// Simple filter expression evaluator for instrumentation opportunities.
24/// Supports integer comparisons (==, !=, <, >, <=, >=), string comparisons
25/// (==, !=), pointer comparisons (==, !=) against null, string prefix checks
26/// (startswith), and logical operators (&&, ||).
27class FilterEvaluator {
28 StringRef Expr;
29 DenseMap<StringRef, int64_t> &IntPropertyValues;
30 DenseMap<StringRef, StringRef> &StringPropertyValues;
31 DenseMap<StringRef, Value *> &PointerPropertyValues;
32 DenseMap<StringRef, PropertyType> &DynamicProperties;
33 StringMap<int32_t> &FlagNameVals;
34 size_t Pos = 0;
35
36public:
37 FilterEvaluator(StringRef Expr,
38 DenseMap<StringRef, int64_t> &IntPropertyValues,
39 DenseMap<StringRef, StringRef> &StringPropertyValues,
40 DenseMap<StringRef, Value *> &PointerPropertyValues,
41 DenseMap<StringRef, PropertyType> &DynamicProperties,
42 StringMap<int32_t> &FlagNameVals)
43 : Expr(Expr), IntPropertyValues(IntPropertyValues),
44 StringPropertyValues(StringPropertyValues),
45 PointerPropertyValues(PointerPropertyValues),
46 DynamicProperties(DynamicProperties), FlagNameVals(FlagNameVals) {}
47
48 Expected<bool> evaluate() {
49 if (Expr.empty())
50 return true;
51
52 Expected<bool> Result = parseOrExpr();
53
54 // Check if we consumed the entire expression.
56 if (Pos < Expr.size() && Result)
57 return createStringError(
58 "unexpected characters at position " + std::to_string(Pos) + ": '" +
59 Expr.substr(Pos, std::min<size_t>(10, Expr.size() - Pos)) + "'");
60
61 return Result;
62 }
63
64private:
65 void skipWhitespace() {
66 while (Pos < Expr.size() && std::isspace(Expr[Pos]))
67 ++Pos;
68 }
69
70 Expected<bool> parseOrExpr() {
71 Expected<bool> Result = parseAndExpr();
72 while (Result) {
74 if (Pos + 1 < Expr.size() && Expr[Pos] == '|' && Expr[Pos + 1] == '|') {
75 Pos += 2;
76 Expected<bool> NextResult = parseAndExpr();
77 if (!NextResult)
78 return NextResult;
79 *Result |= *NextResult;
80 } else {
81 break;
82 }
83 }
84 return Result;
85 }
86
87 Expected<bool> parseAndExpr() {
88 Expected<bool> Result = parsePrimary();
89 while (Result) {
91 if (Pos + 1 < Expr.size() && Expr[Pos] == '&' && Expr[Pos + 1] == '&') {
92 Pos += 2;
93 Expected<bool> NextResult = parsePrimary();
94 if (!NextResult)
95 return NextResult;
96 *Result &= *NextResult;
97 } else {
98 break;
99 }
100 }
101 return Result;
102 }
103
104 Expected<bool> parsePrimary() {
106
107 // Check for opening parenthesis.
108 if (Pos < Expr.size() && Expr[Pos] == '(') {
109 ++Pos; // Skip '('
110 Expected<bool> Result = parseOrExpr();
111
113 if (Result && (Pos >= Expr.size() || Expr[Pos] != ')'))
114 return createStringError("expected ')' at position " +
115 std::to_string(Pos));
116
117 // Skip ')'.
118 ++Pos;
119 return Result;
120 }
121
122 // Otherwise parse a comparison.
123 return parseComparison();
124 }
125
126 // Parse a quoted string literal.
127 Expected<StringRef> parseStringLiteral() {
129 if (Pos >= Expr.size() || Expr[Pos] != '"')
130 return createStringError("expected string literal at position " +
131 std::to_string(Pos));
132
133 // Skip opening quote.
134 ++Pos;
135 size_t Start = Pos;
136 while (Pos < Expr.size() && Expr[Pos] != '"')
137 ++Pos;
138
139 if (Pos >= Expr.size())
140 return createStringError("unclosed string literal starting at position " +
141 std::to_string(Start - 1));
142
143 StringRef Result = Expr.slice(Start, Pos);
144 // Skip closing quote.
145 ++Pos;
146 return Result;
147 }
148
149 Expected<bool> parseComparison() {
151
152 // Check for logical not operator.
153 bool LogicalNot = false;
154 if (Pos < Expr.size() && Expr[Pos] == '!') {
155 LogicalNot = true;
156 ++Pos;
157 }
158
159 // Parse left-hand side (property name).
160 size_t Start = Pos;
161 while (Pos < Expr.size() && (std::isalnum(Expr[Pos]) || Expr[Pos] == '_'))
162 ++Pos;
163
164 StringRef PropName = Expr.slice(Start, Pos);
165 if (PropName.empty())
166 return createStringError("expected property name at position " +
167 std::to_string(Pos));
168
170
171 // Parse property fields and methods.
172 if (Pos < Expr.size() && Expr[Pos] == '.') {
173 ++Pos;
175
176 // Parse field name.
177 Start = Pos;
178 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
179 ++Pos;
180
181 StringRef FieldName = Expr.slice(Start, Pos);
183
184 // Handle flag values.
185 if (PropName == "flags") {
186 auto FlagValIt = IntPropertyValues.find("flags");
187 if (FlagValIt != IntPropertyValues.end()) {
188 auto FlagNameIt = FlagNameVals.find(FieldName);
189 if (FlagNameIt == FlagNameVals.end())
190 return createStringError("Invalid flag '" + FieldName + "'");
191 return ((static_cast<int32_t>(FlagValIt->second) &
192 FlagNameIt->second) == FlagNameIt->second) ^
194 }
195 }
196
197 // Check for .startswith() method call.
198 if (FieldName == "startswith") {
199 // Parse (.
200 if (Pos >= Expr.size() || Expr[Pos] != '(')
201 return createStringError(
202 "expected '(' after 'startswith' at position " +
203 std::to_string(Pos));
204
205 ++Pos;
206
207 // Parse string argument.
208 auto Prefix = parseStringLiteral();
209 if (!Prefix)
210 return Prefix.takeError();
211
213
214 // Parse )
215 if (Pos >= Expr.size() || Expr[Pos] != ')')
216 return createStringError(
217 "expected ')' to close 'startswith' call at position " +
218 std::to_string(Pos));
219
220 ++Pos;
221
222 // Evaluate startswith.
223 auto StrIt = StringPropertyValues.find(PropName);
224 if (StrIt != StringPropertyValues.end())
225 return StrIt->second.starts_with(*Prefix) ^ LogicalNot;
226
227 // If this is a dynamic string property, assume the filter passes.
228 if (DynamicProperties.lookup_or(PropName, UNKNOWN) == STRING)
229 return true;
230
231 return createStringError(
232 "startswith is only valid on string properties not '" + PropName +
233 "'");
234 }
235
236 return createStringError("unknown method '" + FieldName +
237 "' on property '" + PropName + "'");
238 } else if (LogicalNot) {
239 return createStringError("expected boolean value at position " +
240 std::to_string(Start));
241 }
242
243 // Check if this is an integer property.
244 auto IntIt = IntPropertyValues.find(PropName);
245 if (IntIt != IntPropertyValues.end()) {
246 int64_t LHS = IntIt->second;
247
248 // Parse operator.
249 enum OpKind { EQ, NE, LT, GT, LE, GE } Op;
250 if (Pos < Expr.size()) {
251 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
252 Op = EQ;
253 Pos += 2;
254 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
255 Expr[Pos + 1] == '=') {
256 Op = NE;
257 Pos += 2;
258 } else if (Expr[Pos] == '<' && Pos + 1 < Expr.size() &&
259 Expr[Pos + 1] == '=') {
260 Op = LE;
261 Pos += 2;
262 } else if (Expr[Pos] == '>' && Pos + 1 < Expr.size() &&
263 Expr[Pos + 1] == '=') {
264 Op = GE;
265 Pos += 2;
266 } else if (Expr[Pos] == '<') {
267 Op = LT;
268 Pos += 1;
269 } else if (Expr[Pos] == '>') {
270 Op = GT;
271 Pos += 1;
272 } else {
273 return createStringError("expected comparison operator (==, !=, <, "
274 ">, <=, >=) at position " +
275 std::to_string(Pos));
276 }
277 } else {
278 return createStringError(
279 "expected comparison operator after property '" + PropName + "'");
280 }
281
283
284 // Parse right-hand side (constant value).
285 Start = Pos;
286 bool Negative = false;
287 if (Pos < Expr.size() && Expr[Pos] == '-') {
288 Negative = true;
289 ++Pos;
290 }
291
292 size_t DigitStart = Pos;
293
294 // Parse binary literals.
295 if (Pos + 1 < Expr.size() && Expr[Pos] == '0' && Expr[Pos + 1] == 'b') {
296 Pos += 2;
297 while (Pos < Expr.size() && (Expr[Pos] == '0' || Expr[Pos] == '1'))
298 ++Pos;
299 } else {
300 // Parse decimal literals.
301 while (Pos < Expr.size() && std::isdigit(Expr[Pos]))
302 ++Pos;
303 }
304
305 if (Pos == DigitStart)
306 return createStringError("expected integer value at position " +
307 std::to_string(Pos));
308
309 StringRef ValueStr = Expr.slice(Start, Pos);
310 int64_t RHS = 0;
311 if (ValueStr.getAsInteger(0, RHS))
312 return createStringError("invalid integer value '" + ValueStr + "'");
313
314 if (Negative)
315 RHS = -RHS;
316
317 // Evaluate comparison.
318 switch (Op) {
319 case EQ:
320 return LHS == RHS;
321 case NE:
322 return LHS != RHS;
323 case LT:
324 return LHS < RHS;
325 case GT:
326 return LHS > RHS;
327 case LE:
328 return LHS <= RHS;
329 case GE:
330 return LHS >= RHS;
331 }
332 return true;
333 }
334
335 // Check if this is a string property.
336 auto StrIt = StringPropertyValues.find(PropName);
337 if (StrIt != StringPropertyValues.end()) {
338 StringRef LHS = StrIt->second;
339
340 // Parse operator (only == and != for strings).
341 enum OpKind { EQ, NE } Op;
342 if (Pos < Expr.size()) {
343 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
344 Op = EQ;
345 Pos += 2;
346 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
347 Expr[Pos + 1] == '=') {
348 Op = NE;
349 Pos += 2;
350 } else {
351 return createStringError("string property '" + PropName +
352 "' only supports == and != operators");
353 }
354 } else {
355 return createStringError(
356 "expected comparison operator after string property '" + PropName +
357 "'");
358 }
359
361
362 // Parse right-hand side (string literal).
363 auto RHS = parseStringLiteral();
364 if (!RHS)
365 return RHS.takeError();
366
367 // Evaluate comparison.
368 switch (Op) {
369 case EQ:
370 return LHS == *RHS;
371 case NE:
372 return LHS != *RHS;
373 }
374 return true;
375 }
376
377 // Check if this is a pointer property.
378 auto PtrIt = PointerPropertyValues.find(PropName);
379 if (PtrIt != PointerPropertyValues.end()) {
380 Value *LHS = PtrIt->second;
381
382 // Parse operator (only == and != for pointers).
383 enum OpKind { EQ, NE } Op;
384 if (Pos < Expr.size()) {
385 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
386 Op = EQ;
387 Pos += 2;
388 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
389 Expr[Pos + 1] == '=') {
390 Op = NE;
391 Pos += 2;
392 } else {
393 return createStringError("pointer property '" + PropName +
394 "' only supports == and != operators");
395 }
396 } else {
397 return createStringError(
398 "expected comparison operator after pointer property '" + PropName +
399 "'");
400 }
401
403
404 // Parse right-hand side (must be "null").
405 Start = Pos;
406 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
407 ++Pos;
408
409 StringRef RHS = Expr.slice(Start, Pos);
410 if (RHS != "null")
411 return createStringError("pointer comparisons only support 'null' as "
412 "right-hand side, got '" +
413 RHS + "'");
414
415 // Check if the pointer is a constant null.
416 bool IsNull = false;
417 if (auto *C = dyn_cast<Constant>(LHS)) {
418 IsNull = C->isNullValue();
419 } else {
420 // Non-constant pointer - assume filter passes (conservative)
421 return true;
422 }
423
424 // Evaluate comparison
425 switch (Op) {
426 case EQ:
427 return IsNull;
428 case NE:
429 return !IsNull;
430 }
431 return true;
432 }
433
434 // Dynamic property value, assume filter passes.
435 if (DynamicProperties.count(PropName))
436 return true;
437
438 // Unknown property, record an error.
439 return createStringError("expected enabled property name, got '" +
440 PropName + "'");
441 }
442};
443} // anonymous namespace
444
449 if (IO.Filter.empty())
450 return true;
451
452 // Collect constant property values for filter evaluation.
453 DenseMap<StringRef, int64_t> IntPropertyValues;
454 DenseMap<StringRef, StringRef> StringPropertyValues;
455 DenseMap<StringRef, Value *> PointerPropertyValues;
456 DenseMap<StringRef, PropertyType> DynamicProperties;
457
458 for (auto &Arg : IO.IRTArgs) {
459 if (!Arg.Enabled)
460 continue;
461
462 // Get the value for this argument.
463 Value *ArgValue = Arg.GetterCB(V, *Arg.Ty, IConf, IIRB);
464 if (!ArgValue)
465 continue;
466
467 // TODO: This is likely too broad and we might want GetterCB to indicate
468 // changes.
469 Changed = true;
470
471 if (auto *CI = dyn_cast<ConstantInt>(ArgValue)) {
472 // Check for constant integer values.
473 IntPropertyValues[Arg.Name] = CI->getSExtValue();
474 } else if ((Arg.Flags & IRTArg::STRING) && isa<Constant>(ArgValue)) {
475 // Check for constant string values (marked with STRING flag).
476 if (auto *GV = dyn_cast<GlobalVariable>(ArgValue))
477 if (GV->isConstant() && GV->hasInitializer())
478 if (auto *CDA = dyn_cast<ConstantDataArray>(GV->getInitializer()))
479 if (CDA->isCString())
480 StringPropertyValues[Arg.Name] = CDA->getAsCString();
481 } else if (ArgValue->getType()->isPointerTy()) {
482 // Check for pointer values (for null comparisons), after the strings.
483 PointerPropertyValues[Arg.Name] = ArgValue;
484 } else {
485 // If the value is not constant, we skip it - the filter will pass
486 // for dynamic values - but we still want to report broken filters.
487 DynamicProperties[Arg.Name] =
488 Arg.Ty->isIntegerTy()
489 ? INT
490 : (Arg.Flags & IRTArg::STRING
491 ? STRING
492 : (Arg.Ty->isPointerTy() ? POINTER : UNKNOWN));
493 }
494 }
495
496 FilterEvaluator Evaluator(IO.Filter, IntPropertyValues, StringPropertyValues,
497 PointerPropertyValues, DynamicProperties,
498 IO.FlagNames);
499
500 Expected<bool> Result = Evaluator.evaluate();
501 if (!Result) {
502 // Emit an error if the filter is malformed.
504 Twine("malformed filter expression for instrumentation opportunity '") +
505 IO.getName() + Twine("': ") + toString(Result.takeError()) +
506 Twine("\nFilter: ") + IO.Filter,
507 DS_Error));
508 return false;
509 }
510
511 return Result.get();
512}
static bool evaluate(const MCSpecifierExpr &Expr, MCValue &Res, const MCAssembler *Asm)
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file defines the DenseMap class.
static Cursor skipWhitespace(Cursor C)
Skip the leading whitespace characters and return the updated cursor.
Definition MILexer.cpp:85
Value * RHS
Value * LHS
Diagnostic information for IR instrumentation reporting.
This class evaluates LLVM IR, producing the Constant representing each SSA instruction.
Definition Evaluator.h:37
Tagged union holding either a T or a Error.
Definition Error.h:485
LLVM_ABI void diagnose(const DiagnosticInfo &DI)
Report a message to the currently installed diagnostic handler.
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:490
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
Changed
LLVM_ABI bool evaluateFilter(Value &V, bool &Changed, InstrumentationOpportunity &IO, InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB)
Evaluate the filter expression against the current instrumentation opportunity.
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
DWARFExpression::Operation Op
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
#define EQ(a, b)
Definition regexec.c:65
The class that contains the configuration for the instrumentor.
Base class for instrumentation opportunities.
StringMap< int32_t > FlagNames
Flag names and their integer bitmask values.
virtual StringRef getName() const =0
Get the name of the instrumentation opportunity.
SmallVector< IRTArg > IRTArgs
The list of possible arguments for the instrumentation runtime function.
StringRef Filter
A filter expression to be matched against runtime property values.
An IR builder augmented with extra information for the instrumentor pass.