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)).str() +
58 "'");
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 // Parse left-hand side (property name).
152 size_t Start = Pos;
153 while (Pos < Expr.size() && (std::isalnum(Expr[Pos]) || Expr[Pos] == '_'))
154 ++Pos;
155
156 StringRef PropName = Expr.slice(Start, Pos);
157 if (PropName.empty())
158 return createStringError("expected property name at position " +
159 std::to_string(Pos));
160
162
163 // Check for .startswith() method call.
164 if (Pos < Expr.size() && Expr[Pos] == '.') {
165 ++Pos;
167
168 // Parse method name.
169 Start = Pos;
170 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
171 ++Pos;
172
173 StringRef MethodName = Expr.slice(Start, Pos);
175
176 if (MethodName == "startswith") {
177 // Parse (.
178 if (Pos >= Expr.size() || Expr[Pos] != '(')
179 return createStringError(
180 "expected '(' after 'startswith' at position " +
181 std::to_string(Pos));
182
183 ++Pos;
184
185 // Parse string argument.
186 auto Prefix = parseStringLiteral();
187 if (!Prefix)
188 return Prefix.takeError();
189
191
192 // Parse )
193 if (Pos >= Expr.size() || Expr[Pos] != ')')
194 return createStringError(
195 "expected ')' to close 'startswith' call at position " +
196 std::to_string(Pos));
197
198 ++Pos;
199
200 // Evaluate startswith.
201 auto StrIt = StringPropertyValues.find(PropName);
202 if (StrIt != StringPropertyValues.end())
203 return StrIt->second.starts_with(*Prefix);
204
205 // If this is a dynamic string property, assume the filter passes.
206 if (DynamicProperties.lookup_or(PropName, UNKNOWN) == STRING)
207 return true;
208
209 return createStringError(
210 "startswith is only valid on string properties not '" + PropName +
211 "'");
212 }
213
214 return createStringError("unknown method '" + MethodName.str() +
215 "' on property '" + PropName.str() + "'");
216 }
217
218 // Check if this is an integer property.
219 auto IntIt = IntPropertyValues.find(PropName);
220 if (IntIt != IntPropertyValues.end()) {
221 int64_t LHS = IntIt->second;
222
223 // Parse operator.
224 enum OpKind { EQ, NE, LT, GT, LE, GE } Op;
225 if (Pos < Expr.size()) {
226 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
227 Op = EQ;
228 Pos += 2;
229 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
230 Expr[Pos + 1] == '=') {
231 Op = NE;
232 Pos += 2;
233 } else if (Expr[Pos] == '<' && Pos + 1 < Expr.size() &&
234 Expr[Pos + 1] == '=') {
235 Op = LE;
236 Pos += 2;
237 } else if (Expr[Pos] == '>' && Pos + 1 < Expr.size() &&
238 Expr[Pos + 1] == '=') {
239 Op = GE;
240 Pos += 2;
241 } else if (Expr[Pos] == '<') {
242 Op = LT;
243 Pos += 1;
244 } else if (Expr[Pos] == '>') {
245 Op = GT;
246 Pos += 1;
247 } else {
248 return createStringError("expected comparison operator (==, !=, <, "
249 ">, <=, >=) at position " +
250 std::to_string(Pos));
251 }
252 } else {
253 return createStringError(
254 "expected comparison operator after property '" + PropName.str() +
255 "'");
256 }
257
259
260 // Parse right-hand side (constant value).
261 Start = Pos;
262 bool Negative = false;
263 if (Pos < Expr.size() && Expr[Pos] == '-') {
264 Negative = true;
265 ++Pos;
266 }
267
268 size_t DigitStart = Pos;
269 while (Pos < Expr.size() && std::isdigit(Expr[Pos]))
270 ++Pos;
271
272 if (Pos == DigitStart)
273 return createStringError("expected integer value at position " +
274 std::to_string(Pos));
275
276 StringRef ValueStr = Expr.slice(Start, Pos);
277 int64_t RHS = 0;
278 if (ValueStr.getAsInteger(10, RHS))
279 return createStringError("invalid integer value '" + ValueStr.str() +
280 "'");
281
282 if (Negative)
283 RHS = -RHS;
284
285 // Evaluate comparison.
286 switch (Op) {
287 case EQ:
288 return LHS == RHS;
289 case NE:
290 return LHS != RHS;
291 case LT:
292 return LHS < RHS;
293 case GT:
294 return LHS > RHS;
295 case LE:
296 return LHS <= RHS;
297 case GE:
298 return LHS >= RHS;
299 }
300 return true;
301 }
302
303 // Check if this is a string property.
304 auto StrIt = StringPropertyValues.find(PropName);
305 if (StrIt != StringPropertyValues.end()) {
306 StringRef LHS = StrIt->second;
307
308 // Parse operator (only == and != for strings).
309 enum OpKind { EQ, NE } Op;
310 if (Pos < Expr.size()) {
311 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
312 Op = EQ;
313 Pos += 2;
314 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
315 Expr[Pos + 1] == '=') {
316 Op = NE;
317 Pos += 2;
318 } else {
319 return createStringError("string property '" + PropName.str() +
320 "' only supports == and != operators");
321 }
322 } else {
323 return createStringError(
324 "expected comparison operator after string property '" +
325 PropName.str() + "'");
326 }
327
329
330 // Parse right-hand side (string literal).
331 auto RHS = parseStringLiteral();
332 if (!RHS)
333 return RHS.takeError();
334
335 // Evaluate comparison.
336 switch (Op) {
337 case EQ:
338 return LHS == *RHS;
339 case NE:
340 return LHS != *RHS;
341 }
342 return true;
343 }
344
345 // Check if this is a pointer property.
346 auto PtrIt = PointerPropertyValues.find(PropName);
347 if (PtrIt != PointerPropertyValues.end()) {
348 Value *LHS = PtrIt->second;
349
350 // Parse operator (only == and != for pointers).
351 enum OpKind { EQ, NE } Op;
352 if (Pos < Expr.size()) {
353 if (Expr[Pos] == '=' && Pos + 1 < Expr.size() && Expr[Pos + 1] == '=') {
354 Op = EQ;
355 Pos += 2;
356 } else if (Expr[Pos] == '!' && Pos + 1 < Expr.size() &&
357 Expr[Pos + 1] == '=') {
358 Op = NE;
359 Pos += 2;
360 } else {
361 return createStringError("pointer property '" + PropName.str() +
362 "' only supports == and != operators");
363 }
364 } else {
365 return createStringError(
366 "expected comparison operator after pointer property '" +
367 PropName.str() + "'");
368 }
369
371
372 // Parse right-hand side (must be "null").
373 Start = Pos;
374 while (Pos < Expr.size() && std::isalpha(Expr[Pos]))
375 ++Pos;
376
377 StringRef RHS = Expr.slice(Start, Pos);
378 if (RHS != "null")
379 return createStringError("pointer comparisons only support 'null' as "
380 "right-hand side, got '" +
381 RHS.str() + "'");
382
383 // Check if the pointer is a constant null.
384 bool IsNull = false;
385 if (auto *C = dyn_cast<Constant>(LHS)) {
386 IsNull = C->isNullValue();
387 } else {
388 // Non-constant pointer - assume filter passes (conservative)
389 return true;
390 }
391
392 // Evaluate comparison
393 switch (Op) {
394 case EQ:
395 return IsNull;
396 case NE:
397 return !IsNull;
398 }
399 return true;
400 }
401
402 // Dynamic property value, assume filter passes.
403 if (DynamicProperties.count(PropName))
404 return true;
405
406 // Unknown property, record an error.
407 return createStringError("expected enabled property name, got '" +
408 PropName.str() + "'");
409 }
410};
411} // anonymous namespace
412
417 if (IO.Filter.empty())
418 return true;
419
420 // Collect constant property values for filter evaluation.
421 DenseMap<StringRef, int64_t> IntPropertyValues;
422 DenseMap<StringRef, StringRef> StringPropertyValues;
423 DenseMap<StringRef, Value *> PointerPropertyValues;
424 DenseMap<StringRef, PropertyType> DynamicProperties;
425
426 for (auto &Arg : IO.IRTArgs) {
427 if (!Arg.Enabled)
428 continue;
429
430 // Get the value for this argument.
431 Value *ArgValue = Arg.GetterCB(V, *Arg.Ty, IConf, IIRB);
432 if (!ArgValue)
433 continue;
434
435 if (auto *CI = dyn_cast<ConstantInt>(ArgValue)) {
436 // Check for constant integer values.
437 IntPropertyValues[Arg.Name] = CI->getSExtValue();
438 } else if ((Arg.Flags & IRTArg::STRING) && isa<Constant>(ArgValue)) {
439 // Check for constant string values (marked with STRING flag).
440 if (auto *GV = dyn_cast<GlobalVariable>(ArgValue))
441 if (GV->isConstant() && GV->hasInitializer())
442 if (auto *CDA = dyn_cast<ConstantDataArray>(GV->getInitializer()))
443 if (CDA->isCString())
444 StringPropertyValues[Arg.Name] = CDA->getAsCString();
445 } else if (ArgValue->getType()->isPointerTy()) {
446 // Check for pointer values (for null comparisons), after the strings.
447 PointerPropertyValues[Arg.Name] = ArgValue;
448 } else {
449 // If the value is not constant, we skip it - the filter will pass
450 // for dynamic values - but we still want to report broken filters.
451 DynamicProperties[Arg.Name] =
452 Arg.Ty->isIntegerTy()
453 ? INT
454 : (Arg.Flags & IRTArg::STRING
455 ? STRING
456 : (Arg.Ty->isPointerTy() ? POINTER : UNKNOWN));
457 }
458 }
459
460 FilterEvaluator Evaluator(IO.Filter, IntPropertyValues, StringPropertyValues,
461 PointerPropertyValues, DynamicProperties);
462
463 Expected<bool> Result = Evaluator.evaluate();
464 if (!Result) {
465 // Emit an error if the filter is malformed.
467 Twine("malformed filter expression for instrumentation opportunity '") +
468 IO.getName() + Twine("': ") + toString(Result.takeError()) +
469 Twine("\nFilter: ") + IO.Filter,
470 DS_Error));
471 return false;
472 }
473
474 return Result.get();
475}
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
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
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:284
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
LLVM_ABI bool evaluateFilter(Value &V, 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.