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