| .. | .. |
|---|
| 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); |
|---|