.. | .. |
---|
12 | 12 | * Initially based on the 'trace' prototype by Thomas Gleixner: |
---|
13 | 13 | * |
---|
14 | 14 | * http://lwn.net/Articles/415728/ ("Announcing a new utility: 'trace'") |
---|
15 | | - * |
---|
16 | | - * Released under the GPL v2. (and only v2, not any later version) |
---|
17 | 15 | */ |
---|
18 | 16 | |
---|
| 17 | +#include "util/record.h" |
---|
19 | 18 | #include <traceevent/event-parse.h> |
---|
20 | 19 | #include <api/fs/tracing_path.h> |
---|
| 20 | +#include <bpf/bpf.h> |
---|
| 21 | +#include "util/bpf_map.h" |
---|
| 22 | +#include "util/rlimit.h" |
---|
21 | 23 | #include "builtin.h" |
---|
22 | 24 | #include "util/cgroup.h" |
---|
23 | 25 | #include "util/color.h" |
---|
| 26 | +#include "util/config.h" |
---|
24 | 27 | #include "util/debug.h" |
---|
| 28 | +#include "util/dso.h" |
---|
25 | 29 | #include "util/env.h" |
---|
26 | 30 | #include "util/event.h" |
---|
| 31 | +#include "util/evsel.h" |
---|
| 32 | +#include "util/evsel_fprintf.h" |
---|
| 33 | +#include "util/synthetic-events.h" |
---|
27 | 34 | #include "util/evlist.h" |
---|
| 35 | +#include "util/evswitch.h" |
---|
| 36 | +#include "util/mmap.h" |
---|
| 37 | +#include <subcmd/pager.h> |
---|
28 | 38 | #include <subcmd/exec-cmd.h> |
---|
29 | 39 | #include "util/machine.h" |
---|
| 40 | +#include "util/map.h" |
---|
| 41 | +#include "util/symbol.h" |
---|
30 | 42 | #include "util/path.h" |
---|
31 | 43 | #include "util/session.h" |
---|
32 | 44 | #include "util/thread.h" |
---|
.. | .. |
---|
35 | 47 | #include "util/intlist.h" |
---|
36 | 48 | #include "util/thread_map.h" |
---|
37 | 49 | #include "util/stat.h" |
---|
| 50 | +#include "util/tool.h" |
---|
| 51 | +#include "util/util.h" |
---|
38 | 52 | #include "trace/beauty/beauty.h" |
---|
39 | 53 | #include "trace-event.h" |
---|
40 | 54 | #include "util/parse-events.h" |
---|
.. | .. |
---|
44 | 58 | #include "string2.h" |
---|
45 | 59 | #include "syscalltbl.h" |
---|
46 | 60 | #include "rb_resort.h" |
---|
| 61 | +#include "../perf.h" |
---|
47 | 62 | |
---|
48 | 63 | #include <errno.h> |
---|
49 | 64 | #include <inttypes.h> |
---|
.. | .. |
---|
57 | 72 | #include <linux/random.h> |
---|
58 | 73 | #include <linux/stringify.h> |
---|
59 | 74 | #include <linux/time64.h> |
---|
| 75 | +#include <linux/zalloc.h> |
---|
60 | 76 | #include <fcntl.h> |
---|
| 77 | +#include <sys/sysmacros.h> |
---|
61 | 78 | |
---|
62 | | -#include "sane_ctype.h" |
---|
| 79 | +#include <linux/ctype.h> |
---|
| 80 | +#include <perf/mmap.h> |
---|
63 | 81 | |
---|
64 | 82 | #ifndef O_CLOEXEC |
---|
65 | 83 | # define O_CLOEXEC 02000000 |
---|
.. | .. |
---|
69 | 87 | # define F_LINUX_SPECIFIC_BASE 1024 |
---|
70 | 88 | #endif |
---|
71 | 89 | |
---|
| 90 | +/* |
---|
| 91 | + * strtoul: Go from a string to a value, i.e. for msr: MSR_FS_BASE to 0xc0000100 |
---|
| 92 | + */ |
---|
| 93 | +struct syscall_arg_fmt { |
---|
| 94 | + size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); |
---|
| 95 | + bool (*strtoul)(char *bf, size_t size, struct syscall_arg *arg, u64 *val); |
---|
| 96 | + unsigned long (*mask_val)(struct syscall_arg *arg, unsigned long val); |
---|
| 97 | + void *parm; |
---|
| 98 | + const char *name; |
---|
| 99 | + u16 nr_entries; // for arrays |
---|
| 100 | + bool show_zero; |
---|
| 101 | +}; |
---|
| 102 | + |
---|
| 103 | +struct syscall_fmt { |
---|
| 104 | + const char *name; |
---|
| 105 | + const char *alias; |
---|
| 106 | + struct { |
---|
| 107 | + const char *sys_enter, |
---|
| 108 | + *sys_exit; |
---|
| 109 | + } bpf_prog_name; |
---|
| 110 | + struct syscall_arg_fmt arg[6]; |
---|
| 111 | + u8 nr_args; |
---|
| 112 | + bool errpid; |
---|
| 113 | + bool timeout; |
---|
| 114 | + bool hexret; |
---|
| 115 | +}; |
---|
| 116 | + |
---|
72 | 117 | struct trace { |
---|
73 | 118 | struct perf_tool tool; |
---|
74 | 119 | struct syscalltbl *sctbl; |
---|
75 | 120 | struct { |
---|
76 | | - int max; |
---|
77 | 121 | struct syscall *table; |
---|
| 122 | + struct bpf_map *map; |
---|
| 123 | + struct { // per syscall BPF_MAP_TYPE_PROG_ARRAY |
---|
| 124 | + struct bpf_map *sys_enter, |
---|
| 125 | + *sys_exit; |
---|
| 126 | + } prog_array; |
---|
78 | 127 | struct { |
---|
79 | | - struct perf_evsel *sys_enter, |
---|
| 128 | + struct evsel *sys_enter, |
---|
80 | 129 | *sys_exit, |
---|
81 | 130 | *augmented; |
---|
82 | 131 | } events; |
---|
| 132 | + struct bpf_program *unaugmented_prog; |
---|
83 | 133 | } syscalls; |
---|
| 134 | + struct { |
---|
| 135 | + struct bpf_map *map; |
---|
| 136 | + } dump; |
---|
84 | 137 | struct record_opts opts; |
---|
85 | | - struct perf_evlist *evlist; |
---|
| 138 | + struct evlist *evlist; |
---|
86 | 139 | struct machine *host; |
---|
87 | 140 | struct thread *current; |
---|
| 141 | + struct bpf_object *bpf_obj; |
---|
88 | 142 | struct cgroup *cgroup; |
---|
89 | 143 | u64 base_time; |
---|
90 | 144 | FILE *output; |
---|
91 | 145 | unsigned long nr_events; |
---|
| 146 | + unsigned long nr_events_printed; |
---|
| 147 | + unsigned long max_events; |
---|
| 148 | + struct evswitch evswitch; |
---|
92 | 149 | struct strlist *ev_qualifier; |
---|
93 | 150 | struct { |
---|
94 | 151 | size_t nr; |
---|
.. | .. |
---|
97 | 154 | struct { |
---|
98 | 155 | size_t nr; |
---|
99 | 156 | pid_t *entries; |
---|
| 157 | + struct bpf_map *map; |
---|
100 | 158 | } filter_pids; |
---|
101 | 159 | double duration_filter; |
---|
102 | 160 | double runtime_ms; |
---|
.. | .. |
---|
106 | 164 | } stats; |
---|
107 | 165 | unsigned int max_stack; |
---|
108 | 166 | unsigned int min_stack; |
---|
| 167 | + int raw_augmented_syscalls_args_size; |
---|
| 168 | + bool raw_augmented_syscalls; |
---|
| 169 | + bool fd_path_disabled; |
---|
| 170 | + bool sort_events; |
---|
109 | 171 | bool not_ev_qualifier; |
---|
110 | 172 | bool live; |
---|
111 | 173 | bool full_time; |
---|
.. | .. |
---|
113 | 175 | bool multiple_threads; |
---|
114 | 176 | bool summary; |
---|
115 | 177 | bool summary_only; |
---|
| 178 | + bool errno_summary; |
---|
116 | 179 | bool failure_only; |
---|
117 | 180 | bool show_comm; |
---|
118 | 181 | bool print_sample; |
---|
119 | 182 | bool show_tool_stats; |
---|
120 | 183 | bool trace_syscalls; |
---|
| 184 | + bool libtraceevent_print; |
---|
121 | 185 | bool kernel_syscallchains; |
---|
| 186 | + s16 args_alignment; |
---|
| 187 | + bool show_tstamp; |
---|
| 188 | + bool show_duration; |
---|
| 189 | + bool show_zeros; |
---|
| 190 | + bool show_arg_names; |
---|
| 191 | + bool show_string_prefix; |
---|
122 | 192 | bool force; |
---|
123 | 193 | bool vfs_getname; |
---|
124 | 194 | int trace_pgfaults; |
---|
| 195 | + char *perfconfig_events; |
---|
| 196 | + struct { |
---|
| 197 | + struct ordered_events data; |
---|
| 198 | + u64 last; |
---|
| 199 | + } oe; |
---|
125 | 200 | }; |
---|
126 | 201 | |
---|
127 | 202 | struct tp_field { |
---|
.. | .. |
---|
181 | 256 | return 0; |
---|
182 | 257 | } |
---|
183 | 258 | |
---|
184 | | -static int tp_field__init_uint(struct tp_field *field, struct format_field *format_field, bool needs_swap) |
---|
| 259 | +static int tp_field__init_uint(struct tp_field *field, struct tep_format_field *format_field, bool needs_swap) |
---|
185 | 260 | { |
---|
186 | 261 | return __tp_field__init_uint(field, format_field->size, format_field->offset, needs_swap); |
---|
187 | 262 | } |
---|
.. | .. |
---|
198 | 273 | return 0; |
---|
199 | 274 | } |
---|
200 | 275 | |
---|
201 | | -static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field) |
---|
| 276 | +static int tp_field__init_ptr(struct tp_field *field, struct tep_format_field *format_field) |
---|
202 | 277 | { |
---|
203 | 278 | return __tp_field__init_ptr(field, format_field->offset); |
---|
204 | 279 | } |
---|
.. | .. |
---|
210 | 285 | }; |
---|
211 | 286 | }; |
---|
212 | 287 | |
---|
213 | | -static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel, |
---|
214 | | - struct tp_field *field, |
---|
215 | | - const char *name) |
---|
| 288 | +/* |
---|
| 289 | + * The evsel->priv as used by 'perf trace' |
---|
| 290 | + * sc: for raw_syscalls:sys_{enter,exit} and syscalls:sys_{enter,exit}_SYSCALLNAME |
---|
| 291 | + * fmt: for all the other tracepoints |
---|
| 292 | + */ |
---|
| 293 | +struct evsel_trace { |
---|
| 294 | + struct syscall_tp sc; |
---|
| 295 | + struct syscall_arg_fmt *fmt; |
---|
| 296 | +}; |
---|
| 297 | + |
---|
| 298 | +static struct evsel_trace *evsel_trace__new(void) |
---|
216 | 299 | { |
---|
217 | | - struct format_field *format_field = perf_evsel__field(evsel, name); |
---|
| 300 | + return zalloc(sizeof(struct evsel_trace)); |
---|
| 301 | +} |
---|
| 302 | + |
---|
| 303 | +static void evsel_trace__delete(struct evsel_trace *et) |
---|
| 304 | +{ |
---|
| 305 | + if (et == NULL) |
---|
| 306 | + return; |
---|
| 307 | + |
---|
| 308 | + zfree(&et->fmt); |
---|
| 309 | + free(et); |
---|
| 310 | +} |
---|
| 311 | + |
---|
| 312 | +/* |
---|
| 313 | + * Used with raw_syscalls:sys_{enter,exit} and with the |
---|
| 314 | + * syscalls:sys_{enter,exit}_SYSCALL tracepoints |
---|
| 315 | + */ |
---|
| 316 | +static inline struct syscall_tp *__evsel__syscall_tp(struct evsel *evsel) |
---|
| 317 | +{ |
---|
| 318 | + struct evsel_trace *et = evsel->priv; |
---|
| 319 | + |
---|
| 320 | + return &et->sc; |
---|
| 321 | +} |
---|
| 322 | + |
---|
| 323 | +static struct syscall_tp *evsel__syscall_tp(struct evsel *evsel) |
---|
| 324 | +{ |
---|
| 325 | + if (evsel->priv == NULL) { |
---|
| 326 | + evsel->priv = evsel_trace__new(); |
---|
| 327 | + if (evsel->priv == NULL) |
---|
| 328 | + return NULL; |
---|
| 329 | + } |
---|
| 330 | + |
---|
| 331 | + return __evsel__syscall_tp(evsel); |
---|
| 332 | +} |
---|
| 333 | + |
---|
| 334 | +/* |
---|
| 335 | + * Used with all the other tracepoints. |
---|
| 336 | + */ |
---|
| 337 | +static inline struct syscall_arg_fmt *__evsel__syscall_arg_fmt(struct evsel *evsel) |
---|
| 338 | +{ |
---|
| 339 | + struct evsel_trace *et = evsel->priv; |
---|
| 340 | + |
---|
| 341 | + return et->fmt; |
---|
| 342 | +} |
---|
| 343 | + |
---|
| 344 | +static struct syscall_arg_fmt *evsel__syscall_arg_fmt(struct evsel *evsel) |
---|
| 345 | +{ |
---|
| 346 | + struct evsel_trace *et = evsel->priv; |
---|
| 347 | + |
---|
| 348 | + if (evsel->priv == NULL) { |
---|
| 349 | + et = evsel->priv = evsel_trace__new(); |
---|
| 350 | + |
---|
| 351 | + if (et == NULL) |
---|
| 352 | + return NULL; |
---|
| 353 | + } |
---|
| 354 | + |
---|
| 355 | + if (et->fmt == NULL) { |
---|
| 356 | + et->fmt = calloc(evsel->tp_format->format.nr_fields, sizeof(struct syscall_arg_fmt)); |
---|
| 357 | + if (et->fmt == NULL) |
---|
| 358 | + goto out_delete; |
---|
| 359 | + } |
---|
| 360 | + |
---|
| 361 | + return __evsel__syscall_arg_fmt(evsel); |
---|
| 362 | + |
---|
| 363 | +out_delete: |
---|
| 364 | + evsel_trace__delete(evsel->priv); |
---|
| 365 | + evsel->priv = NULL; |
---|
| 366 | + return NULL; |
---|
| 367 | +} |
---|
| 368 | + |
---|
| 369 | +static int evsel__init_tp_uint_field(struct evsel *evsel, struct tp_field *field, const char *name) |
---|
| 370 | +{ |
---|
| 371 | + struct tep_format_field *format_field = evsel__field(evsel, name); |
---|
218 | 372 | |
---|
219 | 373 | if (format_field == NULL) |
---|
220 | 374 | return -1; |
---|
.. | .. |
---|
223 | 377 | } |
---|
224 | 378 | |
---|
225 | 379 | #define perf_evsel__init_sc_tp_uint_field(evsel, name) \ |
---|
226 | | - ({ struct syscall_tp *sc = evsel->priv;\ |
---|
227 | | - perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); }) |
---|
| 380 | + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ |
---|
| 381 | + evsel__init_tp_uint_field(evsel, &sc->name, #name); }) |
---|
228 | 382 | |
---|
229 | | -static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, |
---|
230 | | - struct tp_field *field, |
---|
231 | | - const char *name) |
---|
| 383 | +static int evsel__init_tp_ptr_field(struct evsel *evsel, struct tp_field *field, const char *name) |
---|
232 | 384 | { |
---|
233 | | - struct format_field *format_field = perf_evsel__field(evsel, name); |
---|
| 385 | + struct tep_format_field *format_field = evsel__field(evsel, name); |
---|
234 | 386 | |
---|
235 | 387 | if (format_field == NULL) |
---|
236 | 388 | return -1; |
---|
.. | .. |
---|
239 | 391 | } |
---|
240 | 392 | |
---|
241 | 393 | #define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ |
---|
242 | | - ({ struct syscall_tp *sc = evsel->priv;\ |
---|
243 | | - perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) |
---|
| 394 | + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ |
---|
| 395 | + evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) |
---|
244 | 396 | |
---|
245 | | -static void perf_evsel__delete_priv(struct perf_evsel *evsel) |
---|
| 397 | +static void evsel__delete_priv(struct evsel *evsel) |
---|
246 | 398 | { |
---|
247 | 399 | zfree(&evsel->priv); |
---|
248 | | - perf_evsel__delete(evsel); |
---|
| 400 | + evsel__delete(evsel); |
---|
249 | 401 | } |
---|
250 | 402 | |
---|
251 | | -static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel) |
---|
| 403 | +static int evsel__init_syscall_tp(struct evsel *evsel) |
---|
252 | 404 | { |
---|
253 | | - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); |
---|
| 405 | + struct syscall_tp *sc = evsel__syscall_tp(evsel); |
---|
254 | 406 | |
---|
255 | | - if (evsel->priv != NULL) { |
---|
256 | | - if (perf_evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr")) |
---|
257 | | - goto out_delete; |
---|
| 407 | + if (sc != NULL) { |
---|
| 408 | + if (evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") && |
---|
| 409 | + evsel__init_tp_uint_field(evsel, &sc->id, "nr")) |
---|
| 410 | + return -ENOENT; |
---|
258 | 411 | return 0; |
---|
259 | 412 | } |
---|
260 | 413 | |
---|
261 | 414 | return -ENOMEM; |
---|
262 | | -out_delete: |
---|
263 | | - zfree(&evsel->priv); |
---|
264 | | - return -ENOENT; |
---|
265 | 415 | } |
---|
266 | 416 | |
---|
267 | | -static int perf_evsel__init_augmented_syscall_tp(struct perf_evsel *evsel) |
---|
| 417 | +static int evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp) |
---|
268 | 418 | { |
---|
269 | | - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); |
---|
| 419 | + struct syscall_tp *sc = evsel__syscall_tp(evsel); |
---|
270 | 420 | |
---|
271 | | - if (evsel->priv != NULL) { /* field, sizeof_field, offsetof_field */ |
---|
272 | | - if (__tp_field__init_uint(&sc->id, sizeof(long), sizeof(long long), evsel->needs_swap)) |
---|
273 | | - goto out_delete; |
---|
| 421 | + if (sc != NULL) { |
---|
| 422 | + struct tep_format_field *syscall_id = evsel__field(tp, "id"); |
---|
| 423 | + if (syscall_id == NULL) |
---|
| 424 | + syscall_id = evsel__field(tp, "__syscall_nr"); |
---|
| 425 | + if (syscall_id == NULL || |
---|
| 426 | + __tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) |
---|
| 427 | + return -EINVAL; |
---|
274 | 428 | |
---|
275 | 429 | return 0; |
---|
276 | 430 | } |
---|
277 | 431 | |
---|
278 | 432 | return -ENOMEM; |
---|
279 | | -out_delete: |
---|
280 | | - zfree(&evsel->priv); |
---|
281 | | - return -EINVAL; |
---|
282 | 433 | } |
---|
283 | 434 | |
---|
284 | | -static int perf_evsel__init_augmented_syscall_tp_args(struct perf_evsel *evsel) |
---|
| 435 | +static int evsel__init_augmented_syscall_tp_args(struct evsel *evsel) |
---|
285 | 436 | { |
---|
286 | | - struct syscall_tp *sc = evsel->priv; |
---|
| 437 | + struct syscall_tp *sc = __evsel__syscall_tp(evsel); |
---|
287 | 438 | |
---|
288 | 439 | return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); |
---|
289 | 440 | } |
---|
290 | 441 | |
---|
291 | | -static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler) |
---|
| 442 | +static int evsel__init_augmented_syscall_tp_ret(struct evsel *evsel) |
---|
292 | 443 | { |
---|
293 | | - evsel->priv = malloc(sizeof(struct syscall_tp)); |
---|
294 | | - if (evsel->priv != NULL) { |
---|
| 444 | + struct syscall_tp *sc = __evsel__syscall_tp(evsel); |
---|
| 445 | + |
---|
| 446 | + return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap); |
---|
| 447 | +} |
---|
| 448 | + |
---|
| 449 | +static int evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler) |
---|
| 450 | +{ |
---|
| 451 | + if (evsel__syscall_tp(evsel) != NULL) { |
---|
295 | 452 | if (perf_evsel__init_sc_tp_uint_field(evsel, id)) |
---|
296 | | - goto out_delete; |
---|
| 453 | + return -ENOENT; |
---|
297 | 454 | |
---|
298 | 455 | evsel->handler = handler; |
---|
299 | 456 | return 0; |
---|
300 | 457 | } |
---|
301 | 458 | |
---|
302 | 459 | return -ENOMEM; |
---|
303 | | - |
---|
304 | | -out_delete: |
---|
305 | | - zfree(&evsel->priv); |
---|
306 | | - return -ENOENT; |
---|
307 | 460 | } |
---|
308 | 461 | |
---|
309 | | -static struct perf_evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler) |
---|
| 462 | +static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler) |
---|
310 | 463 | { |
---|
311 | | - struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); |
---|
| 464 | + struct evsel *evsel = evsel__newtp("raw_syscalls", direction); |
---|
312 | 465 | |
---|
313 | 466 | /* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */ |
---|
314 | 467 | if (IS_ERR(evsel)) |
---|
315 | | - evsel = perf_evsel__newtp("syscalls", direction); |
---|
| 468 | + evsel = evsel__newtp("syscalls", direction); |
---|
316 | 469 | |
---|
317 | 470 | if (IS_ERR(evsel)) |
---|
318 | 471 | return NULL; |
---|
319 | 472 | |
---|
320 | | - if (perf_evsel__init_raw_syscall_tp(evsel, handler)) |
---|
| 473 | + if (evsel__init_raw_syscall_tp(evsel, handler)) |
---|
321 | 474 | goto out_delete; |
---|
322 | 475 | |
---|
323 | 476 | return evsel; |
---|
324 | 477 | |
---|
325 | 478 | out_delete: |
---|
326 | | - perf_evsel__delete_priv(evsel); |
---|
| 479 | + evsel__delete_priv(evsel); |
---|
327 | 480 | return NULL; |
---|
328 | 481 | } |
---|
329 | 482 | |
---|
330 | 483 | #define perf_evsel__sc_tp_uint(evsel, name, sample) \ |
---|
331 | | - ({ struct syscall_tp *fields = evsel->priv; \ |
---|
| 484 | + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ |
---|
332 | 485 | fields->name.integer(&fields->name, sample); }) |
---|
333 | 486 | |
---|
334 | 487 | #define perf_evsel__sc_tp_ptr(evsel, name, sample) \ |
---|
335 | | - ({ struct syscall_tp *fields = evsel->priv; \ |
---|
| 488 | + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ |
---|
336 | 489 | fields->name.pointer(&fields->name, sample); }) |
---|
337 | 490 | |
---|
338 | | -size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, int val) |
---|
| 491 | +size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val) |
---|
339 | 492 | { |
---|
340 | 493 | int idx = val - sa->offset; |
---|
341 | 494 | |
---|
342 | | - if (idx < 0 || idx >= sa->nr_entries || sa->entries[idx] == NULL) |
---|
343 | | - return scnprintf(bf, size, intfmt, val); |
---|
| 495 | + if (idx < 0 || idx >= sa->nr_entries || sa->entries[idx] == NULL) { |
---|
| 496 | + size_t printed = scnprintf(bf, size, intfmt, val); |
---|
| 497 | + if (show_suffix) |
---|
| 498 | + printed += scnprintf(bf + printed, size - printed, " /* %s??? */", sa->prefix); |
---|
| 499 | + return printed; |
---|
| 500 | + } |
---|
344 | 501 | |
---|
345 | | - return scnprintf(bf, size, "%s", sa->entries[idx]); |
---|
| 502 | + return scnprintf(bf, size, "%s%s", sa->entries[idx], show_suffix ? sa->prefix : ""); |
---|
| 503 | +} |
---|
| 504 | + |
---|
| 505 | +size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val) |
---|
| 506 | +{ |
---|
| 507 | + int idx = val - sa->offset; |
---|
| 508 | + |
---|
| 509 | + if (idx < 0 || idx >= sa->nr_entries || sa->entries[idx] == NULL) { |
---|
| 510 | + size_t printed = scnprintf(bf, size, intfmt, val); |
---|
| 511 | + if (show_prefix) |
---|
| 512 | + printed += scnprintf(bf + printed, size - printed, " /* %s??? */", sa->prefix); |
---|
| 513 | + return printed; |
---|
| 514 | + } |
---|
| 515 | + |
---|
| 516 | + return scnprintf(bf, size, "%s%s", show_prefix ? sa->prefix : "", sa->entries[idx]); |
---|
346 | 517 | } |
---|
347 | 518 | |
---|
348 | 519 | static size_t __syscall_arg__scnprintf_strarray(char *bf, size_t size, |
---|
349 | 520 | const char *intfmt, |
---|
350 | 521 | struct syscall_arg *arg) |
---|
351 | 522 | { |
---|
352 | | - return strarray__scnprintf(arg->parm, bf, size, intfmt, arg->val); |
---|
| 523 | + return strarray__scnprintf(arg->parm, bf, size, intfmt, arg->show_string_prefix, arg->val); |
---|
353 | 524 | } |
---|
354 | 525 | |
---|
355 | 526 | static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, |
---|
.. | .. |
---|
360 | 531 | |
---|
361 | 532 | #define SCA_STRARRAY syscall_arg__scnprintf_strarray |
---|
362 | 533 | |
---|
363 | | -struct strarrays { |
---|
364 | | - int nr_entries; |
---|
365 | | - struct strarray **entries; |
---|
366 | | -}; |
---|
| 534 | +bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) |
---|
| 535 | +{ |
---|
| 536 | + return strarray__strtoul(arg->parm, bf, size, ret); |
---|
| 537 | +} |
---|
367 | 538 | |
---|
368 | | -#define DEFINE_STRARRAYS(array) struct strarrays strarrays__##array = { \ |
---|
369 | | - .nr_entries = ARRAY_SIZE(array), \ |
---|
370 | | - .entries = array, \ |
---|
| 539 | +bool syscall_arg__strtoul_strarray_flags(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) |
---|
| 540 | +{ |
---|
| 541 | + return strarray__strtoul_flags(arg->parm, bf, size, ret); |
---|
| 542 | +} |
---|
| 543 | + |
---|
| 544 | +bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) |
---|
| 545 | +{ |
---|
| 546 | + return strarrays__strtoul(arg->parm, bf, size, ret); |
---|
| 547 | +} |
---|
| 548 | + |
---|
| 549 | +size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg) |
---|
| 550 | +{ |
---|
| 551 | + return strarray__scnprintf_flags(arg->parm, bf, size, arg->show_string_prefix, arg->val); |
---|
| 552 | +} |
---|
| 553 | + |
---|
| 554 | +size_t strarrays__scnprintf(struct strarrays *sas, char *bf, size_t size, const char *intfmt, bool show_prefix, int val) |
---|
| 555 | +{ |
---|
| 556 | + size_t printed; |
---|
| 557 | + int i; |
---|
| 558 | + |
---|
| 559 | + for (i = 0; i < sas->nr_entries; ++i) { |
---|
| 560 | + struct strarray *sa = sas->entries[i]; |
---|
| 561 | + int idx = val - sa->offset; |
---|
| 562 | + |
---|
| 563 | + if (idx >= 0 && idx < sa->nr_entries) { |
---|
| 564 | + if (sa->entries[idx] == NULL) |
---|
| 565 | + break; |
---|
| 566 | + return scnprintf(bf, size, "%s%s", show_prefix ? sa->prefix : "", sa->entries[idx]); |
---|
| 567 | + } |
---|
| 568 | + } |
---|
| 569 | + |
---|
| 570 | + printed = scnprintf(bf, size, intfmt, val); |
---|
| 571 | + if (show_prefix) |
---|
| 572 | + printed += scnprintf(bf + printed, size - printed, " /* %s??? */", sas->entries[0]->prefix); |
---|
| 573 | + return printed; |
---|
| 574 | +} |
---|
| 575 | + |
---|
| 576 | +bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret) |
---|
| 577 | +{ |
---|
| 578 | + int i; |
---|
| 579 | + |
---|
| 580 | + for (i = 0; i < sa->nr_entries; ++i) { |
---|
| 581 | + if (sa->entries[i] && strncmp(sa->entries[i], bf, size) == 0 && sa->entries[i][size] == '\0') { |
---|
| 582 | + *ret = sa->offset + i; |
---|
| 583 | + return true; |
---|
| 584 | + } |
---|
| 585 | + } |
---|
| 586 | + |
---|
| 587 | + return false; |
---|
| 588 | +} |
---|
| 589 | + |
---|
| 590 | +bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret) |
---|
| 591 | +{ |
---|
| 592 | + u64 val = 0; |
---|
| 593 | + char *tok = bf, *sep, *end; |
---|
| 594 | + |
---|
| 595 | + *ret = 0; |
---|
| 596 | + |
---|
| 597 | + while (size != 0) { |
---|
| 598 | + int toklen = size; |
---|
| 599 | + |
---|
| 600 | + sep = memchr(tok, '|', size); |
---|
| 601 | + if (sep != NULL) { |
---|
| 602 | + size -= sep - tok + 1; |
---|
| 603 | + |
---|
| 604 | + end = sep - 1; |
---|
| 605 | + while (end > tok && isspace(*end)) |
---|
| 606 | + --end; |
---|
| 607 | + |
---|
| 608 | + toklen = end - tok + 1; |
---|
| 609 | + } |
---|
| 610 | + |
---|
| 611 | + while (isspace(*tok)) |
---|
| 612 | + ++tok; |
---|
| 613 | + |
---|
| 614 | + if (isalpha(*tok) || *tok == '_') { |
---|
| 615 | + if (!strarray__strtoul(sa, tok, toklen, &val)) |
---|
| 616 | + return false; |
---|
| 617 | + } else { |
---|
| 618 | + bool is_hexa = tok[0] == 0 && (tok[1] = 'x' || tok[1] == 'X'); |
---|
| 619 | + |
---|
| 620 | + val = strtoul(tok, NULL, is_hexa ? 16 : 0); |
---|
| 621 | + } |
---|
| 622 | + |
---|
| 623 | + *ret |= (1 << (val - 1)); |
---|
| 624 | + |
---|
| 625 | + if (sep == NULL) |
---|
| 626 | + break; |
---|
| 627 | + tok = sep + 1; |
---|
| 628 | + } |
---|
| 629 | + |
---|
| 630 | + return true; |
---|
| 631 | +} |
---|
| 632 | + |
---|
| 633 | +bool strarrays__strtoul(struct strarrays *sas, char *bf, size_t size, u64 *ret) |
---|
| 634 | +{ |
---|
| 635 | + int i; |
---|
| 636 | + |
---|
| 637 | + for (i = 0; i < sas->nr_entries; ++i) { |
---|
| 638 | + struct strarray *sa = sas->entries[i]; |
---|
| 639 | + |
---|
| 640 | + if (strarray__strtoul(sa, bf, size, ret)) |
---|
| 641 | + return true; |
---|
| 642 | + } |
---|
| 643 | + |
---|
| 644 | + return false; |
---|
371 | 645 | } |
---|
372 | 646 | |
---|
373 | 647 | size_t syscall_arg__scnprintf_strarrays(char *bf, size_t size, |
---|
374 | 648 | struct syscall_arg *arg) |
---|
375 | 649 | { |
---|
376 | | - struct strarrays *sas = arg->parm; |
---|
377 | | - int i; |
---|
378 | | - |
---|
379 | | - for (i = 0; i < sas->nr_entries; ++i) { |
---|
380 | | - struct strarray *sa = sas->entries[i]; |
---|
381 | | - int idx = arg->val - sa->offset; |
---|
382 | | - |
---|
383 | | - if (idx >= 0 && idx < sa->nr_entries) { |
---|
384 | | - if (sa->entries[idx] == NULL) |
---|
385 | | - break; |
---|
386 | | - return scnprintf(bf, size, "%s", sa->entries[idx]); |
---|
387 | | - } |
---|
388 | | - } |
---|
389 | | - |
---|
390 | | - return scnprintf(bf, size, "%d", arg->val); |
---|
| 650 | + return strarrays__scnprintf(arg->parm, bf, size, "%d", arg->show_string_prefix, arg->val); |
---|
391 | 651 | } |
---|
392 | 652 | |
---|
393 | 653 | #ifndef AT_FDCWD |
---|
.. | .. |
---|
398 | 658 | struct syscall_arg *arg) |
---|
399 | 659 | { |
---|
400 | 660 | int fd = arg->val; |
---|
| 661 | + const char *prefix = "AT_FD"; |
---|
401 | 662 | |
---|
402 | 663 | if (fd == AT_FDCWD) |
---|
403 | | - return scnprintf(bf, size, "CWD"); |
---|
| 664 | + return scnprintf(bf, size, "%s%s", arg->show_string_prefix ? prefix : "", "CWD"); |
---|
404 | 665 | |
---|
405 | 666 | return syscall_arg__scnprintf_fd(bf, size, arg); |
---|
406 | 667 | } |
---|
.. | .. |
---|
417 | 678 | return scnprintf(bf, size, "%#lx", arg->val); |
---|
418 | 679 | } |
---|
419 | 680 | |
---|
| 681 | +size_t syscall_arg__scnprintf_ptr(char *bf, size_t size, struct syscall_arg *arg) |
---|
| 682 | +{ |
---|
| 683 | + if (arg->val == 0) |
---|
| 684 | + return scnprintf(bf, size, "NULL"); |
---|
| 685 | + return syscall_arg__scnprintf_hex(bf, size, arg); |
---|
| 686 | +} |
---|
| 687 | + |
---|
420 | 688 | size_t syscall_arg__scnprintf_int(char *bf, size_t size, struct syscall_arg *arg) |
---|
421 | 689 | { |
---|
422 | 690 | return scnprintf(bf, size, "%d", arg->val); |
---|
.. | .. |
---|
427 | 695 | return scnprintf(bf, size, "%ld", arg->val); |
---|
428 | 696 | } |
---|
429 | 697 | |
---|
| 698 | +static size_t syscall_arg__scnprintf_char_array(char *bf, size_t size, struct syscall_arg *arg) |
---|
| 699 | +{ |
---|
| 700 | + // XXX Hey, maybe for sched:sched_switch prev/next comm fields we can |
---|
| 701 | + // fill missing comms using thread__set_comm()... |
---|
| 702 | + // here or in a special syscall_arg__scnprintf_pid_sched_tp... |
---|
| 703 | + return scnprintf(bf, size, "\"%-.*s\"", arg->fmt->nr_entries ?: arg->len, arg->val); |
---|
| 704 | +} |
---|
| 705 | + |
---|
| 706 | +#define SCA_CHAR_ARRAY syscall_arg__scnprintf_char_array |
---|
| 707 | + |
---|
430 | 708 | static const char *bpf_cmd[] = { |
---|
431 | 709 | "MAP_CREATE", "MAP_LOOKUP_ELEM", "MAP_UPDATE_ELEM", "MAP_DELETE_ELEM", |
---|
432 | 710 | "MAP_GET_NEXT_KEY", "PROG_LOAD", |
---|
433 | 711 | }; |
---|
434 | | -static DEFINE_STRARRAY(bpf_cmd); |
---|
| 712 | +static DEFINE_STRARRAY(bpf_cmd, "BPF_"); |
---|
| 713 | + |
---|
| 714 | +static const char *fsmount_flags[] = { |
---|
| 715 | + [1] = "CLOEXEC", |
---|
| 716 | +}; |
---|
| 717 | +static DEFINE_STRARRAY(fsmount_flags, "FSMOUNT_"); |
---|
| 718 | + |
---|
| 719 | +#include "trace/beauty/generated/fsconfig_arrays.c" |
---|
| 720 | + |
---|
| 721 | +static DEFINE_STRARRAY(fsconfig_cmds, "FSCONFIG_"); |
---|
435 | 722 | |
---|
436 | 723 | static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", }; |
---|
437 | | -static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, 1); |
---|
| 724 | +static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, "EPOLL_CTL_", 1); |
---|
438 | 725 | |
---|
439 | 726 | static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; |
---|
440 | | -static DEFINE_STRARRAY(itimers); |
---|
| 727 | +static DEFINE_STRARRAY(itimers, "ITIMER_"); |
---|
441 | 728 | |
---|
442 | 729 | static const char *keyctl_options[] = { |
---|
443 | 730 | "GET_KEYRING_ID", "JOIN_SESSION_KEYRING", "UPDATE", "REVOKE", "CHOWN", |
---|
.. | .. |
---|
446 | 733 | "ASSUME_AUTHORITY", "GET_SECURITY", "SESSION_TO_PARENT", "REJECT", |
---|
447 | 734 | "INSTANTIATE_IOV", "INVALIDATE", "GET_PERSISTENT", |
---|
448 | 735 | }; |
---|
449 | | -static DEFINE_STRARRAY(keyctl_options); |
---|
| 736 | +static DEFINE_STRARRAY(keyctl_options, "KEYCTL_"); |
---|
450 | 737 | |
---|
451 | 738 | static const char *whences[] = { "SET", "CUR", "END", |
---|
452 | 739 | #ifdef SEEK_DATA |
---|
.. | .. |
---|
456 | 743 | "HOLE", |
---|
457 | 744 | #endif |
---|
458 | 745 | }; |
---|
459 | | -static DEFINE_STRARRAY(whences); |
---|
| 746 | +static DEFINE_STRARRAY(whences, "SEEK_"); |
---|
460 | 747 | |
---|
461 | 748 | static const char *fcntl_cmds[] = { |
---|
462 | 749 | "DUPFD", "GETFD", "SETFD", "GETFL", "SETFL", "GETLK", "SETLK", |
---|
.. | .. |
---|
464 | 751 | "SETLK64", "SETLKW64", "SETOWN_EX", "GETOWN_EX", |
---|
465 | 752 | "GETOWNER_UIDS", |
---|
466 | 753 | }; |
---|
467 | | -static DEFINE_STRARRAY(fcntl_cmds); |
---|
| 754 | +static DEFINE_STRARRAY(fcntl_cmds, "F_"); |
---|
468 | 755 | |
---|
469 | 756 | static const char *fcntl_linux_specific_cmds[] = { |
---|
470 | 757 | "SETLEASE", "GETLEASE", "NOTIFY", [5] = "CANCELLK", "DUPFD_CLOEXEC", |
---|
.. | .. |
---|
472 | 759 | "GET_RW_HINT", "SET_RW_HINT", "GET_FILE_RW_HINT", "SET_FILE_RW_HINT", |
---|
473 | 760 | }; |
---|
474 | 761 | |
---|
475 | | -static DEFINE_STRARRAY_OFFSET(fcntl_linux_specific_cmds, F_LINUX_SPECIFIC_BASE); |
---|
| 762 | +static DEFINE_STRARRAY_OFFSET(fcntl_linux_specific_cmds, "F_", F_LINUX_SPECIFIC_BASE); |
---|
476 | 763 | |
---|
477 | 764 | static struct strarray *fcntl_cmds_arrays[] = { |
---|
478 | 765 | &strarray__fcntl_cmds, |
---|
.. | .. |
---|
486 | 773 | "MEMLOCK", "AS", "LOCKS", "SIGPENDING", "MSGQUEUE", "NICE", "RTPRIO", |
---|
487 | 774 | "RTTIME", |
---|
488 | 775 | }; |
---|
489 | | -static DEFINE_STRARRAY(rlimit_resources); |
---|
| 776 | +static DEFINE_STRARRAY(rlimit_resources, "RLIMIT_"); |
---|
490 | 777 | |
---|
491 | 778 | static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; |
---|
492 | | -static DEFINE_STRARRAY(sighow); |
---|
| 779 | +static DEFINE_STRARRAY(sighow, "SIG_"); |
---|
493 | 780 | |
---|
494 | 781 | static const char *clockid[] = { |
---|
495 | 782 | "REALTIME", "MONOTONIC", "PROCESS_CPUTIME_ID", "THREAD_CPUTIME_ID", |
---|
496 | 783 | "MONOTONIC_RAW", "REALTIME_COARSE", "MONOTONIC_COARSE", "BOOTTIME", |
---|
497 | 784 | "REALTIME_ALARM", "BOOTTIME_ALARM", "SGI_CYCLE", "TAI" |
---|
498 | 785 | }; |
---|
499 | | -static DEFINE_STRARRAY(clockid); |
---|
500 | | - |
---|
501 | | -static const char *socket_families[] = { |
---|
502 | | - "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", |
---|
503 | | - "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", |
---|
504 | | - "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", |
---|
505 | | - "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", |
---|
506 | | - "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", |
---|
507 | | - "ALG", "NFC", "VSOCK", |
---|
508 | | -}; |
---|
509 | | -static DEFINE_STRARRAY(socket_families); |
---|
| 786 | +static DEFINE_STRARRAY(clockid, "CLOCK_"); |
---|
510 | 787 | |
---|
511 | 788 | static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, |
---|
512 | 789 | struct syscall_arg *arg) |
---|
513 | 790 | { |
---|
| 791 | + bool show_prefix = arg->show_string_prefix; |
---|
| 792 | + const char *suffix = "_OK"; |
---|
514 | 793 | size_t printed = 0; |
---|
515 | 794 | int mode = arg->val; |
---|
516 | 795 | |
---|
517 | 796 | if (mode == F_OK) /* 0 */ |
---|
518 | | - return scnprintf(bf, size, "F"); |
---|
| 797 | + return scnprintf(bf, size, "F%s", show_prefix ? suffix : ""); |
---|
519 | 798 | #define P_MODE(n) \ |
---|
520 | 799 | if (mode & n##_OK) { \ |
---|
521 | | - printed += scnprintf(bf + printed, size - printed, "%s", #n); \ |
---|
| 800 | + printed += scnprintf(bf + printed, size - printed, "%s%s", #n, show_prefix ? suffix : ""); \ |
---|
522 | 801 | mode &= ~n##_OK; \ |
---|
523 | 802 | } |
---|
524 | 803 | |
---|
.. | .. |
---|
543 | 822 | static size_t syscall_arg__scnprintf_pipe_flags(char *bf, size_t size, |
---|
544 | 823 | struct syscall_arg *arg) |
---|
545 | 824 | { |
---|
| 825 | + bool show_prefix = arg->show_string_prefix; |
---|
| 826 | + const char *prefix = "O_"; |
---|
546 | 827 | int printed = 0, flags = arg->val; |
---|
547 | 828 | |
---|
548 | 829 | #define P_FLAG(n) \ |
---|
549 | 830 | if (flags & O_##n) { \ |
---|
550 | | - printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ |
---|
| 831 | + printed += scnprintf(bf + printed, size - printed, "%s%s%s", printed ? "|" : "", show_prefix ? prefix : "", #n); \ |
---|
551 | 832 | flags &= ~O_##n; \ |
---|
552 | 833 | } |
---|
553 | 834 | |
---|
.. | .. |
---|
573 | 854 | static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, |
---|
574 | 855 | struct syscall_arg *arg) |
---|
575 | 856 | { |
---|
| 857 | + bool show_prefix = arg->show_string_prefix; |
---|
| 858 | + const char *prefix = "GRND_"; |
---|
576 | 859 | int printed = 0, flags = arg->val; |
---|
577 | 860 | |
---|
578 | 861 | #define P_FLAG(n) \ |
---|
579 | 862 | if (flags & GRND_##n) { \ |
---|
580 | | - printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ |
---|
| 863 | + printed += scnprintf(bf + printed, size - printed, "%s%s%s", printed ? "|" : "", show_prefix ? prefix : "", #n); \ |
---|
581 | 864 | flags &= ~GRND_##n; \ |
---|
582 | 865 | } |
---|
583 | 866 | |
---|
.. | .. |
---|
595 | 878 | |
---|
596 | 879 | #define STRARRAY(name, array) \ |
---|
597 | 880 | { .scnprintf = SCA_STRARRAY, \ |
---|
| 881 | + .strtoul = STUL_STRARRAY, \ |
---|
| 882 | + .parm = &strarray__##array, } |
---|
| 883 | + |
---|
| 884 | +#define STRARRAY_FLAGS(name, array) \ |
---|
| 885 | + { .scnprintf = SCA_STRARRAY_FLAGS, \ |
---|
| 886 | + .strtoul = STUL_STRARRAY_FLAGS, \ |
---|
598 | 887 | .parm = &strarray__##array, } |
---|
599 | 888 | |
---|
600 | 889 | #include "trace/beauty/arch_errno_names.c" |
---|
.. | .. |
---|
613 | 902 | #include "trace/beauty/socket_type.c" |
---|
614 | 903 | #include "trace/beauty/waitid_options.c" |
---|
615 | 904 | |
---|
616 | | -struct syscall_arg_fmt { |
---|
617 | | - size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); |
---|
618 | | - void *parm; |
---|
619 | | - const char *name; |
---|
620 | | - bool show_zero; |
---|
621 | | -}; |
---|
622 | | - |
---|
623 | | -static struct syscall_fmt { |
---|
624 | | - const char *name; |
---|
625 | | - const char *alias; |
---|
626 | | - struct syscall_arg_fmt arg[6]; |
---|
627 | | - u8 nr_args; |
---|
628 | | - bool errpid; |
---|
629 | | - bool timeout; |
---|
630 | | - bool hexret; |
---|
631 | | -} syscall_fmts[] = { |
---|
| 905 | +static struct syscall_fmt syscall_fmts[] = { |
---|
632 | 906 | { .name = "access", |
---|
633 | 907 | .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, |
---|
| 908 | + { .name = "arch_prctl", |
---|
| 909 | + .arg = { [0] = { .scnprintf = SCA_X86_ARCH_PRCTL_CODE, /* code */ }, |
---|
| 910 | + [1] = { .scnprintf = SCA_PTR, /* arg2 */ }, }, }, |
---|
| 911 | + { .name = "bind", |
---|
| 912 | + .arg = { [0] = { .scnprintf = SCA_INT, /* fd */ }, |
---|
| 913 | + [1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ }, |
---|
| 914 | + [2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, }, |
---|
634 | 915 | { .name = "bpf", |
---|
635 | 916 | .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, |
---|
636 | 917 | { .name = "brk", .hexret = true, |
---|
637 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* brk */ }, }, }, |
---|
| 918 | + .arg = { [0] = { .scnprintf = SCA_PTR, /* brk */ }, }, }, |
---|
638 | 919 | { .name = "clock_gettime", |
---|
639 | 920 | .arg = { [0] = STRARRAY(clk_id, clockid), }, }, |
---|
640 | 921 | { .name = "clone", .errpid = true, .nr_args = 5, |
---|
.. | .. |
---|
645 | 926 | [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, |
---|
646 | 927 | { .name = "close", |
---|
647 | 928 | .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, |
---|
| 929 | + { .name = "connect", |
---|
| 930 | + .arg = { [0] = { .scnprintf = SCA_INT, /* fd */ }, |
---|
| 931 | + [1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ }, |
---|
| 932 | + [2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, }, |
---|
648 | 933 | { .name = "epoll_ctl", |
---|
649 | 934 | .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, |
---|
650 | 935 | { .name = "eventfd2", |
---|
.. | .. |
---|
654 | 939 | { .name = "fchownat", |
---|
655 | 940 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, |
---|
656 | 941 | { .name = "fcntl", |
---|
657 | | - .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ |
---|
| 942 | + .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ |
---|
| 943 | + .strtoul = STUL_STRARRAYS, |
---|
658 | 944 | .parm = &strarrays__fcntl_cmds_arrays, |
---|
659 | 945 | .show_zero = true, }, |
---|
660 | 946 | [2] = { .scnprintf = SCA_FCNTL_ARG, /* arg */ }, }, }, |
---|
661 | 947 | { .name = "flock", |
---|
662 | 948 | .arg = { [1] = { .scnprintf = SCA_FLOCK, /* cmd */ }, }, }, |
---|
| 949 | + { .name = "fsconfig", |
---|
| 950 | + .arg = { [1] = STRARRAY(cmd, fsconfig_cmds), }, }, |
---|
| 951 | + { .name = "fsmount", |
---|
| 952 | + .arg = { [1] = STRARRAY_FLAGS(flags, fsmount_flags), |
---|
| 953 | + [2] = { .scnprintf = SCA_FSMOUNT_ATTR_FLAGS, /* attr_flags */ }, }, }, |
---|
| 954 | + { .name = "fspick", |
---|
| 955 | + .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, |
---|
| 956 | + [1] = { .scnprintf = SCA_FILENAME, /* path */ }, |
---|
| 957 | + [2] = { .scnprintf = SCA_FSPICK_FLAGS, /* flags */ }, }, }, |
---|
663 | 958 | { .name = "fstat", .alias = "newfstat", }, |
---|
664 | 959 | { .name = "fstatat", .alias = "newfstatat", }, |
---|
665 | 960 | { .name = "futex", |
---|
.. | .. |
---|
710 | 1005 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, |
---|
711 | 1006 | { .name = "mknodat", |
---|
712 | 1007 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, |
---|
713 | | - { .name = "mlock", |
---|
714 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, }, }, |
---|
715 | | - { .name = "mlockall", |
---|
716 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, }, }, |
---|
717 | 1008 | { .name = "mmap", .hexret = true, |
---|
718 | 1009 | /* The standard mmap maps to old_mmap on s390x */ |
---|
719 | 1010 | #if defined(__s390x__) |
---|
720 | 1011 | .alias = "old_mmap", |
---|
721 | 1012 | #endif |
---|
722 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, |
---|
723 | | - [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, |
---|
724 | | - [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, }, }, |
---|
| 1013 | + .arg = { [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, |
---|
| 1014 | + [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ |
---|
| 1015 | + .strtoul = STUL_STRARRAY_FLAGS, |
---|
| 1016 | + .parm = &strarray__mmap_flags, }, |
---|
| 1017 | + [5] = { .scnprintf = SCA_HEX, /* offset */ }, }, }, |
---|
| 1018 | + { .name = "mount", |
---|
| 1019 | + .arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ }, |
---|
| 1020 | + [3] = { .scnprintf = SCA_MOUNT_FLAGS, /* flags */ |
---|
| 1021 | + .mask_val = SCAMV_MOUNT_FLAGS, /* flags */ }, }, }, |
---|
| 1022 | + { .name = "move_mount", |
---|
| 1023 | + .arg = { [0] = { .scnprintf = SCA_FDAT, /* from_dfd */ }, |
---|
| 1024 | + [1] = { .scnprintf = SCA_FILENAME, /* from_pathname */ }, |
---|
| 1025 | + [2] = { .scnprintf = SCA_FDAT, /* to_dfd */ }, |
---|
| 1026 | + [3] = { .scnprintf = SCA_FILENAME, /* to_pathname */ }, |
---|
| 1027 | + [4] = { .scnprintf = SCA_MOVE_MOUNT_FLAGS, /* flags */ }, }, }, |
---|
725 | 1028 | { .name = "mprotect", |
---|
726 | 1029 | .arg = { [0] = { .scnprintf = SCA_HEX, /* start */ }, |
---|
727 | 1030 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, }, |
---|
728 | 1031 | { .name = "mq_unlink", |
---|
729 | 1032 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* u_name */ }, }, }, |
---|
730 | 1033 | { .name = "mremap", .hexret = true, |
---|
731 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, |
---|
732 | | - [3] = { .scnprintf = SCA_MREMAP_FLAGS, /* flags */ }, |
---|
733 | | - [4] = { .scnprintf = SCA_HEX, /* new_addr */ }, }, }, |
---|
734 | | - { .name = "munlock", |
---|
735 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, }, }, |
---|
736 | | - { .name = "munmap", |
---|
737 | | - .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, }, }, |
---|
| 1034 | + .arg = { [3] = { .scnprintf = SCA_MREMAP_FLAGS, /* flags */ }, }, }, |
---|
738 | 1035 | { .name = "name_to_handle_at", |
---|
739 | 1036 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
---|
740 | 1037 | { .name = "newfstatat", |
---|
.. | .. |
---|
763 | 1060 | [3] = { .scnprintf = SCA_INT, /* pkey */ }, }, }, |
---|
764 | 1061 | { .name = "poll", .timeout = true, }, |
---|
765 | 1062 | { .name = "ppoll", .timeout = true, }, |
---|
766 | | - { .name = "prctl", .alias = "arch_prctl", |
---|
767 | | - .arg = { [0] = { .scnprintf = SCA_PRCTL_OPTION, /* option */ }, |
---|
| 1063 | + { .name = "prctl", |
---|
| 1064 | + .arg = { [0] = { .scnprintf = SCA_PRCTL_OPTION, /* option */ |
---|
| 1065 | + .strtoul = STUL_STRARRAY, |
---|
| 1066 | + .parm = &strarray__prctl_options, }, |
---|
768 | 1067 | [1] = { .scnprintf = SCA_PRCTL_ARG2, /* arg2 */ }, |
---|
769 | 1068 | [2] = { .scnprintf = SCA_PRCTL_ARG3, /* arg3 */ }, }, }, |
---|
770 | 1069 | { .name = "pread", .alias = "pread64", }, |
---|
.. | .. |
---|
781 | 1080 | { .name = "recvmsg", |
---|
782 | 1081 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
---|
783 | 1082 | { .name = "renameat", |
---|
784 | | - .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
---|
| 1083 | + .arg = { [0] = { .scnprintf = SCA_FDAT, /* olddirfd */ }, |
---|
| 1084 | + [2] = { .scnprintf = SCA_FDAT, /* newdirfd */ }, }, }, |
---|
| 1085 | + { .name = "renameat2", |
---|
| 1086 | + .arg = { [0] = { .scnprintf = SCA_FDAT, /* olddirfd */ }, |
---|
| 1087 | + [2] = { .scnprintf = SCA_FDAT, /* newdirfd */ }, |
---|
| 1088 | + [4] = { .scnprintf = SCA_RENAMEAT2_FLAGS, /* flags */ }, }, }, |
---|
785 | 1089 | { .name = "rt_sigaction", |
---|
786 | 1090 | .arg = { [0] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
---|
787 | 1091 | { .name = "rt_sigprocmask", |
---|
.. | .. |
---|
796 | 1100 | .arg = { [0] = { .scnprintf = SCA_SECCOMP_OP, /* op */ }, |
---|
797 | 1101 | [1] = { .scnprintf = SCA_SECCOMP_FLAGS, /* flags */ }, }, }, |
---|
798 | 1102 | { .name = "select", .timeout = true, }, |
---|
| 1103 | + { .name = "sendfile", .alias = "sendfile64", }, |
---|
799 | 1104 | { .name = "sendmmsg", |
---|
800 | 1105 | .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
---|
801 | 1106 | { .name = "sendmsg", |
---|
802 | 1107 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
---|
803 | 1108 | { .name = "sendto", |
---|
804 | | - .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
---|
| 1109 | + .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, |
---|
| 1110 | + [4] = { .scnprintf = SCA_SOCKADDR, /* addr */ }, }, }, |
---|
805 | 1111 | { .name = "set_tid_address", .errpid = true, }, |
---|
806 | 1112 | { .name = "setitimer", |
---|
807 | 1113 | .arg = { [0] = STRARRAY(which, itimers), }, }, |
---|
.. | .. |
---|
826 | 1132 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* specialfile */ }, }, }, |
---|
827 | 1133 | { .name = "symlinkat", |
---|
828 | 1134 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
---|
| 1135 | + { .name = "sync_file_range", |
---|
| 1136 | + .arg = { [3] = { .scnprintf = SCA_SYNC_FILE_RANGE_FLAGS, /* flags */ }, }, }, |
---|
829 | 1137 | { .name = "tgkill", |
---|
830 | 1138 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
---|
831 | 1139 | { .name = "tkill", |
---|
832 | 1140 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
---|
| 1141 | + { .name = "umount2", .alias = "umount", |
---|
| 1142 | + .arg = { [0] = { .scnprintf = SCA_FILENAME, /* name */ }, }, }, |
---|
833 | 1143 | { .name = "uname", .alias = "newuname", }, |
---|
834 | 1144 | { .name = "unlinkat", |
---|
835 | 1145 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
---|
.. | .. |
---|
847 | 1157 | return strcmp(name, fmt->name); |
---|
848 | 1158 | } |
---|
849 | 1159 | |
---|
| 1160 | +static struct syscall_fmt *__syscall_fmt__find(struct syscall_fmt *fmts, const int nmemb, const char *name) |
---|
| 1161 | +{ |
---|
| 1162 | + return bsearch(name, fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); |
---|
| 1163 | +} |
---|
| 1164 | + |
---|
850 | 1165 | static struct syscall_fmt *syscall_fmt__find(const char *name) |
---|
851 | 1166 | { |
---|
852 | 1167 | const int nmemb = ARRAY_SIZE(syscall_fmts); |
---|
853 | | - return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); |
---|
| 1168 | + return __syscall_fmt__find(syscall_fmts, nmemb, name); |
---|
| 1169 | +} |
---|
| 1170 | + |
---|
| 1171 | +static struct syscall_fmt *__syscall_fmt__find_by_alias(struct syscall_fmt *fmts, const int nmemb, const char *alias) |
---|
| 1172 | +{ |
---|
| 1173 | + int i; |
---|
| 1174 | + |
---|
| 1175 | + for (i = 0; i < nmemb; ++i) { |
---|
| 1176 | + if (fmts[i].alias && strcmp(fmts[i].alias, alias) == 0) |
---|
| 1177 | + return &fmts[i]; |
---|
| 1178 | + } |
---|
| 1179 | + |
---|
| 1180 | + return NULL; |
---|
| 1181 | +} |
---|
| 1182 | + |
---|
| 1183 | +static struct syscall_fmt *syscall_fmt__find_by_alias(const char *alias) |
---|
| 1184 | +{ |
---|
| 1185 | + const int nmemb = ARRAY_SIZE(syscall_fmts); |
---|
| 1186 | + return __syscall_fmt__find_by_alias(syscall_fmts, nmemb, alias); |
---|
854 | 1187 | } |
---|
855 | 1188 | |
---|
856 | 1189 | /* |
---|
857 | 1190 | * is_exit: is this "exit" or "exit_group"? |
---|
858 | 1191 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. |
---|
| 1192 | + * args_size: sum of the sizes of the syscall arguments, anything after that is augmented stuff: pathname for openat, etc. |
---|
| 1193 | + * nonexistent: Just a hole in the syscall table, syscall id not allocated |
---|
859 | 1194 | */ |
---|
860 | 1195 | struct syscall { |
---|
861 | | - struct event_format *tp_format; |
---|
| 1196 | + struct tep_event *tp_format; |
---|
862 | 1197 | int nr_args; |
---|
| 1198 | + int args_size; |
---|
| 1199 | + struct { |
---|
| 1200 | + struct bpf_program *sys_enter, |
---|
| 1201 | + *sys_exit; |
---|
| 1202 | + } bpf_prog; |
---|
863 | 1203 | bool is_exit; |
---|
864 | 1204 | bool is_open; |
---|
865 | | - struct format_field *args; |
---|
| 1205 | + bool nonexistent; |
---|
| 1206 | + struct tep_format_field *args; |
---|
866 | 1207 | const char *name; |
---|
867 | 1208 | struct syscall_fmt *fmt; |
---|
868 | 1209 | struct syscall_arg_fmt *arg_fmt; |
---|
| 1210 | +}; |
---|
| 1211 | + |
---|
| 1212 | +/* |
---|
| 1213 | + * Must match what is in the BPF program: |
---|
| 1214 | + * |
---|
| 1215 | + * tools/perf/examples/bpf/augmented_raw_syscalls.c |
---|
| 1216 | + */ |
---|
| 1217 | +struct bpf_map_syscall_entry { |
---|
| 1218 | + bool enabled; |
---|
| 1219 | + u16 string_args_len[6]; |
---|
869 | 1220 | }; |
---|
870 | 1221 | |
---|
871 | 1222 | /* |
---|
.. | .. |
---|
914 | 1265 | char *name; |
---|
915 | 1266 | } filename; |
---|
916 | 1267 | struct { |
---|
917 | | - int max; |
---|
918 | | - char **table; |
---|
919 | | - } paths; |
---|
| 1268 | + int max; |
---|
| 1269 | + struct file *table; |
---|
| 1270 | + } files; |
---|
920 | 1271 | |
---|
921 | 1272 | struct intlist *syscall_stats; |
---|
922 | 1273 | }; |
---|
.. | .. |
---|
925 | 1276 | { |
---|
926 | 1277 | struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace)); |
---|
927 | 1278 | |
---|
928 | | - if (ttrace) |
---|
929 | | - ttrace->paths.max = -1; |
---|
930 | | - |
---|
931 | | - ttrace->syscall_stats = intlist__new(NULL); |
---|
| 1279 | + if (ttrace) { |
---|
| 1280 | + ttrace->files.max = -1; |
---|
| 1281 | + ttrace->syscall_stats = intlist__new(NULL); |
---|
| 1282 | + } |
---|
932 | 1283 | |
---|
933 | 1284 | return ttrace; |
---|
934 | 1285 | } |
---|
.. | .. |
---|
970 | 1321 | |
---|
971 | 1322 | static const size_t trace__entry_str_size = 2048; |
---|
972 | 1323 | |
---|
| 1324 | +static struct file *thread_trace__files_entry(struct thread_trace *ttrace, int fd) |
---|
| 1325 | +{ |
---|
| 1326 | + if (fd < 0) |
---|
| 1327 | + return NULL; |
---|
| 1328 | + |
---|
| 1329 | + if (fd > ttrace->files.max) { |
---|
| 1330 | + struct file *nfiles = realloc(ttrace->files.table, (fd + 1) * sizeof(struct file)); |
---|
| 1331 | + |
---|
| 1332 | + if (nfiles == NULL) |
---|
| 1333 | + return NULL; |
---|
| 1334 | + |
---|
| 1335 | + if (ttrace->files.max != -1) { |
---|
| 1336 | + memset(nfiles + ttrace->files.max + 1, 0, |
---|
| 1337 | + (fd - ttrace->files.max) * sizeof(struct file)); |
---|
| 1338 | + } else { |
---|
| 1339 | + memset(nfiles, 0, (fd + 1) * sizeof(struct file)); |
---|
| 1340 | + } |
---|
| 1341 | + |
---|
| 1342 | + ttrace->files.table = nfiles; |
---|
| 1343 | + ttrace->files.max = fd; |
---|
| 1344 | + } |
---|
| 1345 | + |
---|
| 1346 | + return ttrace->files.table + fd; |
---|
| 1347 | +} |
---|
| 1348 | + |
---|
| 1349 | +struct file *thread__files_entry(struct thread *thread, int fd) |
---|
| 1350 | +{ |
---|
| 1351 | + return thread_trace__files_entry(thread__priv(thread), fd); |
---|
| 1352 | +} |
---|
| 1353 | + |
---|
973 | 1354 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) |
---|
974 | 1355 | { |
---|
975 | 1356 | struct thread_trace *ttrace = thread__priv(thread); |
---|
| 1357 | + struct file *file = thread_trace__files_entry(ttrace, fd); |
---|
976 | 1358 | |
---|
977 | | - if (fd > ttrace->paths.max) { |
---|
978 | | - char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); |
---|
979 | | - |
---|
980 | | - if (npath == NULL) |
---|
981 | | - return -1; |
---|
982 | | - |
---|
983 | | - if (ttrace->paths.max != -1) { |
---|
984 | | - memset(npath + ttrace->paths.max + 1, 0, |
---|
985 | | - (fd - ttrace->paths.max) * sizeof(char *)); |
---|
986 | | - } else { |
---|
987 | | - memset(npath, 0, (fd + 1) * sizeof(char *)); |
---|
988 | | - } |
---|
989 | | - |
---|
990 | | - ttrace->paths.table = npath; |
---|
991 | | - ttrace->paths.max = fd; |
---|
| 1359 | + if (file != NULL) { |
---|
| 1360 | + struct stat st; |
---|
| 1361 | + if (stat(pathname, &st) == 0) |
---|
| 1362 | + file->dev_maj = major(st.st_rdev); |
---|
| 1363 | + file->pathname = strdup(pathname); |
---|
| 1364 | + if (file->pathname) |
---|
| 1365 | + return 0; |
---|
992 | 1366 | } |
---|
993 | 1367 | |
---|
994 | | - ttrace->paths.table[fd] = strdup(pathname); |
---|
995 | | - |
---|
996 | | - return ttrace->paths.table[fd] != NULL ? 0 : -1; |
---|
| 1368 | + return -1; |
---|
997 | 1369 | } |
---|
998 | 1370 | |
---|
999 | 1371 | static int thread__read_fd_path(struct thread *thread, int fd) |
---|
.. | .. |
---|
1027 | 1399 | { |
---|
1028 | 1400 | struct thread_trace *ttrace = thread__priv(thread); |
---|
1029 | 1401 | |
---|
1030 | | - if (ttrace == NULL) |
---|
| 1402 | + if (ttrace == NULL || trace->fd_path_disabled) |
---|
1031 | 1403 | return NULL; |
---|
1032 | 1404 | |
---|
1033 | 1405 | if (fd < 0) |
---|
1034 | 1406 | return NULL; |
---|
1035 | 1407 | |
---|
1036 | | - if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) { |
---|
| 1408 | + if ((fd > ttrace->files.max || ttrace->files.table[fd].pathname == NULL)) { |
---|
1037 | 1409 | if (!trace->live) |
---|
1038 | 1410 | return NULL; |
---|
1039 | 1411 | ++trace->stats.proc_getname; |
---|
.. | .. |
---|
1041 | 1413 | return NULL; |
---|
1042 | 1414 | } |
---|
1043 | 1415 | |
---|
1044 | | - return ttrace->paths.table[fd]; |
---|
| 1416 | + return ttrace->files.table[fd].pathname; |
---|
1045 | 1417 | } |
---|
1046 | 1418 | |
---|
1047 | 1419 | size_t syscall_arg__scnprintf_fd(char *bf, size_t size, struct syscall_arg *arg) |
---|
.. | .. |
---|
1080 | 1452 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); |
---|
1081 | 1453 | struct thread_trace *ttrace = thread__priv(arg->thread); |
---|
1082 | 1454 | |
---|
1083 | | - if (ttrace && fd >= 0 && fd <= ttrace->paths.max) |
---|
1084 | | - zfree(&ttrace->paths.table[fd]); |
---|
| 1455 | + if (ttrace && fd >= 0 && fd <= ttrace->files.max) |
---|
| 1456 | + zfree(&ttrace->files.table[fd].pathname); |
---|
1085 | 1457 | |
---|
1086 | 1458 | return printed; |
---|
1087 | 1459 | } |
---|
.. | .. |
---|
1095 | 1467 | ttrace->filename.entry_str_pos = bf - ttrace->entry_str; |
---|
1096 | 1468 | } |
---|
1097 | 1469 | |
---|
| 1470 | +static size_t syscall_arg__scnprintf_augmented_string(struct syscall_arg *arg, char *bf, size_t size) |
---|
| 1471 | +{ |
---|
| 1472 | + struct augmented_arg *augmented_arg = arg->augmented.args; |
---|
| 1473 | + size_t printed = scnprintf(bf, size, "\"%.*s\"", augmented_arg->size, augmented_arg->value); |
---|
| 1474 | + /* |
---|
| 1475 | + * So that the next arg with a payload can consume its augmented arg, i.e. for rename* syscalls |
---|
| 1476 | + * we would have two strings, each prefixed by its size. |
---|
| 1477 | + */ |
---|
| 1478 | + int consumed = sizeof(*augmented_arg) + augmented_arg->size; |
---|
| 1479 | + |
---|
| 1480 | + arg->augmented.args = ((void *)arg->augmented.args) + consumed; |
---|
| 1481 | + arg->augmented.size -= consumed; |
---|
| 1482 | + |
---|
| 1483 | + return printed; |
---|
| 1484 | +} |
---|
| 1485 | + |
---|
1098 | 1486 | static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, |
---|
1099 | 1487 | struct syscall_arg *arg) |
---|
1100 | 1488 | { |
---|
1101 | 1489 | unsigned long ptr = arg->val; |
---|
| 1490 | + |
---|
| 1491 | + if (arg->augmented.args) |
---|
| 1492 | + return syscall_arg__scnprintf_augmented_string(arg, bf, size); |
---|
1102 | 1493 | |
---|
1103 | 1494 | if (!arg->trace->vfs_getname) |
---|
1104 | 1495 | return scnprintf(bf, size, "%#x", ptr); |
---|
.. | .. |
---|
1142 | 1533 | interrupted = sig == SIGINT; |
---|
1143 | 1534 | } |
---|
1144 | 1535 | |
---|
1145 | | -static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, |
---|
1146 | | - u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) |
---|
| 1536 | +static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread, FILE *fp) |
---|
1147 | 1537 | { |
---|
1148 | | - size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); |
---|
1149 | | - printed += fprintf_duration(duration, duration_calculated, fp); |
---|
| 1538 | + size_t printed = 0; |
---|
1150 | 1539 | |
---|
1151 | 1540 | if (trace->multiple_threads) { |
---|
1152 | 1541 | if (trace->show_comm) |
---|
.. | .. |
---|
1155 | 1544 | } |
---|
1156 | 1545 | |
---|
1157 | 1546 | return printed; |
---|
| 1547 | +} |
---|
| 1548 | + |
---|
| 1549 | +static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, |
---|
| 1550 | + u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) |
---|
| 1551 | +{ |
---|
| 1552 | + size_t printed = 0; |
---|
| 1553 | + |
---|
| 1554 | + if (trace->show_tstamp) |
---|
| 1555 | + printed = trace__fprintf_tstamp(trace, tstamp, fp); |
---|
| 1556 | + if (trace->show_duration) |
---|
| 1557 | + printed += fprintf_duration(duration, duration_calculated, fp); |
---|
| 1558 | + return printed + trace__fprintf_comm_tid(trace, thread, fp); |
---|
1158 | 1559 | } |
---|
1159 | 1560 | |
---|
1160 | 1561 | static int trace__process_event(struct trace *trace, struct machine *machine, |
---|
.. | .. |
---|
1194 | 1595 | |
---|
1195 | 1596 | if (symbol_conf.kptr_restrict) { |
---|
1196 | 1597 | pr_warning("Kernel address maps (/proc/{kallsyms,modules}) are restricted.\n\n" |
---|
1197 | | - "Check /proc/sys/kernel/kptr_restrict.\n\n" |
---|
| 1598 | + "Check /proc/sys/kernel/kptr_restrict and /proc/sys/kernel/perf_event_paranoid.\n\n" |
---|
1198 | 1599 | "Kernel samples will not be resolved.\n"); |
---|
1199 | 1600 | machine->kptr_restrict_warned = true; |
---|
1200 | 1601 | return NULL; |
---|
.. | .. |
---|
1203 | 1604 | return machine__resolve_kernel_addr(vmachine, addrp, modp); |
---|
1204 | 1605 | } |
---|
1205 | 1606 | |
---|
1206 | | -static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
---|
| 1607 | +static int trace__symbols_init(struct trace *trace, struct evlist *evlist) |
---|
1207 | 1608 | { |
---|
1208 | 1609 | int err = symbol__init(NULL); |
---|
1209 | 1610 | |
---|
.. | .. |
---|
1219 | 1620 | goto out; |
---|
1220 | 1621 | |
---|
1221 | 1622 | err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, |
---|
1222 | | - evlist->threads, trace__tool_process, false, |
---|
1223 | | - trace->opts.proc_map_timeout, 1); |
---|
| 1623 | + evlist->core.threads, trace__tool_process, false, |
---|
| 1624 | + 1); |
---|
1224 | 1625 | out: |
---|
1225 | 1626 | if (err) |
---|
1226 | 1627 | symbol__exit(); |
---|
.. | .. |
---|
1256 | 1657 | return 0; |
---|
1257 | 1658 | } |
---|
1258 | 1659 | |
---|
1259 | | -static int syscall__set_arg_fmts(struct syscall *sc) |
---|
1260 | | -{ |
---|
1261 | | - struct format_field *field; |
---|
1262 | | - int idx = 0, len; |
---|
| 1660 | +static struct syscall_arg_fmt syscall_arg_fmts__by_name[] = { |
---|
| 1661 | + { .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, }, |
---|
| 1662 | + { .name = "vector", .scnprintf = SCA_X86_IRQ_VECTORS, .strtoul = STUL_X86_IRQ_VECTORS, }, |
---|
| 1663 | +}; |
---|
1263 | 1664 | |
---|
1264 | | - for (field = sc->args; field; field = field->next, ++idx) { |
---|
1265 | | - if (sc->fmt && sc->fmt->arg[idx].scnprintf) |
---|
| 1665 | +static int syscall_arg_fmt__cmp(const void *name, const void *fmtp) |
---|
| 1666 | +{ |
---|
| 1667 | + const struct syscall_arg_fmt *fmt = fmtp; |
---|
| 1668 | + return strcmp(name, fmt->name); |
---|
| 1669 | +} |
---|
| 1670 | + |
---|
| 1671 | +static struct syscall_arg_fmt * |
---|
| 1672 | +__syscall_arg_fmt__find_by_name(struct syscall_arg_fmt *fmts, const int nmemb, const char *name) |
---|
| 1673 | +{ |
---|
| 1674 | + return bsearch(name, fmts, nmemb, sizeof(struct syscall_arg_fmt), syscall_arg_fmt__cmp); |
---|
| 1675 | +} |
---|
| 1676 | + |
---|
| 1677 | +static struct syscall_arg_fmt *syscall_arg_fmt__find_by_name(const char *name) |
---|
| 1678 | +{ |
---|
| 1679 | + const int nmemb = ARRAY_SIZE(syscall_arg_fmts__by_name); |
---|
| 1680 | + return __syscall_arg_fmt__find_by_name(syscall_arg_fmts__by_name, nmemb, name); |
---|
| 1681 | +} |
---|
| 1682 | + |
---|
| 1683 | +static struct tep_format_field * |
---|
| 1684 | +syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field) |
---|
| 1685 | +{ |
---|
| 1686 | + struct tep_format_field *last_field = NULL; |
---|
| 1687 | + int len; |
---|
| 1688 | + |
---|
| 1689 | + for (; field; field = field->next, ++arg) { |
---|
| 1690 | + last_field = field; |
---|
| 1691 | + |
---|
| 1692 | + if (arg->scnprintf) |
---|
1266 | 1693 | continue; |
---|
1267 | 1694 | |
---|
| 1695 | + len = strlen(field->name); |
---|
| 1696 | + |
---|
1268 | 1697 | if (strcmp(field->type, "const char *") == 0 && |
---|
1269 | | - (strcmp(field->name, "filename") == 0 || |
---|
1270 | | - strcmp(field->name, "path") == 0 || |
---|
1271 | | - strcmp(field->name, "pathname") == 0)) |
---|
1272 | | - sc->arg_fmt[idx].scnprintf = SCA_FILENAME; |
---|
1273 | | - else if (field->flags & FIELD_IS_POINTER) |
---|
1274 | | - sc->arg_fmt[idx].scnprintf = syscall_arg__scnprintf_hex; |
---|
| 1698 | + ((len >= 4 && strcmp(field->name + len - 4, "name") == 0) || |
---|
| 1699 | + strstr(field->name, "path") != NULL)) |
---|
| 1700 | + arg->scnprintf = SCA_FILENAME; |
---|
| 1701 | + else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr")) |
---|
| 1702 | + arg->scnprintf = SCA_PTR; |
---|
1275 | 1703 | else if (strcmp(field->type, "pid_t") == 0) |
---|
1276 | | - sc->arg_fmt[idx].scnprintf = SCA_PID; |
---|
| 1704 | + arg->scnprintf = SCA_PID; |
---|
1277 | 1705 | else if (strcmp(field->type, "umode_t") == 0) |
---|
1278 | | - sc->arg_fmt[idx].scnprintf = SCA_MODE_T; |
---|
1279 | | - else if ((strcmp(field->type, "int") == 0 || |
---|
| 1706 | + arg->scnprintf = SCA_MODE_T; |
---|
| 1707 | + else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) { |
---|
| 1708 | + arg->scnprintf = SCA_CHAR_ARRAY; |
---|
| 1709 | + arg->nr_entries = field->arraylen; |
---|
| 1710 | + } else if ((strcmp(field->type, "int") == 0 || |
---|
1280 | 1711 | strcmp(field->type, "unsigned int") == 0 || |
---|
1281 | 1712 | strcmp(field->type, "long") == 0) && |
---|
1282 | | - (len = strlen(field->name)) >= 2 && |
---|
1283 | | - strcmp(field->name + len - 2, "fd") == 0) { |
---|
| 1713 | + len >= 2 && strcmp(field->name + len - 2, "fd") == 0) { |
---|
1284 | 1714 | /* |
---|
1285 | 1715 | * /sys/kernel/tracing/events/syscalls/sys_enter* |
---|
1286 | 1716 | * egrep 'field:.*fd;' .../format|sed -r 's/.*field:([a-z ]+) [a-z_]*fd.+/\1/g'|sort|uniq -c |
---|
.. | .. |
---|
1288 | 1718 | * 23 unsigned int |
---|
1289 | 1719 | * 7 unsigned long |
---|
1290 | 1720 | */ |
---|
1291 | | - sc->arg_fmt[idx].scnprintf = SCA_FD; |
---|
| 1721 | + arg->scnprintf = SCA_FD; |
---|
| 1722 | + } else { |
---|
| 1723 | + struct syscall_arg_fmt *fmt = syscall_arg_fmt__find_by_name(field->name); |
---|
| 1724 | + |
---|
| 1725 | + if (fmt) { |
---|
| 1726 | + arg->scnprintf = fmt->scnprintf; |
---|
| 1727 | + arg->strtoul = fmt->strtoul; |
---|
| 1728 | + } |
---|
1292 | 1729 | } |
---|
1293 | 1730 | } |
---|
| 1731 | + |
---|
| 1732 | + return last_field; |
---|
| 1733 | +} |
---|
| 1734 | + |
---|
| 1735 | +static int syscall__set_arg_fmts(struct syscall *sc) |
---|
| 1736 | +{ |
---|
| 1737 | + struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args); |
---|
| 1738 | + |
---|
| 1739 | + if (last_field) |
---|
| 1740 | + sc->args_size = last_field->offset + last_field->size; |
---|
1294 | 1741 | |
---|
1295 | 1742 | return 0; |
---|
1296 | 1743 | } |
---|
.. | .. |
---|
1301 | 1748 | struct syscall *sc; |
---|
1302 | 1749 | const char *name = syscalltbl__name(trace->sctbl, id); |
---|
1303 | 1750 | |
---|
1304 | | - if (name == NULL) |
---|
1305 | | - return -1; |
---|
| 1751 | +#ifdef HAVE_SYSCALL_TABLE_SUPPORT |
---|
| 1752 | + if (trace->syscalls.table == NULL) { |
---|
| 1753 | + trace->syscalls.table = calloc(trace->sctbl->syscalls.max_id + 1, sizeof(*sc)); |
---|
| 1754 | + if (trace->syscalls.table == NULL) |
---|
| 1755 | + return -ENOMEM; |
---|
| 1756 | + } |
---|
| 1757 | +#else |
---|
| 1758 | + if (id > trace->sctbl->syscalls.max_id || (id == 0 && trace->syscalls.table == NULL)) { |
---|
| 1759 | + // When using libaudit we don't know beforehand what is the max syscall id |
---|
| 1760 | + struct syscall *table = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); |
---|
1306 | 1761 | |
---|
1307 | | - if (id > trace->syscalls.max) { |
---|
1308 | | - struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); |
---|
| 1762 | + if (table == NULL) |
---|
| 1763 | + return -ENOMEM; |
---|
1309 | 1764 | |
---|
1310 | | - if (nsyscalls == NULL) |
---|
1311 | | - return -1; |
---|
| 1765 | + // Need to memset from offset 0 and +1 members if brand new |
---|
| 1766 | + if (trace->syscalls.table == NULL) |
---|
| 1767 | + memset(table, 0, (id + 1) * sizeof(*sc)); |
---|
| 1768 | + else |
---|
| 1769 | + memset(table + trace->sctbl->syscalls.max_id + 1, 0, (id - trace->sctbl->syscalls.max_id) * sizeof(*sc)); |
---|
1312 | 1770 | |
---|
1313 | | - if (trace->syscalls.max != -1) { |
---|
1314 | | - memset(nsyscalls + trace->syscalls.max + 1, 0, |
---|
1315 | | - (id - trace->syscalls.max) * sizeof(*sc)); |
---|
1316 | | - } else { |
---|
1317 | | - memset(nsyscalls, 0, (id + 1) * sizeof(*sc)); |
---|
1318 | | - } |
---|
| 1771 | + trace->syscalls.table = table; |
---|
| 1772 | + trace->sctbl->syscalls.max_id = id; |
---|
| 1773 | + } |
---|
| 1774 | +#endif |
---|
| 1775 | + sc = trace->syscalls.table + id; |
---|
| 1776 | + if (sc->nonexistent) |
---|
| 1777 | + return 0; |
---|
1319 | 1778 | |
---|
1320 | | - trace->syscalls.table = nsyscalls; |
---|
1321 | | - trace->syscalls.max = id; |
---|
| 1779 | + if (name == NULL) { |
---|
| 1780 | + sc->nonexistent = true; |
---|
| 1781 | + return 0; |
---|
1322 | 1782 | } |
---|
1323 | 1783 | |
---|
1324 | | - sc = trace->syscalls.table + id; |
---|
1325 | 1784 | sc->name = name; |
---|
1326 | | - |
---|
1327 | 1785 | sc->fmt = syscall_fmt__find(sc->name); |
---|
1328 | 1786 | |
---|
1329 | 1787 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); |
---|
.. | .. |
---|
1335 | 1793 | } |
---|
1336 | 1794 | |
---|
1337 | 1795 | if (syscall__alloc_arg_fmts(sc, IS_ERR(sc->tp_format) ? 6 : sc->tp_format->format.nr_fields)) |
---|
1338 | | - return -1; |
---|
| 1796 | + return -ENOMEM; |
---|
1339 | 1797 | |
---|
1340 | 1798 | if (IS_ERR(sc->tp_format)) |
---|
1341 | | - return -1; |
---|
| 1799 | + return PTR_ERR(sc->tp_format); |
---|
1342 | 1800 | |
---|
1343 | 1801 | sc->args = sc->tp_format->format.fields; |
---|
1344 | 1802 | /* |
---|
.. | .. |
---|
1357 | 1815 | return syscall__set_arg_fmts(sc); |
---|
1358 | 1816 | } |
---|
1359 | 1817 | |
---|
| 1818 | +static int evsel__init_tp_arg_scnprintf(struct evsel *evsel) |
---|
| 1819 | +{ |
---|
| 1820 | + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); |
---|
| 1821 | + |
---|
| 1822 | + if (fmt != NULL) { |
---|
| 1823 | + syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields); |
---|
| 1824 | + return 0; |
---|
| 1825 | + } |
---|
| 1826 | + |
---|
| 1827 | + return -ENOMEM; |
---|
| 1828 | +} |
---|
| 1829 | + |
---|
| 1830 | +static int intcmp(const void *a, const void *b) |
---|
| 1831 | +{ |
---|
| 1832 | + const int *one = a, *another = b; |
---|
| 1833 | + |
---|
| 1834 | + return *one - *another; |
---|
| 1835 | +} |
---|
| 1836 | + |
---|
1360 | 1837 | static int trace__validate_ev_qualifier(struct trace *trace) |
---|
1361 | 1838 | { |
---|
1362 | | - int err = 0, i; |
---|
1363 | | - size_t nr_allocated; |
---|
| 1839 | + int err = 0; |
---|
| 1840 | + bool printed_invalid_prefix = false; |
---|
1364 | 1841 | struct str_node *pos; |
---|
| 1842 | + size_t nr_used = 0, nr_allocated = strlist__nr_entries(trace->ev_qualifier); |
---|
1365 | 1843 | |
---|
1366 | | - trace->ev_qualifier_ids.nr = strlist__nr_entries(trace->ev_qualifier); |
---|
1367 | | - trace->ev_qualifier_ids.entries = malloc(trace->ev_qualifier_ids.nr * |
---|
| 1844 | + trace->ev_qualifier_ids.entries = malloc(nr_allocated * |
---|
1368 | 1845 | sizeof(trace->ev_qualifier_ids.entries[0])); |
---|
1369 | 1846 | |
---|
1370 | 1847 | if (trace->ev_qualifier_ids.entries == NULL) { |
---|
.. | .. |
---|
1373 | 1850 | err = -EINVAL; |
---|
1374 | 1851 | goto out; |
---|
1375 | 1852 | } |
---|
1376 | | - |
---|
1377 | | - nr_allocated = trace->ev_qualifier_ids.nr; |
---|
1378 | | - i = 0; |
---|
1379 | 1853 | |
---|
1380 | 1854 | strlist__for_each_entry(pos, trace->ev_qualifier) { |
---|
1381 | 1855 | const char *sc = pos->s; |
---|
.. | .. |
---|
1386 | 1860 | if (id >= 0) |
---|
1387 | 1861 | goto matches; |
---|
1388 | 1862 | |
---|
1389 | | - if (err == 0) { |
---|
1390 | | - fputs("Error:\tInvalid syscall ", trace->output); |
---|
1391 | | - err = -EINVAL; |
---|
| 1863 | + if (!printed_invalid_prefix) { |
---|
| 1864 | + pr_debug("Skipping unknown syscalls: "); |
---|
| 1865 | + printed_invalid_prefix = true; |
---|
1392 | 1866 | } else { |
---|
1393 | | - fputs(", ", trace->output); |
---|
| 1867 | + pr_debug(", "); |
---|
1394 | 1868 | } |
---|
1395 | 1869 | |
---|
1396 | | - fputs(sc, trace->output); |
---|
| 1870 | + pr_debug("%s", sc); |
---|
| 1871 | + continue; |
---|
1397 | 1872 | } |
---|
1398 | 1873 | matches: |
---|
1399 | | - trace->ev_qualifier_ids.entries[i++] = id; |
---|
| 1874 | + trace->ev_qualifier_ids.entries[nr_used++] = id; |
---|
1400 | 1875 | if (match_next == -1) |
---|
1401 | 1876 | continue; |
---|
1402 | 1877 | |
---|
.. | .. |
---|
1404 | 1879 | id = syscalltbl__strglobmatch_next(trace->sctbl, sc, &match_next); |
---|
1405 | 1880 | if (id < 0) |
---|
1406 | 1881 | break; |
---|
1407 | | - if (nr_allocated == trace->ev_qualifier_ids.nr) { |
---|
| 1882 | + if (nr_allocated == nr_used) { |
---|
1408 | 1883 | void *entries; |
---|
1409 | 1884 | |
---|
1410 | 1885 | nr_allocated += 8; |
---|
.. | .. |
---|
1417 | 1892 | } |
---|
1418 | 1893 | trace->ev_qualifier_ids.entries = entries; |
---|
1419 | 1894 | } |
---|
1420 | | - trace->ev_qualifier_ids.nr++; |
---|
1421 | | - trace->ev_qualifier_ids.entries[i++] = id; |
---|
| 1895 | + trace->ev_qualifier_ids.entries[nr_used++] = id; |
---|
1422 | 1896 | } |
---|
1423 | 1897 | } |
---|
1424 | 1898 | |
---|
1425 | | - if (err < 0) { |
---|
1426 | | - fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'" |
---|
1427 | | - "\nHint:\tand: 'man syscalls'\n", trace->output); |
---|
1428 | | -out_free: |
---|
1429 | | - zfree(&trace->ev_qualifier_ids.entries); |
---|
1430 | | - trace->ev_qualifier_ids.nr = 0; |
---|
1431 | | - } |
---|
| 1899 | + trace->ev_qualifier_ids.nr = nr_used; |
---|
| 1900 | + qsort(trace->ev_qualifier_ids.entries, nr_used, sizeof(int), intcmp); |
---|
1432 | 1901 | out: |
---|
| 1902 | + if (printed_invalid_prefix) |
---|
| 1903 | + pr_debug("\n"); |
---|
1433 | 1904 | return err; |
---|
| 1905 | +out_free: |
---|
| 1906 | + zfree(&trace->ev_qualifier_ids.entries); |
---|
| 1907 | + trace->ev_qualifier_ids.nr = 0; |
---|
| 1908 | + goto out; |
---|
| 1909 | +} |
---|
| 1910 | + |
---|
| 1911 | +static __maybe_unused bool trace__syscall_enabled(struct trace *trace, int id) |
---|
| 1912 | +{ |
---|
| 1913 | + bool in_ev_qualifier; |
---|
| 1914 | + |
---|
| 1915 | + if (trace->ev_qualifier_ids.nr == 0) |
---|
| 1916 | + return true; |
---|
| 1917 | + |
---|
| 1918 | + in_ev_qualifier = bsearch(&id, trace->ev_qualifier_ids.entries, |
---|
| 1919 | + trace->ev_qualifier_ids.nr, sizeof(int), intcmp) != NULL; |
---|
| 1920 | + |
---|
| 1921 | + if (in_ev_qualifier) |
---|
| 1922 | + return !trace->not_ev_qualifier; |
---|
| 1923 | + |
---|
| 1924 | + return trace->not_ev_qualifier; |
---|
1434 | 1925 | } |
---|
1435 | 1926 | |
---|
1436 | 1927 | /* |
---|
.. | .. |
---|
1459 | 1950 | return scnprintf(bf, size, "arg%d: ", arg->idx); |
---|
1460 | 1951 | } |
---|
1461 | 1952 | |
---|
1462 | | -static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, |
---|
1463 | | - struct syscall_arg *arg, unsigned long val) |
---|
| 1953 | +/* |
---|
| 1954 | + * Check if the value is in fact zero, i.e. mask whatever needs masking, such |
---|
| 1955 | + * as mount 'flags' argument that needs ignoring some magic flag, see comment |
---|
| 1956 | + * in tools/perf/trace/beauty/mount_flags.c |
---|
| 1957 | + */ |
---|
| 1958 | +static unsigned long syscall_arg_fmt__mask_val(struct syscall_arg_fmt *fmt, struct syscall_arg *arg, unsigned long val) |
---|
1464 | 1959 | { |
---|
1465 | | - if (sc->arg_fmt && sc->arg_fmt[arg->idx].scnprintf) { |
---|
| 1960 | + if (fmt && fmt->mask_val) |
---|
| 1961 | + return fmt->mask_val(arg, val); |
---|
| 1962 | + |
---|
| 1963 | + return val; |
---|
| 1964 | +} |
---|
| 1965 | + |
---|
| 1966 | +static size_t syscall_arg_fmt__scnprintf_val(struct syscall_arg_fmt *fmt, char *bf, size_t size, |
---|
| 1967 | + struct syscall_arg *arg, unsigned long val) |
---|
| 1968 | +{ |
---|
| 1969 | + if (fmt && fmt->scnprintf) { |
---|
1466 | 1970 | arg->val = val; |
---|
1467 | | - if (sc->arg_fmt[arg->idx].parm) |
---|
1468 | | - arg->parm = sc->arg_fmt[arg->idx].parm; |
---|
1469 | | - return sc->arg_fmt[arg->idx].scnprintf(bf, size, arg); |
---|
| 1971 | + if (fmt->parm) |
---|
| 1972 | + arg->parm = fmt->parm; |
---|
| 1973 | + return fmt->scnprintf(bf, size, arg); |
---|
1470 | 1974 | } |
---|
1471 | 1975 | return scnprintf(bf, size, "%ld", val); |
---|
1472 | 1976 | } |
---|
1473 | 1977 | |
---|
1474 | 1978 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
---|
1475 | | - unsigned char *args, struct trace *trace, |
---|
1476 | | - struct thread *thread) |
---|
| 1979 | + unsigned char *args, void *augmented_args, int augmented_args_size, |
---|
| 1980 | + struct trace *trace, struct thread *thread) |
---|
1477 | 1981 | { |
---|
1478 | 1982 | size_t printed = 0; |
---|
1479 | 1983 | unsigned long val; |
---|
1480 | 1984 | u8 bit = 1; |
---|
1481 | 1985 | struct syscall_arg arg = { |
---|
1482 | 1986 | .args = args, |
---|
| 1987 | + .augmented = { |
---|
| 1988 | + .size = augmented_args_size, |
---|
| 1989 | + .args = augmented_args, |
---|
| 1990 | + }, |
---|
1483 | 1991 | .idx = 0, |
---|
1484 | 1992 | .mask = 0, |
---|
1485 | 1993 | .trace = trace, |
---|
1486 | 1994 | .thread = thread, |
---|
| 1995 | + .show_string_prefix = trace->show_string_prefix, |
---|
1487 | 1996 | }; |
---|
1488 | 1997 | struct thread_trace *ttrace = thread__priv(thread); |
---|
1489 | 1998 | |
---|
.. | .. |
---|
1495 | 2004 | ttrace->ret_scnprintf = NULL; |
---|
1496 | 2005 | |
---|
1497 | 2006 | if (sc->args != NULL) { |
---|
1498 | | - struct format_field *field; |
---|
| 2007 | + struct tep_format_field *field; |
---|
1499 | 2008 | |
---|
1500 | 2009 | for (field = sc->args; field; |
---|
1501 | 2010 | field = field->next, ++arg.idx, bit <<= 1) { |
---|
1502 | 2011 | if (arg.mask & bit) |
---|
1503 | 2012 | continue; |
---|
1504 | 2013 | |
---|
| 2014 | + arg.fmt = &sc->arg_fmt[arg.idx]; |
---|
1505 | 2015 | val = syscall_arg__val(&arg, arg.idx); |
---|
| 2016 | + /* |
---|
| 2017 | + * Some syscall args need some mask, most don't and |
---|
| 2018 | + * return val untouched. |
---|
| 2019 | + */ |
---|
| 2020 | + val = syscall_arg_fmt__mask_val(&sc->arg_fmt[arg.idx], &arg, val); |
---|
1506 | 2021 | |
---|
1507 | 2022 | /* |
---|
1508 | 2023 | * Suppress this argument if its value is zero and |
---|
.. | .. |
---|
1510 | 2025 | * strarray for it. |
---|
1511 | 2026 | */ |
---|
1512 | 2027 | if (val == 0 && |
---|
| 2028 | + !trace->show_zeros && |
---|
1513 | 2029 | !(sc->arg_fmt && |
---|
1514 | 2030 | (sc->arg_fmt[arg.idx].show_zero || |
---|
1515 | 2031 | sc->arg_fmt[arg.idx].scnprintf == SCA_STRARRAY || |
---|
.. | .. |
---|
1517 | 2033 | sc->arg_fmt[arg.idx].parm)) |
---|
1518 | 2034 | continue; |
---|
1519 | 2035 | |
---|
1520 | | - printed += scnprintf(bf + printed, size - printed, |
---|
1521 | | - "%s%s: ", printed ? ", " : "", field->name); |
---|
1522 | | - printed += syscall__scnprintf_val(sc, bf + printed, size - printed, &arg, val); |
---|
| 2036 | + printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); |
---|
| 2037 | + |
---|
| 2038 | + if (trace->show_arg_names) |
---|
| 2039 | + printed += scnprintf(bf + printed, size - printed, "%s: ", field->name); |
---|
| 2040 | + |
---|
| 2041 | + printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx], |
---|
| 2042 | + bf + printed, size - printed, &arg, val); |
---|
1523 | 2043 | } |
---|
1524 | 2044 | } else if (IS_ERR(sc->tp_format)) { |
---|
1525 | 2045 | /* |
---|
.. | .. |
---|
1534 | 2054 | if (printed) |
---|
1535 | 2055 | printed += scnprintf(bf + printed, size - printed, ", "); |
---|
1536 | 2056 | printed += syscall__scnprintf_name(sc, bf + printed, size - printed, &arg); |
---|
1537 | | - printed += syscall__scnprintf_val(sc, bf + printed, size - printed, &arg, val); |
---|
| 2057 | + printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx], bf + printed, size - printed, &arg, val); |
---|
1538 | 2058 | next_arg: |
---|
1539 | 2059 | ++arg.idx; |
---|
1540 | 2060 | bit <<= 1; |
---|
.. | .. |
---|
1544 | 2064 | return printed; |
---|
1545 | 2065 | } |
---|
1546 | 2066 | |
---|
1547 | | -typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2067 | +typedef int (*tracepoint_handler)(struct trace *trace, struct evsel *evsel, |
---|
1548 | 2068 | union perf_event *event, |
---|
1549 | 2069 | struct perf_sample *sample); |
---|
1550 | 2070 | |
---|
1551 | 2071 | static struct syscall *trace__syscall_info(struct trace *trace, |
---|
1552 | | - struct perf_evsel *evsel, int id) |
---|
| 2072 | + struct evsel *evsel, int id) |
---|
1553 | 2073 | { |
---|
| 2074 | + int err = 0; |
---|
1554 | 2075 | |
---|
1555 | 2076 | if (id < 0) { |
---|
1556 | 2077 | |
---|
.. | .. |
---|
1567 | 2088 | if (verbose > 1) { |
---|
1568 | 2089 | static u64 n; |
---|
1569 | 2090 | fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n", |
---|
1570 | | - id, perf_evsel__name(evsel), ++n); |
---|
| 2091 | + id, evsel__name(evsel), ++n); |
---|
1571 | 2092 | } |
---|
1572 | 2093 | return NULL; |
---|
1573 | 2094 | } |
---|
1574 | 2095 | |
---|
1575 | | - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && |
---|
1576 | | - trace__read_syscall_info(trace, id)) |
---|
| 2096 | + err = -EINVAL; |
---|
| 2097 | + |
---|
| 2098 | +#ifdef HAVE_SYSCALL_TABLE_SUPPORT |
---|
| 2099 | + if (id > trace->sctbl->syscalls.max_id) { |
---|
| 2100 | +#else |
---|
| 2101 | + if (id >= trace->sctbl->syscalls.max_id) { |
---|
| 2102 | + /* |
---|
| 2103 | + * With libaudit we don't know beforehand what is the max_id, |
---|
| 2104 | + * so we let trace__read_syscall_info() figure that out as we |
---|
| 2105 | + * go on reading syscalls. |
---|
| 2106 | + */ |
---|
| 2107 | + err = trace__read_syscall_info(trace, id); |
---|
| 2108 | + if (err) |
---|
| 2109 | +#endif |
---|
| 2110 | + goto out_cant_read; |
---|
| 2111 | + } |
---|
| 2112 | + |
---|
| 2113 | + if ((trace->syscalls.table == NULL || trace->syscalls.table[id].name == NULL) && |
---|
| 2114 | + (err = trace__read_syscall_info(trace, id)) != 0) |
---|
1577 | 2115 | goto out_cant_read; |
---|
1578 | 2116 | |
---|
1579 | | - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) |
---|
| 2117 | + if (trace->syscalls.table[id].name == NULL) { |
---|
| 2118 | + if (trace->syscalls.table[id].nonexistent) |
---|
| 2119 | + return NULL; |
---|
1580 | 2120 | goto out_cant_read; |
---|
| 2121 | + } |
---|
1581 | 2122 | |
---|
1582 | 2123 | return &trace->syscalls.table[id]; |
---|
1583 | 2124 | |
---|
1584 | 2125 | out_cant_read: |
---|
1585 | 2126 | if (verbose > 0) { |
---|
1586 | | - fprintf(trace->output, "Problems reading syscall %d", id); |
---|
1587 | | - if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) |
---|
| 2127 | + char sbuf[STRERR_BUFSIZE]; |
---|
| 2128 | + fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, str_error_r(-err, sbuf, sizeof(sbuf))); |
---|
| 2129 | + if (id <= trace->sctbl->syscalls.max_id && trace->syscalls.table[id].name != NULL) |
---|
1588 | 2130 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); |
---|
1589 | 2131 | fputs(" information\n", trace->output); |
---|
1590 | 2132 | } |
---|
1591 | 2133 | return NULL; |
---|
1592 | 2134 | } |
---|
1593 | 2135 | |
---|
1594 | | -static void thread__update_stats(struct thread_trace *ttrace, |
---|
1595 | | - int id, struct perf_sample *sample) |
---|
| 2136 | +struct syscall_stats { |
---|
| 2137 | + struct stats stats; |
---|
| 2138 | + u64 nr_failures; |
---|
| 2139 | + int max_errno; |
---|
| 2140 | + u32 *errnos; |
---|
| 2141 | +}; |
---|
| 2142 | + |
---|
| 2143 | +static void thread__update_stats(struct thread *thread, struct thread_trace *ttrace, |
---|
| 2144 | + int id, struct perf_sample *sample, long err, bool errno_summary) |
---|
1596 | 2145 | { |
---|
1597 | 2146 | struct int_node *inode; |
---|
1598 | | - struct stats *stats; |
---|
| 2147 | + struct syscall_stats *stats; |
---|
1599 | 2148 | u64 duration = 0; |
---|
1600 | 2149 | |
---|
1601 | 2150 | inode = intlist__findnew(ttrace->syscall_stats, id); |
---|
.. | .. |
---|
1604 | 2153 | |
---|
1605 | 2154 | stats = inode->priv; |
---|
1606 | 2155 | if (stats == NULL) { |
---|
1607 | | - stats = malloc(sizeof(struct stats)); |
---|
| 2156 | + stats = malloc(sizeof(*stats)); |
---|
1608 | 2157 | if (stats == NULL) |
---|
1609 | 2158 | return; |
---|
1610 | | - init_stats(stats); |
---|
| 2159 | + |
---|
| 2160 | + stats->nr_failures = 0; |
---|
| 2161 | + stats->max_errno = 0; |
---|
| 2162 | + stats->errnos = NULL; |
---|
| 2163 | + init_stats(&stats->stats); |
---|
1611 | 2164 | inode->priv = stats; |
---|
1612 | 2165 | } |
---|
1613 | 2166 | |
---|
1614 | 2167 | if (ttrace->entry_time && sample->time > ttrace->entry_time) |
---|
1615 | 2168 | duration = sample->time - ttrace->entry_time; |
---|
1616 | 2169 | |
---|
1617 | | - update_stats(stats, duration); |
---|
| 2170 | + update_stats(&stats->stats, duration); |
---|
| 2171 | + |
---|
| 2172 | + if (err < 0) { |
---|
| 2173 | + ++stats->nr_failures; |
---|
| 2174 | + |
---|
| 2175 | + if (!errno_summary) |
---|
| 2176 | + return; |
---|
| 2177 | + |
---|
| 2178 | + err = -err; |
---|
| 2179 | + if (err > stats->max_errno) { |
---|
| 2180 | + u32 *new_errnos = realloc(stats->errnos, err * sizeof(u32)); |
---|
| 2181 | + |
---|
| 2182 | + if (new_errnos) { |
---|
| 2183 | + memset(new_errnos + stats->max_errno, 0, (err - stats->max_errno) * sizeof(u32)); |
---|
| 2184 | + } else { |
---|
| 2185 | + pr_debug("Not enough memory for errno stats for thread \"%s\"(%d/%d), results will be incomplete\n", |
---|
| 2186 | + thread__comm_str(thread), thread->pid_, thread->tid); |
---|
| 2187 | + return; |
---|
| 2188 | + } |
---|
| 2189 | + |
---|
| 2190 | + stats->errnos = new_errnos; |
---|
| 2191 | + stats->max_errno = err; |
---|
| 2192 | + } |
---|
| 2193 | + |
---|
| 2194 | + ++stats->errnos[err - 1]; |
---|
| 2195 | + } |
---|
1618 | 2196 | } |
---|
1619 | 2197 | |
---|
1620 | 2198 | static int trace__printf_interrupted_entry(struct trace *trace) |
---|
1621 | 2199 | { |
---|
1622 | 2200 | struct thread_trace *ttrace; |
---|
1623 | 2201 | size_t printed; |
---|
| 2202 | + int len; |
---|
1624 | 2203 | |
---|
1625 | 2204 | if (trace->failure_only || trace->current == NULL) |
---|
1626 | 2205 | return 0; |
---|
.. | .. |
---|
1631 | 2210 | return 0; |
---|
1632 | 2211 | |
---|
1633 | 2212 | printed = trace__fprintf_entry_head(trace, trace->current, 0, false, ttrace->entry_time, trace->output); |
---|
1634 | | - printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); |
---|
| 2213 | + printed += len = fprintf(trace->output, "%s)", ttrace->entry_str); |
---|
| 2214 | + |
---|
| 2215 | + if (len < trace->args_alignment - 4) |
---|
| 2216 | + printed += fprintf(trace->output, "%-*s", trace->args_alignment - 4 - len, " "); |
---|
| 2217 | + |
---|
| 2218 | + printed += fprintf(trace->output, " ...\n"); |
---|
| 2219 | + |
---|
1635 | 2220 | ttrace->entry_pending = false; |
---|
| 2221 | + ++trace->nr_events_printed; |
---|
1636 | 2222 | |
---|
1637 | 2223 | return printed; |
---|
1638 | 2224 | } |
---|
1639 | 2225 | |
---|
1640 | | -static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2226 | +static int trace__fprintf_sample(struct trace *trace, struct evsel *evsel, |
---|
1641 | 2227 | struct perf_sample *sample, struct thread *thread) |
---|
1642 | 2228 | { |
---|
1643 | 2229 | int printed = 0; |
---|
.. | .. |
---|
1646 | 2232 | double ts = (double)sample->time / NSEC_PER_MSEC; |
---|
1647 | 2233 | |
---|
1648 | 2234 | printed += fprintf(trace->output, "%22s %10.3f %s %d/%d [%d]\n", |
---|
1649 | | - perf_evsel__name(evsel), ts, |
---|
| 2235 | + evsel__name(evsel), ts, |
---|
1650 | 2236 | thread__comm_str(thread), |
---|
1651 | 2237 | sample->pid, sample->tid, sample->cpu); |
---|
1652 | 2238 | } |
---|
.. | .. |
---|
1654 | 2240 | return printed; |
---|
1655 | 2241 | } |
---|
1656 | 2242 | |
---|
1657 | | -static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2243 | +static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, int raw_augmented_args_size) |
---|
| 2244 | +{ |
---|
| 2245 | + void *augmented_args = NULL; |
---|
| 2246 | + /* |
---|
| 2247 | + * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter |
---|
| 2248 | + * and there we get all 6 syscall args plus the tracepoint common fields |
---|
| 2249 | + * that gets calculated at the start and the syscall_nr (another long). |
---|
| 2250 | + * So we check if that is the case and if so don't look after the |
---|
| 2251 | + * sc->args_size but always after the full raw_syscalls:sys_enter payload, |
---|
| 2252 | + * which is fixed. |
---|
| 2253 | + * |
---|
| 2254 | + * We'll revisit this later to pass s->args_size to the BPF augmenter |
---|
| 2255 | + * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it |
---|
| 2256 | + * copies only what we need for each syscall, like what happens when we |
---|
| 2257 | + * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace |
---|
| 2258 | + * traffic to just what is needed for each syscall. |
---|
| 2259 | + */ |
---|
| 2260 | + int args_size = raw_augmented_args_size ?: sc->args_size; |
---|
| 2261 | + |
---|
| 2262 | + *augmented_args_size = sample->raw_size - args_size; |
---|
| 2263 | + if (*augmented_args_size > 0) |
---|
| 2264 | + augmented_args = sample->raw_data + args_size; |
---|
| 2265 | + |
---|
| 2266 | + return augmented_args; |
---|
| 2267 | +} |
---|
| 2268 | + |
---|
| 2269 | +static int trace__sys_enter(struct trace *trace, struct evsel *evsel, |
---|
1658 | 2270 | union perf_event *event __maybe_unused, |
---|
1659 | 2271 | struct perf_sample *sample) |
---|
1660 | 2272 | { |
---|
1661 | 2273 | char *msg; |
---|
1662 | 2274 | void *args; |
---|
1663 | | - size_t printed = 0; |
---|
| 2275 | + int printed = 0; |
---|
1664 | 2276 | struct thread *thread; |
---|
1665 | 2277 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
---|
| 2278 | + int augmented_args_size = 0; |
---|
| 2279 | + void *augmented_args = NULL; |
---|
1666 | 2280 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
---|
1667 | 2281 | struct thread_trace *ttrace; |
---|
1668 | 2282 | |
---|
.. | .. |
---|
1686 | 2300 | |
---|
1687 | 2301 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) |
---|
1688 | 2302 | trace__printf_interrupted_entry(trace); |
---|
1689 | | - |
---|
| 2303 | + /* |
---|
| 2304 | + * If this is raw_syscalls.sys_enter, then it always comes with the 6 possible |
---|
| 2305 | + * arguments, even if the syscall being handled, say "openat", uses only 4 arguments |
---|
| 2306 | + * this breaks syscall__augmented_args() check for augmented args, as we calculate |
---|
| 2307 | + * syscall->args_size using each syscalls:sys_enter_NAME tracefs format file, |
---|
| 2308 | + * so when handling, say the openat syscall, we end up getting 6 args for the |
---|
| 2309 | + * raw_syscalls:sys_enter event, when we expected just 4, we end up mistakenly |
---|
| 2310 | + * thinking that the extra 2 u64 args are the augmented filename, so just check |
---|
| 2311 | + * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. |
---|
| 2312 | + */ |
---|
| 2313 | + if (evsel != trace->syscalls.events.sys_enter) |
---|
| 2314 | + augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); |
---|
1690 | 2315 | ttrace->entry_time = sample->time; |
---|
1691 | 2316 | msg = ttrace->entry_str; |
---|
1692 | 2317 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); |
---|
1693 | 2318 | |
---|
1694 | 2319 | printed += syscall__scnprintf_args(sc, msg + printed, trace__entry_str_size - printed, |
---|
1695 | | - args, trace, thread); |
---|
| 2320 | + args, augmented_args, augmented_args_size, trace, thread); |
---|
1696 | 2321 | |
---|
1697 | 2322 | if (sc->is_exit) { |
---|
1698 | 2323 | if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { |
---|
| 2324 | + int alignment = 0; |
---|
| 2325 | + |
---|
1699 | 2326 | trace__fprintf_entry_head(trace, thread, 0, false, ttrace->entry_time, trace->output); |
---|
1700 | | - fprintf(trace->output, "%-70s)\n", ttrace->entry_str); |
---|
| 2327 | + printed = fprintf(trace->output, "%s)", ttrace->entry_str); |
---|
| 2328 | + if (trace->args_alignment > printed) |
---|
| 2329 | + alignment = trace->args_alignment - printed; |
---|
| 2330 | + fprintf(trace->output, "%*s= ?\n", alignment, " "); |
---|
1701 | 2331 | } |
---|
1702 | 2332 | } else { |
---|
1703 | 2333 | ttrace->entry_pending = true; |
---|
.. | .. |
---|
1715 | 2345 | return err; |
---|
1716 | 2346 | } |
---|
1717 | 2347 | |
---|
1718 | | -static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2348 | +static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, |
---|
1719 | 2349 | struct perf_sample *sample) |
---|
1720 | 2350 | { |
---|
1721 | 2351 | struct thread_trace *ttrace; |
---|
.. | .. |
---|
1723 | 2353 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
---|
1724 | 2354 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
---|
1725 | 2355 | char msg[1024]; |
---|
1726 | | - void *args; |
---|
| 2356 | + void *args, *augmented_args = NULL; |
---|
| 2357 | + int augmented_args_size; |
---|
1727 | 2358 | |
---|
1728 | 2359 | if (sc == NULL) |
---|
1729 | 2360 | return -1; |
---|
.. | .. |
---|
1738 | 2369 | goto out_put; |
---|
1739 | 2370 | |
---|
1740 | 2371 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
---|
1741 | | - syscall__scnprintf_args(sc, msg, sizeof(msg), args, trace, thread); |
---|
| 2372 | + augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); |
---|
| 2373 | + syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); |
---|
1742 | 2374 | fprintf(trace->output, "%s", msg); |
---|
1743 | 2375 | err = 0; |
---|
1744 | 2376 | out_put: |
---|
.. | .. |
---|
1746 | 2378 | return err; |
---|
1747 | 2379 | } |
---|
1748 | 2380 | |
---|
1749 | | -static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2381 | +static int trace__resolve_callchain(struct trace *trace, struct evsel *evsel, |
---|
1750 | 2382 | struct perf_sample *sample, |
---|
1751 | 2383 | struct callchain_cursor *cursor) |
---|
1752 | 2384 | { |
---|
1753 | 2385 | struct addr_location al; |
---|
1754 | | - int max_stack = evsel->attr.sample_max_stack ? |
---|
1755 | | - evsel->attr.sample_max_stack : |
---|
| 2386 | + int max_stack = evsel->core.attr.sample_max_stack ? |
---|
| 2387 | + evsel->core.attr.sample_max_stack : |
---|
1756 | 2388 | trace->max_stack; |
---|
| 2389 | + int err; |
---|
1757 | 2390 | |
---|
1758 | | - if (machine__resolve(trace->host, &al, sample) < 0 || |
---|
1759 | | - thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack)) |
---|
| 2391 | + if (machine__resolve(trace->host, &al, sample) < 0) |
---|
1760 | 2392 | return -1; |
---|
1761 | 2393 | |
---|
1762 | | - return 0; |
---|
| 2394 | + err = thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack); |
---|
| 2395 | + addr_location__put(&al); |
---|
| 2396 | + return err; |
---|
1763 | 2397 | } |
---|
1764 | 2398 | |
---|
1765 | 2399 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) |
---|
.. | .. |
---|
1769 | 2403 | EVSEL__PRINT_DSO | |
---|
1770 | 2404 | EVSEL__PRINT_UNKNOWN_AS_ADDR; |
---|
1771 | 2405 | |
---|
1772 | | - return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output); |
---|
| 2406 | + return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, symbol_conf.bt_stop_list, trace->output); |
---|
1773 | 2407 | } |
---|
1774 | 2408 | |
---|
1775 | | -static const char *errno_to_name(struct perf_evsel *evsel, int err) |
---|
| 2409 | +static const char *errno_to_name(struct evsel *evsel, int err) |
---|
1776 | 2410 | { |
---|
1777 | | - struct perf_env *env = perf_evsel__env(evsel); |
---|
| 2411 | + struct perf_env *env = evsel__env(evsel); |
---|
1778 | 2412 | const char *arch_name = perf_env__arch(env); |
---|
1779 | 2413 | |
---|
1780 | 2414 | return arch_syscalls__strerrno(arch_name, err); |
---|
1781 | 2415 | } |
---|
1782 | 2416 | |
---|
1783 | | -static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2417 | +static int trace__sys_exit(struct trace *trace, struct evsel *evsel, |
---|
1784 | 2418 | union perf_event *event __maybe_unused, |
---|
1785 | 2419 | struct perf_sample *sample) |
---|
1786 | 2420 | { |
---|
.. | .. |
---|
1788 | 2422 | u64 duration = 0; |
---|
1789 | 2423 | bool duration_calculated = false; |
---|
1790 | 2424 | struct thread *thread; |
---|
1791 | | - int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0; |
---|
| 2425 | + int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0, printed = 0; |
---|
| 2426 | + int alignment = trace->args_alignment; |
---|
1792 | 2427 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
---|
1793 | 2428 | struct thread_trace *ttrace; |
---|
1794 | 2429 | |
---|
.. | .. |
---|
1802 | 2437 | |
---|
1803 | 2438 | trace__fprintf_sample(trace, evsel, sample, thread); |
---|
1804 | 2439 | |
---|
1805 | | - if (trace->summary) |
---|
1806 | | - thread__update_stats(ttrace, id, sample); |
---|
1807 | | - |
---|
1808 | 2440 | ret = perf_evsel__sc_tp_uint(evsel, ret, sample); |
---|
1809 | 2441 | |
---|
1810 | | - if (sc->is_open && ret >= 0 && ttrace->filename.pending_open) { |
---|
| 2442 | + if (trace->summary) |
---|
| 2443 | + thread__update_stats(thread, ttrace, id, sample, ret, trace->errno_summary); |
---|
| 2444 | + |
---|
| 2445 | + if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) { |
---|
1811 | 2446 | trace__set_fd_pathname(thread, ret, ttrace->filename.name); |
---|
1812 | 2447 | ttrace->filename.pending_open = false; |
---|
1813 | 2448 | ++trace->stats.vfs_getname; |
---|
.. | .. |
---|
1836 | 2471 | trace__fprintf_entry_head(trace, thread, duration, duration_calculated, ttrace->entry_time, trace->output); |
---|
1837 | 2472 | |
---|
1838 | 2473 | if (ttrace->entry_pending) { |
---|
1839 | | - fprintf(trace->output, "%-70s", ttrace->entry_str); |
---|
| 2474 | + printed = fprintf(trace->output, "%s", ttrace->entry_str); |
---|
1840 | 2475 | } else { |
---|
1841 | | - fprintf(trace->output, " ... ["); |
---|
| 2476 | + printed += fprintf(trace->output, " ... ["); |
---|
1842 | 2477 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); |
---|
1843 | | - fprintf(trace->output, "]: %s()", sc->name); |
---|
| 2478 | + printed += 9; |
---|
| 2479 | + printed += fprintf(trace->output, "]: %s()", sc->name); |
---|
1844 | 2480 | } |
---|
| 2481 | + |
---|
| 2482 | + printed++; /* the closing ')' */ |
---|
| 2483 | + |
---|
| 2484 | + if (alignment > printed) |
---|
| 2485 | + alignment -= printed; |
---|
| 2486 | + else |
---|
| 2487 | + alignment = 0; |
---|
| 2488 | + |
---|
| 2489 | + fprintf(trace->output, ")%*s= ", alignment, " "); |
---|
1845 | 2490 | |
---|
1846 | 2491 | if (sc->fmt == NULL) { |
---|
1847 | 2492 | if (ret < 0) |
---|
1848 | 2493 | goto errno_print; |
---|
1849 | 2494 | signed_print: |
---|
1850 | | - fprintf(trace->output, ") = %ld", ret); |
---|
| 2495 | + fprintf(trace->output, "%ld", ret); |
---|
1851 | 2496 | } else if (ret < 0) { |
---|
1852 | 2497 | errno_print: { |
---|
1853 | 2498 | char bf[STRERR_BUFSIZE]; |
---|
1854 | 2499 | const char *emsg = str_error_r(-ret, bf, sizeof(bf)), |
---|
1855 | 2500 | *e = errno_to_name(evsel, -ret); |
---|
1856 | 2501 | |
---|
1857 | | - fprintf(trace->output, ") = -1 %s %s", e, emsg); |
---|
| 2502 | + fprintf(trace->output, "-1 %s (%s)", e, emsg); |
---|
1858 | 2503 | } |
---|
1859 | 2504 | } else if (ret == 0 && sc->fmt->timeout) |
---|
1860 | | - fprintf(trace->output, ") = 0 Timeout"); |
---|
| 2505 | + fprintf(trace->output, "0 (Timeout)"); |
---|
1861 | 2506 | else if (ttrace->ret_scnprintf) { |
---|
1862 | 2507 | char bf[1024]; |
---|
1863 | 2508 | struct syscall_arg arg = { |
---|
.. | .. |
---|
1867 | 2512 | }; |
---|
1868 | 2513 | ttrace->ret_scnprintf(bf, sizeof(bf), &arg); |
---|
1869 | 2514 | ttrace->ret_scnprintf = NULL; |
---|
1870 | | - fprintf(trace->output, ") = %s", bf); |
---|
| 2515 | + fprintf(trace->output, "%s", bf); |
---|
1871 | 2516 | } else if (sc->fmt->hexret) |
---|
1872 | | - fprintf(trace->output, ") = %#lx", ret); |
---|
| 2517 | + fprintf(trace->output, "%#lx", ret); |
---|
1873 | 2518 | else if (sc->fmt->errpid) { |
---|
1874 | 2519 | struct thread *child = machine__find_thread(trace->host, ret, ret); |
---|
1875 | 2520 | |
---|
1876 | 2521 | if (child != NULL) { |
---|
1877 | | - fprintf(trace->output, ") = %ld", ret); |
---|
| 2522 | + fprintf(trace->output, "%ld", ret); |
---|
1878 | 2523 | if (child->comm_set) |
---|
1879 | 2524 | fprintf(trace->output, " (%s)", thread__comm_str(child)); |
---|
1880 | 2525 | thread__put(child); |
---|
.. | .. |
---|
1884 | 2529 | |
---|
1885 | 2530 | fputc('\n', trace->output); |
---|
1886 | 2531 | |
---|
| 2532 | + /* |
---|
| 2533 | + * We only consider an 'event' for the sake of --max-events a non-filtered |
---|
| 2534 | + * sys_enter + sys_exit and other tracepoint events. |
---|
| 2535 | + */ |
---|
| 2536 | + if (++trace->nr_events_printed == trace->max_events && trace->max_events != ULONG_MAX) |
---|
| 2537 | + interrupted = true; |
---|
| 2538 | + |
---|
1887 | 2539 | if (callchain_ret > 0) |
---|
1888 | 2540 | trace__fprintf_callchain(trace, sample); |
---|
1889 | 2541 | else if (callchain_ret < 0) |
---|
1890 | | - pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
---|
| 2542 | + pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel)); |
---|
1891 | 2543 | out: |
---|
1892 | 2544 | ttrace->entry_pending = false; |
---|
1893 | 2545 | err = 0; |
---|
.. | .. |
---|
1896 | 2548 | return err; |
---|
1897 | 2549 | } |
---|
1898 | 2550 | |
---|
1899 | | -static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2551 | +static int trace__vfs_getname(struct trace *trace, struct evsel *evsel, |
---|
1900 | 2552 | union perf_event *event __maybe_unused, |
---|
1901 | 2553 | struct perf_sample *sample) |
---|
1902 | 2554 | { |
---|
.. | .. |
---|
1905 | 2557 | size_t filename_len, entry_str_len, to_move; |
---|
1906 | 2558 | ssize_t remaining_space; |
---|
1907 | 2559 | char *pos; |
---|
1908 | | - const char *filename = perf_evsel__rawptr(evsel, sample, "pathname"); |
---|
| 2560 | + const char *filename = evsel__rawptr(evsel, sample, "pathname"); |
---|
1909 | 2561 | |
---|
1910 | 2562 | if (!thread) |
---|
1911 | 2563 | goto out; |
---|
.. | .. |
---|
1957 | 2609 | return 0; |
---|
1958 | 2610 | } |
---|
1959 | 2611 | |
---|
1960 | | -static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2612 | +static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel, |
---|
1961 | 2613 | union perf_event *event __maybe_unused, |
---|
1962 | 2614 | struct perf_sample *sample) |
---|
1963 | 2615 | { |
---|
1964 | | - u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
---|
| 2616 | + u64 runtime = evsel__intval(evsel, sample, "runtime"); |
---|
1965 | 2617 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
---|
1966 | 2618 | struct thread *thread = machine__findnew_thread(trace->host, |
---|
1967 | 2619 | sample->pid, |
---|
.. | .. |
---|
1980 | 2632 | out_dump: |
---|
1981 | 2633 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", |
---|
1982 | 2634 | evsel->name, |
---|
1983 | | - perf_evsel__strval(evsel, sample, "comm"), |
---|
1984 | | - (pid_t)perf_evsel__intval(evsel, sample, "pid"), |
---|
| 2635 | + evsel__strval(evsel, sample, "comm"), |
---|
| 2636 | + (pid_t)evsel__intval(evsel, sample, "pid"), |
---|
1985 | 2637 | runtime, |
---|
1986 | | - perf_evsel__intval(evsel, sample, "vruntime")); |
---|
| 2638 | + evsel__intval(evsel, sample, "vruntime")); |
---|
1987 | 2639 | goto out_put; |
---|
1988 | 2640 | } |
---|
1989 | 2641 | |
---|
.. | .. |
---|
2016 | 2668 | { |
---|
2017 | 2669 | binary__fprintf(sample->raw_data, sample->raw_size, 8, |
---|
2018 | 2670 | bpf_output__printer, NULL, trace->output); |
---|
| 2671 | + ++trace->nr_events_printed; |
---|
2019 | 2672 | } |
---|
2020 | 2673 | |
---|
2021 | | -static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, |
---|
| 2674 | +static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, struct perf_sample *sample, |
---|
| 2675 | + struct thread *thread, void *augmented_args, int augmented_args_size) |
---|
| 2676 | +{ |
---|
| 2677 | + char bf[2048]; |
---|
| 2678 | + size_t size = sizeof(bf); |
---|
| 2679 | + struct tep_format_field *field = evsel->tp_format->format.fields; |
---|
| 2680 | + struct syscall_arg_fmt *arg = __evsel__syscall_arg_fmt(evsel); |
---|
| 2681 | + size_t printed = 0; |
---|
| 2682 | + unsigned long val; |
---|
| 2683 | + u8 bit = 1; |
---|
| 2684 | + struct syscall_arg syscall_arg = { |
---|
| 2685 | + .augmented = { |
---|
| 2686 | + .size = augmented_args_size, |
---|
| 2687 | + .args = augmented_args, |
---|
| 2688 | + }, |
---|
| 2689 | + .idx = 0, |
---|
| 2690 | + .mask = 0, |
---|
| 2691 | + .trace = trace, |
---|
| 2692 | + .thread = thread, |
---|
| 2693 | + .show_string_prefix = trace->show_string_prefix, |
---|
| 2694 | + }; |
---|
| 2695 | + |
---|
| 2696 | + for (; field && arg; field = field->next, ++syscall_arg.idx, bit <<= 1, ++arg) { |
---|
| 2697 | + if (syscall_arg.mask & bit) |
---|
| 2698 | + continue; |
---|
| 2699 | + |
---|
| 2700 | + syscall_arg.len = 0; |
---|
| 2701 | + syscall_arg.fmt = arg; |
---|
| 2702 | + if (field->flags & TEP_FIELD_IS_ARRAY) { |
---|
| 2703 | + int offset = field->offset; |
---|
| 2704 | + |
---|
| 2705 | + if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
---|
| 2706 | + offset = format_field__intval(field, sample, evsel->needs_swap); |
---|
| 2707 | + syscall_arg.len = offset >> 16; |
---|
| 2708 | + offset &= 0xffff; |
---|
| 2709 | + } |
---|
| 2710 | + |
---|
| 2711 | + val = (uintptr_t)(sample->raw_data + offset); |
---|
| 2712 | + } else |
---|
| 2713 | + val = format_field__intval(field, sample, evsel->needs_swap); |
---|
| 2714 | + /* |
---|
| 2715 | + * Some syscall args need some mask, most don't and |
---|
| 2716 | + * return val untouched. |
---|
| 2717 | + */ |
---|
| 2718 | + val = syscall_arg_fmt__mask_val(arg, &syscall_arg, val); |
---|
| 2719 | + |
---|
| 2720 | + /* |
---|
| 2721 | + * Suppress this argument if its value is zero and |
---|
| 2722 | + * and we don't have a string associated in an |
---|
| 2723 | + * strarray for it. |
---|
| 2724 | + */ |
---|
| 2725 | + if (val == 0 && |
---|
| 2726 | + !trace->show_zeros && |
---|
| 2727 | + !((arg->show_zero || |
---|
| 2728 | + arg->scnprintf == SCA_STRARRAY || |
---|
| 2729 | + arg->scnprintf == SCA_STRARRAYS) && |
---|
| 2730 | + arg->parm)) |
---|
| 2731 | + continue; |
---|
| 2732 | + |
---|
| 2733 | + printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); |
---|
| 2734 | + |
---|
| 2735 | + /* |
---|
| 2736 | + * XXX Perhaps we should have a show_tp_arg_names, |
---|
| 2737 | + * leaving show_arg_names just for syscalls? |
---|
| 2738 | + */ |
---|
| 2739 | + if (1 || trace->show_arg_names) |
---|
| 2740 | + printed += scnprintf(bf + printed, size - printed, "%s: ", field->name); |
---|
| 2741 | + |
---|
| 2742 | + printed += syscall_arg_fmt__scnprintf_val(arg, bf + printed, size - printed, &syscall_arg, val); |
---|
| 2743 | + } |
---|
| 2744 | + |
---|
| 2745 | + return printed + fprintf(trace->output, "%s", bf); |
---|
| 2746 | +} |
---|
| 2747 | + |
---|
| 2748 | +static int trace__event_handler(struct trace *trace, struct evsel *evsel, |
---|
2022 | 2749 | union perf_event *event __maybe_unused, |
---|
2023 | 2750 | struct perf_sample *sample) |
---|
2024 | 2751 | { |
---|
| 2752 | + struct thread *thread; |
---|
2025 | 2753 | int callchain_ret = 0; |
---|
| 2754 | + /* |
---|
| 2755 | + * Check if we called perf_evsel__disable(evsel) due to, for instance, |
---|
| 2756 | + * this event's max_events having been hit and this is an entry coming |
---|
| 2757 | + * from the ring buffer that we should discard, since the max events |
---|
| 2758 | + * have already been considered/printed. |
---|
| 2759 | + */ |
---|
| 2760 | + if (evsel->disabled) |
---|
| 2761 | + return 0; |
---|
| 2762 | + |
---|
| 2763 | + thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); |
---|
2026 | 2764 | |
---|
2027 | 2765 | if (sample->callchain) { |
---|
2028 | 2766 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); |
---|
.. | .. |
---|
2036 | 2774 | trace__printf_interrupted_entry(trace); |
---|
2037 | 2775 | trace__fprintf_tstamp(trace, sample->time, trace->output); |
---|
2038 | 2776 | |
---|
2039 | | - if (trace->trace_syscalls) |
---|
| 2777 | + if (trace->trace_syscalls && trace->show_duration) |
---|
2040 | 2778 | fprintf(trace->output, "( ): "); |
---|
2041 | 2779 | |
---|
2042 | | - fprintf(trace->output, "%s:", evsel->name); |
---|
| 2780 | + if (thread) |
---|
| 2781 | + trace__fprintf_comm_tid(trace, thread, trace->output); |
---|
2043 | 2782 | |
---|
2044 | | - if (perf_evsel__is_bpf_output(evsel)) { |
---|
2045 | | - if (evsel == trace->syscalls.events.augmented) |
---|
| 2783 | + if (evsel == trace->syscalls.events.augmented) { |
---|
| 2784 | + int id = perf_evsel__sc_tp_uint(evsel, id, sample); |
---|
| 2785 | + struct syscall *sc = trace__syscall_info(trace, evsel, id); |
---|
| 2786 | + |
---|
| 2787 | + if (sc) { |
---|
| 2788 | + fprintf(trace->output, "%s(", sc->name); |
---|
2046 | 2789 | trace__fprintf_sys_enter(trace, evsel, sample); |
---|
2047 | | - else |
---|
2048 | | - bpf_output__fprintf(trace, sample); |
---|
| 2790 | + fputc(')', trace->output); |
---|
| 2791 | + goto newline; |
---|
| 2792 | + } |
---|
| 2793 | + |
---|
| 2794 | + /* |
---|
| 2795 | + * XXX: Not having the associated syscall info or not finding/adding |
---|
| 2796 | + * the thread should never happen, but if it does... |
---|
| 2797 | + * fall thru and print it as a bpf_output event. |
---|
| 2798 | + */ |
---|
| 2799 | + } |
---|
| 2800 | + |
---|
| 2801 | + fprintf(trace->output, "%s(", evsel->name); |
---|
| 2802 | + |
---|
| 2803 | + if (evsel__is_bpf_output(evsel)) { |
---|
| 2804 | + bpf_output__fprintf(trace, sample); |
---|
2049 | 2805 | } else if (evsel->tp_format) { |
---|
2050 | 2806 | if (strncmp(evsel->tp_format->name, "sys_enter_", 10) || |
---|
2051 | 2807 | trace__fprintf_sys_enter(trace, evsel, sample)) { |
---|
2052 | | - event_format__fprintf(evsel->tp_format, sample->cpu, |
---|
2053 | | - sample->raw_data, sample->raw_size, |
---|
2054 | | - trace->output); |
---|
| 2808 | + if (trace->libtraceevent_print) { |
---|
| 2809 | + event_format__fprintf(evsel->tp_format, sample->cpu, |
---|
| 2810 | + sample->raw_data, sample->raw_size, |
---|
| 2811 | + trace->output); |
---|
| 2812 | + } else { |
---|
| 2813 | + trace__fprintf_tp_fields(trace, evsel, sample, thread, NULL, 0); |
---|
| 2814 | + } |
---|
2055 | 2815 | } |
---|
2056 | 2816 | } |
---|
2057 | 2817 | |
---|
2058 | | - fprintf(trace->output, "\n"); |
---|
| 2818 | +newline: |
---|
| 2819 | + fprintf(trace->output, ")\n"); |
---|
2059 | 2820 | |
---|
2060 | 2821 | if (callchain_ret > 0) |
---|
2061 | 2822 | trace__fprintf_callchain(trace, sample); |
---|
2062 | 2823 | else if (callchain_ret < 0) |
---|
2063 | | - pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
---|
| 2824 | + pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel)); |
---|
| 2825 | + |
---|
| 2826 | + ++trace->nr_events_printed; |
---|
| 2827 | + |
---|
| 2828 | + if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { |
---|
| 2829 | + evsel__disable(evsel); |
---|
| 2830 | + evsel__close(evsel); |
---|
| 2831 | + } |
---|
2064 | 2832 | out: |
---|
| 2833 | + thread__put(thread); |
---|
2065 | 2834 | return 0; |
---|
2066 | 2835 | } |
---|
2067 | 2836 | |
---|
.. | .. |
---|
2083 | 2852 | } |
---|
2084 | 2853 | |
---|
2085 | 2854 | static int trace__pgfault(struct trace *trace, |
---|
2086 | | - struct perf_evsel *evsel, |
---|
| 2855 | + struct evsel *evsel, |
---|
2087 | 2856 | union perf_event *event __maybe_unused, |
---|
2088 | 2857 | struct perf_sample *sample) |
---|
2089 | 2858 | { |
---|
.. | .. |
---|
2109 | 2878 | if (ttrace == NULL) |
---|
2110 | 2879 | goto out_put; |
---|
2111 | 2880 | |
---|
2112 | | - if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) |
---|
| 2881 | + if (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) |
---|
2113 | 2882 | ttrace->pfmaj++; |
---|
2114 | 2883 | else |
---|
2115 | 2884 | ttrace->pfmin++; |
---|
.. | .. |
---|
2122 | 2891 | trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output); |
---|
2123 | 2892 | |
---|
2124 | 2893 | fprintf(trace->output, "%sfault [", |
---|
2125 | | - evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? |
---|
| 2894 | + evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? |
---|
2126 | 2895 | "maj" : "min"); |
---|
2127 | 2896 | |
---|
2128 | 2897 | print_location(trace->output, sample, &al, false, true); |
---|
.. | .. |
---|
2147 | 2916 | if (callchain_ret > 0) |
---|
2148 | 2917 | trace__fprintf_callchain(trace, sample); |
---|
2149 | 2918 | else if (callchain_ret < 0) |
---|
2150 | | - pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
---|
| 2919 | + pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel)); |
---|
| 2920 | + |
---|
| 2921 | + ++trace->nr_events_printed; |
---|
2151 | 2922 | out: |
---|
2152 | 2923 | err = 0; |
---|
2153 | 2924 | out_put: |
---|
.. | .. |
---|
2156 | 2927 | } |
---|
2157 | 2928 | |
---|
2158 | 2929 | static void trace__set_base_time(struct trace *trace, |
---|
2159 | | - struct perf_evsel *evsel, |
---|
| 2930 | + struct evsel *evsel, |
---|
2160 | 2931 | struct perf_sample *sample) |
---|
2161 | 2932 | { |
---|
2162 | 2933 | /* |
---|
.. | .. |
---|
2168 | 2939 | * appears in our event stream (vfs_getname comes to mind). |
---|
2169 | 2940 | */ |
---|
2170 | 2941 | if (trace->base_time == 0 && !trace->full_time && |
---|
2171 | | - (evsel->attr.sample_type & PERF_SAMPLE_TIME)) |
---|
| 2942 | + (evsel->core.attr.sample_type & PERF_SAMPLE_TIME)) |
---|
2172 | 2943 | trace->base_time = sample->time; |
---|
2173 | 2944 | } |
---|
2174 | 2945 | |
---|
2175 | 2946 | static int trace__process_sample(struct perf_tool *tool, |
---|
2176 | 2947 | union perf_event *event, |
---|
2177 | 2948 | struct perf_sample *sample, |
---|
2178 | | - struct perf_evsel *evsel, |
---|
| 2949 | + struct evsel *evsel, |
---|
2179 | 2950 | struct machine *machine __maybe_unused) |
---|
2180 | 2951 | { |
---|
2181 | 2952 | struct trace *trace = container_of(tool, struct trace, tool); |
---|
.. | .. |
---|
2209 | 2980 | "-m", "1024", |
---|
2210 | 2981 | "-c", "1", |
---|
2211 | 2982 | }; |
---|
2212 | | - |
---|
| 2983 | + pid_t pid = getpid(); |
---|
| 2984 | + char *filter = asprintf__tp_filter_pids(1, &pid); |
---|
2213 | 2985 | const char * const sc_args[] = { "-e", }; |
---|
2214 | 2986 | unsigned int sc_args_nr = ARRAY_SIZE(sc_args); |
---|
2215 | 2987 | const char * const majpf_args[] = { "-e", "major-faults" }; |
---|
2216 | 2988 | unsigned int majpf_args_nr = ARRAY_SIZE(majpf_args); |
---|
2217 | 2989 | const char * const minpf_args[] = { "-e", "minor-faults" }; |
---|
2218 | 2990 | unsigned int minpf_args_nr = ARRAY_SIZE(minpf_args); |
---|
| 2991 | + int err = -1; |
---|
2219 | 2992 | |
---|
2220 | | - /* +1 is for the event string below */ |
---|
2221 | | - rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 1 + |
---|
| 2993 | + /* +3 is for the event string below and the pid filter */ |
---|
| 2994 | + rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 3 + |
---|
2222 | 2995 | majpf_args_nr + minpf_args_nr + argc; |
---|
2223 | 2996 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
---|
2224 | 2997 | |
---|
2225 | | - if (rec_argv == NULL) |
---|
2226 | | - return -ENOMEM; |
---|
| 2998 | + if (rec_argv == NULL || filter == NULL) |
---|
| 2999 | + goto out_free; |
---|
2227 | 3000 | |
---|
2228 | 3001 | j = 0; |
---|
2229 | 3002 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
---|
.. | .. |
---|
2240 | 3013 | rec_argv[j++] = "syscalls:sys_enter,syscalls:sys_exit"; |
---|
2241 | 3014 | else { |
---|
2242 | 3015 | pr_err("Neither raw_syscalls nor syscalls events exist.\n"); |
---|
2243 | | - free(rec_argv); |
---|
2244 | | - return -1; |
---|
| 3016 | + goto out_free; |
---|
2245 | 3017 | } |
---|
2246 | 3018 | } |
---|
| 3019 | + |
---|
| 3020 | + rec_argv[j++] = "--filter"; |
---|
| 3021 | + rec_argv[j++] = filter; |
---|
2247 | 3022 | |
---|
2248 | 3023 | if (trace->trace_pgfaults & TRACE_PFMAJ) |
---|
2249 | 3024 | for (i = 0; i < majpf_args_nr; i++) |
---|
.. | .. |
---|
2256 | 3031 | for (i = 0; i < (unsigned int)argc; i++) |
---|
2257 | 3032 | rec_argv[j++] = argv[i]; |
---|
2258 | 3033 | |
---|
2259 | | - return cmd_record(j, rec_argv); |
---|
| 3034 | + err = cmd_record(j, rec_argv); |
---|
| 3035 | +out_free: |
---|
| 3036 | + free(filter); |
---|
| 3037 | + free(rec_argv); |
---|
| 3038 | + return err; |
---|
2260 | 3039 | } |
---|
2261 | 3040 | |
---|
2262 | 3041 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); |
---|
2263 | 3042 | |
---|
2264 | | -static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist) |
---|
| 3043 | +static bool evlist__add_vfs_getname(struct evlist *evlist) |
---|
2265 | 3044 | { |
---|
2266 | 3045 | bool found = false; |
---|
2267 | | - struct perf_evsel *evsel, *tmp; |
---|
2268 | | - struct parse_events_error err = { .idx = 0, }; |
---|
2269 | | - int ret = parse_events(evlist, "probe:vfs_getname*", &err); |
---|
| 3046 | + struct evsel *evsel, *tmp; |
---|
| 3047 | + struct parse_events_error err; |
---|
| 3048 | + int ret; |
---|
2270 | 3049 | |
---|
2271 | | - if (ret) |
---|
| 3050 | + bzero(&err, sizeof(err)); |
---|
| 3051 | + ret = parse_events(evlist, "probe:vfs_getname*", &err); |
---|
| 3052 | + if (ret) { |
---|
| 3053 | + free(err.str); |
---|
| 3054 | + free(err.help); |
---|
| 3055 | + free(err.first_str); |
---|
| 3056 | + free(err.first_help); |
---|
2272 | 3057 | return false; |
---|
| 3058 | + } |
---|
2273 | 3059 | |
---|
2274 | 3060 | evlist__for_each_entry_safe(evlist, evsel, tmp) { |
---|
2275 | | - if (!strstarts(perf_evsel__name(evsel), "probe:vfs_getname")) |
---|
| 3061 | + if (!strstarts(evsel__name(evsel), "probe:vfs_getname")) |
---|
2276 | 3062 | continue; |
---|
2277 | 3063 | |
---|
2278 | | - if (perf_evsel__field(evsel, "pathname")) { |
---|
| 3064 | + if (evsel__field(evsel, "pathname")) { |
---|
2279 | 3065 | evsel->handler = trace__vfs_getname; |
---|
2280 | 3066 | found = true; |
---|
2281 | 3067 | continue; |
---|
2282 | 3068 | } |
---|
2283 | 3069 | |
---|
2284 | | - list_del_init(&evsel->node); |
---|
| 3070 | + list_del_init(&evsel->core.node); |
---|
2285 | 3071 | evsel->evlist = NULL; |
---|
2286 | | - perf_evsel__delete(evsel); |
---|
| 3072 | + evsel__delete(evsel); |
---|
2287 | 3073 | } |
---|
2288 | 3074 | |
---|
2289 | 3075 | return found; |
---|
2290 | 3076 | } |
---|
2291 | 3077 | |
---|
2292 | | -static struct perf_evsel *perf_evsel__new_pgfault(u64 config) |
---|
| 3078 | +static struct evsel *evsel__new_pgfault(u64 config) |
---|
2293 | 3079 | { |
---|
2294 | | - struct perf_evsel *evsel; |
---|
| 3080 | + struct evsel *evsel; |
---|
2295 | 3081 | struct perf_event_attr attr = { |
---|
2296 | 3082 | .type = PERF_TYPE_SOFTWARE, |
---|
2297 | 3083 | .mmap_data = 1, |
---|
.. | .. |
---|
2302 | 3088 | |
---|
2303 | 3089 | event_attr_init(&attr); |
---|
2304 | 3090 | |
---|
2305 | | - evsel = perf_evsel__new(&attr); |
---|
| 3091 | + evsel = evsel__new(&attr); |
---|
2306 | 3092 | if (evsel) |
---|
2307 | 3093 | evsel->handler = trace__pgfault; |
---|
2308 | 3094 | |
---|
.. | .. |
---|
2312 | 3098 | static void trace__handle_event(struct trace *trace, union perf_event *event, struct perf_sample *sample) |
---|
2313 | 3099 | { |
---|
2314 | 3100 | const u32 type = event->header.type; |
---|
2315 | | - struct perf_evsel *evsel; |
---|
| 3101 | + struct evsel *evsel; |
---|
2316 | 3102 | |
---|
2317 | 3103 | if (type != PERF_RECORD_SAMPLE) { |
---|
2318 | 3104 | trace__process_event(trace, trace->host, event, sample); |
---|
.. | .. |
---|
2325 | 3111 | return; |
---|
2326 | 3112 | } |
---|
2327 | 3113 | |
---|
| 3114 | + if (evswitch__discard(&trace->evswitch, evsel)) |
---|
| 3115 | + return; |
---|
| 3116 | + |
---|
2328 | 3117 | trace__set_base_time(trace, evsel, sample); |
---|
2329 | 3118 | |
---|
2330 | | - if (evsel->attr.type == PERF_TYPE_TRACEPOINT && |
---|
| 3119 | + if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT && |
---|
2331 | 3120 | sample->raw_data == NULL) { |
---|
2332 | 3121 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
---|
2333 | | - perf_evsel__name(evsel), sample->tid, |
---|
| 3122 | + evsel__name(evsel), sample->tid, |
---|
2334 | 3123 | sample->cpu, sample->raw_size); |
---|
2335 | 3124 | } else { |
---|
2336 | 3125 | tracepoint_handler handler = evsel->handler; |
---|
2337 | 3126 | handler(trace, evsel, event, sample); |
---|
2338 | 3127 | } |
---|
| 3128 | + |
---|
| 3129 | + if (trace->nr_events_printed >= trace->max_events && trace->max_events != ULONG_MAX) |
---|
| 3130 | + interrupted = true; |
---|
2339 | 3131 | } |
---|
2340 | 3132 | |
---|
2341 | 3133 | static int trace__add_syscall_newtp(struct trace *trace) |
---|
2342 | 3134 | { |
---|
2343 | 3135 | int ret = -1; |
---|
2344 | | - struct perf_evlist *evlist = trace->evlist; |
---|
2345 | | - struct perf_evsel *sys_enter, *sys_exit; |
---|
| 3136 | + struct evlist *evlist = trace->evlist; |
---|
| 3137 | + struct evsel *sys_enter, *sys_exit; |
---|
2346 | 3138 | |
---|
2347 | 3139 | sys_enter = perf_evsel__raw_syscall_newtp("sys_enter", trace__sys_enter); |
---|
2348 | 3140 | if (sys_enter == NULL) |
---|
.. | .. |
---|
2358 | 3150 | if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) |
---|
2359 | 3151 | goto out_delete_sys_exit; |
---|
2360 | 3152 | |
---|
2361 | | - perf_evsel__config_callchain(sys_enter, &trace->opts, &callchain_param); |
---|
2362 | | - perf_evsel__config_callchain(sys_exit, &trace->opts, &callchain_param); |
---|
| 3153 | + evsel__config_callchain(sys_enter, &trace->opts, &callchain_param); |
---|
| 3154 | + evsel__config_callchain(sys_exit, &trace->opts, &callchain_param); |
---|
2363 | 3155 | |
---|
2364 | | - perf_evlist__add(evlist, sys_enter); |
---|
2365 | | - perf_evlist__add(evlist, sys_exit); |
---|
| 3156 | + evlist__add(evlist, sys_enter); |
---|
| 3157 | + evlist__add(evlist, sys_exit); |
---|
2366 | 3158 | |
---|
2367 | 3159 | if (callchain_param.enabled && !trace->kernel_syscallchains) { |
---|
2368 | 3160 | /* |
---|
.. | .. |
---|
2370 | 3162 | * leading to the syscall, allow overriding that for |
---|
2371 | 3163 | * debugging reasons using --kernel_syscall_callchains |
---|
2372 | 3164 | */ |
---|
2373 | | - sys_exit->attr.exclude_callchain_kernel = 1; |
---|
| 3165 | + sys_exit->core.attr.exclude_callchain_kernel = 1; |
---|
2374 | 3166 | } |
---|
2375 | 3167 | |
---|
2376 | 3168 | trace->syscalls.events.sys_enter = sys_enter; |
---|
.. | .. |
---|
2381 | 3173 | return ret; |
---|
2382 | 3174 | |
---|
2383 | 3175 | out_delete_sys_exit: |
---|
2384 | | - perf_evsel__delete_priv(sys_exit); |
---|
| 3176 | + evsel__delete_priv(sys_exit); |
---|
2385 | 3177 | out_delete_sys_enter: |
---|
2386 | | - perf_evsel__delete_priv(sys_enter); |
---|
| 3178 | + evsel__delete_priv(sys_enter); |
---|
2387 | 3179 | goto out; |
---|
2388 | 3180 | } |
---|
2389 | 3181 | |
---|
2390 | | -static int trace__set_ev_qualifier_filter(struct trace *trace) |
---|
| 3182 | +static int trace__set_ev_qualifier_tp_filter(struct trace *trace) |
---|
2391 | 3183 | { |
---|
2392 | 3184 | int err = -1; |
---|
2393 | | - struct perf_evsel *sys_exit; |
---|
| 3185 | + struct evsel *sys_exit; |
---|
2394 | 3186 | char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier, |
---|
2395 | 3187 | trace->ev_qualifier_ids.nr, |
---|
2396 | 3188 | trace->ev_qualifier_ids.entries); |
---|
.. | .. |
---|
2398 | 3190 | if (filter == NULL) |
---|
2399 | 3191 | goto out_enomem; |
---|
2400 | 3192 | |
---|
2401 | | - if (!perf_evsel__append_tp_filter(trace->syscalls.events.sys_enter, |
---|
2402 | | - filter)) { |
---|
| 3193 | + if (!evsel__append_tp_filter(trace->syscalls.events.sys_enter, filter)) { |
---|
2403 | 3194 | sys_exit = trace->syscalls.events.sys_exit; |
---|
2404 | | - err = perf_evsel__append_tp_filter(sys_exit, filter); |
---|
| 3195 | + err = evsel__append_tp_filter(sys_exit, filter); |
---|
2405 | 3196 | } |
---|
2406 | 3197 | |
---|
2407 | 3198 | free(filter); |
---|
.. | .. |
---|
2412 | 3203 | goto out; |
---|
2413 | 3204 | } |
---|
2414 | 3205 | |
---|
| 3206 | +#ifdef HAVE_LIBBPF_SUPPORT |
---|
| 3207 | +static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace, const char *name) |
---|
| 3208 | +{ |
---|
| 3209 | + if (trace->bpf_obj == NULL) |
---|
| 3210 | + return NULL; |
---|
| 3211 | + |
---|
| 3212 | + return bpf_object__find_map_by_name(trace->bpf_obj, name); |
---|
| 3213 | +} |
---|
| 3214 | + |
---|
| 3215 | +static void trace__set_bpf_map_filtered_pids(struct trace *trace) |
---|
| 3216 | +{ |
---|
| 3217 | + trace->filter_pids.map = trace__find_bpf_map_by_name(trace, "pids_filtered"); |
---|
| 3218 | +} |
---|
| 3219 | + |
---|
| 3220 | +static void trace__set_bpf_map_syscalls(struct trace *trace) |
---|
| 3221 | +{ |
---|
| 3222 | + trace->syscalls.map = trace__find_bpf_map_by_name(trace, "syscalls"); |
---|
| 3223 | + trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter"); |
---|
| 3224 | + trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit"); |
---|
| 3225 | +} |
---|
| 3226 | + |
---|
| 3227 | +static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name) |
---|
| 3228 | +{ |
---|
| 3229 | + if (trace->bpf_obj == NULL) |
---|
| 3230 | + return NULL; |
---|
| 3231 | + |
---|
| 3232 | + return bpf_object__find_program_by_title(trace->bpf_obj, name); |
---|
| 3233 | +} |
---|
| 3234 | + |
---|
| 3235 | +static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, struct syscall *sc, |
---|
| 3236 | + const char *prog_name, const char *type) |
---|
| 3237 | +{ |
---|
| 3238 | + struct bpf_program *prog; |
---|
| 3239 | + |
---|
| 3240 | + if (prog_name == NULL) { |
---|
| 3241 | + char default_prog_name[256]; |
---|
| 3242 | + scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->name); |
---|
| 3243 | + prog = trace__find_bpf_program_by_title(trace, default_prog_name); |
---|
| 3244 | + if (prog != NULL) |
---|
| 3245 | + goto out_found; |
---|
| 3246 | + if (sc->fmt && sc->fmt->alias) { |
---|
| 3247 | + scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->fmt->alias); |
---|
| 3248 | + prog = trace__find_bpf_program_by_title(trace, default_prog_name); |
---|
| 3249 | + if (prog != NULL) |
---|
| 3250 | + goto out_found; |
---|
| 3251 | + } |
---|
| 3252 | + goto out_unaugmented; |
---|
| 3253 | + } |
---|
| 3254 | + |
---|
| 3255 | + prog = trace__find_bpf_program_by_title(trace, prog_name); |
---|
| 3256 | + |
---|
| 3257 | + if (prog != NULL) { |
---|
| 3258 | +out_found: |
---|
| 3259 | + return prog; |
---|
| 3260 | + } |
---|
| 3261 | + |
---|
| 3262 | + pr_debug("Couldn't find BPF prog \"%s\" to associate with syscalls:sys_%s_%s, not augmenting it\n", |
---|
| 3263 | + prog_name, type, sc->name); |
---|
| 3264 | +out_unaugmented: |
---|
| 3265 | + return trace->syscalls.unaugmented_prog; |
---|
| 3266 | +} |
---|
| 3267 | + |
---|
| 3268 | +static void trace__init_syscall_bpf_progs(struct trace *trace, int id) |
---|
| 3269 | +{ |
---|
| 3270 | + struct syscall *sc = trace__syscall_info(trace, NULL, id); |
---|
| 3271 | + |
---|
| 3272 | + if (sc == NULL) |
---|
| 3273 | + return; |
---|
| 3274 | + |
---|
| 3275 | + sc->bpf_prog.sys_enter = trace__find_syscall_bpf_prog(trace, sc, sc->fmt ? sc->fmt->bpf_prog_name.sys_enter : NULL, "enter"); |
---|
| 3276 | + sc->bpf_prog.sys_exit = trace__find_syscall_bpf_prog(trace, sc, sc->fmt ? sc->fmt->bpf_prog_name.sys_exit : NULL, "exit"); |
---|
| 3277 | +} |
---|
| 3278 | + |
---|
| 3279 | +static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int id) |
---|
| 3280 | +{ |
---|
| 3281 | + struct syscall *sc = trace__syscall_info(trace, NULL, id); |
---|
| 3282 | + return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->syscalls.unaugmented_prog); |
---|
| 3283 | +} |
---|
| 3284 | + |
---|
| 3285 | +static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id) |
---|
| 3286 | +{ |
---|
| 3287 | + struct syscall *sc = trace__syscall_info(trace, NULL, id); |
---|
| 3288 | + return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->syscalls.unaugmented_prog); |
---|
| 3289 | +} |
---|
| 3290 | + |
---|
| 3291 | +static void trace__init_bpf_map_syscall_args(struct trace *trace, int id, struct bpf_map_syscall_entry *entry) |
---|
| 3292 | +{ |
---|
| 3293 | + struct syscall *sc = trace__syscall_info(trace, NULL, id); |
---|
| 3294 | + int arg = 0; |
---|
| 3295 | + |
---|
| 3296 | + if (sc == NULL) |
---|
| 3297 | + goto out; |
---|
| 3298 | + |
---|
| 3299 | + for (; arg < sc->nr_args; ++arg) { |
---|
| 3300 | + entry->string_args_len[arg] = 0; |
---|
| 3301 | + if (sc->arg_fmt[arg].scnprintf == SCA_FILENAME) { |
---|
| 3302 | + /* Should be set like strace -s strsize */ |
---|
| 3303 | + entry->string_args_len[arg] = PATH_MAX; |
---|
| 3304 | + } |
---|
| 3305 | + } |
---|
| 3306 | +out: |
---|
| 3307 | + for (; arg < 6; ++arg) |
---|
| 3308 | + entry->string_args_len[arg] = 0; |
---|
| 3309 | +} |
---|
| 3310 | +static int trace__set_ev_qualifier_bpf_filter(struct trace *trace) |
---|
| 3311 | +{ |
---|
| 3312 | + int fd = bpf_map__fd(trace->syscalls.map); |
---|
| 3313 | + struct bpf_map_syscall_entry value = { |
---|
| 3314 | + .enabled = !trace->not_ev_qualifier, |
---|
| 3315 | + }; |
---|
| 3316 | + int err = 0; |
---|
| 3317 | + size_t i; |
---|
| 3318 | + |
---|
| 3319 | + for (i = 0; i < trace->ev_qualifier_ids.nr; ++i) { |
---|
| 3320 | + int key = trace->ev_qualifier_ids.entries[i]; |
---|
| 3321 | + |
---|
| 3322 | + if (value.enabled) { |
---|
| 3323 | + trace__init_bpf_map_syscall_args(trace, key, &value); |
---|
| 3324 | + trace__init_syscall_bpf_progs(trace, key); |
---|
| 3325 | + } |
---|
| 3326 | + |
---|
| 3327 | + err = bpf_map_update_elem(fd, &key, &value, BPF_EXIST); |
---|
| 3328 | + if (err) |
---|
| 3329 | + break; |
---|
| 3330 | + } |
---|
| 3331 | + |
---|
| 3332 | + return err; |
---|
| 3333 | +} |
---|
| 3334 | + |
---|
| 3335 | +static int __trace__init_syscalls_bpf_map(struct trace *trace, bool enabled) |
---|
| 3336 | +{ |
---|
| 3337 | + int fd = bpf_map__fd(trace->syscalls.map); |
---|
| 3338 | + struct bpf_map_syscall_entry value = { |
---|
| 3339 | + .enabled = enabled, |
---|
| 3340 | + }; |
---|
| 3341 | + int err = 0, key; |
---|
| 3342 | + |
---|
| 3343 | + for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) { |
---|
| 3344 | + if (enabled) |
---|
| 3345 | + trace__init_bpf_map_syscall_args(trace, key, &value); |
---|
| 3346 | + |
---|
| 3347 | + err = bpf_map_update_elem(fd, &key, &value, BPF_ANY); |
---|
| 3348 | + if (err) |
---|
| 3349 | + break; |
---|
| 3350 | + } |
---|
| 3351 | + |
---|
| 3352 | + return err; |
---|
| 3353 | +} |
---|
| 3354 | + |
---|
| 3355 | +static int trace__init_syscalls_bpf_map(struct trace *trace) |
---|
| 3356 | +{ |
---|
| 3357 | + bool enabled = true; |
---|
| 3358 | + |
---|
| 3359 | + if (trace->ev_qualifier_ids.nr) |
---|
| 3360 | + enabled = trace->not_ev_qualifier; |
---|
| 3361 | + |
---|
| 3362 | + return __trace__init_syscalls_bpf_map(trace, enabled); |
---|
| 3363 | +} |
---|
| 3364 | + |
---|
| 3365 | +static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc) |
---|
| 3366 | +{ |
---|
| 3367 | + struct tep_format_field *field, *candidate_field; |
---|
| 3368 | + int id; |
---|
| 3369 | + |
---|
| 3370 | + /* |
---|
| 3371 | + * We're only interested in syscalls that have a pointer: |
---|
| 3372 | + */ |
---|
| 3373 | + for (field = sc->args; field; field = field->next) { |
---|
| 3374 | + if (field->flags & TEP_FIELD_IS_POINTER) |
---|
| 3375 | + goto try_to_find_pair; |
---|
| 3376 | + } |
---|
| 3377 | + |
---|
| 3378 | + return NULL; |
---|
| 3379 | + |
---|
| 3380 | +try_to_find_pair: |
---|
| 3381 | + for (id = 0; id < trace->sctbl->syscalls.nr_entries; ++id) { |
---|
| 3382 | + struct syscall *pair = trace__syscall_info(trace, NULL, id); |
---|
| 3383 | + struct bpf_program *pair_prog; |
---|
| 3384 | + bool is_candidate = false; |
---|
| 3385 | + |
---|
| 3386 | + if (pair == NULL || pair == sc || |
---|
| 3387 | + pair->bpf_prog.sys_enter == trace->syscalls.unaugmented_prog) |
---|
| 3388 | + continue; |
---|
| 3389 | + |
---|
| 3390 | + for (field = sc->args, candidate_field = pair->args; |
---|
| 3391 | + field && candidate_field; field = field->next, candidate_field = candidate_field->next) { |
---|
| 3392 | + bool is_pointer = field->flags & TEP_FIELD_IS_POINTER, |
---|
| 3393 | + candidate_is_pointer = candidate_field->flags & TEP_FIELD_IS_POINTER; |
---|
| 3394 | + |
---|
| 3395 | + if (is_pointer) { |
---|
| 3396 | + if (!candidate_is_pointer) { |
---|
| 3397 | + // The candidate just doesn't copies our pointer arg, might copy other pointers we want. |
---|
| 3398 | + continue; |
---|
| 3399 | + } |
---|
| 3400 | + } else { |
---|
| 3401 | + if (candidate_is_pointer) { |
---|
| 3402 | + // The candidate might copy a pointer we don't have, skip it. |
---|
| 3403 | + goto next_candidate; |
---|
| 3404 | + } |
---|
| 3405 | + continue; |
---|
| 3406 | + } |
---|
| 3407 | + |
---|
| 3408 | + if (strcmp(field->type, candidate_field->type)) |
---|
| 3409 | + goto next_candidate; |
---|
| 3410 | + |
---|
| 3411 | + is_candidate = true; |
---|
| 3412 | + } |
---|
| 3413 | + |
---|
| 3414 | + if (!is_candidate) |
---|
| 3415 | + goto next_candidate; |
---|
| 3416 | + |
---|
| 3417 | + /* |
---|
| 3418 | + * Check if the tentative pair syscall augmenter has more pointers, if it has, |
---|
| 3419 | + * then it may be collecting that and we then can't use it, as it would collect |
---|
| 3420 | + * more than what is common to the two syscalls. |
---|
| 3421 | + */ |
---|
| 3422 | + if (candidate_field) { |
---|
| 3423 | + for (candidate_field = candidate_field->next; candidate_field; candidate_field = candidate_field->next) |
---|
| 3424 | + if (candidate_field->flags & TEP_FIELD_IS_POINTER) |
---|
| 3425 | + goto next_candidate; |
---|
| 3426 | + } |
---|
| 3427 | + |
---|
| 3428 | + pair_prog = pair->bpf_prog.sys_enter; |
---|
| 3429 | + /* |
---|
| 3430 | + * If the pair isn't enabled, then its bpf_prog.sys_enter will not |
---|
| 3431 | + * have been searched for, so search it here and if it returns the |
---|
| 3432 | + * unaugmented one, then ignore it, otherwise we'll reuse that BPF |
---|
| 3433 | + * program for a filtered syscall on a non-filtered one. |
---|
| 3434 | + * |
---|
| 3435 | + * For instance, we have "!syscalls:sys_enter_renameat" and that is |
---|
| 3436 | + * useful for "renameat2". |
---|
| 3437 | + */ |
---|
| 3438 | + if (pair_prog == NULL) { |
---|
| 3439 | + pair_prog = trace__find_syscall_bpf_prog(trace, pair, pair->fmt ? pair->fmt->bpf_prog_name.sys_enter : NULL, "enter"); |
---|
| 3440 | + if (pair_prog == trace->syscalls.unaugmented_prog) |
---|
| 3441 | + goto next_candidate; |
---|
| 3442 | + } |
---|
| 3443 | + |
---|
| 3444 | + pr_debug("Reusing \"%s\" BPF sys_enter augmenter for \"%s\"\n", pair->name, sc->name); |
---|
| 3445 | + return pair_prog; |
---|
| 3446 | + next_candidate: |
---|
| 3447 | + continue; |
---|
| 3448 | + } |
---|
| 3449 | + |
---|
| 3450 | + return NULL; |
---|
| 3451 | +} |
---|
| 3452 | + |
---|
| 3453 | +static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace) |
---|
| 3454 | +{ |
---|
| 3455 | + int map_enter_fd = bpf_map__fd(trace->syscalls.prog_array.sys_enter), |
---|
| 3456 | + map_exit_fd = bpf_map__fd(trace->syscalls.prog_array.sys_exit); |
---|
| 3457 | + int err = 0, key; |
---|
| 3458 | + |
---|
| 3459 | + for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) { |
---|
| 3460 | + int prog_fd; |
---|
| 3461 | + |
---|
| 3462 | + if (!trace__syscall_enabled(trace, key)) |
---|
| 3463 | + continue; |
---|
| 3464 | + |
---|
| 3465 | + trace__init_syscall_bpf_progs(trace, key); |
---|
| 3466 | + |
---|
| 3467 | + // It'll get at least the "!raw_syscalls:unaugmented" |
---|
| 3468 | + prog_fd = trace__bpf_prog_sys_enter_fd(trace, key); |
---|
| 3469 | + err = bpf_map_update_elem(map_enter_fd, &key, &prog_fd, BPF_ANY); |
---|
| 3470 | + if (err) |
---|
| 3471 | + break; |
---|
| 3472 | + prog_fd = trace__bpf_prog_sys_exit_fd(trace, key); |
---|
| 3473 | + err = bpf_map_update_elem(map_exit_fd, &key, &prog_fd, BPF_ANY); |
---|
| 3474 | + if (err) |
---|
| 3475 | + break; |
---|
| 3476 | + } |
---|
| 3477 | + |
---|
| 3478 | + /* |
---|
| 3479 | + * Now lets do a second pass looking for enabled syscalls without |
---|
| 3480 | + * an augmenter that have a signature that is a superset of another |
---|
| 3481 | + * syscall with an augmenter so that we can auto-reuse it. |
---|
| 3482 | + * |
---|
| 3483 | + * I.e. if we have an augmenter for the "open" syscall that has |
---|
| 3484 | + * this signature: |
---|
| 3485 | + * |
---|
| 3486 | + * int open(const char *pathname, int flags, mode_t mode); |
---|
| 3487 | + * |
---|
| 3488 | + * I.e. that will collect just the first string argument, then we |
---|
| 3489 | + * can reuse it for the 'creat' syscall, that has this signature: |
---|
| 3490 | + * |
---|
| 3491 | + * int creat(const char *pathname, mode_t mode); |
---|
| 3492 | + * |
---|
| 3493 | + * and for: |
---|
| 3494 | + * |
---|
| 3495 | + * int stat(const char *pathname, struct stat *statbuf); |
---|
| 3496 | + * int lstat(const char *pathname, struct stat *statbuf); |
---|
| 3497 | + * |
---|
| 3498 | + * Because the 'open' augmenter will collect the first arg as a string, |
---|
| 3499 | + * and leave alone all the other args, which already helps with |
---|
| 3500 | + * beautifying 'stat' and 'lstat''s pathname arg. |
---|
| 3501 | + * |
---|
| 3502 | + * Then, in time, when 'stat' gets an augmenter that collects both |
---|
| 3503 | + * first and second arg (this one on the raw_syscalls:sys_exit prog |
---|
| 3504 | + * array tail call, then that one will be used. |
---|
| 3505 | + */ |
---|
| 3506 | + for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) { |
---|
| 3507 | + struct syscall *sc = trace__syscall_info(trace, NULL, key); |
---|
| 3508 | + struct bpf_program *pair_prog; |
---|
| 3509 | + int prog_fd; |
---|
| 3510 | + |
---|
| 3511 | + if (sc == NULL || sc->bpf_prog.sys_enter == NULL) |
---|
| 3512 | + continue; |
---|
| 3513 | + |
---|
| 3514 | + /* |
---|
| 3515 | + * For now we're just reusing the sys_enter prog, and if it |
---|
| 3516 | + * already has an augmenter, we don't need to find one. |
---|
| 3517 | + */ |
---|
| 3518 | + if (sc->bpf_prog.sys_enter != trace->syscalls.unaugmented_prog) |
---|
| 3519 | + continue; |
---|
| 3520 | + |
---|
| 3521 | + /* |
---|
| 3522 | + * Look at all the other syscalls for one that has a signature |
---|
| 3523 | + * that is close enough that we can share: |
---|
| 3524 | + */ |
---|
| 3525 | + pair_prog = trace__find_usable_bpf_prog_entry(trace, sc); |
---|
| 3526 | + if (pair_prog == NULL) |
---|
| 3527 | + continue; |
---|
| 3528 | + |
---|
| 3529 | + sc->bpf_prog.sys_enter = pair_prog; |
---|
| 3530 | + |
---|
| 3531 | + /* |
---|
| 3532 | + * Update the BPF_MAP_TYPE_PROG_SHARED for raw_syscalls:sys_enter |
---|
| 3533 | + * with the fd for the program we're reusing: |
---|
| 3534 | + */ |
---|
| 3535 | + prog_fd = bpf_program__fd(sc->bpf_prog.sys_enter); |
---|
| 3536 | + err = bpf_map_update_elem(map_enter_fd, &key, &prog_fd, BPF_ANY); |
---|
| 3537 | + if (err) |
---|
| 3538 | + break; |
---|
| 3539 | + } |
---|
| 3540 | + |
---|
| 3541 | + |
---|
| 3542 | + return err; |
---|
| 3543 | +} |
---|
| 3544 | + |
---|
| 3545 | +static void trace__delete_augmented_syscalls(struct trace *trace) |
---|
| 3546 | +{ |
---|
| 3547 | + struct evsel *evsel, *tmp; |
---|
| 3548 | + |
---|
| 3549 | + evlist__remove(trace->evlist, trace->syscalls.events.augmented); |
---|
| 3550 | + evsel__delete(trace->syscalls.events.augmented); |
---|
| 3551 | + trace->syscalls.events.augmented = NULL; |
---|
| 3552 | + |
---|
| 3553 | + evlist__for_each_entry_safe(trace->evlist, tmp, evsel) { |
---|
| 3554 | + if (evsel->bpf_obj == trace->bpf_obj) { |
---|
| 3555 | + evlist__remove(trace->evlist, evsel); |
---|
| 3556 | + evsel__delete(evsel); |
---|
| 3557 | + } |
---|
| 3558 | + |
---|
| 3559 | + } |
---|
| 3560 | + |
---|
| 3561 | + bpf_object__close(trace->bpf_obj); |
---|
| 3562 | + trace->bpf_obj = NULL; |
---|
| 3563 | +} |
---|
| 3564 | +#else // HAVE_LIBBPF_SUPPORT |
---|
| 3565 | +static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace __maybe_unused, |
---|
| 3566 | + const char *name __maybe_unused) |
---|
| 3567 | +{ |
---|
| 3568 | + return NULL; |
---|
| 3569 | +} |
---|
| 3570 | + |
---|
| 3571 | +static void trace__set_bpf_map_filtered_pids(struct trace *trace __maybe_unused) |
---|
| 3572 | +{ |
---|
| 3573 | +} |
---|
| 3574 | + |
---|
| 3575 | +static void trace__set_bpf_map_syscalls(struct trace *trace __maybe_unused) |
---|
| 3576 | +{ |
---|
| 3577 | +} |
---|
| 3578 | + |
---|
| 3579 | +static int trace__set_ev_qualifier_bpf_filter(struct trace *trace __maybe_unused) |
---|
| 3580 | +{ |
---|
| 3581 | + return 0; |
---|
| 3582 | +} |
---|
| 3583 | + |
---|
| 3584 | +static int trace__init_syscalls_bpf_map(struct trace *trace __maybe_unused) |
---|
| 3585 | +{ |
---|
| 3586 | + return 0; |
---|
| 3587 | +} |
---|
| 3588 | + |
---|
| 3589 | +static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace __maybe_unused, |
---|
| 3590 | + const char *name __maybe_unused) |
---|
| 3591 | +{ |
---|
| 3592 | + return NULL; |
---|
| 3593 | +} |
---|
| 3594 | + |
---|
| 3595 | +static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace __maybe_unused) |
---|
| 3596 | +{ |
---|
| 3597 | + return 0; |
---|
| 3598 | +} |
---|
| 3599 | + |
---|
| 3600 | +static void trace__delete_augmented_syscalls(struct trace *trace __maybe_unused) |
---|
| 3601 | +{ |
---|
| 3602 | +} |
---|
| 3603 | +#endif // HAVE_LIBBPF_SUPPORT |
---|
| 3604 | + |
---|
| 3605 | +static bool trace__only_augmented_syscalls_evsels(struct trace *trace) |
---|
| 3606 | +{ |
---|
| 3607 | + struct evsel *evsel; |
---|
| 3608 | + |
---|
| 3609 | + evlist__for_each_entry(trace->evlist, evsel) { |
---|
| 3610 | + if (evsel == trace->syscalls.events.augmented || |
---|
| 3611 | + evsel->bpf_obj == trace->bpf_obj) |
---|
| 3612 | + continue; |
---|
| 3613 | + |
---|
| 3614 | + return false; |
---|
| 3615 | + } |
---|
| 3616 | + |
---|
| 3617 | + return true; |
---|
| 3618 | +} |
---|
| 3619 | + |
---|
| 3620 | +static int trace__set_ev_qualifier_filter(struct trace *trace) |
---|
| 3621 | +{ |
---|
| 3622 | + if (trace->syscalls.map) |
---|
| 3623 | + return trace__set_ev_qualifier_bpf_filter(trace); |
---|
| 3624 | + if (trace->syscalls.events.sys_enter) |
---|
| 3625 | + return trace__set_ev_qualifier_tp_filter(trace); |
---|
| 3626 | + return 0; |
---|
| 3627 | +} |
---|
| 3628 | + |
---|
| 3629 | +static int bpf_map__set_filter_pids(struct bpf_map *map __maybe_unused, |
---|
| 3630 | + size_t npids __maybe_unused, pid_t *pids __maybe_unused) |
---|
| 3631 | +{ |
---|
| 3632 | + int err = 0; |
---|
| 3633 | +#ifdef HAVE_LIBBPF_SUPPORT |
---|
| 3634 | + bool value = true; |
---|
| 3635 | + int map_fd = bpf_map__fd(map); |
---|
| 3636 | + size_t i; |
---|
| 3637 | + |
---|
| 3638 | + for (i = 0; i < npids; ++i) { |
---|
| 3639 | + err = bpf_map_update_elem(map_fd, &pids[i], &value, BPF_ANY); |
---|
| 3640 | + if (err) |
---|
| 3641 | + break; |
---|
| 3642 | + } |
---|
| 3643 | +#endif |
---|
| 3644 | + return err; |
---|
| 3645 | +} |
---|
| 3646 | + |
---|
2415 | 3647 | static int trace__set_filter_loop_pids(struct trace *trace) |
---|
2416 | 3648 | { |
---|
2417 | | - unsigned int nr = 1; |
---|
| 3649 | + unsigned int nr = 1, err; |
---|
2418 | 3650 | pid_t pids[32] = { |
---|
2419 | 3651 | getpid(), |
---|
2420 | 3652 | }; |
---|
.. | .. |
---|
2426 | 3658 | if (parent == NULL) |
---|
2427 | 3659 | break; |
---|
2428 | 3660 | |
---|
2429 | | - if (!strcmp(thread__comm_str(parent), "sshd")) { |
---|
| 3661 | + if (!strcmp(thread__comm_str(parent), "sshd") || |
---|
| 3662 | + strstarts(thread__comm_str(parent), "gnome-terminal")) { |
---|
2430 | 3663 | pids[nr++] = parent->tid; |
---|
2431 | 3664 | break; |
---|
2432 | 3665 | } |
---|
2433 | 3666 | thread = parent; |
---|
2434 | 3667 | } |
---|
2435 | 3668 | |
---|
2436 | | - return perf_evlist__set_filter_pids(trace->evlist, nr, pids); |
---|
| 3669 | + err = perf_evlist__append_tp_filter_pids(trace->evlist, nr, pids); |
---|
| 3670 | + if (!err && trace->filter_pids.map) |
---|
| 3671 | + err = bpf_map__set_filter_pids(trace->filter_pids.map, nr, pids); |
---|
| 3672 | + |
---|
| 3673 | + return err; |
---|
| 3674 | +} |
---|
| 3675 | + |
---|
| 3676 | +static int trace__set_filter_pids(struct trace *trace) |
---|
| 3677 | +{ |
---|
| 3678 | + int err = 0; |
---|
| 3679 | + /* |
---|
| 3680 | + * Better not use !target__has_task() here because we need to cover the |
---|
| 3681 | + * case where no threads were specified in the command line, but a |
---|
| 3682 | + * workload was, and in that case we will fill in the thread_map when |
---|
| 3683 | + * we fork the workload in perf_evlist__prepare_workload. |
---|
| 3684 | + */ |
---|
| 3685 | + if (trace->filter_pids.nr > 0) { |
---|
| 3686 | + err = perf_evlist__append_tp_filter_pids(trace->evlist, trace->filter_pids.nr, |
---|
| 3687 | + trace->filter_pids.entries); |
---|
| 3688 | + if (!err && trace->filter_pids.map) { |
---|
| 3689 | + err = bpf_map__set_filter_pids(trace->filter_pids.map, trace->filter_pids.nr, |
---|
| 3690 | + trace->filter_pids.entries); |
---|
| 3691 | + } |
---|
| 3692 | + } else if (perf_thread_map__pid(trace->evlist->core.threads, 0) == -1) { |
---|
| 3693 | + err = trace__set_filter_loop_pids(trace); |
---|
| 3694 | + } |
---|
| 3695 | + |
---|
| 3696 | + return err; |
---|
| 3697 | +} |
---|
| 3698 | + |
---|
| 3699 | +static int __trace__deliver_event(struct trace *trace, union perf_event *event) |
---|
| 3700 | +{ |
---|
| 3701 | + struct evlist *evlist = trace->evlist; |
---|
| 3702 | + struct perf_sample sample; |
---|
| 3703 | + int err; |
---|
| 3704 | + |
---|
| 3705 | + err = perf_evlist__parse_sample(evlist, event, &sample); |
---|
| 3706 | + if (err) |
---|
| 3707 | + fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
---|
| 3708 | + else |
---|
| 3709 | + trace__handle_event(trace, event, &sample); |
---|
| 3710 | + |
---|
| 3711 | + return 0; |
---|
| 3712 | +} |
---|
| 3713 | + |
---|
| 3714 | +static int __trace__flush_events(struct trace *trace) |
---|
| 3715 | +{ |
---|
| 3716 | + u64 first = ordered_events__first_time(&trace->oe.data); |
---|
| 3717 | + u64 flush = trace->oe.last - NSEC_PER_SEC; |
---|
| 3718 | + |
---|
| 3719 | + /* Is there some thing to flush.. */ |
---|
| 3720 | + if (first && first < flush) |
---|
| 3721 | + return ordered_events__flush_time(&trace->oe.data, flush); |
---|
| 3722 | + |
---|
| 3723 | + return 0; |
---|
| 3724 | +} |
---|
| 3725 | + |
---|
| 3726 | +static int trace__flush_events(struct trace *trace) |
---|
| 3727 | +{ |
---|
| 3728 | + return !trace->sort_events ? 0 : __trace__flush_events(trace); |
---|
| 3729 | +} |
---|
| 3730 | + |
---|
| 3731 | +static int trace__deliver_event(struct trace *trace, union perf_event *event) |
---|
| 3732 | +{ |
---|
| 3733 | + int err; |
---|
| 3734 | + |
---|
| 3735 | + if (!trace->sort_events) |
---|
| 3736 | + return __trace__deliver_event(trace, event); |
---|
| 3737 | + |
---|
| 3738 | + err = perf_evlist__parse_sample_timestamp(trace->evlist, event, &trace->oe.last); |
---|
| 3739 | + if (err && err != -1) |
---|
| 3740 | + return err; |
---|
| 3741 | + |
---|
| 3742 | + err = ordered_events__queue(&trace->oe.data, event, trace->oe.last, 0); |
---|
| 3743 | + if (err) |
---|
| 3744 | + return err; |
---|
| 3745 | + |
---|
| 3746 | + return trace__flush_events(trace); |
---|
| 3747 | +} |
---|
| 3748 | + |
---|
| 3749 | +static int ordered_events__deliver_event(struct ordered_events *oe, |
---|
| 3750 | + struct ordered_event *event) |
---|
| 3751 | +{ |
---|
| 3752 | + struct trace *trace = container_of(oe, struct trace, oe.data); |
---|
| 3753 | + |
---|
| 3754 | + return __trace__deliver_event(trace, event->event); |
---|
| 3755 | +} |
---|
| 3756 | + |
---|
| 3757 | +static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg) |
---|
| 3758 | +{ |
---|
| 3759 | + struct tep_format_field *field; |
---|
| 3760 | + struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel); |
---|
| 3761 | + |
---|
| 3762 | + if (evsel->tp_format == NULL || fmt == NULL) |
---|
| 3763 | + return NULL; |
---|
| 3764 | + |
---|
| 3765 | + for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) |
---|
| 3766 | + if (strcmp(field->name, arg) == 0) |
---|
| 3767 | + return fmt; |
---|
| 3768 | + |
---|
| 3769 | + return NULL; |
---|
| 3770 | +} |
---|
| 3771 | + |
---|
| 3772 | +static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel *evsel) |
---|
| 3773 | +{ |
---|
| 3774 | + char *tok, *left = evsel->filter, *new_filter = evsel->filter; |
---|
| 3775 | + |
---|
| 3776 | + while ((tok = strpbrk(left, "=<>!")) != NULL) { |
---|
| 3777 | + char *right = tok + 1, *right_end; |
---|
| 3778 | + |
---|
| 3779 | + if (*right == '=') |
---|
| 3780 | + ++right; |
---|
| 3781 | + |
---|
| 3782 | + while (isspace(*right)) |
---|
| 3783 | + ++right; |
---|
| 3784 | + |
---|
| 3785 | + if (*right == '\0') |
---|
| 3786 | + break; |
---|
| 3787 | + |
---|
| 3788 | + while (!isalpha(*left)) |
---|
| 3789 | + if (++left == tok) { |
---|
| 3790 | + /* |
---|
| 3791 | + * Bail out, can't find the name of the argument that is being |
---|
| 3792 | + * used in the filter, let it try to set this filter, will fail later. |
---|
| 3793 | + */ |
---|
| 3794 | + return 0; |
---|
| 3795 | + } |
---|
| 3796 | + |
---|
| 3797 | + right_end = right + 1; |
---|
| 3798 | + while (isalnum(*right_end) || *right_end == '_' || *right_end == '|') |
---|
| 3799 | + ++right_end; |
---|
| 3800 | + |
---|
| 3801 | + if (isalpha(*right)) { |
---|
| 3802 | + struct syscall_arg_fmt *fmt; |
---|
| 3803 | + int left_size = tok - left, |
---|
| 3804 | + right_size = right_end - right; |
---|
| 3805 | + char arg[128]; |
---|
| 3806 | + |
---|
| 3807 | + while (isspace(left[left_size - 1])) |
---|
| 3808 | + --left_size; |
---|
| 3809 | + |
---|
| 3810 | + scnprintf(arg, sizeof(arg), "%.*s", left_size, left); |
---|
| 3811 | + |
---|
| 3812 | + fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg); |
---|
| 3813 | + if (fmt == NULL) { |
---|
| 3814 | + pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", |
---|
| 3815 | + arg, evsel->name, evsel->filter); |
---|
| 3816 | + return -1; |
---|
| 3817 | + } |
---|
| 3818 | + |
---|
| 3819 | + pr_debug2("trying to expand \"%s\" \"%.*s\" \"%.*s\" -> ", |
---|
| 3820 | + arg, (int)(right - tok), tok, right_size, right); |
---|
| 3821 | + |
---|
| 3822 | + if (fmt->strtoul) { |
---|
| 3823 | + u64 val; |
---|
| 3824 | + struct syscall_arg syscall_arg = { |
---|
| 3825 | + .parm = fmt->parm, |
---|
| 3826 | + }; |
---|
| 3827 | + |
---|
| 3828 | + if (fmt->strtoul(right, right_size, &syscall_arg, &val)) { |
---|
| 3829 | + char *n, expansion[19]; |
---|
| 3830 | + int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); |
---|
| 3831 | + int expansion_offset = right - new_filter; |
---|
| 3832 | + |
---|
| 3833 | + pr_debug("%s", expansion); |
---|
| 3834 | + |
---|
| 3835 | + if (asprintf(&n, "%.*s%s%s", expansion_offset, new_filter, expansion, right_end) < 0) { |
---|
| 3836 | + pr_debug(" out of memory!\n"); |
---|
| 3837 | + free(new_filter); |
---|
| 3838 | + return -1; |
---|
| 3839 | + } |
---|
| 3840 | + if (new_filter != evsel->filter) |
---|
| 3841 | + free(new_filter); |
---|
| 3842 | + left = n + expansion_offset + expansion_lenght; |
---|
| 3843 | + new_filter = n; |
---|
| 3844 | + } else { |
---|
| 3845 | + pr_err("\"%.*s\" not found for \"%s\" in \"%s\", can't set filter \"%s\"\n", |
---|
| 3846 | + right_size, right, arg, evsel->name, evsel->filter); |
---|
| 3847 | + return -1; |
---|
| 3848 | + } |
---|
| 3849 | + } else { |
---|
| 3850 | + pr_err("No resolver (strtoul) for \"%s\" in \"%s\", can't set filter \"%s\"\n", |
---|
| 3851 | + arg, evsel->name, evsel->filter); |
---|
| 3852 | + return -1; |
---|
| 3853 | + } |
---|
| 3854 | + |
---|
| 3855 | + pr_debug("\n"); |
---|
| 3856 | + } else { |
---|
| 3857 | + left = right_end; |
---|
| 3858 | + } |
---|
| 3859 | + } |
---|
| 3860 | + |
---|
| 3861 | + if (new_filter != evsel->filter) { |
---|
| 3862 | + pr_debug("New filter for %s: %s\n", evsel->name, new_filter); |
---|
| 3863 | + evsel__set_filter(evsel, new_filter); |
---|
| 3864 | + free(new_filter); |
---|
| 3865 | + } |
---|
| 3866 | + |
---|
| 3867 | + return 0; |
---|
| 3868 | +} |
---|
| 3869 | + |
---|
| 3870 | +static int trace__expand_filters(struct trace *trace, struct evsel **err_evsel) |
---|
| 3871 | +{ |
---|
| 3872 | + struct evlist *evlist = trace->evlist; |
---|
| 3873 | + struct evsel *evsel; |
---|
| 3874 | + |
---|
| 3875 | + evlist__for_each_entry(evlist, evsel) { |
---|
| 3876 | + if (evsel->filter == NULL) |
---|
| 3877 | + continue; |
---|
| 3878 | + |
---|
| 3879 | + if (trace__expand_filter(trace, evsel)) { |
---|
| 3880 | + *err_evsel = evsel; |
---|
| 3881 | + return -1; |
---|
| 3882 | + } |
---|
| 3883 | + } |
---|
| 3884 | + |
---|
| 3885 | + return 0; |
---|
2437 | 3886 | } |
---|
2438 | 3887 | |
---|
2439 | 3888 | static int trace__run(struct trace *trace, int argc, const char **argv) |
---|
2440 | 3889 | { |
---|
2441 | | - struct perf_evlist *evlist = trace->evlist; |
---|
2442 | | - struct perf_evsel *evsel, *pgfault_maj = NULL, *pgfault_min = NULL; |
---|
| 3890 | + struct evlist *evlist = trace->evlist; |
---|
| 3891 | + struct evsel *evsel, *pgfault_maj = NULL, *pgfault_min = NULL; |
---|
2443 | 3892 | int err = -1, i; |
---|
2444 | 3893 | unsigned long before; |
---|
2445 | 3894 | const bool forks = argc > 0; |
---|
.. | .. |
---|
2447 | 3896 | |
---|
2448 | 3897 | trace->live = true; |
---|
2449 | 3898 | |
---|
2450 | | - if (trace->trace_syscalls && trace__add_syscall_newtp(trace)) |
---|
2451 | | - goto out_error_raw_syscalls; |
---|
| 3899 | + if (!trace->raw_augmented_syscalls) { |
---|
| 3900 | + if (trace->trace_syscalls && trace__add_syscall_newtp(trace)) |
---|
| 3901 | + goto out_error_raw_syscalls; |
---|
2452 | 3902 | |
---|
2453 | | - if (trace->trace_syscalls) |
---|
2454 | | - trace->vfs_getname = perf_evlist__add_vfs_getname(evlist); |
---|
| 3903 | + if (trace->trace_syscalls) |
---|
| 3904 | + trace->vfs_getname = evlist__add_vfs_getname(evlist); |
---|
| 3905 | + } |
---|
2455 | 3906 | |
---|
2456 | 3907 | if ((trace->trace_pgfaults & TRACE_PFMAJ)) { |
---|
2457 | | - pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); |
---|
| 3908 | + pgfault_maj = evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); |
---|
2458 | 3909 | if (pgfault_maj == NULL) |
---|
2459 | 3910 | goto out_error_mem; |
---|
2460 | | - perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); |
---|
2461 | | - perf_evlist__add(evlist, pgfault_maj); |
---|
| 3911 | + evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); |
---|
| 3912 | + evlist__add(evlist, pgfault_maj); |
---|
2462 | 3913 | } |
---|
2463 | 3914 | |
---|
2464 | 3915 | if ((trace->trace_pgfaults & TRACE_PFMIN)) { |
---|
2465 | | - pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); |
---|
| 3916 | + pgfault_min = evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); |
---|
2466 | 3917 | if (pgfault_min == NULL) |
---|
2467 | 3918 | goto out_error_mem; |
---|
2468 | | - perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); |
---|
2469 | | - perf_evlist__add(evlist, pgfault_min); |
---|
| 3919 | + evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); |
---|
| 3920 | + evlist__add(evlist, pgfault_min); |
---|
2470 | 3921 | } |
---|
2471 | 3922 | |
---|
2472 | 3923 | if (trace->sched && |
---|
2473 | | - perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
---|
2474 | | - trace__sched_stat_runtime)) |
---|
| 3924 | + evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime)) |
---|
2475 | 3925 | goto out_error_sched_stat_runtime; |
---|
2476 | | - |
---|
2477 | 3926 | /* |
---|
2478 | 3927 | * If a global cgroup was set, apply it to all the events without an |
---|
2479 | 3928 | * explicit cgroup. I.e.: |
---|
.. | .. |
---|
2528 | 3977 | } |
---|
2529 | 3978 | } |
---|
2530 | 3979 | |
---|
2531 | | - err = perf_evlist__open(evlist); |
---|
| 3980 | + err = evlist__open(evlist); |
---|
2532 | 3981 | if (err < 0) |
---|
2533 | 3982 | goto out_error_open; |
---|
2534 | 3983 | |
---|
.. | .. |
---|
2542 | 3991 | goto out_error_open; |
---|
2543 | 3992 | } |
---|
2544 | 3993 | |
---|
2545 | | - /* |
---|
2546 | | - * Better not use !target__has_task() here because we need to cover the |
---|
2547 | | - * case where no threads were specified in the command line, but a |
---|
2548 | | - * workload was, and in that case we will fill in the thread_map when |
---|
2549 | | - * we fork the workload in perf_evlist__prepare_workload. |
---|
2550 | | - */ |
---|
2551 | | - if (trace->filter_pids.nr > 0) |
---|
2552 | | - err = perf_evlist__set_filter_pids(evlist, trace->filter_pids.nr, trace->filter_pids.entries); |
---|
2553 | | - else if (thread_map__pid(evlist->threads, 0) == -1) |
---|
2554 | | - err = trace__set_filter_loop_pids(trace); |
---|
2555 | | - |
---|
| 3994 | + err = trace__set_filter_pids(trace); |
---|
2556 | 3995 | if (err < 0) |
---|
2557 | 3996 | goto out_error_mem; |
---|
| 3997 | + |
---|
| 3998 | + if (trace->syscalls.map) |
---|
| 3999 | + trace__init_syscalls_bpf_map(trace); |
---|
| 4000 | + |
---|
| 4001 | + if (trace->syscalls.prog_array.sys_enter) |
---|
| 4002 | + trace__init_syscalls_bpf_prog_array_maps(trace); |
---|
2558 | 4003 | |
---|
2559 | 4004 | if (trace->ev_qualifier_ids.nr > 0) { |
---|
2560 | 4005 | err = trace__set_ev_qualifier_filter(trace); |
---|
2561 | 4006 | if (err < 0) |
---|
2562 | 4007 | goto out_errno; |
---|
2563 | 4008 | |
---|
2564 | | - pr_debug("event qualifier tracepoint filter: %s\n", |
---|
2565 | | - trace->syscalls.events.sys_exit->filter); |
---|
| 4009 | + if (trace->syscalls.events.sys_exit) { |
---|
| 4010 | + pr_debug("event qualifier tracepoint filter: %s\n", |
---|
| 4011 | + trace->syscalls.events.sys_exit->filter); |
---|
| 4012 | + } |
---|
2566 | 4013 | } |
---|
2567 | 4014 | |
---|
| 4015 | + /* |
---|
| 4016 | + * If the "close" syscall is not traced, then we will not have the |
---|
| 4017 | + * opportunity to, in syscall_arg__scnprintf_close_fd() invalidate the |
---|
| 4018 | + * fd->pathname table and were ending up showing the last value set by |
---|
| 4019 | + * syscalls opening a pathname and associating it with a descriptor or |
---|
| 4020 | + * reading it from /proc/pid/fd/ in cases where that doesn't make |
---|
| 4021 | + * sense. |
---|
| 4022 | + * |
---|
| 4023 | + * So just disable this beautifier (SCA_FD, SCA_FDAT) when 'close' is |
---|
| 4024 | + * not in use. |
---|
| 4025 | + */ |
---|
| 4026 | + trace->fd_path_disabled = !trace__syscall_enabled(trace, syscalltbl__id(trace->sctbl, "close")); |
---|
| 4027 | + |
---|
| 4028 | + err = trace__expand_filters(trace, &evsel); |
---|
| 4029 | + if (err) |
---|
| 4030 | + goto out_delete_evlist; |
---|
2568 | 4031 | err = perf_evlist__apply_filters(evlist, &evsel); |
---|
2569 | 4032 | if (err < 0) |
---|
2570 | 4033 | goto out_error_apply_filters; |
---|
2571 | 4034 | |
---|
2572 | | - err = perf_evlist__mmap(evlist, trace->opts.mmap_pages); |
---|
| 4035 | + if (trace->dump.map) |
---|
| 4036 | + bpf_map__fprintf(trace->dump.map, trace->output); |
---|
| 4037 | + |
---|
| 4038 | + err = evlist__mmap(evlist, trace->opts.mmap_pages); |
---|
2573 | 4039 | if (err < 0) |
---|
2574 | 4040 | goto out_error_mmap; |
---|
2575 | 4041 | |
---|
2576 | 4042 | if (!target__none(&trace->opts.target) && !trace->opts.initial_delay) |
---|
2577 | | - perf_evlist__enable(evlist); |
---|
| 4043 | + evlist__enable(evlist); |
---|
2578 | 4044 | |
---|
2579 | 4045 | if (forks) |
---|
2580 | 4046 | perf_evlist__start_workload(evlist); |
---|
2581 | 4047 | |
---|
2582 | 4048 | if (trace->opts.initial_delay) { |
---|
2583 | 4049 | usleep(trace->opts.initial_delay * 1000); |
---|
2584 | | - perf_evlist__enable(evlist); |
---|
| 4050 | + evlist__enable(evlist); |
---|
2585 | 4051 | } |
---|
2586 | 4052 | |
---|
2587 | | - trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || |
---|
2588 | | - evlist->threads->nr > 1 || |
---|
2589 | | - perf_evlist__first(evlist)->attr.inherit; |
---|
| 4053 | + trace->multiple_threads = perf_thread_map__pid(evlist->core.threads, 0) == -1 || |
---|
| 4054 | + evlist->core.threads->nr > 1 || |
---|
| 4055 | + evlist__first(evlist)->core.attr.inherit; |
---|
2590 | 4056 | |
---|
2591 | 4057 | /* |
---|
2592 | | - * Now that we already used evsel->attr to ask the kernel to setup the |
---|
2593 | | - * events, lets reuse evsel->attr.sample_max_stack as the limit in |
---|
| 4058 | + * Now that we already used evsel->core.attr to ask the kernel to setup the |
---|
| 4059 | + * events, lets reuse evsel->core.attr.sample_max_stack as the limit in |
---|
2594 | 4060 | * trace__resolve_callchain(), allowing per-event max-stack settings |
---|
2595 | | - * to override an explicitely set --max-stack global setting. |
---|
| 4061 | + * to override an explicitly set --max-stack global setting. |
---|
2596 | 4062 | */ |
---|
2597 | 4063 | evlist__for_each_entry(evlist, evsel) { |
---|
2598 | 4064 | if (evsel__has_callchain(evsel) && |
---|
2599 | | - evsel->attr.sample_max_stack == 0) |
---|
2600 | | - evsel->attr.sample_max_stack = trace->max_stack; |
---|
| 4065 | + evsel->core.attr.sample_max_stack == 0) |
---|
| 4066 | + evsel->core.attr.sample_max_stack = trace->max_stack; |
---|
2601 | 4067 | } |
---|
2602 | 4068 | again: |
---|
2603 | 4069 | before = trace->nr_events; |
---|
2604 | 4070 | |
---|
2605 | | - for (i = 0; i < evlist->nr_mmaps; i++) { |
---|
| 4071 | + for (i = 0; i < evlist->core.nr_mmaps; i++) { |
---|
2606 | 4072 | union perf_event *event; |
---|
2607 | | - struct perf_mmap *md; |
---|
| 4073 | + struct mmap *md; |
---|
2608 | 4074 | |
---|
2609 | 4075 | md = &evlist->mmap[i]; |
---|
2610 | | - if (perf_mmap__read_init(md) < 0) |
---|
| 4076 | + if (perf_mmap__read_init(&md->core) < 0) |
---|
2611 | 4077 | continue; |
---|
2612 | 4078 | |
---|
2613 | | - while ((event = perf_mmap__read_event(md)) != NULL) { |
---|
2614 | | - struct perf_sample sample; |
---|
2615 | | - |
---|
| 4079 | + while ((event = perf_mmap__read_event(&md->core)) != NULL) { |
---|
2616 | 4080 | ++trace->nr_events; |
---|
2617 | 4081 | |
---|
2618 | | - err = perf_evlist__parse_sample(evlist, event, &sample); |
---|
2619 | | - if (err) { |
---|
2620 | | - fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
---|
2621 | | - goto next_event; |
---|
2622 | | - } |
---|
| 4082 | + err = trace__deliver_event(trace, event); |
---|
| 4083 | + if (err) |
---|
| 4084 | + goto out_disable; |
---|
2623 | 4085 | |
---|
2624 | | - trace__handle_event(trace, event, &sample); |
---|
2625 | | -next_event: |
---|
2626 | | - perf_mmap__consume(md); |
---|
| 4086 | + perf_mmap__consume(&md->core); |
---|
2627 | 4087 | |
---|
2628 | 4088 | if (interrupted) |
---|
2629 | 4089 | goto out_disable; |
---|
2630 | 4090 | |
---|
2631 | 4091 | if (done && !draining) { |
---|
2632 | | - perf_evlist__disable(evlist); |
---|
| 4092 | + evlist__disable(evlist); |
---|
2633 | 4093 | draining = true; |
---|
2634 | 4094 | } |
---|
2635 | 4095 | } |
---|
2636 | | - perf_mmap__read_done(md); |
---|
| 4096 | + perf_mmap__read_done(&md->core); |
---|
2637 | 4097 | } |
---|
2638 | 4098 | |
---|
2639 | 4099 | if (trace->nr_events == before) { |
---|
2640 | 4100 | int timeout = done ? 100 : -1; |
---|
2641 | 4101 | |
---|
2642 | | - if (!draining && perf_evlist__poll(evlist, timeout) > 0) { |
---|
2643 | | - if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0) |
---|
| 4102 | + if (!draining && evlist__poll(evlist, timeout) > 0) { |
---|
| 4103 | + if (evlist__filter_pollfd(evlist, POLLERR | POLLHUP | POLLNVAL) == 0) |
---|
2644 | 4104 | draining = true; |
---|
2645 | 4105 | |
---|
2646 | 4106 | goto again; |
---|
| 4107 | + } else { |
---|
| 4108 | + if (trace__flush_events(trace)) |
---|
| 4109 | + goto out_disable; |
---|
2647 | 4110 | } |
---|
2648 | 4111 | } else { |
---|
2649 | 4112 | goto again; |
---|
.. | .. |
---|
2652 | 4115 | out_disable: |
---|
2653 | 4116 | thread__zput(trace->current); |
---|
2654 | 4117 | |
---|
2655 | | - perf_evlist__disable(evlist); |
---|
| 4118 | + evlist__disable(evlist); |
---|
| 4119 | + |
---|
| 4120 | + if (trace->sort_events) |
---|
| 4121 | + ordered_events__flush(&trace->oe.data, OE_FLUSH__FINAL); |
---|
2656 | 4122 | |
---|
2657 | 4123 | if (!err) { |
---|
2658 | 4124 | if (trace->summary) |
---|
.. | .. |
---|
2670 | 4136 | out_delete_evlist: |
---|
2671 | 4137 | trace__symbols__exit(trace); |
---|
2672 | 4138 | |
---|
2673 | | - perf_evlist__delete(evlist); |
---|
| 4139 | + evlist__delete(evlist); |
---|
2674 | 4140 | cgroup__put(trace->cgroup); |
---|
2675 | 4141 | trace->evlist = NULL; |
---|
2676 | 4142 | trace->live = false; |
---|
.. | .. |
---|
2687 | 4153 | goto out_error; |
---|
2688 | 4154 | |
---|
2689 | 4155 | out_error_mmap: |
---|
2690 | | - perf_evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf)); |
---|
| 4156 | + evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf)); |
---|
2691 | 4157 | goto out_error; |
---|
2692 | 4158 | |
---|
2693 | 4159 | out_error_open: |
---|
2694 | | - perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); |
---|
| 4160 | + evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); |
---|
2695 | 4161 | |
---|
2696 | 4162 | out_error: |
---|
2697 | 4163 | fprintf(trace->output, "%s\n", errbuf); |
---|
.. | .. |
---|
2700 | 4166 | out_error_apply_filters: |
---|
2701 | 4167 | fprintf(trace->output, |
---|
2702 | 4168 | "Failed to set filter \"%s\" on event %s with %d (%s)\n", |
---|
2703 | | - evsel->filter, perf_evsel__name(evsel), errno, |
---|
| 4169 | + evsel->filter, evsel__name(evsel), errno, |
---|
2704 | 4170 | str_error_r(errno, errbuf, sizeof(errbuf))); |
---|
2705 | 4171 | goto out_delete_evlist; |
---|
2706 | 4172 | } |
---|
.. | .. |
---|
2715 | 4181 | |
---|
2716 | 4182 | static int trace__replay(struct trace *trace) |
---|
2717 | 4183 | { |
---|
2718 | | - const struct perf_evsel_str_handler handlers[] = { |
---|
| 4184 | + const struct evsel_str_handler handlers[] = { |
---|
2719 | 4185 | { "probe:vfs_getname", trace__vfs_getname, }, |
---|
2720 | 4186 | }; |
---|
2721 | 4187 | struct perf_data data = { |
---|
2722 | | - .file = { |
---|
2723 | | - .path = input_name, |
---|
2724 | | - }, |
---|
2725 | | - .mode = PERF_DATA_MODE_READ, |
---|
2726 | | - .force = trace->force, |
---|
| 4188 | + .path = input_name, |
---|
| 4189 | + .mode = PERF_DATA_MODE_READ, |
---|
| 4190 | + .force = trace->force, |
---|
2727 | 4191 | }; |
---|
2728 | 4192 | struct perf_session *session; |
---|
2729 | | - struct perf_evsel *evsel; |
---|
| 4193 | + struct evsel *evsel; |
---|
2730 | 4194 | int err = -1; |
---|
2731 | 4195 | |
---|
2732 | 4196 | trace->tool.sample = trace__process_sample; |
---|
.. | .. |
---|
2747 | 4211 | trace->multiple_threads = true; |
---|
2748 | 4212 | |
---|
2749 | 4213 | session = perf_session__new(&data, false, &trace->tool); |
---|
2750 | | - if (session == NULL) |
---|
2751 | | - return -1; |
---|
| 4214 | + if (IS_ERR(session)) |
---|
| 4215 | + return PTR_ERR(session); |
---|
2752 | 4216 | |
---|
2753 | 4217 | if (trace->opts.target.pid) |
---|
2754 | 4218 | symbol_conf.pid_list_str = strdup(trace->opts.target.pid); |
---|
.. | .. |
---|
2773 | 4237 | "syscalls:sys_enter"); |
---|
2774 | 4238 | |
---|
2775 | 4239 | if (evsel && |
---|
2776 | | - (perf_evsel__init_raw_syscall_tp(evsel, trace__sys_enter) < 0 || |
---|
| 4240 | + (evsel__init_raw_syscall_tp(evsel, trace__sys_enter) < 0 || |
---|
2777 | 4241 | perf_evsel__init_sc_tp_ptr_field(evsel, args))) { |
---|
2778 | 4242 | pr_err("Error during initialize raw_syscalls:sys_enter event\n"); |
---|
2779 | 4243 | goto out; |
---|
.. | .. |
---|
2785 | 4249 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
---|
2786 | 4250 | "syscalls:sys_exit"); |
---|
2787 | 4251 | if (evsel && |
---|
2788 | | - (perf_evsel__init_raw_syscall_tp(evsel, trace__sys_exit) < 0 || |
---|
| 4252 | + (evsel__init_raw_syscall_tp(evsel, trace__sys_exit) < 0 || |
---|
2789 | 4253 | perf_evsel__init_sc_tp_uint_field(evsel, ret))) { |
---|
2790 | 4254 | pr_err("Error during initialize raw_syscalls:sys_exit event\n"); |
---|
2791 | 4255 | goto out; |
---|
2792 | 4256 | } |
---|
2793 | 4257 | |
---|
2794 | 4258 | evlist__for_each_entry(session->evlist, evsel) { |
---|
2795 | | - if (evsel->attr.type == PERF_TYPE_SOFTWARE && |
---|
2796 | | - (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ || |
---|
2797 | | - evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN || |
---|
2798 | | - evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS)) |
---|
| 4259 | + if (evsel->core.attr.type == PERF_TYPE_SOFTWARE && |
---|
| 4260 | + (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ || |
---|
| 4261 | + evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN || |
---|
| 4262 | + evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS)) |
---|
2799 | 4263 | evsel->handler = trace__pgfault; |
---|
2800 | 4264 | } |
---|
2801 | 4265 | |
---|
.. | .. |
---|
2824 | 4288 | } |
---|
2825 | 4289 | |
---|
2826 | 4290 | DEFINE_RESORT_RB(syscall_stats, a->msecs > b->msecs, |
---|
2827 | | - struct stats *stats; |
---|
2828 | | - double msecs; |
---|
2829 | | - int syscall; |
---|
| 4291 | + struct syscall_stats *stats; |
---|
| 4292 | + double msecs; |
---|
| 4293 | + int syscall; |
---|
2830 | 4294 | ) |
---|
2831 | 4295 | { |
---|
2832 | 4296 | struct int_node *source = rb_entry(nd, struct int_node, rb_node); |
---|
2833 | | - struct stats *stats = source->priv; |
---|
| 4297 | + struct syscall_stats *stats = source->priv; |
---|
2834 | 4298 | |
---|
2835 | 4299 | entry->syscall = source->i; |
---|
2836 | 4300 | entry->stats = stats; |
---|
2837 | | - entry->msecs = stats ? (u64)stats->n * (avg_stats(stats) / NSEC_PER_MSEC) : 0; |
---|
| 4301 | + entry->msecs = stats ? (u64)stats->stats.n * (avg_stats(&stats->stats) / NSEC_PER_MSEC) : 0; |
---|
2838 | 4302 | } |
---|
2839 | 4303 | |
---|
2840 | 4304 | static size_t thread__dump_stats(struct thread_trace *ttrace, |
---|
.. | .. |
---|
2850 | 4314 | |
---|
2851 | 4315 | printed += fprintf(fp, "\n"); |
---|
2852 | 4316 | |
---|
2853 | | - printed += fprintf(fp, " syscall calls total min avg max stddev\n"); |
---|
2854 | | - printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); |
---|
2855 | | - printed += fprintf(fp, " --------------- -------- --------- --------- --------- --------- ------\n"); |
---|
| 4317 | + printed += fprintf(fp, " syscall calls errors total min avg max stddev\n"); |
---|
| 4318 | + printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); |
---|
| 4319 | + printed += fprintf(fp, " --------------- -------- ------ -------- --------- --------- --------- ------\n"); |
---|
2856 | 4320 | |
---|
2857 | 4321 | resort_rb__for_each_entry(nd, syscall_stats) { |
---|
2858 | | - struct stats *stats = syscall_stats_entry->stats; |
---|
| 4322 | + struct syscall_stats *stats = syscall_stats_entry->stats; |
---|
2859 | 4323 | if (stats) { |
---|
2860 | | - double min = (double)(stats->min) / NSEC_PER_MSEC; |
---|
2861 | | - double max = (double)(stats->max) / NSEC_PER_MSEC; |
---|
2862 | | - double avg = avg_stats(stats); |
---|
| 4324 | + double min = (double)(stats->stats.min) / NSEC_PER_MSEC; |
---|
| 4325 | + double max = (double)(stats->stats.max) / NSEC_PER_MSEC; |
---|
| 4326 | + double avg = avg_stats(&stats->stats); |
---|
2863 | 4327 | double pct; |
---|
2864 | | - u64 n = (u64) stats->n; |
---|
| 4328 | + u64 n = (u64)stats->stats.n; |
---|
2865 | 4329 | |
---|
2866 | | - pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; |
---|
| 4330 | + pct = avg ? 100.0 * stddev_stats(&stats->stats) / avg : 0.0; |
---|
2867 | 4331 | avg /= NSEC_PER_MSEC; |
---|
2868 | 4332 | |
---|
2869 | 4333 | sc = &trace->syscalls.table[syscall_stats_entry->syscall]; |
---|
2870 | 4334 | printed += fprintf(fp, " %-15s", sc->name); |
---|
2871 | | - printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f %9.3f", |
---|
2872 | | - n, syscall_stats_entry->msecs, min, avg); |
---|
| 4335 | + printed += fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", |
---|
| 4336 | + n, stats->nr_failures, syscall_stats_entry->msecs, min, avg); |
---|
2873 | 4337 | printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); |
---|
| 4338 | + |
---|
| 4339 | + if (trace->errno_summary && stats->nr_failures) { |
---|
| 4340 | + const char *arch_name = perf_env__arch(trace->host->env); |
---|
| 4341 | + int e; |
---|
| 4342 | + |
---|
| 4343 | + for (e = 0; e < stats->max_errno; ++e) { |
---|
| 4344 | + if (stats->errnos[e] != 0) |
---|
| 4345 | + fprintf(fp, "\t\t\t\t%s: %d\n", arch_syscalls__strerrno(arch_name, e + 1), stats->errnos[e]); |
---|
| 4346 | + } |
---|
| 4347 | + } |
---|
2874 | 4348 | } |
---|
2875 | 4349 | } |
---|
2876 | 4350 | |
---|
.. | .. |
---|
2951 | 4425 | return 0; |
---|
2952 | 4426 | } |
---|
2953 | 4427 | |
---|
2954 | | -static int trace__set_filter_pids(const struct option *opt, const char *str, |
---|
2955 | | - int unset __maybe_unused) |
---|
| 4428 | +static int trace__set_filter_pids_from_option(const struct option *opt, const char *str, |
---|
| 4429 | + int unset __maybe_unused) |
---|
2956 | 4430 | { |
---|
2957 | 4431 | int ret = -1; |
---|
2958 | 4432 | size_t i; |
---|
.. | .. |
---|
3017 | 4491 | return 0; |
---|
3018 | 4492 | } |
---|
3019 | 4493 | |
---|
3020 | | -static void evlist__set_evsel_handler(struct perf_evlist *evlist, void *handler) |
---|
| 4494 | +static void evlist__set_default_evsel_handler(struct evlist *evlist, void *handler) |
---|
3021 | 4495 | { |
---|
3022 | | - struct perf_evsel *evsel; |
---|
| 4496 | + struct evsel *evsel; |
---|
3023 | 4497 | |
---|
3024 | | - evlist__for_each_entry(evlist, evsel) |
---|
3025 | | - evsel->handler = handler; |
---|
| 4498 | + evlist__for_each_entry(evlist, evsel) { |
---|
| 4499 | + if (evsel->handler == NULL) |
---|
| 4500 | + evsel->handler = handler; |
---|
| 4501 | + } |
---|
3026 | 4502 | } |
---|
3027 | 4503 | |
---|
3028 | | -static int evlist__set_syscall_tp_fields(struct perf_evlist *evlist) |
---|
| 4504 | +static void evsel__set_syscall_arg_fmt(struct evsel *evsel, const char *name) |
---|
3029 | 4505 | { |
---|
3030 | | - struct perf_evsel *evsel; |
---|
| 4506 | + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); |
---|
| 4507 | + |
---|
| 4508 | + if (fmt) { |
---|
| 4509 | + struct syscall_fmt *scfmt = syscall_fmt__find(name); |
---|
| 4510 | + |
---|
| 4511 | + if (scfmt) { |
---|
| 4512 | + int skip = 0; |
---|
| 4513 | + |
---|
| 4514 | + if (strcmp(evsel->tp_format->format.fields->name, "__syscall_nr") == 0 || |
---|
| 4515 | + strcmp(evsel->tp_format->format.fields->name, "nr") == 0) |
---|
| 4516 | + ++skip; |
---|
| 4517 | + |
---|
| 4518 | + memcpy(fmt + skip, scfmt->arg, (evsel->tp_format->format.nr_fields - skip) * sizeof(*fmt)); |
---|
| 4519 | + } |
---|
| 4520 | + } |
---|
| 4521 | +} |
---|
| 4522 | + |
---|
| 4523 | +static int evlist__set_syscall_tp_fields(struct evlist *evlist) |
---|
| 4524 | +{ |
---|
| 4525 | + struct evsel *evsel; |
---|
3031 | 4526 | |
---|
3032 | 4527 | evlist__for_each_entry(evlist, evsel) { |
---|
3033 | 4528 | if (evsel->priv || !evsel->tp_format) |
---|
3034 | 4529 | continue; |
---|
3035 | 4530 | |
---|
3036 | | - if (strcmp(evsel->tp_format->system, "syscalls")) |
---|
| 4531 | + if (strcmp(evsel->tp_format->system, "syscalls")) { |
---|
| 4532 | + evsel__init_tp_arg_scnprintf(evsel); |
---|
3037 | 4533 | continue; |
---|
| 4534 | + } |
---|
3038 | 4535 | |
---|
3039 | | - if (perf_evsel__init_syscall_tp(evsel)) |
---|
| 4536 | + if (evsel__init_syscall_tp(evsel)) |
---|
3040 | 4537 | return -1; |
---|
3041 | 4538 | |
---|
3042 | 4539 | if (!strncmp(evsel->tp_format->name, "sys_enter_", 10)) { |
---|
3043 | | - struct syscall_tp *sc = evsel->priv; |
---|
| 4540 | + struct syscall_tp *sc = __evsel__syscall_tp(evsel); |
---|
3044 | 4541 | |
---|
3045 | 4542 | if (__tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64))) |
---|
3046 | 4543 | return -1; |
---|
| 4544 | + |
---|
| 4545 | + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_enter_") - 1); |
---|
3047 | 4546 | } else if (!strncmp(evsel->tp_format->name, "sys_exit_", 9)) { |
---|
3048 | | - struct syscall_tp *sc = evsel->priv; |
---|
| 4547 | + struct syscall_tp *sc = __evsel__syscall_tp(evsel); |
---|
3049 | 4548 | |
---|
3050 | 4549 | if (__tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap)) |
---|
3051 | 4550 | return -1; |
---|
| 4551 | + |
---|
| 4552 | + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_exit_") - 1); |
---|
3052 | 4553 | } |
---|
3053 | 4554 | } |
---|
3054 | 4555 | |
---|
.. | .. |
---|
3072 | 4573 | int len = strlen(str) + 1, err = -1, list, idx; |
---|
3073 | 4574 | char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); |
---|
3074 | 4575 | char group_name[PATH_MAX]; |
---|
| 4576 | + struct syscall_fmt *fmt; |
---|
3075 | 4577 | |
---|
3076 | 4578 | if (strace_groups_dir == NULL) |
---|
3077 | 4579 | return -1; |
---|
.. | .. |
---|
3089 | 4591 | if (syscalltbl__id(trace->sctbl, s) >= 0 || |
---|
3090 | 4592 | syscalltbl__strglobmatch_first(trace->sctbl, s, &idx) >= 0) { |
---|
3091 | 4593 | list = 1; |
---|
| 4594 | + goto do_concat; |
---|
| 4595 | + } |
---|
| 4596 | + |
---|
| 4597 | + fmt = syscall_fmt__find_by_alias(s); |
---|
| 4598 | + if (fmt != NULL) { |
---|
| 4599 | + list = 1; |
---|
| 4600 | + s = fmt->name; |
---|
3092 | 4601 | } else { |
---|
3093 | 4602 | path__join(group_name, sizeof(group_name), strace_groups_dir, s); |
---|
3094 | 4603 | if (access(group_name, R_OK) == 0) |
---|
3095 | 4604 | list = 1; |
---|
3096 | 4605 | } |
---|
3097 | | - |
---|
| 4606 | +do_concat: |
---|
3098 | 4607 | if (lists[list]) { |
---|
3099 | 4608 | sprintf(lists[list] + strlen(lists[list]), ",%s", s); |
---|
3100 | 4609 | } else { |
---|
.. | .. |
---|
3130 | 4639 | err = 0; |
---|
3131 | 4640 | |
---|
3132 | 4641 | if (lists[0]) { |
---|
3133 | | - struct option o = OPT_CALLBACK('e', "event", &trace->evlist, "event", |
---|
3134 | | - "event selector. use 'perf list' to list available events", |
---|
3135 | | - parse_events_option); |
---|
| 4642 | + struct option o = { |
---|
| 4643 | + .value = &trace->evlist, |
---|
| 4644 | + }; |
---|
3136 | 4645 | err = parse_events_option(&o, lists[0], 0); |
---|
3137 | 4646 | } |
---|
3138 | 4647 | out: |
---|
.. | .. |
---|
3146 | 4655 | { |
---|
3147 | 4656 | struct trace *trace = opt->value; |
---|
3148 | 4657 | |
---|
3149 | | - if (!list_empty(&trace->evlist->entries)) |
---|
3150 | | - return parse_cgroups(opt, str, unset); |
---|
3151 | | - |
---|
| 4658 | + if (!list_empty(&trace->evlist->core.entries)) { |
---|
| 4659 | + struct option o = { |
---|
| 4660 | + .value = &trace->evlist, |
---|
| 4661 | + }; |
---|
| 4662 | + return parse_cgroups(&o, str, unset); |
---|
| 4663 | + } |
---|
3152 | 4664 | trace->cgroup = evlist__findnew_cgroup(trace->evlist, str); |
---|
3153 | 4665 | |
---|
3154 | 4666 | return 0; |
---|
| 4667 | +} |
---|
| 4668 | + |
---|
| 4669 | +static int trace__config(const char *var, const char *value, void *arg) |
---|
| 4670 | +{ |
---|
| 4671 | + struct trace *trace = arg; |
---|
| 4672 | + int err = 0; |
---|
| 4673 | + |
---|
| 4674 | + if (!strcmp(var, "trace.add_events")) { |
---|
| 4675 | + trace->perfconfig_events = strdup(value); |
---|
| 4676 | + if (trace->perfconfig_events == NULL) { |
---|
| 4677 | + pr_err("Not enough memory for %s\n", "trace.add_events"); |
---|
| 4678 | + return -1; |
---|
| 4679 | + } |
---|
| 4680 | + } else if (!strcmp(var, "trace.show_timestamp")) { |
---|
| 4681 | + trace->show_tstamp = perf_config_bool(var, value); |
---|
| 4682 | + } else if (!strcmp(var, "trace.show_duration")) { |
---|
| 4683 | + trace->show_duration = perf_config_bool(var, value); |
---|
| 4684 | + } else if (!strcmp(var, "trace.show_arg_names")) { |
---|
| 4685 | + trace->show_arg_names = perf_config_bool(var, value); |
---|
| 4686 | + if (!trace->show_arg_names) |
---|
| 4687 | + trace->show_zeros = true; |
---|
| 4688 | + } else if (!strcmp(var, "trace.show_zeros")) { |
---|
| 4689 | + bool new_show_zeros = perf_config_bool(var, value); |
---|
| 4690 | + if (!trace->show_arg_names && !new_show_zeros) { |
---|
| 4691 | + pr_warning("trace.show_zeros has to be set when trace.show_arg_names=no\n"); |
---|
| 4692 | + goto out; |
---|
| 4693 | + } |
---|
| 4694 | + trace->show_zeros = new_show_zeros; |
---|
| 4695 | + } else if (!strcmp(var, "trace.show_prefix")) { |
---|
| 4696 | + trace->show_string_prefix = perf_config_bool(var, value); |
---|
| 4697 | + } else if (!strcmp(var, "trace.no_inherit")) { |
---|
| 4698 | + trace->opts.no_inherit = perf_config_bool(var, value); |
---|
| 4699 | + } else if (!strcmp(var, "trace.args_alignment")) { |
---|
| 4700 | + int args_alignment = 0; |
---|
| 4701 | + if (perf_config_int(&args_alignment, var, value) == 0) |
---|
| 4702 | + trace->args_alignment = args_alignment; |
---|
| 4703 | + } else if (!strcmp(var, "trace.tracepoint_beautifiers")) { |
---|
| 4704 | + if (strcasecmp(value, "libtraceevent") == 0) |
---|
| 4705 | + trace->libtraceevent_print = true; |
---|
| 4706 | + else if (strcasecmp(value, "libbeauty") == 0) |
---|
| 4707 | + trace->libtraceevent_print = false; |
---|
| 4708 | + } |
---|
| 4709 | +out: |
---|
| 4710 | + return err; |
---|
3155 | 4711 | } |
---|
3156 | 4712 | |
---|
3157 | 4713 | int cmd_trace(int argc, const char **argv) |
---|
.. | .. |
---|
3164 | 4720 | NULL |
---|
3165 | 4721 | }; |
---|
3166 | 4722 | struct trace trace = { |
---|
3167 | | - .syscalls = { |
---|
3168 | | - . max = -1, |
---|
3169 | | - }, |
---|
3170 | 4723 | .opts = { |
---|
3171 | 4724 | .target = { |
---|
3172 | 4725 | .uid = UINT_MAX, |
---|
.. | .. |
---|
3176 | 4729 | .user_interval = ULLONG_MAX, |
---|
3177 | 4730 | .no_buffering = true, |
---|
3178 | 4731 | .mmap_pages = UINT_MAX, |
---|
3179 | | - .proc_map_timeout = 500, |
---|
3180 | 4732 | }, |
---|
3181 | 4733 | .output = stderr, |
---|
3182 | 4734 | .show_comm = true, |
---|
| 4735 | + .show_tstamp = true, |
---|
| 4736 | + .show_duration = true, |
---|
| 4737 | + .show_arg_names = true, |
---|
| 4738 | + .args_alignment = 70, |
---|
3183 | 4739 | .trace_syscalls = false, |
---|
3184 | 4740 | .kernel_syscallchains = false, |
---|
3185 | 4741 | .max_stack = UINT_MAX, |
---|
| 4742 | + .max_events = ULONG_MAX, |
---|
3186 | 4743 | }; |
---|
| 4744 | + const char *map_dump_str = NULL; |
---|
3187 | 4745 | const char *output_name = NULL; |
---|
3188 | 4746 | const struct option trace_options[] = { |
---|
3189 | 4747 | OPT_CALLBACK('e', "event", &trace, "event", |
---|
3190 | 4748 | "event/syscall selector. use 'perf list' to list available events", |
---|
3191 | 4749 | trace__parse_events_option), |
---|
| 4750 | + OPT_CALLBACK(0, "filter", &trace.evlist, "filter", |
---|
| 4751 | + "event filter", parse_filter), |
---|
3192 | 4752 | OPT_BOOLEAN(0, "comm", &trace.show_comm, |
---|
3193 | 4753 | "show the thread COMM next to its id"), |
---|
3194 | 4754 | OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), |
---|
.. | .. |
---|
3201 | 4761 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
---|
3202 | 4762 | "trace events on existing thread id"), |
---|
3203 | 4763 | OPT_CALLBACK(0, "filter-pids", &trace, "CSV list of pids", |
---|
3204 | | - "pids to filter (by the kernel)", trace__set_filter_pids), |
---|
| 4764 | + "pids to filter (by the kernel)", trace__set_filter_pids_from_option), |
---|
3205 | 4765 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, |
---|
3206 | 4766 | "system-wide collection from all CPUs"), |
---|
3207 | 4767 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
---|
.. | .. |
---|
3216 | 4776 | OPT_CALLBACK(0, "duration", &trace, "float", |
---|
3217 | 4777 | "show only events with duration > N.M ms", |
---|
3218 | 4778 | trace__set_duration), |
---|
| 4779 | +#ifdef HAVE_LIBBPF_SUPPORT |
---|
| 4780 | + OPT_STRING(0, "map-dump", &map_dump_str, "BPF map", "BPF map to periodically dump"), |
---|
| 4781 | +#endif |
---|
3219 | 4782 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), |
---|
3220 | 4783 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
---|
3221 | 4784 | OPT_BOOLEAN('T', "time", &trace.full_time, |
---|
.. | .. |
---|
3226 | 4789 | "Show only syscall summary with statistics"), |
---|
3227 | 4790 | OPT_BOOLEAN('S', "with-summary", &trace.summary, |
---|
3228 | 4791 | "Show all syscalls and summary with statistics"), |
---|
| 4792 | + OPT_BOOLEAN(0, "errno-summary", &trace.errno_summary, |
---|
| 4793 | + "Show errno stats per syscall, use with -s or -S"), |
---|
3229 | 4794 | OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min", |
---|
3230 | 4795 | "Trace pagefaults", parse_pagefaults, "maj"), |
---|
3231 | 4796 | OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), |
---|
.. | .. |
---|
3233 | 4798 | OPT_CALLBACK(0, "call-graph", &trace.opts, |
---|
3234 | 4799 | "record_mode[,record_size]", record_callchain_help, |
---|
3235 | 4800 | &record_parse_callchain_opt), |
---|
| 4801 | + OPT_BOOLEAN(0, "libtraceevent_print", &trace.libtraceevent_print, |
---|
| 4802 | + "Use libtraceevent to print the tracepoint arguments."), |
---|
3236 | 4803 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, |
---|
3237 | 4804 | "Show the kernel callchains on the syscall exit path"), |
---|
| 4805 | + OPT_ULONG(0, "max-events", &trace.max_events, |
---|
| 4806 | + "Set the maximum number of events to print, exit after that is reached. "), |
---|
3238 | 4807 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, |
---|
3239 | 4808 | "Set the minimum stack depth when parsing the callchain, " |
---|
3240 | 4809 | "anything below the specified depth will be ignored."), |
---|
.. | .. |
---|
3242 | 4811 | "Set the maximum stack depth when parsing the callchain, " |
---|
3243 | 4812 | "anything beyond the specified depth will be ignored. " |
---|
3244 | 4813 | "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)), |
---|
| 4814 | + OPT_BOOLEAN(0, "sort-events", &trace.sort_events, |
---|
| 4815 | + "Sort batch of events before processing, use if getting out of order events"), |
---|
3245 | 4816 | OPT_BOOLEAN(0, "print-sample", &trace.print_sample, |
---|
3246 | 4817 | "print the PERF_RECORD_SAMPLE PERF_SAMPLE_ info, for debugging"), |
---|
3247 | | - OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, |
---|
| 4818 | + OPT_UINTEGER(0, "proc-map-timeout", &proc_map_timeout, |
---|
3248 | 4819 | "per thread proc mmap processing timeout in ms"), |
---|
3249 | 4820 | OPT_CALLBACK('G', "cgroup", &trace, "name", "monitor event in cgroup name only", |
---|
3250 | 4821 | trace__parse_cgroups), |
---|
3251 | | - OPT_UINTEGER('D', "delay", &trace.opts.initial_delay, |
---|
| 4822 | + OPT_INTEGER('D', "delay", &trace.opts.initial_delay, |
---|
3252 | 4823 | "ms to wait before starting measurement after program " |
---|
3253 | 4824 | "start"), |
---|
| 4825 | + OPTS_EVSWITCH(&trace.evswitch), |
---|
3254 | 4826 | OPT_END() |
---|
3255 | 4827 | }; |
---|
3256 | 4828 | bool __maybe_unused max_stack_user_set = true; |
---|
3257 | 4829 | bool mmap_pages_user_set = true; |
---|
3258 | | - struct perf_evsel *evsel; |
---|
| 4830 | + struct evsel *evsel; |
---|
3259 | 4831 | const char * const trace_subcommands[] = { "record", NULL }; |
---|
3260 | 4832 | int err = -1; |
---|
3261 | 4833 | char bf[BUFSIZ]; |
---|
.. | .. |
---|
3263 | 4835 | signal(SIGSEGV, sighandler_dump_stack); |
---|
3264 | 4836 | signal(SIGFPE, sighandler_dump_stack); |
---|
3265 | 4837 | |
---|
3266 | | - trace.evlist = perf_evlist__new(); |
---|
| 4838 | + trace.evlist = evlist__new(); |
---|
3267 | 4839 | trace.sctbl = syscalltbl__new(); |
---|
3268 | 4840 | |
---|
3269 | 4841 | if (trace.evlist == NULL || trace.sctbl == NULL) { |
---|
.. | .. |
---|
3272 | 4844 | goto out; |
---|
3273 | 4845 | } |
---|
3274 | 4846 | |
---|
| 4847 | + /* |
---|
| 4848 | + * Parsing .perfconfig may entail creating a BPF event, that may need |
---|
| 4849 | + * to create BPF maps, so bump RLIM_MEMLOCK as the default 64K setting |
---|
| 4850 | + * is too small. This affects just this process, not touching the |
---|
| 4851 | + * global setting. If it fails we'll get something in 'perf trace -v' |
---|
| 4852 | + * to help diagnose the problem. |
---|
| 4853 | + */ |
---|
| 4854 | + rlimit__bump_memlock(); |
---|
| 4855 | + |
---|
| 4856 | + err = perf_config(trace__config, &trace); |
---|
| 4857 | + if (err) |
---|
| 4858 | + goto out; |
---|
| 4859 | + |
---|
3275 | 4860 | argc = parse_options_subcommand(argc, argv, trace_options, trace_subcommands, |
---|
3276 | 4861 | trace_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
---|
| 4862 | + |
---|
| 4863 | + /* |
---|
| 4864 | + * Here we already passed thru trace__parse_events_option() and it has |
---|
| 4865 | + * already figured out if -e syscall_name, if not but if --event |
---|
| 4866 | + * foo:bar was used, the user is interested _just_ in those, say, |
---|
| 4867 | + * tracepoint events, not in the strace-like syscall-name-based mode. |
---|
| 4868 | + * |
---|
| 4869 | + * This is important because we need to check if strace-like mode is |
---|
| 4870 | + * needed to decided if we should filter out the eBPF |
---|
| 4871 | + * __augmented_syscalls__ code, if it is in the mix, say, via |
---|
| 4872 | + * .perfconfig trace.add_events, and filter those out. |
---|
| 4873 | + */ |
---|
| 4874 | + if (!trace.trace_syscalls && !trace.trace_pgfaults && |
---|
| 4875 | + trace.evlist->core.nr_entries == 0 /* Was --events used? */) { |
---|
| 4876 | + trace.trace_syscalls = true; |
---|
| 4877 | + } |
---|
| 4878 | + /* |
---|
| 4879 | + * Now that we have --verbose figured out, lets see if we need to parse |
---|
| 4880 | + * events from .perfconfig, so that if those events fail parsing, say some |
---|
| 4881 | + * BPF program fails, then we'll be able to use --verbose to see what went |
---|
| 4882 | + * wrong in more detail. |
---|
| 4883 | + */ |
---|
| 4884 | + if (trace.perfconfig_events != NULL) { |
---|
| 4885 | + struct parse_events_error parse_err; |
---|
| 4886 | + |
---|
| 4887 | + bzero(&parse_err, sizeof(parse_err)); |
---|
| 4888 | + err = parse_events(trace.evlist, trace.perfconfig_events, &parse_err); |
---|
| 4889 | + if (err) { |
---|
| 4890 | + parse_events_print_error(&parse_err, trace.perfconfig_events); |
---|
| 4891 | + goto out; |
---|
| 4892 | + } |
---|
| 4893 | + } |
---|
3277 | 4894 | |
---|
3278 | 4895 | if ((nr_cgroups || trace.cgroup) && !trace.opts.target.system_wide) { |
---|
3279 | 4896 | usage_with_options_msg(trace_usage, trace_options, |
---|
.. | .. |
---|
3288 | 4905 | } |
---|
3289 | 4906 | |
---|
3290 | 4907 | if (evsel) { |
---|
3291 | | - if (perf_evsel__init_augmented_syscall_tp(evsel) || |
---|
3292 | | - perf_evsel__init_augmented_syscall_tp_args(evsel)) |
---|
3293 | | - goto out; |
---|
3294 | 4908 | trace.syscalls.events.augmented = evsel; |
---|
| 4909 | + |
---|
| 4910 | + evsel = perf_evlist__find_tracepoint_by_name(trace.evlist, "raw_syscalls:sys_enter"); |
---|
| 4911 | + if (evsel == NULL) { |
---|
| 4912 | + pr_err("ERROR: raw_syscalls:sys_enter not found in the augmented BPF object\n"); |
---|
| 4913 | + goto out; |
---|
| 4914 | + } |
---|
| 4915 | + |
---|
| 4916 | + if (evsel->bpf_obj == NULL) { |
---|
| 4917 | + pr_err("ERROR: raw_syscalls:sys_enter not associated to a BPF object\n"); |
---|
| 4918 | + goto out; |
---|
| 4919 | + } |
---|
| 4920 | + |
---|
| 4921 | + trace.bpf_obj = evsel->bpf_obj; |
---|
| 4922 | + |
---|
| 4923 | + /* |
---|
| 4924 | + * If we have _just_ the augmenter event but don't have a |
---|
| 4925 | + * explicit --syscalls, then assume we want all strace-like |
---|
| 4926 | + * syscalls: |
---|
| 4927 | + */ |
---|
| 4928 | + if (!trace.trace_syscalls && trace__only_augmented_syscalls_evsels(&trace)) |
---|
| 4929 | + trace.trace_syscalls = true; |
---|
| 4930 | + /* |
---|
| 4931 | + * So, if we have a syscall augmenter, but trace_syscalls, aka |
---|
| 4932 | + * strace-like syscall tracing is not set, then we need to trow |
---|
| 4933 | + * away the augmenter, i.e. all the events that were created |
---|
| 4934 | + * from that BPF object file. |
---|
| 4935 | + * |
---|
| 4936 | + * This is more to fix the current .perfconfig trace.add_events |
---|
| 4937 | + * style of setting up the strace-like eBPF based syscall point |
---|
| 4938 | + * payload augmenter. |
---|
| 4939 | + * |
---|
| 4940 | + * All this complexity will be avoided by adding an alternative |
---|
| 4941 | + * to trace.add_events in the form of |
---|
| 4942 | + * trace.bpf_augmented_syscalls, that will be only parsed if we |
---|
| 4943 | + * need it. |
---|
| 4944 | + * |
---|
| 4945 | + * .perfconfig trace.add_events is still useful if we want, for |
---|
| 4946 | + * instance, have msr_write.msr in some .perfconfig profile based |
---|
| 4947 | + * 'perf trace --config determinism.profile' mode, where for some |
---|
| 4948 | + * particular goal/workload type we want a set of events and |
---|
| 4949 | + * output mode (with timings, etc) instead of having to add |
---|
| 4950 | + * all via the command line. |
---|
| 4951 | + * |
---|
| 4952 | + * Also --config to specify an alternate .perfconfig file needs |
---|
| 4953 | + * to be implemented. |
---|
| 4954 | + */ |
---|
| 4955 | + if (!trace.trace_syscalls) { |
---|
| 4956 | + trace__delete_augmented_syscalls(&trace); |
---|
| 4957 | + } else { |
---|
| 4958 | + trace__set_bpf_map_filtered_pids(&trace); |
---|
| 4959 | + trace__set_bpf_map_syscalls(&trace); |
---|
| 4960 | + trace.syscalls.unaugmented_prog = trace__find_bpf_program_by_title(&trace, "!raw_syscalls:unaugmented"); |
---|
| 4961 | + } |
---|
3295 | 4962 | } |
---|
3296 | 4963 | |
---|
3297 | 4964 | err = bpf__setup_stdout(trace.evlist); |
---|
.. | .. |
---|
3302 | 4969 | } |
---|
3303 | 4970 | |
---|
3304 | 4971 | err = -1; |
---|
| 4972 | + |
---|
| 4973 | + if (map_dump_str) { |
---|
| 4974 | + trace.dump.map = trace__find_bpf_map_by_name(&trace, map_dump_str); |
---|
| 4975 | + if (trace.dump.map == NULL) { |
---|
| 4976 | + pr_err("ERROR: BPF map \"%s\" not found\n", map_dump_str); |
---|
| 4977 | + goto out; |
---|
| 4978 | + } |
---|
| 4979 | + } |
---|
3305 | 4980 | |
---|
3306 | 4981 | if (trace.trace_pgfaults) { |
---|
3307 | 4982 | trace.opts.sample_address = true; |
---|
.. | .. |
---|
3329 | 5004 | symbol_conf.use_callchain = true; |
---|
3330 | 5005 | } |
---|
3331 | 5006 | |
---|
3332 | | - if (trace.evlist->nr_entries > 0) { |
---|
3333 | | - evlist__set_evsel_handler(trace.evlist, trace__event_handler); |
---|
| 5007 | + if (trace.evlist->core.nr_entries > 0) { |
---|
| 5008 | + evlist__set_default_evsel_handler(trace.evlist, trace__event_handler); |
---|
3334 | 5009 | if (evlist__set_syscall_tp_fields(trace.evlist)) { |
---|
3335 | 5010 | perror("failed to set syscalls:* tracepoint fields"); |
---|
3336 | 5011 | goto out; |
---|
3337 | 5012 | } |
---|
3338 | 5013 | } |
---|
3339 | 5014 | |
---|
| 5015 | + if (trace.sort_events) { |
---|
| 5016 | + ordered_events__init(&trace.oe.data, ordered_events__deliver_event, &trace); |
---|
| 5017 | + ordered_events__set_copy_on_queue(&trace.oe.data, true); |
---|
| 5018 | + } |
---|
| 5019 | + |
---|
| 5020 | + /* |
---|
| 5021 | + * If we are augmenting syscalls, then combine what we put in the |
---|
| 5022 | + * __augmented_syscalls__ BPF map with what is in the |
---|
| 5023 | + * syscalls:sys_exit_FOO tracepoints, i.e. just like we do without BPF, |
---|
| 5024 | + * combining raw_syscalls:sys_enter with raw_syscalls:sys_exit. |
---|
| 5025 | + * |
---|
| 5026 | + * We'll switch to look at two BPF maps, one for sys_enter and the |
---|
| 5027 | + * other for sys_exit when we start augmenting the sys_exit paths with |
---|
| 5028 | + * buffers that are being copied from kernel to userspace, think 'read' |
---|
| 5029 | + * syscall. |
---|
| 5030 | + */ |
---|
| 5031 | + if (trace.syscalls.events.augmented) { |
---|
| 5032 | + evlist__for_each_entry(trace.evlist, evsel) { |
---|
| 5033 | + bool raw_syscalls_sys_exit = strcmp(evsel__name(evsel), "raw_syscalls:sys_exit") == 0; |
---|
| 5034 | + |
---|
| 5035 | + if (raw_syscalls_sys_exit) { |
---|
| 5036 | + trace.raw_augmented_syscalls = true; |
---|
| 5037 | + goto init_augmented_syscall_tp; |
---|
| 5038 | + } |
---|
| 5039 | + |
---|
| 5040 | + if (trace.syscalls.events.augmented->priv == NULL && |
---|
| 5041 | + strstr(evsel__name(evsel), "syscalls:sys_enter")) { |
---|
| 5042 | + struct evsel *augmented = trace.syscalls.events.augmented; |
---|
| 5043 | + if (evsel__init_augmented_syscall_tp(augmented, evsel) || |
---|
| 5044 | + evsel__init_augmented_syscall_tp_args(augmented)) |
---|
| 5045 | + goto out; |
---|
| 5046 | + /* |
---|
| 5047 | + * Augmented is __augmented_syscalls__ BPF_OUTPUT event |
---|
| 5048 | + * Above we made sure we can get from the payload the tp fields |
---|
| 5049 | + * that we get from syscalls:sys_enter tracefs format file. |
---|
| 5050 | + */ |
---|
| 5051 | + augmented->handler = trace__sys_enter; |
---|
| 5052 | + /* |
---|
| 5053 | + * Now we do the same for the *syscalls:sys_enter event so that |
---|
| 5054 | + * if we handle it directly, i.e. if the BPF prog returns 0 so |
---|
| 5055 | + * as not to filter it, then we'll handle it just like we would |
---|
| 5056 | + * for the BPF_OUTPUT one: |
---|
| 5057 | + */ |
---|
| 5058 | + if (evsel__init_augmented_syscall_tp(evsel, evsel) || |
---|
| 5059 | + evsel__init_augmented_syscall_tp_args(evsel)) |
---|
| 5060 | + goto out; |
---|
| 5061 | + evsel->handler = trace__sys_enter; |
---|
| 5062 | + } |
---|
| 5063 | + |
---|
| 5064 | + if (strstarts(evsel__name(evsel), "syscalls:sys_exit_")) { |
---|
| 5065 | + struct syscall_tp *sc; |
---|
| 5066 | +init_augmented_syscall_tp: |
---|
| 5067 | + if (evsel__init_augmented_syscall_tp(evsel, evsel)) |
---|
| 5068 | + goto out; |
---|
| 5069 | + sc = __evsel__syscall_tp(evsel); |
---|
| 5070 | + /* |
---|
| 5071 | + * For now with BPF raw_augmented we hook into |
---|
| 5072 | + * raw_syscalls:sys_enter and there we get all |
---|
| 5073 | + * 6 syscall args plus the tracepoint common |
---|
| 5074 | + * fields and the syscall_nr (another long). |
---|
| 5075 | + * So we check if that is the case and if so |
---|
| 5076 | + * don't look after the sc->args_size but |
---|
| 5077 | + * always after the full raw_syscalls:sys_enter |
---|
| 5078 | + * payload, which is fixed. |
---|
| 5079 | + * |
---|
| 5080 | + * We'll revisit this later to pass |
---|
| 5081 | + * s->args_size to the BPF augmenter (now |
---|
| 5082 | + * tools/perf/examples/bpf/augmented_raw_syscalls.c, |
---|
| 5083 | + * so that it copies only what we need for each |
---|
| 5084 | + * syscall, like what happens when we use |
---|
| 5085 | + * syscalls:sys_enter_NAME, so that we reduce |
---|
| 5086 | + * the kernel/userspace traffic to just what is |
---|
| 5087 | + * needed for each syscall. |
---|
| 5088 | + */ |
---|
| 5089 | + if (trace.raw_augmented_syscalls) |
---|
| 5090 | + trace.raw_augmented_syscalls_args_size = (6 + 1) * sizeof(long) + sc->id.offset; |
---|
| 5091 | + evsel__init_augmented_syscall_tp_ret(evsel); |
---|
| 5092 | + evsel->handler = trace__sys_exit; |
---|
| 5093 | + } |
---|
| 5094 | + } |
---|
| 5095 | + } |
---|
| 5096 | + |
---|
3340 | 5097 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) |
---|
3341 | 5098 | return trace__record(&trace, argc-1, &argv[1]); |
---|
| 5099 | + |
---|
| 5100 | + /* Using just --errno-summary will trigger --summary */ |
---|
| 5101 | + if (trace.errno_summary && !trace.summary && !trace.summary_only) |
---|
| 5102 | + trace.summary_only = true; |
---|
3342 | 5103 | |
---|
3343 | 5104 | /* summary_only implies summary option, but don't overwrite summary if set */ |
---|
3344 | 5105 | if (trace.summary_only) |
---|
3345 | 5106 | trace.summary = trace.summary_only; |
---|
3346 | | - |
---|
3347 | | - if (!trace.trace_syscalls && !trace.trace_pgfaults && |
---|
3348 | | - trace.evlist->nr_entries == 0 /* Was --events used? */) { |
---|
3349 | | - trace.trace_syscalls = true; |
---|
3350 | | - } |
---|
3351 | 5107 | |
---|
3352 | 5108 | if (output_name != NULL) { |
---|
3353 | 5109 | err = trace__open_output(&trace, output_name); |
---|
.. | .. |
---|
3356 | 5112 | goto out; |
---|
3357 | 5113 | } |
---|
3358 | 5114 | } |
---|
| 5115 | + |
---|
| 5116 | + err = evswitch__init(&trace.evswitch, trace.evlist, stderr); |
---|
| 5117 | + if (err) |
---|
| 5118 | + goto out_close; |
---|
3359 | 5119 | |
---|
3360 | 5120 | err = target__validate(&trace.opts.target); |
---|
3361 | 5121 | if (err) { |
---|
.. | .. |
---|
3383 | 5143 | if (output_name != NULL) |
---|
3384 | 5144 | fclose(trace.output); |
---|
3385 | 5145 | out: |
---|
| 5146 | + zfree(&trace.perfconfig_events); |
---|
3386 | 5147 | return err; |
---|
3387 | 5148 | } |
---|