diff options
Diffstat (limited to 'tools')
60 files changed, 1301 insertions, 287 deletions
diff --git a/tools/arch/x86/include/asm/irq_vectors.h b/tools/arch/x86/include/asm/irq_vectors.h new file mode 100644 index 000000000000..889f8b1b5b7f --- /dev/null +++ b/tools/arch/x86/include/asm/irq_vectors.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_IRQ_VECTORS_H +#define _ASM_X86_IRQ_VECTORS_H + +#include <linux/threads.h> +/* + * Linux IRQ vector layout. + * + * There are 256 IDT entries (per CPU - each entry is 8 bytes) which can + * be defined by Linux. They are used as a jump table by the CPU when a + * given vector is triggered - by a CPU-external, CPU-internal or + * software-triggered event. + * + * Linux sets the kernel code address each entry jumps to early during + * bootup, and never changes them. This is the general layout of the + * IDT entries: + * + * Vectors 0 ... 31 : system traps and exceptions - hardcoded events + * Vectors 32 ... 127 : device interrupts + * Vector 128 : legacy int80 syscall interface + * Vectors 129 ... LOCAL_TIMER_VECTOR-1 + * Vectors LOCAL_TIMER_VECTOR ... 255 : special interrupts + * + * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table. + * + * This file enumerates the exact layout of them: + */ + +#define NMI_VECTOR 0x02 +#define MCE_VECTOR 0x12 + +/* + * IDT vectors usable for external interrupt sources start at 0x20. + * (0x80 is the syscall vector, 0x30-0x3f are for ISA) + */ +#define FIRST_EXTERNAL_VECTOR 0x20 + +/* + * Reserve the lowest usable vector (and hence lowest priority) 0x20 for + * triggering cleanup after irq migration. 0x21-0x2f will still be used + * for device interrupts. + */ +#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR + +#define IA32_SYSCALL_VECTOR 0x80 + +/* + * Vectors 0x30-0x3f are used for ISA interrupts. + * round up to the next 16-vector boundary + */ +#define ISA_IRQ_VECTOR(irq) (((FIRST_EXTERNAL_VECTOR + 16) & ~15) + irq) + +/* + * Special IRQ vectors used by the SMP architecture, 0xf0-0xff + * + * some of the following vectors are 'rare', they are merged + * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. + * TLB, reschedule and local APIC vectors are performance-critical. + */ + +#define SPURIOUS_APIC_VECTOR 0xff +/* + * Sanity check + */ +#if ((SPURIOUS_APIC_VECTOR & 0x0F) != 0x0F) +# error SPURIOUS_APIC_VECTOR definition error +#endif + +#define ERROR_APIC_VECTOR 0xfe +#define RESCHEDULE_VECTOR 0xfd +#define CALL_FUNCTION_VECTOR 0xfc +#define CALL_FUNCTION_SINGLE_VECTOR 0xfb +#define THERMAL_APIC_VECTOR 0xfa +#define THRESHOLD_APIC_VECTOR 0xf9 +#define REBOOT_VECTOR 0xf8 + +/* + * Generic system vector for platform specific use + */ +#define X86_PLATFORM_IPI_VECTOR 0xf7 + +/* + * IRQ work vector: + */ +#define IRQ_WORK_VECTOR 0xf6 + +#define UV_BAU_MESSAGE 0xf5 +#define DEFERRED_ERROR_VECTOR 0xf4 + +/* Vector on which hypervisor callbacks will be delivered */ +#define HYPERVISOR_CALLBACK_VECTOR 0xf3 + +/* Vector for KVM to deliver posted interrupt IPI */ +#ifdef CONFIG_HAVE_KVM +#define POSTED_INTR_VECTOR 0xf2 +#define POSTED_INTR_WAKEUP_VECTOR 0xf1 +#define POSTED_INTR_NESTED_VECTOR 0xf0 +#endif + +#define MANAGED_IRQ_SHUTDOWN_VECTOR 0xef + +#if IS_ENABLED(CONFIG_HYPERV) +#define HYPERV_REENLIGHTENMENT_VECTOR 0xee +#define HYPERV_STIMER0_VECTOR 0xed +#endif + +#define LOCAL_TIMER_VECTOR 0xec + +#define NR_VECTORS 256 + +#ifdef CONFIG_X86_LOCAL_APIC +#define FIRST_SYSTEM_VECTOR LOCAL_TIMER_VECTOR +#else +#define FIRST_SYSTEM_VECTOR NR_VECTORS +#endif + +/* + * Size the maximum number of interrupts. + * + * If the irq_desc[] array has a sparse layout, we can size things + * generously - it scales up linearly with the maximum number of CPUs, + * and the maximum number of IO-APICs, whichever is higher. + * + * In other cases we size more conservatively, to not create too large + * static arrays. + */ + +#define NR_IRQS_LEGACY 16 + +#define CPU_VECTOR_LIMIT (64 * NR_CPUS) +#define IO_APIC_VECTOR_LIMIT (32 * MAX_IO_APICS) + +#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_PCI_MSI) +#define NR_IRQS \ + (CPU_VECTOR_LIMIT > IO_APIC_VECTOR_LIMIT ? \ + (NR_VECTORS + CPU_VECTOR_LIMIT) : \ + (NR_VECTORS + IO_APIC_VECTOR_LIMIT)) +#elif defined(CONFIG_X86_IO_APIC) +#define NR_IRQS (NR_VECTORS + IO_APIC_VECTOR_LIMIT) +#elif defined(CONFIG_PCI_MSI) +#define NR_IRQS (NR_VECTORS + CPU_VECTOR_LIMIT) +#else +#define NR_IRQS NR_IRQS_LEGACY +#endif + +#endif /* _ASM_X86_IRQ_VECTORS_H */ diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 18ed1b0fceb3..6345db33c533 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -36,6 +36,9 @@ Enable debugging output. Print how named events are resolved internally into perf events, and also any extra expressions computed by perf stat. +--deprecated:: +Print deprecated events. By default the deprecated events are hidden. + [[EVENT_MODIFIERS]] EVENT MODIFIERS --------------- diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 930c51c01201..a9af4e440e80 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -323,6 +323,12 @@ The output is SMI cycles%, equals to (aperf - unhalted core cycles) / aperf Users who wants to get the actual value can apply --no-metric-only. +--all-kernel:: +Configure all used events to run in kernel space. + +--all-user:: +Configure all used events to run in user space. + EXAMPLES -------- diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3bb89c2e9020..abc9b5d83312 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -146,6 +146,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. Show all syscalls followed by a summary by thread with min, max, and average times (in msec) and relative stddev. +--errno-summary:: + To be used with -s or -S, to show stats for the errnos experienced by + syscalls, using only this option will trigger --summary. + --tool_stats:: Show tool stats such as number of times fd->pathname was discovered thru hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc. diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 063202c53b64..1783427da9b0 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -265,6 +265,11 @@ LDFLAGS += -Wl,-z,noexecstack EXTLIBS = -lpthread -lrt -lm -ldl +ifneq ($(TCMALLOC),) + CFLAGS += -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free + EXTLIBS += -ltcmalloc +endif + ifeq ($(FEATURES_DUMP),) include $(srctree)/tools/build/Makefile.feature else diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index a099a8a89447..1cd294468a1f 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -114,6 +114,8 @@ include ../scripts/utilities.mak # Define NO_LIBZSTD if you do not want support of Zstandard based runtime # trace compression in record mode. # +# Define TCMALLOC to enable tcmalloc heap profiling. +# # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL @@ -544,6 +546,12 @@ x86_arch_prctl_code_tbl := $(srctree)/tools/perf/trace/beauty/x86_arch_prctl.sh $(x86_arch_prctl_code_array): $(x86_arch_asm_uapi_dir)/prctl.h $(x86_arch_prctl_code_tbl) $(Q)$(SHELL) '$(x86_arch_prctl_code_tbl)' $(x86_arch_asm_uapi_dir) > $@ +x86_arch_irq_vectors_array := $(beauty_outdir)/x86_arch_irq_vectors_array.c +x86_arch_irq_vectors_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh + +$(x86_arch_irq_vectors_array): $(x86_arch_asm_dir)/irq_vectors.h $(x86_arch_irq_vectors_tbl) + $(Q)$(SHELL) '$(x86_arch_irq_vectors_tbl)' $(x86_arch_asm_dir) > $@ + x86_arch_MSRs_array := $(beauty_outdir)/x86_arch_MSRs_array.c x86_arch_MSRs_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_msr.sh @@ -684,6 +692,7 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc $(perf_ioctl_array) \ $(prctl_option_array) \ $(usbdevfs_ioctl_array) \ + $(x86_arch_irq_vectors_array) \ $(x86_arch_MSRs_array) \ $(x86_arch_prctl_code_array) \ $(rename_flags_array) \ @@ -989,6 +998,7 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea $(OUTPUT)$(perf_ioctl_array) \ $(OUTPUT)$(prctl_option_array) \ $(OUTPUT)$(usbdevfs_ioctl_array) \ + $(OUTPUT)$(x86_arch_irq_vectors_array) \ $(OUTPUT)$(x86_arch_MSRs_array) \ $(OUTPUT)$(x86_arch_prctl_code_array) \ $(OUTPUT)$(rename_flags_array) \ diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 08e62ae9d37e..965ef017496f 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -26,6 +26,7 @@ int cmd_list(int argc, const char **argv) int i; bool raw_dump = false; bool long_desc_flag = false; + bool deprecated = false; struct option list_options[] = { OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), OPT_BOOLEAN('d', "desc", &desc_flag, @@ -34,6 +35,8 @@ int cmd_list(int argc, const char **argv) "Print longer event descriptions."), OPT_BOOLEAN(0, "details", &details_flag, "Print information on the perf event names and expressions used internally by events."), + OPT_BOOLEAN(0, "deprecated", &deprecated, + "Print deprecated events."), OPT_INCR(0, "debug", &verbose, "Enable debugging output"), OPT_END() @@ -55,7 +58,7 @@ int cmd_list(int argc, const char **argv) if (argc == 0) { print_events(NULL, raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, deprecated); return 0; } @@ -78,7 +81,8 @@ int cmd_list(int argc, const char **argv) print_hwcache_events(NULL, raw_dump); else if (strcmp(argv[i], "pmu") == 0) print_pmu_events(NULL, raw_dump, !desc_flag, - long_desc_flag, details_flag); + long_desc_flag, details_flag, + deprecated); else if (strcmp(argv[i], "sdt") == 0) print_sdt_events(NULL, NULL, raw_dump); else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) @@ -91,7 +95,8 @@ int cmd_list(int argc, const char **argv) if (sep == NULL) { print_events(argv[i], raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, + deprecated); continue; } sep_idx = sep - argv[i]; @@ -117,7 +122,8 @@ int cmd_list(int argc, const char **argv) print_hwcache_events(s, raw_dump); print_pmu_events(s, raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, + deprecated); print_tracepoint_events(NULL, s, raw_dump); print_sdt_events(NULL, s, raw_dump); metricgroup__print(true, true, s, raw_dump, details_flag); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index aae0e57c60fb..7accaf8ef689 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -399,6 +399,13 @@ static int report__setup_sample_type(struct report *rep) PERF_SAMPLE_BRANCH_ANY)) rep->nonany_branch_mode = true; +#ifndef HAVE_LIBUNWIND_SUPPORT + if (dwarf_callchain_users) { + ui__warning("Please install libunwind development packages " + "during the perf build.\n"); + } +#endif + return 0; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 1c797a948ada..f86c5cce5b2c 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3864,10 +3864,11 @@ int cmd_script(int argc, const char **argv) goto out_delete; if (script.time_str) { - err = perf_time__parse_for_ranges(script.time_str, session, + err = perf_time__parse_for_ranges_reltime(script.time_str, session, &script.ptime_range, &script.range_size, - &script.range_num); + &script.range_num, + reltime); if (err < 0) goto out_delete; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 468fc49420ce..c88d4e118409 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -803,6 +803,12 @@ static struct option stat_options[] = { OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", "monitor specified metrics or metric groups (separated by ,)", parse_metric_groups), + OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, + "Configure all used events to run in kernel space.", + PARSE_OPT_EXCLUSIVE), + OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, + "Configure all used events to run in user space.", + PARSE_OPT_EXCLUSIVE), OPT_END() }; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 144d417ddb22..43c05eae1768 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -175,6 +175,7 @@ struct trace { bool multiple_threads; bool summary; bool summary_only; + bool errno_summary; bool failure_only; bool show_comm; bool print_sample; @@ -284,6 +285,87 @@ struct syscall_tp { }; }; +/* + * The evsel->priv as used by 'perf trace' + * sc: for raw_syscalls:sys_{enter,exit} and syscalls:sys_{enter,exit}_SYSCALLNAME + * fmt: for all the other tracepoints + */ +struct evsel_trace { + struct syscall_tp sc; + struct syscall_arg_fmt *fmt; +}; + +static struct evsel_trace *evsel_trace__new(void) +{ + return zalloc(sizeof(struct evsel_trace)); +} + +static void evsel_trace__delete(struct evsel_trace *et) +{ + if (et == NULL) + return; + + zfree(&et->fmt); + free(et); +} + +/* + * Used with raw_syscalls:sys_{enter,exit} and with the + * syscalls:sys_{enter,exit}_SYSCALL tracepoints + */ +static inline struct syscall_tp *__evsel__syscall_tp(struct evsel *evsel) +{ + struct evsel_trace *et = evsel->priv; + + return &et->sc; +} + +static struct syscall_tp *evsel__syscall_tp(struct evsel *evsel) +{ + if (evsel->priv == NULL) { + evsel->priv = evsel_trace__new(); + if (evsel->priv == NULL) + return NULL; + } + + return __evsel__syscall_tp(evsel); +} + +/* + * Used with all the other tracepoints. + */ +static inline struct syscall_arg_fmt *__evsel__syscall_arg_fmt(struct evsel *evsel) +{ + struct evsel_trace *et = evsel->priv; + + return et->fmt; +} + +static struct syscall_arg_fmt *evsel__syscall_arg_fmt(struct evsel *evsel) +{ + struct evsel_trace *et = evsel->priv; + + if (evsel->priv == NULL) { + et = evsel->priv = evsel_trace__new(); + + if (et == NULL) + return NULL; + } + + if (et->fmt == NULL) { + et->fmt = calloc(evsel->tp_format->format.nr_fields, sizeof(struct syscall_arg_fmt)); + if (et->fmt == NULL) + goto out_delete; + } + + return __evsel__syscall_arg_fmt(evsel); + +out_delete: + evsel_trace__delete(evsel->priv); + evsel->priv = NULL; + return NULL; +} + static int perf_evsel__init_tp_uint_field(struct evsel *evsel, struct tp_field *field, const char *name) @@ -297,7 +379,7 @@ static int perf_evsel__init_tp_uint_field(struct evsel *evsel, } #define perf_evsel__init_sc_tp_uint_field(evsel, name) \ - ({ struct syscall_tp *sc = evsel->priv;\ + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); }) static int perf_evsel__init_tp_ptr_field(struct evsel *evsel, @@ -313,7 +395,7 @@ static int perf_evsel__init_tp_ptr_field(struct evsel *evsel, } #define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ - ({ struct syscall_tp *sc = evsel->priv;\ + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) static void evsel__delete_priv(struct evsel *evsel) @@ -324,73 +406,61 @@ static void evsel__delete_priv(struct evsel *evsel) static int perf_evsel__init_syscall_tp(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); + struct syscall_tp *sc = evsel__syscall_tp(evsel); - if (evsel->priv != NULL) { + if (sc != NULL) { if (perf_evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") && perf_evsel__init_tp_uint_field(evsel, &sc->id, "nr")) - goto out_delete; + return -ENOENT; return 0; } return -ENOMEM; -out_delete: - zfree(&evsel->priv); - return -ENOENT; } static int perf_evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp) { - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); + struct syscall_tp *sc = evsel__syscall_tp(evsel); - if (evsel->priv != NULL) { + if (sc != NULL) { struct tep_format_field *syscall_id = perf_evsel__field(tp, "id"); if (syscall_id == NULL) syscall_id = perf_evsel__field(tp, "__syscall_nr"); - if (syscall_id == NULL) - goto out_delete; - if (__tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) - goto out_delete; + if (syscall_id == NULL || + __tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) + return -EINVAL; return 0; } return -ENOMEM; -out_delete: - zfree(&evsel->priv); - return -EINVAL; } static int perf_evsel__init_augmented_syscall_tp_args(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); } static int perf_evsel__init_augmented_syscall_tp_ret(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap); } static int perf_evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler) { - evsel->priv = malloc(sizeof(struct syscall_tp)); - if (evsel->priv != NULL) { + if (evsel__syscall_tp(evsel) != NULL) { if (perf_evsel__init_sc_tp_uint_field(evsel, id)) - goto out_delete; + return -ENOENT; evsel->handler = handler; return 0; } return -ENOMEM; - -out_delete: - zfree(&evsel->priv); - return -ENOENT; } static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler) @@ -415,13 +485,27 @@ out_delete: } #define perf_evsel__sc_tp_uint(evsel, name, sample) \ - ({ struct syscall_tp *fields = evsel->priv; \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ fields->name.integer(&fields->name, sample); }) #define perf_evsel__sc_tp_ptr(evsel, name, sample) \ - ({ struct syscall_tp *fields = evsel->priv; \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ fields->name.pointer(&fields->name, sample); }) +size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val) +{ + int idx = val - sa->offset; + + if (idx < 0 || idx >= sa->nr_entries || sa->entries[idx] == NULL) { + size_t printed = scnprintf(bf, size, intfmt, val); + if (show_suffix) + printed += scnprintf(bf + printed, size - printed, " /* %s??? */", sa->prefix); + return printed; + } + + return scnprintf(bf, size, "%s%s", sa->entries[idx], show_suffix ? sa->prefix : ""); +} + size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val) { int idx = val - sa->offset; @@ -451,6 +535,21 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, #define SCA_STRARRAY syscall_arg__scnprintf_strarray +bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarray__strtoul(arg->parm, bf, size, ret); +} + +bool syscall_arg__strtoul_strarray_flags(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarray__strtoul_flags(arg->parm, bf, size, ret); +} + +bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarrays__strtoul(arg->parm, bf, size, ret); +} + size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg) { return strarray__scnprintf_flags(arg->parm, bf, size, arg->show_string_prefix, arg->val); @@ -492,6 +591,49 @@ bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret) return false; } +bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret) +{ + u64 val = 0; + char *tok = bf, *sep, *end; + + *ret = 0; + + while (size != 0) { + int toklen = size; + + sep = memchr(tok, '|', size); + if (sep != NULL) { + size -= sep - tok + 1; + + end = sep - 1; + while (end > tok && isspace(*end)) + --end; + + toklen = end - tok + 1; + } + + while (isspace(*tok)) + ++tok; + + if (isalpha(*tok) || *tok == '_') { + if (!strarray__strtoul(sa, tok, toklen, &val)) + return false; + } else { + bool is_hexa = tok[0] == 0 && (tok[1] = 'x' || tok[1] == 'X'); + + val = strtoul(tok, NULL, is_hexa ? 16 : 0); + } + + *ret |= (1 << (val - 1)); + + if (sep == NULL) + break; + tok = sep + 1; + } + + return true; +} + bool strarrays__strtoul(struct strarrays *sas, char *bf, size_t size, u64 *ret) { int i; @@ -562,7 +704,7 @@ static size_t syscall_arg__scnprintf_char_array(char *bf, size_t size, struct sy // XXX Hey, maybe for sched:sched_switch prev/next comm fields we can // fill missing comms using thread__set_comm()... // here or in a special syscall_arg__scnprintf_pid_sched_tp... - return scnprintf(bf, size, "\"%-.*s\"", arg->fmt->nr_entries, arg->val); + return scnprintf(bf, size, "\"%-.*s\"", arg->fmt->nr_entries ?: arg->len, arg->val); } #define SCA_CHAR_ARRAY syscall_arg__scnprintf_char_array @@ -740,10 +882,12 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ + .strtoul = STUL_STRARRAY, \ .parm = &strarray__##array, } #define STRARRAY_FLAGS(name, array) \ { .scnprintf = SCA_STRARRAY_FLAGS, \ + .strtoul = STUL_STRARRAY_FLAGS, \ .parm = &strarray__##array, } #include "trace/beauty/arch_errno_names.c" @@ -799,7 +943,8 @@ static struct syscall_fmt syscall_fmts[] = { { .name = "fchownat", .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, { .name = "fcntl", - .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ + .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ + .strtoul = STUL_STRARRAYS, .parm = &strarrays__fcntl_cmds_arrays, .show_zero = true, }, [2] = { .scnprintf = SCA_FCNTL_ARG, /* arg */ }, }, }, @@ -870,7 +1015,9 @@ static struct syscall_fmt syscall_fmts[] = { .alias = "old_mmap", #endif .arg = { [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, - [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, + [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ + .strtoul = STUL_STRARRAY_FLAGS, + .parm = &strarray__mmap_flags, }, [5] = { .scnprintf = SCA_HEX, /* offset */ }, }, }, { .name = "mount", .arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ }, @@ -1513,7 +1660,8 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args) } static struct syscall_arg_fmt syscall_arg_fmts__by_name[] = { - { .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, } + { .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, }, + { .name = "vector", .scnprintf = SCA_X86_IRQ_VECTORS, .strtoul = STUL_X86_IRQ_VECTORS, }, }; static int syscall_arg_fmt__cmp(const void *name, const void *fmtp) @@ -1558,7 +1706,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field arg->scnprintf = SCA_PID; else if (strcmp(field->type, "umode_t") == 0) arg->scnprintf = SCA_MODE_T; - else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstarts(field->type, "char")) { + else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) { arg->scnprintf = SCA_CHAR_ARRAY; arg->nr_entries = field->arraylen; } else if ((strcmp(field->type, "int") == 0 || @@ -1653,11 +1801,10 @@ static int trace__read_syscall_info(struct trace *trace, int id) static int perf_evsel__init_tp_arg_scnprintf(struct evsel *evsel) { - int nr_args = evsel->tp_format->format.nr_fields; + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); - evsel->priv = calloc(nr_args, sizeof(struct syscall_arg_fmt)); - if (evsel->priv != NULL) { - syscall_arg_fmt__init_array(evsel->priv, evsel->tp_format->format.fields); + if (fmt != NULL) { + syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields); return 0; } @@ -1958,11 +2105,18 @@ out_cant_read: return NULL; } -static void thread__update_stats(struct thread_trace *ttrace, - int id, struct perf_sample *sample) +struct syscall_stats { + struct stats stats; + u64 nr_failures; + int max_errno; + u32 *errnos; +}; + +static void thread__update_stats(struct thread *thread, struct thread_trace *ttrace, + int id, struct perf_sample *sample, long err, bool errno_summary) { struct int_node *inode; - struct stats *stats; + struct syscall_stats *stats; u64 duration = 0; inode = intlist__findnew(ttrace->syscall_stats, id); @@ -1971,17 +2125,46 @@ static void thread__update_stats(struct thread_trace *ttrace, stats = inode->priv; if (stats == NULL) { - stats = malloc(sizeof(struct stats)); + stats = malloc(sizeof(*stats)); if (stats == NULL) return; - init_stats(stats); + + stats->nr_failures = 0; + stats->max_errno = 0; + stats->errnos = NULL; + init_stats(&stats->stats); inode->priv = stats; } if (ttrace->entry_time && sample->time > ttrace->entry_time) duration = sample->time - ttrace->entry_time; - update_stats(stats, duration); + update_stats(&stats->stats, duration); + + if (err < 0) { + ++stats->nr_failures; + + if (!errno_summary) + return; + + err = -err; + if (err > stats->max_errno) { + u32 *new_errnos = realloc(stats->errnos, err * sizeof(u32)); + + if (new_errnos) { + memset(new_errnos + stats->max_errno, 0, (err - stats->max_errno) * sizeof(u32)); + } else { + pr_debug("Not enough memory for errno stats for thread \"%s\"(%d/%d), results will be incomplete\n", + thread__comm_str(thread), thread->pid_, thread->tid); + return; + } + + stats->errnos = new_errnos; + stats->max_errno = err; + } + + ++stats->errnos[err - 1]; + } } static int trace__printf_interrupted_entry(struct trace *trace) @@ -2226,11 +2409,11 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, trace__fprintf_sample(trace, evsel, sample, thread); - if (trace->summary) - thread__update_stats(ttrace, id, sample); - ret = perf_evsel__sc_tp_uint(evsel, ret, sample); + if (trace->summary) + thread__update_stats(thread, ttrace, id, sample, ret, trace->errno_summary); + if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) { trace__set_fd_pathname(thread, ret, ttrace->filename.name); ttrace->filename.pending_open = false; @@ -2466,7 +2649,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, char bf[2048]; size_t size = sizeof(bf); struct tep_format_field *field = evsel->tp_format->format.fields; - struct syscall_arg_fmt *arg = evsel->priv; + struct syscall_arg_fmt *arg = __evsel__syscall_arg_fmt(evsel); size_t printed = 0; unsigned long val; u8 bit = 1; @@ -2486,10 +2669,19 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, if (syscall_arg.mask & bit) continue; + syscall_arg.len = 0; syscall_arg.fmt = arg; - if (field->flags & TEP_FIELD_IS_ARRAY) - val = (uintptr_t)(sample->raw_data + field->offset); - else + if (field->flags & TEP_FIELD_IS_ARRAY) { + int offset = field->offset; + + if (field->flags & TEP_FIELD_IS_DYNAMIC) { + offset = format_field__intval(field, sample, evsel->needs_swap); + syscall_arg.len = offset >> 16; + offset &= 0xffff; + } + + val = (uintptr_t)(sample->raw_data + offset); + } else val = format_field__intval(field, sample, evsel->needs_swap); /* * Some syscall args need some mask, most don't and @@ -2592,12 +2784,6 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, } else { trace__fprintf_tp_fields(trace, evsel, sample, thread, NULL, 0); } - ++trace->nr_events_printed; - - if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { - evsel__disable(evsel); - evsel__close(evsel); - } } } @@ -2608,6 +2794,13 @@ newline: trace__fprintf_callchain(trace, sample); else if (callchain_ret < 0) pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); + + ++trace->nr_events_printed; + + if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { + evsel__disable(evsel); + evsel__close(evsel); + } out: thread__put(thread); return 0; @@ -2759,21 +2952,23 @@ static int trace__record(struct trace *trace, int argc, const char **argv) "-m", "1024", "-c", "1", }; - + pid_t pid = getpid(); + char *filter = asprintf__tp_filter_pids(1, &pid); const char * const sc_args[] = { "-e", }; unsigned int sc_args_nr = ARRAY_SIZE(sc_args); const char * const majpf_args[] = { "-e", "major-faults" }; unsigned int majpf_args_nr = ARRAY_SIZE(majpf_args); const char * const minpf_args[] = { "-e", "minor-faults" }; unsigned int minpf_args_nr = ARRAY_SIZE(minpf_args); + int err = -1; - /* +1 is for the event string below */ - rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 1 + + /* +3 is for the event string below and the pid filter */ + rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 3 + majpf_args_nr + minpf_args_nr + argc; rec_argv = calloc(rec_argc + 1, sizeof(char *)); - if (rec_argv == NULL) - return -ENOMEM; + if (rec_argv == NULL || filter == NULL) + goto out_free; j = 0; for (i = 0; i < ARRAY_SIZE(record_args); i++) @@ -2790,11 +2985,13 @@ static int trace__record(struct trace *trace, int argc, const char **argv) rec_argv[j++] = "syscalls:sys_enter,syscalls:sys_exit"; else { pr_err("Neither raw_syscalls nor syscalls events exist.\n"); - free(rec_argv); - return -1; + goto out_free; } } + rec_argv[j++] = "--filter"; + rec_argv[j++] = filter; + if (trace->trace_pgfaults & TRACE_PFMAJ) for (i = 0; i < majpf_args_nr; i++) rec_argv[j++] = majpf_args[i]; @@ -2806,7 +3003,11 @@ static int trace__record(struct trace *trace, int argc, const char **argv) for (i = 0; i < (unsigned int)argc; i++) rec_argv[j++] = argv[i]; - return cmd_record(j, rec_argv); + err = cmd_record(j, rec_argv); +out_free: + free(filter); + free(rec_argv); + return err; } static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); @@ -3488,7 +3689,7 @@ static int ordered_events__deliver_event(struct ordered_events *oe, static struct syscall_arg_fmt *perf_evsel__syscall_arg_fmt(struct evsel *evsel, char *arg) { struct tep_format_field *field; - struct syscall_arg_fmt *fmt = evsel->priv; + struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel); if (evsel->tp_format == NULL || fmt == NULL) return NULL; @@ -3526,7 +3727,7 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel } right_end = right + 1; - while (isalnum(*right_end) || *right_end == '_') + while (isalnum(*right_end) || *right_end == '_' || *right_end == '|') ++right_end; if (isalpha(*right)) { @@ -3542,8 +3743,8 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel fmt = perf_evsel__syscall_arg_fmt(evsel, arg); if (fmt == NULL) { - pr_debug("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", - arg, evsel->name, evsel->filter); + pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", + arg, evsel->name, evsel->filter); return -1; } @@ -3552,7 +3753,11 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel if (fmt->strtoul) { u64 val; - if (fmt->strtoul(right, right_size, NULL, &val)) { + struct syscall_arg syscall_arg = { + .parm = fmt->parm, + }; + + if (fmt->strtoul(right, right_size, &syscall_arg, &val)) { char *n, expansion[19]; int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); int expansion_offset = right - new_filter; @@ -4016,17 +4221,17 @@ static size_t trace__fprintf_threads_header(FILE *fp) } DEFINE_RESORT_RB(syscall_stats, a->msecs > b->msecs, - struct stats *stats; - double msecs; - int syscall; + struct syscall_stats *stats; + double msecs; + int syscall; ) { struct int_node *source = rb_entry(nd, struct int_node, rb_node); - struct stats *stats = source->priv; + struct syscall_stats *stats = source->priv; entry->syscall = source->i; entry->stats = stats; - entry->msecs = stats ? (u64)stats->n * (avg_stats(stats) / NSEC_PER_MSEC) : 0; + entry->msecs = stats ? (u64)stats->stats.n * (avg_stats(&stats->stats) / NSEC_PER_MSEC) : 0; } static size_t thread__dump_stats(struct thread_trace *ttrace, @@ -4042,27 +4247,37 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, printed += fprintf(fp, "\n"); - printed += fprintf(fp, " syscall calls total min avg max stddev\n"); - printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); - printed += fprintf(fp, " --------------- -------- --------- --------- --------- --------- ------\n"); + printed += fprintf(fp, " syscall calls errors total min avg max stddev\n"); + printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); + printed += fprintf(fp, " --------------- -------- ------ -------- --------- --------- --------- ------\n"); resort_rb__for_each_entry(nd, syscall_stats) { - struct stats *stats = syscall_stats_entry->stats; + struct syscall_stats *stats = syscall_stats_entry->stats; if (stats) { - double min = (double)(stats->min) / NSEC_PER_MSEC; - double max = (double)(stats->max) / NSEC_PER_MSEC; - double avg = avg_stats(stats); + double min = (double)(stats->stats.min) / NSEC_PER_MSEC; + double max = (double)(stats->stats.max) / NSEC_PER_MSEC; + double avg = avg_stats(&stats->stats); double pct; - u64 n = (u64) stats->n; + u64 n = (u64)stats->stats.n; - pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; + pct = avg ? 100.0 * stddev_stats(&stats->stats) / avg : 0.0; avg /= NSEC_PER_MSEC; sc = &trace->syscalls.table[syscall_stats_entry->syscall]; printed += fprintf(fp, " %-15s", sc->name); - printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f %9.3f", - n, syscall_stats_entry->msecs, min, avg); + printed += fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", + n, stats->nr_failures, syscall_stats_entry->msecs, min, avg); printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); + + if (trace->errno_summary && stats->nr_failures) { + const char *arch_name = perf_env__arch(trace->host->env); + int e; + + for (e = 0; e < stats->max_errno; ++e) { + if (stats->errnos[e] != 0) + fprintf(fp, "\t\t\t\t%s: %d\n", arch_syscalls__strerrno(arch_name, e + 1), stats->errnos[e]); + } + } } } @@ -4219,6 +4434,25 @@ static void evlist__set_default_evsel_handler(struct evlist *evlist, void *handl } } +static void evsel__set_syscall_arg_fmt(struct evsel *evsel, const char *name) +{ + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); + + if (fmt) { + struct syscall_fmt *scfmt = syscall_fmt__find(name); + + if (scfmt) { + int skip = 0; + + if (strcmp(evsel->tp_format->format.fields->name, "__syscall_nr") == 0 || + strcmp(evsel->tp_format->format.fields->name, "nr") == 0) + ++skip; + + memcpy(fmt + skip, scfmt->arg, (evsel->tp_format->format.nr_fields - skip) * sizeof(*fmt)); + } + } +} + static int evlist__set_syscall_tp_fields(struct evlist *evlist) { struct evsel *evsel; @@ -4236,15 +4470,19 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist) return -1; if (!strncmp(evsel->tp_format->name, "sys_enter_", 10)) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); if (__tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64))) return -1; + + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_enter_") - 1); } else if (!strncmp(evsel->tp_format->name, "sys_exit_", 9)) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); if (__tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap)) return -1; + + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_exit_") - 1); } } @@ -4501,6 +4739,8 @@ int cmd_trace(int argc, const char **argv) "Show only syscall summary with statistics"), OPT_BOOLEAN('S', "with-summary", &trace.summary, "Show all syscalls and summary with statistics"), + OPT_BOOLEAN(0, "errno-summary", &trace.errno_summary, + "Show errno stats per syscall, use with -s or -S"), OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min", "Trace pagefaults", parse_pagefaults, "maj"), OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), @@ -4775,7 +5015,7 @@ int cmd_trace(int argc, const char **argv) init_augmented_syscall_tp: if (perf_evsel__init_augmented_syscall_tp(evsel, evsel)) goto out; - sc = evsel->priv; + sc = __evsel__syscall_tp(evsel); /* * For now with BPF raw_augmented we hook into * raw_syscalls:sys_enter and there we get all @@ -4806,6 +5046,10 @@ init_augmented_syscall_tp: if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) return trace__record(&trace, argc-1, &argv[1]); + /* Using just --errno-summary will trigger --summary */ + if (trace.errno_summary && !trace.summary && !trace.summary_only) + trace.summary_only = true; + /* summary_only implies summary option, but don't overwrite summary if set */ if (trace.summary_only) trace.summary = trace.summary_only; diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 93c46d38024e..48290a0c917c 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -28,6 +28,7 @@ arch/x86/include/asm/disabled-features.h arch/x86/include/asm/required-features.h arch/x86/include/asm/cpufeatures.h arch/x86/include/asm/inat_types.h +arch/x86/include/asm/irq_vectors.h arch/x86/include/asm/msr-index.h arch/x86/include/uapi/asm/prctl.h arch/x86/lib/x86-opcode-map.txt diff --git a/tools/perf/lib/Makefile b/tools/perf/lib/Makefile index 0889c9c3ec19..0f233638ef1f 100644 --- a/tools/perf/lib/Makefile +++ b/tools/perf/lib/Makefile @@ -107,6 +107,7 @@ else endif LIBAPI = $(API_PATH)libapi.a +export LIBAPI $(LIBAPI): FORCE $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index 65045614c938..205ddbb80bc1 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -338,15 +338,13 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo int i; struct perf_mmap *map; - evlist->nr_mmaps = perf_cpu_map__nr(evlist->cpus); - if (perf_cpu_map__empty(evlist->cpus)) - evlist->nr_mmaps = perf_thread_map__nr(evlist->threads); - map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); if (!map) return NULL; for (i = 0; i < evlist->nr_mmaps; i++) { + struct perf_mmap *prev = i ? &map[i - 1] : NULL; + /* * When the perf_mmap() call is made we grab one refcount, plus * one extra to let perf_mmap__consume() get the last @@ -356,7 +354,7 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and * thus does perf_mmap__get() on it. */ - perf_mmap__init(&map[i], overwrite, NULL); + perf_mmap__init(&map[i], prev, overwrite, NULL); } return map; @@ -382,18 +380,22 @@ static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, static struct perf_mmap* perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx) { - struct perf_mmap *map = &evlist->mmap[idx]; + struct perf_mmap *maps; - if (overwrite) { - if (!evlist->mmap_ovw) { - evlist->mmap_ovw = perf_evlist__alloc_mmap(evlist, true); - if (!evlist->mmap_ovw) - return NULL; - } - map = &evlist->mmap_ovw[idx]; + maps = overwrite ? evlist->mmap_ovw : evlist->mmap; + + if (!maps) { + maps = perf_evlist__alloc_mmap(evlist, overwrite); + if (!maps) + return NULL; + + if (overwrite) + evlist->mmap_ovw = maps; + else + evlist->mmap = maps; } - return map; + return &maps[idx]; } #define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) @@ -405,6 +407,15 @@ perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp, return perf_mmap__mmap(map, mp, output, cpu); } +static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (overwrite) + evlist->mmap_ovw_first = map; + else + evlist->mmap_first = map; +} + static int mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, int idx, struct perf_mmap_param *mp, int cpu_idx, @@ -460,6 +471,9 @@ mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, if (ops->mmap(map, mp, *output, evlist_cpu) < 0) return -1; + + if (!idx) + perf_evlist__set_mmap_first(evlist, map, overwrite); } else { if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) return -1; @@ -542,6 +556,17 @@ out_unmap: return -1; } +static int perf_evlist__nr_mmaps(struct perf_evlist *evlist) +{ + int nr_mmaps; + + nr_mmaps = perf_cpu_map__nr(evlist->cpus); + if (perf_cpu_map__empty(evlist->cpus)) + nr_mmaps = perf_thread_map__nr(evlist->threads); + + return nr_mmaps; +} + int perf_evlist__mmap_ops(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, struct perf_mmap_param *mp) @@ -553,10 +578,9 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist, if (!ops || !ops->get || !ops->mmap) return -EINVAL; - if (!evlist->mmap) - evlist->mmap = perf_evlist__alloc_mmap(evlist, false); - if (!evlist->mmap) - return -ENOMEM; + mp->mask = evlist->mmap_len - page_size - 1; + + evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); perf_evlist__for_each_entry(evlist, evsel) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && @@ -583,7 +607,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, int pages) }; evlist->mmap_len = (pages + 1) * page_size; - mp.mask = evlist->mmap_len - page_size - 1; return perf_evlist__mmap_ops(evlist, &ops, &mp); } @@ -605,3 +628,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist) zfree(&evlist->mmap); zfree(&evlist->mmap_ovw); } + +struct perf_mmap* +perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (map) + return map->next; + + return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first; +} diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h index be0b25a70730..a2fbccf1922f 100644 --- a/tools/perf/lib/include/internal/evlist.h +++ b/tools/perf/lib/include/internal/evlist.h @@ -25,6 +25,8 @@ struct perf_evlist { struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; struct perf_mmap *mmap; struct perf_mmap *mmap_ovw; + struct perf_mmap *mmap_first; + struct perf_mmap *mmap_ovw_first; }; typedef void @@ -48,6 +50,7 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, struct perf_mmap_param *mp); +void perf_evlist__init(struct perf_evlist *evlist); void perf_evlist__exit(struct perf_evlist *evlist); /** diff --git a/tools/perf/lib/include/internal/evsel.h b/tools/perf/lib/include/internal/evsel.h index a69b8299c36f..1ffd083b235e 100644 --- a/tools/perf/lib/include/internal/evsel.h +++ b/tools/perf/lib/include/internal/evsel.h @@ -50,6 +50,7 @@ struct perf_evsel { bool system_wide; }; +void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); void perf_evsel__close_fd(struct perf_evsel *evsel); void perf_evsel__free_fd(struct perf_evsel *evsel); diff --git a/tools/perf/lib/include/internal/mmap.h b/tools/perf/lib/include/internal/mmap.h index ee536c4441bb..be7556e0a2b2 100644 --- a/tools/perf/lib/include/internal/mmap.h +++ b/tools/perf/lib/include/internal/mmap.h @@ -32,6 +32,7 @@ struct perf_mmap { u64 flush; libperf_unmap_cb_t unmap_cb; char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); + struct perf_mmap *next; }; struct perf_mmap_param { @@ -41,8 +42,8 @@ struct perf_mmap_param { size_t perf_mmap__mmap_len(struct perf_mmap *map); -void perf_mmap__init(struct perf_mmap *map, bool overwrite, - libperf_unmap_cb_t unmap_cb); +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb); int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, int fd, int cpu); void perf_mmap__munmap(struct perf_mmap *map); diff --git a/tools/perf/lib/include/internal/tests.h b/tools/perf/lib/include/internal/tests.h index b7a20cd24ee1..2093e8868a67 100644 --- a/tools/perf/lib/include/internal/tests.h +++ b/tools/perf/lib/include/internal/tests.h @@ -4,14 +4,28 @@ #include <stdio.h> -#define __T_START fprintf(stdout, "- running %s...", __FILE__) -#define __T_OK fprintf(stdout, "OK\n") -#define __T_FAIL fprintf(stdout, "FAIL\n") +int tests_failed; + +#define __T_START \ +do { \ + fprintf(stdout, "- running %s...", __FILE__); \ + fflush(NULL); \ + tests_failed = 0; \ +} while (0) + +#define __T_END \ +do { \ + if (tests_failed) \ + fprintf(stdout, " FAILED (%d)\n", tests_failed); \ + else \ + fprintf(stdout, "OK\n"); \ +} while (0) #define __T(text, cond) \ do { \ if (!(cond)) { \ fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + tests_failed++; \ return -1; \ } \ } while (0) diff --git a/tools/perf/lib/include/perf/core.h b/tools/perf/lib/include/perf/core.h index 2a80e4b6f819..a3f6d68edad7 100644 --- a/tools/perf/lib/include/perf/core.h +++ b/tools/perf/lib/include/perf/core.h @@ -9,6 +9,7 @@ #endif enum libperf_print_level { + LIBPERF_ERR, LIBPERF_WARN, LIBPERF_INFO, LIBPERF_DEBUG, diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h index 16f526e74d13..0a7479dc13bf 100644 --- a/tools/perf/lib/include/perf/evlist.h +++ b/tools/perf/lib/include/perf/evlist.h @@ -3,13 +3,13 @@ #define __LIBPERF_EVLIST_H #include <perf/core.h> +#include <stdbool.h> struct perf_evlist; struct perf_evsel; struct perf_cpu_map; struct perf_thread_map; -LIBPERF_API void perf_evlist__init(struct perf_evlist *evlist); LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *evsel); LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist, @@ -38,4 +38,12 @@ LIBPERF_API int perf_evlist__filter_pollfd(struct perf_evlist *evlist, LIBPERF_API int perf_evlist__mmap(struct perf_evlist *evlist, int pages); LIBPERF_API void perf_evlist__munmap(struct perf_evlist *evlist); +LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, + struct perf_mmap *map, + bool overwrite); +#define perf_evlist__for_each_mmap(evlist, pos, overwrite) \ + for ((pos) = perf_evlist__next_mmap((evlist), NULL, overwrite); \ + (pos) != NULL; \ + (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite)) + #endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h index 4388667f265c..557f5815a9c9 100644 --- a/tools/perf/lib/include/perf/evsel.h +++ b/tools/perf/lib/include/perf/evsel.h @@ -21,8 +21,6 @@ struct perf_counts_values { }; }; -LIBPERF_API void perf_evsel__init(struct perf_evsel *evsel, - struct perf_event_attr *attr); LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel); LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, diff --git a/tools/perf/lib/internal.h b/tools/perf/lib/internal.h index 37db745e1502..2c27e158de6b 100644 --- a/tools/perf/lib/internal.h +++ b/tools/perf/lib/internal.h @@ -2,6 +2,8 @@ #ifndef __LIBPERF_INTERNAL_H #define __LIBPERF_INTERNAL_H +#include <perf/core.h> + void libperf_print(enum libperf_print_level level, const char *format, ...) __attribute__((format(printf, 2, 3))); @@ -11,6 +13,7 @@ do { \ libperf_print(level, "libperf: " fmt, ##__VA_ARGS__); \ } while (0) +#define pr_err(fmt, ...) __pr(LIBPERF_ERR, fmt, ##__VA_ARGS__) #define pr_warning(fmt, ...) __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__) #define pr_info(fmt, ...) __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__) #define pr_debug(fmt, ...) __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__) diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map index 2184aba36c3f..7be1af8a546c 100644 --- a/tools/perf/lib/libperf.map +++ b/tools/perf/lib/libperf.map @@ -21,7 +21,6 @@ LIBPERF_0.0.1 { perf_evsel__delete; perf_evsel__enable; perf_evsel__disable; - perf_evsel__init; perf_evsel__open; perf_evsel__close; perf_evsel__read; @@ -34,7 +33,6 @@ LIBPERF_0.0.1 { perf_evlist__close; perf_evlist__enable; perf_evlist__disable; - perf_evlist__init; perf_evlist__add; perf_evlist__remove; perf_evlist__next; @@ -43,6 +41,7 @@ LIBPERF_0.0.1 { perf_evlist__mmap; perf_evlist__munmap; perf_evlist__filter_pollfd; + perf_evlist__next_mmap; perf_mmap__consume; perf_mmap__read_init; perf_mmap__read_done; diff --git a/tools/perf/lib/mmap.c b/tools/perf/lib/mmap.c index 0752c193b0fb..79d5ed6c38cc 100644 --- a/tools/perf/lib/mmap.c +++ b/tools/perf/lib/mmap.c @@ -13,13 +13,15 @@ #include <linux/kernel.h> #include "internal.h" -void perf_mmap__init(struct perf_mmap *map, bool overwrite, - libperf_unmap_cb_t unmap_cb) +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb) { map->fd = -1; map->overwrite = overwrite; map->unmap_cb = unmap_cb; refcount_set(&map->refcnt, 0); + if (prev) + prev->next = map; } size_t perf_mmap__mmap_len(struct perf_mmap *map) diff --git a/tools/perf/lib/tests/Makefile b/tools/perf/lib/tests/Makefile index 1ee4e9ba848b..a43cd08c5c03 100644 --- a/tools/perf/lib/tests/Makefile +++ b/tools/perf/lib/tests/Makefile @@ -16,13 +16,13 @@ all: include $(srctree)/tools/scripts/Makefile.include -INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include +INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/lib $(TESTS_A): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) $(TESTS_SO): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) -lperf + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) $(LIBAPI) -lperf all: $(TESTS_A) $(TESTS_SO) diff --git a/tools/perf/lib/tests/test-cpumap.c b/tools/perf/lib/tests/test-cpumap.c index aa34c20df07e..c8d45091e7c2 100644 --- a/tools/perf/lib/tests/test-cpumap.c +++ b/tools/perf/lib/tests/test-cpumap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv) perf_cpu_map__put(cpus); perf_cpu_map__put(cpus); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c index e6b2ab2e2bde..6d8ebe0c2504 100644 --- a/tools/perf/lib/tests/test-evlist.c +++ b/tools/perf/lib/tests/test-evlist.c @@ -1,12 +1,23 @@ // SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET) +#include <sched.h> #include <stdio.h> #include <stdarg.h> +#include <unistd.h> +#include <stdlib.h> #include <linux/perf_event.h> +#include <linux/limits.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/prctl.h> #include <perf/cpumap.h> #include <perf/threadmap.h> #include <perf/evlist.h> #include <perf/evsel.h> +#include <perf/mmap.h> +#include <perf/event.h> #include <internal/tests.h> +#include <api/fs/fs.h> static int libperf_print(enum libperf_print_level level, const char *fmt, va_list ap) @@ -181,6 +192,210 @@ static int test_stat_thread_enable(void) return 0; } +static int test_mmap_thread(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + char path[PATH_MAX]; + int id, err, pid, go_pipe[2]; + union perf_event *event; + char bf; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + err = pipe(go_pipe); + __T("failed to create pipe", err == 0); + + fflush(NULL); + + pid = fork(); + if (!pid) { + int i; + + read(go_pipe[0], &bf, 1); + + /* Generate 100 prctl calls. */ + for (i = 0; i < 100; i++) + prctl(0, 0, 0, 0, 0); + + exit(0); + } + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + cpus = perf_cpu_map__dummy_new(); + __T("failed to create cpus", cpus); + + perf_thread_map__set_pid(threads, 0, pid); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, threads); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + /* kick the child and wait for it to finish */ + write(go_pipe[1], &bf, 1); + waitpid(pid, NULL, 0); + + /* + * There's no need to call perf_evlist__disable, + * monitored process is dead now. + */ + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + perf_thread_map__put(threads); + perf_cpu_map__put(cpus); + + /* + * The generated prctl calls should match the + * number of events in the buffer. + */ + __T("failed count", count == 100); + + return 0; +} + +static int test_mmap_cpus(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + cpu_set_t saved_mask; + char path[PATH_MAX]; + int id, err, cpu, tmp; + union perf_event *event; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + cpus = perf_cpu_map__new(NULL); + __T("failed to create cpus", cpus); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, NULL); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_getaffinity failed", err == 0); + + perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + + err = sched_setaffinity(0, sizeof(mask), &mask); + __T("sched_setaffinity failed", err == 0); + + prctl(0, 0, 0, 0, 0); + } + + err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_setaffinity failed", err == 0); + + perf_evlist__disable(evlist); + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + /* + * The generated prctl events should match the + * number of cpus or be bigger (we are system-wide). + */ + __T("failed count", count >= perf_cpu_map__nr(cpus)); + + perf_cpu_map__put(cpus); + + return 0; +} + int main(int argc, char **argv) { __T_START; @@ -190,7 +405,9 @@ int main(int argc, char **argv) test_stat_cpu(); test_stat_thread(); test_stat_thread_enable(); + test_mmap_thread(); + test_mmap_cpus(); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-evsel.c b/tools/perf/lib/tests/test-evsel.c index 1b6c4285ac2b..135722ac965b 100644 --- a/tools/perf/lib/tests/test-evsel.c +++ b/tools/perf/lib/tests/test-evsel.c @@ -130,6 +130,6 @@ int main(int argc, char **argv) test_stat_thread(); test_stat_thread_enable(); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-threadmap.c b/tools/perf/lib/tests/test-threadmap.c index 8c5f47247d9e..7dc4d6fbedde 100644 --- a/tools/perf/lib/tests/test-threadmap.c +++ b/tools/perf/lib/tests/test-threadmap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv) perf_thread_map__put(threads); perf_thread_map__put(threads); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json index 0d1556fcdffe..7da86942dae2 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json @@ -1,5 +1,19 @@ [ { + "EventCode": "0x00", + "EventName": "uncore_hisi_ddrc.flux_wr", + "BriefDescription": "DDRC total write operations", + "PublicDescription": "DDRC total write operations", + "Unit": "hisi_sccl,ddrc", + }, + { + "EventCode": "0x01", + "EventName": "uncore_hisi_ddrc.flux_rd", + "BriefDescription": "DDRC total read operations", + "PublicDescription": "DDRC total read operations", + "Unit": "hisi_sccl,ddrc", + }, + { "EventCode": "0x02", "EventName": "uncore_hisi_ddrc.flux_wcmd", "BriefDescription": "DDRC write commands", @@ -15,7 +29,7 @@ }, { "EventCode": "0x04", - "EventName": "uncore_hisi_ddrc.flux_wr", + "EventName": "uncore_hisi_ddrc.pre_cmd", "BriefDescription": "DDRC precharge commands", "PublicDescription": "DDRC precharge commands", "Unit": "hisi_sccl,ddrc", diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json index 447d3064de90..3be418a248ea 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json @@ -21,6 +21,13 @@ "Unit": "hisi_sccl,hha", }, { + "EventCode": "0x03", + "EventName": "uncore_hisi_hha.rx_ccix", + "BriefDescription": "Count of the number of operations that HHA has received from CCIX", + "PublicDescription": "Count of the number of operations that HHA has received from CCIX", + "Unit": "hisi_sccl,hha", + }, + { "EventCode": "0x1c", "EventName": "uncore_hisi_hha.rd_ddr_64b", "BriefDescription": "The number of read operations sent by HHA to DDRC which size is 64 bytes", @@ -29,7 +36,7 @@ }, { "EventCode": "0x1d", - "EventName": "uncore_hisi_hha.wr_dr_64b", + "EventName": "uncore_hisi_hha.wr_ddr_64b", "BriefDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes", "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes", "Unit": "hisi_sccl,hha", @@ -48,4 +55,18 @@ "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 128 bytes", "Unit": "hisi_sccl,hha", }, + { + "EventCode": "0x20", + "EventName": "uncore_hisi_hha.spill_num", + "BriefDescription": "Count of the number of spill operations that the HHA has sent", + "PublicDescription": "Count of the number of spill operations that the HHA has sent", + "Unit": "hisi_sccl,hha", + }, + { + "EventCode": "0x21", + "EventName": "uncore_hisi_hha.spill_success", + "BriefDescription": "Count of the number of successful spill operations that the HHA has sent", + "PublicDescription": "Count of the number of successful spill operations that the HHA has sent", + "Unit": "hisi_sccl,hha", + }, ] diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json index ca48747642e1..f463d0acfaef 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json @@ -34,4 +34,60 @@ "PublicDescription": "l3c precharge commands", "Unit": "hisi_sccl,l3c", }, + { + "EventCode": "0x20", + "EventName": "uncore_hisi_l3c.rd_spipe", + "BriefDescription": "Count of the number of read lines that come from this cluster of CPU core in spipe", + "PublicDescription": "Count of the number of read lines that come from this cluster of CPU core in spipe", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x21", + "EventName": "uncore_hisi_l3c.wr_spipe", + "BriefDescription": "Count of the number of write lines that come from this cluster of CPU core in spipe", + "PublicDescription": "Count of the number of write lines that come from this cluster of CPU core in spipe", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x22", + "EventName": "uncore_hisi_l3c.rd_hit_spipe", + "BriefDescription": "Count of the number of read lines that hits in spipe of this L3C", + "PublicDescription": "Count of the number of read lines that hits in spipe of this L3C", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x23", + "EventName": "uncore_hisi_l3c.wr_hit_spipe", + "BriefDescription": "Count of the number of write lines that hits in spipe of this L3C", + "PublicDescription": "Count of the number of write lines that hits in spipe of this L3C", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x29", + "EventName": "uncore_hisi_l3c.back_invalid", + "BriefDescription": "Count of the number of L3C back invalid operations", + "PublicDescription": "Count of the number of L3C back invalid operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x40", + "EventName": "uncore_hisi_l3c.retry_cpu", + "BriefDescription": "Count of the number of retry that L3C suppresses the CPU operations", + "PublicDescription": "Count of the number of retry that L3C suppresses the CPU operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x41", + "EventName": "uncore_hisi_l3c.retry_ring", + "BriefDescription": "Count of the number of retry that L3C suppresses the ring operations", + "PublicDescription": "Count of the number of retry that L3C suppresses the ring operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x42", + "EventName": "uncore_hisi_l3c.prefetch_drop", + "BriefDescription": "Count of the number of prefetch drops from this L3C", + "PublicDescription": "Count of the number of prefetch drops from this L3C", + "Unit": "hisi_sccl,l3c", + }, ] diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c index e2837260ca4d..7d69727f44bd 100644 --- a/tools/perf/pmu-events/jevents.c +++ b/tools/perf/pmu-events/jevents.c @@ -322,7 +322,8 @@ static int print_events_table_entry(void *data, char *name, char *event, char *desc, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group) + char *metric_name, char *metric_group, + char *deprecated) { struct perf_entry_data *pd = data; FILE *outfp = pd->outfp; @@ -354,6 +355,8 @@ static int print_events_table_entry(void *data, char *name, char *event, fprintf(outfp, "\t.metric_name = \"%s\",\n", metric_name); if (metric_group) fprintf(outfp, "\t.metric_group = \"%s\",\n", metric_group); + if (deprecated) + fprintf(outfp, "\t.deprecated = \"%s\",\n", deprecated); fprintf(outfp, "},\n"); return 0; @@ -371,6 +374,7 @@ struct event_struct { char *metric_expr; char *metric_name; char *metric_group; + char *deprecated; }; #define ADD_EVENT_FIELD(field) do { if (field) { \ @@ -398,6 +402,7 @@ struct event_struct { op(metric_expr); \ op(metric_name); \ op(metric_group); \ + op(deprecated); \ } while (0) static LIST_HEAD(arch_std_events); @@ -416,7 +421,8 @@ static void free_arch_std_events(void) static int save_arch_std_events(void *data, char *name, char *event, char *desc, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group) + char *metric_name, char *metric_group, + char *deprecated) { struct event_struct *es; @@ -479,7 +485,8 @@ static int try_fixup(const char *fn, char *arch_std, char **event, char **desc, char **name, char **long_desc, char **pmu, char **filter, char **perpkg, char **unit, char **metric_expr, char **metric_name, - char **metric_group, unsigned long long eventcode) + char **metric_group, unsigned long long eventcode, + char **deprecated) { /* try to find matching event from arch standard values */ struct event_struct *es; @@ -507,7 +514,8 @@ int json_events(const char *fn, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group), + char *metric_name, char *metric_group, + char *deprecated), void *data) { int err; @@ -536,6 +544,7 @@ int json_events(const char *fn, char *metric_expr = NULL; char *metric_name = NULL; char *metric_group = NULL; + char *deprecated = NULL; char *arch_std = NULL; unsigned long long eventcode = 0; struct msrmap *msr = NULL; @@ -614,6 +623,8 @@ int json_events(const char *fn, addfield(map, &unit, "", "", val); } else if (json_streq(map, field, "PerPkg")) { addfield(map, &perpkg, "", "", val); + } else if (json_streq(map, field, "Deprecated")) { + addfield(map, &deprecated, "", "", val); } else if (json_streq(map, field, "MetricName")) { addfield(map, &metric_name, "", "", val); } else if (json_streq(map, field, "MetricGroup")) { @@ -658,12 +669,14 @@ int json_events(const char *fn, err = try_fixup(fn, arch_std, &event, &desc, &name, &long_desc, &pmu, &filter, &perpkg, &unit, &metric_expr, &metric_name, - &metric_group, eventcode); + &metric_group, eventcode, + &deprecated); if (err) goto free_strings; } err = func(data, name, real_event(name, event), desc, long_desc, - pmu, unit, perpkg, metric_expr, metric_name, metric_group); + pmu, unit, perpkg, metric_expr, metric_name, + metric_group, deprecated); free_strings: free(event); free(desc); @@ -673,6 +686,7 @@ free_strings: free(pmu); free(filter); free(perpkg); + free(deprecated); free(unit); free(metric_expr); free(metric_name); diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h index 4684c673c445..5cda49a42143 100644 --- a/tools/perf/pmu-events/jevents.h +++ b/tools/perf/pmu-events/jevents.h @@ -7,7 +7,8 @@ int json_events(const char *fn, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group), + char *metric_name, char *metric_group, + char *deprecated), void *data); char *get_cpu_str(void); diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index 92a4d15ee0b9..caeb577d36c9 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -17,6 +17,7 @@ struct pmu_event { const char *metric_expr; const char *metric_name; const char *metric_group; + const char *deprecated; }; /* diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 016bba2c142d..d0b935356274 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -10,11 +10,7 @@ #include <unistd.h> #include <string.h> #include <sys/ioctl.h> -#include <time.h> #include <fcntl.h> -#include <signal.h> -#include <sys/mman.h> -#include <linux/compiler.h> #include <linux/hw_breakpoint.h> #include "tests.h" @@ -192,3 +188,19 @@ int test__bp_accounting(struct test *test __maybe_unused, int subtest __maybe_un return bp_accounting(wp_cnt, share); } + +bool test__bp_account_is_supported(void) +{ + /* + * PowerPC and S390 do not support creation of instruction + * breakpoints using the perf_event interface. + * + * Just disable the test for these architectures until these + * issues are resolved. + */ +#if defined(__powerpc__) || defined(__s390x__) + return false; +#else + return true; +#endif +} diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index c1c2c13de254..166f411568a5 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -49,14 +49,6 @@ asm ( "__test_function:\n" "incq (%rdi)\n" "ret\n"); -#elif defined (__aarch64__) -extern void __test_function(volatile long *ptr); -asm ( - ".globl __test_function\n" - "__test_function:\n" - "str x30, [x0]\n" - "ret\n"); - #else static void __test_function(volatile long *ptr) { @@ -302,10 +294,15 @@ bool test__bp_signal_is_supported(void) * stepping into the SIGIO handler and getting stuck on the * breakpointed instruction. * + * Since arm64 has the same issue with arm for the single-step + * handling, this case also gets suck on the breakpointed + * instruction. + * * Just disable the test for these architectures until these * issues are resolved. */ -#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) +#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) || \ + defined(__aarch64__) return false; #else return true; diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 55774baffc2a..8b286e9b7549 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -121,7 +121,7 @@ static struct test generic_tests[] = { { .desc = "Breakpoint accounting", .func = test__bp_accounting, - .is_supported = test__bp_signal_is_supported, + .is_supported = test__bp_account_is_supported, }, { .desc = "Watchpoint", diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 4965f8b9055b..adaff9044331 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -54,6 +54,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused struct perf_cpu_map *cpus; struct perf_thread_map *threads; struct mmap *md; + int retry_count = 0; signal(SIGCHLD, sig_handler); @@ -111,6 +112,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused if (evlist__mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); + err = -1; goto out_delete_evlist; } @@ -132,6 +134,13 @@ retry: out_init: if (!exited || !nr_exit) { evlist__poll(evlist, -1); + + if (retry_count++ > 1000) { + pr_debug("Failed after retrying 1000 times\n"); + err = -1; + goto out_free_maps; + } + goto retry; } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 72912eb473cb..9837b6e93023 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -111,6 +111,7 @@ int test__map_groups__merge_in(struct test *t, int subtest); int test__time_utils(struct test *t, int subtest); bool test__bp_signal_is_supported(void); +bool test__bp_account_is_supported(void); bool test__wp_is_supported(void); #if defined(__arm__) || defined(__aarch64__) diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 77ad80a399fd..5a61043c2ff7 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -28,9 +28,11 @@ struct strarray { } size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val); +size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val); size_t strarray__scnprintf_flags(struct strarray *sa, char *bf, size_t size, bool show_prefix, unsigned long flags); bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret); +bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret); struct trace; struct thread; @@ -87,6 +89,7 @@ struct syscall_arg_fmt; /** * @val: value of syscall argument being formatted + * @len: for tracepoint dynamic arrays, if fmt->nr_entries == 0, then its not a fixed array, look at arg->len * @args: All the args, use syscall_args__val(arg, nth) to access one * @augmented_args: Extra data that can be collected, for instance, with eBPF for expanding the pathname for open, etc * @augmented_args_size: augmented_args total payload size @@ -109,6 +112,7 @@ struct syscall_arg { struct thread *thread; struct trace *trace; void *parm; + u16 len; u8 idx; u8 mask; bool show_string_prefix; @@ -119,6 +123,21 @@ unsigned long syscall_arg__val(struct syscall_arg *arg, u8 idx); size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg); #define SCA_STRARRAY_FLAGS syscall_arg__scnprintf_strarray_flags +bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAY syscall_arg__strtoul_strarray + +bool syscall_arg__strtoul_strarray_flags(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAY_FLAGS syscall_arg__strtoul_strarray_flags + +bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAYS syscall_arg__strtoul_strarrays + +size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_X86_IRQ_VECTORS syscall_arg__scnprintf_x86_irq_vectors + +bool syscall_arg__strtoul_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_X86_IRQ_VECTORS syscall_arg__strtoul_x86_irq_vectors + size_t syscall_arg__scnprintf_x86_MSR(char *bf, size_t size, struct syscall_arg *arg); #define SCA_X86_MSR syscall_arg__scnprintf_x86_MSR diff --git a/tools/perf/trace/beauty/mmap.c b/tools/perf/trace/beauty/mmap.c index 859a8a9db2c6..9fa771a90d79 100644 --- a/tools/perf/trace/beauty/mmap.c +++ b/tools/perf/trace/beauty/mmap.c @@ -33,11 +33,11 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot -static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix) -{ #include "trace/beauty/generated/mmap_flags_array.c" static DEFINE_STRARRAY(mmap_flags, "MAP_"); +static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix) +{ return strarray__scnprintf_flags(&strarray__mmap_flags, bf, size, show_prefix, flags); } diff --git a/tools/perf/trace/beauty/tracepoints/Build b/tools/perf/trace/beauty/tracepoints/Build index 625a67663de3..e35087fdd108 100644 --- a/tools/perf/trace/beauty/tracepoints/Build +++ b/tools/perf/trace/beauty/tracepoints/Build @@ -1 +1,2 @@ +perf-y += x86_irq_vectors.o perf-y += x86_msr.o diff --git a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c new file mode 100644 index 000000000000..8eb9bc8534ac --- /dev/null +++ b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * trace/beauty/x86_irq_vectors.c + * + * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + */ + +#include "trace/beauty/beauty.h" + +#include "trace/beauty/generated/x86_arch_irq_vectors_array.c" + +static DEFINE_STRARRAY(x86_irq_vectors, "_VECTOR"); + +static size_t x86_irq_vectors__scnprintf(unsigned long vector, char *bf, size_t size, bool show_prefix) +{ + return strarray__scnprintf_suffix(&strarray__x86_irq_vectors, bf, size, "%#x", show_prefix, vector); +} + +size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg) +{ + unsigned long vector = arg->val; + + return x86_irq_vectors__scnprintf(vector, bf, size, arg->show_string_prefix); +} + +bool syscall_arg__strtoul_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg __maybe_unused, u64 *ret) +{ + return strarray__strtoul(&strarray__x86_irq_vectors, bf, size, ret); +} diff --git a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh new file mode 100755 index 000000000000..f920003723b3 --- /dev/null +++ b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# SPDX-License-Identifier: LGPL-2.1 +# (C) 2019, Arnaldo Carvalho de Melo <acme@redhat.com> + +if [ $# -ne 1 ] ; then + arch_x86_header_dir=tools/arch/x86/include/asm/ +else + arch_x86_header_dir=$1 +fi + +x86_irq_vectors=${arch_x86_header_dir}/irq_vectors.h + +# FIRST_EXTERNAL_VECTOR is not that useful, find what is its number +# and then replace whatever is using it and that is useful, which at +# the time of writing of this script was: IRQ_MOVE_CLEANUP_VECTOR. + +first_external_regex='^#define[[:space:]]+FIRST_EXTERNAL_VECTOR[[:space:]]+(0x[[:xdigit:]]+)$' +first_external_vector=$(egrep ${first_external_regex} ${x86_irq_vectors} | sed -r "s/${first_external_regex}/\1/g") + +printf "static const char *x86_irq_vectors[] = {\n" +regex='^#define[[:space:]]+([[:alnum:]_]+)_VECTOR[[:space:]]+(0x[[:xdigit:]]+)$' +sed -r "s/FIRST_EXTERNAL_VECTOR/${first_external_vector}/g" ${x86_irq_vectors} | \ +egrep ${regex} | \ + sed -r "s/${regex}/\2 \1/g" | sort -n | \ + xargs printf "\t[%s] = \"%s\",\n" +printf "};\n\n" + diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index bc69ca63b88e..ef1866a902c4 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -43,6 +43,7 @@ #include <linux/string.h> #include <bpf/libbpf.h> #include <subcmd/parse-options.h> +#include <subcmd/run-command.h> /* FIXME: For the HE_COLORSET */ #include "ui/browser.h" @@ -1489,44 +1490,26 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start * means that it's not a disassembly line so should be treated differently. * The ops.raw part will be parsed further according to type of the instruction. */ -static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, +static int symbol__parse_objdump_line(struct symbol *sym, struct annotate_args *args, - int *line_nr) + char *parsed_line, int *line_nr) { struct map *map = args->ms.map; struct annotation *notes = symbol__annotation(sym); struct disasm_line *dl; - char *line = NULL, *parsed_line, *tmp, *tmp2; - size_t line_len; + char *tmp; s64 line_ip, offset = -1; regmatch_t match[2]; - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - line_ip = -1; - parsed_line = strim(line); - /* /filename:linenr ? Save line number and ignore. */ if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) { *line_nr = atoi(parsed_line + match[1].rm_so); return 0; } - tmp = skip_spaces(parsed_line); - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') - line_ip = -1; - } - - if (line_ip != -1) { + /* Process hex address followed by ':'. */ + line_ip = strtoull(parsed_line, &tmp, 16); + if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') { u64 start = map__rip_2objdump(map, sym->start), end = map__rip_2objdump(map, sym->end); @@ -1534,7 +1517,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, if ((u64)line_ip < start || (u64)line_ip >= end) offset = -1; else - parsed_line = tmp2 + 1; + parsed_line = tmp + 1; } args->offset = offset; @@ -1543,7 +1526,6 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, args->ms.sym = sym; dl = disasm_line__new(args); - free(line); (*line_nr)++; if (dl == NULL) @@ -1861,6 +1843,67 @@ static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, } #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +/* + * Possibly create a new version of line with tabs expanded. Returns the + * existing or new line, storage is updated if a new line is allocated. If + * allocation fails then NULL is returned. + */ +static char *expand_tabs(char *line, char **storage, size_t *storage_len) +{ + size_t i, src, dst, len, new_storage_len, num_tabs; + char *new_line; + size_t line_len = strlen(line); + + for (num_tabs = 0, i = 0; i < line_len; i++) + if (line[i] == '\t') + num_tabs++; + + if (num_tabs == 0) + return line; + + /* + * Space for the line and '\0', less the leading and trailing + * spaces. Each tab may introduce 7 additional spaces. + */ + new_storage_len = line_len + 1 + (num_tabs * 7); + + new_line = malloc(new_storage_len); + if (new_line == NULL) { + pr_err("Failure allocating memory for tab expansion\n"); + return NULL; + } + + /* + * Copy regions starting at src and expand tabs. If there are two + * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces + * are inserted. + */ + for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) { + if (line[i] == '\t') { + len = i - src; + memcpy(&new_line[dst], &line[src], len); + dst += len; + new_line[dst++] = ' '; + while (dst % 8 != 0) + new_line[dst++] = ' '; + src = i + 1; + num_tabs--; + } + } + + /* Expand the last region. */ + len = line_len + 1 - src; + memcpy(&new_line[dst], &line[src], len); + dst += len; + new_line[dst] = '\0'; + + free(*storage); + *storage = new_line; + *storage_len = new_storage_len; + return new_line; + +} + static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) { struct annotation_options *opts = args->options; @@ -1872,10 +1915,19 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) struct kcore_extract kce; bool delete_extract = false; bool decomp = false; - int stdout_fd[2]; int lineno = 0; int nline; - pid_t pid; + char *line; + size_t line_len; + const char *objdump_argv[] = { + "/bin/sh", + "-c", + NULL, /* Will be the objdump command to run. */ + "--", + NULL, /* Will be the symfs path. */ + NULL, + }; + struct child_process objdump_process; int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); if (err) @@ -1905,7 +1957,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) - goto out; + return -1; decomp = true; strcpy(symfs_filename, tmp); @@ -1914,13 +1966,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C \"$1\" 2>/dev/null|grep -v \"$1:\"|expand", + " -l -d %s %s -C \"$1\"", opts->objdump_path ?: "objdump", opts->disassembler_style ? "-M " : "", opts->disassembler_style ?: "", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), - opts->show_asm_raw ? "" : "--no-show-raw", + opts->show_asm_raw ? "" : "--no-show-raw-insn", opts->annotate_src ? "-S" : ""); if (err < 0) { @@ -1930,55 +1982,73 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) pr_debug("Executing: %s\n", command); - err = -1; - if (pipe(stdout_fd) < 0) { - pr_err("Failure creating the pipe to run %s\n", command); - goto out_free_command; - } + objdump_argv[2] = command; + objdump_argv[4] = symfs_filename; - pid = fork(); - if (pid < 0) { - pr_err("Failure forking to run %s\n", command); - goto out_close_stdout; - } - - if (pid == 0) { - close(stdout_fd[0]); - dup2(stdout_fd[1], 1); - close(stdout_fd[1]); - execl("/bin/sh", "sh", "-c", command, "--", symfs_filename, - NULL); - perror(command); - exit(-1); + /* Create a pipe to read from for stdout */ + memset(&objdump_process, 0, sizeof(objdump_process)); + objdump_process.argv = objdump_argv; + objdump_process.out = -1; + if (start_command(&objdump_process)) { + pr_err("Failure starting to run %s\n", command); + err = -1; + goto out_free_command; } - close(stdout_fd[1]); - - file = fdopen(stdout_fd[0], "r"); + file = fdopen(objdump_process.out, "r"); if (!file) { pr_err("Failure creating FILE stream for %s\n", command); /* * If we were using debug info should retry with * original binary. */ - goto out_free_command; + err = -1; + goto out_close_stdout; } + /* Storage for getline. */ + line = NULL; + line_len = 0; + nline = 0; while (!feof(file)) { + const char *match; + char *expanded_line; + + if (getline(&line, &line_len, file) < 0 || !line) + break; + + /* Skip lines containing "filename:" */ + match = strstr(line, symfs_filename); + if (match && match[strlen(symfs_filename)] == ':') + continue; + + expanded_line = strim(line); + expanded_line = expand_tabs(expanded_line, &line, &line_len); + if (!expanded_line) + break; + /* * The source code line number (lineno) needs to be kept in * across calls to symbol__parse_objdump_line(), so that it * can associate it with the instructions till the next one. * See disasm_line__new() and struct disasm_line::line_nr. */ - if (symbol__parse_objdump_line(sym, file, args, &lineno) < 0) + if (symbol__parse_objdump_line(sym, args, expanded_line, + &lineno) < 0) break; nline++; } + free(line); + + err = finish_command(&objdump_process); + if (err) + pr_err("Error running %s\n", command); - if (nline == 0) + if (nline == 0) { + err = -1; pr_err("No output from %s\n", command); + } /* * kallsyms does not have symbol sizes so there may a nop at the end. @@ -1988,23 +2058,21 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) delete_last_nop(sym); fclose(file); - err = 0; + +out_close_stdout: + close(objdump_process.out); + out_free_command: free(command); -out_remove_tmp: - close(stdout_fd[0]); +out_remove_tmp: if (decomp) unlink(symfs_filename); if (delete_extract) kcore_extract__delete(&kce); -out: - return err; -out_close_stdout: - close(stdout_fd[1]); - goto out_free_command; + return err; } static void calc_percent(struct sym_hist *sym_hist, diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 8793b4e322b0..fdce590d2278 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -21,6 +21,7 @@ #include "../perf.h" #include "asm/bug.h" #include "bpf-event.h" +#include "util/string2.h" #include <signal.h> #include <unistd.h> #include <sched.h> @@ -598,14 +599,13 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, int i; struct mmap *map; - evlist->core.nr_mmaps = perf_cpu_map__nr(evlist->core.cpus); - if (perf_cpu_map__empty(evlist->core.cpus)) - evlist->core.nr_mmaps = perf_thread_map__nr(evlist->core.threads); map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap)); if (!map) return NULL; for (i = 0; i < evlist->core.nr_mmaps; i++) { + struct perf_mmap *prev = i ? &map[i - 1].core : NULL; + /* * When the perf_mmap() call is made we grab one refcount, plus * one extra to let perf_mmap__consume() get the last @@ -615,7 +615,7 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and * thus does perf_mmap__get() on it. */ - perf_mmap__init(&map[i].core, overwrite, perf_mmap__unmap_cb); + perf_mmap__init(&map[i].core, prev, overwrite, perf_mmap__unmap_cb); } return map; @@ -636,19 +636,21 @@ static struct perf_mmap* perf_evlist__mmap_cb_get(struct perf_evlist *_evlist, bool overwrite, int idx) { struct evlist *evlist = container_of(_evlist, struct evlist, core); - struct mmap *maps = evlist->mmap; + struct mmap *maps; - if (overwrite) { - maps = evlist->overwrite_mmap; + maps = overwrite ? evlist->overwrite_mmap : evlist->mmap; - if (!maps) { - maps = evlist__alloc_mmap(evlist, true); - if (!maps) - return NULL; + if (!maps) { + maps = evlist__alloc_mmap(evlist, overwrite); + if (!maps) + return NULL; + if (overwrite) { evlist->overwrite_mmap = maps; if (evlist->bkw_mmap_state == BKW_MMAP_NOTREADY) perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_RUNNING); + } else { + evlist->mmap = maps; } } @@ -809,14 +811,8 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned int pages, .mmap = perf_evlist__mmap_cb_mmap, }; - if (!evlist->mmap) - evlist->mmap = evlist__alloc_mmap(evlist, false); - if (!evlist->mmap) - return -ENOMEM; - evlist->core.mmap_len = evlist__mmap_size(pages); pr_debug("mmap size %zuB\n", evlist->core.mmap_len); - mp.core.mask = evlist->core.mmap_len - page_size - 1; auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->core.mmap_len, auxtrace_pages, auxtrace_overwrite); @@ -959,7 +955,7 @@ int perf_evlist__append_tp_filter(struct evlist *evlist, const char *filter) return err; } -static char *asprintf__tp_filter_pids(size_t npids, pid_t *pids) +char *asprintf__tp_filter_pids(size_t npids, pid_t *pids) { char *filter; size_t i; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b5e2adef49de..db882f630f7e 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2600,7 +2600,7 @@ out_enomem: * Print the help text for the event symbols: */ void print_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc, bool details_flag) + bool long_desc, bool details_flag, bool deprecated) { print_symbol_events(event_glob, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX, name_only); @@ -2612,7 +2612,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, print_hwcache_events(event_glob, name_only); print_pmu_events(event_glob, name_only, quiet_flag, long_desc, - details_flag); + details_flag, deprecated); if (event_glob != NULL) return; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 616ca1eda0eb..769e07cddaa2 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -195,7 +195,7 @@ void parse_events_evlist_error(struct parse_events_state *parse_state, int idx, const char *str); void print_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc, bool details_flag); + bool long_desc, bool details_flag, bool deprecated); struct event_symbol { const char *symbol; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 5608da82ad23..adbe97e941dd 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -308,7 +308,8 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, char *long_desc, char *topic, char *unit, char *perpkg, char *metric_expr, - char *metric_name) + char *metric_name, + char *deprecated) { struct parse_events_term *term; struct perf_pmu_alias *alias; @@ -325,6 +326,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->unit[0] = '\0'; alias->per_pkg = false; alias->snapshot = false; + alias->deprecated = false; ret = parse_events_terms(&alias->terms, val); if (ret) { @@ -379,6 +381,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; alias->str = strdup(newval); + if (deprecated) + alias->deprecated = true; + if (!perf_pmu_merge_alias(alias, list)) list_add_tail(&alias->list, list); @@ -400,7 +405,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI strim(buf); return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); } static inline bool pmu_alias_info_file(char *name) @@ -787,7 +792,8 @@ new_alias: (char *)pe->long_desc, (char *)pe->topic, (char *)pe->unit, (char *)pe->perpkg, (char *)pe->metric_expr, - (char *)pe->metric_name); + (char *)pe->metric_name, + (char *)pe->deprecated); } } @@ -1383,7 +1389,7 @@ static void wordwrap(char *s, int start, int max, int corr) } void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc, bool details_flag) + bool long_desc, bool details_flag, bool deprecated) { struct perf_pmu *pmu; struct perf_pmu_alias *alias; @@ -1414,6 +1420,9 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, format_alias(buf, sizeof(buf), pmu, alias); bool is_cpu = !strcmp(pmu->name, "cpu"); + if (alias->deprecated && !deprecated) + continue; + if (event_glob != NULL && !(strglobmatch_nocase(name, event_glob) || (!is_cpu && strglobmatch_nocase(alias->name, diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index f36ade6df76d..3e8cd31a89cc 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -57,6 +57,7 @@ struct perf_pmu_alias { double scale; bool per_pkg; bool snapshot; + bool deprecated; char *metric_expr; char *metric_name; }; @@ -85,7 +86,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); void print_pmu_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc, bool details_flag); + bool long_desc, bool details_flag, + bool deprecated); bool pmu_have_event(const char *pname, const char *name); int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4); diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 15961854ba67..741f040648b5 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -539,10 +539,11 @@ static int perl_stop_script(void) static int perl_generate_script(struct tep_handle *pevent, const char *outfile) { + int i, not_first, count, nr_events; + struct tep_event **all_events; struct tep_event *event = NULL; struct tep_format_field *f; char fname[PATH_MAX]; - int not_first, count; FILE *ofp; sprintf(fname, "%s.pl", outfile); @@ -603,8 +604,11 @@ sub print_backtrace\n\ }\n\n\ "); + nr_events = tep_get_events_count(pevent); + all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID); - while ((event = trace_find_next_event(pevent, event))) { + for (i = 0; all_events && i < nr_events; i++) { + event = all_events[i]; fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); fprintf(ofp, "\tmy ("); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 5d341efc3237..93c03b39cd9c 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1687,10 +1687,11 @@ static int python_stop_script(void) static int python_generate_script(struct tep_handle *pevent, const char *outfile) { + int i, not_first, count, nr_events; + struct tep_event **all_events; struct tep_event *event = NULL; struct tep_format_field *f; char fname[PATH_MAX]; - int not_first, count; FILE *ofp; sprintf(fname, "%s.py", outfile); @@ -1735,7 +1736,11 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile fprintf(ofp, "def trace_end():\n"); fprintf(ofp, "\tprint(\"in trace_end\")\n\n"); - while ((event = trace_find_next_event(pevent, event))) { + nr_events = tep_get_events_count(pevent); + all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID); + + for (i = 0; all_events && i < nr_events; i++) { + event = all_events[i]; fprintf(ofp, "def %s__%s(", event->system, event->name); fprintf(ofp, "event_name, "); fprintf(ofp, "context, "); diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index ebdd130557fb..6822e4ffe224 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -490,6 +490,16 @@ int create_perf_stat_counter(struct evsel *evsel, if (config->identifier) attr->sample_type = PERF_SAMPLE_IDENTIFIER; + if (config->all_user) { + attr->exclude_kernel = 1; + attr->exclude_user = 0; + } + + if (config->all_kernel) { + attr->exclude_kernel = 0; + attr->exclude_user = 1; + } + /* * Disabling all counters initially, they will be enabled * either manually by us or by kernel via enable_on_exec diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index edbeb2f63e8d..081c4a5113c6 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -106,6 +106,8 @@ struct perf_stat_config { bool big_num; bool no_merge; bool walltime_run_table; + bool all_kernel; + bool all_user; FILE *output; unsigned int interval; unsigned int timeout; diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h index 708805f5573e..73df616ced43 100644 --- a/tools/perf/util/string2.h +++ b/tools/perf/util/string2.h @@ -4,6 +4,7 @@ #include <linux/string.h> #include <linux/types.h> +#include <sys/types.h> // pid_t #include <stddef.h> #include <string.h> @@ -32,6 +33,8 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int return asprintf_expr_inout_ints(var, false, nints, ints); } +char *asprintf__tp_filter_pids(size_t npids, pid_t *pids); + char *strpbrk_esc(char *str, const char *stopset); char *strdup_esc(const char *str); diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 9796a2e43f67..302443921681 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -458,10 +458,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, return true; } -int perf_time__parse_for_ranges(const char *time_str, +int perf_time__parse_for_ranges_reltime(const char *time_str, struct perf_session *session, struct perf_time_interval **ranges, - int *range_size, int *range_num) + int *range_size, int *range_num, + bool reltime) { bool has_percent = strchr(time_str, '%'); struct perf_time_interval *ptime_range; @@ -471,7 +472,7 @@ int perf_time__parse_for_ranges(const char *time_str, if (!ptime_range) return -ENOMEM; - if (has_percent) { + if (has_percent || reltime) { if (session->evlist->first_sample_time == 0 && session->evlist->last_sample_time == 0) { pr_err("HINT: no first/last sample time found in perf data.\n" @@ -479,7 +480,9 @@ int perf_time__parse_for_ranges(const char *time_str, "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n"); goto error; } + } + if (has_percent) { num = perf_time__percent_parse_str( ptime_range, size, time_str, @@ -492,6 +495,15 @@ int perf_time__parse_for_ranges(const char *time_str, if (num < 0) goto error_invalid; + if (reltime) { + int i; + + for (i = 0; i < num; i++) { + ptime_range[i].start += session->evlist->first_sample_time; + ptime_range[i].end += session->evlist->first_sample_time; + } + } + *range_size = size; *range_num = num; *ranges = ptime_range; @@ -504,6 +516,15 @@ error: return ret; } +int perf_time__parse_for_ranges(const char *time_str, + struct perf_session *session, + struct perf_time_interval **ranges, + int *range_size, int *range_num) +{ + return perf_time__parse_for_ranges_reltime(time_str, session, ranges, + range_size, range_num, false); +} + int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) { u64 sec = timestamp / NSEC_PER_SEC; diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h index 4f42988eb2f7..1142b0bddd5e 100644 --- a/tools/perf/util/time-utils.h +++ b/tools/perf/util/time-utils.h @@ -26,6 +26,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, struct perf_session; +int perf_time__parse_for_ranges_reltime(const char *str, struct perf_session *session, + struct perf_time_interval **ranges, + int *range_size, int *range_num, + bool reltime); + int perf_time__parse_for_ranges(const char *str, struct perf_session *session, struct perf_time_interval **ranges, int *range_size, int *range_num); diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 5d6bfc70b210..9634f0ae57be 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -173,37 +173,6 @@ int parse_event_file(struct tep_handle *pevent, return tep_parse_event(pevent, buf, size, sys); } -struct tep_event *trace_find_next_event(struct tep_handle *pevent, - struct tep_event *event) -{ - static int idx; - int events_count; - struct tep_event *all_events; - - all_events = tep_get_first_event(pevent); - events_count = tep_get_events_count(pevent); - if (!pevent || !all_events || events_count < 1) - return NULL; - - if (!event) { - idx = 0; - return all_events; - } - - if (idx < events_count && event == (all_events + idx)) { - idx++; - if (idx == events_count) - return NULL; - return (all_events + idx); - } - - for (idx = 1; idx < events_count; idx++) { - if (event == (all_events + (idx - 1))) - return (all_events + idx); - } - return NULL; -} - struct flag { const char *name; unsigned long long value; diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 2e158387b3d7..72fdf2a3577c 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -47,8 +47,6 @@ void parse_saved_cmdline(struct tep_handle *pevent, char *file, unsigned int siz ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); -struct tep_event *trace_find_next_event(struct tep_handle *pevent, - struct tep_event *event); unsigned long long read_size(struct tep_event *event, void *ptr, int size); unsigned long long eval_flag(const char *flag); |