linux/tools/perf/builtin-trace.c
<<
>>
Prefs
   1#include "builtin.h"
   2#include "util/evlist.h"
   3#include "util/parse-options.h"
   4#include "util/thread_map.h"
   5#include "event-parse.h"
   6
   7#include <libaudit.h>
   8#include <stdlib.h>
   9
  10static struct syscall_fmt {
  11        const char *name;
  12        const char *alias;
  13        bool       errmsg;
  14        bool       timeout;
  15} syscall_fmts[] = {
  16        { .name     = "arch_prctl", .errmsg = true, .alias = "prctl", },
  17        { .name     = "fstat",      .errmsg = true, .alias = "newfstat", },
  18        { .name     = "fstatat",    .errmsg = true, .alias = "newfstatat", },
  19        { .name     = "futex",      .errmsg = true, },
  20        { .name     = "poll",       .errmsg = true, .timeout = true, },
  21        { .name     = "ppoll",      .errmsg = true, .timeout = true, },
  22        { .name     = "read",       .errmsg = true, },
  23        { .name     = "recvfrom",   .errmsg = true, },
  24        { .name     = "select",     .errmsg = true, .timeout = true, },
  25        { .name     = "stat",       .errmsg = true, .alias = "newstat", },
  26};
  27
  28static int syscall_fmt__cmp(const void *name, const void *fmtp)
  29{
  30        const struct syscall_fmt *fmt = fmtp;
  31        return strcmp(name, fmt->name);
  32}
  33
  34static struct syscall_fmt *syscall_fmt__find(const char *name)
  35{
  36        const int nmemb = ARRAY_SIZE(syscall_fmts);
  37        return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
  38}
  39
  40struct syscall {
  41        struct event_format *tp_format;
  42        const char          *name;
  43        struct syscall_fmt  *fmt;
  44};
  45
  46struct trace {
  47        int                     audit_machine;
  48        struct {
  49                int             max;
  50                struct syscall  *table;
  51        } syscalls;
  52        struct perf_record_opts opts;
  53};
  54
  55static int trace__read_syscall_info(struct trace *trace, int id)
  56{
  57        char tp_name[128];
  58        struct syscall *sc;
  59        const char *name = audit_syscall_to_name(id, trace->audit_machine);
  60
  61        if (name == NULL)
  62                return -1;
  63
  64        if (id > trace->syscalls.max) {
  65                struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
  66
  67                if (nsyscalls == NULL)
  68                        return -1;
  69
  70                if (trace->syscalls.max != -1) {
  71                        memset(nsyscalls + trace->syscalls.max + 1, 0,
  72                               (id - trace->syscalls.max) * sizeof(*sc));
  73                } else {
  74                        memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
  75                }
  76
  77                trace->syscalls.table = nsyscalls;
  78                trace->syscalls.max   = id;
  79        }
  80
  81        sc = trace->syscalls.table + id;
  82        sc->name = name;
  83        sc->fmt  = syscall_fmt__find(sc->name);
  84
  85        snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
  86        sc->tp_format = event_format__new("syscalls", tp_name);
  87
  88        if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
  89                snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
  90                sc->tp_format = event_format__new("syscalls", tp_name);
  91        }
  92
  93        return sc->tp_format != NULL ? 0 : -1;
  94}
  95
  96static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp)
  97{
  98        int i = 0;
  99        size_t printed = 0;
 100
 101        if (sc->tp_format != NULL) {
 102                struct format_field *field;
 103
 104                for (field = sc->tp_format->format.fields->next; field; field = field->next) {
 105                        printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "",
 106                                           field->name, args[i++]);
 107                }
 108        } else {
 109                while (i < 6) {
 110                        printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]);
 111                        ++i;
 112                }
 113        }
 114
 115        return printed;
 116}
 117
 118typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
 119                                  struct perf_sample *sample);
 120
 121static struct syscall *trace__syscall_info(struct trace *trace,
 122                                           struct perf_evsel *evsel,
 123                                           struct perf_sample *sample)
 124{
 125        int id = perf_evsel__intval(evsel, sample, "id");
 126
 127        if (id < 0) {
 128                printf("Invalid syscall %d id, skipping...\n", id);
 129                return NULL;
 130        }
 131
 132        if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
 133            trace__read_syscall_info(trace, id))
 134                goto out_cant_read;
 135
 136        if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
 137                goto out_cant_read;
 138
 139        return &trace->syscalls.table[id];
 140
 141out_cant_read:
 142        printf("Problems reading syscall %d information\n", id);
 143        return NULL;
 144}
 145
 146static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
 147                            struct perf_sample *sample)
 148{
 149        void *args;
 150        struct syscall *sc = trace__syscall_info(trace, evsel, sample);
 151
 152        if (sc == NULL)
 153                return -1;
 154
 155        args = perf_evsel__rawptr(evsel, sample, "args");
 156        if (args == NULL) {
 157                printf("Problems reading syscall arguments\n");
 158                return -1;
 159        }
 160
 161        printf("%s(", sc->name);
 162        syscall__fprintf_args(sc, args, stdout);
 163
 164        return 0;
 165}
 166
 167static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
 168                           struct perf_sample *sample)
 169{
 170        int ret;
 171        struct syscall *sc = trace__syscall_info(trace, evsel, sample);
 172
 173        if (sc == NULL)
 174                return -1;
 175
 176        ret = perf_evsel__intval(evsel, sample, "ret");
 177
 178        if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
 179                char bf[256];
 180                const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
 181                           *e = audit_errno_to_name(-ret);
 182
 183                printf(") = -1 %s %s", e, emsg);
 184        } else if (ret == 0 && sc->fmt && sc->fmt->timeout)
 185                printf(") = 0 Timeout");
 186        else
 187                printf(") = %d", ret);
 188
 189        putchar('\n');
 190        return 0;
 191}
 192
 193static int trace__run(struct trace *trace)
 194{
 195        struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
 196        struct perf_evsel *evsel;
 197        int err = -1, i, nr_events = 0, before;
 198
 199        if (evlist == NULL) {
 200                printf("Not enough memory to run!\n");
 201                goto out;
 202        }
 203
 204        if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) ||
 205            perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) {
 206                printf("Couldn't read the raw_syscalls tracepoints information!\n");
 207                goto out_delete_evlist;
 208        }
 209
 210        err = perf_evlist__create_maps(evlist, &trace->opts.target);
 211        if (err < 0) {
 212                printf("Problems parsing the target to trace, check your options!\n");
 213                goto out_delete_evlist;
 214        }
 215
 216        perf_evlist__config_attrs(evlist, &trace->opts);
 217
 218        err = perf_evlist__open(evlist);
 219        if (err < 0) {
 220                printf("Couldn't create the events: %s\n", strerror(errno));
 221                goto out_delete_evlist;
 222        }
 223
 224        err = perf_evlist__mmap(evlist, UINT_MAX, false);
 225        if (err < 0) {
 226                printf("Couldn't mmap the events: %s\n", strerror(errno));
 227                goto out_delete_evlist;
 228        }
 229
 230        perf_evlist__enable(evlist);
 231again:
 232        before = nr_events;
 233
 234        for (i = 0; i < evlist->nr_mmaps; i++) {
 235                union perf_event *event;
 236
 237                while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
 238                        const u32 type = event->header.type;
 239                        tracepoint_handler handler;
 240                        struct perf_sample sample;
 241
 242                        ++nr_events;
 243
 244                        switch (type) {
 245                        case PERF_RECORD_SAMPLE:
 246                                break;
 247                        case PERF_RECORD_LOST:
 248                                printf("LOST %" PRIu64 " events!\n", event->lost.lost);
 249                                continue;
 250                        default:
 251                                printf("Unexpected %s event, skipping...\n",
 252                                        perf_event__name(type));
 253                                continue;
 254                        }
 255
 256                        err = perf_evlist__parse_sample(evlist, event, &sample);
 257                        if (err) {
 258                                printf("Can't parse sample, err = %d, skipping...\n", err);
 259                                continue;
 260                        }
 261
 262                        evsel = perf_evlist__id2evsel(evlist, sample.id);
 263                        if (evsel == NULL) {
 264                                printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
 265                                continue;
 266                        }
 267
 268                        if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
 269                                printf("%d ", sample.tid);
 270
 271                        if (sample.raw_data == NULL) {
 272                                printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
 273                                       perf_evsel__name(evsel), sample.tid,
 274                                       sample.cpu, sample.raw_size);
 275                                continue;
 276                        }
 277
 278                        handler = evsel->handler.func;
 279                        handler(trace, evsel, &sample);
 280                }
 281        }
 282
 283        if (nr_events == before)
 284                poll(evlist->pollfd, evlist->nr_fds, -1);
 285
 286        goto again;
 287
 288out_delete_evlist:
 289        perf_evlist__delete(evlist);
 290out:
 291        return err;
 292}
 293
 294int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
 295{
 296        const char * const trace_usage[] = {
 297                "perf trace [<options>]",
 298                NULL
 299        };
 300        struct trace trace = {
 301                .audit_machine = audit_detect_machine(),
 302                .syscalls = {
 303                        . max = -1,
 304                },
 305                .opts = {
 306                        .target = {
 307                                .uid       = UINT_MAX,
 308                                .uses_mmap = true,
 309                        },
 310                        .user_freq     = UINT_MAX,
 311                        .user_interval = ULLONG_MAX,
 312                        .no_delay      = true,
 313                        .mmap_pages    = 1024,
 314                },
 315        };
 316        const struct option trace_options[] = {
 317        OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
 318                    "trace events on existing process id"),
 319        OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
 320                    "trace events on existing thread id"),
 321        OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide,
 322                    "system-wide collection from all CPUs"),
 323        OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu",
 324                    "list of cpus to monitor"),
 325        OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
 326                    "child tasks do not inherit counters"),
 327        OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages,
 328                     "number of mmap data pages"),
 329        OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
 330                   "user to profile"),
 331        OPT_END()
 332        };
 333        int err;
 334
 335        argc = parse_options(argc, argv, trace_options, trace_usage, 0);
 336        if (argc)
 337                usage_with_options(trace_usage, trace_options);
 338
 339        err = perf_target__parse_uid(&trace.opts.target);
 340        if (err) {
 341                char bf[BUFSIZ];
 342                perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
 343                printf("%s", bf);
 344                return err;
 345        }
 346
 347        return trace__run(&trace);
 348}
 349
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.