1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * builtin-ftrace.c 4 * 5 * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> 6 */ 7 8 #include "builtin.h" 9 10 #include <errno.h> 11 #include <unistd.h> 12 #include <signal.h> 13 #include <stdlib.h> 14 #include <fcntl.h> 15 #include <poll.h> 16 #include <linux/capability.h> 17 #include <linux/string.h> 18 19 #include "debug.h" 20 #include <subcmd/pager.h> 21 #include <subcmd/parse-options.h> 22 #include <api/fs/tracing_path.h> 23 #include "evlist.h" 24 #include "target.h" 25 #include "cpumap.h" 26 #include "thread_map.h" 27 #include "util/cap.h" 28 #include "util/config.h" 29 30 #define DEFAULT_TRACER "function_graph" 31 32 struct perf_ftrace { 33 struct evlist *evlist; 34 struct target target; 35 const char *tracer; 36 struct list_head filters; 37 struct list_head notrace; 38 struct list_head graph_funcs; 39 struct list_head nograph_funcs; 40 int graph_depth; 41 }; 42 43 struct filter_entry { 44 struct list_head list; 45 char name[]; 46 }; 47 48 static bool done; 49 50 static void sig_handler(int sig __maybe_unused) 51 { 52 done = true; 53 } 54 55 /* 56 * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since 57 * we asked by setting its exec_error to the function below, 58 * ftrace__workload_exec_failed_signal. 59 * 60 * XXX We need to handle this more appropriately, emitting an error, etc. 61 */ 62 static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, 63 siginfo_t *info __maybe_unused, 64 void *ucontext __maybe_unused) 65 { 66 /* workload_exec_errno = info->si_value.sival_int; */ 67 done = true; 68 } 69 70 static int __write_tracing_file(const char *name, const char *val, bool append) 71 { 72 char *file; 73 int fd, ret = -1; 74 ssize_t size = strlen(val); 75 int flags = O_WRONLY; 76 char errbuf[512]; 77 char *val_copy; 78 79 file = get_tracing_file(name); 80 if (!file) { 81 pr_debug("cannot get tracing file: %s\n", name); 82 return -1; 83 } 84 85 if (append) 86 flags |= O_APPEND; 87 else 88 flags |= O_TRUNC; 89 90 fd = open(file, flags); 91 if (fd < 0) { 92 pr_debug("cannot open tracing file: %s: %s\n", 93 name, str_error_r(errno, errbuf, sizeof(errbuf))); 94 goto out; 95 } 96 97 /* 98 * Copy the original value and append a '\n'. Without this, 99 * the kernel can hide possible errors. 100 */ 101 val_copy = strdup(val); 102 if (!val_copy) 103 goto out_close; 104 val_copy[size] = '\n'; 105 106 if (write(fd, val_copy, size + 1) == size + 1) 107 ret = 0; 108 else 109 pr_debug("write '%s' to tracing/%s failed: %s\n", 110 val, name, str_error_r(errno, errbuf, sizeof(errbuf))); 111 112 free(val_copy); 113 out_close: 114 close(fd); 115 out: 116 put_tracing_file(file); 117 return ret; 118 } 119 120 static int write_tracing_file(const char *name, const char *val) 121 { 122 return __write_tracing_file(name, val, false); 123 } 124 125 static int append_tracing_file(const char *name, const char *val) 126 { 127 return __write_tracing_file(name, val, true); 128 } 129 130 static int reset_tracing_cpu(void); 131 static void reset_tracing_filters(void); 132 133 static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) 134 { 135 if (write_tracing_file("tracing_on", "0") < 0) 136 return -1; 137 138 if (write_tracing_file("current_tracer", "nop") < 0) 139 return -1; 140 141 if (write_tracing_file("set_ftrace_pid", " ") < 0) 142 return -1; 143 144 if (reset_tracing_cpu() < 0) 145 return -1; 146 147 if (write_tracing_file("max_graph_depth", "0") < 0) 148 return -1; 149 150 reset_tracing_filters(); 151 return 0; 152 } 153 154 static int set_tracing_pid(struct perf_ftrace *ftrace) 155 { 156 int i; 157 char buf[16]; 158 159 if (target__has_cpu(&ftrace->target)) 160 return 0; 161 162 for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) { 163 scnprintf(buf, sizeof(buf), "%d", 164 ftrace->evlist->core.threads->map[i]); 165 if (append_tracing_file("set_ftrace_pid", buf) < 0) 166 return -1; 167 } 168 return 0; 169 } 170 171 static int set_tracing_cpumask(struct perf_cpu_map *cpumap) 172 { 173 char *cpumask; 174 size_t mask_size; 175 int ret; 176 int last_cpu; 177 178 last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1); 179 mask_size = last_cpu / 4 + 2; /* one more byte for EOS */ 180 mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */ 181 182 cpumask = malloc(mask_size); 183 if (cpumask == NULL) { 184 pr_debug("failed to allocate cpu mask\n"); 185 return -1; 186 } 187 188 cpu_map__snprint_mask(cpumap, cpumask, mask_size); 189 190 ret = write_tracing_file("tracing_cpumask", cpumask); 191 192 free(cpumask); 193 return ret; 194 } 195 196 static int set_tracing_cpu(struct perf_ftrace *ftrace) 197 { 198 struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus; 199 200 if (!target__has_cpu(&ftrace->target)) 201 return 0; 202 203 return set_tracing_cpumask(cpumap); 204 } 205 206 static int reset_tracing_cpu(void) 207 { 208 struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL); 209 int ret; 210 211 ret = set_tracing_cpumask(cpumap); 212 perf_cpu_map__put(cpumap); 213 return ret; 214 } 215 216 static int __set_tracing_filter(const char *filter_file, struct list_head *funcs) 217 { 218 struct filter_entry *pos; 219 220 list_for_each_entry(pos, funcs, list) { 221 if (append_tracing_file(filter_file, pos->name) < 0) 222 return -1; 223 } 224 225 return 0; 226 } 227 228 static int set_tracing_filters(struct perf_ftrace *ftrace) 229 { 230 int ret; 231 232 ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters); 233 if (ret < 0) 234 return ret; 235 236 ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace); 237 if (ret < 0) 238 return ret; 239 240 ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs); 241 if (ret < 0) 242 return ret; 243 244 /* old kernels do not have this filter */ 245 __set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs); 246 247 return ret; 248 } 249 250 static void reset_tracing_filters(void) 251 { 252 write_tracing_file("set_ftrace_filter", " "); 253 write_tracing_file("set_ftrace_notrace", " "); 254 write_tracing_file("set_graph_function", " "); 255 write_tracing_file("set_graph_notrace", " "); 256 } 257 258 static int set_tracing_depth(struct perf_ftrace *ftrace) 259 { 260 char buf[16]; 261 262 if (ftrace->graph_depth == 0) 263 return 0; 264 265 if (ftrace->graph_depth < 0) { 266 pr_err("invalid graph depth: %d\n", ftrace->graph_depth); 267 return -1; 268 } 269 270 snprintf(buf, sizeof(buf), "%d", ftrace->graph_depth); 271 272 if (write_tracing_file("max_graph_depth", buf) < 0) 273 return -1; 274 275 return 0; 276 } 277 278 static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) 279 { 280 char *trace_file; 281 int trace_fd; 282 char buf[4096]; 283 struct pollfd pollfd = { 284 .events = POLLIN, 285 }; 286 287 if (!(perf_cap__capable(CAP_PERFMON) || 288 perf_cap__capable(CAP_SYS_ADMIN))) { 289 pr_err("ftrace only works for %s!\n", 290 #ifdef HAVE_LIBCAP_SUPPORT 291 "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" 292 #else 293 "root" 294 #endif 295 ); 296 return -1; 297 } 298 299 signal(SIGINT, sig_handler); 300 signal(SIGUSR1, sig_handler); 301 signal(SIGCHLD, sig_handler); 302 signal(SIGPIPE, sig_handler); 303 304 if (reset_tracing_files(ftrace) < 0) { 305 pr_err("failed to reset ftrace\n"); 306 goto out; 307 } 308 309 /* reset ftrace buffer */ 310 if (write_tracing_file("trace", "0") < 0) 311 goto out; 312 313 if (argc && perf_evlist__prepare_workload(ftrace->evlist, 314 &ftrace->target, argv, false, 315 ftrace__workload_exec_failed_signal) < 0) { 316 goto out; 317 } 318 319 if (set_tracing_pid(ftrace) < 0) { 320 pr_err("failed to set ftrace pid\n"); 321 goto out_reset; 322 } 323 324 if (set_tracing_cpu(ftrace) < 0) { 325 pr_err("failed to set tracing cpumask\n"); 326 goto out_reset; 327 } 328 329 if (set_tracing_filters(ftrace) < 0) { 330 pr_err("failed to set tracing filters\n"); 331 goto out_reset; 332 } 333 334 if (set_tracing_depth(ftrace) < 0) { 335 pr_err("failed to set graph depth\n"); 336 goto out_reset; 337 } 338 339 if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { 340 pr_err("failed to set current_tracer to %s\n", ftrace->tracer); 341 goto out_reset; 342 } 343 344 setup_pager(); 345 346 trace_file = get_tracing_file("trace_pipe"); 347 if (!trace_file) { 348 pr_err("failed to open trace_pipe\n"); 349 goto out_reset; 350 } 351 352 trace_fd = open(trace_file, O_RDONLY); 353 354 put_tracing_file(trace_file); 355 356 if (trace_fd < 0) { 357 pr_err("failed to open trace_pipe\n"); 358 goto out_reset; 359 } 360 361 fcntl(trace_fd, F_SETFL, O_NONBLOCK); 362 pollfd.fd = trace_fd; 363 364 if (write_tracing_file("tracing_on", "1") < 0) { 365 pr_err("can't enable tracing\n"); 366 goto out_close_fd; 367 } 368 369 perf_evlist__start_workload(ftrace->evlist); 370 371 while (!done) { 372 if (poll(&pollfd, 1, -1) < 0) 373 break; 374 375 if (pollfd.revents & POLLIN) { 376 int n = read(trace_fd, buf, sizeof(buf)); 377 if (n < 0) 378 break; 379 if (fwrite(buf, n, 1, stdout) != 1) 380 break; 381 } 382 } 383 384 write_tracing_file("tracing_on", "0"); 385 386 /* read remaining buffer contents */ 387 while (true) { 388 int n = read(trace_fd, buf, sizeof(buf)); 389 if (n <= 0) 390 break; 391 if (fwrite(buf, n, 1, stdout) != 1) 392 break; 393 } 394 395 out_close_fd: 396 close(trace_fd); 397 out_reset: 398 reset_tracing_files(ftrace); 399 out: 400 return done ? 0 : -1; 401 } 402 403 static int perf_ftrace_config(const char *var, const char *value, void *cb) 404 { 405 struct perf_ftrace *ftrace = cb; 406 407 if (!strstarts(var, "ftrace.")) 408 return 0; 409 410 if (strcmp(var, "ftrace.tracer")) 411 return -1; 412 413 if (!strcmp(value, "function_graph") || 414 !strcmp(value, "function")) { 415 ftrace->tracer = value; 416 return 0; 417 } 418 419 pr_err("Please select \"function_graph\" (default) or \"function\"\n"); 420 return -1; 421 } 422 423 static int parse_filter_func(const struct option *opt, const char *str, 424 int unset __maybe_unused) 425 { 426 struct list_head *head = opt->value; 427 struct filter_entry *entry; 428 429 entry = malloc(sizeof(*entry) + strlen(str) + 1); 430 if (entry == NULL) 431 return -ENOMEM; 432 433 strcpy(entry->name, str); 434 list_add_tail(&entry->list, head); 435 436 return 0; 437 } 438 439 static void delete_filter_func(struct list_head *head) 440 { 441 struct filter_entry *pos, *tmp; 442 443 list_for_each_entry_safe(pos, tmp, head, list) { 444 list_del_init(&pos->list); 445 free(pos); 446 } 447 } 448 449 int cmd_ftrace(int argc, const char **argv) 450 { 451 int ret; 452 struct perf_ftrace ftrace = { 453 .tracer = DEFAULT_TRACER, 454 .target = { .uid = UINT_MAX, }, 455 }; 456 const char * const ftrace_usage[] = { 457 "perf ftrace [<options>] [<command>]", 458 "perf ftrace [<options>] -- <command> [<options>]", 459 NULL 460 }; 461 const struct option ftrace_options[] = { 462 OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", 463 "tracer to use: function_graph(default) or function"), 464 OPT_STRING('p', "pid", &ftrace.target.pid, "pid", 465 "trace on existing process id"), 466 OPT_INCR('v', "verbose", &verbose, 467 "be more verbose"), 468 OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide, 469 "system-wide collection from all CPUs"), 470 OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", 471 "list of cpus to monitor"), 472 OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", 473 "trace given functions only", parse_filter_func), 474 OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func", 475 "do not trace given functions", parse_filter_func), 476 OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func", 477 "Set graph filter on given functions", parse_filter_func), 478 OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func", 479 "Set nograph filter on given functions", parse_filter_func), 480 OPT_INTEGER('D', "graph-depth", &ftrace.graph_depth, 481 "Max depth for function graph tracer"), 482 OPT_END() 483 }; 484 485 INIT_LIST_HEAD(&ftrace.filters); 486 INIT_LIST_HEAD(&ftrace.notrace); 487 INIT_LIST_HEAD(&ftrace.graph_funcs); 488 INIT_LIST_HEAD(&ftrace.nograph_funcs); 489 490 ret = perf_config(perf_ftrace_config, &ftrace); 491 if (ret < 0) 492 return -1; 493 494 argc = parse_options(argc, argv, ftrace_options, ftrace_usage, 495 PARSE_OPT_STOP_AT_NON_OPTION); 496 if (!argc && target__none(&ftrace.target)) 497 usage_with_options(ftrace_usage, ftrace_options); 498 499 ret = target__validate(&ftrace.target); 500 if (ret) { 501 char errbuf[512]; 502 503 target__strerror(&ftrace.target, ret, errbuf, 512); 504 pr_err("%s\n", errbuf); 505 goto out_delete_filters; 506 } 507 508 ftrace.evlist = evlist__new(); 509 if (ftrace.evlist == NULL) { 510 ret = -ENOMEM; 511 goto out_delete_filters; 512 } 513 514 ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); 515 if (ret < 0) 516 goto out_delete_evlist; 517 518 ret = __cmd_ftrace(&ftrace, argc, argv); 519 520 out_delete_evlist: 521 evlist__delete(ftrace.evlist); 522 523 out_delete_filters: 524 delete_filter_func(&ftrace.filters); 525 delete_filter_func(&ftrace.notrace); 526 delete_filter_func(&ftrace.graph_funcs); 527 delete_filter_func(&ftrace.nograph_funcs); 528 529 return ret; 530 } 531