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