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, APInt> &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, DenseMap<StringRef, APInt> &IntPropertyValues,
38 DenseMap<StringRef, StringRef> &StringPropertyValues,
39 DenseMap<StringRef, Value *> &PointerPropertyValues,
40 DenseMap<StringRef, PropertyType> &DynamicProperties,
41 StringMap<int32_t> &FlagNameVals)
42 : Expr(Expr), IntPropertyValues(IntPropertyValues),
43 StringPropertyValues(StringPropertyValues),
44 PointerPropertyValues(PointerPropertyValues),
45 DynamicProperties(DynamicProperties), FlagNameVals(FlagNameVals) {}
46
47 Expected<bool> evaluate() {
48 if (Expr.empty())
49 return true;
50
51 Expected<bool> Result = parseOrExpr();
52
53 // Check if we consumed the entire expression.
55 if (Pos < Expr.size() && Result)
56 return createStringError(
57 "unexpected characters at position " + std::to_string(Pos) + ": '" +
58 Expr.substr(Pos, std::min<size_t>(10, Expr.size() - Pos)) + "'");
59
60 return Result;
61 }
62
63private:
64 void skipWhitespace() {
65 while (Pos < Expr.size() && std::isspace(Expr[Pos]))
66 ++Pos;
67 }
68
69 Expected<bool> parseOrExpr() {
70 Expected<bool> Result = parseAndExpr();
71 while (Result) {
73 if (Pos + 1 < Expr.size() && Expr[Pos] == '|' && Expr[Pos + 1] == '|') {
74 Pos += 2;
75 Expected<bool> NextResult = parseAndExpr();
76 if (!NextResult)
77 return NextResult;
78 *Result |= *NextResult;
79 } else {
80 break;
81 }
82 }
83 return Result;
84 }
85
86 Expected<bool> parseAndExpr() {
87 Expected<bool> Result = parsePrimary();
88 while (Result) {
90 if (Pos + 1 < Expr.size() && Expr[Pos] == '&' && Expr[Pos + 1] == '&') {
91 Pos += 2;
92 Expected<bool> NextResult = parsePrimary();
93 if (!NextResult)
94 return NextResult;
95 *Result &= *NextResult;
96 } else {
97 break;
98 }
99 }
100 return Result;
101 }
102
103 Expected<bool> parsePrimary() {
105
106 // Check for opening parenthesis.
107 if (Pos < Expr.size() && Expr[Pos] == '(') {
108 ++Pos; // Skip '('
109 Expected<bool> Result = parseOrExpr();
110
112 if (Result && (Pos >= Expr.size() || Expr[Pos] != ')'))
113 return createStringError("expected ')' at position " +
114 std::to_string(Pos));
115
116 // Skip ')'.
117 ++Pos;
118 return Result;
119 }
120
121 // Otherwise parse a comparison.
122 return parseComparison();
123 }
124
125 // Parse a quoted string literal.
126 Expected<StringRef> parseStringLiteral() {
128 if (Pos >= Expr.size() || Expr[Pos] != '"')
129 return createStringError("expected string literal at position " +
130 std::to_string(Pos));
131
132 // Skip opening quote.
133 ++Pos;
134 size_t Start = Pos;
135 while (Pos < Expr.size() && Expr[Pos] != '"')
136 ++Pos;
137
138 if (Pos >= Expr.size())
139 return createStringError("unclosed string literal starting at position " +
140 std::to_string(Start - 1));
141
142 StringRef Result = Expr.slice(Start, Pos);
143 // Skip closing quote.
144 ++Pos;
145 return Result;
146 }
147
148 Expected<bool> parseComparison() {
150
151 // Check for logical not operator.
152 bool LogicalNot = false;
153 if (Pos < Expr.size() && Expr[Pos] == '!') {
154 LogicalNot = true;
155 ++Pos;
156 }
157
158 // Parse left-hand side (property name).
159 size_t Start = Pos;
160 while (Pos < Expr.size() && (std::isalnum(Expr[Pos]) || Expr[Pos] == '_'))
161 ++Pos;
162
163 StringRef PropName = Expr.slice(Start, Pos);
164 if (PropName.empty())
165 return createStringError("expected property name at position " +
166 std::to_string(Pos));
167
169
170 // Parse property fields and methods.
171 if (Pos < Expr.size() && Expr[Pos] == '.') {
172 ++Pos;
174
175 // Parse field name.
176 Start = Pos;
177 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
178 ++Pos;
179
180 StringRef FieldName = Expr.slice(Start, Pos);
182
183 // Handle flag values.
184 if (PropName == "flags") {
185 auto FlagValIt = IntPropertyValues.find("flags");
186 if (FlagValIt != IntPropertyValues.end()) {
187 auto FlagNameIt = FlagNameVals.find(FieldName);
188 if (FlagNameIt == FlagNameVals.end())
189 return createStringError("Invalid flag '" + FieldName + "'");
190 return ((static_cast<int32_t>(FlagValIt->second.getZExtValue()) &
191 FlagNameIt->second) == FlagNameIt->second) ^
193 }
194 }
195
196 // Check for .startswith() method call.
197 if (FieldName == "startswith") {
198 // Parse (.
199 if (Pos >= Expr.size() || Expr[Pos] != '(')
200 return createStringError(
201 "expected '(' after 'startswith' at position " +
202 std::to_string(Pos));
203
204 ++Pos;
205
206 // Parse string argument.
207 auto Prefix = parseStringLiteral();
208 if (!Prefix)
209 return Prefix.takeError();
210
212
213 // Parse )
214 if (Pos >= Expr.size() || Expr[Pos] != ')')
215 return createStringError(
216 "expected ')' to close 'startswith' call at position " +
217 std::to_string(Pos));
218
219 ++Pos;
220
221 // Evaluate startswith.
222 auto StrIt = StringPropertyValues.find(PropName);
223 if (StrIt != StringPropertyValues.end())
224 return StrIt->second.starts_with(*Prefix) ^ LogicalNot;
225
226 // If this is a dynamic string property, assume the filter passes.
227 if (DynamicProperties.lookup_or(PropName, UNKNOWN) == STRING)
228 return true;
229
230 return createStringError(
231 "startswith is only valid on string properties not '" + PropName +
232 "'");
233 }
234
235 return createStringError("unknown method '" + FieldName +
236 "' on property '" + PropName + "'");
237 } else if (LogicalNot) {
238 return createStringError("expected boolean value at position " +
239 std::to_string(Start));
240 }
241
242 // Check if this is an integer property.
243 auto IntIt = IntPropertyValues.find(PropName);
244 if (IntIt != IntPropertyValues.end()) {
245 auto LHS = IntIt->second;
246
247 // Parse operator.
248 enum OpKind { EQ, NE, LT, GT, LE, GE } Op;
249 if (Pos < Expr.size()) {
250 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
251 Op = EQ;
252 Pos += 2;
253 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
254 Expr[Pos + 1] == '=') {
255 Op = NE;
256 Pos += 2;
257 } else if (Expr[Pos] == '<' && Pos + 1 < Expr.size() &&
258 Expr[Pos + 1] == '=') {
259 Op = LE;
260 Pos += 2;
261 } else if (Expr[Pos] == '>' && Pos + 1 < Expr.size() &&
262 Expr[Pos + 1] == '=') {
263 Op = GE;
264 Pos += 2;
265 } else if (Expr[Pos] == '<') {
266 Op = LT;
267 Pos += 1;
268 } else if (Expr[Pos] == '>') {
269 Op = GT;
270 Pos += 1;
271 } else {
272 return createStringError("expected comparison operator (==, !=, <, "
273 ">, <=, >=) at position " +
274 std::to_string(Pos));
275 }
276 } else {
277 return createStringError(
278 "expected comparison operator after property '" + PropName + "'");
279 }
280
282
283 // Parse right-hand side (constant value).
284 Start = Pos;
285 bool Negative = false;
286 if (Pos < Expr.size() && Expr[Pos] == '-') {
287 Negative = true;
288 ++Pos;
289 }
290
291 size_t DigitStart = Pos;
292 uint8_t DigitRadix = 10;
293
294 // Parse binary literals.
295 if (Pos + 1 < Expr.size() && Expr[Pos] == '0' && Expr[Pos + 1] == 'b') {
296 DigitStart = (Pos += 2);
297 DigitRadix = 2;
298 while (Pos < Expr.size() && (Expr[Pos] == '0' || Expr[Pos] == '1'))
299 ++Pos;
300 } else {
301 // Parse decimal literals.
302 while (Pos < Expr.size() && std::isdigit(Expr[Pos]))
303 ++Pos;
304 }
305
306 if (Pos == DigitStart)
307 return createStringError("expected integer value at position " +
308 std::to_string(Pos));
309
310 StringRef ValueStr = Expr.slice(DigitStart, Pos);
311 APInt RHS(LHS.getBitWidth(), ValueStr, DigitRadix);
312
313 if (Negative)
314 RHS.negate();
315
316 // Evaluate comparison.
317 switch (Op) {
318 case EQ:
319 return LHS == RHS;
320 case NE:
321 return LHS != RHS;
322 case LT:
323 return LHS.slt(RHS);
324 case GT:
325 return LHS.sgt(RHS);
326 case LE:
327 return LHS.sle(RHS);
328 case GE:
329 return LHS.sge(RHS);
330 }
331 return true;
332 }
333
334 // Check if this is a string property.
335 auto StrIt = StringPropertyValues.find(PropName);
336 if (StrIt != StringPropertyValues.end()) {
337 StringRef LHS = StrIt->second;
338
339 // Parse operator (only == and != for strings).
340 enum OpKind { EQ, NE } Op;
341 if (Pos < Expr.size()) {
342 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
343 Op = EQ;
344 Pos += 2;
345 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
346 Expr[Pos + 1] == '=') {
347 Op = NE;
348 Pos += 2;
349 } else {
350 return createStringError("string property '" + PropName +
351 "' only supports == and != operators");
352 }
353 } else {
354 return createStringError(
355 "expected comparison operator after string property '" + PropName +
356 "'");
357 }
358
360
361 // Parse right-hand side (string literal).
362 auto RHS = parseStringLiteral();
363 if (!RHS)
364 return RHS.takeError();
365
366 // Evaluate comparison.
367 switch (Op) {
368 case EQ:
369 return LHS == *RHS;
370 case NE:
371 return LHS != *RHS;
372 }
373 return true;
374 }
375
376 // Check if this is a pointer property.
377 auto PtrIt = PointerPropertyValues.find(PropName);
378 if (PtrIt != PointerPropertyValues.end()) {
379 Value *LHS = PtrIt->second;
380
381 // Parse operator (only == and != for pointers).
382 enum OpKind { EQ, NE } Op;
383 if (Pos < Expr.size()) {
384 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
385 Op = EQ;
386 Pos += 2;
387 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
388 Expr[Pos + 1] == '=') {
389 Op = NE;
390 Pos += 2;
391 } else {
392 return createStringError("pointer property '" + PropName +
393 "' only supports == and != operators");
394 }
395 } else {
396 return createStringError(
397 "expected comparison operator after pointer property '" + PropName +
398 "'");
399 }
400
402
403 // Parse right-hand side (must be "null").
404 Start = Pos;
405 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
406 ++Pos;
407
408 StringRef RHS = Expr.slice(Start, Pos);
409 if (RHS != "null")
410 return createStringError("pointer comparisons only support 'null' as "
411 "right-hand side, got '" +
412 RHS + "'");
413
414 // Check if the pointer is a constant null.
415 bool IsNull = false;
416 if (auto *C = dyn_cast<Constant>(LHS)) {
417 IsNull = C->isNullValue();
418 } else {
419 // Non-constant pointer - assume filter passes (conservative)
420 return true;
421 }
422
423 // Evaluate comparison
424 switch (Op) {
425 case EQ:
426 return IsNull;
427 case NE:
428 return !IsNull;
429 }
430 return true;
431 }
432
433 // Dynamic property value, assume filter passes.
434 if (DynamicProperties.count(PropName))
435 return true;
436
437 // Unknown property, record an error.
438 return createStringError("expected enabled property name, got '" +
439 PropName + "'");
440 }
441};
442} // anonymous namespace
443
448 if (IO.Filter.empty())
449 return true;
450
451 // Collect constant property values for filter evaluation.
452 DenseMap<StringRef, APInt> IntPropertyValues;
453 DenseMap<StringRef, StringRef> StringPropertyValues;
454 DenseMap<StringRef, Value *> PointerPropertyValues;
455 DenseMap<StringRef, PropertyType> DynamicProperties;
456
457 for (auto &Arg : IO.IRTArgs) {
458 if (!Arg.Enabled)
459 continue;
460
461 // Get the value for this argument.
462 Value *ArgValue = Arg.GetterCB(V, *Arg.Ty, IConf, IIRB);
463 if (!ArgValue)
464 continue;
465
466 // TODO: This is likely too broad and we might want GetterCB to indicate
467 // changes.
468 Changed = true;
469
470 if (auto *CI = dyn_cast<ConstantInt>(ArgValue)) {
471 // Check for constant integer values.
472 IntPropertyValues[Arg.Name] = CI->getValue();
473 } else if ((Arg.Flags & IRTArg::STRING) && isa<Constant>(ArgValue)) {
474 // Check for constant string values (marked with STRING flag).
475 if (auto *GV = dyn_cast<GlobalVariable>(ArgValue))
476 if (GV->isConstant() && GV->hasInitializer())
477 if (auto *CDA = dyn_cast<ConstantDataArray>(GV->getInitializer()))
478 if (CDA->isCString())
479 StringPropertyValues[Arg.Name] = CDA->getAsCString();
480 } else if (ArgValue->getType()->isPointerTy()) {
481 // Check for pointer values (for null comparisons), after the strings.
482 PointerPropertyValues[Arg.Name] = ArgValue;
483 } else {
484 // If the value is not constant, we skip it - the filter will pass
485 // for dynamic values - but we still want to report broken filters.
486 DynamicProperties[Arg.Name] =
487 Arg.Ty->isIntegerTy()
488 ? INT
489 : (Arg.Flags & IRTArg::STRING
490 ? STRING
491 : (Arg.Ty->isPointerTy() ? POINTER : UNKNOWN));
492 }
493 }
494
495 FilterEvaluator Evaluator(IO.Filter, IntPropertyValues, StringPropertyValues,
496 PointerPropertyValues, DynamicProperties,
497 IO.FlagNames);
498
499 Expected<bool> Result = Evaluator.evaluate();
500 if (!Result) {
501 // Emit an error if the filter is malformed.
503 Twine("malformed filter expression for instrumentation opportunity '") +
504 IO.getName() + Twine("': ") + toString(Result.takeError()) +
505 Twine("\nFilter: ") + IO.Filter,
506 DS_Error));
507 return false;
508 }
509
510 return Result.get();
511}
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.
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:277
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
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.