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