diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 125 |
1 files changed, 70 insertions, 55 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 84debdbad327..e7f1b182fc15 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "builtin.h" | 21 | #include "builtin.h" |
22 | #include "util/color.h" | 22 | #include "util/color.h" |
23 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/env.h" | ||
24 | #include "util/event.h" | 25 | #include "util/event.h" |
25 | #include "util/evlist.h" | 26 | #include "util/evlist.h" |
26 | #include <subcmd/exec-cmd.h> | 27 | #include <subcmd/exec-cmd.h> |
@@ -45,18 +46,17 @@ | |||
45 | 46 | ||
46 | #include <errno.h> | 47 | #include <errno.h> |
47 | #include <inttypes.h> | 48 | #include <inttypes.h> |
48 | #include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */ | ||
49 | #include <poll.h> | 49 | #include <poll.h> |
50 | #include <signal.h> | 50 | #include <signal.h> |
51 | #include <stdlib.h> | 51 | #include <stdlib.h> |
52 | #include <string.h> | 52 | #include <string.h> |
53 | #include <linux/err.h> | 53 | #include <linux/err.h> |
54 | #include <linux/filter.h> | 54 | #include <linux/filter.h> |
55 | #include <linux/audit.h> | ||
56 | #include <linux/kernel.h> | 55 | #include <linux/kernel.h> |
57 | #include <linux/random.h> | 56 | #include <linux/random.h> |
58 | #include <linux/stringify.h> | 57 | #include <linux/stringify.h> |
59 | #include <linux/time64.h> | 58 | #include <linux/time64.h> |
59 | #include <fcntl.h> | ||
60 | 60 | ||
61 | #include "sane_ctype.h" | 61 | #include "sane_ctype.h" |
62 | 62 | ||
@@ -111,6 +111,7 @@ struct trace { | |||
111 | bool summary; | 111 | bool summary; |
112 | bool summary_only; | 112 | bool summary_only; |
113 | bool show_comm; | 113 | bool show_comm; |
114 | bool print_sample; | ||
114 | bool show_tool_stats; | 115 | bool show_tool_stats; |
115 | bool trace_syscalls; | 116 | bool trace_syscalls; |
116 | bool kernel_syscallchains; | 117 | bool kernel_syscallchains; |
@@ -545,9 +546,10 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, | |||
545 | { .scnprintf = SCA_STRARRAY, \ | 546 | { .scnprintf = SCA_STRARRAY, \ |
546 | .parm = &strarray__##array, } | 547 | .parm = &strarray__##array, } |
547 | 548 | ||
549 | #include "trace/beauty/arch_errno_names.c" | ||
548 | #include "trace/beauty/eventfd.c" | 550 | #include "trace/beauty/eventfd.c" |
549 | #include "trace/beauty/flock.c" | ||
550 | #include "trace/beauty/futex_op.c" | 551 | #include "trace/beauty/futex_op.c" |
552 | #include "trace/beauty/futex_val3.c" | ||
551 | #include "trace/beauty/mmap.c" | 553 | #include "trace/beauty/mmap.c" |
552 | #include "trace/beauty/mode_t.c" | 554 | #include "trace/beauty/mode_t.c" |
553 | #include "trace/beauty/msg_flags.c" | 555 | #include "trace/beauty/msg_flags.c" |
@@ -610,7 +612,8 @@ static struct syscall_fmt { | |||
610 | { .name = "fstat", .alias = "newfstat", }, | 612 | { .name = "fstat", .alias = "newfstat", }, |
611 | { .name = "fstatat", .alias = "newfstatat", }, | 613 | { .name = "fstatat", .alias = "newfstatat", }, |
612 | { .name = "futex", | 614 | { .name = "futex", |
613 | .arg = { [1] = { .scnprintf = SCA_FUTEX_OP, /* op */ }, }, }, | 615 | .arg = { [1] = { .scnprintf = SCA_FUTEX_OP, /* op */ }, |
616 | [5] = { .scnprintf = SCA_FUTEX_VAL3, /* val3 */ }, }, }, | ||
614 | { .name = "futimesat", | 617 | { .name = "futimesat", |
615 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, | 618 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, |
616 | { .name = "getitimer", | 619 | { .name = "getitimer", |
@@ -622,6 +625,7 @@ static struct syscall_fmt { | |||
622 | .arg = { [2] = { .scnprintf = SCA_GETRANDOM_FLAGS, /* flags */ }, }, }, | 625 | .arg = { [2] = { .scnprintf = SCA_GETRANDOM_FLAGS, /* flags */ }, }, }, |
623 | { .name = "getrlimit", | 626 | { .name = "getrlimit", |
624 | .arg = { [0] = STRARRAY(resource, rlimit_resources), }, }, | 627 | .arg = { [0] = STRARRAY(resource, rlimit_resources), }, }, |
628 | { .name = "gettid", .errpid = true, }, | ||
625 | { .name = "ioctl", | 629 | { .name = "ioctl", |
626 | .arg = { | 630 | .arg = { |
627 | #if defined(__i386__) || defined(__x86_64__) | 631 | #if defined(__i386__) || defined(__x86_64__) |
@@ -819,7 +823,7 @@ static size_t fprintf_duration(unsigned long t, bool calculated, FILE *fp) | |||
819 | size_t printed = fprintf(fp, "("); | 823 | size_t printed = fprintf(fp, "("); |
820 | 824 | ||
821 | if (!calculated) | 825 | if (!calculated) |
822 | printed += fprintf(fp, " ? "); | 826 | printed += fprintf(fp, " "); |
823 | else if (duration >= 1.0) | 827 | else if (duration >= 1.0) |
824 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); | 828 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); |
825 | else if (duration >= 0.01) | 829 | else if (duration >= 0.01) |
@@ -1554,10 +1558,9 @@ static void thread__update_stats(struct thread_trace *ttrace, | |||
1554 | update_stats(stats, duration); | 1558 | update_stats(stats, duration); |
1555 | } | 1559 | } |
1556 | 1560 | ||
1557 | static int trace__printf_interrupted_entry(struct trace *trace, struct perf_sample *sample) | 1561 | static int trace__printf_interrupted_entry(struct trace *trace) |
1558 | { | 1562 | { |
1559 | struct thread_trace *ttrace; | 1563 | struct thread_trace *ttrace; |
1560 | u64 duration; | ||
1561 | size_t printed; | 1564 | size_t printed; |
1562 | 1565 | ||
1563 | if (trace->current == NULL) | 1566 | if (trace->current == NULL) |
@@ -1568,15 +1571,30 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp | |||
1568 | if (!ttrace->entry_pending) | 1571 | if (!ttrace->entry_pending) |
1569 | return 0; | 1572 | return 0; |
1570 | 1573 | ||
1571 | duration = sample->time - ttrace->entry_time; | 1574 | printed = trace__fprintf_entry_head(trace, trace->current, 0, false, ttrace->entry_time, trace->output); |
1572 | |||
1573 | printed = trace__fprintf_entry_head(trace, trace->current, duration, true, ttrace->entry_time, trace->output); | ||
1574 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); | 1575 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); |
1575 | ttrace->entry_pending = false; | 1576 | ttrace->entry_pending = false; |
1576 | 1577 | ||
1577 | return printed; | 1578 | return printed; |
1578 | } | 1579 | } |
1579 | 1580 | ||
1581 | static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, | ||
1582 | struct perf_sample *sample, struct thread *thread) | ||
1583 | { | ||
1584 | int printed = 0; | ||
1585 | |||
1586 | if (trace->print_sample) { | ||
1587 | double ts = (double)sample->time / NSEC_PER_MSEC; | ||
1588 | |||
1589 | printed += fprintf(trace->output, "%22s %10.3f %s %d/%d [%d]\n", | ||
1590 | perf_evsel__name(evsel), ts, | ||
1591 | thread__comm_str(thread), | ||
1592 | sample->pid, sample->tid, sample->cpu); | ||
1593 | } | ||
1594 | |||
1595 | return printed; | ||
1596 | } | ||
1597 | |||
1580 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 1598 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
1581 | union perf_event *event __maybe_unused, | 1599 | union perf_event *event __maybe_unused, |
1582 | struct perf_sample *sample) | 1600 | struct perf_sample *sample) |
@@ -1597,6 +1615,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1597 | if (ttrace == NULL) | 1615 | if (ttrace == NULL) |
1598 | goto out_put; | 1616 | goto out_put; |
1599 | 1617 | ||
1618 | trace__fprintf_sample(trace, evsel, sample, thread); | ||
1619 | |||
1600 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1620 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
1601 | 1621 | ||
1602 | if (ttrace->entry_str == NULL) { | 1622 | if (ttrace->entry_str == NULL) { |
@@ -1606,7 +1626,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1606 | } | 1626 | } |
1607 | 1627 | ||
1608 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) | 1628 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) |
1609 | trace__printf_interrupted_entry(trace, sample); | 1629 | trace__printf_interrupted_entry(trace); |
1610 | 1630 | ||
1611 | ttrace->entry_time = sample->time; | 1631 | ttrace->entry_time = sample->time; |
1612 | msg = ttrace->entry_str; | 1632 | msg = ttrace->entry_str; |
@@ -1641,9 +1661,12 @@ static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evse | |||
1641 | struct callchain_cursor *cursor) | 1661 | struct callchain_cursor *cursor) |
1642 | { | 1662 | { |
1643 | struct addr_location al; | 1663 | struct addr_location al; |
1664 | int max_stack = evsel->attr.sample_max_stack ? | ||
1665 | evsel->attr.sample_max_stack : | ||
1666 | trace->max_stack; | ||
1644 | 1667 | ||
1645 | if (machine__resolve(trace->host, &al, sample) < 0 || | 1668 | if (machine__resolve(trace->host, &al, sample) < 0 || |
1646 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, trace->max_stack)) | 1669 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack)) |
1647 | return -1; | 1670 | return -1; |
1648 | 1671 | ||
1649 | return 0; | 1672 | return 0; |
@@ -1659,6 +1682,14 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam | |||
1659 | return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output); | 1682 | return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output); |
1660 | } | 1683 | } |
1661 | 1684 | ||
1685 | static const char *errno_to_name(struct perf_evsel *evsel, int err) | ||
1686 | { | ||
1687 | struct perf_env *env = perf_evsel__env(evsel); | ||
1688 | const char *arch_name = perf_env__arch(env); | ||
1689 | |||
1690 | return arch_syscalls__strerrno(arch_name, err); | ||
1691 | } | ||
1692 | |||
1662 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | 1693 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, |
1663 | union perf_event *event __maybe_unused, | 1694 | union perf_event *event __maybe_unused, |
1664 | struct perf_sample *sample) | 1695 | struct perf_sample *sample) |
@@ -1679,6 +1710,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1679 | if (ttrace == NULL) | 1710 | if (ttrace == NULL) |
1680 | goto out_put; | 1711 | goto out_put; |
1681 | 1712 | ||
1713 | trace__fprintf_sample(trace, evsel, sample, thread); | ||
1714 | |||
1682 | if (trace->summary) | 1715 | if (trace->summary) |
1683 | thread__update_stats(ttrace, id, sample); | 1716 | thread__update_stats(ttrace, id, sample); |
1684 | 1717 | ||
@@ -1729,7 +1762,7 @@ signed_print: | |||
1729 | errno_print: { | 1762 | errno_print: { |
1730 | char bf[STRERR_BUFSIZE]; | 1763 | char bf[STRERR_BUFSIZE]; |
1731 | const char *emsg = str_error_r(-ret, bf, sizeof(bf)), | 1764 | const char *emsg = str_error_r(-ret, bf, sizeof(bf)), |
1732 | *e = audit_errno_to_name(-ret); | 1765 | *e = errno_to_name(evsel, -ret); |
1733 | 1766 | ||
1734 | fprintf(trace->output, ") = -1 %s %s", e, emsg); | 1767 | fprintf(trace->output, ") = -1 %s %s", e, emsg); |
1735 | } | 1768 | } |
@@ -1910,7 +1943,7 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
1910 | } | 1943 | } |
1911 | } | 1944 | } |
1912 | 1945 | ||
1913 | trace__printf_interrupted_entry(trace, sample); | 1946 | trace__printf_interrupted_entry(trace); |
1914 | trace__fprintf_tstamp(trace, sample->time, trace->output); | 1947 | trace__fprintf_tstamp(trace, sample->time, trace->output); |
1915 | 1948 | ||
1916 | if (trace->trace_syscalls) | 1949 | if (trace->trace_syscalls) |
@@ -2221,6 +2254,9 @@ static int trace__add_syscall_newtp(struct trace *trace) | |||
2221 | if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) | 2254 | if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) |
2222 | goto out_delete_sys_exit; | 2255 | goto out_delete_sys_exit; |
2223 | 2256 | ||
2257 | perf_evsel__config_callchain(sys_enter, &trace->opts, &callchain_param); | ||
2258 | perf_evsel__config_callchain(sys_exit, &trace->opts, &callchain_param); | ||
2259 | |||
2224 | perf_evlist__add(evlist, sys_enter); | 2260 | perf_evlist__add(evlist, sys_enter); |
2225 | perf_evlist__add(evlist, sys_exit); | 2261 | perf_evlist__add(evlist, sys_exit); |
2226 | 2262 | ||
@@ -2317,6 +2353,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2317 | pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); | 2353 | pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ); |
2318 | if (pgfault_maj == NULL) | 2354 | if (pgfault_maj == NULL) |
2319 | goto out_error_mem; | 2355 | goto out_error_mem; |
2356 | perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); | ||
2320 | perf_evlist__add(evlist, pgfault_maj); | 2357 | perf_evlist__add(evlist, pgfault_maj); |
2321 | } | 2358 | } |
2322 | 2359 | ||
@@ -2324,6 +2361,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2324 | pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); | 2361 | pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN); |
2325 | if (pgfault_min == NULL) | 2362 | if (pgfault_min == NULL) |
2326 | goto out_error_mem; | 2363 | goto out_error_mem; |
2364 | perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); | ||
2327 | perf_evlist__add(evlist, pgfault_min); | 2365 | perf_evlist__add(evlist, pgfault_min); |
2328 | } | 2366 | } |
2329 | 2367 | ||
@@ -2344,45 +2382,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2344 | goto out_delete_evlist; | 2382 | goto out_delete_evlist; |
2345 | } | 2383 | } |
2346 | 2384 | ||
2347 | perf_evlist__config(evlist, &trace->opts, NULL); | 2385 | perf_evlist__config(evlist, &trace->opts, &callchain_param); |
2348 | |||
2349 | if (callchain_param.enabled) { | ||
2350 | bool use_identifier = false; | ||
2351 | |||
2352 | if (trace->syscalls.events.sys_exit) { | ||
2353 | perf_evsel__config_callchain(trace->syscalls.events.sys_exit, | ||
2354 | &trace->opts, &callchain_param); | ||
2355 | use_identifier = true; | ||
2356 | } | ||
2357 | |||
2358 | if (pgfault_maj) { | ||
2359 | perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param); | ||
2360 | use_identifier = true; | ||
2361 | } | ||
2362 | |||
2363 | if (pgfault_min) { | ||
2364 | perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param); | ||
2365 | use_identifier = true; | ||
2366 | } | ||
2367 | |||
2368 | if (use_identifier) { | ||
2369 | /* | ||
2370 | * Now we have evsels with different sample_ids, use | ||
2371 | * PERF_SAMPLE_IDENTIFIER to map from sample to evsel | ||
2372 | * from a fixed position in each ring buffer record. | ||
2373 | * | ||
2374 | * As of this the changeset introducing this comment, this | ||
2375 | * isn't strictly needed, as the fields that can come before | ||
2376 | * PERF_SAMPLE_ID are all used, but we'll probably disable | ||
2377 | * some of those for things like copying the payload of | ||
2378 | * pointer syscall arguments, and for vfs_getname we don't | ||
2379 | * need PERF_SAMPLE_ADDR and PERF_SAMPLE_IP, so do this | ||
2380 | * here as a warning we need to use PERF_SAMPLE_IDENTIFIER. | ||
2381 | */ | ||
2382 | perf_evlist__set_sample_bit(evlist, IDENTIFIER); | ||
2383 | perf_evlist__reset_sample_bit(evlist, ID); | ||
2384 | } | ||
2385 | } | ||
2386 | 2386 | ||
2387 | signal(SIGCHLD, sig_handler); | 2387 | signal(SIGCHLD, sig_handler); |
2388 | signal(SIGINT, sig_handler); | 2388 | signal(SIGINT, sig_handler); |
@@ -2437,7 +2437,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2437 | if (err < 0) | 2437 | if (err < 0) |
2438 | goto out_error_apply_filters; | 2438 | goto out_error_apply_filters; |
2439 | 2439 | ||
2440 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); | 2440 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages); |
2441 | if (err < 0) | 2441 | if (err < 0) |
2442 | goto out_error_mmap; | 2442 | goto out_error_mmap; |
2443 | 2443 | ||
@@ -2455,6 +2455,18 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2455 | trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || | 2455 | trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || |
2456 | evlist->threads->nr > 1 || | 2456 | evlist->threads->nr > 1 || |
2457 | perf_evlist__first(evlist)->attr.inherit; | 2457 | perf_evlist__first(evlist)->attr.inherit; |
2458 | |||
2459 | /* | ||
2460 | * Now that we already used evsel->attr to ask the kernel to setup the | ||
2461 | * events, lets reuse evsel->attr.sample_max_stack as the limit in | ||
2462 | * trace__resolve_callchain(), allowing per-event max-stack settings | ||
2463 | * to override an explicitely set --max-stack global setting. | ||
2464 | */ | ||
2465 | evlist__for_each_entry(evlist, evsel) { | ||
2466 | if ((evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) && | ||
2467 | evsel->attr.sample_max_stack == 0) | ||
2468 | evsel->attr.sample_max_stack = trace->max_stack; | ||
2469 | } | ||
2458 | again: | 2470 | again: |
2459 | before = trace->nr_events; | 2471 | before = trace->nr_events; |
2460 | 2472 | ||
@@ -3046,6 +3058,8 @@ int cmd_trace(int argc, const char **argv) | |||
3046 | "Set the maximum stack depth when parsing the callchain, " | 3058 | "Set the maximum stack depth when parsing the callchain, " |
3047 | "anything beyond the specified depth will be ignored. " | 3059 | "anything beyond the specified depth will be ignored. " |
3048 | "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)), | 3060 | "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)), |
3061 | OPT_BOOLEAN(0, "print-sample", &trace.print_sample, | ||
3062 | "print the PERF_RECORD_SAMPLE PERF_SAMPLE_ info, for debugging"), | ||
3049 | OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, | 3063 | OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, |
3050 | "per thread proc mmap processing timeout in ms"), | 3064 | "per thread proc mmap processing timeout in ms"), |
3051 | OPT_UINTEGER('D', "delay", &trace.opts.initial_delay, | 3065 | OPT_UINTEGER('D', "delay", &trace.opts.initial_delay, |
@@ -3097,8 +3111,9 @@ int cmd_trace(int argc, const char **argv) | |||
3097 | } | 3111 | } |
3098 | 3112 | ||
3099 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 3113 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
3100 | if ((trace.min_stack || max_stack_user_set) && !callchain_param.enabled && trace.trace_syscalls) | 3114 | if ((trace.min_stack || max_stack_user_set) && !callchain_param.enabled) { |
3101 | record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false); | 3115 | record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false); |
3116 | } | ||
3102 | #endif | 3117 | #endif |
3103 | 3118 | ||
3104 | if (callchain_param.enabled) { | 3119 | if (callchain_param.enabled) { |