-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
field_analyser.cpp
380 lines (319 loc) · 7.61 KB
/
field_analyser.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#include <iostream>
#include <cassert>
#include "field_analyser.h"
namespace bpftrace {
namespace ast {
void FieldAnalyser::error(const std::string &msg, const location &loc)
{
bpftrace_.error(out_, loc, msg);
}
void FieldAnalyser::warning(const std::string &msg, const location &loc)
{
bpftrace_.warning(out_, loc, msg);
}
void FieldAnalyser::visit(Integer &integer __attribute__((unused)))
{
}
void FieldAnalyser::visit(PositionalParameter ¶m __attribute__((unused)))
{
}
void FieldAnalyser::visit(String &string __attribute__((unused)))
{
}
void FieldAnalyser::visit(StackMode &mode __attribute__((unused)))
{
}
void FieldAnalyser::visit(Identifier &identifier)
{
bpftrace_.btf_set_.insert(identifier.ident);
}
void FieldAnalyser::visit(Jump &jump __attribute__((unused)))
{
}
void FieldAnalyser::check_kfunc_args(void)
{
if (has_kfunc_probe_ && has_mixed_args_)
{
error("Probe has attach points with mixed arguments",
mixed_args_loc_);
}
}
void FieldAnalyser::visit(Builtin &builtin)
{
if (builtin.ident == "ctx")
{
switch (prog_type_)
{
case BPF_PROG_TYPE_KPROBE:
bpftrace_.btf_set_.insert("struct pt_regs");
break;
case BPF_PROG_TYPE_PERF_EVENT:
bpftrace_.btf_set_.insert("struct bpf_perf_event_data");
break;
default:
break;
}
}
else if (builtin.ident == "curtask")
{
type_ = "struct task_struct";
bpftrace_.btf_set_.insert(type_);
}
else if (builtin.ident == "args")
{
has_builtin_args_ = true;
}
else if (builtin.ident == "retval")
{
check_kfunc_args();
auto it = ap_args_.find("$retval");
if (it != ap_args_.end() && it->second.IsCastTy())
type_ = it->second.cast_type;
else
type_ = "";
}
}
void FieldAnalyser::visit(Call &call)
{
if (call.vargs) {
for (Expression *expr : *call.vargs) {
expr->accept(*this);
}
}
}
void FieldAnalyser::visit(Map &map)
{
MapKey key;
if (map.vargs) {
for (Expression *expr : *map.vargs) {
expr->accept(*this);
}
}
}
void FieldAnalyser::visit(Variable &var __attribute__((unused)))
{
}
void FieldAnalyser::visit(ArrayAccess &arr)
{
arr.expr->accept(*this);
arr.indexpr->accept(*this);
}
void FieldAnalyser::visit(Binop &binop)
{
binop.left->accept(*this);
binop.right->accept(*this);
}
void FieldAnalyser::visit(Unop &unop)
{
unop.expr->accept(*this);
}
void FieldAnalyser::visit(Ternary &ternary)
{
ternary.cond->accept(*this);
ternary.left->accept(*this);
ternary.right->accept(*this);
}
void FieldAnalyser::visit(While &while_block)
{
while_block.cond->accept(*this);
for (Statement *stmt : *while_block.stmts)
{
stmt->accept(*this);
}
}
void FieldAnalyser::visit(If &if_block)
{
if_block.cond->accept(*this);
for (Statement *stmt : *if_block.stmts) {
stmt->accept(*this);
}
if (if_block.else_stmts) {
for (Statement *stmt : *if_block.else_stmts) {
stmt->accept(*this);
}
}
}
void FieldAnalyser::visit(Unroll &unroll)
{
// visit statements in unroll once
for (Statement *stmt : *unroll.stmts)
{
stmt->accept(*this);
}
}
void FieldAnalyser::visit(FieldAccess &acc)
{
has_builtin_args_ = false;
acc.expr->accept(*this);
if (has_builtin_args_)
{
check_kfunc_args();
auto it = ap_args_.find(acc.field);
if (it != ap_args_.end() && it->second.IsCastTy())
type_ = it->second.cast_type;
else
type_ = "";
has_builtin_args_ = false;
}
else if (!type_.empty())
{
type_ = bpftrace_.btf_.type_of(type_, acc.field);
bpftrace_.btf_set_.insert(type_);
}
}
void FieldAnalyser::visit(Cast &cast)
{
cast.expr->accept(*this);
type_ = cast.cast_type;
assert(!type_.empty());
bpftrace_.btf_set_.insert(type_);
}
void FieldAnalyser::visit(Tuple &tuple)
{
for (Expression *expr : *tuple.elems)
expr->accept(*this);
}
void FieldAnalyser::visit(ExprStatement &expr)
{
expr.expr->accept(*this);
}
void FieldAnalyser::visit(AssignMapStatement &assignment)
{
assignment.map->accept(*this);
assignment.expr->accept(*this);
}
void FieldAnalyser::visit(AssignVarStatement &assignment)
{
assignment.expr->accept(*this);
}
void FieldAnalyser::visit(Predicate &pred)
{
pred.expr->accept(*this);
}
bool FieldAnalyser::compare_args(const std::map<std::string, SizedType>& args1,
const std::map<std::string, SizedType>& args2)
{
auto pred = [](auto a, auto b) { return a.first == b.first; };
return args1.size() == args2.size() &&
std::equal(args1.begin(), args1.end(), args2.begin(), pred);
}
bool FieldAnalyser::resolve_args(AttachPoint &ap)
{
bool kretfunc = ap.provider == "kretfunc";
std::string func = ap.func;
// load AP arguments into ap_args_
ap_args_.clear();
if (ap.need_expansion)
{
std::set<std::string> matches;
// Find all the matches for the wildcard..
try
{
matches = bpftrace_.find_wildcard_matches(ap);
}
catch (const WildcardException &e)
{
std::cerr << e.what() << std::endl;
return false;
}
// ... and check if they share same arguments.
//
// If they have different arguments, we have a potential
// problem, but only if the 'args->arg' is actually used.
// So far we just set has_mixed_args_ bool and continue.
bool first = true;
for (auto func : matches)
{
std::map<std::string, SizedType> args;
if (!bpftrace_.btf_.resolve_args(func, first ? ap_args_ : args, kretfunc))
{
if (!first && !compare_args(args, ap_args_))
{
has_mixed_args_ = true;
mixed_args_loc_ = ap.loc;
break;
}
}
first = false;
}
}
else if (bpftrace_.btf_.resolve_args(ap.func, ap_args_, kretfunc))
{
error("Failed to resolve probe arguments", ap.loc);
return false;
}
// check if we already stored arguments for this probe
auto it = bpftrace_.btf_ap_args_.find(probe_->name());
if (it != bpftrace_.btf_ap_args_.end())
{
// we did, and it's different.. save the state and
// triger the error if there's args->xxx detected
if (!compare_args(it->second, ap_args_))
{
has_mixed_args_ = true;
mixed_args_loc_ = ap.loc;
}
}
else
{
// store/save args for each kfunc ap for later processing
bpftrace_.btf_ap_args_.insert({ probe_->name(), ap_args_ });
}
return true;
}
void FieldAnalyser::visit(AttachPoint &ap)
{
if (ap.provider == "kfunc" || ap.provider == "kretfunc")
{
has_kfunc_probe_ = true;
// starting new attach point, clear and load new
// variables/arguments for kfunc if detected
if (resolve_args(ap))
{
// pick up cast arguments immediately and let the
// FieldAnalyser to resolve args builtin
for (const auto& arg : ap_args_)
{
auto stype = arg.second;
if (stype.IsCastTy())
bpftrace_.btf_set_.insert(stype.cast_type);
}
}
}
}
void FieldAnalyser::visit(Probe &probe)
{
has_kfunc_probe_ = false;
has_mixed_args_ = false;
probe_ = &probe;
for (AttachPoint *ap : *probe.attach_points) {
ap->accept(*this);
ProbeType pt = probetype(ap->provider);
prog_type_ = progtype(pt);
}
if (probe.pred) {
probe.pred->accept(*this);
}
for (Statement *stmt : *probe.stmts) {
stmt->accept(*this);
}
}
void FieldAnalyser::visit(Program &program)
{
for (Probe *probe : *program.probes)
probe->accept(*this);
}
int FieldAnalyser::analyse()
{
if (bpftrace_.btf_.has_data())
root_->accept(*this);
std::string errors = err_.str();
if (!errors.empty())
{
std::cerr << errors;
return 1;
}
return 0;
}
} // namespace ast
} // namespace bpftrace