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
  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);
 101out:
 102        put_tracing_file(file);
 103        return ret;
 104}
 105
 106static int write_tracing_file(const char *name, const char *val)
 107{
 108        return __write_tracing_file(name, val, false);
 109}
 110
 111static int append_tracing_file(const char *name, const char *val)
 112{
 113        return __write_tracing_file(name, val, true);
 114}
 115
 116static int reset_tracing_cpu(void);
 117static void reset_tracing_filters(void);
 118
 119static 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
 140static 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
 157static 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
 182static 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
 192static 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
 202static 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
 214static 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
 236static 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
 244static 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
 264static 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
 372out_close_fd:
 373        close(trace_fd);
 374out_reset:
 375        reset_tracing_files(ftrace);
 376out:
 377        return done ? 0 : -1;
 378}
 379
 380static int perf_ftrace_config(const char *var, const char *value, void *cb)
 381{
 382        struct perf_ftrace *ftrace = cb;
 383
 384        if (prefixcmp(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
 400static 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
 416static 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
 426int 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
 497out_delete_evlist:
 498        perf_evlist__delete(ftrace.evlist);
 499
 500out_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