diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 98 |
1 files changed, 90 insertions, 8 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 90289f31dd87..dc8a6c4986ce 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -89,6 +89,8 @@ struct trace { | |||
89 | u64 base_time; | 89 | u64 base_time; |
90 | FILE *output; | 90 | FILE *output; |
91 | unsigned long nr_events; | 91 | unsigned long nr_events; |
92 | unsigned long nr_events_printed; | ||
93 | unsigned long max_events; | ||
92 | struct strlist *ev_qualifier; | 94 | struct strlist *ev_qualifier; |
93 | struct { | 95 | struct { |
94 | size_t nr; | 96 | size_t nr; |
@@ -612,6 +614,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, | |||
612 | 614 | ||
613 | struct syscall_arg_fmt { | 615 | struct syscall_arg_fmt { |
614 | size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); | 616 | size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); |
617 | unsigned long (*mask_val)(struct syscall_arg *arg, unsigned long val); | ||
615 | void *parm; | 618 | void *parm; |
616 | const char *name; | 619 | const char *name; |
617 | bool show_zero; | 620 | bool show_zero; |
@@ -723,6 +726,10 @@ static struct syscall_fmt { | |||
723 | .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, | 726 | .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, |
724 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, | 727 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, |
725 | [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, }, }, | 728 | [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, }, }, |
729 | { .name = "mount", | ||
730 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ }, | ||
731 | [3] = { .scnprintf = SCA_MOUNT_FLAGS, /* flags */ | ||
732 | .mask_val = SCAMV_MOUNT_FLAGS, /* flags */ }, }, }, | ||
726 | { .name = "mprotect", | 733 | { .name = "mprotect", |
727 | .arg = { [0] = { .scnprintf = SCA_HEX, /* start */ }, | 734 | .arg = { [0] = { .scnprintf = SCA_HEX, /* start */ }, |
728 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, }, | 735 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, }, |
@@ -832,7 +839,8 @@ static struct syscall_fmt { | |||
832 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 839 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
833 | { .name = "tkill", | 840 | { .name = "tkill", |
834 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 841 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
835 | { .name = "umount2", .alias = "umount", }, | 842 | { .name = "umount2", .alias = "umount", |
843 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* name */ }, }, }, | ||
836 | { .name = "uname", .alias = "newuname", }, | 844 | { .name = "uname", .alias = "newuname", }, |
837 | { .name = "unlinkat", | 845 | { .name = "unlinkat", |
838 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, | 846 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
@@ -856,6 +864,18 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) | |||
856 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); | 864 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); |
857 | } | 865 | } |
858 | 866 | ||
867 | static struct syscall_fmt *syscall_fmt__find_by_alias(const char *alias) | ||
868 | { | ||
869 | int i, nmemb = ARRAY_SIZE(syscall_fmts); | ||
870 | |||
871 | for (i = 0; i < nmemb; ++i) { | ||
872 | if (syscall_fmts[i].alias && strcmp(syscall_fmts[i].alias, alias) == 0) | ||
873 | return &syscall_fmts[i]; | ||
874 | } | ||
875 | |||
876 | return NULL; | ||
877 | } | ||
878 | |||
859 | /* | 879 | /* |
860 | * is_exit: is this "exit" or "exit_group"? | 880 | * is_exit: is this "exit" or "exit_group"? |
861 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. | 881 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. |
@@ -1485,6 +1505,19 @@ static size_t syscall__scnprintf_name(struct syscall *sc, char *bf, size_t size, | |||
1485 | return scnprintf(bf, size, "arg%d: ", arg->idx); | 1505 | return scnprintf(bf, size, "arg%d: ", arg->idx); |
1486 | } | 1506 | } |
1487 | 1507 | ||
1508 | /* | ||
1509 | * Check if the value is in fact zero, i.e. mask whatever needs masking, such | ||
1510 | * as mount 'flags' argument that needs ignoring some magic flag, see comment | ||
1511 | * in tools/perf/trace/beauty/mount_flags.c | ||
1512 | */ | ||
1513 | static unsigned long syscall__mask_val(struct syscall *sc, struct syscall_arg *arg, unsigned long val) | ||
1514 | { | ||
1515 | if (sc->arg_fmt && sc->arg_fmt[arg->idx].mask_val) | ||
1516 | return sc->arg_fmt[arg->idx].mask_val(arg, val); | ||
1517 | |||
1518 | return val; | ||
1519 | } | ||
1520 | |||
1488 | static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, | 1521 | static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, |
1489 | struct syscall_arg *arg, unsigned long val) | 1522 | struct syscall_arg *arg, unsigned long val) |
1490 | { | 1523 | { |
@@ -1533,6 +1566,11 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1533 | continue; | 1566 | continue; |
1534 | 1567 | ||
1535 | val = syscall_arg__val(&arg, arg.idx); | 1568 | val = syscall_arg__val(&arg, arg.idx); |
1569 | /* | ||
1570 | * Some syscall args need some mask, most don't and | ||
1571 | * return val untouched. | ||
1572 | */ | ||
1573 | val = syscall__mask_val(sc, &arg, val); | ||
1536 | 1574 | ||
1537 | /* | 1575 | /* |
1538 | * Suppress this argument if its value is zero and | 1576 | * Suppress this argument if its value is zero and |
@@ -1664,6 +1702,8 @@ static int trace__printf_interrupted_entry(struct trace *trace) | |||
1664 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); | 1702 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); |
1665 | ttrace->entry_pending = false; | 1703 | ttrace->entry_pending = false; |
1666 | 1704 | ||
1705 | ++trace->nr_events_printed; | ||
1706 | |||
1667 | return printed; | 1707 | return printed; |
1668 | } | 1708 | } |
1669 | 1709 | ||
@@ -1810,12 +1850,14 @@ static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evse | |||
1810 | int max_stack = evsel->attr.sample_max_stack ? | 1850 | int max_stack = evsel->attr.sample_max_stack ? |
1811 | evsel->attr.sample_max_stack : | 1851 | evsel->attr.sample_max_stack : |
1812 | trace->max_stack; | 1852 | trace->max_stack; |
1853 | int err; | ||
1813 | 1854 | ||
1814 | if (machine__resolve(trace->host, &al, sample) < 0 || | 1855 | if (machine__resolve(trace->host, &al, sample) < 0) |
1815 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack)) | ||
1816 | return -1; | 1856 | return -1; |
1817 | 1857 | ||
1818 | return 0; | 1858 | err = thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack); |
1859 | addr_location__put(&al); | ||
1860 | return err; | ||
1819 | } | 1861 | } |
1820 | 1862 | ||
1821 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) | 1863 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) |
@@ -1940,6 +1982,13 @@ errno_print: { | |||
1940 | 1982 | ||
1941 | fputc('\n', trace->output); | 1983 | fputc('\n', trace->output); |
1942 | 1984 | ||
1985 | /* | ||
1986 | * We only consider an 'event' for the sake of --max-events a non-filtered | ||
1987 | * sys_enter + sys_exit and other tracepoint events. | ||
1988 | */ | ||
1989 | if (++trace->nr_events_printed == trace->max_events && trace->max_events != ULONG_MAX) | ||
1990 | interrupted = true; | ||
1991 | |||
1943 | if (callchain_ret > 0) | 1992 | if (callchain_ret > 0) |
1944 | trace__fprintf_callchain(trace, sample); | 1993 | trace__fprintf_callchain(trace, sample); |
1945 | else if (callchain_ret < 0) | 1994 | else if (callchain_ret < 0) |
@@ -2072,14 +2121,25 @@ static void bpf_output__fprintf(struct trace *trace, | |||
2072 | { | 2121 | { |
2073 | binary__fprintf(sample->raw_data, sample->raw_size, 8, | 2122 | binary__fprintf(sample->raw_data, sample->raw_size, 8, |
2074 | bpf_output__printer, NULL, trace->output); | 2123 | bpf_output__printer, NULL, trace->output); |
2124 | ++trace->nr_events_printed; | ||
2075 | } | 2125 | } |
2076 | 2126 | ||
2077 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | 2127 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, |
2078 | union perf_event *event __maybe_unused, | 2128 | union perf_event *event __maybe_unused, |
2079 | struct perf_sample *sample) | 2129 | struct perf_sample *sample) |
2080 | { | 2130 | { |
2081 | struct thread *thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); | 2131 | struct thread *thread; |
2082 | int callchain_ret = 0; | 2132 | int callchain_ret = 0; |
2133 | /* | ||
2134 | * Check if we called perf_evsel__disable(evsel) due to, for instance, | ||
2135 | * this event's max_events having been hit and this is an entry coming | ||
2136 | * from the ring buffer that we should discard, since the max events | ||
2137 | * have already been considered/printed. | ||
2138 | */ | ||
2139 | if (evsel->disabled) | ||
2140 | return 0; | ||
2141 | |||
2142 | thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); | ||
2083 | 2143 | ||
2084 | if (sample->callchain) { | 2144 | if (sample->callchain) { |
2085 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); | 2145 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); |
@@ -2127,6 +2187,12 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
2127 | event_format__fprintf(evsel->tp_format, sample->cpu, | 2187 | event_format__fprintf(evsel->tp_format, sample->cpu, |
2128 | sample->raw_data, sample->raw_size, | 2188 | sample->raw_data, sample->raw_size, |
2129 | trace->output); | 2189 | trace->output); |
2190 | ++trace->nr_events_printed; | ||
2191 | |||
2192 | if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { | ||
2193 | perf_evsel__disable(evsel); | ||
2194 | perf_evsel__close(evsel); | ||
2195 | } | ||
2130 | } | 2196 | } |
2131 | } | 2197 | } |
2132 | 2198 | ||
@@ -2137,8 +2203,8 @@ newline: | |||
2137 | trace__fprintf_callchain(trace, sample); | 2203 | trace__fprintf_callchain(trace, sample); |
2138 | else if (callchain_ret < 0) | 2204 | else if (callchain_ret < 0) |
2139 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | 2205 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
2140 | thread__put(thread); | ||
2141 | out: | 2206 | out: |
2207 | thread__put(thread); | ||
2142 | return 0; | 2208 | return 0; |
2143 | } | 2209 | } |
2144 | 2210 | ||
@@ -2225,6 +2291,8 @@ static int trace__pgfault(struct trace *trace, | |||
2225 | trace__fprintf_callchain(trace, sample); | 2291 | trace__fprintf_callchain(trace, sample); |
2226 | else if (callchain_ret < 0) | 2292 | else if (callchain_ret < 0) |
2227 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | 2293 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
2294 | |||
2295 | ++trace->nr_events_printed; | ||
2228 | out: | 2296 | out: |
2229 | err = 0; | 2297 | err = 0; |
2230 | out_put: | 2298 | out_put: |
@@ -2402,6 +2470,9 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st | |||
2402 | tracepoint_handler handler = evsel->handler; | 2470 | tracepoint_handler handler = evsel->handler; |
2403 | handler(trace, evsel, event, sample); | 2471 | handler(trace, evsel, event, sample); |
2404 | } | 2472 | } |
2473 | |||
2474 | if (trace->nr_events_printed >= trace->max_events && trace->max_events != ULONG_MAX) | ||
2475 | interrupted = true; | ||
2405 | } | 2476 | } |
2406 | 2477 | ||
2407 | static int trace__add_syscall_newtp(struct trace *trace) | 2478 | static int trace__add_syscall_newtp(struct trace *trace) |
@@ -2706,7 +2777,7 @@ next_event: | |||
2706 | int timeout = done ? 100 : -1; | 2777 | int timeout = done ? 100 : -1; |
2707 | 2778 | ||
2708 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { | 2779 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { |
2709 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0) | 2780 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP | POLLNVAL) == 0) |
2710 | draining = true; | 2781 | draining = true; |
2711 | 2782 | ||
2712 | goto again; | 2783 | goto again; |
@@ -3138,6 +3209,7 @@ static int trace__parse_events_option(const struct option *opt, const char *str, | |||
3138 | int len = strlen(str) + 1, err = -1, list, idx; | 3209 | int len = strlen(str) + 1, err = -1, list, idx; |
3139 | char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); | 3210 | char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); |
3140 | char group_name[PATH_MAX]; | 3211 | char group_name[PATH_MAX]; |
3212 | struct syscall_fmt *fmt; | ||
3141 | 3213 | ||
3142 | if (strace_groups_dir == NULL) | 3214 | if (strace_groups_dir == NULL) |
3143 | return -1; | 3215 | return -1; |
@@ -3155,12 +3227,19 @@ static int trace__parse_events_option(const struct option *opt, const char *str, | |||
3155 | if (syscalltbl__id(trace->sctbl, s) >= 0 || | 3227 | if (syscalltbl__id(trace->sctbl, s) >= 0 || |
3156 | syscalltbl__strglobmatch_first(trace->sctbl, s, &idx) >= 0) { | 3228 | syscalltbl__strglobmatch_first(trace->sctbl, s, &idx) >= 0) { |
3157 | list = 1; | 3229 | list = 1; |
3230 | goto do_concat; | ||
3231 | } | ||
3232 | |||
3233 | fmt = syscall_fmt__find_by_alias(s); | ||
3234 | if (fmt != NULL) { | ||
3235 | list = 1; | ||
3236 | s = fmt->name; | ||
3158 | } else { | 3237 | } else { |
3159 | path__join(group_name, sizeof(group_name), strace_groups_dir, s); | 3238 | path__join(group_name, sizeof(group_name), strace_groups_dir, s); |
3160 | if (access(group_name, R_OK) == 0) | 3239 | if (access(group_name, R_OK) == 0) |
3161 | list = 1; | 3240 | list = 1; |
3162 | } | 3241 | } |
3163 | 3242 | do_concat: | |
3164 | if (lists[list]) { | 3243 | if (lists[list]) { |
3165 | sprintf(lists[list] + strlen(lists[list]), ",%s", s); | 3244 | sprintf(lists[list] + strlen(lists[list]), ",%s", s); |
3166 | } else { | 3245 | } else { |
@@ -3249,6 +3328,7 @@ int cmd_trace(int argc, const char **argv) | |||
3249 | .trace_syscalls = false, | 3328 | .trace_syscalls = false, |
3250 | .kernel_syscallchains = false, | 3329 | .kernel_syscallchains = false, |
3251 | .max_stack = UINT_MAX, | 3330 | .max_stack = UINT_MAX, |
3331 | .max_events = ULONG_MAX, | ||
3252 | }; | 3332 | }; |
3253 | const char *output_name = NULL; | 3333 | const char *output_name = NULL; |
3254 | const struct option trace_options[] = { | 3334 | const struct option trace_options[] = { |
@@ -3301,6 +3381,8 @@ int cmd_trace(int argc, const char **argv) | |||
3301 | &record_parse_callchain_opt), | 3381 | &record_parse_callchain_opt), |
3302 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, | 3382 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, |
3303 | "Show the kernel callchains on the syscall exit path"), | 3383 | "Show the kernel callchains on the syscall exit path"), |
3384 | OPT_ULONG(0, "max-events", &trace.max_events, | ||
3385 | "Set the maximum number of events to print, exit after that is reached. "), | ||
3304 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, | 3386 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, |
3305 | "Set the minimum stack depth when parsing the callchain, " | 3387 | "Set the minimum stack depth when parsing the callchain, " |
3306 | "anything below the specified depth will be ignored."), | 3388 | "anything below the specified depth will be ignored."), |