.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * builtin-ftrace.c |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> |
---|
5 | | - * |
---|
6 | | - * Released under the GPL v2. |
---|
| 6 | + * Copyright (c) 2020 Changbin Du <changbin.du@gmail.com>, significant enhancement. |
---|
7 | 7 | */ |
---|
8 | 8 | |
---|
9 | 9 | #include "builtin.h" |
---|
10 | | -#include "perf.h" |
---|
11 | 10 | |
---|
12 | 11 | #include <errno.h> |
---|
13 | 12 | #include <unistd.h> |
---|
14 | 13 | #include <signal.h> |
---|
| 14 | +#include <stdlib.h> |
---|
15 | 15 | #include <fcntl.h> |
---|
16 | 16 | #include <poll.h> |
---|
| 17 | +#include <linux/capability.h> |
---|
| 18 | +#include <linux/string.h> |
---|
17 | 19 | |
---|
18 | 20 | #include "debug.h" |
---|
| 21 | +#include <subcmd/pager.h> |
---|
19 | 22 | #include <subcmd/parse-options.h> |
---|
20 | 23 | #include <api/fs/tracing_path.h> |
---|
21 | 24 | #include "evlist.h" |
---|
22 | 25 | #include "target.h" |
---|
23 | 26 | #include "cpumap.h" |
---|
24 | 27 | #include "thread_map.h" |
---|
| 28 | +#include "strfilter.h" |
---|
| 29 | +#include "util/cap.h" |
---|
25 | 30 | #include "util/config.h" |
---|
26 | | - |
---|
| 31 | +#include "util/units.h" |
---|
| 32 | +#include "util/parse-sublevel-options.h" |
---|
27 | 33 | |
---|
28 | 34 | #define DEFAULT_TRACER "function_graph" |
---|
29 | 35 | |
---|
30 | 36 | struct perf_ftrace { |
---|
31 | | - struct perf_evlist *evlist; |
---|
| 37 | + struct evlist *evlist; |
---|
32 | 38 | struct target target; |
---|
33 | 39 | const char *tracer; |
---|
34 | 40 | struct list_head filters; |
---|
.. | .. |
---|
36 | 42 | struct list_head graph_funcs; |
---|
37 | 43 | struct list_head nograph_funcs; |
---|
38 | 44 | int graph_depth; |
---|
| 45 | + unsigned long percpu_buffer_size; |
---|
| 46 | + bool inherit; |
---|
| 47 | + int func_stack_trace; |
---|
| 48 | + int func_irq_info; |
---|
| 49 | + int graph_nosleep_time; |
---|
| 50 | + int graph_noirqs; |
---|
| 51 | + int graph_verbose; |
---|
| 52 | + int graph_thresh; |
---|
| 53 | + unsigned int initial_delay; |
---|
39 | 54 | }; |
---|
40 | 55 | |
---|
41 | 56 | struct filter_entry { |
---|
.. | .. |
---|
43 | 58 | char name[]; |
---|
44 | 59 | }; |
---|
45 | 60 | |
---|
| 61 | +static volatile int workload_exec_errno; |
---|
46 | 62 | static bool done; |
---|
47 | 63 | |
---|
48 | 64 | static void sig_handler(int sig __maybe_unused) |
---|
.. | .. |
---|
61 | 77 | siginfo_t *info __maybe_unused, |
---|
62 | 78 | void *ucontext __maybe_unused) |
---|
63 | 79 | { |
---|
64 | | - /* workload_exec_errno = info->si_value.sival_int; */ |
---|
| 80 | + workload_exec_errno = info->si_value.sival_int; |
---|
65 | 81 | done = true; |
---|
66 | 82 | } |
---|
67 | 83 | |
---|
.. | .. |
---|
125 | 141 | return __write_tracing_file(name, val, true); |
---|
126 | 142 | } |
---|
127 | 143 | |
---|
| 144 | +static int read_tracing_file_to_stdout(const char *name) |
---|
| 145 | +{ |
---|
| 146 | + char buf[4096]; |
---|
| 147 | + char *file; |
---|
| 148 | + int fd; |
---|
| 149 | + int ret = -1; |
---|
| 150 | + |
---|
| 151 | + file = get_tracing_file(name); |
---|
| 152 | + if (!file) { |
---|
| 153 | + pr_debug("cannot get tracing file: %s\n", name); |
---|
| 154 | + return -1; |
---|
| 155 | + } |
---|
| 156 | + |
---|
| 157 | + fd = open(file, O_RDONLY); |
---|
| 158 | + if (fd < 0) { |
---|
| 159 | + pr_debug("cannot open tracing file: %s: %s\n", |
---|
| 160 | + name, str_error_r(errno, buf, sizeof(buf))); |
---|
| 161 | + goto out; |
---|
| 162 | + } |
---|
| 163 | + |
---|
| 164 | + /* read contents to stdout */ |
---|
| 165 | + while (true) { |
---|
| 166 | + int n = read(fd, buf, sizeof(buf)); |
---|
| 167 | + if (n == 0) |
---|
| 168 | + break; |
---|
| 169 | + else if (n < 0) |
---|
| 170 | + goto out_close; |
---|
| 171 | + |
---|
| 172 | + if (fwrite(buf, n, 1, stdout) != 1) |
---|
| 173 | + goto out_close; |
---|
| 174 | + } |
---|
| 175 | + ret = 0; |
---|
| 176 | + |
---|
| 177 | +out_close: |
---|
| 178 | + close(fd); |
---|
| 179 | +out: |
---|
| 180 | + put_tracing_file(file); |
---|
| 181 | + return ret; |
---|
| 182 | +} |
---|
| 183 | + |
---|
| 184 | +static int read_tracing_file_by_line(const char *name, |
---|
| 185 | + void (*cb)(char *str, void *arg), |
---|
| 186 | + void *cb_arg) |
---|
| 187 | +{ |
---|
| 188 | + char *line = NULL; |
---|
| 189 | + size_t len = 0; |
---|
| 190 | + char *file; |
---|
| 191 | + FILE *fp; |
---|
| 192 | + |
---|
| 193 | + file = get_tracing_file(name); |
---|
| 194 | + if (!file) { |
---|
| 195 | + pr_debug("cannot get tracing file: %s\n", name); |
---|
| 196 | + return -1; |
---|
| 197 | + } |
---|
| 198 | + |
---|
| 199 | + fp = fopen(file, "r"); |
---|
| 200 | + if (fp == NULL) { |
---|
| 201 | + pr_debug("cannot open tracing file: %s\n", name); |
---|
| 202 | + put_tracing_file(file); |
---|
| 203 | + return -1; |
---|
| 204 | + } |
---|
| 205 | + |
---|
| 206 | + while (getline(&line, &len, fp) != -1) { |
---|
| 207 | + cb(line, cb_arg); |
---|
| 208 | + } |
---|
| 209 | + |
---|
| 210 | + if (line) |
---|
| 211 | + free(line); |
---|
| 212 | + |
---|
| 213 | + fclose(fp); |
---|
| 214 | + put_tracing_file(file); |
---|
| 215 | + return 0; |
---|
| 216 | +} |
---|
| 217 | + |
---|
| 218 | +static int write_tracing_file_int(const char *name, int value) |
---|
| 219 | +{ |
---|
| 220 | + char buf[16]; |
---|
| 221 | + |
---|
| 222 | + snprintf(buf, sizeof(buf), "%d", value); |
---|
| 223 | + if (write_tracing_file(name, buf) < 0) |
---|
| 224 | + return -1; |
---|
| 225 | + |
---|
| 226 | + return 0; |
---|
| 227 | +} |
---|
| 228 | + |
---|
| 229 | +static int write_tracing_option_file(const char *name, const char *val) |
---|
| 230 | +{ |
---|
| 231 | + char *file; |
---|
| 232 | + int ret; |
---|
| 233 | + |
---|
| 234 | + if (asprintf(&file, "options/%s", name) < 0) |
---|
| 235 | + return -1; |
---|
| 236 | + |
---|
| 237 | + ret = __write_tracing_file(file, val, false); |
---|
| 238 | + free(file); |
---|
| 239 | + return ret; |
---|
| 240 | +} |
---|
| 241 | + |
---|
128 | 242 | static int reset_tracing_cpu(void); |
---|
129 | 243 | static void reset_tracing_filters(void); |
---|
| 244 | + |
---|
| 245 | +static void reset_tracing_options(struct perf_ftrace *ftrace __maybe_unused) |
---|
| 246 | +{ |
---|
| 247 | + write_tracing_option_file("function-fork", "0"); |
---|
| 248 | + write_tracing_option_file("func_stack_trace", "0"); |
---|
| 249 | + write_tracing_option_file("sleep-time", "1"); |
---|
| 250 | + write_tracing_option_file("funcgraph-irqs", "1"); |
---|
| 251 | + write_tracing_option_file("funcgraph-proc", "0"); |
---|
| 252 | + write_tracing_option_file("funcgraph-abstime", "0"); |
---|
| 253 | + write_tracing_option_file("latency-format", "0"); |
---|
| 254 | + write_tracing_option_file("irq-info", "0"); |
---|
| 255 | +} |
---|
130 | 256 | |
---|
131 | 257 | static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) |
---|
132 | 258 | { |
---|
.. | .. |
---|
145 | 271 | if (write_tracing_file("max_graph_depth", "0") < 0) |
---|
146 | 272 | return -1; |
---|
147 | 273 | |
---|
| 274 | + if (write_tracing_file("tracing_thresh", "0") < 0) |
---|
| 275 | + return -1; |
---|
| 276 | + |
---|
148 | 277 | reset_tracing_filters(); |
---|
| 278 | + reset_tracing_options(ftrace); |
---|
149 | 279 | return 0; |
---|
150 | 280 | } |
---|
151 | 281 | |
---|
.. | .. |
---|
157 | 287 | if (target__has_cpu(&ftrace->target)) |
---|
158 | 288 | return 0; |
---|
159 | 289 | |
---|
160 | | - for (i = 0; i < thread_map__nr(ftrace->evlist->threads); i++) { |
---|
| 290 | + for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) { |
---|
161 | 291 | scnprintf(buf, sizeof(buf), "%d", |
---|
162 | | - ftrace->evlist->threads->map[i]); |
---|
| 292 | + perf_thread_map__pid(ftrace->evlist->core.threads, i)); |
---|
163 | 293 | if (append_tracing_file("set_ftrace_pid", buf) < 0) |
---|
164 | 294 | return -1; |
---|
165 | 295 | } |
---|
166 | 296 | return 0; |
---|
167 | 297 | } |
---|
168 | 298 | |
---|
169 | | -static int set_tracing_cpumask(struct cpu_map *cpumap) |
---|
| 299 | +static int set_tracing_cpumask(struct perf_cpu_map *cpumap) |
---|
170 | 300 | { |
---|
171 | 301 | char *cpumask; |
---|
172 | 302 | size_t mask_size; |
---|
.. | .. |
---|
193 | 323 | |
---|
194 | 324 | static int set_tracing_cpu(struct perf_ftrace *ftrace) |
---|
195 | 325 | { |
---|
196 | | - struct cpu_map *cpumap = ftrace->evlist->cpus; |
---|
| 326 | + struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus; |
---|
197 | 327 | |
---|
198 | 328 | if (!target__has_cpu(&ftrace->target)) |
---|
199 | 329 | return 0; |
---|
.. | .. |
---|
201 | 331 | return set_tracing_cpumask(cpumap); |
---|
202 | 332 | } |
---|
203 | 333 | |
---|
| 334 | +static int set_tracing_func_stack_trace(struct perf_ftrace *ftrace) |
---|
| 335 | +{ |
---|
| 336 | + if (!ftrace->func_stack_trace) |
---|
| 337 | + return 0; |
---|
| 338 | + |
---|
| 339 | + if (write_tracing_option_file("func_stack_trace", "1") < 0) |
---|
| 340 | + return -1; |
---|
| 341 | + |
---|
| 342 | + return 0; |
---|
| 343 | +} |
---|
| 344 | + |
---|
| 345 | +static int set_tracing_func_irqinfo(struct perf_ftrace *ftrace) |
---|
| 346 | +{ |
---|
| 347 | + if (!ftrace->func_irq_info) |
---|
| 348 | + return 0; |
---|
| 349 | + |
---|
| 350 | + if (write_tracing_option_file("irq-info", "1") < 0) |
---|
| 351 | + return -1; |
---|
| 352 | + |
---|
| 353 | + return 0; |
---|
| 354 | +} |
---|
| 355 | + |
---|
204 | 356 | static int reset_tracing_cpu(void) |
---|
205 | 357 | { |
---|
206 | | - struct cpu_map *cpumap = cpu_map__new(NULL); |
---|
| 358 | + struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL); |
---|
207 | 359 | int ret; |
---|
208 | 360 | |
---|
209 | 361 | ret = set_tracing_cpumask(cpumap); |
---|
210 | | - cpu_map__put(cpumap); |
---|
| 362 | + perf_cpu_map__put(cpumap); |
---|
211 | 363 | return ret; |
---|
212 | 364 | } |
---|
213 | 365 | |
---|
.. | .. |
---|
255 | 407 | |
---|
256 | 408 | static int set_tracing_depth(struct perf_ftrace *ftrace) |
---|
257 | 409 | { |
---|
258 | | - char buf[16]; |
---|
259 | | - |
---|
260 | 410 | if (ftrace->graph_depth == 0) |
---|
261 | 411 | return 0; |
---|
262 | 412 | |
---|
.. | .. |
---|
265 | 415 | return -1; |
---|
266 | 416 | } |
---|
267 | 417 | |
---|
268 | | - snprintf(buf, sizeof(buf), "%d", ftrace->graph_depth); |
---|
269 | | - |
---|
270 | | - if (write_tracing_file("max_graph_depth", buf) < 0) |
---|
| 418 | + if (write_tracing_file_int("max_graph_depth", ftrace->graph_depth) < 0) |
---|
271 | 419 | return -1; |
---|
| 420 | + |
---|
| 421 | + return 0; |
---|
| 422 | +} |
---|
| 423 | + |
---|
| 424 | +static int set_tracing_percpu_buffer_size(struct perf_ftrace *ftrace) |
---|
| 425 | +{ |
---|
| 426 | + int ret; |
---|
| 427 | + |
---|
| 428 | + if (ftrace->percpu_buffer_size == 0) |
---|
| 429 | + return 0; |
---|
| 430 | + |
---|
| 431 | + ret = write_tracing_file_int("buffer_size_kb", |
---|
| 432 | + ftrace->percpu_buffer_size / 1024); |
---|
| 433 | + if (ret < 0) |
---|
| 434 | + return ret; |
---|
| 435 | + |
---|
| 436 | + return 0; |
---|
| 437 | +} |
---|
| 438 | + |
---|
| 439 | +static int set_tracing_trace_inherit(struct perf_ftrace *ftrace) |
---|
| 440 | +{ |
---|
| 441 | + if (!ftrace->inherit) |
---|
| 442 | + return 0; |
---|
| 443 | + |
---|
| 444 | + if (write_tracing_option_file("function-fork", "1") < 0) |
---|
| 445 | + return -1; |
---|
| 446 | + |
---|
| 447 | + return 0; |
---|
| 448 | +} |
---|
| 449 | + |
---|
| 450 | +static int set_tracing_sleep_time(struct perf_ftrace *ftrace) |
---|
| 451 | +{ |
---|
| 452 | + if (!ftrace->graph_nosleep_time) |
---|
| 453 | + return 0; |
---|
| 454 | + |
---|
| 455 | + if (write_tracing_option_file("sleep-time", "0") < 0) |
---|
| 456 | + return -1; |
---|
| 457 | + |
---|
| 458 | + return 0; |
---|
| 459 | +} |
---|
| 460 | + |
---|
| 461 | +static int set_tracing_funcgraph_irqs(struct perf_ftrace *ftrace) |
---|
| 462 | +{ |
---|
| 463 | + if (!ftrace->graph_noirqs) |
---|
| 464 | + return 0; |
---|
| 465 | + |
---|
| 466 | + if (write_tracing_option_file("funcgraph-irqs", "0") < 0) |
---|
| 467 | + return -1; |
---|
| 468 | + |
---|
| 469 | + return 0; |
---|
| 470 | +} |
---|
| 471 | + |
---|
| 472 | +static int set_tracing_funcgraph_verbose(struct perf_ftrace *ftrace) |
---|
| 473 | +{ |
---|
| 474 | + if (!ftrace->graph_verbose) |
---|
| 475 | + return 0; |
---|
| 476 | + |
---|
| 477 | + if (write_tracing_option_file("funcgraph-proc", "1") < 0) |
---|
| 478 | + return -1; |
---|
| 479 | + |
---|
| 480 | + if (write_tracing_option_file("funcgraph-abstime", "1") < 0) |
---|
| 481 | + return -1; |
---|
| 482 | + |
---|
| 483 | + if (write_tracing_option_file("latency-format", "1") < 0) |
---|
| 484 | + return -1; |
---|
| 485 | + |
---|
| 486 | + return 0; |
---|
| 487 | +} |
---|
| 488 | + |
---|
| 489 | +static int set_tracing_thresh(struct perf_ftrace *ftrace) |
---|
| 490 | +{ |
---|
| 491 | + int ret; |
---|
| 492 | + |
---|
| 493 | + if (ftrace->graph_thresh == 0) |
---|
| 494 | + return 0; |
---|
| 495 | + |
---|
| 496 | + ret = write_tracing_file_int("tracing_thresh", ftrace->graph_thresh); |
---|
| 497 | + if (ret < 0) |
---|
| 498 | + return ret; |
---|
| 499 | + |
---|
| 500 | + return 0; |
---|
| 501 | +} |
---|
| 502 | + |
---|
| 503 | +static int set_tracing_options(struct perf_ftrace *ftrace) |
---|
| 504 | +{ |
---|
| 505 | + if (set_tracing_pid(ftrace) < 0) { |
---|
| 506 | + pr_err("failed to set ftrace pid\n"); |
---|
| 507 | + return -1; |
---|
| 508 | + } |
---|
| 509 | + |
---|
| 510 | + if (set_tracing_cpu(ftrace) < 0) { |
---|
| 511 | + pr_err("failed to set tracing cpumask\n"); |
---|
| 512 | + return -1; |
---|
| 513 | + } |
---|
| 514 | + |
---|
| 515 | + if (set_tracing_func_stack_trace(ftrace) < 0) { |
---|
| 516 | + pr_err("failed to set tracing option func_stack_trace\n"); |
---|
| 517 | + return -1; |
---|
| 518 | + } |
---|
| 519 | + |
---|
| 520 | + if (set_tracing_func_irqinfo(ftrace) < 0) { |
---|
| 521 | + pr_err("failed to set tracing option irq-info\n"); |
---|
| 522 | + return -1; |
---|
| 523 | + } |
---|
| 524 | + |
---|
| 525 | + if (set_tracing_filters(ftrace) < 0) { |
---|
| 526 | + pr_err("failed to set tracing filters\n"); |
---|
| 527 | + return -1; |
---|
| 528 | + } |
---|
| 529 | + |
---|
| 530 | + if (set_tracing_depth(ftrace) < 0) { |
---|
| 531 | + pr_err("failed to set graph depth\n"); |
---|
| 532 | + return -1; |
---|
| 533 | + } |
---|
| 534 | + |
---|
| 535 | + if (set_tracing_percpu_buffer_size(ftrace) < 0) { |
---|
| 536 | + pr_err("failed to set tracing per-cpu buffer size\n"); |
---|
| 537 | + return -1; |
---|
| 538 | + } |
---|
| 539 | + |
---|
| 540 | + if (set_tracing_trace_inherit(ftrace) < 0) { |
---|
| 541 | + pr_err("failed to set tracing option function-fork\n"); |
---|
| 542 | + return -1; |
---|
| 543 | + } |
---|
| 544 | + |
---|
| 545 | + if (set_tracing_sleep_time(ftrace) < 0) { |
---|
| 546 | + pr_err("failed to set tracing option sleep-time\n"); |
---|
| 547 | + return -1; |
---|
| 548 | + } |
---|
| 549 | + |
---|
| 550 | + if (set_tracing_funcgraph_irqs(ftrace) < 0) { |
---|
| 551 | + pr_err("failed to set tracing option funcgraph-irqs\n"); |
---|
| 552 | + return -1; |
---|
| 553 | + } |
---|
| 554 | + |
---|
| 555 | + if (set_tracing_funcgraph_verbose(ftrace) < 0) { |
---|
| 556 | + pr_err("failed to set tracing option funcgraph-proc/funcgraph-abstime\n"); |
---|
| 557 | + return -1; |
---|
| 558 | + } |
---|
| 559 | + |
---|
| 560 | + if (set_tracing_thresh(ftrace) < 0) { |
---|
| 561 | + pr_err("failed to set tracing thresh\n"); |
---|
| 562 | + return -1; |
---|
| 563 | + } |
---|
272 | 564 | |
---|
273 | 565 | return 0; |
---|
274 | 566 | } |
---|
.. | .. |
---|
282 | 574 | .events = POLLIN, |
---|
283 | 575 | }; |
---|
284 | 576 | |
---|
285 | | - if (geteuid() != 0) { |
---|
286 | | - pr_err("ftrace only works for root!\n"); |
---|
| 577 | + if (!(perf_cap__capable(CAP_PERFMON) || |
---|
| 578 | + perf_cap__capable(CAP_SYS_ADMIN))) { |
---|
| 579 | + pr_err("ftrace only works for %s!\n", |
---|
| 580 | +#ifdef HAVE_LIBCAP_SUPPORT |
---|
| 581 | + "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" |
---|
| 582 | +#else |
---|
| 583 | + "root" |
---|
| 584 | +#endif |
---|
| 585 | + ); |
---|
287 | 586 | return -1; |
---|
288 | 587 | } |
---|
289 | 588 | |
---|
.. | .. |
---|
307 | 606 | goto out; |
---|
308 | 607 | } |
---|
309 | 608 | |
---|
310 | | - if (set_tracing_pid(ftrace) < 0) { |
---|
311 | | - pr_err("failed to set ftrace pid\n"); |
---|
| 609 | + if (set_tracing_options(ftrace) < 0) |
---|
312 | 610 | goto out_reset; |
---|
313 | | - } |
---|
314 | | - |
---|
315 | | - if (set_tracing_cpu(ftrace) < 0) { |
---|
316 | | - pr_err("failed to set tracing cpumask\n"); |
---|
317 | | - goto out_reset; |
---|
318 | | - } |
---|
319 | | - |
---|
320 | | - if (set_tracing_filters(ftrace) < 0) { |
---|
321 | | - pr_err("failed to set tracing filters\n"); |
---|
322 | | - goto out_reset; |
---|
323 | | - } |
---|
324 | | - |
---|
325 | | - if (set_tracing_depth(ftrace) < 0) { |
---|
326 | | - pr_err("failed to set graph depth\n"); |
---|
327 | | - goto out_reset; |
---|
328 | | - } |
---|
329 | 611 | |
---|
330 | 612 | if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { |
---|
331 | 613 | pr_err("failed to set current_tracer to %s\n", ftrace->tracer); |
---|
.. | .. |
---|
352 | 634 | fcntl(trace_fd, F_SETFL, O_NONBLOCK); |
---|
353 | 635 | pollfd.fd = trace_fd; |
---|
354 | 636 | |
---|
355 | | - if (write_tracing_file("tracing_on", "1") < 0) { |
---|
356 | | - pr_err("can't enable tracing\n"); |
---|
357 | | - goto out_close_fd; |
---|
| 637 | + /* display column headers */ |
---|
| 638 | + read_tracing_file_to_stdout("trace"); |
---|
| 639 | + |
---|
| 640 | + if (!ftrace->initial_delay) { |
---|
| 641 | + if (write_tracing_file("tracing_on", "1") < 0) { |
---|
| 642 | + pr_err("can't enable tracing\n"); |
---|
| 643 | + goto out_close_fd; |
---|
| 644 | + } |
---|
358 | 645 | } |
---|
359 | 646 | |
---|
360 | 647 | perf_evlist__start_workload(ftrace->evlist); |
---|
| 648 | + |
---|
| 649 | + if (ftrace->initial_delay) { |
---|
| 650 | + usleep(ftrace->initial_delay * 1000); |
---|
| 651 | + if (write_tracing_file("tracing_on", "1") < 0) { |
---|
| 652 | + pr_err("can't enable tracing\n"); |
---|
| 653 | + goto out_close_fd; |
---|
| 654 | + } |
---|
| 655 | + } |
---|
361 | 656 | |
---|
362 | 657 | while (!done) { |
---|
363 | 658 | if (poll(&pollfd, 1, -1) < 0) |
---|
.. | .. |
---|
374 | 669 | |
---|
375 | 670 | write_tracing_file("tracing_on", "0"); |
---|
376 | 671 | |
---|
| 672 | + if (workload_exec_errno) { |
---|
| 673 | + const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf)); |
---|
| 674 | + /* flush stdout first so below error msg appears at the end. */ |
---|
| 675 | + fflush(stdout); |
---|
| 676 | + pr_err("workload failed: %s\n", emsg); |
---|
| 677 | + goto out_close_fd; |
---|
| 678 | + } |
---|
| 679 | + |
---|
377 | 680 | /* read remaining buffer contents */ |
---|
378 | 681 | while (true) { |
---|
379 | 682 | int n = read(trace_fd, buf, sizeof(buf)); |
---|
.. | .. |
---|
388 | 691 | out_reset: |
---|
389 | 692 | reset_tracing_files(ftrace); |
---|
390 | 693 | out: |
---|
391 | | - return done ? 0 : -1; |
---|
| 694 | + return (done && !workload_exec_errno) ? 0 : -1; |
---|
392 | 695 | } |
---|
393 | 696 | |
---|
394 | 697 | static int perf_ftrace_config(const char *var, const char *value, void *cb) |
---|
.. | .. |
---|
409 | 712 | |
---|
410 | 713 | pr_err("Please select \"function_graph\" (default) or \"function\"\n"); |
---|
411 | 714 | return -1; |
---|
| 715 | +} |
---|
| 716 | + |
---|
| 717 | +static void list_function_cb(char *str, void *arg) |
---|
| 718 | +{ |
---|
| 719 | + struct strfilter *filter = (struct strfilter *)arg; |
---|
| 720 | + |
---|
| 721 | + if (strfilter__compare(filter, str)) |
---|
| 722 | + printf("%s", str); |
---|
| 723 | +} |
---|
| 724 | + |
---|
| 725 | +static int opt_list_avail_functions(const struct option *opt __maybe_unused, |
---|
| 726 | + const char *str, int unset) |
---|
| 727 | +{ |
---|
| 728 | + struct strfilter *filter; |
---|
| 729 | + const char *err = NULL; |
---|
| 730 | + int ret; |
---|
| 731 | + |
---|
| 732 | + if (unset || !str) |
---|
| 733 | + return -1; |
---|
| 734 | + |
---|
| 735 | + filter = strfilter__new(str, &err); |
---|
| 736 | + if (!filter) |
---|
| 737 | + return err ? -EINVAL : -ENOMEM; |
---|
| 738 | + |
---|
| 739 | + ret = strfilter__or(filter, str, &err); |
---|
| 740 | + if (ret == -EINVAL) { |
---|
| 741 | + pr_err("Filter parse error at %td.\n", err - str + 1); |
---|
| 742 | + pr_err("Source: \"%s\"\n", str); |
---|
| 743 | + pr_err(" %*c\n", (int)(err - str + 1), '^'); |
---|
| 744 | + strfilter__delete(filter); |
---|
| 745 | + return ret; |
---|
| 746 | + } |
---|
| 747 | + |
---|
| 748 | + ret = read_tracing_file_by_line("available_filter_functions", |
---|
| 749 | + list_function_cb, filter); |
---|
| 750 | + strfilter__delete(filter); |
---|
| 751 | + if (ret < 0) |
---|
| 752 | + return ret; |
---|
| 753 | + |
---|
| 754 | + exit(0); |
---|
412 | 755 | } |
---|
413 | 756 | |
---|
414 | 757 | static int parse_filter_func(const struct option *opt, const char *str, |
---|
.. | .. |
---|
432 | 775 | struct filter_entry *pos, *tmp; |
---|
433 | 776 | |
---|
434 | 777 | list_for_each_entry_safe(pos, tmp, head, list) { |
---|
435 | | - list_del(&pos->list); |
---|
| 778 | + list_del_init(&pos->list); |
---|
436 | 779 | free(pos); |
---|
437 | 780 | } |
---|
| 781 | +} |
---|
| 782 | + |
---|
| 783 | +static int parse_buffer_size(const struct option *opt, |
---|
| 784 | + const char *str, int unset) |
---|
| 785 | +{ |
---|
| 786 | + unsigned long *s = (unsigned long *)opt->value; |
---|
| 787 | + static struct parse_tag tags_size[] = { |
---|
| 788 | + { .tag = 'B', .mult = 1 }, |
---|
| 789 | + { .tag = 'K', .mult = 1 << 10 }, |
---|
| 790 | + { .tag = 'M', .mult = 1 << 20 }, |
---|
| 791 | + { .tag = 'G', .mult = 1 << 30 }, |
---|
| 792 | + { .tag = 0 }, |
---|
| 793 | + }; |
---|
| 794 | + unsigned long val; |
---|
| 795 | + |
---|
| 796 | + if (unset) { |
---|
| 797 | + *s = 0; |
---|
| 798 | + return 0; |
---|
| 799 | + } |
---|
| 800 | + |
---|
| 801 | + val = parse_tag_value(str, tags_size); |
---|
| 802 | + if (val != (unsigned long) -1) { |
---|
| 803 | + if (val < 1024) { |
---|
| 804 | + pr_err("buffer size too small, must larger than 1KB."); |
---|
| 805 | + return -1; |
---|
| 806 | + } |
---|
| 807 | + *s = val; |
---|
| 808 | + return 0; |
---|
| 809 | + } |
---|
| 810 | + |
---|
| 811 | + return -1; |
---|
| 812 | +} |
---|
| 813 | + |
---|
| 814 | +static int parse_func_tracer_opts(const struct option *opt, |
---|
| 815 | + const char *str, int unset) |
---|
| 816 | +{ |
---|
| 817 | + int ret; |
---|
| 818 | + struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value; |
---|
| 819 | + struct sublevel_option func_tracer_opts[] = { |
---|
| 820 | + { .name = "call-graph", .value_ptr = &ftrace->func_stack_trace }, |
---|
| 821 | + { .name = "irq-info", .value_ptr = &ftrace->func_irq_info }, |
---|
| 822 | + { .name = NULL, } |
---|
| 823 | + }; |
---|
| 824 | + |
---|
| 825 | + if (unset) |
---|
| 826 | + return 0; |
---|
| 827 | + |
---|
| 828 | + ret = perf_parse_sublevel_options(str, func_tracer_opts); |
---|
| 829 | + if (ret) |
---|
| 830 | + return ret; |
---|
| 831 | + |
---|
| 832 | + return 0; |
---|
| 833 | +} |
---|
| 834 | + |
---|
| 835 | +static int parse_graph_tracer_opts(const struct option *opt, |
---|
| 836 | + const char *str, int unset) |
---|
| 837 | +{ |
---|
| 838 | + int ret; |
---|
| 839 | + struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value; |
---|
| 840 | + struct sublevel_option graph_tracer_opts[] = { |
---|
| 841 | + { .name = "nosleep-time", .value_ptr = &ftrace->graph_nosleep_time }, |
---|
| 842 | + { .name = "noirqs", .value_ptr = &ftrace->graph_noirqs }, |
---|
| 843 | + { .name = "verbose", .value_ptr = &ftrace->graph_verbose }, |
---|
| 844 | + { .name = "thresh", .value_ptr = &ftrace->graph_thresh }, |
---|
| 845 | + { .name = "depth", .value_ptr = &ftrace->graph_depth }, |
---|
| 846 | + { .name = NULL, } |
---|
| 847 | + }; |
---|
| 848 | + |
---|
| 849 | + if (unset) |
---|
| 850 | + return 0; |
---|
| 851 | + |
---|
| 852 | + ret = perf_parse_sublevel_options(str, graph_tracer_opts); |
---|
| 853 | + if (ret) |
---|
| 854 | + return ret; |
---|
| 855 | + |
---|
| 856 | + return 0; |
---|
| 857 | +} |
---|
| 858 | + |
---|
| 859 | +static void select_tracer(struct perf_ftrace *ftrace) |
---|
| 860 | +{ |
---|
| 861 | + bool graph = !list_empty(&ftrace->graph_funcs) || |
---|
| 862 | + !list_empty(&ftrace->nograph_funcs); |
---|
| 863 | + bool func = !list_empty(&ftrace->filters) || |
---|
| 864 | + !list_empty(&ftrace->notrace); |
---|
| 865 | + |
---|
| 866 | + /* The function_graph has priority over function tracer. */ |
---|
| 867 | + if (graph) |
---|
| 868 | + ftrace->tracer = "function_graph"; |
---|
| 869 | + else if (func) |
---|
| 870 | + ftrace->tracer = "function"; |
---|
| 871 | + /* Otherwise, the default tracer is used. */ |
---|
| 872 | + |
---|
| 873 | + pr_debug("%s tracer is used\n", ftrace->tracer); |
---|
438 | 874 | } |
---|
439 | 875 | |
---|
440 | 876 | int cmd_ftrace(int argc, const char **argv) |
---|
.. | .. |
---|
451 | 887 | }; |
---|
452 | 888 | const struct option ftrace_options[] = { |
---|
453 | 889 | OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", |
---|
454 | | - "tracer to use: function_graph(default) or function"), |
---|
| 890 | + "Tracer to use: function_graph(default) or function"), |
---|
| 891 | + OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]", |
---|
| 892 | + "Show available functions to filter", |
---|
| 893 | + opt_list_avail_functions, "*"), |
---|
455 | 894 | OPT_STRING('p', "pid", &ftrace.target.pid, "pid", |
---|
456 | | - "trace on existing process id"), |
---|
| 895 | + "Trace on existing process id"), |
---|
| 896 | + /* TODO: Add short option -t after -t/--tracer can be removed. */ |
---|
| 897 | + OPT_STRING(0, "tid", &ftrace.target.tid, "tid", |
---|
| 898 | + "Trace on existing thread id (exclusive to --pid)"), |
---|
457 | 899 | OPT_INCR('v', "verbose", &verbose, |
---|
458 | | - "be more verbose"), |
---|
| 900 | + "Be more verbose"), |
---|
459 | 901 | OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide, |
---|
460 | | - "system-wide collection from all CPUs"), |
---|
| 902 | + "System-wide collection from all CPUs"), |
---|
461 | 903 | OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", |
---|
462 | | - "list of cpus to monitor"), |
---|
| 904 | + "List of cpus to monitor"), |
---|
463 | 905 | OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", |
---|
464 | | - "trace given functions only", parse_filter_func), |
---|
| 906 | + "Trace given functions using function tracer", |
---|
| 907 | + parse_filter_func), |
---|
465 | 908 | OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func", |
---|
466 | | - "do not trace given functions", parse_filter_func), |
---|
| 909 | + "Do not trace given functions", parse_filter_func), |
---|
| 910 | + OPT_CALLBACK(0, "func-opts", &ftrace, "options", |
---|
| 911 | + "Function tracer options, available options: call-graph,irq-info", |
---|
| 912 | + parse_func_tracer_opts), |
---|
467 | 913 | OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func", |
---|
468 | | - "Set graph filter on given functions", parse_filter_func), |
---|
| 914 | + "Trace given functions using function_graph tracer", |
---|
| 915 | + parse_filter_func), |
---|
469 | 916 | OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func", |
---|
470 | 917 | "Set nograph filter on given functions", parse_filter_func), |
---|
471 | | - OPT_INTEGER('D', "graph-depth", &ftrace.graph_depth, |
---|
472 | | - "Max depth for function graph tracer"), |
---|
| 918 | + OPT_CALLBACK(0, "graph-opts", &ftrace, "options", |
---|
| 919 | + "Graph tracer options, available options: nosleep-time,noirqs,verbose,thresh=<n>,depth=<n>", |
---|
| 920 | + parse_graph_tracer_opts), |
---|
| 921 | + OPT_CALLBACK('m', "buffer-size", &ftrace.percpu_buffer_size, "size", |
---|
| 922 | + "Size of per cpu buffer, needs to use a B, K, M or G suffix.", parse_buffer_size), |
---|
| 923 | + OPT_BOOLEAN(0, "inherit", &ftrace.inherit, |
---|
| 924 | + "Trace children processes"), |
---|
| 925 | + OPT_UINTEGER('D', "delay", &ftrace.initial_delay, |
---|
| 926 | + "Number of milliseconds to wait before starting tracing after program start"), |
---|
473 | 927 | OPT_END() |
---|
474 | 928 | }; |
---|
475 | 929 | |
---|
.. | .. |
---|
485 | 939 | argc = parse_options(argc, argv, ftrace_options, ftrace_usage, |
---|
486 | 940 | PARSE_OPT_STOP_AT_NON_OPTION); |
---|
487 | 941 | if (!argc && target__none(&ftrace.target)) |
---|
488 | | - usage_with_options(ftrace_usage, ftrace_options); |
---|
| 942 | + ftrace.target.system_wide = true; |
---|
| 943 | + |
---|
| 944 | + select_tracer(&ftrace); |
---|
489 | 945 | |
---|
490 | 946 | ret = target__validate(&ftrace.target); |
---|
491 | 947 | if (ret) { |
---|
.. | .. |
---|
496 | 952 | goto out_delete_filters; |
---|
497 | 953 | } |
---|
498 | 954 | |
---|
499 | | - ftrace.evlist = perf_evlist__new(); |
---|
| 955 | + ftrace.evlist = evlist__new(); |
---|
500 | 956 | if (ftrace.evlist == NULL) { |
---|
501 | 957 | ret = -ENOMEM; |
---|
502 | 958 | goto out_delete_filters; |
---|
.. | .. |
---|
509 | 965 | ret = __cmd_ftrace(&ftrace, argc, argv); |
---|
510 | 966 | |
---|
511 | 967 | out_delete_evlist: |
---|
512 | | - perf_evlist__delete(ftrace.evlist); |
---|
| 968 | + evlist__delete(ftrace.evlist); |
---|
513 | 969 | |
---|
514 | 970 | out_delete_filters: |
---|
515 | 971 | delete_filter_func(&ftrace.filters); |
---|