diff options
55 files changed, 2543 insertions, 1573 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 2a62b96600ad..abaed4f8bb7f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -2867,16 +2867,11 @@ static int perf_event_modify_breakpoint(struct perf_event *bp, | |||
| 2867 | _perf_event_disable(bp); | 2867 | _perf_event_disable(bp); |
| 2868 | 2868 | ||
| 2869 | err = modify_user_hw_breakpoint_check(bp, attr, true); | 2869 | err = modify_user_hw_breakpoint_check(bp, attr, true); |
| 2870 | if (err) { | ||
| 2871 | if (!bp->attr.disabled) | ||
| 2872 | _perf_event_enable(bp); | ||
| 2873 | 2870 | ||
| 2874 | return err; | 2871 | if (!bp->attr.disabled) |
| 2875 | } | ||
| 2876 | |||
| 2877 | if (!attr->disabled) | ||
| 2878 | _perf_event_enable(bp); | 2872 | _perf_event_enable(bp); |
| 2879 | return 0; | 2873 | |
| 2874 | return err; | ||
| 2880 | } | 2875 | } |
| 2881 | 2876 | ||
| 2882 | static int perf_event_modify_attr(struct perf_event *event, | 2877 | static int perf_event_modify_attr(struct perf_event *event, |
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index b3814fce5ecb..d6b56180827c 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c | |||
| @@ -509,6 +509,8 @@ modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *a | |||
| 509 | */ | 509 | */ |
| 510 | int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) | 510 | int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) |
| 511 | { | 511 | { |
| 512 | int err; | ||
| 513 | |||
| 512 | /* | 514 | /* |
| 513 | * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it | 515 | * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it |
| 514 | * will not be possible to raise IPIs that invoke __perf_event_disable. | 516 | * will not be possible to raise IPIs that invoke __perf_event_disable. |
| @@ -520,15 +522,12 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att | |||
| 520 | else | 522 | else |
| 521 | perf_event_disable(bp); | 523 | perf_event_disable(bp); |
| 522 | 524 | ||
| 523 | if (!attr->disabled) { | 525 | err = modify_user_hw_breakpoint_check(bp, attr, false); |
| 524 | int err = modify_user_hw_breakpoint_check(bp, attr, false); | ||
| 525 | 526 | ||
| 526 | if (err) | 527 | if (!bp->attr.disabled) |
| 527 | return err; | ||
| 528 | perf_event_enable(bp); | 528 | perf_event_enable(bp); |
| 529 | bp->attr.disabled = 0; | 529 | |
| 530 | } | 530 | return err; |
| 531 | return 0; | ||
| 532 | } | 531 | } |
| 533 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); | 532 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); |
| 534 | 533 | ||
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index ce1e20227c64..70a42bec6931 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <netinet/in.h> | 24 | #include <netinet/in.h> |
| 25 | #include "event-parse.h" | 25 | #include "event-parse.h" |
| 26 | #include "event-utils.h" | 26 | #include "event-utils.h" |
| 27 | #include "trace-seq.h" | ||
| 27 | 28 | ||
| 28 | static const char *input_buf; | 29 | static const char *input_buf; |
| 29 | static unsigned long long input_buf_ptr; | 30 | static unsigned long long input_buf_ptr; |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 44b7c2d41f9f..fa665c66bfa4 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
| @@ -26,17 +26,12 @@ | |||
| 26 | #include <regex.h> | 26 | #include <regex.h> |
| 27 | #include <string.h> | 27 | #include <string.h> |
| 28 | 28 | ||
| 29 | #include "trace-seq.h" | ||
| 30 | |||
| 29 | #ifndef __maybe_unused | 31 | #ifndef __maybe_unused |
| 30 | #define __maybe_unused __attribute__((unused)) | 32 | #define __maybe_unused __attribute__((unused)) |
| 31 | #endif | 33 | #endif |
| 32 | 34 | ||
| 33 | /* ----------------------- trace_seq ----------------------- */ | ||
| 34 | |||
| 35 | |||
| 36 | #ifndef TRACE_SEQ_BUF_SIZE | ||
| 37 | #define TRACE_SEQ_BUF_SIZE 4096 | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #ifndef DEBUG_RECORD | 35 | #ifndef DEBUG_RECORD |
| 41 | #define DEBUG_RECORD 0 | 36 | #define DEBUG_RECORD 0 |
| 42 | #endif | 37 | #endif |
| @@ -59,43 +54,6 @@ struct tep_record { | |||
| 59 | #endif | 54 | #endif |
| 60 | }; | 55 | }; |
| 61 | 56 | ||
| 62 | enum trace_seq_fail { | ||
| 63 | TRACE_SEQ__GOOD, | ||
| 64 | TRACE_SEQ__BUFFER_POISONED, | ||
| 65 | TRACE_SEQ__MEM_ALLOC_FAILED, | ||
| 66 | }; | ||
| 67 | |||
| 68 | /* | ||
| 69 | * Trace sequences are used to allow a function to call several other functions | ||
| 70 | * to create a string of data to use (up to a max of PAGE_SIZE). | ||
| 71 | */ | ||
| 72 | |||
| 73 | struct trace_seq { | ||
| 74 | char *buffer; | ||
| 75 | unsigned int buffer_size; | ||
| 76 | unsigned int len; | ||
| 77 | unsigned int readpos; | ||
| 78 | enum trace_seq_fail state; | ||
| 79 | }; | ||
| 80 | |||
| 81 | void trace_seq_init(struct trace_seq *s); | ||
| 82 | void trace_seq_reset(struct trace_seq *s); | ||
| 83 | void trace_seq_destroy(struct trace_seq *s); | ||
| 84 | |||
| 85 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | ||
| 86 | __attribute__ ((format (printf, 2, 3))); | ||
| 87 | extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) | ||
| 88 | __attribute__ ((format (printf, 2, 0))); | ||
| 89 | |||
| 90 | extern int trace_seq_puts(struct trace_seq *s, const char *str); | ||
| 91 | extern int trace_seq_putc(struct trace_seq *s, unsigned char c); | ||
| 92 | |||
| 93 | extern void trace_seq_terminate(struct trace_seq *s); | ||
| 94 | |||
| 95 | extern int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp); | ||
| 96 | extern int trace_seq_do_printf(struct trace_seq *s); | ||
| 97 | |||
| 98 | |||
| 99 | /* ----------------------- pevent ----------------------- */ | 57 | /* ----------------------- pevent ----------------------- */ |
| 100 | 58 | ||
| 101 | struct tep_handle; | 59 | struct tep_handle; |
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c index f17e25097e1e..ec16a103c0cc 100644 --- a/tools/lib/traceevent/event-plugin.c +++ b/tools/lib/traceevent/event-plugin.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <dirent.h> | 15 | #include <dirent.h> |
| 16 | #include "event-parse.h" | 16 | #include "event-parse.h" |
| 17 | #include "event-utils.h" | 17 | #include "event-utils.h" |
| 18 | #include "trace-seq.h" | ||
| 18 | 19 | ||
| 19 | #define LOCAL_PLUGIN_DIR ".traceevent/plugins" | 20 | #define LOCAL_PLUGIN_DIR ".traceevent/plugins" |
| 20 | 21 | ||
diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c index 424747475d37..2919042e7dc2 100644 --- a/tools/lib/traceevent/plugin_function.c +++ b/tools/lib/traceevent/plugin_function.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | #include "event-parse.h" | 24 | #include "event-parse.h" |
| 25 | #include "event-utils.h" | 25 | #include "event-utils.h" |
| 26 | #include "trace-seq.h" | ||
| 26 | 27 | ||
| 27 | static struct func_stack { | 28 | static struct func_stack { |
| 28 | int size; | 29 | int size; |
diff --git a/tools/lib/traceevent/plugin_hrtimer.c b/tools/lib/traceevent/plugin_hrtimer.c index b43bfec565d8..29b608076ea0 100644 --- a/tools/lib/traceevent/plugin_hrtimer.c +++ b/tools/lib/traceevent/plugin_hrtimer.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <string.h> | 23 | #include <string.h> |
| 24 | 24 | ||
| 25 | #include "event-parse.h" | 25 | #include "event-parse.h" |
| 26 | #include "trace-seq.h" | ||
| 26 | 27 | ||
| 27 | static int timer_expire_handler(struct trace_seq *s, | 28 | static int timer_expire_handler(struct trace_seq *s, |
| 28 | struct tep_record *record, | 29 | struct tep_record *record, |
diff --git a/tools/lib/traceevent/plugin_jbd2.c b/tools/lib/traceevent/plugin_jbd2.c index 45a9acd19640..a5e34135dd6a 100644 --- a/tools/lib/traceevent/plugin_jbd2.c +++ b/tools/lib/traceevent/plugin_jbd2.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <string.h> | 22 | #include <string.h> |
| 23 | 23 | ||
| 24 | #include "event-parse.h" | 24 | #include "event-parse.h" |
| 25 | #include "trace-seq.h" | ||
| 25 | 26 | ||
| 26 | #define MINORBITS 20 | 27 | #define MINORBITS 20 |
| 27 | #define MINORMASK ((1U << MINORBITS) - 1) | 28 | #define MINORMASK ((1U << MINORBITS) - 1) |
diff --git a/tools/lib/traceevent/plugin_kmem.c b/tools/lib/traceevent/plugin_kmem.c index 73966b05abce..a7a162575e2c 100644 --- a/tools/lib/traceevent/plugin_kmem.c +++ b/tools/lib/traceevent/plugin_kmem.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <string.h> | 22 | #include <string.h> |
| 23 | 23 | ||
| 24 | #include "event-parse.h" | 24 | #include "event-parse.h" |
| 25 | #include "trace-seq.h" | ||
| 25 | 26 | ||
| 26 | static int call_site_handler(struct trace_seq *s, struct tep_record *record, | 27 | static int call_site_handler(struct trace_seq *s, struct tep_record *record, |
| 27 | struct event_format *event, void *context) | 28 | struct event_format *event, void *context) |
diff --git a/tools/lib/traceevent/plugin_kvm.c b/tools/lib/traceevent/plugin_kvm.c index 1d0d15906225..a0dfd3d0f197 100644 --- a/tools/lib/traceevent/plugin_kvm.c +++ b/tools/lib/traceevent/plugin_kvm.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <stdint.h> | 23 | #include <stdint.h> |
| 24 | 24 | ||
| 25 | #include "event-parse.h" | 25 | #include "event-parse.h" |
| 26 | #include "trace-seq.h" | ||
| 26 | 27 | ||
| 27 | #ifdef HAVE_UDIS86 | 28 | #ifdef HAVE_UDIS86 |
| 28 | 29 | ||
diff --git a/tools/lib/traceevent/plugin_mac80211.c b/tools/lib/traceevent/plugin_mac80211.c index de50a5316203..0b7779444b63 100644 --- a/tools/lib/traceevent/plugin_mac80211.c +++ b/tools/lib/traceevent/plugin_mac80211.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <string.h> | 22 | #include <string.h> |
| 23 | 23 | ||
| 24 | #include "event-parse.h" | 24 | #include "event-parse.h" |
| 25 | #include "trace-seq.h" | ||
| 25 | 26 | ||
| 26 | #define INDENT 65 | 27 | #define INDENT 65 |
| 27 | 28 | ||
diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c index eecb4bd95c11..582d3be2849b 100644 --- a/tools/lib/traceevent/plugin_sched_switch.c +++ b/tools/lib/traceevent/plugin_sched_switch.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <string.h> | 22 | #include <string.h> |
| 23 | 23 | ||
| 24 | #include "event-parse.h" | 24 | #include "event-parse.h" |
| 25 | #include "trace-seq.h" | ||
| 25 | 26 | ||
| 26 | static void write_state(struct trace_seq *s, int val) | 27 | static void write_state(struct trace_seq *s, int val) |
| 27 | { | 28 | { |
diff --git a/tools/lib/traceevent/plugin_scsi.c b/tools/lib/traceevent/plugin_scsi.c index 5ec346f6b842..4eba25cc1431 100644 --- a/tools/lib/traceevent/plugin_scsi.c +++ b/tools/lib/traceevent/plugin_scsi.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <string.h> | 3 | #include <string.h> |
| 4 | #include <inttypes.h> | 4 | #include <inttypes.h> |
| 5 | #include "event-parse.h" | 5 | #include "event-parse.h" |
| 6 | #include "trace-seq.h" | ||
| 6 | 7 | ||
| 7 | typedef unsigned long sector_t; | 8 | typedef unsigned long sector_t; |
| 8 | typedef uint64_t u64; | 9 | typedef uint64_t u64; |
diff --git a/tools/lib/traceevent/plugin_xen.c b/tools/lib/traceevent/plugin_xen.c index b2acbd6e9c86..bc0496e4c296 100644 --- a/tools/lib/traceevent/plugin_xen.c +++ b/tools/lib/traceevent/plugin_xen.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
| 4 | #include <string.h> | 4 | #include <string.h> |
| 5 | #include "event-parse.h" | 5 | #include "event-parse.h" |
| 6 | #include "trace-seq.h" | ||
| 6 | 7 | ||
| 7 | #define __HYPERVISOR_set_trap_table 0 | 8 | #define __HYPERVISOR_set_trap_table 0 |
| 8 | #define __HYPERVISOR_mmu_update 1 | 9 | #define __HYPERVISOR_mmu_update 1 |
diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c index e3bac4543d3b..8ff1d55954d1 100644 --- a/tools/lib/traceevent/trace-seq.c +++ b/tools/lib/traceevent/trace-seq.c | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | 3 | * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> |
| 4 | * | 4 | * |
| 5 | */ | 5 | */ |
| 6 | #include "trace-seq.h" | ||
| 7 | |||
| 6 | #include <stdio.h> | 8 | #include <stdio.h> |
| 7 | #include <stdlib.h> | 9 | #include <stdlib.h> |
| 8 | #include <string.h> | 10 | #include <string.h> |
diff --git a/tools/lib/traceevent/trace-seq.h b/tools/lib/traceevent/trace-seq.h new file mode 100644 index 000000000000..d68ec69f8d1a --- /dev/null +++ b/tools/lib/traceevent/trace-seq.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // SPDX-License-Identifier: LGPL-2.1 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
| 4 | * | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _TRACE_SEQ_H | ||
| 8 | #define _TRACE_SEQ_H | ||
| 9 | |||
| 10 | #include <stdarg.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | |||
| 13 | /* ----------------------- trace_seq ----------------------- */ | ||
| 14 | |||
| 15 | #ifndef TRACE_SEQ_BUF_SIZE | ||
| 16 | #define TRACE_SEQ_BUF_SIZE 4096 | ||
| 17 | #endif | ||
| 18 | |||
| 19 | enum trace_seq_fail { | ||
| 20 | TRACE_SEQ__GOOD, | ||
| 21 | TRACE_SEQ__BUFFER_POISONED, | ||
| 22 | TRACE_SEQ__MEM_ALLOC_FAILED, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Trace sequences are used to allow a function to call several other functions | ||
| 27 | * to create a string of data to use (up to a max of PAGE_SIZE). | ||
| 28 | */ | ||
| 29 | |||
| 30 | struct trace_seq { | ||
| 31 | char *buffer; | ||
| 32 | unsigned int buffer_size; | ||
| 33 | unsigned int len; | ||
| 34 | unsigned int readpos; | ||
| 35 | enum trace_seq_fail state; | ||
| 36 | }; | ||
| 37 | |||
| 38 | void trace_seq_init(struct trace_seq *s); | ||
| 39 | void trace_seq_reset(struct trace_seq *s); | ||
| 40 | void trace_seq_destroy(struct trace_seq *s); | ||
| 41 | |||
| 42 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | ||
| 43 | __attribute__ ((format (printf, 2, 3))); | ||
| 44 | extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) | ||
| 45 | __attribute__ ((format (printf, 2, 0))); | ||
| 46 | |||
| 47 | extern int trace_seq_puts(struct trace_seq *s, const char *str); | ||
| 48 | extern int trace_seq_putc(struct trace_seq *s, unsigned char c); | ||
| 49 | |||
| 50 | extern void trace_seq_terminate(struct trace_seq *s); | ||
| 51 | |||
| 52 | extern int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp); | ||
| 53 | extern int trace_seq_do_printf(struct trace_seq *s); | ||
| 54 | |||
| 55 | #endif /* _TRACE_SEQ_H */ | ||
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index b3d1b12a5081..92514fb3689f 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -777,14 +777,14 @@ endif | |||
| 777 | $(call QUIET_INSTALL, libexec) \ | 777 | $(call QUIET_INSTALL, libexec) \ |
| 778 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 778 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
| 779 | ifndef NO_LIBBPF | 779 | ifndef NO_LIBBPF |
| 780 | $(call QUIET_INSTALL, lib) \ | 780 | $(call QUIET_INSTALL, bpf-headers) \ |
| 781 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf' | 781 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \ |
| 782 | $(call QUIET_INSTALL, include/bpf) \ | 782 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf/linux'; \ |
| 783 | $(INSTALL) include/bpf/*.h '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf' | 783 | $(INSTALL) include/bpf/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \ |
| 784 | $(call QUIET_INSTALL, lib) \ | 784 | $(INSTALL) include/bpf/linux/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf/linux' |
| 785 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' | 785 | $(call QUIET_INSTALL, bpf-examples) \ |
| 786 | $(call QUIET_INSTALL, examples/bpf) \ | 786 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'; \ |
| 787 | $(INSTALL) examples/bpf/*.c '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' | 787 | $(INSTALL) examples/bpf/*.c -t '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' |
| 788 | endif | 788 | endif |
| 789 | $(call QUIET_INSTALL, perf-archive) \ | 789 | $(call QUIET_INSTALL, perf-archive) \ |
| 790 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 790 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile index f013b115dc86..dbef716a1913 100644 --- a/tools/perf/arch/arm64/Makefile +++ b/tools/perf/arch/arm64/Makefile | |||
| @@ -11,7 +11,8 @@ PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 | |||
| 11 | 11 | ||
| 12 | out := $(OUTPUT)arch/arm64/include/generated/asm | 12 | out := $(OUTPUT)arch/arm64/include/generated/asm |
| 13 | header := $(out)/syscalls.c | 13 | header := $(out)/syscalls.c |
| 14 | sysdef := $(srctree)/tools/include/uapi/asm-generic/unistd.h | 14 | incpath := $(srctree)/tools |
| 15 | sysdef := $(srctree)/tools/arch/arm64/include/uapi/asm/unistd.h | ||
| 15 | sysprf := $(srctree)/tools/perf/arch/arm64/entry/syscalls/ | 16 | sysprf := $(srctree)/tools/perf/arch/arm64/entry/syscalls/ |
| 16 | systbl := $(sysprf)/mksyscalltbl | 17 | systbl := $(sysprf)/mksyscalltbl |
| 17 | 18 | ||
| @@ -19,7 +20,7 @@ systbl := $(sysprf)/mksyscalltbl | |||
| 19 | _dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)') | 20 | _dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)') |
| 20 | 21 | ||
| 21 | $(header): $(sysdef) $(systbl) | 22 | $(header): $(sysdef) $(systbl) |
| 22 | $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(sysdef) > $@ | 23 | $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@ |
| 23 | 24 | ||
| 24 | clean:: | 25 | clean:: |
| 25 | $(call QUIET_CLEAN, arm64) $(RM) $(header) | 26 | $(call QUIET_CLEAN, arm64) $(RM) $(header) |
diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index 6688977e4ac7..76c6345a57d5 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c | |||
| @@ -8,6 +8,63 @@ struct arm64_annotate { | |||
| 8 | jump_insn; | 8 | jump_insn; |
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | static int arm64_mov__parse(struct arch *arch __maybe_unused, | ||
| 12 | struct ins_operands *ops, | ||
| 13 | struct map_symbol *ms __maybe_unused) | ||
| 14 | { | ||
| 15 | char *s = strchr(ops->raw, ','), *target, *endptr; | ||
| 16 | |||
| 17 | if (s == NULL) | ||
| 18 | return -1; | ||
| 19 | |||
| 20 | *s = '\0'; | ||
| 21 | ops->source.raw = strdup(ops->raw); | ||
| 22 | *s = ','; | ||
| 23 | |||
| 24 | if (ops->source.raw == NULL) | ||
| 25 | return -1; | ||
| 26 | |||
| 27 | target = ++s; | ||
| 28 | ops->target.raw = strdup(target); | ||
| 29 | if (ops->target.raw == NULL) | ||
| 30 | goto out_free_source; | ||
| 31 | |||
| 32 | ops->target.addr = strtoull(target, &endptr, 16); | ||
| 33 | if (endptr == target) | ||
| 34 | goto out_free_target; | ||
| 35 | |||
| 36 | s = strchr(endptr, '<'); | ||
| 37 | if (s == NULL) | ||
| 38 | goto out_free_target; | ||
| 39 | endptr = strchr(s + 1, '>'); | ||
| 40 | if (endptr == NULL) | ||
| 41 | goto out_free_target; | ||
| 42 | |||
| 43 | *endptr = '\0'; | ||
| 44 | *s = ' '; | ||
| 45 | ops->target.name = strdup(s); | ||
| 46 | *s = '<'; | ||
| 47 | *endptr = '>'; | ||
| 48 | if (ops->target.name == NULL) | ||
| 49 | goto out_free_target; | ||
| 50 | |||
| 51 | return 0; | ||
| 52 | |||
| 53 | out_free_target: | ||
| 54 | zfree(&ops->target.raw); | ||
| 55 | out_free_source: | ||
| 56 | zfree(&ops->source.raw); | ||
| 57 | return -1; | ||
| 58 | } | ||
| 59 | |||
| 60 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | ||
| 61 | struct ins_operands *ops); | ||
| 62 | |||
| 63 | static struct ins_ops arm64_mov_ops = { | ||
| 64 | .parse = arm64_mov__parse, | ||
| 65 | .scnprintf = mov__scnprintf, | ||
| 66 | }; | ||
| 67 | |||
| 11 | static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) | 68 | static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) |
| 12 | { | 69 | { |
| 13 | struct arm64_annotate *arm = arch->priv; | 70 | struct arm64_annotate *arm = arch->priv; |
| @@ -21,7 +78,7 @@ static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const | |||
| 21 | else if (!strcmp(name, "ret")) | 78 | else if (!strcmp(name, "ret")) |
| 22 | ops = &ret_ops; | 79 | ops = &ret_ops; |
| 23 | else | 80 | else |
| 24 | return NULL; | 81 | ops = &arm64_mov_ops; |
| 25 | 82 | ||
| 26 | arch__associate_ins_ops(arch, name, ops); | 83 | arch__associate_ins_ops(arch, name, ops); |
| 27 | return ops; | 84 | return ops; |
diff --git a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl index 52e197317d3e..2dbb8cade048 100755 --- a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl +++ b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl | |||
| @@ -11,7 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | gcc=$1 | 12 | gcc=$1 |
| 13 | hostcc=$2 | 13 | hostcc=$2 |
| 14 | input=$3 | 14 | incpath=$3 |
| 15 | input=$4 | ||
| 15 | 16 | ||
| 16 | if ! test -r $input; then | 17 | if ! test -r $input; then |
| 17 | echo "Could not read input file" >&2 | 18 | echo "Could not read input file" >&2 |
| @@ -28,7 +29,6 @@ create_table_from_c() | |||
| 28 | 29 | ||
| 29 | cat <<-_EoHEADER | 30 | cat <<-_EoHEADER |
| 30 | #include <stdio.h> | 31 | #include <stdio.h> |
| 31 | #define __ARCH_WANT_RENAMEAT | ||
| 32 | #include "$input" | 32 | #include "$input" |
| 33 | int main(int argc, char *argv[]) | 33 | int main(int argc, char *argv[]) |
| 34 | { | 34 | { |
| @@ -42,7 +42,7 @@ create_table_from_c() | |||
| 42 | printf "%s\n" " printf(\"#define SYSCALLTBL_ARM64_MAX_ID %d\\n\", __NR_$last_sc);" | 42 | printf "%s\n" " printf(\"#define SYSCALLTBL_ARM64_MAX_ID %d\\n\", __NR_$last_sc);" |
| 43 | printf "}\n" | 43 | printf "}\n" |
| 44 | 44 | ||
| 45 | } | $hostcc -o $create_table_exe -x c - | 45 | } | $hostcc -I $incpath/include/uapi -o $create_table_exe -x c - |
| 46 | 46 | ||
| 47 | $create_table_exe | 47 | $create_table_exe |
| 48 | 48 | ||
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index 20e7d74d86cd..10a44e946f77 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c | |||
| @@ -22,15 +22,16 @@ bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) | |||
| 22 | 22 | ||
| 23 | #endif | 23 | #endif |
| 24 | 24 | ||
| 25 | #if !defined(_CALL_ELF) || _CALL_ELF != 2 | ||
| 26 | int arch__choose_best_symbol(struct symbol *syma, | 25 | int arch__choose_best_symbol(struct symbol *syma, |
| 27 | struct symbol *symb __maybe_unused) | 26 | struct symbol *symb __maybe_unused) |
| 28 | { | 27 | { |
| 29 | char *sym = syma->name; | 28 | char *sym = syma->name; |
| 30 | 29 | ||
| 30 | #if !defined(_CALL_ELF) || _CALL_ELF != 2 | ||
| 31 | /* Skip over any initial dot */ | 31 | /* Skip over any initial dot */ |
| 32 | if (*sym == '.') | 32 | if (*sym == '.') |
| 33 | sym++; | 33 | sym++; |
| 34 | #endif | ||
| 34 | 35 | ||
| 35 | /* Avoid "SyS" kernel syscall aliases */ | 36 | /* Avoid "SyS" kernel syscall aliases */ |
| 36 | if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3)) | 37 | if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3)) |
| @@ -41,6 +42,7 @@ int arch__choose_best_symbol(struct symbol *syma, | |||
| 41 | return SYMBOL_A; | 42 | return SYMBOL_A; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 45 | #if !defined(_CALL_ELF) || _CALL_ELF != 2 | ||
| 44 | /* Allow matching against dot variants */ | 46 | /* Allow matching against dot variants */ |
| 45 | int arch__compare_symbol_names(const char *namea, const char *nameb) | 47 | int arch__compare_symbol_names(const char *namea, const char *nameb) |
| 46 | { | 48 | { |
diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index cee4e2f7c057..de0dd66dbb48 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c | |||
| @@ -100,8 +100,6 @@ out_free_source: | |||
| 100 | return -1; | 100 | return -1; |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | ||
| 104 | struct ins_operands *ops); | ||
| 105 | 103 | ||
| 106 | static struct ins_ops s390_mov_ops = { | 104 | static struct ins_ops s390_mov_ops = { |
| 107 | .parse = s390_mov__parse, | 105 | .parse = s390_mov__parse, |
diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index c1bd979b957b..613709cfbbd0 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h | |||
| @@ -9,6 +9,7 @@ struct test; | |||
| 9 | int test__rdpmc(struct test *test __maybe_unused, int subtest); | 9 | int test__rdpmc(struct test *test __maybe_unused, int subtest); |
| 10 | int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest); | 10 | int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest); |
| 11 | int test__insn_x86(struct test *test __maybe_unused, int subtest); | 11 | int test__insn_x86(struct test *test __maybe_unused, int subtest); |
| 12 | int test__bp_modify(struct test *test, int subtest); | ||
| 12 | 13 | ||
| 13 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 14 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
| 14 | struct thread; | 15 | struct thread; |
diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 8e2c5a38c3b9..586849ff83a0 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build | |||
| @@ -5,3 +5,4 @@ libperf-y += arch-tests.o | |||
| 5 | libperf-y += rdpmc.o | 5 | libperf-y += rdpmc.o |
| 6 | libperf-y += perf-time-to-tsc.o | 6 | libperf-y += perf-time-to-tsc.o |
| 7 | libperf-$(CONFIG_AUXTRACE) += insn-x86.o | 7 | libperf-$(CONFIG_AUXTRACE) += insn-x86.o |
| 8 | libperf-$(CONFIG_X86_64) += bp-modify.o | ||
diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index cc1802ff5410..d47d3f8e3c8e 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c | |||
| @@ -24,6 +24,12 @@ struct test arch_tests[] = { | |||
| 24 | .func = test__insn_x86, | 24 | .func = test__insn_x86, |
| 25 | }, | 25 | }, |
| 26 | #endif | 26 | #endif |
| 27 | #if defined(__x86_64__) | ||
| 28 | { | ||
| 29 | .desc = "x86 bp modify", | ||
| 30 | .func = test__bp_modify, | ||
| 31 | }, | ||
| 32 | #endif | ||
| 27 | { | 33 | { |
| 28 | .func = NULL, | 34 | .func = NULL, |
| 29 | }, | 35 | }, |
diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c new file mode 100644 index 000000000000..f53e4406709f --- /dev/null +++ b/tools/perf/arch/x86/tests/bp-modify.c | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/compiler.h> | ||
| 3 | #include <sys/types.h> | ||
| 4 | #include <sys/wait.h> | ||
| 5 | #include <sys/user.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include <unistd.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <sys/ptrace.h> | ||
| 11 | #include <asm/ptrace.h> | ||
| 12 | #include <errno.h> | ||
| 13 | #include "debug.h" | ||
| 14 | #include "tests/tests.h" | ||
| 15 | #include "arch-tests.h" | ||
| 16 | |||
| 17 | static noinline int bp_1(void) | ||
| 18 | { | ||
| 19 | pr_debug("in %s\n", __func__); | ||
| 20 | return 0; | ||
| 21 | } | ||
| 22 | |||
| 23 | static noinline int bp_2(void) | ||
| 24 | { | ||
| 25 | pr_debug("in %s\n", __func__); | ||
| 26 | return 0; | ||
| 27 | } | ||
| 28 | |||
| 29 | static int spawn_child(void) | ||
| 30 | { | ||
| 31 | int child = fork(); | ||
| 32 | |||
| 33 | if (child == 0) { | ||
| 34 | /* | ||
| 35 | * The child sets itself for as tracee and | ||
| 36 | * waits in signal for parent to trace it, | ||
| 37 | * then it calls bp_1 and quits. | ||
| 38 | */ | ||
| 39 | int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL); | ||
| 40 | |||
| 41 | if (err) { | ||
| 42 | pr_debug("failed to PTRACE_TRACEME\n"); | ||
| 43 | exit(1); | ||
| 44 | } | ||
| 45 | |||
| 46 | raise(SIGCONT); | ||
| 47 | bp_1(); | ||
| 48 | exit(0); | ||
| 49 | } | ||
| 50 | |||
| 51 | return child; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* | ||
| 55 | * This tests creates HW breakpoint, tries to | ||
| 56 | * change it and checks it was properly changed. | ||
| 57 | */ | ||
| 58 | static int bp_modify1(void) | ||
| 59 | { | ||
| 60 | pid_t child; | ||
| 61 | int status; | ||
| 62 | unsigned long rip = 0, dr7 = 1; | ||
| 63 | |||
| 64 | child = spawn_child(); | ||
| 65 | |||
| 66 | waitpid(child, &status, 0); | ||
| 67 | if (WIFEXITED(status)) { | ||
| 68 | pr_debug("tracee exited prematurely 1\n"); | ||
| 69 | return TEST_FAIL; | ||
| 70 | } | ||
| 71 | |||
| 72 | /* | ||
| 73 | * The parent does following steps: | ||
| 74 | * - creates a new breakpoint (id 0) for bp_2 function | ||
| 75 | * - changes that breakponit to bp_1 function | ||
| 76 | * - waits for the breakpoint to hit and checks | ||
| 77 | * it has proper rip of bp_1 function | ||
| 78 | * - detaches the child | ||
| 79 | */ | ||
| 80 | if (ptrace(PTRACE_POKEUSER, child, | ||
| 81 | offsetof(struct user, u_debugreg[0]), bp_2)) { | ||
| 82 | pr_debug("failed to set breakpoint, 1st time: %s\n", | ||
| 83 | strerror(errno)); | ||
| 84 | goto out; | ||
| 85 | } | ||
| 86 | |||
| 87 | if (ptrace(PTRACE_POKEUSER, child, | ||
| 88 | offsetof(struct user, u_debugreg[0]), bp_1)) { | ||
| 89 | pr_debug("failed to set breakpoint, 2nd time: %s\n", | ||
| 90 | strerror(errno)); | ||
| 91 | goto out; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (ptrace(PTRACE_POKEUSER, child, | ||
| 95 | offsetof(struct user, u_debugreg[7]), dr7)) { | ||
| 96 | pr_debug("failed to set dr7: %s\n", strerror(errno)); | ||
| 97 | goto out; | ||
| 98 | } | ||
| 99 | |||
| 100 | if (ptrace(PTRACE_CONT, child, NULL, NULL)) { | ||
| 101 | pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); | ||
| 102 | goto out; | ||
| 103 | } | ||
| 104 | |||
| 105 | waitpid(child, &status, 0); | ||
| 106 | if (WIFEXITED(status)) { | ||
| 107 | pr_debug("tracee exited prematurely 2\n"); | ||
| 108 | return TEST_FAIL; | ||
| 109 | } | ||
| 110 | |||
| 111 | rip = ptrace(PTRACE_PEEKUSER, child, | ||
| 112 | offsetof(struct user_regs_struct, rip), NULL); | ||
| 113 | if (rip == (unsigned long) -1) { | ||
| 114 | pr_debug("failed to PTRACE_PEEKUSER: %s\n", | ||
| 115 | strerror(errno)); | ||
| 116 | goto out; | ||
| 117 | } | ||
| 118 | |||
| 119 | pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); | ||
| 120 | |||
| 121 | out: | ||
| 122 | if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { | ||
| 123 | pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); | ||
| 124 | return TEST_FAIL; | ||
| 125 | } | ||
| 126 | |||
| 127 | return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* | ||
| 131 | * This tests creates HW breakpoint, tries to | ||
| 132 | * change it to bogus value and checks the original | ||
| 133 | * breakpoint is hit. | ||
| 134 | */ | ||
| 135 | static int bp_modify2(void) | ||
| 136 | { | ||
| 137 | pid_t child; | ||
| 138 | int status; | ||
| 139 | unsigned long rip = 0, dr7 = 1; | ||
| 140 | |||
| 141 | child = spawn_child(); | ||
| 142 | |||
| 143 | waitpid(child, &status, 0); | ||
| 144 | if (WIFEXITED(status)) { | ||
| 145 | pr_debug("tracee exited prematurely 1\n"); | ||
| 146 | return TEST_FAIL; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | * The parent does following steps: | ||
| 151 | * - creates a new breakpoint (id 0) for bp_1 function | ||
| 152 | * - tries to change that breakpoint to (-1) address | ||
| 153 | * - waits for the breakpoint to hit and checks | ||
| 154 | * it has proper rip of bp_1 function | ||
| 155 | * - detaches the child | ||
| 156 | */ | ||
| 157 | if (ptrace(PTRACE_POKEUSER, child, | ||
| 158 | offsetof(struct user, u_debugreg[0]), bp_1)) { | ||
| 159 | pr_debug("failed to set breakpoint: %s\n", | ||
| 160 | strerror(errno)); | ||
| 161 | goto out; | ||
| 162 | } | ||
| 163 | |||
| 164 | if (ptrace(PTRACE_POKEUSER, child, | ||
| 165 | offsetof(struct user, u_debugreg[7]), dr7)) { | ||
| 166 | pr_debug("failed to set dr7: %s\n", strerror(errno)); | ||
| 167 | goto out; | ||
| 168 | } | ||
| 169 | |||
| 170 | if (!ptrace(PTRACE_POKEUSER, child, | ||
| 171 | offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) { | ||
| 172 | pr_debug("failed, breakpoint set to bogus address\n"); | ||
| 173 | goto out; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (ptrace(PTRACE_CONT, child, NULL, NULL)) { | ||
| 177 | pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); | ||
| 178 | goto out; | ||
| 179 | } | ||
| 180 | |||
| 181 | waitpid(child, &status, 0); | ||
| 182 | if (WIFEXITED(status)) { | ||
| 183 | pr_debug("tracee exited prematurely 2\n"); | ||
| 184 | return TEST_FAIL; | ||
| 185 | } | ||
| 186 | |||
| 187 | rip = ptrace(PTRACE_PEEKUSER, child, | ||
| 188 | offsetof(struct user_regs_struct, rip), NULL); | ||
| 189 | if (rip == (unsigned long) -1) { | ||
| 190 | pr_debug("failed to PTRACE_PEEKUSER: %s\n", | ||
| 191 | strerror(errno)); | ||
| 192 | goto out; | ||
| 193 | } | ||
| 194 | |||
| 195 | pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); | ||
| 196 | |||
| 197 | out: | ||
| 198 | if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { | ||
| 199 | pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); | ||
| 200 | return TEST_FAIL; | ||
| 201 | } | ||
| 202 | |||
| 203 | return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; | ||
| 204 | } | ||
| 205 | |||
| 206 | int test__bp_modify(struct test *test __maybe_unused, | ||
| 207 | int subtest __maybe_unused) | ||
| 208 | { | ||
| 209 | TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); | ||
| 210 | TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2()); | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 22ebeb92ac51..9853552bcf16 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -758,7 +758,7 @@ static int record__synthesize(struct record *rec, bool tail) | |||
| 758 | * We need to synthesize events first, because some | 758 | * We need to synthesize events first, because some |
| 759 | * features works on top of them (on report side). | 759 | * features works on top of them (on report side). |
| 760 | */ | 760 | */ |
| 761 | err = perf_event__synthesize_attrs(tool, session, | 761 | err = perf_event__synthesize_attrs(tool, rec->evlist, |
| 762 | process_synthesized_event); | 762 | process_synthesized_event); |
| 763 | if (err < 0) { | 763 | if (err < 0) { |
| 764 | pr_err("Couldn't synthesize attrs.\n"); | 764 | pr_err("Couldn't synthesize attrs.\n"); |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ba481d73f910..6176bae177c2 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -1544,7 +1544,8 @@ struct metric_ctx { | |||
| 1544 | FILE *fp; | 1544 | FILE *fp; |
| 1545 | }; | 1545 | }; |
| 1546 | 1546 | ||
| 1547 | static void script_print_metric(void *ctx, const char *color, | 1547 | static void script_print_metric(struct perf_stat_config *config __maybe_unused, |
| 1548 | void *ctx, const char *color, | ||
| 1548 | const char *fmt, | 1549 | const char *fmt, |
| 1549 | const char *unit, double val) | 1550 | const char *unit, double val) |
| 1550 | { | 1551 | { |
| @@ -1562,7 +1563,8 @@ static void script_print_metric(void *ctx, const char *color, | |||
| 1562 | fprintf(mctx->fp, " %s\n", unit); | 1563 | fprintf(mctx->fp, " %s\n", unit); |
| 1563 | } | 1564 | } |
| 1564 | 1565 | ||
| 1565 | static void script_new_line(void *ctx) | 1566 | static void script_new_line(struct perf_stat_config *config __maybe_unused, |
| 1567 | void *ctx) | ||
| 1566 | { | 1568 | { |
| 1567 | struct metric_ctx *mctx = ctx; | 1569 | struct metric_ctx *mctx = ctx; |
| 1568 | 1570 | ||
| @@ -1608,7 +1610,7 @@ static void perf_sample__fprint_metric(struct perf_script *script, | |||
| 1608 | evsel_script(evsel)->val = val; | 1610 | evsel_script(evsel)->val = val; |
| 1609 | if (evsel_script(evsel->leader)->gnum == evsel->leader->nr_members) { | 1611 | if (evsel_script(evsel->leader)->gnum == evsel->leader->nr_members) { |
| 1610 | for_each_group_member (ev2, evsel->leader) { | 1612 | for_each_group_member (ev2, evsel->leader) { |
| 1611 | perf_stat__print_shadow_stats(ev2, | 1613 | perf_stat__print_shadow_stats(&stat_config, ev2, |
| 1612 | evsel_script(ev2)->val, | 1614 | evsel_script(ev2)->val, |
| 1613 | sample->cpu, | 1615 | sample->cpu, |
| 1614 | &ctx, | 1616 | &ctx, |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d097b5b47eb8..0b0e3961d511 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -88,8 +88,6 @@ | |||
| 88 | #include "sane_ctype.h" | 88 | #include "sane_ctype.h" |
| 89 | 89 | ||
| 90 | #define DEFAULT_SEPARATOR " " | 90 | #define DEFAULT_SEPARATOR " " |
| 91 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
| 92 | #define CNTR_NOT_COUNTED "<not counted>" | ||
| 93 | #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi" | 91 | #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi" |
| 94 | 92 | ||
| 95 | static void print_counters(struct timespec *ts, int argc, const char **argv); | 93 | static void print_counters(struct timespec *ts, int argc, const char **argv); |
| @@ -137,54 +135,30 @@ static const char *smi_cost_attrs = { | |||
| 137 | 135 | ||
| 138 | static struct perf_evlist *evsel_list; | 136 | static struct perf_evlist *evsel_list; |
| 139 | 137 | ||
| 140 | static struct rblist metric_events; | ||
| 141 | |||
| 142 | static struct target target = { | 138 | static struct target target = { |
| 143 | .uid = UINT_MAX, | 139 | .uid = UINT_MAX, |
| 144 | }; | 140 | }; |
| 145 | 141 | ||
| 146 | typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); | ||
| 147 | |||
| 148 | #define METRIC_ONLY_LEN 20 | 142 | #define METRIC_ONLY_LEN 20 |
| 149 | 143 | ||
| 150 | static int run_count = 1; | ||
| 151 | static bool no_inherit = false; | ||
| 152 | static volatile pid_t child_pid = -1; | 144 | static volatile pid_t child_pid = -1; |
| 153 | static bool null_run = false; | ||
| 154 | static int detailed_run = 0; | 145 | static int detailed_run = 0; |
| 155 | static bool transaction_run; | 146 | static bool transaction_run; |
| 156 | static bool topdown_run = false; | 147 | static bool topdown_run = false; |
| 157 | static bool smi_cost = false; | 148 | static bool smi_cost = false; |
| 158 | static bool smi_reset = false; | 149 | static bool smi_reset = false; |
| 159 | static bool big_num = true; | ||
| 160 | static int big_num_opt = -1; | 150 | static int big_num_opt = -1; |
| 161 | static const char *csv_sep = NULL; | ||
| 162 | static bool csv_output = false; | ||
| 163 | static bool group = false; | 151 | static bool group = false; |
| 164 | static const char *pre_cmd = NULL; | 152 | static const char *pre_cmd = NULL; |
| 165 | static const char *post_cmd = NULL; | 153 | static const char *post_cmd = NULL; |
| 166 | static bool sync_run = false; | 154 | static bool sync_run = false; |
| 167 | static unsigned int initial_delay = 0; | ||
| 168 | static unsigned int unit_width = 4; /* strlen("unit") */ | ||
| 169 | static bool forever = false; | 155 | static bool forever = false; |
| 170 | static bool metric_only = false; | ||
| 171 | static bool force_metric_only = false; | 156 | static bool force_metric_only = false; |
| 172 | static bool no_merge = false; | ||
| 173 | static bool walltime_run_table = false; | ||
| 174 | static struct timespec ref_time; | 157 | static struct timespec ref_time; |
| 175 | static struct cpu_map *aggr_map; | ||
| 176 | static aggr_get_id_t aggr_get_id; | ||
| 177 | static bool append_file; | 158 | static bool append_file; |
| 178 | static bool interval_count; | 159 | static bool interval_count; |
| 179 | static bool interval_clear; | ||
| 180 | static const char *output_name; | 160 | static const char *output_name; |
| 181 | static int output_fd; | 161 | static int output_fd; |
| 182 | static int print_free_counters_hint; | ||
| 183 | static int print_mixed_hw_group_error; | ||
| 184 | static u64 *walltime_run; | ||
| 185 | static bool ru_display = false; | ||
| 186 | static struct rusage ru_data; | ||
| 187 | static unsigned int metric_only_len = METRIC_ONLY_LEN; | ||
| 188 | 162 | ||
| 189 | struct perf_stat { | 163 | struct perf_stat { |
| 190 | bool record; | 164 | bool record; |
| @@ -204,15 +178,15 @@ static struct perf_stat perf_stat; | |||
| 204 | static volatile int done = 0; | 178 | static volatile int done = 0; |
| 205 | 179 | ||
| 206 | static struct perf_stat_config stat_config = { | 180 | static struct perf_stat_config stat_config = { |
| 207 | .aggr_mode = AGGR_GLOBAL, | 181 | .aggr_mode = AGGR_GLOBAL, |
| 208 | .scale = true, | 182 | .scale = true, |
| 183 | .unit_width = 4, /* strlen("unit") */ | ||
| 184 | .run_count = 1, | ||
| 185 | .metric_only_len = METRIC_ONLY_LEN, | ||
| 186 | .walltime_nsecs_stats = &walltime_nsecs_stats, | ||
| 187 | .big_num = true, | ||
| 209 | }; | 188 | }; |
| 210 | 189 | ||
| 211 | static bool is_duration_time(struct perf_evsel *evsel) | ||
| 212 | { | ||
| 213 | return !strcmp(evsel->name, "duration_time"); | ||
| 214 | } | ||
| 215 | |||
| 216 | static inline void diff_timespec(struct timespec *r, struct timespec *a, | 190 | static inline void diff_timespec(struct timespec *r, struct timespec *a, |
| 217 | struct timespec *b) | 191 | struct timespec *b) |
| 218 | { | 192 | { |
| @@ -236,66 +210,6 @@ static void perf_stat__reset_stats(void) | |||
| 236 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); | 210 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); |
| 237 | } | 211 | } |
| 238 | 212 | ||
| 239 | static int create_perf_stat_counter(struct perf_evsel *evsel) | ||
| 240 | { | ||
| 241 | struct perf_event_attr *attr = &evsel->attr; | ||
| 242 | struct perf_evsel *leader = evsel->leader; | ||
| 243 | |||
| 244 | if (stat_config.scale) { | ||
| 245 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
| 246 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
| 247 | } | ||
| 248 | |||
| 249 | /* | ||
| 250 | * The event is part of non trivial group, let's enable | ||
| 251 | * the group read (for leader) and ID retrieval for all | ||
| 252 | * members. | ||
| 253 | */ | ||
| 254 | if (leader->nr_members > 1) | ||
| 255 | attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP; | ||
| 256 | |||
| 257 | attr->inherit = !no_inherit; | ||
| 258 | |||
| 259 | /* | ||
| 260 | * Some events get initialized with sample_(period/type) set, | ||
| 261 | * like tracepoints. Clear it up for counting. | ||
| 262 | */ | ||
| 263 | attr->sample_period = 0; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless | ||
| 267 | * while avoiding that older tools show confusing messages. | ||
| 268 | * | ||
| 269 | * However for pipe sessions we need to keep it zero, | ||
| 270 | * because script's perf_evsel__check_attr is triggered | ||
| 271 | * by attr->sample_type != 0, and we can't run it on | ||
| 272 | * stat sessions. | ||
| 273 | */ | ||
| 274 | if (!(STAT_RECORD && perf_stat.data.is_pipe)) | ||
| 275 | attr->sample_type = PERF_SAMPLE_IDENTIFIER; | ||
| 276 | |||
| 277 | /* | ||
| 278 | * Disabling all counters initially, they will be enabled | ||
| 279 | * either manually by us or by kernel via enable_on_exec | ||
| 280 | * set later. | ||
| 281 | */ | ||
| 282 | if (perf_evsel__is_group_leader(evsel)) { | ||
| 283 | attr->disabled = 1; | ||
| 284 | |||
| 285 | /* | ||
| 286 | * In case of initial_delay we enable tracee | ||
| 287 | * events manually. | ||
| 288 | */ | ||
| 289 | if (target__none(&target) && !initial_delay) | ||
| 290 | attr->enable_on_exec = 1; | ||
| 291 | } | ||
| 292 | |||
| 293 | if (target__has_cpu(&target) && !target__has_per_thread(&target)) | ||
| 294 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
| 295 | |||
| 296 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); | ||
| 297 | } | ||
| 298 | |||
| 299 | static int process_synthesized_event(struct perf_tool *tool __maybe_unused, | 213 | static int process_synthesized_event(struct perf_tool *tool __maybe_unused, |
| 300 | union perf_event *event, | 214 | union perf_event *event, |
| 301 | struct perf_sample *sample __maybe_unused, | 215 | struct perf_sample *sample __maybe_unused, |
| @@ -428,15 +342,15 @@ static void process_interval(void) | |||
| 428 | 342 | ||
| 429 | static void enable_counters(void) | 343 | static void enable_counters(void) |
| 430 | { | 344 | { |
| 431 | if (initial_delay) | 345 | if (stat_config.initial_delay) |
| 432 | usleep(initial_delay * USEC_PER_MSEC); | 346 | usleep(stat_config.initial_delay * USEC_PER_MSEC); |
| 433 | 347 | ||
| 434 | /* | 348 | /* |
| 435 | * We need to enable counters only if: | 349 | * We need to enable counters only if: |
| 436 | * - we don't have tracee (attaching to task or cpu) | 350 | * - we don't have tracee (attaching to task or cpu) |
| 437 | * - we have initial delay configured | 351 | * - we have initial delay configured |
| 438 | */ | 352 | */ |
| 439 | if (!target__none(&target) || initial_delay) | 353 | if (!target__none(&target) || stat_config.initial_delay) |
| 440 | perf_evlist__enable(evsel_list); | 354 | perf_evlist__enable(evsel_list); |
| 441 | } | 355 | } |
| 442 | 356 | ||
| @@ -464,80 +378,6 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf | |||
| 464 | workload_exec_errno = info->si_value.sival_int; | 378 | workload_exec_errno = info->si_value.sival_int; |
| 465 | } | 379 | } |
| 466 | 380 | ||
| 467 | static int perf_stat_synthesize_config(bool is_pipe) | ||
| 468 | { | ||
| 469 | int err; | ||
| 470 | |||
| 471 | if (is_pipe) { | ||
| 472 | err = perf_event__synthesize_attrs(NULL, perf_stat.session, | ||
| 473 | process_synthesized_event); | ||
| 474 | if (err < 0) { | ||
| 475 | pr_err("Couldn't synthesize attrs.\n"); | ||
| 476 | return err; | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | err = perf_event__synthesize_extra_attr(NULL, | ||
| 481 | evsel_list, | ||
| 482 | process_synthesized_event, | ||
| 483 | is_pipe); | ||
| 484 | |||
| 485 | err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, | ||
| 486 | process_synthesized_event, | ||
| 487 | NULL); | ||
| 488 | if (err < 0) { | ||
| 489 | pr_err("Couldn't synthesize thread map.\n"); | ||
| 490 | return err; | ||
| 491 | } | ||
| 492 | |||
| 493 | err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, | ||
| 494 | process_synthesized_event, NULL); | ||
| 495 | if (err < 0) { | ||
| 496 | pr_err("Couldn't synthesize thread map.\n"); | ||
| 497 | return err; | ||
| 498 | } | ||
| 499 | |||
| 500 | err = perf_event__synthesize_stat_config(NULL, &stat_config, | ||
| 501 | process_synthesized_event, NULL); | ||
| 502 | if (err < 0) { | ||
| 503 | pr_err("Couldn't synthesize config.\n"); | ||
| 504 | return err; | ||
| 505 | } | ||
| 506 | |||
| 507 | return 0; | ||
| 508 | } | ||
| 509 | |||
| 510 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
| 511 | |||
| 512 | static int __store_counter_ids(struct perf_evsel *counter) | ||
| 513 | { | ||
| 514 | int cpu, thread; | ||
| 515 | |||
| 516 | for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) { | ||
| 517 | for (thread = 0; thread < xyarray__max_y(counter->fd); | ||
| 518 | thread++) { | ||
| 519 | int fd = FD(counter, cpu, thread); | ||
| 520 | |||
| 521 | if (perf_evlist__id_add_fd(evsel_list, counter, | ||
| 522 | cpu, thread, fd) < 0) | ||
| 523 | return -1; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | return 0; | ||
| 528 | } | ||
| 529 | |||
| 530 | static int store_counter_ids(struct perf_evsel *counter) | ||
| 531 | { | ||
| 532 | struct cpu_map *cpus = counter->cpus; | ||
| 533 | struct thread_map *threads = counter->threads; | ||
| 534 | |||
| 535 | if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) | ||
| 536 | return -ENOMEM; | ||
| 537 | |||
| 538 | return __store_counter_ids(counter); | ||
| 539 | } | ||
| 540 | |||
| 541 | static bool perf_evsel__should_store_id(struct perf_evsel *counter) | 381 | static bool perf_evsel__should_store_id(struct perf_evsel *counter) |
| 542 | { | 382 | { |
| 543 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; | 383 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; |
| @@ -609,7 +449,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) | |||
| 609 | 449 | ||
| 610 | evlist__for_each_entry(evsel_list, counter) { | 450 | evlist__for_each_entry(evsel_list, counter) { |
| 611 | try_again: | 451 | try_again: |
| 612 | if (create_perf_stat_counter(counter) < 0) { | 452 | if (create_perf_stat_counter(counter, &stat_config, &target) < 0) { |
| 613 | 453 | ||
| 614 | /* Weak group failed. Reset the group. */ | 454 | /* Weak group failed. Reset the group. */ |
| 615 | if ((errno == EINVAL || errno == EBADF) && | 455 | if ((errno == EINVAL || errno == EBADF) && |
| @@ -664,11 +504,11 @@ try_again: | |||
| 664 | counter->supported = true; | 504 | counter->supported = true; |
| 665 | 505 | ||
| 666 | l = strlen(counter->unit); | 506 | l = strlen(counter->unit); |
| 667 | if (l > unit_width) | 507 | if (l > stat_config.unit_width) |
| 668 | unit_width = l; | 508 | stat_config.unit_width = l; |
| 669 | 509 | ||
| 670 | if (perf_evsel__should_store_id(counter) && | 510 | if (perf_evsel__should_store_id(counter) && |
| 671 | store_counter_ids(counter)) | 511 | perf_evsel__store_ids(counter, evsel_list)) |
| 672 | return -1; | 512 | return -1; |
| 673 | } | 513 | } |
| 674 | 514 | ||
| @@ -699,7 +539,8 @@ try_again: | |||
| 699 | if (err < 0) | 539 | if (err < 0) |
| 700 | return err; | 540 | return err; |
| 701 | 541 | ||
| 702 | err = perf_stat_synthesize_config(is_pipe); | 542 | err = perf_stat_synthesize_config(&stat_config, NULL, evsel_list, |
| 543 | process_synthesized_event, is_pipe); | ||
| 703 | if (err < 0) | 544 | if (err < 0) |
| 704 | return err; | 545 | return err; |
| 705 | } | 546 | } |
| @@ -724,7 +565,7 @@ try_again: | |||
| 724 | break; | 565 | break; |
| 725 | } | 566 | } |
| 726 | } | 567 | } |
| 727 | wait4(child_pid, &status, 0, &ru_data); | 568 | wait4(child_pid, &status, 0, &stat_config.ru_data); |
| 728 | 569 | ||
| 729 | if (workload_exec_errno) { | 570 | if (workload_exec_errno) { |
| 730 | const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); | 571 | const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); |
| @@ -752,8 +593,8 @@ try_again: | |||
| 752 | 593 | ||
| 753 | t1 = rdclock(); | 594 | t1 = rdclock(); |
| 754 | 595 | ||
| 755 | if (walltime_run_table) | 596 | if (stat_config.walltime_run_table) |
| 756 | walltime_run[run_idx] = t1 - t0; | 597 | stat_config.walltime_run[run_idx] = t1 - t0; |
| 757 | 598 | ||
| 758 | update_stats(&walltime_nsecs_stats, t1 - t0); | 599 | update_stats(&walltime_nsecs_stats, t1 - t0); |
| 759 | 600 | ||
| @@ -795,1105 +636,14 @@ static int run_perf_stat(int argc, const char **argv, int run_idx) | |||
| 795 | return ret; | 636 | return ret; |
| 796 | } | 637 | } |
| 797 | 638 | ||
| 798 | static void print_running(u64 run, u64 ena) | ||
| 799 | { | ||
| 800 | if (csv_output) { | ||
| 801 | fprintf(stat_config.output, "%s%" PRIu64 "%s%.2f", | ||
| 802 | csv_sep, | ||
| 803 | run, | ||
| 804 | csv_sep, | ||
| 805 | ena ? 100.0 * run / ena : 100.0); | ||
| 806 | } else if (run != ena) { | ||
| 807 | fprintf(stat_config.output, " (%.2f%%)", 100.0 * run / ena); | ||
| 808 | } | ||
| 809 | } | ||
| 810 | |||
| 811 | static void print_noise_pct(double total, double avg) | ||
| 812 | { | ||
| 813 | double pct = rel_stddev_stats(total, avg); | ||
| 814 | |||
| 815 | if (csv_output) | ||
| 816 | fprintf(stat_config.output, "%s%.2f%%", csv_sep, pct); | ||
| 817 | else if (pct) | ||
| 818 | fprintf(stat_config.output, " ( +-%6.2f%% )", pct); | ||
| 819 | } | ||
| 820 | |||
| 821 | static void print_noise(struct perf_evsel *evsel, double avg) | ||
| 822 | { | ||
| 823 | struct perf_stat_evsel *ps; | ||
| 824 | |||
| 825 | if (run_count == 1) | ||
| 826 | return; | ||
| 827 | |||
| 828 | ps = evsel->stats; | ||
| 829 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | ||
| 830 | } | ||
| 831 | |||
| 832 | static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | ||
| 833 | { | ||
| 834 | switch (stat_config.aggr_mode) { | ||
| 835 | case AGGR_CORE: | ||
| 836 | fprintf(stat_config.output, "S%d-C%*d%s%*d%s", | ||
| 837 | cpu_map__id_to_socket(id), | ||
| 838 | csv_output ? 0 : -8, | ||
| 839 | cpu_map__id_to_cpu(id), | ||
| 840 | csv_sep, | ||
| 841 | csv_output ? 0 : 4, | ||
| 842 | nr, | ||
| 843 | csv_sep); | ||
| 844 | break; | ||
| 845 | case AGGR_SOCKET: | ||
| 846 | fprintf(stat_config.output, "S%*d%s%*d%s", | ||
| 847 | csv_output ? 0 : -5, | ||
| 848 | id, | ||
| 849 | csv_sep, | ||
| 850 | csv_output ? 0 : 4, | ||
| 851 | nr, | ||
| 852 | csv_sep); | ||
| 853 | break; | ||
| 854 | case AGGR_NONE: | ||
| 855 | fprintf(stat_config.output, "CPU%*d%s", | ||
| 856 | csv_output ? 0 : -4, | ||
| 857 | perf_evsel__cpus(evsel)->map[id], csv_sep); | ||
| 858 | break; | ||
| 859 | case AGGR_THREAD: | ||
| 860 | fprintf(stat_config.output, "%*s-%*d%s", | ||
| 861 | csv_output ? 0 : 16, | ||
| 862 | thread_map__comm(evsel->threads, id), | ||
| 863 | csv_output ? 0 : -8, | ||
| 864 | thread_map__pid(evsel->threads, id), | ||
| 865 | csv_sep); | ||
| 866 | break; | ||
| 867 | case AGGR_GLOBAL: | ||
| 868 | case AGGR_UNSET: | ||
| 869 | default: | ||
| 870 | break; | ||
| 871 | } | ||
| 872 | } | ||
| 873 | |||
| 874 | struct outstate { | ||
| 875 | FILE *fh; | ||
| 876 | bool newline; | ||
| 877 | const char *prefix; | ||
| 878 | int nfields; | ||
| 879 | int id, nr; | ||
| 880 | struct perf_evsel *evsel; | ||
| 881 | }; | ||
| 882 | |||
| 883 | #define METRIC_LEN 35 | ||
| 884 | |||
| 885 | static void new_line_std(void *ctx) | ||
| 886 | { | ||
| 887 | struct outstate *os = ctx; | ||
| 888 | |||
| 889 | os->newline = true; | ||
| 890 | } | ||
| 891 | |||
| 892 | static void do_new_line_std(struct outstate *os) | ||
| 893 | { | ||
| 894 | fputc('\n', os->fh); | ||
| 895 | fputs(os->prefix, os->fh); | ||
| 896 | aggr_printout(os->evsel, os->id, os->nr); | ||
| 897 | if (stat_config.aggr_mode == AGGR_NONE) | ||
| 898 | fprintf(os->fh, " "); | ||
| 899 | fprintf(os->fh, " "); | ||
| 900 | } | ||
| 901 | |||
| 902 | static void print_metric_std(void *ctx, const char *color, const char *fmt, | ||
| 903 | const char *unit, double val) | ||
| 904 | { | ||
| 905 | struct outstate *os = ctx; | ||
| 906 | FILE *out = os->fh; | ||
| 907 | int n; | ||
| 908 | bool newline = os->newline; | ||
| 909 | |||
| 910 | os->newline = false; | ||
| 911 | |||
| 912 | if (unit == NULL || fmt == NULL) { | ||
| 913 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
| 914 | return; | ||
| 915 | } | ||
| 916 | |||
| 917 | if (newline) | ||
| 918 | do_new_line_std(os); | ||
| 919 | |||
| 920 | n = fprintf(out, " # "); | ||
| 921 | if (color) | ||
| 922 | n += color_fprintf(out, color, fmt, val); | ||
| 923 | else | ||
| 924 | n += fprintf(out, fmt, val); | ||
| 925 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
| 926 | } | ||
| 927 | |||
| 928 | static void new_line_csv(void *ctx) | ||
| 929 | { | ||
| 930 | struct outstate *os = ctx; | ||
| 931 | int i; | ||
| 932 | |||
| 933 | fputc('\n', os->fh); | ||
| 934 | if (os->prefix) | ||
| 935 | fprintf(os->fh, "%s%s", os->prefix, csv_sep); | ||
| 936 | aggr_printout(os->evsel, os->id, os->nr); | ||
| 937 | for (i = 0; i < os->nfields; i++) | ||
| 938 | fputs(csv_sep, os->fh); | ||
| 939 | } | ||
| 940 | |||
| 941 | static void print_metric_csv(void *ctx, | ||
| 942 | const char *color __maybe_unused, | ||
| 943 | const char *fmt, const char *unit, double val) | ||
| 944 | { | ||
| 945 | struct outstate *os = ctx; | ||
| 946 | FILE *out = os->fh; | ||
| 947 | char buf[64], *vals, *ends; | ||
| 948 | |||
| 949 | if (unit == NULL || fmt == NULL) { | ||
| 950 | fprintf(out, "%s%s", csv_sep, csv_sep); | ||
| 951 | return; | ||
| 952 | } | ||
| 953 | snprintf(buf, sizeof(buf), fmt, val); | ||
| 954 | ends = vals = ltrim(buf); | ||
| 955 | while (isdigit(*ends) || *ends == '.') | ||
| 956 | ends++; | ||
| 957 | *ends = 0; | ||
| 958 | while (isspace(*unit)) | ||
| 959 | unit++; | ||
| 960 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); | ||
| 961 | } | ||
| 962 | |||
| 963 | /* Filter out some columns that don't work well in metrics only mode */ | ||
| 964 | |||
| 965 | static bool valid_only_metric(const char *unit) | ||
| 966 | { | ||
| 967 | if (!unit) | ||
| 968 | return false; | ||
| 969 | if (strstr(unit, "/sec") || | ||
| 970 | strstr(unit, "hz") || | ||
| 971 | strstr(unit, "Hz") || | ||
| 972 | strstr(unit, "CPUs utilized")) | ||
| 973 | return false; | ||
| 974 | return true; | ||
| 975 | } | ||
| 976 | |||
| 977 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
| 978 | const char *unit) | ||
| 979 | { | ||
| 980 | if (!strncmp(unit, "of all", 6)) { | ||
| 981 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
| 982 | unit); | ||
| 983 | return buf; | ||
| 984 | } | ||
| 985 | return unit; | ||
| 986 | } | ||
| 987 | |||
| 988 | static void print_metric_only(void *ctx, const char *color, const char *fmt, | ||
| 989 | const char *unit, double val) | ||
| 990 | { | ||
| 991 | struct outstate *os = ctx; | ||
| 992 | FILE *out = os->fh; | ||
| 993 | char buf[1024], str[1024]; | ||
| 994 | unsigned mlen = metric_only_len; | ||
| 995 | |||
| 996 | if (!valid_only_metric(unit)) | ||
| 997 | return; | ||
| 998 | unit = fixunit(buf, os->evsel, unit); | ||
| 999 | if (mlen < strlen(unit)) | ||
| 1000 | mlen = strlen(unit) + 1; | ||
| 1001 | |||
| 1002 | if (color) | ||
| 1003 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | ||
| 1004 | |||
| 1005 | color_snprintf(str, sizeof(str), color ?: "", fmt, val); | ||
| 1006 | fprintf(out, "%*s ", mlen, str); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, | ||
| 1010 | const char *fmt, | ||
| 1011 | const char *unit, double val) | ||
| 1012 | { | ||
| 1013 | struct outstate *os = ctx; | ||
| 1014 | FILE *out = os->fh; | ||
| 1015 | char buf[64], *vals, *ends; | ||
| 1016 | char tbuf[1024]; | ||
| 1017 | |||
| 1018 | if (!valid_only_metric(unit)) | ||
| 1019 | return; | ||
| 1020 | unit = fixunit(tbuf, os->evsel, unit); | ||
| 1021 | snprintf(buf, sizeof buf, fmt, val); | ||
| 1022 | ends = vals = ltrim(buf); | ||
| 1023 | while (isdigit(*ends) || *ends == '.') | ||
| 1024 | ends++; | ||
| 1025 | *ends = 0; | ||
| 1026 | fprintf(out, "%s%s", vals, csv_sep); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | static void new_line_metric(void *ctx __maybe_unused) | ||
| 1030 | { | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | static void print_metric_header(void *ctx, const char *color __maybe_unused, | ||
| 1034 | const char *fmt __maybe_unused, | ||
| 1035 | const char *unit, double val __maybe_unused) | ||
| 1036 | { | ||
| 1037 | struct outstate *os = ctx; | ||
| 1038 | char tbuf[1024]; | ||
| 1039 | |||
| 1040 | if (!valid_only_metric(unit)) | ||
| 1041 | return; | ||
| 1042 | unit = fixunit(tbuf, os->evsel, unit); | ||
| 1043 | if (csv_output) | ||
| 1044 | fprintf(os->fh, "%s%s", unit, csv_sep); | ||
| 1045 | else | ||
| 1046 | fprintf(os->fh, "%*s ", metric_only_len, unit); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | static int first_shadow_cpu(struct perf_evsel *evsel, int id) | ||
| 1050 | { | ||
| 1051 | int i; | ||
| 1052 | |||
| 1053 | if (!aggr_get_id) | ||
| 1054 | return 0; | ||
| 1055 | |||
| 1056 | if (stat_config.aggr_mode == AGGR_NONE) | ||
| 1057 | return id; | ||
| 1058 | |||
| 1059 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
| 1060 | return 0; | ||
| 1061 | |||
| 1062 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
| 1063 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
| 1064 | |||
| 1065 | if (aggr_get_id(evsel_list->cpus, cpu2) == id) | ||
| 1066 | return cpu2; | ||
| 1067 | } | ||
| 1068 | return 0; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | ||
| 1072 | { | ||
| 1073 | FILE *output = stat_config.output; | ||
| 1074 | double sc = evsel->scale; | ||
| 1075 | const char *fmt; | ||
| 1076 | |||
| 1077 | if (csv_output) { | ||
| 1078 | fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; | ||
| 1079 | } else { | ||
| 1080 | if (big_num) | ||
| 1081 | fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s"; | ||
| 1082 | else | ||
| 1083 | fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s"; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | aggr_printout(evsel, id, nr); | ||
| 1087 | |||
| 1088 | fprintf(output, fmt, avg, csv_sep); | ||
| 1089 | |||
| 1090 | if (evsel->unit) | ||
| 1091 | fprintf(output, "%-*s%s", | ||
| 1092 | csv_output ? 0 : unit_width, | ||
| 1093 | evsel->unit, csv_sep); | ||
| 1094 | |||
| 1095 | fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel)); | ||
| 1096 | |||
| 1097 | if (evsel->cgrp) | ||
| 1098 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | static bool is_mixed_hw_group(struct perf_evsel *counter) | ||
| 1102 | { | ||
| 1103 | struct perf_evlist *evlist = counter->evlist; | ||
| 1104 | u32 pmu_type = counter->attr.type; | ||
| 1105 | struct perf_evsel *pos; | ||
| 1106 | |||
| 1107 | if (counter->nr_members < 2) | ||
| 1108 | return false; | ||
| 1109 | |||
| 1110 | evlist__for_each_entry(evlist, pos) { | ||
| 1111 | /* software events can be part of any hardware group */ | ||
| 1112 | if (pos->attr.type == PERF_TYPE_SOFTWARE) | ||
| 1113 | continue; | ||
| 1114 | if (pmu_type == PERF_TYPE_SOFTWARE) { | ||
| 1115 | pmu_type = pos->attr.type; | ||
| 1116 | continue; | ||
| 1117 | } | ||
| 1118 | if (pmu_type != pos->attr.type) | ||
| 1119 | return true; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | return false; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, | ||
| 1126 | char *prefix, u64 run, u64 ena, double noise, | ||
| 1127 | struct runtime_stat *st) | ||
| 1128 | { | ||
| 1129 | struct perf_stat_output_ctx out; | ||
| 1130 | struct outstate os = { | ||
| 1131 | .fh = stat_config.output, | ||
| 1132 | .prefix = prefix ? prefix : "", | ||
| 1133 | .id = id, | ||
| 1134 | .nr = nr, | ||
| 1135 | .evsel = counter, | ||
| 1136 | }; | ||
| 1137 | print_metric_t pm = print_metric_std; | ||
| 1138 | void (*nl)(void *); | ||
| 1139 | |||
| 1140 | if (metric_only) { | ||
| 1141 | nl = new_line_metric; | ||
| 1142 | if (csv_output) | ||
| 1143 | pm = print_metric_only_csv; | ||
| 1144 | else | ||
| 1145 | pm = print_metric_only; | ||
| 1146 | } else | ||
| 1147 | nl = new_line_std; | ||
| 1148 | |||
| 1149 | if (csv_output && !metric_only) { | ||
| 1150 | static int aggr_fields[] = { | ||
| 1151 | [AGGR_GLOBAL] = 0, | ||
| 1152 | [AGGR_THREAD] = 1, | ||
| 1153 | [AGGR_NONE] = 1, | ||
| 1154 | [AGGR_SOCKET] = 2, | ||
| 1155 | [AGGR_CORE] = 2, | ||
| 1156 | }; | ||
| 1157 | |||
| 1158 | pm = print_metric_csv; | ||
| 1159 | nl = new_line_csv; | ||
| 1160 | os.nfields = 3; | ||
| 1161 | os.nfields += aggr_fields[stat_config.aggr_mode]; | ||
| 1162 | if (counter->cgrp) | ||
| 1163 | os.nfields++; | ||
| 1164 | } | ||
| 1165 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
| 1166 | if (metric_only) { | ||
| 1167 | pm(&os, NULL, "", "", 0); | ||
| 1168 | return; | ||
| 1169 | } | ||
| 1170 | aggr_printout(counter, id, nr); | ||
| 1171 | |||
| 1172 | fprintf(stat_config.output, "%*s%s", | ||
| 1173 | csv_output ? 0 : 18, | ||
| 1174 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
| 1175 | csv_sep); | ||
| 1176 | |||
| 1177 | if (counter->supported) { | ||
| 1178 | print_free_counters_hint = 1; | ||
| 1179 | if (is_mixed_hw_group(counter)) | ||
| 1180 | print_mixed_hw_group_error = 1; | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | fprintf(stat_config.output, "%-*s%s", | ||
| 1184 | csv_output ? 0 : unit_width, | ||
| 1185 | counter->unit, csv_sep); | ||
| 1186 | |||
| 1187 | fprintf(stat_config.output, "%*s", | ||
| 1188 | csv_output ? 0 : -25, | ||
| 1189 | perf_evsel__name(counter)); | ||
| 1190 | |||
| 1191 | if (counter->cgrp) | ||
| 1192 | fprintf(stat_config.output, "%s%s", | ||
| 1193 | csv_sep, counter->cgrp->name); | ||
| 1194 | |||
| 1195 | if (!csv_output) | ||
| 1196 | pm(&os, NULL, NULL, "", 0); | ||
| 1197 | print_noise(counter, noise); | ||
| 1198 | print_running(run, ena); | ||
| 1199 | if (csv_output) | ||
| 1200 | pm(&os, NULL, NULL, "", 0); | ||
| 1201 | return; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | if (!metric_only) | ||
| 1205 | abs_printout(id, nr, counter, uval); | ||
| 1206 | |||
| 1207 | out.print_metric = pm; | ||
| 1208 | out.new_line = nl; | ||
| 1209 | out.ctx = &os; | ||
| 1210 | out.force_header = false; | ||
| 1211 | |||
| 1212 | if (csv_output && !metric_only) { | ||
| 1213 | print_noise(counter, noise); | ||
| 1214 | print_running(run, ena); | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | perf_stat__print_shadow_stats(counter, uval, | ||
| 1218 | first_shadow_cpu(counter, id), | ||
| 1219 | &out, &metric_events, st); | ||
| 1220 | if (!csv_output && !metric_only) { | ||
| 1221 | print_noise(counter, noise); | ||
| 1222 | print_running(run, ena); | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | static void aggr_update_shadow(void) | ||
| 1227 | { | ||
| 1228 | int cpu, s2, id, s; | ||
| 1229 | u64 val; | ||
| 1230 | struct perf_evsel *counter; | ||
| 1231 | |||
| 1232 | for (s = 0; s < aggr_map->nr; s++) { | ||
| 1233 | id = aggr_map->map[s]; | ||
| 1234 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1235 | val = 0; | ||
| 1236 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 1237 | s2 = aggr_get_id(evsel_list->cpus, cpu); | ||
| 1238 | if (s2 != id) | ||
| 1239 | continue; | ||
| 1240 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
| 1241 | } | ||
| 1242 | perf_stat__update_shadow_stats(counter, val, | ||
| 1243 | first_shadow_cpu(counter, id), | ||
| 1244 | &rt_stat); | ||
| 1245 | } | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | static void uniquify_event_name(struct perf_evsel *counter) | ||
| 1250 | { | ||
| 1251 | char *new_name; | ||
| 1252 | char *config; | ||
| 1253 | |||
| 1254 | if (counter->uniquified_name || | ||
| 1255 | !counter->pmu_name || !strncmp(counter->name, counter->pmu_name, | ||
| 1256 | strlen(counter->pmu_name))) | ||
| 1257 | return; | ||
| 1258 | |||
| 1259 | config = strchr(counter->name, '/'); | ||
| 1260 | if (config) { | ||
| 1261 | if (asprintf(&new_name, | ||
| 1262 | "%s%s", counter->pmu_name, config) > 0) { | ||
| 1263 | free(counter->name); | ||
| 1264 | counter->name = new_name; | ||
| 1265 | } | ||
| 1266 | } else { | ||
| 1267 | if (asprintf(&new_name, | ||
| 1268 | "%s [%s]", counter->name, counter->pmu_name) > 0) { | ||
| 1269 | free(counter->name); | ||
| 1270 | counter->name = new_name; | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | counter->uniquified_name = true; | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | static void collect_all_aliases(struct perf_evsel *counter, | ||
| 1278 | void (*cb)(struct perf_evsel *counter, void *data, | ||
| 1279 | bool first), | ||
| 1280 | void *data) | ||
| 1281 | { | ||
| 1282 | struct perf_evsel *alias; | ||
| 1283 | |||
| 1284 | alias = list_prepare_entry(counter, &(evsel_list->entries), node); | ||
| 1285 | list_for_each_entry_continue (alias, &evsel_list->entries, node) { | ||
| 1286 | if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || | ||
| 1287 | alias->scale != counter->scale || | ||
| 1288 | alias->cgrp != counter->cgrp || | ||
| 1289 | strcmp(alias->unit, counter->unit) || | ||
| 1290 | perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter)) | ||
| 1291 | break; | ||
| 1292 | alias->merged_stat = true; | ||
| 1293 | cb(alias, data, false); | ||
| 1294 | } | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | static bool collect_data(struct perf_evsel *counter, | ||
| 1298 | void (*cb)(struct perf_evsel *counter, void *data, | ||
| 1299 | bool first), | ||
| 1300 | void *data) | ||
| 1301 | { | ||
| 1302 | if (counter->merged_stat) | ||
| 1303 | return false; | ||
| 1304 | cb(counter, data, true); | ||
| 1305 | if (no_merge) | ||
| 1306 | uniquify_event_name(counter); | ||
| 1307 | else if (counter->auto_merge_stats) | ||
| 1308 | collect_all_aliases(counter, cb, data); | ||
| 1309 | return true; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | struct aggr_data { | ||
| 1313 | u64 ena, run, val; | ||
| 1314 | int id; | ||
| 1315 | int nr; | ||
| 1316 | int cpu; | ||
| 1317 | }; | ||
| 1318 | |||
| 1319 | static void aggr_cb(struct perf_evsel *counter, void *data, bool first) | ||
| 1320 | { | ||
| 1321 | struct aggr_data *ad = data; | ||
| 1322 | int cpu, s2; | ||
| 1323 | |||
| 1324 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 1325 | struct perf_counts_values *counts; | ||
| 1326 | |||
| 1327 | s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); | ||
| 1328 | if (s2 != ad->id) | ||
| 1329 | continue; | ||
| 1330 | if (first) | ||
| 1331 | ad->nr++; | ||
| 1332 | counts = perf_counts(counter->counts, cpu, 0); | ||
| 1333 | /* | ||
| 1334 | * When any result is bad, make them all to give | ||
| 1335 | * consistent output in interval mode. | ||
| 1336 | */ | ||
| 1337 | if (counts->ena == 0 || counts->run == 0 || | ||
| 1338 | counter->counts->scaled == -1) { | ||
| 1339 | ad->ena = 0; | ||
| 1340 | ad->run = 0; | ||
| 1341 | break; | ||
| 1342 | } | ||
| 1343 | ad->val += counts->val; | ||
| 1344 | ad->ena += counts->ena; | ||
| 1345 | ad->run += counts->run; | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | static void print_aggr(char *prefix) | ||
| 1350 | { | ||
| 1351 | FILE *output = stat_config.output; | ||
| 1352 | struct perf_evsel *counter; | ||
| 1353 | int s, id, nr; | ||
| 1354 | double uval; | ||
| 1355 | u64 ena, run, val; | ||
| 1356 | bool first; | ||
| 1357 | |||
| 1358 | if (!(aggr_map || aggr_get_id)) | ||
| 1359 | return; | ||
| 1360 | |||
| 1361 | aggr_update_shadow(); | ||
| 1362 | |||
| 1363 | /* | ||
| 1364 | * With metric_only everything is on a single line. | ||
| 1365 | * Without each counter has its own line. | ||
| 1366 | */ | ||
| 1367 | for (s = 0; s < aggr_map->nr; s++) { | ||
| 1368 | struct aggr_data ad; | ||
| 1369 | if (prefix && metric_only) | ||
| 1370 | fprintf(output, "%s", prefix); | ||
| 1371 | |||
| 1372 | ad.id = id = aggr_map->map[s]; | ||
| 1373 | first = true; | ||
| 1374 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1375 | if (is_duration_time(counter)) | ||
| 1376 | continue; | ||
| 1377 | |||
| 1378 | ad.val = ad.ena = ad.run = 0; | ||
| 1379 | ad.nr = 0; | ||
| 1380 | if (!collect_data(counter, aggr_cb, &ad)) | ||
| 1381 | continue; | ||
| 1382 | nr = ad.nr; | ||
| 1383 | ena = ad.ena; | ||
| 1384 | run = ad.run; | ||
| 1385 | val = ad.val; | ||
| 1386 | if (first && metric_only) { | ||
| 1387 | first = false; | ||
| 1388 | aggr_printout(counter, id, nr); | ||
| 1389 | } | ||
| 1390 | if (prefix && !metric_only) | ||
| 1391 | fprintf(output, "%s", prefix); | ||
| 1392 | |||
| 1393 | uval = val * counter->scale; | ||
| 1394 | printout(id, nr, counter, uval, prefix, run, ena, 1.0, | ||
| 1395 | &rt_stat); | ||
| 1396 | if (!metric_only) | ||
| 1397 | fputc('\n', output); | ||
| 1398 | } | ||
| 1399 | if (metric_only) | ||
| 1400 | fputc('\n', output); | ||
| 1401 | } | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | static int cmp_val(const void *a, const void *b) | ||
| 1405 | { | ||
| 1406 | return ((struct perf_aggr_thread_value *)b)->val - | ||
| 1407 | ((struct perf_aggr_thread_value *)a)->val; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static struct perf_aggr_thread_value *sort_aggr_thread( | ||
| 1411 | struct perf_evsel *counter, | ||
| 1412 | int nthreads, int ncpus, | ||
| 1413 | int *ret) | ||
| 1414 | { | ||
| 1415 | int cpu, thread, i = 0; | ||
| 1416 | double uval; | ||
| 1417 | struct perf_aggr_thread_value *buf; | ||
| 1418 | |||
| 1419 | buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); | ||
| 1420 | if (!buf) | ||
| 1421 | return NULL; | ||
| 1422 | |||
| 1423 | for (thread = 0; thread < nthreads; thread++) { | ||
| 1424 | u64 ena = 0, run = 0, val = 0; | ||
| 1425 | |||
| 1426 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
| 1427 | val += perf_counts(counter->counts, cpu, thread)->val; | ||
| 1428 | ena += perf_counts(counter->counts, cpu, thread)->ena; | ||
| 1429 | run += perf_counts(counter->counts, cpu, thread)->run; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | uval = val * counter->scale; | ||
| 1433 | |||
| 1434 | /* | ||
| 1435 | * Skip value 0 when enabling --per-thread globally, | ||
| 1436 | * otherwise too many 0 output. | ||
| 1437 | */ | ||
| 1438 | if (uval == 0.0 && target__has_per_thread(&target)) | ||
| 1439 | continue; | ||
| 1440 | |||
| 1441 | buf[i].counter = counter; | ||
| 1442 | buf[i].id = thread; | ||
| 1443 | buf[i].uval = uval; | ||
| 1444 | buf[i].val = val; | ||
| 1445 | buf[i].run = run; | ||
| 1446 | buf[i].ena = ena; | ||
| 1447 | i++; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); | ||
| 1451 | |||
| 1452 | if (ret) | ||
| 1453 | *ret = i; | ||
| 1454 | |||
| 1455 | return buf; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | ||
| 1459 | { | ||
| 1460 | FILE *output = stat_config.output; | ||
| 1461 | int nthreads = thread_map__nr(counter->threads); | ||
| 1462 | int ncpus = cpu_map__nr(counter->cpus); | ||
| 1463 | int thread, sorted_threads, id; | ||
| 1464 | struct perf_aggr_thread_value *buf; | ||
| 1465 | |||
| 1466 | buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads); | ||
| 1467 | if (!buf) { | ||
| 1468 | perror("cannot sort aggr thread"); | ||
| 1469 | return; | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | for (thread = 0; thread < sorted_threads; thread++) { | ||
| 1473 | if (prefix) | ||
| 1474 | fprintf(output, "%s", prefix); | ||
| 1475 | |||
| 1476 | id = buf[thread].id; | ||
| 1477 | if (stat_config.stats) | ||
| 1478 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
| 1479 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
| 1480 | &stat_config.stats[id]); | ||
| 1481 | else | ||
| 1482 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
| 1483 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
| 1484 | &rt_stat); | ||
| 1485 | fputc('\n', output); | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | free(buf); | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | struct caggr_data { | ||
| 1492 | double avg, avg_enabled, avg_running; | ||
| 1493 | }; | ||
| 1494 | |||
| 1495 | static void counter_aggr_cb(struct perf_evsel *counter, void *data, | ||
| 1496 | bool first __maybe_unused) | ||
| 1497 | { | ||
| 1498 | struct caggr_data *cd = data; | ||
| 1499 | struct perf_stat_evsel *ps = counter->stats; | ||
| 1500 | |||
| 1501 | cd->avg += avg_stats(&ps->res_stats[0]); | ||
| 1502 | cd->avg_enabled += avg_stats(&ps->res_stats[1]); | ||
| 1503 | cd->avg_running += avg_stats(&ps->res_stats[2]); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | /* | ||
| 1507 | * Print out the results of a single counter: | ||
| 1508 | * aggregated counts in system-wide mode | ||
| 1509 | */ | ||
| 1510 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | ||
| 1511 | { | ||
| 1512 | FILE *output = stat_config.output; | ||
| 1513 | double uval; | ||
| 1514 | struct caggr_data cd = { .avg = 0.0 }; | ||
| 1515 | |||
| 1516 | if (!collect_data(counter, counter_aggr_cb, &cd)) | ||
| 1517 | return; | ||
| 1518 | |||
| 1519 | if (prefix && !metric_only) | ||
| 1520 | fprintf(output, "%s", prefix); | ||
| 1521 | |||
| 1522 | uval = cd.avg * counter->scale; | ||
| 1523 | printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, | ||
| 1524 | cd.avg, &rt_stat); | ||
| 1525 | if (!metric_only) | ||
| 1526 | fprintf(output, "\n"); | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | static void counter_cb(struct perf_evsel *counter, void *data, | ||
| 1530 | bool first __maybe_unused) | ||
| 1531 | { | ||
| 1532 | struct aggr_data *ad = data; | ||
| 1533 | |||
| 1534 | ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; | ||
| 1535 | ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; | ||
| 1536 | ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | /* | ||
| 1540 | * Print out the results of a single counter: | ||
| 1541 | * does not use aggregated count in system-wide | ||
| 1542 | */ | ||
| 1543 | static void print_counter(struct perf_evsel *counter, char *prefix) | ||
| 1544 | { | ||
| 1545 | FILE *output = stat_config.output; | ||
| 1546 | u64 ena, run, val; | ||
| 1547 | double uval; | ||
| 1548 | int cpu; | ||
| 1549 | |||
| 1550 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 1551 | struct aggr_data ad = { .cpu = cpu }; | ||
| 1552 | |||
| 1553 | if (!collect_data(counter, counter_cb, &ad)) | ||
| 1554 | return; | ||
| 1555 | val = ad.val; | ||
| 1556 | ena = ad.ena; | ||
| 1557 | run = ad.run; | ||
| 1558 | |||
| 1559 | if (prefix) | ||
| 1560 | fprintf(output, "%s", prefix); | ||
| 1561 | |||
| 1562 | uval = val * counter->scale; | ||
| 1563 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
| 1564 | &rt_stat); | ||
| 1565 | |||
| 1566 | fputc('\n', output); | ||
| 1567 | } | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | static void print_no_aggr_metric(char *prefix) | ||
| 1571 | { | ||
| 1572 | int cpu; | ||
| 1573 | int nrcpus = 0; | ||
| 1574 | struct perf_evsel *counter; | ||
| 1575 | u64 ena, run, val; | ||
| 1576 | double uval; | ||
| 1577 | |||
| 1578 | nrcpus = evsel_list->cpus->nr; | ||
| 1579 | for (cpu = 0; cpu < nrcpus; cpu++) { | ||
| 1580 | bool first = true; | ||
| 1581 | |||
| 1582 | if (prefix) | ||
| 1583 | fputs(prefix, stat_config.output); | ||
| 1584 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1585 | if (is_duration_time(counter)) | ||
| 1586 | continue; | ||
| 1587 | if (first) { | ||
| 1588 | aggr_printout(counter, cpu, 0); | ||
| 1589 | first = false; | ||
| 1590 | } | ||
| 1591 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
| 1592 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
| 1593 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
| 1594 | |||
| 1595 | uval = val * counter->scale; | ||
| 1596 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
| 1597 | &rt_stat); | ||
| 1598 | } | ||
| 1599 | fputc('\n', stat_config.output); | ||
| 1600 | } | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | static int aggr_header_lens[] = { | ||
| 1604 | [AGGR_CORE] = 18, | ||
| 1605 | [AGGR_SOCKET] = 12, | ||
| 1606 | [AGGR_NONE] = 6, | ||
| 1607 | [AGGR_THREAD] = 24, | ||
| 1608 | [AGGR_GLOBAL] = 0, | ||
| 1609 | }; | ||
| 1610 | |||
| 1611 | static const char *aggr_header_csv[] = { | ||
| 1612 | [AGGR_CORE] = "core,cpus,", | ||
| 1613 | [AGGR_SOCKET] = "socket,cpus", | ||
| 1614 | [AGGR_NONE] = "cpu,", | ||
| 1615 | [AGGR_THREAD] = "comm-pid,", | ||
| 1616 | [AGGR_GLOBAL] = "" | ||
| 1617 | }; | ||
| 1618 | |||
| 1619 | static void print_metric_headers(const char *prefix, bool no_indent) | ||
| 1620 | { | ||
| 1621 | struct perf_stat_output_ctx out; | ||
| 1622 | struct perf_evsel *counter; | ||
| 1623 | struct outstate os = { | ||
| 1624 | .fh = stat_config.output | ||
| 1625 | }; | ||
| 1626 | |||
| 1627 | if (prefix) | ||
| 1628 | fprintf(stat_config.output, "%s", prefix); | ||
| 1629 | |||
| 1630 | if (!csv_output && !no_indent) | ||
| 1631 | fprintf(stat_config.output, "%*s", | ||
| 1632 | aggr_header_lens[stat_config.aggr_mode], ""); | ||
| 1633 | if (csv_output) { | ||
| 1634 | if (stat_config.interval) | ||
| 1635 | fputs("time,", stat_config.output); | ||
| 1636 | fputs(aggr_header_csv[stat_config.aggr_mode], | ||
| 1637 | stat_config.output); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | /* Print metrics headers only */ | ||
| 1641 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1642 | if (is_duration_time(counter)) | ||
| 1643 | continue; | ||
| 1644 | os.evsel = counter; | ||
| 1645 | out.ctx = &os; | ||
| 1646 | out.print_metric = print_metric_header; | ||
| 1647 | out.new_line = new_line_metric; | ||
| 1648 | out.force_header = true; | ||
| 1649 | os.evsel = counter; | ||
| 1650 | perf_stat__print_shadow_stats(counter, 0, | ||
| 1651 | 0, | ||
| 1652 | &out, | ||
| 1653 | &metric_events, | ||
| 1654 | &rt_stat); | ||
| 1655 | } | ||
| 1656 | fputc('\n', stat_config.output); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | static void print_interval(char *prefix, struct timespec *ts) | ||
| 1660 | { | ||
| 1661 | FILE *output = stat_config.output; | ||
| 1662 | static int num_print_interval; | ||
| 1663 | |||
| 1664 | if (interval_clear) | ||
| 1665 | puts(CONSOLE_CLEAR); | ||
| 1666 | |||
| 1667 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | ||
| 1668 | |||
| 1669 | if ((num_print_interval == 0 && !csv_output) || interval_clear) { | ||
| 1670 | switch (stat_config.aggr_mode) { | ||
| 1671 | case AGGR_SOCKET: | ||
| 1672 | fprintf(output, "# time socket cpus"); | ||
| 1673 | if (!metric_only) | ||
| 1674 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 1675 | break; | ||
| 1676 | case AGGR_CORE: | ||
| 1677 | fprintf(output, "# time core cpus"); | ||
| 1678 | if (!metric_only) | ||
| 1679 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 1680 | break; | ||
| 1681 | case AGGR_NONE: | ||
| 1682 | fprintf(output, "# time CPU "); | ||
| 1683 | if (!metric_only) | ||
| 1684 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 1685 | break; | ||
| 1686 | case AGGR_THREAD: | ||
| 1687 | fprintf(output, "# time comm-pid"); | ||
| 1688 | if (!metric_only) | ||
| 1689 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 1690 | break; | ||
| 1691 | case AGGR_GLOBAL: | ||
| 1692 | default: | ||
| 1693 | fprintf(output, "# time"); | ||
| 1694 | if (!metric_only) | ||
| 1695 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 1696 | case AGGR_UNSET: | ||
| 1697 | break; | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | if ((num_print_interval == 0 || interval_clear) && metric_only) | ||
| 1702 | print_metric_headers(" ", true); | ||
| 1703 | if (++num_print_interval == 25) | ||
| 1704 | num_print_interval = 0; | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | static void print_header(int argc, const char **argv) | ||
| 1708 | { | ||
| 1709 | FILE *output = stat_config.output; | ||
| 1710 | int i; | ||
| 1711 | |||
| 1712 | fflush(stdout); | ||
| 1713 | |||
| 1714 | if (!csv_output) { | ||
| 1715 | fprintf(output, "\n"); | ||
| 1716 | fprintf(output, " Performance counter stats for "); | ||
| 1717 | if (target.system_wide) | ||
| 1718 | fprintf(output, "\'system wide"); | ||
| 1719 | else if (target.cpu_list) | ||
| 1720 | fprintf(output, "\'CPU(s) %s", target.cpu_list); | ||
| 1721 | else if (!target__has_task(&target)) { | ||
| 1722 | fprintf(output, "\'%s", argv ? argv[0] : "pipe"); | ||
| 1723 | for (i = 1; argv && (i < argc); i++) | ||
| 1724 | fprintf(output, " %s", argv[i]); | ||
| 1725 | } else if (target.pid) | ||
| 1726 | fprintf(output, "process id \'%s", target.pid); | ||
| 1727 | else | ||
| 1728 | fprintf(output, "thread id \'%s", target.tid); | ||
| 1729 | |||
| 1730 | fprintf(output, "\'"); | ||
| 1731 | if (run_count > 1) | ||
| 1732 | fprintf(output, " (%d runs)", run_count); | ||
| 1733 | fprintf(output, ":\n\n"); | ||
| 1734 | } | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | static int get_precision(double num) | ||
| 1738 | { | ||
| 1739 | if (num > 1) | ||
| 1740 | return 0; | ||
| 1741 | |||
| 1742 | return lround(ceil(-log10(num))); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | static void print_table(FILE *output, int precision, double avg) | ||
| 1746 | { | ||
| 1747 | char tmp[64]; | ||
| 1748 | int idx, indent = 0; | ||
| 1749 | |||
| 1750 | scnprintf(tmp, 64, " %17.*f", precision, avg); | ||
| 1751 | while (tmp[indent] == ' ') | ||
| 1752 | indent++; | ||
| 1753 | |||
| 1754 | fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); | ||
| 1755 | |||
| 1756 | for (idx = 0; idx < run_count; idx++) { | ||
| 1757 | double run = (double) walltime_run[idx] / NSEC_PER_SEC; | ||
| 1758 | int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); | ||
| 1759 | |||
| 1760 | fprintf(output, " %17.*f (%+.*f) ", | ||
| 1761 | precision, run, precision, run - avg); | ||
| 1762 | |||
| 1763 | for (h = 0; h < n; h++) | ||
| 1764 | fprintf(output, "#"); | ||
| 1765 | |||
| 1766 | fprintf(output, "\n"); | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | fprintf(output, "\n%*s# Final result:\n", indent, ""); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | static double timeval2double(struct timeval *t) | ||
| 1773 | { | ||
| 1774 | return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | static void print_footer(void) | ||
| 1778 | { | ||
| 1779 | double avg = avg_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; | ||
| 1780 | FILE *output = stat_config.output; | ||
| 1781 | int n; | ||
| 1782 | |||
| 1783 | if (!null_run) | ||
| 1784 | fprintf(output, "\n"); | ||
| 1785 | |||
| 1786 | if (run_count == 1) { | ||
| 1787 | fprintf(output, " %17.9f seconds time elapsed", avg); | ||
| 1788 | |||
| 1789 | if (ru_display) { | ||
| 1790 | double ru_utime = timeval2double(&ru_data.ru_utime); | ||
| 1791 | double ru_stime = timeval2double(&ru_data.ru_stime); | ||
| 1792 | |||
| 1793 | fprintf(output, "\n\n"); | ||
| 1794 | fprintf(output, " %17.9f seconds user\n", ru_utime); | ||
| 1795 | fprintf(output, " %17.9f seconds sys\n", ru_stime); | ||
| 1796 | } | ||
| 1797 | } else { | ||
| 1798 | double sd = stddev_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; | ||
| 1799 | /* | ||
| 1800 | * Display at most 2 more significant | ||
| 1801 | * digits than the stddev inaccuracy. | ||
| 1802 | */ | ||
| 1803 | int precision = get_precision(sd) + 2; | ||
| 1804 | |||
| 1805 | if (walltime_run_table) | ||
| 1806 | print_table(output, precision, avg); | ||
| 1807 | |||
| 1808 | fprintf(output, " %17.*f +- %.*f seconds time elapsed", | ||
| 1809 | precision, avg, precision, sd); | ||
| 1810 | |||
| 1811 | print_noise_pct(sd, avg); | ||
| 1812 | } | ||
| 1813 | fprintf(output, "\n\n"); | ||
| 1814 | |||
| 1815 | if (print_free_counters_hint && | ||
| 1816 | sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 && | ||
| 1817 | n > 0) | ||
| 1818 | fprintf(output, | ||
| 1819 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | ||
| 1820 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | ||
| 1821 | " perf stat ...\n" | ||
| 1822 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | ||
| 1823 | |||
| 1824 | if (print_mixed_hw_group_error) | ||
| 1825 | fprintf(output, | ||
| 1826 | "The events in group usually have to be from " | ||
| 1827 | "the same PMU. Try reorganizing the group.\n"); | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | static void print_counters(struct timespec *ts, int argc, const char **argv) | 639 | static void print_counters(struct timespec *ts, int argc, const char **argv) |
| 1831 | { | 640 | { |
| 1832 | int interval = stat_config.interval; | ||
| 1833 | struct perf_evsel *counter; | ||
| 1834 | char buf[64], *prefix = NULL; | ||
| 1835 | |||
| 1836 | /* Do not print anything if we record to the pipe. */ | 641 | /* Do not print anything if we record to the pipe. */ |
| 1837 | if (STAT_RECORD && perf_stat.data.is_pipe) | 642 | if (STAT_RECORD && perf_stat.data.is_pipe) |
| 1838 | return; | 643 | return; |
| 1839 | 644 | ||
| 1840 | if (interval) | 645 | perf_evlist__print_counters(evsel_list, &stat_config, &target, |
| 1841 | print_interval(prefix = buf, ts); | 646 | ts, argc, argv); |
| 1842 | else | ||
| 1843 | print_header(argc, argv); | ||
| 1844 | |||
| 1845 | if (metric_only) { | ||
| 1846 | static int num_print_iv; | ||
| 1847 | |||
| 1848 | if (num_print_iv == 0 && !interval) | ||
| 1849 | print_metric_headers(prefix, false); | ||
| 1850 | if (num_print_iv++ == 25) | ||
| 1851 | num_print_iv = 0; | ||
| 1852 | if (stat_config.aggr_mode == AGGR_GLOBAL && prefix) | ||
| 1853 | fprintf(stat_config.output, "%s", prefix); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | switch (stat_config.aggr_mode) { | ||
| 1857 | case AGGR_CORE: | ||
| 1858 | case AGGR_SOCKET: | ||
| 1859 | print_aggr(prefix); | ||
| 1860 | break; | ||
| 1861 | case AGGR_THREAD: | ||
| 1862 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1863 | if (is_duration_time(counter)) | ||
| 1864 | continue; | ||
| 1865 | print_aggr_thread(counter, prefix); | ||
| 1866 | } | ||
| 1867 | break; | ||
| 1868 | case AGGR_GLOBAL: | ||
| 1869 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1870 | if (is_duration_time(counter)) | ||
| 1871 | continue; | ||
| 1872 | print_counter_aggr(counter, prefix); | ||
| 1873 | } | ||
| 1874 | if (metric_only) | ||
| 1875 | fputc('\n', stat_config.output); | ||
| 1876 | break; | ||
| 1877 | case AGGR_NONE: | ||
| 1878 | if (metric_only) | ||
| 1879 | print_no_aggr_metric(prefix); | ||
| 1880 | else { | ||
| 1881 | evlist__for_each_entry(evsel_list, counter) { | ||
| 1882 | if (is_duration_time(counter)) | ||
| 1883 | continue; | ||
| 1884 | print_counter(counter, prefix); | ||
| 1885 | } | ||
| 1886 | } | ||
| 1887 | break; | ||
| 1888 | case AGGR_UNSET: | ||
| 1889 | default: | ||
| 1890 | break; | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | if (!interval && !csv_output) | ||
| 1894 | print_footer(); | ||
| 1895 | |||
| 1896 | fflush(stat_config.output); | ||
| 1897 | } | 647 | } |
| 1898 | 648 | ||
| 1899 | static volatile int signr = -1; | 649 | static volatile int signr = -1; |
| @@ -1950,7 +700,7 @@ static int enable_metric_only(const struct option *opt __maybe_unused, | |||
| 1950 | const char *s __maybe_unused, int unset) | 700 | const char *s __maybe_unused, int unset) |
| 1951 | { | 701 | { |
| 1952 | force_metric_only = true; | 702 | force_metric_only = true; |
| 1953 | metric_only = !unset; | 703 | stat_config.metric_only = !unset; |
| 1954 | return 0; | 704 | return 0; |
| 1955 | } | 705 | } |
| 1956 | 706 | ||
| @@ -1958,7 +708,7 @@ static int parse_metric_groups(const struct option *opt, | |||
| 1958 | const char *str, | 708 | const char *str, |
| 1959 | int unset __maybe_unused) | 709 | int unset __maybe_unused) |
| 1960 | { | 710 | { |
| 1961 | return metricgroup__parse_groups(opt, str, &metric_events); | 711 | return metricgroup__parse_groups(opt, str, &stat_config.metric_events); |
| 1962 | } | 712 | } |
| 1963 | 713 | ||
| 1964 | static const struct option stat_options[] = { | 714 | static const struct option stat_options[] = { |
| @@ -1969,7 +719,7 @@ static const struct option stat_options[] = { | |||
| 1969 | parse_events_option), | 719 | parse_events_option), |
| 1970 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 720 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
| 1971 | "event filter", parse_filter), | 721 | "event filter", parse_filter), |
| 1972 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 722 | OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, |
| 1973 | "child tasks do not inherit counters"), | 723 | "child tasks do not inherit counters"), |
| 1974 | OPT_STRING('p', "pid", &target.pid, "pid", | 724 | OPT_STRING('p', "pid", &target.pid, "pid", |
| 1975 | "stat events on existing process id"), | 725 | "stat events on existing process id"), |
| @@ -1982,11 +732,11 @@ static const struct option stat_options[] = { | |||
| 1982 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), | 732 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), |
| 1983 | OPT_INCR('v', "verbose", &verbose, | 733 | OPT_INCR('v', "verbose", &verbose, |
| 1984 | "be more verbose (show counter open errors, etc)"), | 734 | "be more verbose (show counter open errors, etc)"), |
| 1985 | OPT_INTEGER('r', "repeat", &run_count, | 735 | OPT_INTEGER('r', "repeat", &stat_config.run_count, |
| 1986 | "repeat command and print average + stddev (max: 100, forever: 0)"), | 736 | "repeat command and print average + stddev (max: 100, forever: 0)"), |
| 1987 | OPT_BOOLEAN(0, "table", &walltime_run_table, | 737 | OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, |
| 1988 | "display details about each run (only with -r option)"), | 738 | "display details about each run (only with -r option)"), |
| 1989 | OPT_BOOLEAN('n', "null", &null_run, | 739 | OPT_BOOLEAN('n', "null", &stat_config.null_run, |
| 1990 | "null run - dont start any counters"), | 740 | "null run - dont start any counters"), |
| 1991 | OPT_INCR('d', "detailed", &detailed_run, | 741 | OPT_INCR('d', "detailed", &detailed_run, |
| 1992 | "detailed run - start a lot of events"), | 742 | "detailed run - start a lot of events"), |
| @@ -1999,8 +749,8 @@ static const struct option stat_options[] = { | |||
| 1999 | "list of cpus to monitor in system-wide"), | 749 | "list of cpus to monitor in system-wide"), |
| 2000 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, | 750 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, |
| 2001 | "disable CPU count aggregation", AGGR_NONE), | 751 | "disable CPU count aggregation", AGGR_NONE), |
| 2002 | OPT_BOOLEAN(0, "no-merge", &no_merge, "Do not merge identical named events"), | 752 | OPT_BOOLEAN(0, "no-merge", &stat_config.no_merge, "Do not merge identical named events"), |
| 2003 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 753 | OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", |
| 2004 | "print counts with custom separator"), | 754 | "print counts with custom separator"), |
| 2005 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 755 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
| 2006 | "monitor event in cgroup name only", parse_cgroups), | 756 | "monitor event in cgroup name only", parse_cgroups), |
| @@ -2017,7 +767,7 @@ static const struct option stat_options[] = { | |||
| 2017 | "(overhead is possible for values <= 100ms)"), | 767 | "(overhead is possible for values <= 100ms)"), |
| 2018 | OPT_INTEGER(0, "interval-count", &stat_config.times, | 768 | OPT_INTEGER(0, "interval-count", &stat_config.times, |
| 2019 | "print counts for fixed number of times"), | 769 | "print counts for fixed number of times"), |
| 2020 | OPT_BOOLEAN(0, "interval-clear", &interval_clear, | 770 | OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, |
| 2021 | "clear screen in between new interval"), | 771 | "clear screen in between new interval"), |
| 2022 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, | 772 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, |
| 2023 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), | 773 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), |
| @@ -2027,9 +777,9 @@ static const struct option stat_options[] = { | |||
| 2027 | "aggregate counts per physical processor core", AGGR_CORE), | 777 | "aggregate counts per physical processor core", AGGR_CORE), |
| 2028 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, | 778 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, |
| 2029 | "aggregate counts per thread", AGGR_THREAD), | 779 | "aggregate counts per thread", AGGR_THREAD), |
| 2030 | OPT_UINTEGER('D', "delay", &initial_delay, | 780 | OPT_UINTEGER('D', "delay", &stat_config.initial_delay, |
| 2031 | "ms to wait before starting measurement after program start"), | 781 | "ms to wait before starting measurement after program start"), |
| 2032 | OPT_CALLBACK_NOOPT(0, "metric-only", &metric_only, NULL, | 782 | OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, |
| 2033 | "Only print computed metrics. No raw values", enable_metric_only), | 783 | "Only print computed metrics. No raw values", enable_metric_only), |
| 2034 | OPT_BOOLEAN(0, "topdown", &topdown_run, | 784 | OPT_BOOLEAN(0, "topdown", &topdown_run, |
| 2035 | "measure topdown level 1 statistics"), | 785 | "measure topdown level 1 statistics"), |
| @@ -2041,12 +791,14 @@ static const struct option stat_options[] = { | |||
| 2041 | OPT_END() | 791 | OPT_END() |
| 2042 | }; | 792 | }; |
| 2043 | 793 | ||
| 2044 | static int perf_stat__get_socket(struct cpu_map *map, int cpu) | 794 | static int perf_stat__get_socket(struct perf_stat_config *config __maybe_unused, |
| 795 | struct cpu_map *map, int cpu) | ||
| 2045 | { | 796 | { |
| 2046 | return cpu_map__get_socket(map, cpu, NULL); | 797 | return cpu_map__get_socket(map, cpu, NULL); |
| 2047 | } | 798 | } |
| 2048 | 799 | ||
| 2049 | static int perf_stat__get_core(struct cpu_map *map, int cpu) | 800 | static int perf_stat__get_core(struct perf_stat_config *config __maybe_unused, |
| 801 | struct cpu_map *map, int cpu) | ||
| 2050 | { | 802 | { |
| 2051 | return cpu_map__get_core(map, cpu, NULL); | 803 | return cpu_map__get_core(map, cpu, NULL); |
| 2052 | } | 804 | } |
| @@ -2063,9 +815,8 @@ static int cpu_map__get_max(struct cpu_map *map) | |||
| 2063 | return max; | 815 | return max; |
| 2064 | } | 816 | } |
| 2065 | 817 | ||
| 2066 | static struct cpu_map *cpus_aggr_map; | 818 | static int perf_stat__get_aggr(struct perf_stat_config *config, |
| 2067 | 819 | aggr_get_id_t get_id, struct cpu_map *map, int idx) | |
| 2068 | static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int idx) | ||
| 2069 | { | 820 | { |
| 2070 | int cpu; | 821 | int cpu; |
| 2071 | 822 | ||
| @@ -2074,20 +825,22 @@ static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int id | |||
| 2074 | 825 | ||
| 2075 | cpu = map->map[idx]; | 826 | cpu = map->map[idx]; |
| 2076 | 827 | ||
| 2077 | if (cpus_aggr_map->map[cpu] == -1) | 828 | if (config->cpus_aggr_map->map[cpu] == -1) |
| 2078 | cpus_aggr_map->map[cpu] = get_id(map, idx); | 829 | config->cpus_aggr_map->map[cpu] = get_id(config, map, idx); |
| 2079 | 830 | ||
| 2080 | return cpus_aggr_map->map[cpu]; | 831 | return config->cpus_aggr_map->map[cpu]; |
| 2081 | } | 832 | } |
| 2082 | 833 | ||
| 2083 | static int perf_stat__get_socket_cached(struct cpu_map *map, int idx) | 834 | static int perf_stat__get_socket_cached(struct perf_stat_config *config, |
| 835 | struct cpu_map *map, int idx) | ||
| 2084 | { | 836 | { |
| 2085 | return perf_stat__get_aggr(perf_stat__get_socket, map, idx); | 837 | return perf_stat__get_aggr(config, perf_stat__get_socket, map, idx); |
| 2086 | } | 838 | } |
| 2087 | 839 | ||
| 2088 | static int perf_stat__get_core_cached(struct cpu_map *map, int idx) | 840 | static int perf_stat__get_core_cached(struct perf_stat_config *config, |
| 841 | struct cpu_map *map, int idx) | ||
| 2089 | { | 842 | { |
| 2090 | return perf_stat__get_aggr(perf_stat__get_core, map, idx); | 843 | return perf_stat__get_aggr(config, perf_stat__get_core, map, idx); |
| 2091 | } | 844 | } |
| 2092 | 845 | ||
| 2093 | static int perf_stat_init_aggr_mode(void) | 846 | static int perf_stat_init_aggr_mode(void) |
| @@ -2096,18 +849,18 @@ static int perf_stat_init_aggr_mode(void) | |||
| 2096 | 849 | ||
| 2097 | switch (stat_config.aggr_mode) { | 850 | switch (stat_config.aggr_mode) { |
| 2098 | case AGGR_SOCKET: | 851 | case AGGR_SOCKET: |
| 2099 | if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { | 852 | if (cpu_map__build_socket_map(evsel_list->cpus, &stat_config.aggr_map)) { |
| 2100 | perror("cannot build socket map"); | 853 | perror("cannot build socket map"); |
| 2101 | return -1; | 854 | return -1; |
| 2102 | } | 855 | } |
| 2103 | aggr_get_id = perf_stat__get_socket_cached; | 856 | stat_config.aggr_get_id = perf_stat__get_socket_cached; |
| 2104 | break; | 857 | break; |
| 2105 | case AGGR_CORE: | 858 | case AGGR_CORE: |
| 2106 | if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { | 859 | if (cpu_map__build_core_map(evsel_list->cpus, &stat_config.aggr_map)) { |
| 2107 | perror("cannot build core map"); | 860 | perror("cannot build core map"); |
| 2108 | return -1; | 861 | return -1; |
| 2109 | } | 862 | } |
| 2110 | aggr_get_id = perf_stat__get_core_cached; | 863 | stat_config.aggr_get_id = perf_stat__get_core_cached; |
| 2111 | break; | 864 | break; |
| 2112 | case AGGR_NONE: | 865 | case AGGR_NONE: |
| 2113 | case AGGR_GLOBAL: | 866 | case AGGR_GLOBAL: |
| @@ -2123,16 +876,16 @@ static int perf_stat_init_aggr_mode(void) | |||
| 2123 | * the aggregation translate cpumap. | 876 | * the aggregation translate cpumap. |
| 2124 | */ | 877 | */ |
| 2125 | nr = cpu_map__get_max(evsel_list->cpus); | 878 | nr = cpu_map__get_max(evsel_list->cpus); |
| 2126 | cpus_aggr_map = cpu_map__empty_new(nr + 1); | 879 | stat_config.cpus_aggr_map = cpu_map__empty_new(nr + 1); |
| 2127 | return cpus_aggr_map ? 0 : -ENOMEM; | 880 | return stat_config.cpus_aggr_map ? 0 : -ENOMEM; |
| 2128 | } | 881 | } |
| 2129 | 882 | ||
| 2130 | static void perf_stat__exit_aggr_mode(void) | 883 | static void perf_stat__exit_aggr_mode(void) |
| 2131 | { | 884 | { |
| 2132 | cpu_map__put(aggr_map); | 885 | cpu_map__put(stat_config.aggr_map); |
| 2133 | cpu_map__put(cpus_aggr_map); | 886 | cpu_map__put(stat_config.cpus_aggr_map); |
| 2134 | aggr_map = NULL; | 887 | stat_config.aggr_map = NULL; |
| 2135 | cpus_aggr_map = NULL; | 888 | stat_config.cpus_aggr_map = NULL; |
| 2136 | } | 889 | } |
| 2137 | 890 | ||
| 2138 | static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) | 891 | static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) |
| @@ -2190,12 +943,14 @@ static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, | |||
| 2190 | return cpu_map__build_map(cpus, corep, perf_env__get_core, env); | 943 | return cpu_map__build_map(cpus, corep, perf_env__get_core, env); |
| 2191 | } | 944 | } |
| 2192 | 945 | ||
| 2193 | static int perf_stat__get_socket_file(struct cpu_map *map, int idx) | 946 | static int perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused, |
| 947 | struct cpu_map *map, int idx) | ||
| 2194 | { | 948 | { |
| 2195 | return perf_env__get_socket(map, idx, &perf_stat.session->header.env); | 949 | return perf_env__get_socket(map, idx, &perf_stat.session->header.env); |
| 2196 | } | 950 | } |
| 2197 | 951 | ||
| 2198 | static int perf_stat__get_core_file(struct cpu_map *map, int idx) | 952 | static int perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused, |
| 953 | struct cpu_map *map, int idx) | ||
| 2199 | { | 954 | { |
| 2200 | return perf_env__get_core(map, idx, &perf_stat.session->header.env); | 955 | return perf_env__get_core(map, idx, &perf_stat.session->header.env); |
| 2201 | } | 956 | } |
| @@ -2206,18 +961,18 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st) | |||
| 2206 | 961 | ||
| 2207 | switch (stat_config.aggr_mode) { | 962 | switch (stat_config.aggr_mode) { |
| 2208 | case AGGR_SOCKET: | 963 | case AGGR_SOCKET: |
| 2209 | if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { | 964 | if (perf_env__build_socket_map(env, evsel_list->cpus, &stat_config.aggr_map)) { |
| 2210 | perror("cannot build socket map"); | 965 | perror("cannot build socket map"); |
| 2211 | return -1; | 966 | return -1; |
| 2212 | } | 967 | } |
| 2213 | aggr_get_id = perf_stat__get_socket_file; | 968 | stat_config.aggr_get_id = perf_stat__get_socket_file; |
| 2214 | break; | 969 | break; |
| 2215 | case AGGR_CORE: | 970 | case AGGR_CORE: |
| 2216 | if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { | 971 | if (perf_env__build_core_map(env, evsel_list->cpus, &stat_config.aggr_map)) { |
| 2217 | perror("cannot build core map"); | 972 | perror("cannot build core map"); |
| 2218 | return -1; | 973 | return -1; |
| 2219 | } | 974 | } |
| 2220 | aggr_get_id = perf_stat__get_core_file; | 975 | stat_config.aggr_get_id = perf_stat__get_core_file; |
| 2221 | break; | 976 | break; |
| 2222 | case AGGR_NONE: | 977 | case AGGR_NONE: |
| 2223 | case AGGR_GLOBAL: | 978 | case AGGR_GLOBAL: |
| @@ -2401,7 +1156,7 @@ static int add_default_attributes(void) | |||
| 2401 | struct parse_events_error errinfo; | 1156 | struct parse_events_error errinfo; |
| 2402 | 1157 | ||
| 2403 | /* Set attrs if no event is selected and !null_run: */ | 1158 | /* Set attrs if no event is selected and !null_run: */ |
| 2404 | if (null_run) | 1159 | if (stat_config.null_run) |
| 2405 | return 0; | 1160 | return 0; |
| 2406 | 1161 | ||
| 2407 | if (transaction_run) { | 1162 | if (transaction_run) { |
| @@ -2414,7 +1169,7 @@ static int add_default_attributes(void) | |||
| 2414 | struct option opt = { .value = &evsel_list }; | 1169 | struct option opt = { .value = &evsel_list }; |
| 2415 | 1170 | ||
| 2416 | return metricgroup__parse_groups(&opt, "transaction", | 1171 | return metricgroup__parse_groups(&opt, "transaction", |
| 2417 | &metric_events); | 1172 | &stat_config.metric_events); |
| 2418 | } | 1173 | } |
| 2419 | 1174 | ||
| 2420 | if (pmu_have_event("cpu", "cycles-ct") && | 1175 | if (pmu_have_event("cpu", "cycles-ct") && |
| @@ -2452,7 +1207,7 @@ static int add_default_attributes(void) | |||
| 2452 | if (pmu_have_event("msr", "aperf") && | 1207 | if (pmu_have_event("msr", "aperf") && |
| 2453 | pmu_have_event("msr", "smi")) { | 1208 | pmu_have_event("msr", "smi")) { |
| 2454 | if (!force_metric_only) | 1209 | if (!force_metric_only) |
| 2455 | metric_only = true; | 1210 | stat_config.metric_only = true; |
| 2456 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); | 1211 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); |
| 2457 | } else { | 1212 | } else { |
| 2458 | fprintf(stderr, "To measure SMI cost, it needs " | 1213 | fprintf(stderr, "To measure SMI cost, it needs " |
| @@ -2483,7 +1238,7 @@ static int add_default_attributes(void) | |||
| 2483 | } | 1238 | } |
| 2484 | 1239 | ||
| 2485 | if (!force_metric_only) | 1240 | if (!force_metric_only) |
| 2486 | metric_only = true; | 1241 | stat_config.metric_only = true; |
| 2487 | if (topdown_filter_events(topdown_attrs, &str, | 1242 | if (topdown_filter_events(topdown_attrs, &str, |
| 2488 | arch_topdown_check_group(&warn)) < 0) { | 1243 | arch_topdown_check_group(&warn)) < 0) { |
| 2489 | pr_err("Out of memory\n"); | 1244 | pr_err("Out of memory\n"); |
| @@ -2580,7 +1335,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 2580 | if (output_name) | 1335 | if (output_name) |
| 2581 | data->file.path = output_name; | 1336 | data->file.path = output_name; |
| 2582 | 1337 | ||
| 2583 | if (run_count != 1 || forever) { | 1338 | if (stat_config.run_count != 1 || forever) { |
| 2584 | pr_err("Cannot use -r option with perf stat record.\n"); | 1339 | pr_err("Cannot use -r option with perf stat record.\n"); |
| 2585 | return -1; | 1340 | return -1; |
| 2586 | } | 1341 | } |
| @@ -2853,12 +1608,12 @@ int cmd_stat(int argc, const char **argv) | |||
| 2853 | perf_stat__collect_metric_expr(evsel_list); | 1608 | perf_stat__collect_metric_expr(evsel_list); |
| 2854 | perf_stat__init_shadow_stats(); | 1609 | perf_stat__init_shadow_stats(); |
| 2855 | 1610 | ||
| 2856 | if (csv_sep) { | 1611 | if (stat_config.csv_sep) { |
| 2857 | csv_output = true; | 1612 | stat_config.csv_output = true; |
| 2858 | if (!strcmp(csv_sep, "\\t")) | 1613 | if (!strcmp(stat_config.csv_sep, "\\t")) |
| 2859 | csv_sep = "\t"; | 1614 | stat_config.csv_sep = "\t"; |
| 2860 | } else | 1615 | } else |
| 2861 | csv_sep = DEFAULT_SEPARATOR; | 1616 | stat_config.csv_sep = DEFAULT_SEPARATOR; |
| 2862 | 1617 | ||
| 2863 | if (argc && !strncmp(argv[0], "rec", 3)) { | 1618 | if (argc && !strncmp(argv[0], "rec", 3)) { |
| 2864 | argc = __cmd_record(argc, argv); | 1619 | argc = __cmd_record(argc, argv); |
| @@ -2883,17 +1638,17 @@ int cmd_stat(int argc, const char **argv) | |||
| 2883 | goto out; | 1638 | goto out; |
| 2884 | } | 1639 | } |
| 2885 | 1640 | ||
| 2886 | if (metric_only && stat_config.aggr_mode == AGGR_THREAD) { | 1641 | if (stat_config.metric_only && stat_config.aggr_mode == AGGR_THREAD) { |
| 2887 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); | 1642 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); |
| 2888 | goto out; | 1643 | goto out; |
| 2889 | } | 1644 | } |
| 2890 | 1645 | ||
| 2891 | if (metric_only && run_count > 1) { | 1646 | if (stat_config.metric_only && stat_config.run_count > 1) { |
| 2892 | fprintf(stderr, "--metric-only is not supported with -r\n"); | 1647 | fprintf(stderr, "--metric-only is not supported with -r\n"); |
| 2893 | goto out; | 1648 | goto out; |
| 2894 | } | 1649 | } |
| 2895 | 1650 | ||
| 2896 | if (walltime_run_table && run_count <= 1) { | 1651 | if (stat_config.walltime_run_table && stat_config.run_count <= 1) { |
| 2897 | fprintf(stderr, "--table is only supported with -r\n"); | 1652 | fprintf(stderr, "--table is only supported with -r\n"); |
| 2898 | parse_options_usage(stat_usage, stat_options, "r", 1); | 1653 | parse_options_usage(stat_usage, stat_options, "r", 1); |
| 2899 | parse_options_usage(NULL, stat_options, "table", 0); | 1654 | parse_options_usage(NULL, stat_options, "table", 0); |
| @@ -2931,7 +1686,7 @@ int cmd_stat(int argc, const char **argv) | |||
| 2931 | /* | 1686 | /* |
| 2932 | * let the spreadsheet do the pretty-printing | 1687 | * let the spreadsheet do the pretty-printing |
| 2933 | */ | 1688 | */ |
| 2934 | if (csv_output) { | 1689 | if (stat_config.csv_output) { |
| 2935 | /* User explicitly passed -B? */ | 1690 | /* User explicitly passed -B? */ |
| 2936 | if (big_num_opt == 1) { | 1691 | if (big_num_opt == 1) { |
| 2937 | fprintf(stderr, "-B option not supported with -x\n"); | 1692 | fprintf(stderr, "-B option not supported with -x\n"); |
| @@ -2939,9 +1694,9 @@ int cmd_stat(int argc, const char **argv) | |||
| 2939 | parse_options_usage(NULL, stat_options, "x", 1); | 1694 | parse_options_usage(NULL, stat_options, "x", 1); |
| 2940 | goto out; | 1695 | goto out; |
| 2941 | } else /* Nope, so disable big number formatting */ | 1696 | } else /* Nope, so disable big number formatting */ |
| 2942 | big_num = false; | 1697 | stat_config.big_num = false; |
| 2943 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | 1698 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
| 2944 | big_num = false; | 1699 | stat_config.big_num = false; |
| 2945 | 1700 | ||
| 2946 | setup_system_wide(argc); | 1701 | setup_system_wide(argc); |
| 2947 | 1702 | ||
| @@ -2949,21 +1704,21 @@ int cmd_stat(int argc, const char **argv) | |||
| 2949 | * Display user/system times only for single | 1704 | * Display user/system times only for single |
| 2950 | * run and when there's specified tracee. | 1705 | * run and when there's specified tracee. |
| 2951 | */ | 1706 | */ |
| 2952 | if ((run_count == 1) && target__none(&target)) | 1707 | if ((stat_config.run_count == 1) && target__none(&target)) |
| 2953 | ru_display = true; | 1708 | stat_config.ru_display = true; |
| 2954 | 1709 | ||
| 2955 | if (run_count < 0) { | 1710 | if (stat_config.run_count < 0) { |
| 2956 | pr_err("Run count must be a positive number\n"); | 1711 | pr_err("Run count must be a positive number\n"); |
| 2957 | parse_options_usage(stat_usage, stat_options, "r", 1); | 1712 | parse_options_usage(stat_usage, stat_options, "r", 1); |
| 2958 | goto out; | 1713 | goto out; |
| 2959 | } else if (run_count == 0) { | 1714 | } else if (stat_config.run_count == 0) { |
| 2960 | forever = true; | 1715 | forever = true; |
| 2961 | run_count = 1; | 1716 | stat_config.run_count = 1; |
| 2962 | } | 1717 | } |
| 2963 | 1718 | ||
| 2964 | if (walltime_run_table) { | 1719 | if (stat_config.walltime_run_table) { |
| 2965 | walltime_run = zalloc(run_count * sizeof(walltime_run[0])); | 1720 | stat_config.walltime_run = zalloc(stat_config.run_count * sizeof(stat_config.walltime_run[0])); |
| 2966 | if (!walltime_run) { | 1721 | if (!stat_config.walltime_run) { |
| 2967 | pr_err("failed to setup -r option"); | 1722 | pr_err("failed to setup -r option"); |
| 2968 | goto out; | 1723 | goto out; |
| 2969 | } | 1724 | } |
| @@ -3066,6 +1821,17 @@ int cmd_stat(int argc, const char **argv) | |||
| 3066 | goto out; | 1821 | goto out; |
| 3067 | 1822 | ||
| 3068 | /* | 1823 | /* |
| 1824 | * Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless | ||
| 1825 | * while avoiding that older tools show confusing messages. | ||
| 1826 | * | ||
| 1827 | * However for pipe sessions we need to keep it zero, | ||
| 1828 | * because script's perf_evsel__check_attr is triggered | ||
| 1829 | * by attr->sample_type != 0, and we can't run it on | ||
| 1830 | * stat sessions. | ||
| 1831 | */ | ||
| 1832 | stat_config.identifier = !(STAT_RECORD && perf_stat.data.is_pipe); | ||
| 1833 | |||
| 1834 | /* | ||
| 3069 | * We dont want to block the signals - that would cause | 1835 | * We dont want to block the signals - that would cause |
| 3070 | * child tasks to inherit that and Ctrl-C would not work. | 1836 | * child tasks to inherit that and Ctrl-C would not work. |
| 3071 | * What we want is for Ctrl-C to work in the exec()-ed | 1837 | * What we want is for Ctrl-C to work in the exec()-ed |
| @@ -3079,8 +1845,8 @@ int cmd_stat(int argc, const char **argv) | |||
| 3079 | signal(SIGABRT, skip_signal); | 1845 | signal(SIGABRT, skip_signal); |
| 3080 | 1846 | ||
| 3081 | status = 0; | 1847 | status = 0; |
| 3082 | for (run_idx = 0; forever || run_idx < run_count; run_idx++) { | 1848 | for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) { |
| 3083 | if (run_count != 1 && verbose > 0) | 1849 | if (stat_config.run_count != 1 && verbose > 0) |
| 3084 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1850 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
| 3085 | run_idx + 1); | 1851 | run_idx + 1); |
| 3086 | 1852 | ||
| @@ -3132,7 +1898,7 @@ int cmd_stat(int argc, const char **argv) | |||
| 3132 | perf_stat__exit_aggr_mode(); | 1898 | perf_stat__exit_aggr_mode(); |
| 3133 | perf_evlist__free_stats(evsel_list); | 1899 | perf_evlist__free_stats(evsel_list); |
| 3134 | out: | 1900 | out: |
| 3135 | free(walltime_run); | 1901 | free(stat_config.walltime_run); |
| 3136 | 1902 | ||
| 3137 | if (smi_cost && smi_reset) | 1903 | if (smi_cost && smi_reset) |
| 3138 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); | 1904 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 22ab8e67c760..7ce277d22a91 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -288,6 +288,13 @@ static int perf_evsel__init_augmented_syscall_tp_args(struct perf_evsel *evsel) | |||
| 288 | return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); | 288 | return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | static int perf_evsel__init_augmented_syscall_tp_ret(struct perf_evsel *evsel) | ||
| 292 | { | ||
| 293 | struct syscall_tp *sc = evsel->priv; | ||
| 294 | |||
| 295 | return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap); | ||
| 296 | } | ||
| 297 | |||
| 291 | static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler) | 298 | static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler) |
| 292 | { | 299 | { |
| 293 | evsel->priv = malloc(sizeof(struct syscall_tp)); | 300 | evsel->priv = malloc(sizeof(struct syscall_tp)); |
| @@ -498,16 +505,6 @@ static const char *clockid[] = { | |||
| 498 | }; | 505 | }; |
| 499 | static DEFINE_STRARRAY(clockid); | 506 | static DEFINE_STRARRAY(clockid); |
| 500 | 507 | ||
| 501 | static const char *socket_families[] = { | ||
| 502 | "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", | ||
| 503 | "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", | ||
| 504 | "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", | ||
| 505 | "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", | ||
| 506 | "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", | ||
| 507 | "ALG", "NFC", "VSOCK", | ||
| 508 | }; | ||
| 509 | static DEFINE_STRARRAY(socket_families); | ||
| 510 | |||
| 511 | static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, | 508 | static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, |
| 512 | struct syscall_arg *arg) | 509 | struct syscall_arg *arg) |
| 513 | { | 510 | { |
| @@ -631,6 +628,8 @@ static struct syscall_fmt { | |||
| 631 | } syscall_fmts[] = { | 628 | } syscall_fmts[] = { |
| 632 | { .name = "access", | 629 | { .name = "access", |
| 633 | .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, | 630 | .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, |
| 631 | { .name = "bind", | ||
| 632 | .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ }, }, }, | ||
| 634 | { .name = "bpf", | 633 | { .name = "bpf", |
| 635 | .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, | 634 | .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, |
| 636 | { .name = "brk", .hexret = true, | 635 | { .name = "brk", .hexret = true, |
| @@ -645,6 +644,8 @@ static struct syscall_fmt { | |||
| 645 | [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, | 644 | [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, |
| 646 | { .name = "close", | 645 | { .name = "close", |
| 647 | .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, | 646 | .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, |
| 647 | { .name = "connect", | ||
| 648 | .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ }, }, }, | ||
| 648 | { .name = "epoll_ctl", | 649 | { .name = "epoll_ctl", |
| 649 | .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, | 650 | .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, |
| 650 | { .name = "eventfd2", | 651 | { .name = "eventfd2", |
| @@ -801,7 +802,8 @@ static struct syscall_fmt { | |||
| 801 | { .name = "sendmsg", | 802 | { .name = "sendmsg", |
| 802 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, | 803 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
| 803 | { .name = "sendto", | 804 | { .name = "sendto", |
| 804 | .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, | 805 | .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, |
| 806 | [4] = { .scnprintf = SCA_SOCKADDR, /* addr */ }, }, }, | ||
| 805 | { .name = "set_tid_address", .errpid = true, }, | 807 | { .name = "set_tid_address", .errpid = true, }, |
| 806 | { .name = "setitimer", | 808 | { .name = "setitimer", |
| 807 | .arg = { [0] = STRARRAY(which, itimers), }, }, | 809 | .arg = { [0] = STRARRAY(which, itimers), }, }, |
| @@ -830,6 +832,7 @@ static struct syscall_fmt { | |||
| 830 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 832 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
| 831 | { .name = "tkill", | 833 | { .name = "tkill", |
| 832 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 834 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
| 835 | { .name = "umount2", .alias = "umount", }, | ||
| 833 | { .name = "uname", .alias = "newuname", }, | 836 | { .name = "uname", .alias = "newuname", }, |
| 834 | { .name = "unlinkat", | 837 | { .name = "unlinkat", |
| 835 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, | 838 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
| @@ -856,10 +859,12 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) | |||
| 856 | /* | 859 | /* |
| 857 | * is_exit: is this "exit" or "exit_group"? | 860 | * is_exit: is this "exit" or "exit_group"? |
| 858 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. | 861 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. |
| 862 | * args_size: sum of the sizes of the syscall arguments, anything after that is augmented stuff: pathname for openat, etc. | ||
| 859 | */ | 863 | */ |
| 860 | struct syscall { | 864 | struct syscall { |
| 861 | struct event_format *tp_format; | 865 | struct event_format *tp_format; |
| 862 | int nr_args; | 866 | int nr_args; |
| 867 | int args_size; | ||
| 863 | bool is_exit; | 868 | bool is_exit; |
| 864 | bool is_open; | 869 | bool is_open; |
| 865 | struct format_field *args; | 870 | struct format_field *args; |
| @@ -1095,11 +1100,21 @@ static void thread__set_filename_pos(struct thread *thread, const char *bf, | |||
| 1095 | ttrace->filename.entry_str_pos = bf - ttrace->entry_str; | 1100 | ttrace->filename.entry_str_pos = bf - ttrace->entry_str; |
| 1096 | } | 1101 | } |
| 1097 | 1102 | ||
| 1103 | static size_t syscall_arg__scnprintf_augmented_string(struct syscall_arg *arg, char *bf, size_t size) | ||
| 1104 | { | ||
| 1105 | struct augmented_arg *augmented_arg = arg->augmented.args; | ||
| 1106 | |||
| 1107 | return scnprintf(bf, size, "%.*s", augmented_arg->size, augmented_arg->value); | ||
| 1108 | } | ||
| 1109 | |||
| 1098 | static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, | 1110 | static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, |
| 1099 | struct syscall_arg *arg) | 1111 | struct syscall_arg *arg) |
| 1100 | { | 1112 | { |
| 1101 | unsigned long ptr = arg->val; | 1113 | unsigned long ptr = arg->val; |
| 1102 | 1114 | ||
| 1115 | if (arg->augmented.args) | ||
| 1116 | return syscall_arg__scnprintf_augmented_string(arg, bf, size); | ||
| 1117 | |||
| 1103 | if (!arg->trace->vfs_getname) | 1118 | if (!arg->trace->vfs_getname) |
| 1104 | return scnprintf(bf, size, "%#x", ptr); | 1119 | return scnprintf(bf, size, "%#x", ptr); |
| 1105 | 1120 | ||
| @@ -1142,11 +1157,9 @@ static void sig_handler(int sig) | |||
| 1142 | interrupted = sig == SIGINT; | 1157 | interrupted = sig == SIGINT; |
| 1143 | } | 1158 | } |
| 1144 | 1159 | ||
| 1145 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | 1160 | static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread, FILE *fp) |
| 1146 | u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) | ||
| 1147 | { | 1161 | { |
| 1148 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | 1162 | size_t printed = 0; |
| 1149 | printed += fprintf_duration(duration, duration_calculated, fp); | ||
| 1150 | 1163 | ||
| 1151 | if (trace->multiple_threads) { | 1164 | if (trace->multiple_threads) { |
| 1152 | if (trace->show_comm) | 1165 | if (trace->show_comm) |
| @@ -1157,6 +1170,14 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre | |||
| 1157 | return printed; | 1170 | return printed; |
| 1158 | } | 1171 | } |
| 1159 | 1172 | ||
| 1173 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
| 1174 | u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) | ||
| 1175 | { | ||
| 1176 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
| 1177 | printed += fprintf_duration(duration, duration_calculated, fp); | ||
| 1178 | return printed + trace__fprintf_comm_tid(trace, thread, fp); | ||
| 1179 | } | ||
| 1180 | |||
| 1160 | static int trace__process_event(struct trace *trace, struct machine *machine, | 1181 | static int trace__process_event(struct trace *trace, struct machine *machine, |
| 1161 | union perf_event *event, struct perf_sample *sample) | 1182 | union perf_event *event, struct perf_sample *sample) |
| 1162 | { | 1183 | { |
| @@ -1258,10 +1279,12 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args) | |||
| 1258 | 1279 | ||
| 1259 | static int syscall__set_arg_fmts(struct syscall *sc) | 1280 | static int syscall__set_arg_fmts(struct syscall *sc) |
| 1260 | { | 1281 | { |
| 1261 | struct format_field *field; | 1282 | struct format_field *field, *last_field = NULL; |
| 1262 | int idx = 0, len; | 1283 | int idx = 0, len; |
| 1263 | 1284 | ||
| 1264 | for (field = sc->args; field; field = field->next, ++idx) { | 1285 | for (field = sc->args; field; field = field->next, ++idx) { |
| 1286 | last_field = field; | ||
| 1287 | |||
| 1265 | if (sc->fmt && sc->fmt->arg[idx].scnprintf) | 1288 | if (sc->fmt && sc->fmt->arg[idx].scnprintf) |
| 1266 | continue; | 1289 | continue; |
| 1267 | 1290 | ||
| @@ -1292,6 +1315,9 @@ static int syscall__set_arg_fmts(struct syscall *sc) | |||
| 1292 | } | 1315 | } |
| 1293 | } | 1316 | } |
| 1294 | 1317 | ||
| 1318 | if (last_field) | ||
| 1319 | sc->args_size = last_field->offset + last_field->size; | ||
| 1320 | |||
| 1295 | return 0; | 1321 | return 0; |
| 1296 | } | 1322 | } |
| 1297 | 1323 | ||
| @@ -1472,14 +1498,18 @@ static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, | |||
| 1472 | } | 1498 | } |
| 1473 | 1499 | ||
| 1474 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | 1500 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
| 1475 | unsigned char *args, struct trace *trace, | 1501 | unsigned char *args, void *augmented_args, int augmented_args_size, |
| 1476 | struct thread *thread) | 1502 | struct trace *trace, struct thread *thread) |
| 1477 | { | 1503 | { |
| 1478 | size_t printed = 0; | 1504 | size_t printed = 0; |
| 1479 | unsigned long val; | 1505 | unsigned long val; |
| 1480 | u8 bit = 1; | 1506 | u8 bit = 1; |
| 1481 | struct syscall_arg arg = { | 1507 | struct syscall_arg arg = { |
| 1482 | .args = args, | 1508 | .args = args, |
| 1509 | .augmented = { | ||
| 1510 | .size = augmented_args_size, | ||
| 1511 | .args = augmented_args, | ||
| 1512 | }, | ||
| 1483 | .idx = 0, | 1513 | .idx = 0, |
| 1484 | .mask = 0, | 1514 | .mask = 0, |
| 1485 | .trace = trace, | 1515 | .trace = trace, |
| @@ -1654,6 +1684,17 @@ static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, | |||
| 1654 | return printed; | 1684 | return printed; |
| 1655 | } | 1685 | } |
| 1656 | 1686 | ||
| 1687 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size) | ||
| 1688 | { | ||
| 1689 | void *augmented_args = NULL; | ||
| 1690 | |||
| 1691 | *augmented_args_size = sample->raw_size - sc->args_size; | ||
| 1692 | if (*augmented_args_size > 0) | ||
| 1693 | augmented_args = sample->raw_data + sc->args_size; | ||
| 1694 | |||
| 1695 | return augmented_args; | ||
| 1696 | } | ||
| 1697 | |||
| 1657 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 1698 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
| 1658 | union perf_event *event __maybe_unused, | 1699 | union perf_event *event __maybe_unused, |
| 1659 | struct perf_sample *sample) | 1700 | struct perf_sample *sample) |
| @@ -1663,6 +1704,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
| 1663 | size_t printed = 0; | 1704 | size_t printed = 0; |
| 1664 | struct thread *thread; | 1705 | struct thread *thread; |
| 1665 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; | 1706 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
| 1707 | int augmented_args_size = 0; | ||
| 1708 | void *augmented_args = NULL; | ||
| 1666 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | 1709 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
| 1667 | struct thread_trace *ttrace; | 1710 | struct thread_trace *ttrace; |
| 1668 | 1711 | ||
| @@ -1686,13 +1729,24 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
| 1686 | 1729 | ||
| 1687 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) | 1730 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) |
| 1688 | trace__printf_interrupted_entry(trace); | 1731 | trace__printf_interrupted_entry(trace); |
| 1689 | 1732 | /* | |
| 1733 | * If this is raw_syscalls.sys_enter, then it always comes with the 6 possible | ||
| 1734 | * arguments, even if the syscall being handled, say "openat", uses only 4 arguments | ||
| 1735 | * this breaks syscall__augmented_args() check for augmented args, as we calculate | ||
| 1736 | * syscall->args_size using each syscalls:sys_enter_NAME tracefs format file, | ||
| 1737 | * so when handling, say the openat syscall, we end up getting 6 args for the | ||
| 1738 | * raw_syscalls:sys_enter event, when we expected just 4, we end up mistakenly | ||
| 1739 | * thinking that the extra 2 u64 args are the augmented filename, so just check | ||
| 1740 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. | ||
| 1741 | */ | ||
| 1742 | if (evsel != trace->syscalls.events.sys_enter) | ||
| 1743 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size); | ||
| 1690 | ttrace->entry_time = sample->time; | 1744 | ttrace->entry_time = sample->time; |
| 1691 | msg = ttrace->entry_str; | 1745 | msg = ttrace->entry_str; |
| 1692 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); | 1746 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); |
| 1693 | 1747 | ||
| 1694 | printed += syscall__scnprintf_args(sc, msg + printed, trace__entry_str_size - printed, | 1748 | printed += syscall__scnprintf_args(sc, msg + printed, trace__entry_str_size - printed, |
| 1695 | args, trace, thread); | 1749 | args, augmented_args, augmented_args_size, trace, thread); |
| 1696 | 1750 | ||
| 1697 | if (sc->is_exit) { | 1751 | if (sc->is_exit) { |
| 1698 | if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { | 1752 | if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { |
| @@ -1723,7 +1777,8 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
| 1723 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; | 1777 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
| 1724 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | 1778 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
| 1725 | char msg[1024]; | 1779 | char msg[1024]; |
| 1726 | void *args; | 1780 | void *args, *augmented_args = NULL; |
| 1781 | int augmented_args_size; | ||
| 1727 | 1782 | ||
| 1728 | if (sc == NULL) | 1783 | if (sc == NULL) |
| 1729 | return -1; | 1784 | return -1; |
| @@ -1738,7 +1793,8 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
| 1738 | goto out_put; | 1793 | goto out_put; |
| 1739 | 1794 | ||
| 1740 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1795 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
| 1741 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, trace, thread); | 1796 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size); |
| 1797 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); | ||
| 1742 | fprintf(trace->output, "%s", msg); | 1798 | fprintf(trace->output, "%s", msg); |
| 1743 | err = 0; | 1799 | err = 0; |
| 1744 | out_put: | 1800 | out_put: |
| @@ -2022,6 +2078,7 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
| 2022 | union perf_event *event __maybe_unused, | 2078 | union perf_event *event __maybe_unused, |
| 2023 | struct perf_sample *sample) | 2079 | struct perf_sample *sample) |
| 2024 | { | 2080 | { |
| 2081 | struct thread *thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); | ||
| 2025 | int callchain_ret = 0; | 2082 | int callchain_ret = 0; |
| 2026 | 2083 | ||
| 2027 | if (sample->callchain) { | 2084 | if (sample->callchain) { |
| @@ -2039,13 +2096,31 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
| 2039 | if (trace->trace_syscalls) | 2096 | if (trace->trace_syscalls) |
| 2040 | fprintf(trace->output, "( ): "); | 2097 | fprintf(trace->output, "( ): "); |
| 2041 | 2098 | ||
| 2099 | if (thread) | ||
| 2100 | trace__fprintf_comm_tid(trace, thread, trace->output); | ||
| 2101 | |||
| 2102 | if (evsel == trace->syscalls.events.augmented) { | ||
| 2103 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); | ||
| 2104 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | ||
| 2105 | |||
| 2106 | if (sc) { | ||
| 2107 | fprintf(trace->output, "%s(", sc->name); | ||
| 2108 | trace__fprintf_sys_enter(trace, evsel, sample); | ||
| 2109 | fputc(')', trace->output); | ||
| 2110 | goto newline; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | /* | ||
| 2114 | * XXX: Not having the associated syscall info or not finding/adding | ||
| 2115 | * the thread should never happen, but if it does... | ||
| 2116 | * fall thru and print it as a bpf_output event. | ||
| 2117 | */ | ||
| 2118 | } | ||
| 2119 | |||
| 2042 | fprintf(trace->output, "%s:", evsel->name); | 2120 | fprintf(trace->output, "%s:", evsel->name); |
| 2043 | 2121 | ||
| 2044 | if (perf_evsel__is_bpf_output(evsel)) { | 2122 | if (perf_evsel__is_bpf_output(evsel)) { |
| 2045 | if (evsel == trace->syscalls.events.augmented) | 2123 | bpf_output__fprintf(trace, sample); |
| 2046 | trace__fprintf_sys_enter(trace, evsel, sample); | ||
| 2047 | else | ||
| 2048 | bpf_output__fprintf(trace, sample); | ||
| 2049 | } else if (evsel->tp_format) { | 2124 | } else if (evsel->tp_format) { |
| 2050 | if (strncmp(evsel->tp_format->name, "sys_enter_", 10) || | 2125 | if (strncmp(evsel->tp_format->name, "sys_enter_", 10) || |
| 2051 | trace__fprintf_sys_enter(trace, evsel, sample)) { | 2126 | trace__fprintf_sys_enter(trace, evsel, sample)) { |
| @@ -2055,12 +2130,14 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
| 2055 | } | 2130 | } |
| 2056 | } | 2131 | } |
| 2057 | 2132 | ||
| 2133 | newline: | ||
| 2058 | fprintf(trace->output, "\n"); | 2134 | fprintf(trace->output, "\n"); |
| 2059 | 2135 | ||
| 2060 | if (callchain_ret > 0) | 2136 | if (callchain_ret > 0) |
| 2061 | trace__fprintf_callchain(trace, sample); | 2137 | trace__fprintf_callchain(trace, sample); |
| 2062 | else if (callchain_ret < 0) | 2138 | else if (callchain_ret < 0) |
| 2063 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | 2139 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
| 2140 | thread__put(thread); | ||
| 2064 | out: | 2141 | out: |
| 2065 | return 0; | 2142 | return 0; |
| 2066 | } | 2143 | } |
| @@ -3276,12 +3353,8 @@ int cmd_trace(int argc, const char **argv) | |||
| 3276 | goto out; | 3353 | goto out; |
| 3277 | } | 3354 | } |
| 3278 | 3355 | ||
| 3279 | if (evsel) { | 3356 | if (evsel) |
| 3280 | if (perf_evsel__init_augmented_syscall_tp(evsel) || | ||
| 3281 | perf_evsel__init_augmented_syscall_tp_args(evsel)) | ||
| 3282 | goto out; | ||
| 3283 | trace.syscalls.events.augmented = evsel; | 3357 | trace.syscalls.events.augmented = evsel; |
| 3284 | } | ||
| 3285 | 3358 | ||
| 3286 | err = bpf__setup_stdout(trace.evlist); | 3359 | err = bpf__setup_stdout(trace.evlist); |
| 3287 | if (err) { | 3360 | if (err) { |
| @@ -3326,6 +3399,34 @@ int cmd_trace(int argc, const char **argv) | |||
| 3326 | } | 3399 | } |
| 3327 | } | 3400 | } |
| 3328 | 3401 | ||
| 3402 | /* | ||
| 3403 | * If we are augmenting syscalls, then combine what we put in the | ||
| 3404 | * __augmented_syscalls__ BPF map with what is in the | ||
| 3405 | * syscalls:sys_exit_FOO tracepoints, i.e. just like we do without BPF, | ||
| 3406 | * combining raw_syscalls:sys_enter with raw_syscalls:sys_exit. | ||
| 3407 | * | ||
| 3408 | * We'll switch to look at two BPF maps, one for sys_enter and the | ||
| 3409 | * other for sys_exit when we start augmenting the sys_exit paths with | ||
| 3410 | * buffers that are being copied from kernel to userspace, think 'read' | ||
| 3411 | * syscall. | ||
| 3412 | */ | ||
| 3413 | if (trace.syscalls.events.augmented) { | ||
| 3414 | evsel = trace.syscalls.events.augmented; | ||
| 3415 | |||
| 3416 | if (perf_evsel__init_augmented_syscall_tp(evsel) || | ||
| 3417 | perf_evsel__init_augmented_syscall_tp_args(evsel)) | ||
| 3418 | goto out; | ||
| 3419 | evsel->handler = trace__sys_enter; | ||
| 3420 | |||
| 3421 | evlist__for_each_entry(trace.evlist, evsel) { | ||
| 3422 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { | ||
| 3423 | perf_evsel__init_augmented_syscall_tp(evsel); | ||
| 3424 | perf_evsel__init_augmented_syscall_tp_ret(evsel); | ||
| 3425 | evsel->handler = trace__sys_exit; | ||
| 3426 | } | ||
| 3427 | } | ||
| 3428 | } | ||
| 3429 | |||
| 3329 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) | 3430 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) |
| 3330 | return trace__record(&trace, argc-1, &argv[1]); | 3431 | return trace__record(&trace, argc-1, &argv[1]); |
| 3331 | 3432 | ||
diff --git a/tools/perf/examples/bpf/augmented_syscalls.c b/tools/perf/examples/bpf/augmented_syscalls.c index 69a31386d8cd..2ae44813ef2d 100644 --- a/tools/perf/examples/bpf/augmented_syscalls.c +++ b/tools/perf/examples/bpf/augmented_syscalls.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | 2 | /* |
| 3 | * Augment the openat syscall with the contents of the filename pointer argument. | 3 | * Augment syscalls with the contents of the pointer arguments. |
| 4 | * | 4 | * |
| 5 | * Test it with: | 5 | * Test it with: |
| 6 | * | 6 | * |
| @@ -10,15 +10,14 @@ | |||
| 10 | * the last one should be the one for '/etc/passwd'. | 10 | * the last one should be the one for '/etc/passwd'. |
| 11 | * | 11 | * |
| 12 | * This matches what is marshalled into the raw_syscall:sys_enter payload | 12 | * This matches what is marshalled into the raw_syscall:sys_enter payload |
| 13 | * expected by the 'perf trace' beautifiers, and can be used by them unmodified, | 13 | * expected by the 'perf trace' beautifiers, and can be used by them, that will |
| 14 | * which will be done as that feature is implemented in the next csets, for now | 14 | * check if perf_sample->raw_data is more than what is expected for each |
| 15 | * it will appear in a dump done by the default tracepoint handler in 'perf trace', | 15 | * syscalls:sys_{enter,exit}_SYSCALL tracepoint, uing the extra data as the |
| 16 | * that uses bpf_output__fprintf() to just dump those contents, as done with | 16 | * contents of pointer arguments. |
| 17 | * the bpf-output event associated with the __bpf_output__ map declared in | ||
| 18 | * tools/perf/include/bpf/stdio.h. | ||
| 19 | */ | 17 | */ |
| 20 | 18 | ||
| 21 | #include <stdio.h> | 19 | #include <stdio.h> |
| 20 | #include <linux/socket.h> | ||
| 22 | 21 | ||
| 23 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | 22 | struct bpf_map SEC("maps") __augmented_syscalls__ = { |
| 24 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | 23 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, |
| @@ -27,6 +26,44 @@ struct bpf_map SEC("maps") __augmented_syscalls__ = { | |||
| 27 | .max_entries = __NR_CPUS__, | 26 | .max_entries = __NR_CPUS__, |
| 28 | }; | 27 | }; |
| 29 | 28 | ||
| 29 | struct syscall_exit_args { | ||
| 30 | unsigned long long common_tp_fields; | ||
| 31 | long syscall_nr; | ||
| 32 | long ret; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct augmented_filename { | ||
| 36 | unsigned int size; | ||
| 37 | int reserved; | ||
| 38 | char value[256]; | ||
| 39 | }; | ||
| 40 | |||
| 41 | #define augmented_filename_syscall(syscall) \ | ||
| 42 | struct augmented_enter_##syscall##_args { \ | ||
| 43 | struct syscall_enter_##syscall##_args args; \ | ||
| 44 | struct augmented_filename filename; \ | ||
| 45 | }; \ | ||
| 46 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
| 47 | { \ | ||
| 48 | struct augmented_enter_##syscall##_args augmented_args = { .filename.reserved = 0, }; \ | ||
| 49 | unsigned int len = sizeof(augmented_args); \ | ||
| 50 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
| 51 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, \ | ||
| 52 | sizeof(augmented_args.filename.value), \ | ||
| 53 | args->filename_ptr); \ | ||
| 54 | if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) { \ | ||
| 55 | len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size; \ | ||
| 56 | len &= sizeof(augmented_args.filename.value) - 1; \ | ||
| 57 | } \ | ||
| 58 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
| 59 | &augmented_args, len); \ | ||
| 60 | return 0; \ | ||
| 61 | } \ | ||
| 62 | int syscall_exit(syscall)(struct syscall_exit_args *args) \ | ||
| 63 | { \ | ||
| 64 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ \ | ||
| 65 | } | ||
| 66 | |||
| 30 | struct syscall_enter_openat_args { | 67 | struct syscall_enter_openat_args { |
| 31 | unsigned long long common_tp_fields; | 68 | unsigned long long common_tp_fields; |
| 32 | long syscall_nr; | 69 | long syscall_nr; |
| @@ -36,20 +73,101 @@ struct syscall_enter_openat_args { | |||
| 36 | long mode; | 73 | long mode; |
| 37 | }; | 74 | }; |
| 38 | 75 | ||
| 39 | struct augmented_enter_openat_args { | 76 | augmented_filename_syscall(openat); |
| 40 | struct syscall_enter_openat_args args; | 77 | |
| 41 | char filename[64]; | 78 | struct syscall_enter_open_args { |
| 79 | unsigned long long common_tp_fields; | ||
| 80 | long syscall_nr; | ||
| 81 | char *filename_ptr; | ||
| 82 | long flags; | ||
| 83 | long mode; | ||
| 84 | }; | ||
| 85 | |||
| 86 | augmented_filename_syscall(open); | ||
| 87 | |||
| 88 | struct syscall_enter_inotify_add_watch_args { | ||
| 89 | unsigned long long common_tp_fields; | ||
| 90 | long syscall_nr; | ||
| 91 | long fd; | ||
| 92 | char *filename_ptr; | ||
| 93 | long mask; | ||
| 94 | }; | ||
| 95 | |||
| 96 | augmented_filename_syscall(inotify_add_watch); | ||
| 97 | |||
| 98 | struct statbuf; | ||
| 99 | |||
| 100 | struct syscall_enter_newstat_args { | ||
| 101 | unsigned long long common_tp_fields; | ||
| 102 | long syscall_nr; | ||
| 103 | char *filename_ptr; | ||
| 104 | struct stat *statbuf; | ||
| 42 | }; | 105 | }; |
| 43 | 106 | ||
| 44 | int syscall_enter(openat)(struct syscall_enter_openat_args *args) | 107 | augmented_filename_syscall(newstat); |
| 45 | { | 108 | |
| 46 | struct augmented_enter_openat_args augmented_args; | 109 | #ifndef _K_SS_MAXSIZE |
| 110 | #define _K_SS_MAXSIZE 128 | ||
| 111 | #endif | ||
| 47 | 112 | ||
| 48 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); | 113 | #define augmented_sockaddr_syscall(syscall) \ |
| 49 | probe_read_str(&augmented_args.filename, sizeof(augmented_args.filename), args->filename_ptr); | 114 | struct augmented_enter_##syscall##_args { \ |
| 50 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, | 115 | struct syscall_enter_##syscall##_args args; \ |
| 51 | &augmented_args, sizeof(augmented_args)); | 116 | struct sockaddr_storage addr; \ |
| 52 | return 1; | 117 | }; \ |
| 118 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
| 119 | { \ | ||
| 120 | struct augmented_enter_##syscall##_args augmented_args; \ | ||
| 121 | unsigned long addrlen = sizeof(augmented_args.addr); \ | ||
| 122 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
| 123 | /* FIXME_CLANG_OPTIMIZATION_THAT_ACCESSES_USER_CONTROLLED_ADDRLEN_DESPITE_THIS_CHECK */ \ | ||
| 124 | /* if (addrlen > augmented_args.args.addrlen) */ \ | ||
| 125 | /* addrlen = augmented_args.args.addrlen; */ \ | ||
| 126 | /* */ \ | ||
| 127 | probe_read(&augmented_args.addr, addrlen, args->addr_ptr); \ | ||
| 128 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
| 129 | &augmented_args, \ | ||
| 130 | sizeof(augmented_args) - sizeof(augmented_args.addr) + addrlen); \ | ||
| 131 | return 0; \ | ||
| 132 | } \ | ||
| 133 | int syscall_exit(syscall)(struct syscall_exit_args *args) \ | ||
| 134 | { \ | ||
| 135 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ \ | ||
| 53 | } | 136 | } |
| 54 | 137 | ||
| 138 | struct sockaddr; | ||
| 139 | |||
| 140 | struct syscall_enter_bind_args { | ||
| 141 | unsigned long long common_tp_fields; | ||
| 142 | long syscall_nr; | ||
| 143 | long fd; | ||
| 144 | struct sockaddr *addr_ptr; | ||
| 145 | unsigned long addrlen; | ||
| 146 | }; | ||
| 147 | |||
| 148 | augmented_sockaddr_syscall(bind); | ||
| 149 | |||
| 150 | struct syscall_enter_connect_args { | ||
| 151 | unsigned long long common_tp_fields; | ||
| 152 | long syscall_nr; | ||
| 153 | long fd; | ||
| 154 | struct sockaddr *addr_ptr; | ||
| 155 | unsigned long addrlen; | ||
| 156 | }; | ||
| 157 | |||
| 158 | augmented_sockaddr_syscall(connect); | ||
| 159 | |||
| 160 | struct syscall_enter_sendto_args { | ||
| 161 | unsigned long long common_tp_fields; | ||
| 162 | long syscall_nr; | ||
| 163 | long fd; | ||
| 164 | void *buff; | ||
| 165 | long len; | ||
| 166 | unsigned long flags; | ||
| 167 | struct sockaddr *addr_ptr; | ||
| 168 | long addr_len; | ||
| 169 | }; | ||
| 170 | |||
| 171 | augmented_sockaddr_syscall(sendto); | ||
| 172 | |||
| 55 | license(GPL); | 173 | license(GPL); |
diff --git a/tools/perf/examples/bpf/etcsnoop.c b/tools/perf/examples/bpf/etcsnoop.c new file mode 100644 index 000000000000..b59e8812ee8c --- /dev/null +++ b/tools/perf/examples/bpf/etcsnoop.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Augment the filename syscalls with the contents of the filename pointer argument | ||
| 4 | * filtering only those that do not start with /etc/. | ||
| 5 | * | ||
| 6 | * Test it with: | ||
| 7 | * | ||
| 8 | * perf trace -e tools/perf/examples/bpf/augmented_syscalls.c cat /etc/passwd > /dev/null | ||
| 9 | * | ||
| 10 | * It'll catch some openat syscalls related to the dynamic linked and | ||
| 11 | * the last one should be the one for '/etc/passwd'. | ||
| 12 | * | ||
| 13 | * This matches what is marshalled into the raw_syscall:sys_enter payload | ||
| 14 | * expected by the 'perf trace' beautifiers, and can be used by them unmodified, | ||
| 15 | * which will be done as that feature is implemented in the next csets, for now | ||
| 16 | * it will appear in a dump done by the default tracepoint handler in 'perf trace', | ||
| 17 | * that uses bpf_output__fprintf() to just dump those contents, as done with | ||
| 18 | * the bpf-output event associated with the __bpf_output__ map declared in | ||
| 19 | * tools/perf/include/bpf/stdio.h. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | |||
| 24 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | ||
| 25 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | ||
| 26 | .key_size = sizeof(int), | ||
| 27 | .value_size = sizeof(u32), | ||
| 28 | .max_entries = __NR_CPUS__, | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct augmented_filename { | ||
| 32 | int size; | ||
| 33 | int reserved; | ||
| 34 | char value[64]; | ||
| 35 | }; | ||
| 36 | |||
| 37 | #define augmented_filename_syscall_enter(syscall) \ | ||
| 38 | struct augmented_enter_##syscall##_args { \ | ||
| 39 | struct syscall_enter_##syscall##_args args; \ | ||
| 40 | struct augmented_filename filename; \ | ||
| 41 | }; \ | ||
| 42 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
| 43 | { \ | ||
| 44 | char etc[6] = "/etc/"; \ | ||
| 45 | struct augmented_enter_##syscall##_args augmented_args = { .filename.reserved = 0, }; \ | ||
| 46 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
| 47 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, \ | ||
| 48 | sizeof(augmented_args.filename.value), \ | ||
| 49 | args->filename_ptr); \ | ||
| 50 | if (__builtin_memcmp(augmented_args.filename.value, etc, 4) != 0) \ | ||
| 51 | return 0; \ | ||
| 52 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
| 53 | &augmented_args, \ | ||
| 54 | (sizeof(augmented_args) - sizeof(augmented_args.filename.value) + \ | ||
| 55 | augmented_args.filename.size)); \ | ||
| 56 | return 0; \ | ||
| 57 | } | ||
| 58 | |||
| 59 | struct syscall_enter_openat_args { | ||
| 60 | unsigned long long common_tp_fields; | ||
| 61 | long syscall_nr; | ||
| 62 | long dfd; | ||
| 63 | char *filename_ptr; | ||
| 64 | long flags; | ||
| 65 | long mode; | ||
| 66 | }; | ||
| 67 | |||
| 68 | augmented_filename_syscall_enter(openat); | ||
| 69 | |||
| 70 | struct syscall_enter_open_args { | ||
| 71 | unsigned long long common_tp_fields; | ||
| 72 | long syscall_nr; | ||
| 73 | char *filename_ptr; | ||
| 74 | long flags; | ||
| 75 | long mode; | ||
| 76 | }; | ||
| 77 | |||
| 78 | augmented_filename_syscall_enter(open); | ||
| 79 | |||
| 80 | license(GPL); | ||
diff --git a/tools/perf/include/bpf/bpf.h b/tools/perf/include/bpf/bpf.h index 47897d65e799..52b6d87fe822 100644 --- a/tools/perf/include/bpf/bpf.h +++ b/tools/perf/include/bpf/bpf.h | |||
| @@ -26,6 +26,9 @@ struct bpf_map { | |||
| 26 | #define syscall_enter(name) \ | 26 | #define syscall_enter(name) \ |
| 27 | SEC("syscalls:sys_enter_" #name) syscall_enter_ ## name | 27 | SEC("syscalls:sys_enter_" #name) syscall_enter_ ## name |
| 28 | 28 | ||
| 29 | #define syscall_exit(name) \ | ||
| 30 | SEC("syscalls:sys_exit_" #name) syscall_exit_ ## name | ||
| 31 | |||
| 29 | #define license(name) \ | 32 | #define license(name) \ |
| 30 | char _license[] SEC("license") = #name; \ | 33 | char _license[] SEC("license") = #name; \ |
| 31 | int _version SEC("version") = LINUX_VERSION_CODE; | 34 | int _version SEC("version") = LINUX_VERSION_CODE; |
diff --git a/tools/perf/include/bpf/linux/socket.h b/tools/perf/include/bpf/linux/socket.h new file mode 100644 index 000000000000..7f844568dab8 --- /dev/null +++ b/tools/perf/include/bpf/linux/socket.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
| 2 | #ifndef _UAPI_LINUX_SOCKET_H | ||
| 3 | #define _UAPI_LINUX_SOCKET_H | ||
| 4 | |||
| 5 | /* | ||
| 6 | * Desired design of maximum size and alignment (see RFC2553) | ||
| 7 | */ | ||
| 8 | #define _K_SS_MAXSIZE 128 /* Implementation specific max size */ | ||
| 9 | #define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *)) | ||
| 10 | /* Implementation specific desired alignment */ | ||
| 11 | |||
| 12 | typedef unsigned short __kernel_sa_family_t; | ||
| 13 | |||
| 14 | struct __kernel_sockaddr_storage { | ||
| 15 | __kernel_sa_family_t ss_family; /* address family */ | ||
| 16 | /* Following field(s) are implementation specific */ | ||
| 17 | char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; | ||
| 18 | /* space to achieve desired size, */ | ||
| 19 | /* _SS_MAXSIZE value minus size of ss_family */ | ||
| 20 | } __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ | ||
| 21 | |||
| 22 | #define sockaddr_storage __kernel_sockaddr_storage | ||
| 23 | |||
| 24 | #endif /* _UAPI_LINUX_SOCKET_H */ | ||
diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 3013ac8f83d0..cab7b0aea6ea 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh | |||
| @@ -48,7 +48,7 @@ trace_libc_inet_pton_backtrace() { | |||
| 48 | *) | 48 | *) |
| 49 | eventattr='max-stack=3' | 49 | eventattr='max-stack=3' |
| 50 | echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected | 50 | echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected |
| 51 | echo ".*\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected | 51 | echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected |
| 52 | ;; | 52 | ;; |
| 53 | esac | 53 | esac |
| 54 | 54 | ||
diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index f528ba35e140..c3b0afd67760 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build | |||
| @@ -7,5 +7,6 @@ endif | |||
| 7 | libperf-y += kcmp.o | 7 | libperf-y += kcmp.o |
| 8 | libperf-y += pkey_alloc.o | 8 | libperf-y += pkey_alloc.o |
| 9 | libperf-y += prctl.o | 9 | libperf-y += prctl.o |
| 10 | libperf-y += sockaddr.o | ||
| 10 | libperf-y += socket.o | 11 | libperf-y += socket.o |
| 11 | libperf-y += statx.o | 12 | libperf-y += statx.o |
diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 9615af5d412b..2570152d3909 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h | |||
| @@ -30,9 +30,36 @@ struct thread; | |||
| 30 | 30 | ||
| 31 | size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_t size); | 31 | size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_t size); |
| 32 | 32 | ||
| 33 | extern struct strarray strarray__socket_families; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * augmented_arg: extra payload for syscall pointer arguments | ||
| 37 | |||
| 38 | * If perf_sample->raw_size is more than what a syscall sys_enter_FOO puts, | ||
| 39 | * then its the arguments contents, so that we can show more than just a | ||
| 40 | * pointer. This will be done initially with eBPF, the start of that is at the | ||
| 41 | * tools/perf/examples/bpf/augmented_syscalls.c example for the openat, but | ||
| 42 | * will eventually be done automagically caching the running kernel tracefs | ||
| 43 | * events data into an eBPF C script, that then gets compiled and its .o file | ||
| 44 | * cached for subsequent use. For char pointers like the ones for 'open' like | ||
| 45 | * syscalls its easy, for the rest we should use DWARF or better, BTF, much | ||
| 46 | * more compact. | ||
| 47 | * | ||
| 48 | * @size: 8 if all we need is an integer, otherwise all of the augmented arg. | ||
| 49 | * @int_arg: will be used for integer like pointer contents, like 'accept's 'upeer_addrlen' | ||
| 50 | * @value: u64 aligned, for structs, pathnames | ||
| 51 | */ | ||
| 52 | struct augmented_arg { | ||
| 53 | int size; | ||
| 54 | int int_arg; | ||
| 55 | u64 value[]; | ||
| 56 | }; | ||
| 57 | |||
| 33 | /** | 58 | /** |
| 34 | * @val: value of syscall argument being formatted | 59 | * @val: value of syscall argument being formatted |
| 35 | * @args: All the args, use syscall_args__val(arg, nth) to access one | 60 | * @args: All the args, use syscall_args__val(arg, nth) to access one |
| 61 | * @augmented_args: Extra data that can be collected, for instance, with eBPF for expanding the pathname for open, etc | ||
| 62 | * @augmented_args_size: augmented_args total payload size | ||
| 36 | * @thread: tid state (maps, pid, tid, etc) | 63 | * @thread: tid state (maps, pid, tid, etc) |
| 37 | * @trace: 'perf trace' internals: all threads, etc | 64 | * @trace: 'perf trace' internals: all threads, etc |
| 38 | * @parm: private area, may be an strarray, for instance | 65 | * @parm: private area, may be an strarray, for instance |
| @@ -43,6 +70,10 @@ size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_ | |||
| 43 | struct syscall_arg { | 70 | struct syscall_arg { |
| 44 | unsigned long val; | 71 | unsigned long val; |
| 45 | unsigned char *args; | 72 | unsigned char *args; |
| 73 | struct { | ||
| 74 | struct augmented_arg *args; | ||
| 75 | int size; | ||
| 76 | } augmented; | ||
| 46 | struct thread *thread; | 77 | struct thread *thread; |
| 47 | struct trace *trace; | 78 | struct trace *trace; |
| 48 | void *parm; | 79 | void *parm; |
| @@ -106,6 +137,9 @@ size_t syscall_arg__scnprintf_prctl_arg2(char *bf, size_t size, struct syscall_a | |||
| 106 | size_t syscall_arg__scnprintf_prctl_arg3(char *bf, size_t size, struct syscall_arg *arg); | 137 | size_t syscall_arg__scnprintf_prctl_arg3(char *bf, size_t size, struct syscall_arg *arg); |
| 107 | #define SCA_PRCTL_ARG3 syscall_arg__scnprintf_prctl_arg3 | 138 | #define SCA_PRCTL_ARG3 syscall_arg__scnprintf_prctl_arg3 |
| 108 | 139 | ||
| 140 | size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg *arg); | ||
| 141 | #define SCA_SOCKADDR syscall_arg__scnprintf_sockaddr | ||
| 142 | |||
| 109 | size_t syscall_arg__scnprintf_socket_protocol(char *bf, size_t size, struct syscall_arg *arg); | 143 | size_t syscall_arg__scnprintf_socket_protocol(char *bf, size_t size, struct syscall_arg *arg); |
| 110 | #define SCA_SK_PROTO syscall_arg__scnprintf_socket_protocol | 144 | #define SCA_SK_PROTO syscall_arg__scnprintf_socket_protocol |
| 111 | 145 | ||
diff --git a/tools/perf/trace/beauty/sockaddr.c b/tools/perf/trace/beauty/sockaddr.c new file mode 100644 index 000000000000..71a79f72d9d9 --- /dev/null +++ b/tools/perf/trace/beauty/sockaddr.c | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | // Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
| 3 | |||
| 4 | #include "trace/beauty/beauty.h" | ||
| 5 | #include <sys/socket.h> | ||
| 6 | #include <sys/types.h> | ||
| 7 | #include <sys/un.h> | ||
| 8 | #include <arpa/inet.h> | ||
| 9 | |||
| 10 | static const char *socket_families[] = { | ||
| 11 | "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", | ||
| 12 | "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", | ||
| 13 | "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", | ||
| 14 | "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", | ||
| 15 | "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", | ||
| 16 | "ALG", "NFC", "VSOCK", | ||
| 17 | }; | ||
| 18 | DEFINE_STRARRAY(socket_families); | ||
| 19 | |||
| 20 | static size_t af_inet__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
| 21 | { | ||
| 22 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; | ||
| 23 | char tmp[16]; | ||
| 24 | return scnprintf(bf, size, ", port: %d, addr: %s", ntohs(sin->sin_port), | ||
| 25 | inet_ntop(sin->sin_family, &sin->sin_addr, tmp, sizeof(tmp))); | ||
| 26 | } | ||
| 27 | |||
| 28 | static size_t af_inet6__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
| 29 | { | ||
| 30 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; | ||
| 31 | u32 flowinfo = ntohl(sin6->sin6_flowinfo); | ||
| 32 | char tmp[512]; | ||
| 33 | size_t printed = scnprintf(bf, size, ", port: %d, addr: %s", ntohs(sin6->sin6_port), | ||
| 34 | inet_ntop(sin6->sin6_family, &sin6->sin6_addr, tmp, sizeof(tmp))); | ||
| 35 | if (flowinfo != 0) | ||
| 36 | printed += scnprintf(bf + printed, size - printed, ", flowinfo: %lu", flowinfo); | ||
| 37 | if (sin6->sin6_scope_id != 0) | ||
| 38 | printed += scnprintf(bf + printed, size - printed, ", scope_id: %lu", sin6->sin6_scope_id); | ||
| 39 | |||
| 40 | return printed; | ||
| 41 | } | ||
| 42 | |||
| 43 | static size_t af_local__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
| 44 | { | ||
| 45 | struct sockaddr_un *sun = (struct sockaddr_un *)sa; | ||
| 46 | return scnprintf(bf, size, ", path: %s", sun->sun_path); | ||
| 47 | } | ||
| 48 | |||
| 49 | static size_t (*af_scnprintfs[])(struct sockaddr *sa, char *bf, size_t size) = { | ||
| 50 | [AF_LOCAL] = af_local__scnprintf, | ||
| 51 | [AF_INET] = af_inet__scnprintf, | ||
| 52 | [AF_INET6] = af_inet6__scnprintf, | ||
| 53 | }; | ||
| 54 | |||
| 55 | static size_t syscall_arg__scnprintf_augmented_sockaddr(struct syscall_arg *arg, char *bf, size_t size) | ||
| 56 | { | ||
| 57 | struct sockaddr *sa = (struct sockaddr *)arg->augmented.args; | ||
| 58 | char family[32]; | ||
| 59 | size_t printed; | ||
| 60 | |||
| 61 | strarray__scnprintf(&strarray__socket_families, family, sizeof(family), "%d", sa->sa_family); | ||
| 62 | printed = scnprintf(bf, size, "{ .family: %s", family); | ||
| 63 | |||
| 64 | if (sa->sa_family < ARRAY_SIZE(af_scnprintfs) && af_scnprintfs[sa->sa_family]) | ||
| 65 | printed += af_scnprintfs[sa->sa_family](sa, bf + printed, size - printed); | ||
| 66 | |||
| 67 | return printed + scnprintf(bf + printed, size - printed, " }"); | ||
| 68 | } | ||
| 69 | |||
| 70 | size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg *arg) | ||
| 71 | { | ||
| 72 | if (arg->augmented.args) | ||
| 73 | return syscall_arg__scnprintf_augmented_sockaddr(arg, bf, size); | ||
| 74 | |||
| 75 | return scnprintf(bf, size, "%#x", arg->val); | ||
| 76 | } | ||
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 7efe15b9618d..ecd9f9ceda77 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
| @@ -73,6 +73,7 @@ libperf-y += vdso.o | |||
| 73 | libperf-y += counts.o | 73 | libperf-y += counts.o |
| 74 | libperf-y += stat.o | 74 | libperf-y += stat.o |
| 75 | libperf-y += stat-shadow.o | 75 | libperf-y += stat-shadow.o |
| 76 | libperf-y += stat-display.o | ||
| 76 | libperf-y += record.o | 77 | libperf-y += record.o |
| 77 | libperf-y += srcline.o | 78 | libperf-y += srcline.o |
| 78 | libperf-y += data.o | 79 | libperf-y += data.o |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 20061cf42288..28cd6a17491b 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -246,8 +246,14 @@ find_target: | |||
| 246 | 246 | ||
| 247 | indirect_call: | 247 | indirect_call: |
| 248 | tok = strchr(endptr, '*'); | 248 | tok = strchr(endptr, '*'); |
| 249 | if (tok != NULL) | 249 | if (tok != NULL) { |
| 250 | ops->target.addr = strtoull(tok + 1, NULL, 16); | 250 | endptr++; |
| 251 | |||
| 252 | /* Indirect call can use a non-rip register and offset: callq *0x8(%rbx). | ||
| 253 | * Do not parse such instruction. */ | ||
| 254 | if (strstr(endptr, "(%r") == NULL) | ||
| 255 | ops->target.addr = strtoull(endptr, NULL, 16); | ||
| 256 | } | ||
| 251 | goto find_target; | 257 | goto find_target; |
| 252 | } | 258 | } |
| 253 | 259 | ||
| @@ -276,7 +282,19 @@ bool ins__is_call(const struct ins *ins) | |||
| 276 | return ins->ops == &call_ops || ins->ops == &s390_call_ops; | 282 | return ins->ops == &call_ops || ins->ops == &s390_call_ops; |
| 277 | } | 283 | } |
| 278 | 284 | ||
| 279 | static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms) | 285 | /* |
| 286 | * Prevents from matching commas in the comment section, e.g.: | ||
| 287 | * ffff200008446e70: b.cs ffff2000084470f4 <generic_exec_single+0x314> // b.hs, b.nlast | ||
| 288 | */ | ||
| 289 | static inline const char *validate_comma(const char *c, struct ins_operands *ops) | ||
| 290 | { | ||
| 291 | if (ops->raw_comment && c > ops->raw_comment) | ||
| 292 | return NULL; | ||
| 293 | |||
| 294 | return c; | ||
| 295 | } | ||
| 296 | |||
| 297 | static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) | ||
| 280 | { | 298 | { |
| 281 | struct map *map = ms->map; | 299 | struct map *map = ms->map; |
| 282 | struct symbol *sym = ms->sym; | 300 | struct symbol *sym = ms->sym; |
| @@ -285,6 +303,10 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op | |||
| 285 | }; | 303 | }; |
| 286 | const char *c = strchr(ops->raw, ','); | 304 | const char *c = strchr(ops->raw, ','); |
| 287 | u64 start, end; | 305 | u64 start, end; |
| 306 | |||
| 307 | ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char); | ||
| 308 | c = validate_comma(c, ops); | ||
| 309 | |||
| 288 | /* | 310 | /* |
| 289 | * Examples of lines to parse for the _cpp_lex_token@@Base | 311 | * Examples of lines to parse for the _cpp_lex_token@@Base |
| 290 | * function: | 312 | * function: |
| @@ -304,6 +326,7 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op | |||
| 304 | ops->target.addr = strtoull(c, NULL, 16); | 326 | ops->target.addr = strtoull(c, NULL, 16); |
| 305 | if (!ops->target.addr) { | 327 | if (!ops->target.addr) { |
| 306 | c = strchr(c, ','); | 328 | c = strchr(c, ','); |
| 329 | c = validate_comma(c, ops); | ||
| 307 | if (c++ != NULL) | 330 | if (c++ != NULL) |
| 308 | ops->target.addr = strtoull(c, NULL, 16); | 331 | ops->target.addr = strtoull(c, NULL, 16); |
| 309 | } | 332 | } |
| @@ -361,9 +384,12 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size, | |||
| 361 | return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); | 384 | return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); |
| 362 | 385 | ||
| 363 | c = strchr(ops->raw, ','); | 386 | c = strchr(ops->raw, ','); |
| 387 | c = validate_comma(c, ops); | ||
| 388 | |||
| 364 | if (c != NULL) { | 389 | if (c != NULL) { |
| 365 | const char *c2 = strchr(c + 1, ','); | 390 | const char *c2 = strchr(c + 1, ','); |
| 366 | 391 | ||
| 392 | c2 = validate_comma(c2, ops); | ||
| 367 | /* check for 3-op insn */ | 393 | /* check for 3-op insn */ |
| 368 | if (c2 != NULL) | 394 | if (c2 != NULL) |
| 369 | c = c2; | 395 | c = c2; |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 005a5fe8a8c6..5399ba2321bb 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
| @@ -22,6 +22,7 @@ struct ins { | |||
| 22 | 22 | ||
| 23 | struct ins_operands { | 23 | struct ins_operands { |
| 24 | char *raw; | 24 | char *raw; |
| 25 | char *raw_comment; | ||
| 25 | struct { | 26 | struct { |
| 26 | char *raw; | 27 | char *raw; |
| 27 | char *name; | 28 | char *name; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index c980bbff6353..4ec909d57e9c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -251,8 +251,9 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
| 251 | { | 251 | { |
| 252 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); | 252 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); |
| 253 | 253 | ||
| 254 | if (evsel != NULL) | 254 | if (!evsel) |
| 255 | perf_evsel__init(evsel, attr, idx); | 255 | return NULL; |
| 256 | perf_evsel__init(evsel, attr, idx); | ||
| 256 | 257 | ||
| 257 | if (perf_evsel__is_bpf_output(evsel)) { | 258 | if (perf_evsel__is_bpf_output(evsel)) { |
| 258 | evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | | 259 | evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | |
| @@ -2939,3 +2940,32 @@ struct perf_env *perf_evsel__env(struct perf_evsel *evsel) | |||
| 2939 | return evsel->evlist->env; | 2940 | return evsel->evlist->env; |
| 2940 | return NULL; | 2941 | return NULL; |
| 2941 | } | 2942 | } |
| 2943 | |||
| 2944 | static int store_evsel_ids(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
| 2945 | { | ||
| 2946 | int cpu, thread; | ||
| 2947 | |||
| 2948 | for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { | ||
| 2949 | for (thread = 0; thread < xyarray__max_y(evsel->fd); | ||
| 2950 | thread++) { | ||
| 2951 | int fd = FD(evsel, cpu, thread); | ||
| 2952 | |||
| 2953 | if (perf_evlist__id_add_fd(evlist, evsel, | ||
| 2954 | cpu, thread, fd) < 0) | ||
| 2955 | return -1; | ||
| 2956 | } | ||
| 2957 | } | ||
| 2958 | |||
| 2959 | return 0; | ||
| 2960 | } | ||
| 2961 | |||
| 2962 | int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
| 2963 | { | ||
| 2964 | struct cpu_map *cpus = evsel->cpus; | ||
| 2965 | struct thread_map *threads = evsel->threads; | ||
| 2966 | |||
| 2967 | if (perf_evsel__alloc_id(evsel, cpus->nr, threads->nr)) | ||
| 2968 | return -ENOMEM; | ||
| 2969 | |||
| 2970 | return store_evsel_ids(evsel, evlist); | ||
| 2971 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 163c960614d3..4f8430a85531 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -481,4 +481,5 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, | |||
| 481 | 481 | ||
| 482 | struct perf_env *perf_evsel__env(struct perf_evsel *evsel); | 482 | struct perf_env *perf_evsel__env(struct perf_evsel *evsel); |
| 483 | 483 | ||
| 484 | int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist); | ||
| 484 | #endif /* __PERF_EVSEL_H */ | 485 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3cadc252dd89..91e6d9cfd906 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -3637,13 +3637,13 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) | |||
| 3637 | } | 3637 | } |
| 3638 | 3638 | ||
| 3639 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 3639 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
| 3640 | struct perf_session *session, | 3640 | struct perf_evlist *evlist, |
| 3641 | perf_event__handler_t process) | 3641 | perf_event__handler_t process) |
| 3642 | { | 3642 | { |
| 3643 | struct perf_evsel *evsel; | 3643 | struct perf_evsel *evsel; |
| 3644 | int err = 0; | 3644 | int err = 0; |
| 3645 | 3645 | ||
| 3646 | evlist__for_each_entry(session->evlist, evsel) { | 3646 | evlist__for_each_entry(evlist, evsel) { |
| 3647 | err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, | 3647 | err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, |
| 3648 | evsel->id, process); | 3648 | evsel->id, process); |
| 3649 | if (err) { | 3649 | if (err) { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 6d7fe44aadc0..ff2a1263fb9b 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -124,7 +124,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool, | |||
| 124 | struct perf_event_attr *attr, u32 ids, u64 *id, | 124 | struct perf_event_attr *attr, u32 ids, u64 *id, |
| 125 | perf_event__handler_t process); | 125 | perf_event__handler_t process); |
| 126 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 126 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
| 127 | struct perf_session *session, | 127 | struct perf_evlist *evlist, |
| 128 | perf_event__handler_t process); | 128 | perf_event__handler_t process); |
| 129 | int perf_event__synthesize_event_update_unit(struct perf_tool *tool, | 129 | int perf_event__synthesize_event_update_unit(struct perf_tool *tool, |
| 130 | struct perf_evsel *evsel, | 130 | struct perf_evsel *evsel, |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 19262f98cd4e..5b0b60f00275 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
| @@ -19,7 +19,7 @@ | |||
| 19 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | 19 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ |
| 20 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ | 20 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ |
| 21 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ | 21 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ |
| 22 | "$CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS " \ | 22 | "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ |
| 23 | "-Wno-unused-value -Wno-pointer-sign " \ | 23 | "-Wno-unused-value -Wno-pointer-sign " \ |
| 24 | "-working-directory $WORKING_DIR " \ | 24 | "-working-directory $WORKING_DIR " \ |
| 25 | "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" | 25 | "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 36d0763311ef..3f07a587c8e6 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -320,12 +320,11 @@ int map__load(struct map *map) | |||
| 320 | build_id__sprintf(map->dso->build_id, | 320 | build_id__sprintf(map->dso->build_id, |
| 321 | sizeof(map->dso->build_id), | 321 | sizeof(map->dso->build_id), |
| 322 | sbuild_id); | 322 | sbuild_id); |
| 323 | pr_warning("%s with build id %s not found", | 323 | pr_debug("%s with build id %s not found", name, sbuild_id); |
| 324 | name, sbuild_id); | ||
| 325 | } else | 324 | } else |
| 326 | pr_warning("Failed to open %s", name); | 325 | pr_debug("Failed to open %s", name); |
| 327 | 326 | ||
| 328 | pr_warning(", continuing without symbols\n"); | 327 | pr_debug(", continuing without symbols\n"); |
| 329 | return -1; | 328 | return -1; |
| 330 | } else if (nr == 0) { | 329 | } else if (nr == 0) { |
| 331 | #ifdef HAVE_LIBELF_SUPPORT | 330 | #ifdef HAVE_LIBELF_SUPPORT |
| @@ -334,12 +333,11 @@ int map__load(struct map *map) | |||
| 334 | 333 | ||
| 335 | if (len > sizeof(DSO__DELETED) && | 334 | if (len > sizeof(DSO__DELETED) && |
| 336 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 335 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
| 337 | pr_warning("%.*s was updated (is prelink enabled?). " | 336 | pr_debug("%.*s was updated (is prelink enabled?). " |
| 338 | "Restart the long running apps that use it!\n", | 337 | "Restart the long running apps that use it!\n", |
| 339 | (int)real_len, name); | 338 | (int)real_len, name); |
| 340 | } else { | 339 | } else { |
| 341 | pr_warning("no symbols found in %s, maybe install " | 340 | pr_debug("no symbols found in %s, maybe install a debug package?\n", name); |
| 342 | "a debug package?\n", name); | ||
| 343 | } | 341 | } |
| 344 | #endif | 342 | #endif |
| 345 | return -1; | 343 | return -1; |
| @@ -701,8 +699,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
| 701 | if (verbose >= 2) { | 699 | if (verbose >= 2) { |
| 702 | 700 | ||
| 703 | if (use_browser) { | 701 | if (use_browser) { |
| 704 | pr_warning("overlapping maps in %s " | 702 | pr_debug("overlapping maps in %s (disable tui for more info)\n", |
| 705 | "(disable tui for more info)\n", | ||
| 706 | map->dso->name); | 703 | map->dso->name); |
| 707 | } else { | 704 | } else { |
| 708 | fputs("overlapping maps:\n", fp); | 705 | fputs("overlapping maps:\n", fp); |
diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index d2c78ffd9fee..a2eeebbfb25f 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c | |||
| @@ -147,6 +147,9 @@ | |||
| 147 | #include <linux/bitops.h> | 147 | #include <linux/bitops.h> |
| 148 | #include <linux/log2.h> | 148 | #include <linux/log2.h> |
| 149 | 149 | ||
| 150 | #include <sys/stat.h> | ||
| 151 | #include <sys/types.h> | ||
| 152 | |||
| 150 | #include "cpumap.h" | 153 | #include "cpumap.h" |
| 151 | #include "color.h" | 154 | #include "color.h" |
| 152 | #include "evsel.h" | 155 | #include "evsel.h" |
| @@ -159,6 +162,7 @@ | |||
| 159 | #include "auxtrace.h" | 162 | #include "auxtrace.h" |
| 160 | #include "s390-cpumsf.h" | 163 | #include "s390-cpumsf.h" |
| 161 | #include "s390-cpumsf-kernel.h" | 164 | #include "s390-cpumsf-kernel.h" |
| 165 | #include "config.h" | ||
| 162 | 166 | ||
| 163 | struct s390_cpumsf { | 167 | struct s390_cpumsf { |
| 164 | struct auxtrace auxtrace; | 168 | struct auxtrace auxtrace; |
| @@ -170,6 +174,8 @@ struct s390_cpumsf { | |||
| 170 | u32 pmu_type; | 174 | u32 pmu_type; |
| 171 | u16 machine_type; | 175 | u16 machine_type; |
| 172 | bool data_queued; | 176 | bool data_queued; |
| 177 | bool use_logfile; | ||
| 178 | char *logdir; | ||
| 173 | }; | 179 | }; |
| 174 | 180 | ||
| 175 | struct s390_cpumsf_queue { | 181 | struct s390_cpumsf_queue { |
| @@ -177,6 +183,7 @@ struct s390_cpumsf_queue { | |||
| 177 | unsigned int queue_nr; | 183 | unsigned int queue_nr; |
| 178 | struct auxtrace_buffer *buffer; | 184 | struct auxtrace_buffer *buffer; |
| 179 | int cpu; | 185 | int cpu; |
| 186 | FILE *logfile; | ||
| 180 | }; | 187 | }; |
| 181 | 188 | ||
| 182 | /* Display s390 CPU measurement facility basic-sampling data entry */ | 189 | /* Display s390 CPU measurement facility basic-sampling data entry */ |
| @@ -595,6 +602,12 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq, | |||
| 595 | buffer->use_size = buffer->size; | 602 | buffer->use_size = buffer->size; |
| 596 | buffer->use_data = buffer->data; | 603 | buffer->use_data = buffer->data; |
| 597 | } | 604 | } |
| 605 | if (sfq->logfile) { /* Write into log file */ | ||
| 606 | size_t rc = fwrite(buffer->data, buffer->size, 1, | ||
| 607 | sfq->logfile); | ||
| 608 | if (rc != 1) | ||
| 609 | pr_err("Failed to write auxiliary data\n"); | ||
| 610 | } | ||
| 598 | } else | 611 | } else |
| 599 | buffer = sfq->buffer; | 612 | buffer = sfq->buffer; |
| 600 | 613 | ||
| @@ -606,6 +619,13 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq, | |||
| 606 | return -ENOMEM; | 619 | return -ENOMEM; |
| 607 | buffer->use_size = buffer->size; | 620 | buffer->use_size = buffer->size; |
| 608 | buffer->use_data = buffer->data; | 621 | buffer->use_data = buffer->data; |
| 622 | |||
| 623 | if (sfq->logfile) { /* Write into log file */ | ||
| 624 | size_t rc = fwrite(buffer->data, buffer->size, 1, | ||
| 625 | sfq->logfile); | ||
| 626 | if (rc != 1) | ||
| 627 | pr_err("Failed to write auxiliary data\n"); | ||
| 628 | } | ||
| 609 | } | 629 | } |
| 610 | pr_debug4("%s queue_nr:%d buffer:%" PRId64 " offset:%#" PRIx64 " size:%#zx rest:%#zx\n", | 630 | pr_debug4("%s queue_nr:%d buffer:%" PRId64 " offset:%#" PRIx64 " size:%#zx rest:%#zx\n", |
| 611 | __func__, sfq->queue_nr, buffer->buffer_nr, buffer->offset, | 631 | __func__, sfq->queue_nr, buffer->buffer_nr, buffer->offset, |
| @@ -640,6 +660,23 @@ s390_cpumsf_alloc_queue(struct s390_cpumsf *sf, unsigned int queue_nr) | |||
| 640 | sfq->sf = sf; | 660 | sfq->sf = sf; |
| 641 | sfq->queue_nr = queue_nr; | 661 | sfq->queue_nr = queue_nr; |
| 642 | sfq->cpu = -1; | 662 | sfq->cpu = -1; |
| 663 | if (sf->use_logfile) { | ||
| 664 | char *name; | ||
| 665 | int rc; | ||
| 666 | |||
| 667 | rc = (sf->logdir) | ||
| 668 | ? asprintf(&name, "%s/aux.smp.%02x", | ||
| 669 | sf->logdir, queue_nr) | ||
| 670 | : asprintf(&name, "aux.smp.%02x", queue_nr); | ||
| 671 | if (rc > 0) | ||
| 672 | sfq->logfile = fopen(name, "w"); | ||
| 673 | if (sfq->logfile == NULL) { | ||
| 674 | pr_err("Failed to open auxiliary log file %s," | ||
| 675 | "continue...\n", name); | ||
| 676 | sf->use_logfile = false; | ||
| 677 | } | ||
| 678 | free(name); | ||
| 679 | } | ||
| 643 | return sfq; | 680 | return sfq; |
| 644 | } | 681 | } |
| 645 | 682 | ||
| @@ -850,8 +887,16 @@ static void s390_cpumsf_free_queues(struct perf_session *session) | |||
| 850 | struct auxtrace_queues *queues = &sf->queues; | 887 | struct auxtrace_queues *queues = &sf->queues; |
| 851 | unsigned int i; | 888 | unsigned int i; |
| 852 | 889 | ||
| 853 | for (i = 0; i < queues->nr_queues; i++) | 890 | for (i = 0; i < queues->nr_queues; i++) { |
| 891 | struct s390_cpumsf_queue *sfq = (struct s390_cpumsf_queue *) | ||
| 892 | queues->queue_array[i].priv; | ||
| 893 | |||
| 894 | if (sfq != NULL && sfq->logfile) { | ||
| 895 | fclose(sfq->logfile); | ||
| 896 | sfq->logfile = NULL; | ||
| 897 | } | ||
| 854 | zfree(&queues->queue_array[i].priv); | 898 | zfree(&queues->queue_array[i].priv); |
| 899 | } | ||
| 855 | auxtrace_queues__free(queues); | 900 | auxtrace_queues__free(queues); |
| 856 | } | 901 | } |
| 857 | 902 | ||
| @@ -864,6 +909,7 @@ static void s390_cpumsf_free(struct perf_session *session) | |||
| 864 | auxtrace_heap__free(&sf->heap); | 909 | auxtrace_heap__free(&sf->heap); |
| 865 | s390_cpumsf_free_queues(session); | 910 | s390_cpumsf_free_queues(session); |
| 866 | session->auxtrace = NULL; | 911 | session->auxtrace = NULL; |
| 912 | free(sf->logdir); | ||
| 867 | free(sf); | 913 | free(sf); |
| 868 | } | 914 | } |
| 869 | 915 | ||
| @@ -877,17 +923,55 @@ static int s390_cpumsf_get_type(const char *cpuid) | |||
| 877 | 923 | ||
| 878 | /* Check itrace options set on perf report command. | 924 | /* Check itrace options set on perf report command. |
| 879 | * Return true, if none are set or all options specified can be | 925 | * Return true, if none are set or all options specified can be |
| 880 | * handled on s390. | 926 | * handled on s390 (currently only option 'd' for logging. |
| 881 | * Return false otherwise. | 927 | * Return false otherwise. |
| 882 | */ | 928 | */ |
| 883 | static bool check_auxtrace_itrace(struct itrace_synth_opts *itops) | 929 | static bool check_auxtrace_itrace(struct itrace_synth_opts *itops) |
| 884 | { | 930 | { |
| 931 | bool ison = false; | ||
| 932 | |||
| 885 | if (!itops || !itops->set) | 933 | if (!itops || !itops->set) |
| 886 | return true; | 934 | return true; |
| 887 | pr_err("No --itrace options supported\n"); | 935 | ison = itops->inject || itops->instructions || itops->branches || |
| 936 | itops->transactions || itops->ptwrites || | ||
| 937 | itops->pwr_events || itops->errors || | ||
| 938 | itops->dont_decode || itops->calls || itops->returns || | ||
| 939 | itops->callchain || itops->thread_stack || | ||
| 940 | itops->last_branch; | ||
| 941 | if (!ison) | ||
| 942 | return true; | ||
| 943 | pr_err("Unsupported --itrace options specified\n"); | ||
| 888 | return false; | 944 | return false; |
| 889 | } | 945 | } |
| 890 | 946 | ||
| 947 | /* Check for AUXTRACE dump directory if it is needed. | ||
| 948 | * On failure print an error message but continue. | ||
| 949 | * Return 0 on wrong keyword in config file and 1 otherwise. | ||
| 950 | */ | ||
| 951 | static int s390_cpumsf__config(const char *var, const char *value, void *cb) | ||
| 952 | { | ||
| 953 | struct s390_cpumsf *sf = cb; | ||
| 954 | struct stat stbuf; | ||
| 955 | int rc; | ||
| 956 | |||
| 957 | if (strcmp(var, "auxtrace.dumpdir")) | ||
| 958 | return 0; | ||
| 959 | sf->logdir = strdup(value); | ||
| 960 | if (sf->logdir == NULL) { | ||
| 961 | pr_err("Failed to find auxtrace log directory %s," | ||
| 962 | " continue with current directory...\n", value); | ||
| 963 | return 1; | ||
| 964 | } | ||
| 965 | rc = stat(sf->logdir, &stbuf); | ||
| 966 | if (rc == -1 || !S_ISDIR(stbuf.st_mode)) { | ||
| 967 | pr_err("Missing auxtrace log directory %s," | ||
| 968 | " continue with current directory...\n", value); | ||
| 969 | free(sf->logdir); | ||
| 970 | sf->logdir = NULL; | ||
| 971 | } | ||
| 972 | return 1; | ||
| 973 | } | ||
| 974 | |||
| 891 | int s390_cpumsf_process_auxtrace_info(union perf_event *event, | 975 | int s390_cpumsf_process_auxtrace_info(union perf_event *event, |
| 892 | struct perf_session *session) | 976 | struct perf_session *session) |
| 893 | { | 977 | { |
| @@ -906,6 +990,9 @@ int s390_cpumsf_process_auxtrace_info(union perf_event *event, | |||
| 906 | err = -EINVAL; | 990 | err = -EINVAL; |
| 907 | goto err_free; | 991 | goto err_free; |
| 908 | } | 992 | } |
| 993 | sf->use_logfile = session->itrace_synth_opts->log; | ||
| 994 | if (sf->use_logfile) | ||
| 995 | perf_config(s390_cpumsf__config, sf); | ||
| 909 | 996 | ||
| 910 | err = auxtrace_queues__init(&sf->queues); | 997 | err = auxtrace_queues__init(&sf->queues); |
| 911 | if (err) | 998 | if (err) |
| @@ -940,6 +1027,7 @@ err_free_queues: | |||
| 940 | auxtrace_queues__free(&sf->queues); | 1027 | auxtrace_queues__free(&sf->queues); |
| 941 | session->auxtrace = NULL; | 1028 | session->auxtrace = NULL; |
| 942 | err_free: | 1029 | err_free: |
| 1030 | free(sf->logdir); | ||
| 943 | free(sf); | 1031 | free(sf); |
| 944 | return err; | 1032 | return err; |
| 945 | } | 1033 | } |
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c new file mode 100644 index 000000000000..e7b4c44ebb62 --- /dev/null +++ b/tools/perf/util/stat-display.c | |||
| @@ -0,0 +1,1166 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <inttypes.h> | ||
| 3 | #include <linux/time64.h> | ||
| 4 | #include <math.h> | ||
| 5 | #include "evlist.h" | ||
| 6 | #include "evsel.h" | ||
| 7 | #include "stat.h" | ||
| 8 | #include "top.h" | ||
| 9 | #include "thread_map.h" | ||
| 10 | #include "cpumap.h" | ||
| 11 | #include "string2.h" | ||
| 12 | #include "sane_ctype.h" | ||
| 13 | #include "cgroup.h" | ||
| 14 | #include <math.h> | ||
| 15 | #include <api/fs/fs.h> | ||
| 16 | |||
| 17 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
| 18 | #define CNTR_NOT_COUNTED "<not counted>" | ||
| 19 | |||
| 20 | static bool is_duration_time(struct perf_evsel *evsel) | ||
| 21 | { | ||
| 22 | return !strcmp(evsel->name, "duration_time"); | ||
| 23 | } | ||
| 24 | |||
| 25 | static void print_running(struct perf_stat_config *config, | ||
| 26 | u64 run, u64 ena) | ||
| 27 | { | ||
| 28 | if (config->csv_output) { | ||
| 29 | fprintf(config->output, "%s%" PRIu64 "%s%.2f", | ||
| 30 | config->csv_sep, | ||
| 31 | run, | ||
| 32 | config->csv_sep, | ||
| 33 | ena ? 100.0 * run / ena : 100.0); | ||
| 34 | } else if (run != ena) { | ||
| 35 | fprintf(config->output, " (%.2f%%)", 100.0 * run / ena); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | static void print_noise_pct(struct perf_stat_config *config, | ||
| 40 | double total, double avg) | ||
| 41 | { | ||
| 42 | double pct = rel_stddev_stats(total, avg); | ||
| 43 | |||
| 44 | if (config->csv_output) | ||
| 45 | fprintf(config->output, "%s%.2f%%", config->csv_sep, pct); | ||
| 46 | else if (pct) | ||
| 47 | fprintf(config->output, " ( +-%6.2f%% )", pct); | ||
| 48 | } | ||
| 49 | |||
| 50 | static void print_noise(struct perf_stat_config *config, | ||
| 51 | struct perf_evsel *evsel, double avg) | ||
| 52 | { | ||
| 53 | struct perf_stat_evsel *ps; | ||
| 54 | |||
| 55 | if (config->run_count == 1) | ||
| 56 | return; | ||
| 57 | |||
| 58 | ps = evsel->stats; | ||
| 59 | print_noise_pct(config, stddev_stats(&ps->res_stats[0]), avg); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void aggr_printout(struct perf_stat_config *config, | ||
| 63 | struct perf_evsel *evsel, int id, int nr) | ||
| 64 | { | ||
| 65 | switch (config->aggr_mode) { | ||
| 66 | case AGGR_CORE: | ||
| 67 | fprintf(config->output, "S%d-C%*d%s%*d%s", | ||
| 68 | cpu_map__id_to_socket(id), | ||
| 69 | config->csv_output ? 0 : -8, | ||
| 70 | cpu_map__id_to_cpu(id), | ||
| 71 | config->csv_sep, | ||
| 72 | config->csv_output ? 0 : 4, | ||
| 73 | nr, | ||
| 74 | config->csv_sep); | ||
| 75 | break; | ||
| 76 | case AGGR_SOCKET: | ||
| 77 | fprintf(config->output, "S%*d%s%*d%s", | ||
| 78 | config->csv_output ? 0 : -5, | ||
| 79 | id, | ||
| 80 | config->csv_sep, | ||
| 81 | config->csv_output ? 0 : 4, | ||
| 82 | nr, | ||
| 83 | config->csv_sep); | ||
| 84 | break; | ||
| 85 | case AGGR_NONE: | ||
| 86 | fprintf(config->output, "CPU%*d%s", | ||
| 87 | config->csv_output ? 0 : -4, | ||
| 88 | perf_evsel__cpus(evsel)->map[id], config->csv_sep); | ||
| 89 | break; | ||
| 90 | case AGGR_THREAD: | ||
| 91 | fprintf(config->output, "%*s-%*d%s", | ||
| 92 | config->csv_output ? 0 : 16, | ||
| 93 | thread_map__comm(evsel->threads, id), | ||
| 94 | config->csv_output ? 0 : -8, | ||
| 95 | thread_map__pid(evsel->threads, id), | ||
| 96 | config->csv_sep); | ||
| 97 | break; | ||
| 98 | case AGGR_GLOBAL: | ||
| 99 | case AGGR_UNSET: | ||
| 100 | default: | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | struct outstate { | ||
| 106 | FILE *fh; | ||
| 107 | bool newline; | ||
| 108 | const char *prefix; | ||
| 109 | int nfields; | ||
| 110 | int id, nr; | ||
| 111 | struct perf_evsel *evsel; | ||
| 112 | }; | ||
| 113 | |||
| 114 | #define METRIC_LEN 35 | ||
| 115 | |||
| 116 | static void new_line_std(struct perf_stat_config *config __maybe_unused, | ||
| 117 | void *ctx) | ||
| 118 | { | ||
| 119 | struct outstate *os = ctx; | ||
| 120 | |||
| 121 | os->newline = true; | ||
| 122 | } | ||
| 123 | |||
| 124 | static void do_new_line_std(struct perf_stat_config *config, | ||
| 125 | struct outstate *os) | ||
| 126 | { | ||
| 127 | fputc('\n', os->fh); | ||
| 128 | fputs(os->prefix, os->fh); | ||
| 129 | aggr_printout(config, os->evsel, os->id, os->nr); | ||
| 130 | if (config->aggr_mode == AGGR_NONE) | ||
| 131 | fprintf(os->fh, " "); | ||
| 132 | fprintf(os->fh, " "); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void print_metric_std(struct perf_stat_config *config, | ||
| 136 | void *ctx, const char *color, const char *fmt, | ||
| 137 | const char *unit, double val) | ||
| 138 | { | ||
| 139 | struct outstate *os = ctx; | ||
| 140 | FILE *out = os->fh; | ||
| 141 | int n; | ||
| 142 | bool newline = os->newline; | ||
| 143 | |||
| 144 | os->newline = false; | ||
| 145 | |||
| 146 | if (unit == NULL || fmt == NULL) { | ||
| 147 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (newline) | ||
| 152 | do_new_line_std(config, os); | ||
| 153 | |||
| 154 | n = fprintf(out, " # "); | ||
| 155 | if (color) | ||
| 156 | n += color_fprintf(out, color, fmt, val); | ||
| 157 | else | ||
| 158 | n += fprintf(out, fmt, val); | ||
| 159 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
| 160 | } | ||
| 161 | |||
| 162 | static void new_line_csv(struct perf_stat_config *config, void *ctx) | ||
| 163 | { | ||
| 164 | struct outstate *os = ctx; | ||
| 165 | int i; | ||
| 166 | |||
| 167 | fputc('\n', os->fh); | ||
| 168 | if (os->prefix) | ||
| 169 | fprintf(os->fh, "%s%s", os->prefix, config->csv_sep); | ||
| 170 | aggr_printout(config, os->evsel, os->id, os->nr); | ||
| 171 | for (i = 0; i < os->nfields; i++) | ||
| 172 | fputs(config->csv_sep, os->fh); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void print_metric_csv(struct perf_stat_config *config __maybe_unused, | ||
| 176 | void *ctx, | ||
| 177 | const char *color __maybe_unused, | ||
| 178 | const char *fmt, const char *unit, double val) | ||
| 179 | { | ||
| 180 | struct outstate *os = ctx; | ||
| 181 | FILE *out = os->fh; | ||
| 182 | char buf[64], *vals, *ends; | ||
| 183 | |||
| 184 | if (unit == NULL || fmt == NULL) { | ||
| 185 | fprintf(out, "%s%s", config->csv_sep, config->csv_sep); | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | snprintf(buf, sizeof(buf), fmt, val); | ||
| 189 | ends = vals = ltrim(buf); | ||
| 190 | while (isdigit(*ends) || *ends == '.') | ||
| 191 | ends++; | ||
| 192 | *ends = 0; | ||
| 193 | while (isspace(*unit)) | ||
| 194 | unit++; | ||
| 195 | fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, unit); | ||
| 196 | } | ||
| 197 | |||
| 198 | /* Filter out some columns that don't work well in metrics only mode */ | ||
| 199 | |||
| 200 | static bool valid_only_metric(const char *unit) | ||
| 201 | { | ||
| 202 | if (!unit) | ||
| 203 | return false; | ||
| 204 | if (strstr(unit, "/sec") || | ||
| 205 | strstr(unit, "hz") || | ||
| 206 | strstr(unit, "Hz") || | ||
| 207 | strstr(unit, "CPUs utilized")) | ||
| 208 | return false; | ||
| 209 | return true; | ||
| 210 | } | ||
| 211 | |||
| 212 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
| 213 | const char *unit) | ||
| 214 | { | ||
| 215 | if (!strncmp(unit, "of all", 6)) { | ||
| 216 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
| 217 | unit); | ||
| 218 | return buf; | ||
| 219 | } | ||
| 220 | return unit; | ||
| 221 | } | ||
| 222 | |||
| 223 | static void print_metric_only(struct perf_stat_config *config, | ||
| 224 | void *ctx, const char *color, const char *fmt, | ||
| 225 | const char *unit, double val) | ||
| 226 | { | ||
| 227 | struct outstate *os = ctx; | ||
| 228 | FILE *out = os->fh; | ||
| 229 | char buf[1024], str[1024]; | ||
| 230 | unsigned mlen = config->metric_only_len; | ||
| 231 | |||
| 232 | if (!valid_only_metric(unit)) | ||
| 233 | return; | ||
| 234 | unit = fixunit(buf, os->evsel, unit); | ||
| 235 | if (mlen < strlen(unit)) | ||
| 236 | mlen = strlen(unit) + 1; | ||
| 237 | |||
| 238 | if (color) | ||
| 239 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | ||
| 240 | |||
| 241 | color_snprintf(str, sizeof(str), color ?: "", fmt, val); | ||
| 242 | fprintf(out, "%*s ", mlen, str); | ||
| 243 | } | ||
| 244 | |||
| 245 | static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused, | ||
| 246 | void *ctx, const char *color __maybe_unused, | ||
| 247 | const char *fmt, | ||
| 248 | const char *unit, double val) | ||
| 249 | { | ||
| 250 | struct outstate *os = ctx; | ||
| 251 | FILE *out = os->fh; | ||
| 252 | char buf[64], *vals, *ends; | ||
| 253 | char tbuf[1024]; | ||
| 254 | |||
| 255 | if (!valid_only_metric(unit)) | ||
| 256 | return; | ||
| 257 | unit = fixunit(tbuf, os->evsel, unit); | ||
| 258 | snprintf(buf, sizeof buf, fmt, val); | ||
| 259 | ends = vals = ltrim(buf); | ||
| 260 | while (isdigit(*ends) || *ends == '.') | ||
| 261 | ends++; | ||
| 262 | *ends = 0; | ||
| 263 | fprintf(out, "%s%s", vals, config->csv_sep); | ||
| 264 | } | ||
| 265 | |||
| 266 | static void new_line_metric(struct perf_stat_config *config __maybe_unused, | ||
| 267 | void *ctx __maybe_unused) | ||
| 268 | { | ||
| 269 | } | ||
| 270 | |||
| 271 | static void print_metric_header(struct perf_stat_config *config, | ||
| 272 | void *ctx, const char *color __maybe_unused, | ||
| 273 | const char *fmt __maybe_unused, | ||
| 274 | const char *unit, double val __maybe_unused) | ||
| 275 | { | ||
| 276 | struct outstate *os = ctx; | ||
| 277 | char tbuf[1024]; | ||
| 278 | |||
| 279 | if (!valid_only_metric(unit)) | ||
| 280 | return; | ||
| 281 | unit = fixunit(tbuf, os->evsel, unit); | ||
| 282 | if (config->csv_output) | ||
| 283 | fprintf(os->fh, "%s%s", unit, config->csv_sep); | ||
| 284 | else | ||
| 285 | fprintf(os->fh, "%*s ", config->metric_only_len, unit); | ||
| 286 | } | ||
| 287 | |||
| 288 | static int first_shadow_cpu(struct perf_stat_config *config, | ||
| 289 | struct perf_evsel *evsel, int id) | ||
| 290 | { | ||
| 291 | struct perf_evlist *evlist = evsel->evlist; | ||
| 292 | int i; | ||
| 293 | |||
| 294 | if (!config->aggr_get_id) | ||
| 295 | return 0; | ||
| 296 | |||
| 297 | if (config->aggr_mode == AGGR_NONE) | ||
| 298 | return id; | ||
| 299 | |||
| 300 | if (config->aggr_mode == AGGR_GLOBAL) | ||
| 301 | return 0; | ||
| 302 | |||
| 303 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
| 304 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
| 305 | |||
| 306 | if (config->aggr_get_id(config, evlist->cpus, cpu2) == id) | ||
| 307 | return cpu2; | ||
| 308 | } | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | static void abs_printout(struct perf_stat_config *config, | ||
| 313 | int id, int nr, struct perf_evsel *evsel, double avg) | ||
| 314 | { | ||
| 315 | FILE *output = config->output; | ||
| 316 | double sc = evsel->scale; | ||
| 317 | const char *fmt; | ||
| 318 | |||
| 319 | if (config->csv_output) { | ||
| 320 | fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; | ||
| 321 | } else { | ||
| 322 | if (config->big_num) | ||
| 323 | fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s"; | ||
| 324 | else | ||
| 325 | fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s"; | ||
| 326 | } | ||
| 327 | |||
| 328 | aggr_printout(config, evsel, id, nr); | ||
| 329 | |||
| 330 | fprintf(output, fmt, avg, config->csv_sep); | ||
| 331 | |||
| 332 | if (evsel->unit) | ||
| 333 | fprintf(output, "%-*s%s", | ||
| 334 | config->csv_output ? 0 : config->unit_width, | ||
| 335 | evsel->unit, config->csv_sep); | ||
| 336 | |||
| 337 | fprintf(output, "%-*s", config->csv_output ? 0 : 25, perf_evsel__name(evsel)); | ||
| 338 | |||
| 339 | if (evsel->cgrp) | ||
| 340 | fprintf(output, "%s%s", config->csv_sep, evsel->cgrp->name); | ||
| 341 | } | ||
| 342 | |||
| 343 | static bool is_mixed_hw_group(struct perf_evsel *counter) | ||
| 344 | { | ||
| 345 | struct perf_evlist *evlist = counter->evlist; | ||
| 346 | u32 pmu_type = counter->attr.type; | ||
| 347 | struct perf_evsel *pos; | ||
| 348 | |||
| 349 | if (counter->nr_members < 2) | ||
| 350 | return false; | ||
| 351 | |||
| 352 | evlist__for_each_entry(evlist, pos) { | ||
| 353 | /* software events can be part of any hardware group */ | ||
| 354 | if (pos->attr.type == PERF_TYPE_SOFTWARE) | ||
| 355 | continue; | ||
| 356 | if (pmu_type == PERF_TYPE_SOFTWARE) { | ||
| 357 | pmu_type = pos->attr.type; | ||
| 358 | continue; | ||
| 359 | } | ||
| 360 | if (pmu_type != pos->attr.type) | ||
| 361 | return true; | ||
| 362 | } | ||
| 363 | |||
| 364 | return false; | ||
| 365 | } | ||
| 366 | |||
| 367 | static void printout(struct perf_stat_config *config, int id, int nr, | ||
| 368 | struct perf_evsel *counter, double uval, | ||
| 369 | char *prefix, u64 run, u64 ena, double noise, | ||
| 370 | struct runtime_stat *st) | ||
| 371 | { | ||
| 372 | struct perf_stat_output_ctx out; | ||
| 373 | struct outstate os = { | ||
| 374 | .fh = config->output, | ||
| 375 | .prefix = prefix ? prefix : "", | ||
| 376 | .id = id, | ||
| 377 | .nr = nr, | ||
| 378 | .evsel = counter, | ||
| 379 | }; | ||
| 380 | print_metric_t pm = print_metric_std; | ||
| 381 | new_line_t nl; | ||
| 382 | |||
| 383 | if (config->metric_only) { | ||
| 384 | nl = new_line_metric; | ||
| 385 | if (config->csv_output) | ||
| 386 | pm = print_metric_only_csv; | ||
| 387 | else | ||
| 388 | pm = print_metric_only; | ||
| 389 | } else | ||
| 390 | nl = new_line_std; | ||
| 391 | |||
| 392 | if (config->csv_output && !config->metric_only) { | ||
| 393 | static int aggr_fields[] = { | ||
| 394 | [AGGR_GLOBAL] = 0, | ||
| 395 | [AGGR_THREAD] = 1, | ||
| 396 | [AGGR_NONE] = 1, | ||
| 397 | [AGGR_SOCKET] = 2, | ||
| 398 | [AGGR_CORE] = 2, | ||
| 399 | }; | ||
| 400 | |||
| 401 | pm = print_metric_csv; | ||
| 402 | nl = new_line_csv; | ||
| 403 | os.nfields = 3; | ||
| 404 | os.nfields += aggr_fields[config->aggr_mode]; | ||
| 405 | if (counter->cgrp) | ||
| 406 | os.nfields++; | ||
| 407 | } | ||
| 408 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
| 409 | if (config->metric_only) { | ||
| 410 | pm(config, &os, NULL, "", "", 0); | ||
| 411 | return; | ||
| 412 | } | ||
| 413 | aggr_printout(config, counter, id, nr); | ||
| 414 | |||
| 415 | fprintf(config->output, "%*s%s", | ||
| 416 | config->csv_output ? 0 : 18, | ||
| 417 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
| 418 | config->csv_sep); | ||
| 419 | |||
| 420 | if (counter->supported) { | ||
| 421 | config->print_free_counters_hint = 1; | ||
| 422 | if (is_mixed_hw_group(counter)) | ||
| 423 | config->print_mixed_hw_group_error = 1; | ||
| 424 | } | ||
| 425 | |||
| 426 | fprintf(config->output, "%-*s%s", | ||
| 427 | config->csv_output ? 0 : config->unit_width, | ||
| 428 | counter->unit, config->csv_sep); | ||
| 429 | |||
| 430 | fprintf(config->output, "%*s", | ||
| 431 | config->csv_output ? 0 : -25, | ||
| 432 | perf_evsel__name(counter)); | ||
| 433 | |||
| 434 | if (counter->cgrp) | ||
| 435 | fprintf(config->output, "%s%s", | ||
| 436 | config->csv_sep, counter->cgrp->name); | ||
| 437 | |||
| 438 | if (!config->csv_output) | ||
| 439 | pm(config, &os, NULL, NULL, "", 0); | ||
| 440 | print_noise(config, counter, noise); | ||
| 441 | print_running(config, run, ena); | ||
| 442 | if (config->csv_output) | ||
| 443 | pm(config, &os, NULL, NULL, "", 0); | ||
| 444 | return; | ||
| 445 | } | ||
| 446 | |||
| 447 | if (!config->metric_only) | ||
| 448 | abs_printout(config, id, nr, counter, uval); | ||
| 449 | |||
| 450 | out.print_metric = pm; | ||
| 451 | out.new_line = nl; | ||
| 452 | out.ctx = &os; | ||
| 453 | out.force_header = false; | ||
| 454 | |||
| 455 | if (config->csv_output && !config->metric_only) { | ||
| 456 | print_noise(config, counter, noise); | ||
| 457 | print_running(config, run, ena); | ||
| 458 | } | ||
| 459 | |||
| 460 | perf_stat__print_shadow_stats(config, counter, uval, | ||
| 461 | first_shadow_cpu(config, counter, id), | ||
| 462 | &out, &config->metric_events, st); | ||
| 463 | if (!config->csv_output && !config->metric_only) { | ||
| 464 | print_noise(config, counter, noise); | ||
| 465 | print_running(config, run, ena); | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | static void aggr_update_shadow(struct perf_stat_config *config, | ||
| 470 | struct perf_evlist *evlist) | ||
| 471 | { | ||
| 472 | int cpu, s2, id, s; | ||
| 473 | u64 val; | ||
| 474 | struct perf_evsel *counter; | ||
| 475 | |||
| 476 | for (s = 0; s < config->aggr_map->nr; s++) { | ||
| 477 | id = config->aggr_map->map[s]; | ||
| 478 | evlist__for_each_entry(evlist, counter) { | ||
| 479 | val = 0; | ||
| 480 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 481 | s2 = config->aggr_get_id(config, evlist->cpus, cpu); | ||
| 482 | if (s2 != id) | ||
| 483 | continue; | ||
| 484 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
| 485 | } | ||
| 486 | perf_stat__update_shadow_stats(counter, val, | ||
| 487 | first_shadow_cpu(config, counter, id), | ||
| 488 | &rt_stat); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | static void uniquify_event_name(struct perf_evsel *counter) | ||
| 494 | { | ||
| 495 | char *new_name; | ||
| 496 | char *config; | ||
| 497 | |||
| 498 | if (counter->uniquified_name || | ||
| 499 | !counter->pmu_name || !strncmp(counter->name, counter->pmu_name, | ||
| 500 | strlen(counter->pmu_name))) | ||
| 501 | return; | ||
| 502 | |||
| 503 | config = strchr(counter->name, '/'); | ||
| 504 | if (config) { | ||
| 505 | if (asprintf(&new_name, | ||
| 506 | "%s%s", counter->pmu_name, config) > 0) { | ||
| 507 | free(counter->name); | ||
| 508 | counter->name = new_name; | ||
| 509 | } | ||
| 510 | } else { | ||
| 511 | if (asprintf(&new_name, | ||
| 512 | "%s [%s]", counter->name, counter->pmu_name) > 0) { | ||
| 513 | free(counter->name); | ||
| 514 | counter->name = new_name; | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | counter->uniquified_name = true; | ||
| 519 | } | ||
| 520 | |||
| 521 | static void collect_all_aliases(struct perf_stat_config *config, struct perf_evsel *counter, | ||
| 522 | void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data, | ||
| 523 | bool first), | ||
| 524 | void *data) | ||
| 525 | { | ||
| 526 | struct perf_evlist *evlist = counter->evlist; | ||
| 527 | struct perf_evsel *alias; | ||
| 528 | |||
| 529 | alias = list_prepare_entry(counter, &(evlist->entries), node); | ||
| 530 | list_for_each_entry_continue (alias, &evlist->entries, node) { | ||
| 531 | if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || | ||
| 532 | alias->scale != counter->scale || | ||
| 533 | alias->cgrp != counter->cgrp || | ||
| 534 | strcmp(alias->unit, counter->unit) || | ||
| 535 | perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter)) | ||
| 536 | break; | ||
| 537 | alias->merged_stat = true; | ||
| 538 | cb(config, alias, data, false); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | static bool collect_data(struct perf_stat_config *config, struct perf_evsel *counter, | ||
| 543 | void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data, | ||
| 544 | bool first), | ||
| 545 | void *data) | ||
| 546 | { | ||
| 547 | if (counter->merged_stat) | ||
| 548 | return false; | ||
| 549 | cb(config, counter, data, true); | ||
| 550 | if (config->no_merge) | ||
| 551 | uniquify_event_name(counter); | ||
| 552 | else if (counter->auto_merge_stats) | ||
| 553 | collect_all_aliases(config, counter, cb, data); | ||
| 554 | return true; | ||
| 555 | } | ||
| 556 | |||
| 557 | struct aggr_data { | ||
| 558 | u64 ena, run, val; | ||
| 559 | int id; | ||
| 560 | int nr; | ||
| 561 | int cpu; | ||
| 562 | }; | ||
| 563 | |||
| 564 | static void aggr_cb(struct perf_stat_config *config, | ||
| 565 | struct perf_evsel *counter, void *data, bool first) | ||
| 566 | { | ||
| 567 | struct aggr_data *ad = data; | ||
| 568 | int cpu, s2; | ||
| 569 | |||
| 570 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 571 | struct perf_counts_values *counts; | ||
| 572 | |||
| 573 | s2 = config->aggr_get_id(config, perf_evsel__cpus(counter), cpu); | ||
| 574 | if (s2 != ad->id) | ||
| 575 | continue; | ||
| 576 | if (first) | ||
| 577 | ad->nr++; | ||
| 578 | counts = perf_counts(counter->counts, cpu, 0); | ||
| 579 | /* | ||
| 580 | * When any result is bad, make them all to give | ||
| 581 | * consistent output in interval mode. | ||
| 582 | */ | ||
| 583 | if (counts->ena == 0 || counts->run == 0 || | ||
| 584 | counter->counts->scaled == -1) { | ||
| 585 | ad->ena = 0; | ||
| 586 | ad->run = 0; | ||
| 587 | break; | ||
| 588 | } | ||
| 589 | ad->val += counts->val; | ||
| 590 | ad->ena += counts->ena; | ||
| 591 | ad->run += counts->run; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | static void print_aggr(struct perf_stat_config *config, | ||
| 596 | struct perf_evlist *evlist, | ||
| 597 | char *prefix) | ||
| 598 | { | ||
| 599 | bool metric_only = config->metric_only; | ||
| 600 | FILE *output = config->output; | ||
| 601 | struct perf_evsel *counter; | ||
| 602 | int s, id, nr; | ||
| 603 | double uval; | ||
| 604 | u64 ena, run, val; | ||
| 605 | bool first; | ||
| 606 | |||
| 607 | if (!(config->aggr_map || config->aggr_get_id)) | ||
| 608 | return; | ||
| 609 | |||
| 610 | aggr_update_shadow(config, evlist); | ||
| 611 | |||
| 612 | /* | ||
| 613 | * With metric_only everything is on a single line. | ||
| 614 | * Without each counter has its own line. | ||
| 615 | */ | ||
| 616 | for (s = 0; s < config->aggr_map->nr; s++) { | ||
| 617 | struct aggr_data ad; | ||
| 618 | if (prefix && metric_only) | ||
| 619 | fprintf(output, "%s", prefix); | ||
| 620 | |||
| 621 | ad.id = id = config->aggr_map->map[s]; | ||
| 622 | first = true; | ||
| 623 | evlist__for_each_entry(evlist, counter) { | ||
| 624 | if (is_duration_time(counter)) | ||
| 625 | continue; | ||
| 626 | |||
| 627 | ad.val = ad.ena = ad.run = 0; | ||
| 628 | ad.nr = 0; | ||
| 629 | if (!collect_data(config, counter, aggr_cb, &ad)) | ||
| 630 | continue; | ||
| 631 | nr = ad.nr; | ||
| 632 | ena = ad.ena; | ||
| 633 | run = ad.run; | ||
| 634 | val = ad.val; | ||
| 635 | if (first && metric_only) { | ||
| 636 | first = false; | ||
| 637 | aggr_printout(config, counter, id, nr); | ||
| 638 | } | ||
| 639 | if (prefix && !metric_only) | ||
| 640 | fprintf(output, "%s", prefix); | ||
| 641 | |||
| 642 | uval = val * counter->scale; | ||
| 643 | printout(config, id, nr, counter, uval, prefix, | ||
| 644 | run, ena, 1.0, &rt_stat); | ||
| 645 | if (!metric_only) | ||
| 646 | fputc('\n', output); | ||
| 647 | } | ||
| 648 | if (metric_only) | ||
| 649 | fputc('\n', output); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | static int cmp_val(const void *a, const void *b) | ||
| 654 | { | ||
| 655 | return ((struct perf_aggr_thread_value *)b)->val - | ||
| 656 | ((struct perf_aggr_thread_value *)a)->val; | ||
| 657 | } | ||
| 658 | |||
| 659 | static struct perf_aggr_thread_value *sort_aggr_thread( | ||
| 660 | struct perf_evsel *counter, | ||
| 661 | int nthreads, int ncpus, | ||
| 662 | int *ret, | ||
| 663 | struct target *_target) | ||
| 664 | { | ||
| 665 | int cpu, thread, i = 0; | ||
| 666 | double uval; | ||
| 667 | struct perf_aggr_thread_value *buf; | ||
| 668 | |||
| 669 | buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); | ||
| 670 | if (!buf) | ||
| 671 | return NULL; | ||
| 672 | |||
| 673 | for (thread = 0; thread < nthreads; thread++) { | ||
| 674 | u64 ena = 0, run = 0, val = 0; | ||
| 675 | |||
| 676 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
| 677 | val += perf_counts(counter->counts, cpu, thread)->val; | ||
| 678 | ena += perf_counts(counter->counts, cpu, thread)->ena; | ||
| 679 | run += perf_counts(counter->counts, cpu, thread)->run; | ||
| 680 | } | ||
| 681 | |||
| 682 | uval = val * counter->scale; | ||
| 683 | |||
| 684 | /* | ||
| 685 | * Skip value 0 when enabling --per-thread globally, | ||
| 686 | * otherwise too many 0 output. | ||
| 687 | */ | ||
| 688 | if (uval == 0.0 && target__has_per_thread(_target)) | ||
| 689 | continue; | ||
| 690 | |||
| 691 | buf[i].counter = counter; | ||
| 692 | buf[i].id = thread; | ||
| 693 | buf[i].uval = uval; | ||
| 694 | buf[i].val = val; | ||
| 695 | buf[i].run = run; | ||
| 696 | buf[i].ena = ena; | ||
| 697 | i++; | ||
| 698 | } | ||
| 699 | |||
| 700 | qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); | ||
| 701 | |||
| 702 | if (ret) | ||
| 703 | *ret = i; | ||
| 704 | |||
| 705 | return buf; | ||
| 706 | } | ||
| 707 | |||
| 708 | static void print_aggr_thread(struct perf_stat_config *config, | ||
| 709 | struct target *_target, | ||
| 710 | struct perf_evsel *counter, char *prefix) | ||
| 711 | { | ||
| 712 | FILE *output = config->output; | ||
| 713 | int nthreads = thread_map__nr(counter->threads); | ||
| 714 | int ncpus = cpu_map__nr(counter->cpus); | ||
| 715 | int thread, sorted_threads, id; | ||
| 716 | struct perf_aggr_thread_value *buf; | ||
| 717 | |||
| 718 | buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads, _target); | ||
| 719 | if (!buf) { | ||
| 720 | perror("cannot sort aggr thread"); | ||
| 721 | return; | ||
| 722 | } | ||
| 723 | |||
| 724 | for (thread = 0; thread < sorted_threads; thread++) { | ||
| 725 | if (prefix) | ||
| 726 | fprintf(output, "%s", prefix); | ||
| 727 | |||
| 728 | id = buf[thread].id; | ||
| 729 | if (config->stats) | ||
| 730 | printout(config, id, 0, buf[thread].counter, buf[thread].uval, | ||
| 731 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
| 732 | &config->stats[id]); | ||
| 733 | else | ||
| 734 | printout(config, id, 0, buf[thread].counter, buf[thread].uval, | ||
| 735 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
| 736 | &rt_stat); | ||
| 737 | fputc('\n', output); | ||
| 738 | } | ||
| 739 | |||
| 740 | free(buf); | ||
| 741 | } | ||
| 742 | |||
| 743 | struct caggr_data { | ||
| 744 | double avg, avg_enabled, avg_running; | ||
| 745 | }; | ||
| 746 | |||
| 747 | static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused, | ||
| 748 | struct perf_evsel *counter, void *data, | ||
| 749 | bool first __maybe_unused) | ||
| 750 | { | ||
| 751 | struct caggr_data *cd = data; | ||
| 752 | struct perf_stat_evsel *ps = counter->stats; | ||
| 753 | |||
| 754 | cd->avg += avg_stats(&ps->res_stats[0]); | ||
| 755 | cd->avg_enabled += avg_stats(&ps->res_stats[1]); | ||
| 756 | cd->avg_running += avg_stats(&ps->res_stats[2]); | ||
| 757 | } | ||
| 758 | |||
| 759 | /* | ||
| 760 | * Print out the results of a single counter: | ||
| 761 | * aggregated counts in system-wide mode | ||
| 762 | */ | ||
| 763 | static void print_counter_aggr(struct perf_stat_config *config, | ||
| 764 | struct perf_evsel *counter, char *prefix) | ||
| 765 | { | ||
| 766 | bool metric_only = config->metric_only; | ||
| 767 | FILE *output = config->output; | ||
| 768 | double uval; | ||
| 769 | struct caggr_data cd = { .avg = 0.0 }; | ||
| 770 | |||
| 771 | if (!collect_data(config, counter, counter_aggr_cb, &cd)) | ||
| 772 | return; | ||
| 773 | |||
| 774 | if (prefix && !metric_only) | ||
| 775 | fprintf(output, "%s", prefix); | ||
| 776 | |||
| 777 | uval = cd.avg * counter->scale; | ||
| 778 | printout(config, -1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, | ||
| 779 | cd.avg, &rt_stat); | ||
| 780 | if (!metric_only) | ||
| 781 | fprintf(output, "\n"); | ||
| 782 | } | ||
| 783 | |||
| 784 | static void counter_cb(struct perf_stat_config *config __maybe_unused, | ||
| 785 | struct perf_evsel *counter, void *data, | ||
| 786 | bool first __maybe_unused) | ||
| 787 | { | ||
| 788 | struct aggr_data *ad = data; | ||
| 789 | |||
| 790 | ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; | ||
| 791 | ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; | ||
| 792 | ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; | ||
| 793 | } | ||
| 794 | |||
| 795 | /* | ||
| 796 | * Print out the results of a single counter: | ||
| 797 | * does not use aggregated count in system-wide | ||
| 798 | */ | ||
| 799 | static void print_counter(struct perf_stat_config *config, | ||
| 800 | struct perf_evsel *counter, char *prefix) | ||
| 801 | { | ||
| 802 | FILE *output = config->output; | ||
| 803 | u64 ena, run, val; | ||
| 804 | double uval; | ||
| 805 | int cpu; | ||
| 806 | |||
| 807 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 808 | struct aggr_data ad = { .cpu = cpu }; | ||
| 809 | |||
| 810 | if (!collect_data(config, counter, counter_cb, &ad)) | ||
| 811 | return; | ||
| 812 | val = ad.val; | ||
| 813 | ena = ad.ena; | ||
| 814 | run = ad.run; | ||
| 815 | |||
| 816 | if (prefix) | ||
| 817 | fprintf(output, "%s", prefix); | ||
| 818 | |||
| 819 | uval = val * counter->scale; | ||
| 820 | printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
| 821 | &rt_stat); | ||
| 822 | |||
| 823 | fputc('\n', output); | ||
| 824 | } | ||
| 825 | } | ||
| 826 | |||
| 827 | static void print_no_aggr_metric(struct perf_stat_config *config, | ||
| 828 | struct perf_evlist *evlist, | ||
| 829 | char *prefix) | ||
| 830 | { | ||
| 831 | int cpu; | ||
| 832 | int nrcpus = 0; | ||
| 833 | struct perf_evsel *counter; | ||
| 834 | u64 ena, run, val; | ||
| 835 | double uval; | ||
| 836 | |||
| 837 | nrcpus = evlist->cpus->nr; | ||
| 838 | for (cpu = 0; cpu < nrcpus; cpu++) { | ||
| 839 | bool first = true; | ||
| 840 | |||
| 841 | if (prefix) | ||
| 842 | fputs(prefix, config->output); | ||
| 843 | evlist__for_each_entry(evlist, counter) { | ||
| 844 | if (is_duration_time(counter)) | ||
| 845 | continue; | ||
| 846 | if (first) { | ||
| 847 | aggr_printout(config, counter, cpu, 0); | ||
| 848 | first = false; | ||
| 849 | } | ||
| 850 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
| 851 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
| 852 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
| 853 | |||
| 854 | uval = val * counter->scale; | ||
| 855 | printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
| 856 | &rt_stat); | ||
| 857 | } | ||
| 858 | fputc('\n', config->output); | ||
| 859 | } | ||
| 860 | } | ||
| 861 | |||
| 862 | static int aggr_header_lens[] = { | ||
| 863 | [AGGR_CORE] = 18, | ||
| 864 | [AGGR_SOCKET] = 12, | ||
| 865 | [AGGR_NONE] = 6, | ||
| 866 | [AGGR_THREAD] = 24, | ||
| 867 | [AGGR_GLOBAL] = 0, | ||
| 868 | }; | ||
| 869 | |||
| 870 | static const char *aggr_header_csv[] = { | ||
| 871 | [AGGR_CORE] = "core,cpus,", | ||
| 872 | [AGGR_SOCKET] = "socket,cpus", | ||
| 873 | [AGGR_NONE] = "cpu,", | ||
| 874 | [AGGR_THREAD] = "comm-pid,", | ||
| 875 | [AGGR_GLOBAL] = "" | ||
| 876 | }; | ||
| 877 | |||
| 878 | static void print_metric_headers(struct perf_stat_config *config, | ||
| 879 | struct perf_evlist *evlist, | ||
| 880 | const char *prefix, bool no_indent) | ||
| 881 | { | ||
| 882 | struct perf_stat_output_ctx out; | ||
| 883 | struct perf_evsel *counter; | ||
| 884 | struct outstate os = { | ||
| 885 | .fh = config->output | ||
| 886 | }; | ||
| 887 | |||
| 888 | if (prefix) | ||
| 889 | fprintf(config->output, "%s", prefix); | ||
| 890 | |||
| 891 | if (!config->csv_output && !no_indent) | ||
| 892 | fprintf(config->output, "%*s", | ||
| 893 | aggr_header_lens[config->aggr_mode], ""); | ||
| 894 | if (config->csv_output) { | ||
| 895 | if (config->interval) | ||
| 896 | fputs("time,", config->output); | ||
| 897 | fputs(aggr_header_csv[config->aggr_mode], config->output); | ||
| 898 | } | ||
| 899 | |||
| 900 | /* Print metrics headers only */ | ||
| 901 | evlist__for_each_entry(evlist, counter) { | ||
| 902 | if (is_duration_time(counter)) | ||
| 903 | continue; | ||
| 904 | os.evsel = counter; | ||
| 905 | out.ctx = &os; | ||
| 906 | out.print_metric = print_metric_header; | ||
| 907 | out.new_line = new_line_metric; | ||
| 908 | out.force_header = true; | ||
| 909 | os.evsel = counter; | ||
| 910 | perf_stat__print_shadow_stats(config, counter, 0, | ||
| 911 | 0, | ||
| 912 | &out, | ||
| 913 | &config->metric_events, | ||
| 914 | &rt_stat); | ||
| 915 | } | ||
| 916 | fputc('\n', config->output); | ||
| 917 | } | ||
| 918 | |||
| 919 | static void print_interval(struct perf_stat_config *config, | ||
| 920 | struct perf_evlist *evlist, | ||
| 921 | char *prefix, struct timespec *ts) | ||
| 922 | { | ||
| 923 | bool metric_only = config->metric_only; | ||
| 924 | unsigned int unit_width = config->unit_width; | ||
| 925 | FILE *output = config->output; | ||
| 926 | static int num_print_interval; | ||
| 927 | |||
| 928 | if (config->interval_clear) | ||
| 929 | puts(CONSOLE_CLEAR); | ||
| 930 | |||
| 931 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, config->csv_sep); | ||
| 932 | |||
| 933 | if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) { | ||
| 934 | switch (config->aggr_mode) { | ||
| 935 | case AGGR_SOCKET: | ||
| 936 | fprintf(output, "# time socket cpus"); | ||
| 937 | if (!metric_only) | ||
| 938 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 939 | break; | ||
| 940 | case AGGR_CORE: | ||
| 941 | fprintf(output, "# time core cpus"); | ||
| 942 | if (!metric_only) | ||
| 943 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 944 | break; | ||
| 945 | case AGGR_NONE: | ||
| 946 | fprintf(output, "# time CPU "); | ||
| 947 | if (!metric_only) | ||
| 948 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 949 | break; | ||
| 950 | case AGGR_THREAD: | ||
| 951 | fprintf(output, "# time comm-pid"); | ||
| 952 | if (!metric_only) | ||
| 953 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 954 | break; | ||
| 955 | case AGGR_GLOBAL: | ||
| 956 | default: | ||
| 957 | fprintf(output, "# time"); | ||
| 958 | if (!metric_only) | ||
| 959 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
| 960 | case AGGR_UNSET: | ||
| 961 | break; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | if ((num_print_interval == 0 || config->interval_clear) && metric_only) | ||
| 966 | print_metric_headers(config, evlist, " ", true); | ||
| 967 | if (++num_print_interval == 25) | ||
| 968 | num_print_interval = 0; | ||
| 969 | } | ||
| 970 | |||
| 971 | static void print_header(struct perf_stat_config *config, | ||
| 972 | struct target *_target, | ||
| 973 | int argc, const char **argv) | ||
| 974 | { | ||
| 975 | FILE *output = config->output; | ||
| 976 | int i; | ||
| 977 | |||
| 978 | fflush(stdout); | ||
| 979 | |||
| 980 | if (!config->csv_output) { | ||
| 981 | fprintf(output, "\n"); | ||
| 982 | fprintf(output, " Performance counter stats for "); | ||
| 983 | if (_target->system_wide) | ||
| 984 | fprintf(output, "\'system wide"); | ||
| 985 | else if (_target->cpu_list) | ||
| 986 | fprintf(output, "\'CPU(s) %s", _target->cpu_list); | ||
| 987 | else if (!target__has_task(_target)) { | ||
| 988 | fprintf(output, "\'%s", argv ? argv[0] : "pipe"); | ||
| 989 | for (i = 1; argv && (i < argc); i++) | ||
| 990 | fprintf(output, " %s", argv[i]); | ||
| 991 | } else if (_target->pid) | ||
| 992 | fprintf(output, "process id \'%s", _target->pid); | ||
| 993 | else | ||
| 994 | fprintf(output, "thread id \'%s", _target->tid); | ||
| 995 | |||
| 996 | fprintf(output, "\'"); | ||
| 997 | if (config->run_count > 1) | ||
| 998 | fprintf(output, " (%d runs)", config->run_count); | ||
| 999 | fprintf(output, ":\n\n"); | ||
| 1000 | } | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | static int get_precision(double num) | ||
| 1004 | { | ||
| 1005 | if (num > 1) | ||
| 1006 | return 0; | ||
| 1007 | |||
| 1008 | return lround(ceil(-log10(num))); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | static void print_table(struct perf_stat_config *config, | ||
| 1012 | FILE *output, int precision, double avg) | ||
| 1013 | { | ||
| 1014 | char tmp[64]; | ||
| 1015 | int idx, indent = 0; | ||
| 1016 | |||
| 1017 | scnprintf(tmp, 64, " %17.*f", precision, avg); | ||
| 1018 | while (tmp[indent] == ' ') | ||
| 1019 | indent++; | ||
| 1020 | |||
| 1021 | fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); | ||
| 1022 | |||
| 1023 | for (idx = 0; idx < config->run_count; idx++) { | ||
| 1024 | double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; | ||
| 1025 | int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); | ||
| 1026 | |||
| 1027 | fprintf(output, " %17.*f (%+.*f) ", | ||
| 1028 | precision, run, precision, run - avg); | ||
| 1029 | |||
| 1030 | for (h = 0; h < n; h++) | ||
| 1031 | fprintf(output, "#"); | ||
| 1032 | |||
| 1033 | fprintf(output, "\n"); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | fprintf(output, "\n%*s# Final result:\n", indent, ""); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | static double timeval2double(struct timeval *t) | ||
| 1040 | { | ||
| 1041 | return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | static void print_footer(struct perf_stat_config *config) | ||
| 1045 | { | ||
| 1046 | double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | ||
| 1047 | FILE *output = config->output; | ||
| 1048 | int n; | ||
| 1049 | |||
| 1050 | if (!config->null_run) | ||
| 1051 | fprintf(output, "\n"); | ||
| 1052 | |||
| 1053 | if (config->run_count == 1) { | ||
| 1054 | fprintf(output, " %17.9f seconds time elapsed", avg); | ||
| 1055 | |||
| 1056 | if (config->ru_display) { | ||
| 1057 | double ru_utime = timeval2double(&config->ru_data.ru_utime); | ||
| 1058 | double ru_stime = timeval2double(&config->ru_data.ru_stime); | ||
| 1059 | |||
| 1060 | fprintf(output, "\n\n"); | ||
| 1061 | fprintf(output, " %17.9f seconds user\n", ru_utime); | ||
| 1062 | fprintf(output, " %17.9f seconds sys\n", ru_stime); | ||
| 1063 | } | ||
| 1064 | } else { | ||
| 1065 | double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | ||
| 1066 | /* | ||
| 1067 | * Display at most 2 more significant | ||
| 1068 | * digits than the stddev inaccuracy. | ||
| 1069 | */ | ||
| 1070 | int precision = get_precision(sd) + 2; | ||
| 1071 | |||
| 1072 | if (config->walltime_run_table) | ||
| 1073 | print_table(config, output, precision, avg); | ||
| 1074 | |||
| 1075 | fprintf(output, " %17.*f +- %.*f seconds time elapsed", | ||
| 1076 | precision, avg, precision, sd); | ||
| 1077 | |||
| 1078 | print_noise_pct(config, sd, avg); | ||
| 1079 | } | ||
| 1080 | fprintf(output, "\n\n"); | ||
| 1081 | |||
| 1082 | if (config->print_free_counters_hint && | ||
| 1083 | sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 && | ||
| 1084 | n > 0) | ||
| 1085 | fprintf(output, | ||
| 1086 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | ||
| 1087 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | ||
| 1088 | " perf stat ...\n" | ||
| 1089 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | ||
| 1090 | |||
| 1091 | if (config->print_mixed_hw_group_error) | ||
| 1092 | fprintf(output, | ||
| 1093 | "The events in group usually have to be from " | ||
| 1094 | "the same PMU. Try reorganizing the group.\n"); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | void | ||
| 1098 | perf_evlist__print_counters(struct perf_evlist *evlist, | ||
| 1099 | struct perf_stat_config *config, | ||
| 1100 | struct target *_target, | ||
| 1101 | struct timespec *ts, | ||
| 1102 | int argc, const char **argv) | ||
| 1103 | { | ||
| 1104 | bool metric_only = config->metric_only; | ||
| 1105 | int interval = config->interval; | ||
| 1106 | struct perf_evsel *counter; | ||
| 1107 | char buf[64], *prefix = NULL; | ||
| 1108 | |||
| 1109 | if (interval) | ||
| 1110 | print_interval(config, evlist, prefix = buf, ts); | ||
| 1111 | else | ||
| 1112 | print_header(config, _target, argc, argv); | ||
| 1113 | |||
| 1114 | if (metric_only) { | ||
| 1115 | static int num_print_iv; | ||
| 1116 | |||
| 1117 | if (num_print_iv == 0 && !interval) | ||
| 1118 | print_metric_headers(config, evlist, prefix, false); | ||
| 1119 | if (num_print_iv++ == 25) | ||
| 1120 | num_print_iv = 0; | ||
| 1121 | if (config->aggr_mode == AGGR_GLOBAL && prefix) | ||
| 1122 | fprintf(config->output, "%s", prefix); | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | switch (config->aggr_mode) { | ||
| 1126 | case AGGR_CORE: | ||
| 1127 | case AGGR_SOCKET: | ||
| 1128 | print_aggr(config, evlist, prefix); | ||
| 1129 | break; | ||
| 1130 | case AGGR_THREAD: | ||
| 1131 | evlist__for_each_entry(evlist, counter) { | ||
| 1132 | if (is_duration_time(counter)) | ||
| 1133 | continue; | ||
| 1134 | print_aggr_thread(config, _target, counter, prefix); | ||
| 1135 | } | ||
| 1136 | break; | ||
| 1137 | case AGGR_GLOBAL: | ||
| 1138 | evlist__for_each_entry(evlist, counter) { | ||
| 1139 | if (is_duration_time(counter)) | ||
| 1140 | continue; | ||
| 1141 | print_counter_aggr(config, counter, prefix); | ||
| 1142 | } | ||
| 1143 | if (metric_only) | ||
| 1144 | fputc('\n', config->output); | ||
| 1145 | break; | ||
| 1146 | case AGGR_NONE: | ||
| 1147 | if (metric_only) | ||
| 1148 | print_no_aggr_metric(config, evlist, prefix); | ||
| 1149 | else { | ||
| 1150 | evlist__for_each_entry(evlist, counter) { | ||
| 1151 | if (is_duration_time(counter)) | ||
| 1152 | continue; | ||
| 1153 | print_counter(config, counter, prefix); | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | break; | ||
| 1157 | case AGGR_UNSET: | ||
| 1158 | default: | ||
| 1159 | break; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | if (!interval && !config->csv_output) | ||
| 1163 | print_footer(config); | ||
| 1164 | |||
| 1165 | fflush(config->output); | ||
| 1166 | } | ||
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 99990f5f2512..8ad32763cfff 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c | |||
| @@ -410,7 +410,8 @@ static double runtime_stat_n(struct runtime_stat *st, | |||
| 410 | return v->stats.n; | 410 | return v->stats.n; |
| 411 | } | 411 | } |
| 412 | 412 | ||
| 413 | static void print_stalled_cycles_frontend(int cpu, | 413 | static void print_stalled_cycles_frontend(struct perf_stat_config *config, |
| 414 | int cpu, | ||
| 414 | struct perf_evsel *evsel, double avg, | 415 | struct perf_evsel *evsel, double avg, |
| 415 | struct perf_stat_output_ctx *out, | 416 | struct perf_stat_output_ctx *out, |
| 416 | struct runtime_stat *st) | 417 | struct runtime_stat *st) |
| @@ -427,13 +428,14 @@ static void print_stalled_cycles_frontend(int cpu, | |||
| 427 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); | 428 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); |
| 428 | 429 | ||
| 429 | if (ratio) | 430 | if (ratio) |
| 430 | out->print_metric(out->ctx, color, "%7.2f%%", "frontend cycles idle", | 431 | out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle", |
| 431 | ratio); | 432 | ratio); |
| 432 | else | 433 | else |
| 433 | out->print_metric(out->ctx, NULL, NULL, "frontend cycles idle", 0); | 434 | out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0); |
| 434 | } | 435 | } |
| 435 | 436 | ||
| 436 | static void print_stalled_cycles_backend(int cpu, | 437 | static void print_stalled_cycles_backend(struct perf_stat_config *config, |
| 438 | int cpu, | ||
| 437 | struct perf_evsel *evsel, double avg, | 439 | struct perf_evsel *evsel, double avg, |
| 438 | struct perf_stat_output_ctx *out, | 440 | struct perf_stat_output_ctx *out, |
| 439 | struct runtime_stat *st) | 441 | struct runtime_stat *st) |
| @@ -449,10 +451,11 @@ static void print_stalled_cycles_backend(int cpu, | |||
| 449 | 451 | ||
| 450 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); | 452 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); |
| 451 | 453 | ||
| 452 | out->print_metric(out->ctx, color, "%7.2f%%", "backend cycles idle", ratio); | 454 | out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio); |
| 453 | } | 455 | } |
| 454 | 456 | ||
| 455 | static void print_branch_misses(int cpu, | 457 | static void print_branch_misses(struct perf_stat_config *config, |
| 458 | int cpu, | ||
| 456 | struct perf_evsel *evsel, | 459 | struct perf_evsel *evsel, |
| 457 | double avg, | 460 | double avg, |
| 458 | struct perf_stat_output_ctx *out, | 461 | struct perf_stat_output_ctx *out, |
| @@ -469,10 +472,11 @@ static void print_branch_misses(int cpu, | |||
| 469 | 472 | ||
| 470 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 473 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 471 | 474 | ||
| 472 | out->print_metric(out->ctx, color, "%7.2f%%", "of all branches", ratio); | 475 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio); |
| 473 | } | 476 | } |
| 474 | 477 | ||
| 475 | static void print_l1_dcache_misses(int cpu, | 478 | static void print_l1_dcache_misses(struct perf_stat_config *config, |
| 479 | int cpu, | ||
| 476 | struct perf_evsel *evsel, | 480 | struct perf_evsel *evsel, |
| 477 | double avg, | 481 | double avg, |
| 478 | struct perf_stat_output_ctx *out, | 482 | struct perf_stat_output_ctx *out, |
| @@ -490,10 +494,11 @@ static void print_l1_dcache_misses(int cpu, | |||
| 490 | 494 | ||
| 491 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 495 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 492 | 496 | ||
| 493 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio); | 497 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio); |
| 494 | } | 498 | } |
| 495 | 499 | ||
| 496 | static void print_l1_icache_misses(int cpu, | 500 | static void print_l1_icache_misses(struct perf_stat_config *config, |
| 501 | int cpu, | ||
| 497 | struct perf_evsel *evsel, | 502 | struct perf_evsel *evsel, |
| 498 | double avg, | 503 | double avg, |
| 499 | struct perf_stat_output_ctx *out, | 504 | struct perf_stat_output_ctx *out, |
| @@ -510,10 +515,11 @@ static void print_l1_icache_misses(int cpu, | |||
| 510 | ratio = avg / total * 100.0; | 515 | ratio = avg / total * 100.0; |
| 511 | 516 | ||
| 512 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 517 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 513 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio); | 518 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio); |
| 514 | } | 519 | } |
| 515 | 520 | ||
| 516 | static void print_dtlb_cache_misses(int cpu, | 521 | static void print_dtlb_cache_misses(struct perf_stat_config *config, |
| 522 | int cpu, | ||
| 517 | struct perf_evsel *evsel, | 523 | struct perf_evsel *evsel, |
| 518 | double avg, | 524 | double avg, |
| 519 | struct perf_stat_output_ctx *out, | 525 | struct perf_stat_output_ctx *out, |
| @@ -529,10 +535,11 @@ static void print_dtlb_cache_misses(int cpu, | |||
| 529 | ratio = avg / total * 100.0; | 535 | ratio = avg / total * 100.0; |
| 530 | 536 | ||
| 531 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 537 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 532 | out->print_metric(out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio); | 538 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio); |
| 533 | } | 539 | } |
| 534 | 540 | ||
| 535 | static void print_itlb_cache_misses(int cpu, | 541 | static void print_itlb_cache_misses(struct perf_stat_config *config, |
| 542 | int cpu, | ||
| 536 | struct perf_evsel *evsel, | 543 | struct perf_evsel *evsel, |
| 537 | double avg, | 544 | double avg, |
| 538 | struct perf_stat_output_ctx *out, | 545 | struct perf_stat_output_ctx *out, |
| @@ -548,10 +555,11 @@ static void print_itlb_cache_misses(int cpu, | |||
| 548 | ratio = avg / total * 100.0; | 555 | ratio = avg / total * 100.0; |
| 549 | 556 | ||
| 550 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 557 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 551 | out->print_metric(out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio); | 558 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio); |
| 552 | } | 559 | } |
| 553 | 560 | ||
| 554 | static void print_ll_cache_misses(int cpu, | 561 | static void print_ll_cache_misses(struct perf_stat_config *config, |
| 562 | int cpu, | ||
| 555 | struct perf_evsel *evsel, | 563 | struct perf_evsel *evsel, |
| 556 | double avg, | 564 | double avg, |
| 557 | struct perf_stat_output_ctx *out, | 565 | struct perf_stat_output_ctx *out, |
| @@ -567,7 +575,7 @@ static void print_ll_cache_misses(int cpu, | |||
| 567 | ratio = avg / total * 100.0; | 575 | ratio = avg / total * 100.0; |
| 568 | 576 | ||
| 569 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 577 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
| 570 | out->print_metric(out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio); | 578 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio); |
| 571 | } | 579 | } |
| 572 | 580 | ||
| 573 | /* | 581 | /* |
| @@ -674,7 +682,8 @@ static double td_be_bound(int ctx, int cpu, struct runtime_stat *st) | |||
| 674 | return sanitize_val(1.0 - sum); | 682 | return sanitize_val(1.0 - sum); |
| 675 | } | 683 | } |
| 676 | 684 | ||
| 677 | static void print_smi_cost(int cpu, struct perf_evsel *evsel, | 685 | static void print_smi_cost(struct perf_stat_config *config, |
| 686 | int cpu, struct perf_evsel *evsel, | ||
| 678 | struct perf_stat_output_ctx *out, | 687 | struct perf_stat_output_ctx *out, |
| 679 | struct runtime_stat *st) | 688 | struct runtime_stat *st) |
| 680 | { | 689 | { |
| @@ -694,11 +703,12 @@ static void print_smi_cost(int cpu, struct perf_evsel *evsel, | |||
| 694 | 703 | ||
| 695 | if (cost > 10) | 704 | if (cost > 10) |
| 696 | color = PERF_COLOR_RED; | 705 | color = PERF_COLOR_RED; |
| 697 | out->print_metric(out->ctx, color, "%8.1f%%", "SMI cycles%", cost); | 706 | out->print_metric(config, out->ctx, color, "%8.1f%%", "SMI cycles%", cost); |
| 698 | out->print_metric(out->ctx, NULL, "%4.0f", "SMI#", smi_num); | 707 | out->print_metric(config, out->ctx, NULL, "%4.0f", "SMI#", smi_num); |
| 699 | } | 708 | } |
| 700 | 709 | ||
| 701 | static void generic_metric(const char *metric_expr, | 710 | static void generic_metric(struct perf_stat_config *config, |
| 711 | const char *metric_expr, | ||
| 702 | struct perf_evsel **metric_events, | 712 | struct perf_evsel **metric_events, |
| 703 | char *name, | 713 | char *name, |
| 704 | const char *metric_name, | 714 | const char *metric_name, |
| @@ -737,20 +747,21 @@ static void generic_metric(const char *metric_expr, | |||
| 737 | const char *p = metric_expr; | 747 | const char *p = metric_expr; |
| 738 | 748 | ||
| 739 | if (expr__parse(&ratio, &pctx, &p) == 0) | 749 | if (expr__parse(&ratio, &pctx, &p) == 0) |
| 740 | print_metric(ctxp, NULL, "%8.1f", | 750 | print_metric(config, ctxp, NULL, "%8.1f", |
| 741 | metric_name ? | 751 | metric_name ? |
| 742 | metric_name : | 752 | metric_name : |
| 743 | out->force_header ? name : "", | 753 | out->force_header ? name : "", |
| 744 | ratio); | 754 | ratio); |
| 745 | else | 755 | else |
| 746 | print_metric(ctxp, NULL, NULL, | 756 | print_metric(config, ctxp, NULL, NULL, |
| 747 | out->force_header ? | 757 | out->force_header ? |
| 748 | (metric_name ? metric_name : name) : "", 0); | 758 | (metric_name ? metric_name : name) : "", 0); |
| 749 | } else | 759 | } else |
| 750 | print_metric(ctxp, NULL, NULL, "", 0); | 760 | print_metric(config, ctxp, NULL, NULL, "", 0); |
| 751 | } | 761 | } |
| 752 | 762 | ||
| 753 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | 763 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
| 764 | struct perf_evsel *evsel, | ||
| 754 | double avg, int cpu, | 765 | double avg, int cpu, |
| 755 | struct perf_stat_output_ctx *out, | 766 | struct perf_stat_output_ctx *out, |
| 756 | struct rblist *metric_events, | 767 | struct rblist *metric_events, |
| @@ -769,10 +780,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 769 | 780 | ||
| 770 | if (total) { | 781 | if (total) { |
| 771 | ratio = avg / total; | 782 | ratio = avg / total; |
| 772 | print_metric(ctxp, NULL, "%7.2f ", | 783 | print_metric(config, ctxp, NULL, "%7.2f ", |
| 773 | "insn per cycle", ratio); | 784 | "insn per cycle", ratio); |
| 774 | } else { | 785 | } else { |
| 775 | print_metric(ctxp, NULL, NULL, "insn per cycle", 0); | 786 | print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0); |
| 776 | } | 787 | } |
| 777 | 788 | ||
| 778 | total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, | 789 | total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, |
| @@ -783,20 +794,20 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 783 | ctx, cpu)); | 794 | ctx, cpu)); |
| 784 | 795 | ||
| 785 | if (total && avg) { | 796 | if (total && avg) { |
| 786 | out->new_line(ctxp); | 797 | out->new_line(config, ctxp); |
| 787 | ratio = total / avg; | 798 | ratio = total / avg; |
| 788 | print_metric(ctxp, NULL, "%7.2f ", | 799 | print_metric(config, ctxp, NULL, "%7.2f ", |
| 789 | "stalled cycles per insn", | 800 | "stalled cycles per insn", |
| 790 | ratio); | 801 | ratio); |
| 791 | } else if (have_frontend_stalled) { | 802 | } else if (have_frontend_stalled) { |
| 792 | print_metric(ctxp, NULL, NULL, | 803 | print_metric(config, ctxp, NULL, NULL, |
| 793 | "stalled cycles per insn", 0); | 804 | "stalled cycles per insn", 0); |
| 794 | } | 805 | } |
| 795 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { | 806 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { |
| 796 | if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0) | 807 | if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0) |
| 797 | print_branch_misses(cpu, evsel, avg, out, st); | 808 | print_branch_misses(config, cpu, evsel, avg, out, st); |
| 798 | else | 809 | else |
| 799 | print_metric(ctxp, NULL, NULL, "of all branches", 0); | 810 | print_metric(config, ctxp, NULL, NULL, "of all branches", 0); |
| 800 | } else if ( | 811 | } else if ( |
| 801 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 812 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
| 802 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | | 813 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | |
| @@ -804,9 +815,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 804 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 815 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
| 805 | 816 | ||
| 806 | if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0) | 817 | if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0) |
| 807 | print_l1_dcache_misses(cpu, evsel, avg, out, st); | 818 | print_l1_dcache_misses(config, cpu, evsel, avg, out, st); |
| 808 | else | 819 | else |
| 809 | print_metric(ctxp, NULL, NULL, "of all L1-dcache hits", 0); | 820 | print_metric(config, ctxp, NULL, NULL, "of all L1-dcache hits", 0); |
| 810 | } else if ( | 821 | } else if ( |
| 811 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 822 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
| 812 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | | 823 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | |
| @@ -814,9 +825,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 814 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 825 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
| 815 | 826 | ||
| 816 | if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0) | 827 | if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0) |
| 817 | print_l1_icache_misses(cpu, evsel, avg, out, st); | 828 | print_l1_icache_misses(config, cpu, evsel, avg, out, st); |
| 818 | else | 829 | else |
| 819 | print_metric(ctxp, NULL, NULL, "of all L1-icache hits", 0); | 830 | print_metric(config, ctxp, NULL, NULL, "of all L1-icache hits", 0); |
| 820 | } else if ( | 831 | } else if ( |
| 821 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 832 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
| 822 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | | 833 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | |
| @@ -824,9 +835,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 824 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 835 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
| 825 | 836 | ||
| 826 | if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0) | 837 | if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0) |
| 827 | print_dtlb_cache_misses(cpu, evsel, avg, out, st); | 838 | print_dtlb_cache_misses(config, cpu, evsel, avg, out, st); |
| 828 | else | 839 | else |
| 829 | print_metric(ctxp, NULL, NULL, "of all dTLB cache hits", 0); | 840 | print_metric(config, ctxp, NULL, NULL, "of all dTLB cache hits", 0); |
| 830 | } else if ( | 841 | } else if ( |
| 831 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 842 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
| 832 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | | 843 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | |
| @@ -834,9 +845,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 834 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 845 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
| 835 | 846 | ||
| 836 | if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0) | 847 | if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0) |
| 837 | print_itlb_cache_misses(cpu, evsel, avg, out, st); | 848 | print_itlb_cache_misses(config, cpu, evsel, avg, out, st); |
| 838 | else | 849 | else |
| 839 | print_metric(ctxp, NULL, NULL, "of all iTLB cache hits", 0); | 850 | print_metric(config, ctxp, NULL, NULL, "of all iTLB cache hits", 0); |
| 840 | } else if ( | 851 | } else if ( |
| 841 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 852 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
| 842 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | | 853 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | |
| @@ -844,9 +855,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 844 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 855 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
| 845 | 856 | ||
| 846 | if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0) | 857 | if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0) |
| 847 | print_ll_cache_misses(cpu, evsel, avg, out, st); | 858 | print_ll_cache_misses(config, cpu, evsel, avg, out, st); |
| 848 | else | 859 | else |
| 849 | print_metric(ctxp, NULL, NULL, "of all LL-cache hits", 0); | 860 | print_metric(config, ctxp, NULL, NULL, "of all LL-cache hits", 0); |
| 850 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { | 861 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { |
| 851 | total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu); | 862 | total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu); |
| 852 | 863 | ||
| @@ -854,32 +865,32 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 854 | ratio = avg * 100 / total; | 865 | ratio = avg * 100 / total; |
| 855 | 866 | ||
| 856 | if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0) | 867 | if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0) |
| 857 | print_metric(ctxp, NULL, "%8.3f %%", | 868 | print_metric(config, ctxp, NULL, "%8.3f %%", |
| 858 | "of all cache refs", ratio); | 869 | "of all cache refs", ratio); |
| 859 | else | 870 | else |
| 860 | print_metric(ctxp, NULL, NULL, "of all cache refs", 0); | 871 | print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0); |
| 861 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { | 872 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
| 862 | print_stalled_cycles_frontend(cpu, evsel, avg, out, st); | 873 | print_stalled_cycles_frontend(config, cpu, evsel, avg, out, st); |
| 863 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { | 874 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { |
| 864 | print_stalled_cycles_backend(cpu, evsel, avg, out, st); | 875 | print_stalled_cycles_backend(config, cpu, evsel, avg, out, st); |
| 865 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { | 876 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { |
| 866 | total = runtime_stat_avg(st, STAT_NSECS, 0, cpu); | 877 | total = runtime_stat_avg(st, STAT_NSECS, 0, cpu); |
| 867 | 878 | ||
| 868 | if (total) { | 879 | if (total) { |
| 869 | ratio = avg / total; | 880 | ratio = avg / total; |
| 870 | print_metric(ctxp, NULL, "%8.3f", "GHz", ratio); | 881 | print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio); |
| 871 | } else { | 882 | } else { |
| 872 | print_metric(ctxp, NULL, NULL, "Ghz", 0); | 883 | print_metric(config, ctxp, NULL, NULL, "Ghz", 0); |
| 873 | } | 884 | } |
| 874 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { | 885 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { |
| 875 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); | 886 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); |
| 876 | 887 | ||
| 877 | if (total) | 888 | if (total) |
| 878 | print_metric(ctxp, NULL, | 889 | print_metric(config, ctxp, NULL, |
| 879 | "%7.2f%%", "transactional cycles", | 890 | "%7.2f%%", "transactional cycles", |
| 880 | 100.0 * (avg / total)); | 891 | 100.0 * (avg / total)); |
| 881 | else | 892 | else |
| 882 | print_metric(ctxp, NULL, NULL, "transactional cycles", | 893 | print_metric(config, ctxp, NULL, NULL, "transactional cycles", |
| 883 | 0); | 894 | 0); |
| 884 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { | 895 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { |
| 885 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); | 896 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); |
| @@ -888,10 +899,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 888 | if (total2 < avg) | 899 | if (total2 < avg) |
| 889 | total2 = avg; | 900 | total2 = avg; |
| 890 | if (total) | 901 | if (total) |
| 891 | print_metric(ctxp, NULL, "%7.2f%%", "aborted cycles", | 902 | print_metric(config, ctxp, NULL, "%7.2f%%", "aborted cycles", |
| 892 | 100.0 * ((total2-avg) / total)); | 903 | 100.0 * ((total2-avg) / total)); |
| 893 | else | 904 | else |
| 894 | print_metric(ctxp, NULL, NULL, "aborted cycles", 0); | 905 | print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0); |
| 895 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) { | 906 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) { |
| 896 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, | 907 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, |
| 897 | ctx, cpu); | 908 | ctx, cpu); |
| @@ -900,10 +911,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 900 | ratio = total / avg; | 911 | ratio = total / avg; |
| 901 | 912 | ||
| 902 | if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0) | 913 | if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0) |
| 903 | print_metric(ctxp, NULL, "%8.0f", | 914 | print_metric(config, ctxp, NULL, "%8.0f", |
| 904 | "cycles / transaction", ratio); | 915 | "cycles / transaction", ratio); |
| 905 | else | 916 | else |
| 906 | print_metric(ctxp, NULL, NULL, "cycles / transaction", | 917 | print_metric(config, ctxp, NULL, NULL, "cycles / transaction", |
| 907 | 0); | 918 | 0); |
| 908 | } else if (perf_stat_evsel__is(evsel, ELISION_START)) { | 919 | } else if (perf_stat_evsel__is(evsel, ELISION_START)) { |
| 909 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, | 920 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, |
| @@ -912,33 +923,33 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 912 | if (avg) | 923 | if (avg) |
| 913 | ratio = total / avg; | 924 | ratio = total / avg; |
| 914 | 925 | ||
| 915 | print_metric(ctxp, NULL, "%8.0f", "cycles / elision", ratio); | 926 | print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio); |
| 916 | } else if (perf_evsel__is_clock(evsel)) { | 927 | } else if (perf_evsel__is_clock(evsel)) { |
| 917 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) | 928 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) |
| 918 | print_metric(ctxp, NULL, "%8.3f", "CPUs utilized", | 929 | print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized", |
| 919 | avg / (ratio * evsel->scale)); | 930 | avg / (ratio * evsel->scale)); |
| 920 | else | 931 | else |
| 921 | print_metric(ctxp, NULL, NULL, "CPUs utilized", 0); | 932 | print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0); |
| 922 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) { | 933 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) { |
| 923 | double fe_bound = td_fe_bound(ctx, cpu, st); | 934 | double fe_bound = td_fe_bound(ctx, cpu, st); |
| 924 | 935 | ||
| 925 | if (fe_bound > 0.2) | 936 | if (fe_bound > 0.2) |
| 926 | color = PERF_COLOR_RED; | 937 | color = PERF_COLOR_RED; |
| 927 | print_metric(ctxp, color, "%8.1f%%", "frontend bound", | 938 | print_metric(config, ctxp, color, "%8.1f%%", "frontend bound", |
| 928 | fe_bound * 100.); | 939 | fe_bound * 100.); |
| 929 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) { | 940 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) { |
| 930 | double retiring = td_retiring(ctx, cpu, st); | 941 | double retiring = td_retiring(ctx, cpu, st); |
| 931 | 942 | ||
| 932 | if (retiring > 0.7) | 943 | if (retiring > 0.7) |
| 933 | color = PERF_COLOR_GREEN; | 944 | color = PERF_COLOR_GREEN; |
| 934 | print_metric(ctxp, color, "%8.1f%%", "retiring", | 945 | print_metric(config, ctxp, color, "%8.1f%%", "retiring", |
| 935 | retiring * 100.); | 946 | retiring * 100.); |
| 936 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) { | 947 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) { |
| 937 | double bad_spec = td_bad_spec(ctx, cpu, st); | 948 | double bad_spec = td_bad_spec(ctx, cpu, st); |
| 938 | 949 | ||
| 939 | if (bad_spec > 0.1) | 950 | if (bad_spec > 0.1) |
| 940 | color = PERF_COLOR_RED; | 951 | color = PERF_COLOR_RED; |
| 941 | print_metric(ctxp, color, "%8.1f%%", "bad speculation", | 952 | print_metric(config, ctxp, color, "%8.1f%%", "bad speculation", |
| 942 | bad_spec * 100.); | 953 | bad_spec * 100.); |
| 943 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) { | 954 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) { |
| 944 | double be_bound = td_be_bound(ctx, cpu, st); | 955 | double be_bound = td_be_bound(ctx, cpu, st); |
| @@ -955,12 +966,12 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 955 | if (be_bound > 0.2) | 966 | if (be_bound > 0.2) |
| 956 | color = PERF_COLOR_RED; | 967 | color = PERF_COLOR_RED; |
| 957 | if (td_total_slots(ctx, cpu, st) > 0) | 968 | if (td_total_slots(ctx, cpu, st) > 0) |
| 958 | print_metric(ctxp, color, "%8.1f%%", name, | 969 | print_metric(config, ctxp, color, "%8.1f%%", name, |
| 959 | be_bound * 100.); | 970 | be_bound * 100.); |
| 960 | else | 971 | else |
| 961 | print_metric(ctxp, NULL, NULL, name, 0); | 972 | print_metric(config, ctxp, NULL, NULL, name, 0); |
| 962 | } else if (evsel->metric_expr) { | 973 | } else if (evsel->metric_expr) { |
| 963 | generic_metric(evsel->metric_expr, evsel->metric_events, evsel->name, | 974 | generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name, |
| 964 | evsel->metric_name, avg, cpu, out, st); | 975 | evsel->metric_name, avg, cpu, out, st); |
| 965 | } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) { | 976 | } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) { |
| 966 | char unit = 'M'; | 977 | char unit = 'M'; |
| @@ -975,9 +986,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 975 | unit = 'K'; | 986 | unit = 'K'; |
| 976 | } | 987 | } |
| 977 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); | 988 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); |
| 978 | print_metric(ctxp, NULL, "%8.3f", unit_buf, ratio); | 989 | print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio); |
| 979 | } else if (perf_stat_evsel__is(evsel, SMI_NUM)) { | 990 | } else if (perf_stat_evsel__is(evsel, SMI_NUM)) { |
| 980 | print_smi_cost(cpu, evsel, out, st); | 991 | print_smi_cost(config, cpu, evsel, out, st); |
| 981 | } else { | 992 | } else { |
| 982 | num = 0; | 993 | num = 0; |
| 983 | } | 994 | } |
| @@ -987,12 +998,12 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
| 987 | 998 | ||
| 988 | list_for_each_entry (mexp, &me->head, nd) { | 999 | list_for_each_entry (mexp, &me->head, nd) { |
| 989 | if (num++ > 0) | 1000 | if (num++ > 0) |
| 990 | out->new_line(ctxp); | 1001 | out->new_line(config, ctxp); |
| 991 | generic_metric(mexp->metric_expr, mexp->metric_events, | 1002 | generic_metric(config, mexp->metric_expr, mexp->metric_events, |
| 992 | evsel->name, mexp->metric_name, | 1003 | evsel->name, mexp->metric_name, |
| 993 | avg, cpu, out, st); | 1004 | avg, cpu, out, st); |
| 994 | } | 1005 | } |
| 995 | } | 1006 | } |
| 996 | if (num == 0) | 1007 | if (num == 0) |
| 997 | print_metric(ctxp, NULL, NULL, NULL, 0); | 1008 | print_metric(config, ctxp, NULL, NULL, NULL, 0); |
| 998 | } | 1009 | } |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index a0061e0b0fad..5d3172bcc4ae 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
| @@ -435,3 +435,98 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) | |||
| 435 | 435 | ||
| 436 | return ret; | 436 | return ret; |
| 437 | } | 437 | } |
| 438 | |||
| 439 | int create_perf_stat_counter(struct perf_evsel *evsel, | ||
| 440 | struct perf_stat_config *config, | ||
| 441 | struct target *target) | ||
| 442 | { | ||
| 443 | struct perf_event_attr *attr = &evsel->attr; | ||
| 444 | struct perf_evsel *leader = evsel->leader; | ||
| 445 | |||
| 446 | if (config->scale) { | ||
| 447 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
| 448 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
| 449 | } | ||
| 450 | |||
| 451 | /* | ||
| 452 | * The event is part of non trivial group, let's enable | ||
| 453 | * the group read (for leader) and ID retrieval for all | ||
| 454 | * members. | ||
| 455 | */ | ||
| 456 | if (leader->nr_members > 1) | ||
| 457 | attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP; | ||
| 458 | |||
| 459 | attr->inherit = !config->no_inherit; | ||
| 460 | |||
| 461 | /* | ||
| 462 | * Some events get initialized with sample_(period/type) set, | ||
| 463 | * like tracepoints. Clear it up for counting. | ||
| 464 | */ | ||
| 465 | attr->sample_period = 0; | ||
| 466 | |||
| 467 | if (config->identifier) | ||
| 468 | attr->sample_type = PERF_SAMPLE_IDENTIFIER; | ||
| 469 | |||
| 470 | /* | ||
| 471 | * Disabling all counters initially, they will be enabled | ||
| 472 | * either manually by us or by kernel via enable_on_exec | ||
| 473 | * set later. | ||
| 474 | */ | ||
| 475 | if (perf_evsel__is_group_leader(evsel)) { | ||
| 476 | attr->disabled = 1; | ||
| 477 | |||
| 478 | /* | ||
| 479 | * In case of initial_delay we enable tracee | ||
| 480 | * events manually. | ||
| 481 | */ | ||
| 482 | if (target__none(target) && !config->initial_delay) | ||
| 483 | attr->enable_on_exec = 1; | ||
| 484 | } | ||
| 485 | |||
| 486 | if (target__has_cpu(target) && !target__has_per_thread(target)) | ||
| 487 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
| 488 | |||
| 489 | return perf_evsel__open_per_thread(evsel, evsel->threads); | ||
| 490 | } | ||
| 491 | |||
| 492 | int perf_stat_synthesize_config(struct perf_stat_config *config, | ||
| 493 | struct perf_tool *tool, | ||
| 494 | struct perf_evlist *evlist, | ||
| 495 | perf_event__handler_t process, | ||
| 496 | bool attrs) | ||
| 497 | { | ||
| 498 | int err; | ||
| 499 | |||
| 500 | if (attrs) { | ||
| 501 | err = perf_event__synthesize_attrs(tool, evlist, process); | ||
| 502 | if (err < 0) { | ||
| 503 | pr_err("Couldn't synthesize attrs.\n"); | ||
| 504 | return err; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | err = perf_event__synthesize_extra_attr(tool, evlist, process, | ||
| 509 | attrs); | ||
| 510 | |||
| 511 | err = perf_event__synthesize_thread_map2(tool, evlist->threads, | ||
| 512 | process, NULL); | ||
| 513 | if (err < 0) { | ||
| 514 | pr_err("Couldn't synthesize thread map.\n"); | ||
| 515 | return err; | ||
| 516 | } | ||
| 517 | |||
| 518 | err = perf_event__synthesize_cpu_map(tool, evlist->cpus, | ||
| 519 | process, NULL); | ||
| 520 | if (err < 0) { | ||
| 521 | pr_err("Couldn't synthesize thread map.\n"); | ||
| 522 | return err; | ||
| 523 | } | ||
| 524 | |||
| 525 | err = perf_event__synthesize_stat_config(tool, config, process, NULL); | ||
| 526 | if (err < 0) { | ||
| 527 | pr_err("Couldn't synthesize config.\n"); | ||
| 528 | return err; | ||
| 529 | } | ||
| 530 | |||
| 531 | return 0; | ||
| 532 | } | ||
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 36efb986f7fc..3a13a6dc5a62 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
| @@ -4,8 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
| 6 | #include <stdio.h> | 6 | #include <stdio.h> |
| 7 | #include <sys/types.h> | ||
| 8 | #include <sys/time.h> | ||
| 9 | #include <sys/resource.h> | ||
| 10 | #include <sys/wait.h> | ||
| 7 | #include "xyarray.h" | 11 | #include "xyarray.h" |
| 8 | #include "rblist.h" | 12 | #include "rblist.h" |
| 13 | #include "perf.h" | ||
| 14 | #include "event.h" | ||
| 9 | 15 | ||
| 10 | struct stats { | 16 | struct stats { |
| 11 | double n, mean, M2; | 17 | double n, mean, M2; |
| @@ -84,15 +90,42 @@ struct runtime_stat { | |||
| 84 | struct rblist value_list; | 90 | struct rblist value_list; |
| 85 | }; | 91 | }; |
| 86 | 92 | ||
| 93 | typedef int (*aggr_get_id_t)(struct perf_stat_config *config, | ||
| 94 | struct cpu_map *m, int cpu); | ||
| 95 | |||
| 87 | struct perf_stat_config { | 96 | struct perf_stat_config { |
| 88 | enum aggr_mode aggr_mode; | 97 | enum aggr_mode aggr_mode; |
| 89 | bool scale; | 98 | bool scale; |
| 90 | FILE *output; | 99 | bool no_inherit; |
| 91 | unsigned int interval; | 100 | bool identifier; |
| 92 | unsigned int timeout; | 101 | bool csv_output; |
| 93 | int times; | 102 | bool interval_clear; |
| 94 | struct runtime_stat *stats; | 103 | bool metric_only; |
| 95 | int stats_num; | 104 | bool null_run; |
| 105 | bool ru_display; | ||
| 106 | bool big_num; | ||
| 107 | bool no_merge; | ||
| 108 | bool walltime_run_table; | ||
| 109 | FILE *output; | ||
| 110 | unsigned int interval; | ||
| 111 | unsigned int timeout; | ||
| 112 | unsigned int initial_delay; | ||
| 113 | unsigned int unit_width; | ||
| 114 | unsigned int metric_only_len; | ||
| 115 | int times; | ||
| 116 | int run_count; | ||
| 117 | int print_free_counters_hint; | ||
| 118 | int print_mixed_hw_group_error; | ||
| 119 | struct runtime_stat *stats; | ||
| 120 | int stats_num; | ||
| 121 | const char *csv_sep; | ||
| 122 | struct stats *walltime_nsecs_stats; | ||
| 123 | struct rusage ru_data; | ||
| 124 | struct cpu_map *aggr_map; | ||
| 125 | aggr_get_id_t aggr_get_id; | ||
| 126 | struct cpu_map *cpus_aggr_map; | ||
| 127 | u64 *walltime_run; | ||
| 128 | struct rblist metric_events; | ||
| 96 | }; | 129 | }; |
| 97 | 130 | ||
| 98 | void update_stats(struct stats *stats, u64 val); | 131 | void update_stats(struct stats *stats, u64 val); |
| @@ -130,9 +163,10 @@ bool __perf_evsel_stat__is(struct perf_evsel *evsel, | |||
| 130 | extern struct runtime_stat rt_stat; | 163 | extern struct runtime_stat rt_stat; |
| 131 | extern struct stats walltime_nsecs_stats; | 164 | extern struct stats walltime_nsecs_stats; |
| 132 | 165 | ||
| 133 | typedef void (*print_metric_t)(void *ctx, const char *color, const char *unit, | 166 | typedef void (*print_metric_t)(struct perf_stat_config *config, |
| 167 | void *ctx, const char *color, const char *unit, | ||
| 134 | const char *fmt, double val); | 168 | const char *fmt, double val); |
| 135 | typedef void (*new_line_t )(void *ctx); | 169 | typedef void (*new_line_t)(struct perf_stat_config *config, void *ctx); |
| 136 | 170 | ||
| 137 | void runtime_stat__init(struct runtime_stat *st); | 171 | void runtime_stat__init(struct runtime_stat *st); |
| 138 | void runtime_stat__exit(struct runtime_stat *st); | 172 | void runtime_stat__exit(struct runtime_stat *st); |
| @@ -148,7 +182,8 @@ struct perf_stat_output_ctx { | |||
| 148 | bool force_header; | 182 | bool force_header; |
| 149 | }; | 183 | }; |
| 150 | 184 | ||
| 151 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | 185 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
| 186 | struct perf_evsel *evsel, | ||
| 152 | double avg, int cpu, | 187 | double avg, int cpu, |
| 153 | struct perf_stat_output_ctx *out, | 188 | struct perf_stat_output_ctx *out, |
| 154 | struct rblist *metric_events, | 189 | struct rblist *metric_events, |
| @@ -171,4 +206,19 @@ int perf_event__process_stat_event(struct perf_tool *tool, | |||
| 171 | size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); | 206 | size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); |
| 172 | size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); | 207 | size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); |
| 173 | size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); | 208 | size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); |
| 209 | |||
| 210 | int create_perf_stat_counter(struct perf_evsel *evsel, | ||
| 211 | struct perf_stat_config *config, | ||
| 212 | struct target *target); | ||
| 213 | int perf_stat_synthesize_config(struct perf_stat_config *config, | ||
| 214 | struct perf_tool *tool, | ||
| 215 | struct perf_evlist *evlist, | ||
| 216 | perf_event__handler_t process, | ||
| 217 | bool attrs); | ||
| 218 | void | ||
| 219 | perf_evlist__print_counters(struct perf_evlist *evlist, | ||
| 220 | struct perf_stat_config *config, | ||
| 221 | struct target *_target, | ||
| 222 | struct timespec *ts, | ||
| 223 | int argc, const char **argv); | ||
| 174 | #endif | 224 | #endif |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index c85d0d1a65ed..7b0ca7cbb7de 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
| @@ -377,7 +377,7 @@ out: | |||
| 377 | 377 | ||
| 378 | static int record_saved_cmdline(void) | 378 | static int record_saved_cmdline(void) |
| 379 | { | 379 | { |
| 380 | unsigned int size; | 380 | unsigned long long size; |
| 381 | char *path; | 381 | char *path; |
| 382 | struct stat st; | 382 | struct stat st; |
| 383 | int ret, err = 0; | 383 | int ret, err = 0; |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 920b1d58a068..e76214f8d596 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
| @@ -164,16 +164,15 @@ void parse_ftrace_printk(struct tep_handle *pevent, | |||
| 164 | void parse_saved_cmdline(struct tep_handle *pevent, | 164 | void parse_saved_cmdline(struct tep_handle *pevent, |
| 165 | char *file, unsigned int size __maybe_unused) | 165 | char *file, unsigned int size __maybe_unused) |
| 166 | { | 166 | { |
| 167 | char *comm; | 167 | char comm[17]; /* Max comm length in the kernel is 16. */ |
| 168 | char *line; | 168 | char *line; |
| 169 | char *next = NULL; | 169 | char *next = NULL; |
| 170 | int pid; | 170 | int pid; |
| 171 | 171 | ||
| 172 | line = strtok_r(file, "\n", &next); | 172 | line = strtok_r(file, "\n", &next); |
| 173 | while (line) { | 173 | while (line) { |
| 174 | sscanf(line, "%d %ms", &pid, &comm); | 174 | if (sscanf(line, "%d %16s", &pid, comm) == 2) |
| 175 | tep_register_comm(pevent, comm, pid); | 175 | tep_register_comm(pevent, comm, pid); |
| 176 | free(comm); | ||
| 177 | line = strtok_r(NULL, "\n", &next); | 176 | line = strtok_r(NULL, "\n", &next); |
| 178 | } | 177 | } |
| 179 | } | 178 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 40204ec3a7a2..c69d77d7cf55 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #define _PERF_UTIL_TRACE_EVENT_H | 3 | #define _PERF_UTIL_TRACE_EVENT_H |
| 4 | 4 | ||
| 5 | #include <traceevent/event-parse.h> | 5 | #include <traceevent/event-parse.h> |
| 6 | #include <traceevent/trace-seq.h> | ||
| 6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
| 7 | 8 | ||
| 8 | struct machine; | 9 | struct machine; |
