diff --git a/tools/include/asm-generic/bitops.h b/tools/include/asm-generic/bitops.h index 653d1bad77de2..0304600121daa 100644 --- a/tools/include/asm-generic/bitops.h +++ b/tools/include/asm-generic/bitops.h @@ -13,6 +13,7 @@ */ #include +#include #include #include #include diff --git a/tools/include/asm-generic/bitops/__ffz.h b/tools/include/asm-generic/bitops/__ffz.h new file mode 100644 index 0000000000000..6744bd4cdf465 --- /dev/null +++ b/tools/include/asm-generic/bitops/__ffz.h @@ -0,0 +1,12 @@ +#ifndef _ASM_GENERIC_BITOPS_FFZ_H_ +#define _ASM_GENERIC_BITOPS_FFZ_H_ + +/* + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +#define ffz(x) __ffs(~(x)) + +#endif /* _ASM_GENERIC_BITOPS_FFZ_H_ */ diff --git a/tools/include/asm-generic/bitops/find.h b/tools/include/asm-generic/bitops/find.h index 31f51547fcd41..5538ecdc964a2 100644 --- a/tools/include/asm-generic/bitops/find.h +++ b/tools/include/asm-generic/bitops/find.h @@ -15,6 +15,21 @@ extern unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); #endif +#ifndef find_next_zero_bit + +/** + * find_next_zero_bit - find the next cleared bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + * + * Returns the bit number of the next zero bit + * If no bits are zero, returns @size. + */ +unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, + unsigned long offset); +#endif + #ifndef find_first_bit /** @@ -30,4 +45,17 @@ extern unsigned long find_first_bit(const unsigned long *addr, #endif /* find_first_bit */ +#ifndef find_first_zero_bit + +/** + * find_first_zero_bit - find the first cleared bit in a memory region + * @addr: The address to start the search at + * @size: The maximum number of bits to search + * + * Returns the bit number of the first cleared bit. + * If no bits are zero, returns @size. + */ +unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size); +#endif + #endif /*_TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ */ diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h index 49c929a104eee..fc446343ff417 100644 --- a/tools/include/linux/bitops.h +++ b/tools/include/linux/bitops.h @@ -39,6 +39,11 @@ extern unsigned long __sw_hweight64(__u64 w); (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) +#define for_each_clear_bit(bit, addr, size) \ + for ((bit) = find_first_zero_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) + /* same as for_each_set_bit() but use bit as value to start with */ #define for_each_set_bit_from(bit, addr, size) \ for ((bit) = find_next_bit((addr), (size), (bit)); \ diff --git a/tools/lib/find_bit.c b/tools/lib/find_bit.c index 9122a9e800460..6d8b8f22cf55d 100644 --- a/tools/lib/find_bit.c +++ b/tools/lib/find_bit.c @@ -82,3 +82,28 @@ unsigned long find_first_bit(const unsigned long *addr, unsigned long size) return size; } #endif + +#ifndef find_first_zero_bit +/* + * Find the first cleared bit in a memory region. + */ +unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size) +{ + unsigned long idx; + + for (idx = 0; idx * BITS_PER_LONG < size; idx++) { + if (addr[idx] != ~0UL) + return min(idx * BITS_PER_LONG + ffz(addr[idx]), size); + } + + return size; +} +#endif + +#ifndef find_next_zero_bit +unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + return _find_next_bit(addr, size, offset, ~0UL); +} +#endif diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 7851df1490e0a..c76012ebdb9cc 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -99,8 +99,6 @@ libdir_SQ = $(subst ','\'',$(libdir)) libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) -LIB_FILE = libtraceevent.a libtraceevent.so - CONFIG_INCLUDES = CONFIG_LIBS = CONFIG_FLAGS = @@ -114,6 +112,9 @@ N = EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) +LIB_TARGET = libtraceevent.a libtraceevent.so.$(EVENT_PARSE_VERSION) +LIB_INSTALL = libtraceevent.a libtraceevent.so* + INCLUDES = -I. -I $(srctree)/tools/include $(CONFIG_INCLUDES) # Set compile option CFLAGS @@ -156,11 +157,11 @@ PLUGINS += plugin_cfg80211.so PLUGINS := $(addprefix $(OUTPUT),$(PLUGINS)) PLUGINS_IN := $(PLUGINS:.so=-in.o) -TE_IN := $(OUTPUT)libtraceevent-in.o -LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE)) +TE_IN := $(OUTPUT)libtraceevent-in.o +LIB_TARGET := $(addprefix $(OUTPUT),$(LIB_TARGET)) DYNAMIC_LIST_FILE := $(OUTPUT)libtraceevent-dynamic-list -CMD_TARGETS = $(LIB_FILE) $(PLUGINS) $(DYNAMIC_LIST_FILE) +CMD_TARGETS = $(LIB_TARGET) $(PLUGINS) $(DYNAMIC_LIST_FILE) TARGETS = $(CMD_TARGETS) @@ -171,8 +172,10 @@ all_cmd: $(CMD_TARGETS) $(TE_IN): force $(Q)$(MAKE) $(build)=libtraceevent -$(OUTPUT)libtraceevent.so: $(TE_IN) - $(QUIET_LINK)$(CC) --shared $^ -o $@ +$(OUTPUT)libtraceevent.so.$(EVENT_PARSE_VERSION): $(TE_IN) + $(QUIET_LINK)$(CC) --shared $^ -Wl,-soname,libtraceevent.so.$(EP_VERSION) -o $@ + @ln -sf $(@F) $(OUTPUT)libtraceevent.so + @ln -sf $(@F) $(OUTPUT)libtraceevent.so.$(EP_VERSION) $(OUTPUT)libtraceevent.a: $(TE_IN) $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^ @@ -236,11 +239,15 @@ TAGS: force find . -name '*.[ch]' | xargs etags \ --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' +define do_install_mkdir + if [ ! -d '$(DESTDIR_SQ)$1' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \ + fi +endef + define do_install - if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ - fi; \ - $(INSTALL) $1 '$(DESTDIR_SQ)$2' + $(call do_install_mkdir,$2); \ + $(INSTALL) $(if $3,-m $3,) $1 '$(DESTDIR_SQ)$2' endef define do_install_plugins @@ -257,13 +264,20 @@ define do_generate_dynamic_list_file endef install_lib: all_cmd install_plugins - $(call QUIET_INSTALL, $(LIB_FILE)) \ - $(call do_install,$(LIB_FILE),$(libdir_SQ)) + $(call QUIET_INSTALL, $(LIB_TARGET)) \ + $(call do_install_mkdir,$(libdir_SQ)); \ + cp -fpR $(LIB_INSTALL) $(DESTDIR)$(libdir_SQ) install_plugins: $(PLUGINS) $(call QUIET_INSTALL, trace_plugins) \ $(call do_install_plugins, $(PLUGINS)) +install_headers: + $(call QUIET_INSTALL, headers) \ + $(call do_install,event-parse.h,$(prefix)/include/traceevent,644); \ + $(call do_install,event-utils.h,$(prefix)/include/traceevent,644); \ + $(call do_install,kbuffer.h,$(prefix)/include/traceevent,644) + install: install_lib clean: diff --git a/tools/perf/Documentation/jitdump-specification.txt b/tools/perf/Documentation/jitdump-specification.txt new file mode 100644 index 0000000000000..4c62b07136517 --- /dev/null +++ b/tools/perf/Documentation/jitdump-specification.txt @@ -0,0 +1,170 @@ +JITDUMP specification version 2 +Last Revised: 09/15/2016 +Author: Stephane Eranian + +-------------------------------------------------------- +| Revision | Date | Description | +-------------------------------------------------------- +| 1 | 09/07/2016 | Initial revision | +-------------------------------------------------------- +| 2 | 09/15/2016 | Add JIT_CODE_UNWINDING_INFO | +-------------------------------------------------------- + + +I/ Introduction + + +This document describes the jitdump file format. The file is generated by Just-In-time compiler runtimes to save meta-data information about the generated code, such as address, size, and name of generated functions, the native code generated, the source line information. The data may then be used by performance tools, such as Linux perf to generate function and assembly level profiles. + +The format is not specific to any particular programming language. It can be extended as need be. + +The format of the file is binary. It is self-describing in terms of endianness and is portable across multiple processor architectures. + + +II/ Overview of the format + + +The format requires only sequential accesses, i.e., append only mode. The file starts with a fixed size file header describing the version of the specification, the endianness. + +The header is followed by a series of records, each starting with a fixed size header describing the type of record and its size. It is, itself, followed by the payload for the record. Records can have a variable size even for a given type. + +Each entry in the file is timestamped. All timestamps must use the same clock source. The CLOCK_MONOTONIC clock source is recommended. + + +III/ Jitdump file header format + +Each jitdump file starts with a fixed size header containing the following fields in order: + + +* uint32_t magic : a magic number tagging the file type. The value is 4-byte long and represents the string "JiTD" in ASCII form. It is 0x4A695444 or 0x4454694a depending on the endianness. The field can be used to detect the endianness of the file +* uint32_t version : a 4-byte value representing the format version. It is currently set to 2 +* uint32_t total_size: size in bytes of file header +* uint32_t elf_mach : ELF architecture encoding (ELF e_machine value as specified in /usr/include/elf.h) +* uint32_t pad1 : padding. Reserved for future use +* uint32_t pid : JIT runtime process identification (OS specific) +* uint64_t timestamp : timestamp of when the file was created +* uint64_t flags : a bitmask of flags + +The flags currently defined are as follows: + * bit 0: JITDUMP_FLAGS_ARCH_TIMESTAMP : set if the jitdump file is using an architecture-specific timestamp clock source. For instance, on x86, one could use TSC directly + +IV/ Record header + +The file header is immediately followed by records. Each record starts with a fixed size header describing the record that follows. + +The record header is specified in order as follows: +* uint32_t id : a value identifying the record type (see below) +* uint32_t total_size: the size in bytes of the record including the header. +* uint64_t timestamp : a timestamp of when the record was created. + +The following record types are defined: + * Value 0 : JIT_CODE_LOAD : record describing a jitted function + * Value 1 : JIT_CODE_MOVE : record describing an already jitted function which is moved + * Value 2 : JIT_CODE_DEBUG_INFO: record describing the debug information for a jitted function + * Value 3 : JIT_CODE_CLOSE : record marking the end of the jit runtime (optional) + * Value 4 : JIT_CODE_UNWINDING_INFO: record describing a function unwinding information + + The payload of the record must immediately follow the record header without padding. + +V/ JIT_CODE_LOAD record + + + The record has the following fields following the fixed-size record header in order: + * uint32_t pid: OS process id of the runtime generating the jitted code + * uint32_t tid: OS thread identification of the runtime thread generating the jitted code + * uint64_t vma: virtual address of jitted code start + * uint64_t code_addr: code start address for the jitted code. By default vma = code_addr + * uint64_t code_size: size in bytes of the generated jitted code + * uint64_t code_index: unique identifier for the jitted code (see below) + * char[n]: function name in ASCII including the null termination + * native code: raw byte encoding of the jitted code + + The record header total_size field is inclusive of all components: + * record header + * fixed-sized fields + * function name string, including termination + * native code length + * record specific variable data (e.g., array of data entries) + +The code_index is used to uniquely identify each jitted function. The index can be a monotonically increasing 64-bit value. Each time a function is jitted it gets a new number. This value is used in case the code for a function is moved and avoids having to issue another JIT_CODE_LOAD record. + +The format supports empty functions with no native code. + + +VI/ JIT_CODE_MOVE record + + The record type is optional. + + The record has the following fields following the fixed-size record header in order: + * uint32_t pid : OS process id of the runtime generating the jitted code + * uint32_t tid : OS thread identification of the runtime thread generating the jitted code + * uint64_t vma : new virtual address of jitted code start + * uint64_t old_code_addr: previous code address for the same function + * uint64_t new_code_addr: alternate new code started address for the jitted code. By default it should be equal to the vma address. + * uint64_t code_size : size in bytes of the jitted code + * uint64_t code_index : index referring to the JIT_CODE_LOAD code_index record of when the function was initially jitted + + +The MOVE record can be used in case an already jitted function is simply moved by the runtime inside the code cache. + +The JIT_CODE_MOVE record cannot come before the JIT_CODE_LOAD record for the same function name. The function cannot have changed name, otherwise a new JIT_CODE_LOAD record must be emitted. + +The code size of the function cannot change. + + +VII/ JIT_DEBUG_INFO record + +The record type is optional. + +The record contains source lines debug information, i.e., a way to map a code address back to a source line. This information may be used by the performance tool. + +The record has the following fields following the fixed-size record header in order: + * uint64_t code_addr: address of function for which the debug information is generated + * uint64_t nr_entry : number of debug entries for the function + * debug_entry[n]: array of nr_entry debug entries for the function + +The debug_entry describes the source line information. It is defined as follows in order: +* uint64_t code_addr: address of function for which the debug information is generated +* uint32_t line : source file line number (starting at 1) +* uint32_t discrim : column discriminator, 0 is default +* char name[n] : source file name in ASCII, including null termination + +The debug_entry entries are saved in sequence but given that they have variable sizes due to the file name string, they cannot be indexed directly. +They need to be walked sequentially. The next debug_entry is found at sizeof(debug_entry) + strlen(name) + 1. + +IMPORTANT: + The JIT_CODE_DEBUG for a given function must always be generated BEFORE the JIT_CODE_LOAD for the function. This facilitates greatly the parser for the jitdump file. + + +VIII/ JIT_CODE_CLOSE record + + +The record type is optional. + +The record is used as a marker for the end of the jitted runtime. It can be replaced by the end of the file. + +The JIT_CODE_CLOSE record does not have any specific fields, the record header contains all the information needed. + + +IX/ JIT_CODE_UNWINDING_INFO + + +The record type is optional. + +The record is used to describe the unwinding information for a jitted function. + +The record has the following fields following the fixed-size record header in order: + +uint64_t unwind_data_size : the size in bytes of the unwinding data table at the end of the record +uint64_t eh_frame_hdr_size : the size in bytes of the DWARF EH Frame Header at the start of the unwinding data table at the end of the record +uint64_t mapped_size : the size of the unwinding data mapped in memory +const char unwinding_data[n]: an array of unwinding data, consisting of the EH Frame Header, followed by the actual EH Frame + + +The EH Frame header follows the Linux Standard Base (LSB) specification as described in the document at https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html + + +The EH Frame follows the LSB specicfication as described in the document at https://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html + + +NOTE: The mapped_size is generally either the same as unwind_data_size (if the unwinding data was mapped in memory by the running process) or zero (if the unwinding data is not mapped by the process). If the unwinding data was not mapped, then only the EH Frame Header will be read, which can be used to specify FP based unwinding for a function which does not have unwinding information. diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 92335193dc338..27fc3617c6a42 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -45,9 +45,9 @@ OPTIONS param1 and param2 are defined as formats for the PMU in: /sys/bus/event_source/devices//format/* - There are also some params which are not defined in ...//format/*. + There are also some parameters which are not defined in ...//format/*. These params can be used to overload default config values per event. - Here is a list of the params. + Here are some common parameters: - 'period': Set event sampling period - 'freq': Set event sampling frequency - 'time': Disable/enable time stamping. Acceptable values are 1 for @@ -57,8 +57,11 @@ OPTIONS FP mode, "dwarf" for DWARF mode, "lbr" for LBR mode and "no" for disable callgraph. - 'stack-size': user stack size for dwarf mode + + See the linkperf:perf-list[1] man page for more parameters. + Note: If user explicitly sets options which conflict with the params, - the value set by the params will be overridden. + the value set by the parameters will be overridden. Also not defined in ...//format/* are PMU driver specific configuration parameters. Any configuration parameter preceded by diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 053bbbd84ece3..c01904f388ce8 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -117,7 +117,7 @@ OPTIONS Comma separated list of fields to print. Options are: comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period, iregs, brstack, brstacksym, flags, bpf-output, - callindent. Field list can be prepended with the type, trace, sw or hw, + callindent, insn, insnlen. Field list can be prepended with the type, trace, sw or hw, to indicate to which event type the field list applies. e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace @@ -181,6 +181,10 @@ OPTIONS Instruction Trace decoding. For calls and returns, it will display the name of the symbol indented with spaces to reflect the stack depth. + When doing instruction trace decoding insn and insnlen give the + instruction bytes and the instruction length of the current + instruction. + Finally, a user may not set fields to none for all event types. i.e., -F "" is not allowed. diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 1ab0782369b1f..781b019751a4c 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -39,6 +39,11 @@ OPTIONS Prefixing with ! shows all syscalls but the ones specified. You may need to escape it. +-D msecs:: +--delay msecs:: +After starting the program, wait msecs before measuring. This is useful to +filter out the startup phase of the program, which is often very different. + -o:: --output=:: Output file name. diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 0bda2cca2b3a6..a511e5f31e361 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -51,6 +51,7 @@ tools/include/asm-generic/bitops/arch_hweight.h tools/include/asm-generic/bitops/atomic.h tools/include/asm-generic/bitops/const_hweight.h tools/include/asm-generic/bitops/__ffs.h +tools/include/asm-generic/bitops/__ffz.h tools/include/asm-generic/bitops/__fls.h tools/include/asm-generic/bitops/find.h tools/include/asm-generic/bitops/fls64.h diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 72edf83d76b72..cffdd9cf3ebf7 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -366,7 +366,7 @@ ifndef NO_SDT endif ifdef PERF_HAVE_JITDUMP - ifndef NO_DWARF + ifndef NO_LIBELF $(call detected,CONFIG_JITDUMP) CFLAGS += -DHAVE_JITDUMP endif diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 47d584da58199..dfea6b6355254 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -575,8 +575,6 @@ static FILE *cs_device__open_file(const char *name) snprintf(path, PATH_MAX, "%s" CS_BUS_DEVICE_PATH "%s", sysfs, name); - printf("path: %s\n", path); - if (stat(path, &st) < 0) return NULL; diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index 555263e385c92..e9ce9c7c39b48 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -374,5 +374,5 @@ 543 x32 io_setup compat_sys_io_setup 544 x32 io_submit compat_sys_io_submit 545 x32 execveat compat_sys_execveat/ptregs -534 x32 preadv2 compat_sys_preadv2 -535 x32 pwritev2 compat_sys_pwritev2 +546 x32 preadv2 compat_sys_preadv64v2 +547 x32 pwritev2 compat_sys_pwritev64v2 diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index 8024cd5febd22..d9e5e80bb4d0b 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -39,12 +39,15 @@ static unsigned int threads_starting; static struct stats throughput_stats; static pthread_cond_t thread_parent, thread_worker; +#define SMP_CACHE_BYTES 256 +#define __cacheline_aligned __attribute__ ((aligned (SMP_CACHE_BYTES))) + struct worker { int tid; u_int32_t *futex; pthread_t thread; unsigned long ops; -}; +} __cacheline_aligned; static const struct option options[] = { OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c index c684910e5a482..52504a83b5a15 100644 --- a/tools/perf/bench/mem-functions.c +++ b/tools/perf/bench/mem-functions.c @@ -106,9 +106,10 @@ static double timeval2double(struct timeval *ts) struct bench_mem_info { const struct function *functions; - u64 (*do_cycles)(const struct function *r, size_t size); - double (*do_gettimeofday)(const struct function *r, size_t size); + u64 (*do_cycles)(const struct function *r, size_t size, void *src, void *dst); + double (*do_gettimeofday)(const struct function *r, size_t size, void *src, void *dst); const char *const *usage; + bool alloc_src; }; static void __bench_mem_function(struct bench_mem_info *info, int r_idx, size_t size, double size_total) @@ -116,16 +117,26 @@ static void __bench_mem_function(struct bench_mem_info *info, int r_idx, size_t const struct function *r = &info->functions[r_idx]; double result_bps = 0.0; u64 result_cycles = 0; + void *src = NULL, *dst = zalloc(size); printf("# function '%s' (%s)\n", r->name, r->desc); + if (dst == NULL) + goto out_alloc_failed; + + if (info->alloc_src) { + src = zalloc(size); + if (src == NULL) + goto out_alloc_failed; + } + if (bench_format == BENCH_FORMAT_DEFAULT) printf("# Copying %s bytes ...\n\n", size_str); if (use_cycles) { - result_cycles = info->do_cycles(r, size); + result_cycles = info->do_cycles(r, size, src, dst); } else { - result_bps = info->do_gettimeofday(r, size); + result_bps = info->do_gettimeofday(r, size, src, dst); } switch (bench_format) { @@ -149,6 +160,14 @@ static void __bench_mem_function(struct bench_mem_info *info, int r_idx, size_t BUG_ON(1); break; } + +out_free: + free(src); + free(dst); + return; +out_alloc_failed: + printf("# Memory allocation failed - maybe size (%s) is too large?\n", size_str); + goto out_free; } static int bench_mem_common(int argc, const char **argv, struct bench_mem_info *info) @@ -201,28 +220,14 @@ static int bench_mem_common(int argc, const char **argv, struct bench_mem_info * return 0; } -static void memcpy_alloc_mem(void **dst, void **src, size_t size) -{ - *dst = zalloc(size); - if (!*dst) - die("memory allocation failed - maybe size is too large?\n"); - - *src = zalloc(size); - if (!*src) - die("memory allocation failed - maybe size is too large?\n"); - - /* Make sure to always prefault zero pages even if MMAP_THRESH is crossed: */ - memset(*src, 0, size); -} - -static u64 do_memcpy_cycles(const struct function *r, size_t size) +static u64 do_memcpy_cycles(const struct function *r, size_t size, void *src, void *dst) { u64 cycle_start = 0ULL, cycle_end = 0ULL; - void *src = NULL, *dst = NULL; memcpy_t fn = r->fn.memcpy; int i; - memcpy_alloc_mem(&dst, &src, size); + /* Make sure to always prefault zero pages even if MMAP_THRESH is crossed: */ + memset(src, 0, size); /* * We prefault the freshly allocated memory range here, @@ -235,20 +240,15 @@ static u64 do_memcpy_cycles(const struct function *r, size_t size) fn(dst, src, size); cycle_end = get_cycles(); - free(src); - free(dst); return cycle_end - cycle_start; } -static double do_memcpy_gettimeofday(const struct function *r, size_t size) +static double do_memcpy_gettimeofday(const struct function *r, size_t size, void *src, void *dst) { struct timeval tv_start, tv_end, tv_diff; memcpy_t fn = r->fn.memcpy; - void *src = NULL, *dst = NULL; int i; - memcpy_alloc_mem(&dst, &src, size); - /* * We prefault the freshly allocated memory range here, * to not measure page fault overhead: @@ -262,9 +262,6 @@ static double do_memcpy_gettimeofday(const struct function *r, size_t size) timersub(&tv_end, &tv_start, &tv_diff); - free(src); - free(dst); - return (double)(((double)size * nr_loops) / timeval2double(&tv_diff)); } @@ -294,27 +291,18 @@ int bench_mem_memcpy(int argc, const char **argv, const char *prefix __maybe_unu .do_cycles = do_memcpy_cycles, .do_gettimeofday = do_memcpy_gettimeofday, .usage = bench_mem_memcpy_usage, + .alloc_src = true, }; return bench_mem_common(argc, argv, &info); } -static void memset_alloc_mem(void **dst, size_t size) -{ - *dst = zalloc(size); - if (!*dst) - die("memory allocation failed - maybe size is too large?\n"); -} - -static u64 do_memset_cycles(const struct function *r, size_t size) +static u64 do_memset_cycles(const struct function *r, size_t size, void *src __maybe_unused, void *dst) { u64 cycle_start = 0ULL, cycle_end = 0ULL; memset_t fn = r->fn.memset; - void *dst = NULL; int i; - memset_alloc_mem(&dst, size); - /* * We prefault the freshly allocated memory range here, * to not measure page fault overhead: @@ -326,19 +314,15 @@ static u64 do_memset_cycles(const struct function *r, size_t size) fn(dst, i, size); cycle_end = get_cycles(); - free(dst); return cycle_end - cycle_start; } -static double do_memset_gettimeofday(const struct function *r, size_t size) +static double do_memset_gettimeofday(const struct function *r, size_t size, void *src __maybe_unused, void *dst) { struct timeval tv_start, tv_end, tv_diff; memset_t fn = r->fn.memset; - void *dst = NULL; int i; - memset_alloc_mem(&dst, size); - /* * We prefault the freshly allocated memory range here, * to not measure page fault overhead: @@ -352,7 +336,6 @@ static double do_memset_gettimeofday(const struct function *r, size_t size) timersub(&tv_end, &tv_start, &tv_diff); - free(dst); return (double)(((double)size * nr_loops) / timeval2double(&tv_diff)); } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6e88460cd13d3..8064de8ceedc4 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -207,11 +207,14 @@ static int process_read_event(struct perf_tool *tool, if (rep->show_threads) { const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; - perf_read_values_add_value(&rep->show_threads_values, + int err = perf_read_values_add_value(&rep->show_threads_values, event->read.pid, event->read.tid, event->read.id, name, event->read.value); + + if (err) + return err; } dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, @@ -539,8 +542,11 @@ static int __cmd_report(struct report *rep) } } - if (rep->show_threads) - perf_read_values_init(&rep->show_threads_values); + if (rep->show_threads) { + ret = perf_read_values_init(&rep->show_threads_values); + if (ret) + return ret; + } ret = report__setup_sample_type(rep); if (ret) { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 7228d141a789d..412fb6e65ac06 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -66,6 +66,8 @@ enum perf_output_field { PERF_OUTPUT_WEIGHT = 1U << 18, PERF_OUTPUT_BPF_OUTPUT = 1U << 19, PERF_OUTPUT_CALLINDENT = 1U << 20, + PERF_OUTPUT_INSN = 1U << 21, + PERF_OUTPUT_INSNLEN = 1U << 22, }; struct output_option { @@ -93,6 +95,8 @@ struct output_option { {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT}, {.str = "callindent", .field = PERF_OUTPUT_CALLINDENT}, + {.str = "insn", .field = PERF_OUTPUT_INSN}, + {.str = "insnlen", .field = PERF_OUTPUT_INSNLEN}, }; /* default set to maintain compatibility with current format */ @@ -624,6 +628,20 @@ static void print_sample_callindent(struct perf_sample *sample, printf("%*s", spacing - len, ""); } +static void print_insn(struct perf_sample *sample, + struct perf_event_attr *attr) +{ + if (PRINT_FIELD(INSNLEN)) + printf(" ilen: %d", sample->insn_len); + if (PRINT_FIELD(INSN)) { + int i; + + printf(" insn:"); + for (i = 0; i < sample->insn_len; i++) + printf(" %02x", (unsigned char)sample->insn[i]); + } +} + static void print_sample_bts(struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread, @@ -668,6 +686,8 @@ static void print_sample_bts(struct perf_sample *sample, if (print_srcline_last) map__fprintf_srcline(al->map, al->addr, "\n ", stdout); + print_insn(sample, attr); + printf("\n"); } @@ -911,7 +931,7 @@ static void process_event(struct perf_script *script, if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) print_sample_bpf_output(sample); - + print_insn(sample, attr); printf("\n"); } @@ -2124,7 +2144,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) "Valid types: hw,sw,trace,raw. " "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," "addr,symoff,period,iregs,brstack,brstacksym,flags," - "bpf-output,callindent", parse_output_fields), + "bpf-output,callindent,insn,insnlen", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c298bd3e1d909..5f45166c892d6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -843,7 +843,6 @@ static size_t fprintf_duration(unsigned long t, FILE *fp) */ struct thread_trace { u64 entry_time; - u64 exit_time; bool entry_pending; unsigned long nr_events; unsigned long pfmaj, pfmin; @@ -1452,7 +1451,7 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp duration = sample->time - ttrace->entry_time; - printed = trace__fprintf_entry_head(trace, trace->current, duration, sample->time, trace->output); + printed = trace__fprintf_entry_head(trace, trace->current, duration, ttrace->entry_time, trace->output); printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); ttrace->entry_pending = false; @@ -1499,7 +1498,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, if (sc->is_exit) { if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) { - trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); + trace__fprintf_entry_head(trace, thread, 1, ttrace->entry_time, trace->output); fprintf(trace->output, "%-70s)\n", ttrace->entry_str); } } else { @@ -1571,8 +1570,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ++trace->stats.vfs_getname; } - ttrace->exit_time = sample->time; - if (ttrace->entry_time) { duration = sample->time - ttrace->entry_time; if (trace__filter_duration(trace, duration)) @@ -1592,7 +1589,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, if (trace->summary_only) goto out; - trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); + trace__fprintf_entry_head(trace, thread, duration, ttrace->entry_time, trace->output); if (ttrace->entry_pending) { fprintf(trace->output, "%-70s", ttrace->entry_str); @@ -2310,12 +2307,17 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (err < 0) goto out_error_mmap; - if (!target__none(&trace->opts.target)) + if (!target__none(&trace->opts.target) && !trace->opts.initial_delay) perf_evlist__enable(evlist); if (forks) perf_evlist__start_workload(evlist); + if (trace->opts.initial_delay) { + usleep(trace->opts.initial_delay * 1000); + perf_evlist__enable(evlist); + } + trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 || evlist->threads->nr > 1 || perf_evlist__first(evlist)->attr.inherit; @@ -2816,6 +2818,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)), OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, "per thread proc mmap processing timeout in ms"), + OPT_UINTEGER('D', "delay", &trace.opts.initial_delay, + "ms to wait before starting measurement after program " + "start"), OPT_END() }; bool __maybe_unused max_stack_user_set = true; diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c index 55daefff0d54d..e9651a9d670e9 100644 --- a/tools/perf/jvmti/jvmti_agent.c +++ b/tools/perf/jvmti/jvmti_agent.c @@ -44,11 +44,6 @@ static char jit_path[PATH_MAX]; static void *marker_addr; -/* - * padding buffer - */ -static const char pad_bytes[7]; - static inline pid_t gettid(void) { return (pid_t)syscall(__NR_gettid); @@ -230,7 +225,6 @@ init_arch_timestamp(void) void *jvmti_open(void) { - int pad_cnt; char dump_path[PATH_MAX]; struct jitheader header; int fd; @@ -288,10 +282,6 @@ void *jvmti_open(void) header.total_size = sizeof(header); header.pid = getpid(); - /* calculate amount of padding '\0' */ - pad_cnt = PADDING_8ALIGNED(header.total_size); - header.total_size += pad_cnt; - header.timestamp = perf_get_timestamp(); if (use_arch_timestamp) @@ -301,13 +291,6 @@ void *jvmti_open(void) warn("jvmti: cannot write dumpfile header"); goto error; } - - /* write padding '\0' if necessary */ - if (pad_cnt && !fwrite(pad_bytes, pad_cnt, 1, fp)) { - warn("jvmti: cannot write dumpfile header padding"); - goto error; - } - return fp; error: fclose(fp); @@ -349,7 +332,6 @@ jvmti_write_code(void *agent, char const *sym, static int code_generation = 1; struct jr_code_load rec; size_t sym_len; - size_t padding_count; FILE *fp = agent; int ret = -1; @@ -366,8 +348,6 @@ jvmti_write_code(void *agent, char const *sym, rec.p.id = JIT_CODE_LOAD; rec.p.total_size = sizeof(rec) + sym_len; - padding_count = PADDING_8ALIGNED(rec.p.total_size); - rec.p. total_size += padding_count; rec.p.timestamp = perf_get_timestamp(); rec.code_size = size; @@ -393,9 +373,6 @@ jvmti_write_code(void *agent, char const *sym, ret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); fwrite_unlocked(sym, sym_len, 1, fp); - if (padding_count) - fwrite_unlocked(pad_bytes, padding_count, 1, fp); - if (code) fwrite_unlocked(code, size, 1, fp); @@ -412,7 +389,6 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, { struct jr_code_debug_info rec; size_t sret, len, size, flen; - size_t padding_count; uint64_t addr; const char *fn = file; FILE *fp = agent; @@ -443,16 +419,10 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, * int : line number * int : column discriminator * file[] : source file name - * padding : pad to multiple of 8 bytes */ size += nr_lines * sizeof(struct debug_entry); size += flen * nr_lines; - /* - * pad to 8 bytes - */ - padding_count = PADDING_8ALIGNED(size); - - rec.p.total_size = size + padding_count; + rec.p.total_size = size; /* * If JVM is multi-threaded, nultiple concurrent calls to agent @@ -486,12 +456,6 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, if (sret != 1) goto error; } - if (padding_count) { - sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp); - if (sret != 1) - goto error; - } - funlockfile(fp); return 0; error: diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c index ac12e4b91a926..5612641c69b40 100644 --- a/tools/perf/jvmti/libjvmti.c +++ b/tools/perf/jvmti/libjvmti.c @@ -12,6 +12,19 @@ static int has_line_numbers; void *jvmti_agent; +static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret) +{ + char *err_msg = NULL; + jvmtiError err; + err = (*jvmti)->GetErrorName(jvmti, ret, &err_msg); + if (err == JVMTI_ERROR_NONE) { + warnx("%s failed with %s", msg, err_msg); + (*jvmti)->Deallocate(jvmti, (unsigned char *)err_msg); + } else { + warnx("%s failed with an unknown error %d", msg, ret); + } +} + static jvmtiError do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, jvmti_line_info_t *tab, jint *nr) @@ -22,8 +35,10 @@ do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, jvmtiError ret; ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab); - if (ret != JVMTI_ERROR_NONE) + if (ret != JVMTI_ERROR_NONE) { + print_error(jvmti, "GetLineNumberTable", ret); return ret; + } for (i = 0; i < nr_lines; i++) { if (loc_tab[i].start_location < bci) { @@ -71,6 +86,8 @@ get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t ** /* free what was allocated for nothing */ (*jvmti)->Deallocate(jvmti, (unsigned char *)lne); nr_total += (int)nr; + } else { + print_error(jvmti, "GetLineNumberTable", ret); } } } @@ -130,7 +147,7 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &decl_class); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: cannot get declaring class"); + print_error(jvmti, "GetMethodDeclaringClass", ret); return; } @@ -144,21 +161,21 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: cannot get source filename ret=%d", ret); + print_error(jvmti, "GetSourceFileName", ret); goto error; } ret = (*jvmti)->GetClassSignature(jvmti, decl_class, &class_sign, NULL); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: getclassignature failed"); + print_error(jvmti, "GetClassSignature", ret); goto error; } ret = (*jvmti)->GetMethodName(jvmti, method, &func_name, &func_sign, NULL); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: failed getmethodname"); + print_error(jvmti, "GetMethodName", ret); goto error; } @@ -253,7 +270,7 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused) ret = (*jvmti)->AddCapabilities(jvmti, &caps1); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: acquire compiled_method capability failed"); + print_error(jvmti, "AddCapabilities", ret); return -1; } ret = (*jvmti)->GetJLocationFormat(jvmti, &format); @@ -264,7 +281,9 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused) ret = (*jvmti)->AddCapabilities(jvmti, &caps1); if (ret == JVMTI_ERROR_NONE) has_line_numbers = 1; - } + } else if (ret != JVMTI_ERROR_NONE) + print_error(jvmti, "GetJLocationFormat", ret); + memset(&cb, 0, sizeof(cb)); @@ -273,21 +292,21 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused) ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb)); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: cannot set event callbacks"); + print_error(jvmti, "SetEventCallbacks", ret); return -1; } ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: setnotification failed for method_load"); + print_error(jvmti, "SetEventNotificationMode(METHOD_LOAD)", ret); return -1; } ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); if (ret != JVMTI_ERROR_NONE) { - warnx("jvmti: setnotification failed on code_generated"); + print_error(jvmti, "SetEventNotificationMode(CODE_GENERATED)", ret); return -1; } return 0; diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/backward-ring-buffer.c index e6d1816e431a9..42e892b1e9798 100644 --- a/tools/perf/tests/backward-ring-buffer.c +++ b/tools/perf/tests/backward-ring-buffer.c @@ -97,7 +97,7 @@ int test__backward_ring_buffer(int subtest __maybe_unused) evlist = perf_evlist__new(); if (!evlist) { - pr_debug("No enough memory to create evlist\n"); + pr_debug("Not enough memory to create evlist\n"); return TEST_FAIL; } diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 2673e86ed50fa..8f0298aff222b 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -125,7 +125,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void), /* Instead of perf_evlist__new_default, don't add default events */ evlist = perf_evlist__new(); if (!evlist) { - pr_debug("No enough memory to create evlist\n"); + pr_debug("Not enough memory to create evlist\n"); return TEST_FAIL; } diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 31d6d5a7c2dc5..ddc4c3e59cc1f 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2807,7 +2807,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, do_zoom_dso(browser, actions); continue; case 'V': - browser->show_dso = !browser->show_dso; + verbose = (verbose + 1) % 4; + browser->show_dso = verbose > 0; + ui_helpline__fpush("Verbosity level set to %d\n", + verbose); continue; case 't': actions->thread = thread; diff --git a/tools/perf/util/Build b/tools/perf/util/Build index eb60e613d7954..1dc67efad6340 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -120,7 +120,7 @@ libperf-y += demangle-rust.o ifdef CONFIG_JITDUMP libperf-$(CONFIG_LIBELF) += jitdump.o libperf-$(CONFIG_LIBELF) += genelf.o -libperf-$(CONFIG_LIBELF) += genelf_debug.o +libperf-$(CONFIG_DWARF) += genelf_debug.o endif CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 2b2c9b82f5abc..a5fd275238f77 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -241,7 +241,7 @@ parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) int err = 0; if (!text) { - pr_debug("No enough memory: dup config_str failed\n"); + pr_debug("Not enough memory: dup config_str failed\n"); return ERR_PTR(-ENOMEM); } @@ -531,7 +531,7 @@ static int map_prologue(struct perf_probe_event *pev, int *mapping, ptevs = malloc(array_sz); if (!ptevs) { - pr_debug("No enough memory: alloc ptevs failed\n"); + pr_debug("Not enough memory: alloc ptevs failed\n"); return -ENOMEM; } @@ -604,13 +604,13 @@ static int hook_load_preprocessor(struct bpf_program *prog) priv->need_prologue = true; priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); if (!priv->insns_buf) { - pr_debug("No enough memory: alloc insns_buf failed\n"); + pr_debug("Not enough memory: alloc insns_buf failed\n"); return -ENOMEM; } priv->type_mapping = malloc(sizeof(int) * pev->ntevs); if (!priv->type_mapping) { - pr_debug("No enough memory: alloc type_mapping failed\n"); + pr_debug("Not enough memory: alloc type_mapping failed\n"); return -ENOMEM; } memset(priv->type_mapping, -1, @@ -864,7 +864,7 @@ bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term) op->k.array.ranges = memdup(term->array.ranges, memsz); if (!op->k.array.ranges) { - pr_debug("No enough memory to alloc indices for map\n"); + pr_debug("Not enough memory to alloc indices for map\n"); return -ENOMEM; } op->key_type = BPF_MAP_KEY_RANGES; @@ -929,7 +929,7 @@ bpf_map_priv__clone(struct bpf_map_priv *priv) newpriv = zalloc(sizeof(*newpriv)); if (!newpriv) { - pr_debug("No enough memory to alloc map private\n"); + pr_debug("Not enough memory to alloc map private\n"); return NULL; } INIT_LIST_HEAD(&newpriv->ops_list); @@ -960,7 +960,7 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) if (!priv) { priv = zalloc(sizeof(*priv)); if (!priv) { - pr_debug("No enough memory to alloc map private\n"); + pr_debug("Not enough memory to alloc map private\n"); return -ENOMEM; } INIT_LIST_HEAD(&priv->ops_list); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8d363d5e65a2e..c735c53a26f8f 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -177,6 +177,8 @@ enum { PERF_IP_FLAG_TRACE_BEGIN |\ PERF_IP_FLAG_TRACE_END) +#define MAX_INSN 16 + struct perf_sample { u64 ip; u32 pid, tid; @@ -193,6 +195,7 @@ struct perf_sample { u32 flags; u16 insn_len; u8 cpumode; + char insn[MAX_INSN]; void *raw_data; struct ip_callchain *callchain; struct branch_stack *branch_stack; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8bc271141d9df..e58a2fbf3b160 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -28,6 +28,7 @@ #include "debug.h" #include "trace-event.h" #include "stat.h" +#include "util/parse-branch-options.h" static struct { bool sample_id_all; @@ -708,6 +709,14 @@ static void apply_config_terms(struct perf_evsel *evsel, case PERF_EVSEL__CONFIG_TERM_CALLGRAPH: callgraph_buf = term->val.callgraph; break; + case PERF_EVSEL__CONFIG_TERM_BRANCH: + if (term->val.branch && strcmp(term->val.branch, "no")) { + perf_evsel__set_sample_bit(evsel, BRANCH_STACK); + parse_branch_str(term->val.branch, + &attr->branch_sample_type); + } else + perf_evsel__reset_sample_bit(evsel, BRANCH_STACK); + break; case PERF_EVSEL__CONFIG_TERM_STACK_USER: dump_size = term->val.stack_user; break; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b1503b0ecdff5..8cd7cd2274836 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -47,6 +47,7 @@ enum { PERF_EVSEL__CONFIG_TERM_MAX_STACK, PERF_EVSEL__CONFIG_TERM_OVERWRITE, PERF_EVSEL__CONFIG_TERM_DRV_CFG, + PERF_EVSEL__CONFIG_TERM_BRANCH, PERF_EVSEL__CONFIG_TERM_MAX, }; @@ -63,6 +64,7 @@ struct perf_evsel_config_term { int max_stack; bool inherit; bool overwrite; + char *branch; } val; }; diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index c1ef805c6a8f4..c540d47583e76 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -19,12 +19,18 @@ #include #include #include +#ifdef HAVE_DWARF_SUPPORT #include +#endif #include "perf.h" #include "genelf.h" #include "../util/jitdump.h" +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + #define JVMTI #define BUILD_ID_URANDOM /* different uuid for each run */ @@ -67,6 +73,8 @@ static char shd_string_table[] = { '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ + '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', '_', 'h', 'd', 'r', 0, /* 90 */ + '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, /* 104 */ }; static struct buildid_note { @@ -147,6 +155,86 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod } #endif +static int +jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, + uint64_t unwinding_size, uint64_t base_offset) +{ + Elf_Data *d; + Elf_Scn *scn; + Elf_Shdr *shdr; + uint64_t unwinding_table_size = unwinding_size - unwinding_header_size; + + /* + * setup eh_frame section + */ + scn = elf_newscn(e); + if (!scn) { + warnx("cannot create section"); + return -1; + } + + d = elf_newdata(scn); + if (!d) { + warnx("cannot get new data"); + return -1; + } + + d->d_align = 8; + d->d_off = 0LL; + d->d_buf = unwinding; + d->d_type = ELF_T_BYTE; + d->d_size = unwinding_table_size; + d->d_version = EV_CURRENT; + + shdr = elf_getshdr(scn); + if (!shdr) { + warnx("cannot get section header"); + return -1; + } + + shdr->sh_name = 104; + shdr->sh_type = SHT_PROGBITS; + shdr->sh_addr = base_offset; + shdr->sh_flags = SHF_ALLOC; + shdr->sh_entsize = 0; + + /* + * setup eh_frame_hdr section + */ + scn = elf_newscn(e); + if (!scn) { + warnx("cannot create section"); + return -1; + } + + d = elf_newdata(scn); + if (!d) { + warnx("cannot get new data"); + return -1; + } + + d->d_align = 4; + d->d_off = 0LL; + d->d_buf = unwinding + unwinding_table_size; + d->d_type = ELF_T_BYTE; + d->d_size = unwinding_header_size; + d->d_version = EV_CURRENT; + + shdr = elf_getshdr(scn); + if (!shdr) { + warnx("cannot get section header"); + return -1; + } + + shdr->sh_name = 90; + shdr->sh_type = SHT_PROGBITS; + shdr->sh_addr = base_offset + unwinding_table_size; + shdr->sh_flags = SHF_ALLOC; + shdr->sh_entsize = 0; + + return 0; +} + /* * fd: file descriptor open for writing for the output file * load_addr: code load address (could be zero, just used for buildid) @@ -157,13 +245,15 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod int jit_write_elf(int fd, uint64_t load_addr, const char *sym, const void *code, int csize, - void *debug, int nr_debug_entries) + void *debug __maybe_unused, int nr_debug_entries __maybe_unused, + void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size) { Elf *e; Elf_Data *d; Elf_Scn *scn; Elf_Ehdr *ehdr; Elf_Shdr *shdr; + uint64_t eh_frame_base_offset; char *strsym = NULL; int symlen; int retval = -1; @@ -194,7 +284,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, ehdr->e_type = ET_DYN; ehdr->e_entry = GEN_ELF_TEXT_OFFSET; ehdr->e_version = EV_CURRENT; - ehdr->e_shstrndx= 2; /* shdr index for section name */ + ehdr->e_shstrndx= unwinding ? 4 : 2; /* shdr index for section name */ /* * setup text section @@ -230,6 +320,18 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; shdr->sh_entsize = 0; + /* + * Setup .eh_frame_hdr and .eh_frame + */ + if (unwinding) { + eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize); + retval = jit_add_eh_frame_info(e, unwinding, + unwinding_header_size, unwinding_size, + eh_frame_base_offset); + if (retval) + goto error; + } + /* * setup section headers string table */ @@ -298,7 +400,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_type = SHT_SYMTAB; shdr->sh_flags = 0; shdr->sh_entsize = sizeof(Elf_Sym); - shdr->sh_link = 4; /* index of .strtab section */ + shdr->sh_link = unwinding ? 6 : 4; /* index of .strtab section */ /* * setup symbols string table @@ -386,11 +488,14 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_size = sizeof(bnote); shdr->sh_entsize = 0; +#ifdef HAVE_DWARF_SUPPORT if (debug && nr_debug_entries) { retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); if (retval) goto error; - } else { + } else +#endif + { if (elf_update(e, ELF_C_WRITE) < 0) { warnx("elf_update 4 failed"); goto error; diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index 2fbeb59c4bdd9..2424bd9862a30 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -3,9 +3,12 @@ /* genelf.c */ int jit_write_elf(int fd, uint64_t code_addr, const char *sym, - const void *code, int csize, void *debug, int nr_debug_entries); + const void *code, int csize, void *debug, int nr_debug_entries, + void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size); +#ifdef HAVE_DWARF_SUPPORT /* genelf_debug.c */ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); +#endif #if defined(__arm__) #define GEN_ELF_ARCH EM_ARM diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2f3eded54b0cc..d89c9c7ef4e54 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2250,11 +2250,28 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) struct header_print_data hd; struct perf_header *header = &session->header; int fd = perf_data_file__fd(session->file); + struct stat st; + int ret, bit; + hd.fp = fp; hd.full = full; + ret = fstat(fd, &st); + if (ret == -1) + return -1; + + fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); + perf_header__process_sections(header, fd, &hd, perf_file_section__fprintf_info); + + fprintf(fp, "# missing features: "); + for_each_clear_bit(bit, header->adds_features, HEADER_LAST_FEATURE) { + if (bit) + fprintf(fp, "%s ", feat_ops[bit].name); + } + + fprintf(fp, "\n"); return 0; } @@ -2273,7 +2290,7 @@ static int do_write_feat(int fd, struct perf_header *h, int type, err = feat_ops[type].write(fd, h, evlist); if (err < 0) { - pr_debug("failed to write feature %d\n", type); + pr_debug("failed to write feature %s\n", feat_ops[type].name); /* undo anything written */ lseek(fd, (*p)->offset, SEEK_SET); diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index f545ec1e758a7..6c2eb5da4afc0 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -295,6 +295,7 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, sample.cpu = btsq->cpu; sample.flags = btsq->sample_flags; sample.insn_len = btsq->intel_pt_insn.length; + memcpy(sample.insn, btsq->intel_pt_insn.buf, INTEL_PT_INSN_BUF_SZ); if (bts->synth_opts.inject) { event.sample.header.size = bts->branches_event_size; @@ -319,15 +320,12 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) struct machine *machine = btsq->bts->machine; struct thread *thread; struct addr_location al; - unsigned char buf[1024]; - size_t bufsz; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; ssize_t len; int x86_64; uint8_t cpumode; int err = -1; - bufsz = intel_pt_insn_max_size(); - if (machine__kernel_ip(machine, ip)) cpumode = PERF_RECORD_MISC_KERNEL; else @@ -341,7 +339,8 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) if (!al.map || !al.map->dso) goto out_put; - len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, bufsz); + len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, + INTEL_PT_INSN_BUF_SZ); if (len <= 0) goto out_put; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 16c06d3ae5772..e4e7dc781d21d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -980,6 +980,8 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder, out_no_progress: decoder->state.insn_op = intel_pt_insn->op; decoder->state.insn_len = intel_pt_insn->length; + memcpy(decoder->state.insn, intel_pt_insn->buf, + INTEL_PT_INSN_BUF_SZ); if (decoder->tx_flags & INTEL_PT_IN_TX) decoder->state.flags |= INTEL_PT_IN_TX; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h index 89399985fa4d4..e90619a43c0ce 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h @@ -66,6 +66,7 @@ struct intel_pt_state { uint32_t flags; enum intel_pt_insn_op insn_op; int insn_len; + char insn[INTEL_PT_INSN_BUF_SZ]; }; struct intel_pt_insn; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index d23138c06665a..7913363bde5c0 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -27,6 +27,10 @@ #include "intel-pt-insn-decoder.h" +#if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN +#error Instruction buffer size too small +#endif + /* Based on branch_type() from perf_event_intel_lbr.c */ static void intel_pt_insn_decoder(struct insn *insn, struct intel_pt_insn *intel_pt_insn) @@ -166,10 +170,10 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, if (!insn_complete(&insn) || insn.length > len) return -1; intel_pt_insn_decoder(&insn, intel_pt_insn); - if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ) + if (insn.length < INTEL_PT_INSN_BUF_SZ) memcpy(intel_pt_insn->buf, buf, insn.length); else - memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ); + memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ); return 0; } @@ -211,11 +215,6 @@ int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, return 0; } -size_t intel_pt_insn_max_size(void) -{ - return MAX_INSN_SIZE; -} - int intel_pt_insn_type(enum intel_pt_insn_op op) { switch (op) { diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h index b0adbf37323e4..37ec5627ae9b0 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h @@ -20,7 +20,7 @@ #include #define INTEL_PT_INSN_DESC_MAX 32 -#define INTEL_PT_INSN_DBG_BUF_SZ 16 +#define INTEL_PT_INSN_BUF_SZ 16 enum intel_pt_insn_op { INTEL_PT_OP_OTHER, @@ -47,7 +47,7 @@ struct intel_pt_insn { enum intel_pt_insn_branch branch; int length; int32_t rel; - unsigned char buf[INTEL_PT_INSN_DBG_BUF_SZ]; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; }; int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, @@ -58,8 +58,6 @@ const char *intel_pt_insn_name(enum intel_pt_insn_op op); int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, size_t buf_len); -size_t intel_pt_insn_max_size(void); - int intel_pt_insn_type(enum intel_pt_insn_op op); #endif diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c index 319bef33a64b2..e02bc7b166a0e 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c @@ -119,8 +119,8 @@ void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip) if (intel_pt_log_open()) return; - if (len > INTEL_PT_INSN_DBG_BUF_SZ) - len = INTEL_PT_INSN_DBG_BUF_SZ; + if (len > INTEL_PT_INSN_BUF_SZ) + len = INTEL_PT_INSN_BUF_SZ; intel_pt_print_data(intel_pt_insn->buf, len, ip, 8); if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) fprintf(f, "%s\n", desc); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dc041d4368c81..85d5eeb66c753 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -143,6 +143,7 @@ struct intel_pt_queue { u32 flags; u16 insn_len; u64 last_insn_cnt; + char insn[INTEL_PT_INSN_BUF_SZ]; }; static void intel_pt_dump(struct intel_pt *pt __maybe_unused, @@ -315,6 +316,7 @@ struct intel_pt_cache_entry { enum intel_pt_insn_branch branch; int length; int32_t rel; + char insn[INTEL_PT_INSN_BUF_SZ]; }; static int intel_pt_config_div(const char *var, const char *value, void *data) @@ -400,6 +402,7 @@ static int intel_pt_cache_add(struct dso *dso, struct machine *machine, e->branch = intel_pt_insn->branch; e->length = intel_pt_insn->length; e->rel = intel_pt_insn->rel; + memcpy(e->insn, intel_pt_insn->buf, INTEL_PT_INSN_BUF_SZ); err = auxtrace_cache__add(c, offset, &e->entry); if (err) @@ -428,8 +431,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, struct machine *machine = ptq->pt->machine; struct thread *thread; struct addr_location al; - unsigned char buf[1024]; - size_t bufsz; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; ssize_t len; int x86_64; u8 cpumode; @@ -437,11 +439,11 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, u64 insn_cnt = 0; bool one_map = true; + intel_pt_insn->length = 0; + if (to_ip && *ip == to_ip) goto out_no_cache; - bufsz = intel_pt_insn_max_size(); - if (*ip >= ptq->pt->kernel_start) cpumode = PERF_RECORD_MISC_KERNEL; else @@ -478,6 +480,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, intel_pt_insn->branch = e->branch; intel_pt_insn->length = e->length; intel_pt_insn->rel = e->rel; + memcpy(intel_pt_insn->buf, e->insn, + INTEL_PT_INSN_BUF_SZ); intel_pt_log_insn_no_data(intel_pt_insn, *ip); return 0; } @@ -493,7 +497,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, while (1) { len = dso__data_read_offset(al.map->dso, machine, - offset, buf, bufsz); + offset, buf, + INTEL_PT_INSN_BUF_SZ); if (len <= 0) return -EINVAL; @@ -900,6 +905,7 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq) if (ptq->state->flags & INTEL_PT_IN_TX) ptq->flags |= PERF_IP_FLAG_IN_TX; ptq->insn_len = ptq->state->insn_len; + memcpy(ptq->insn, ptq->state->insn, INTEL_PT_INSN_BUF_SZ); } } @@ -1080,6 +1086,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); /* * perf report cannot handle events without a branch stack when using @@ -1141,6 +1148,7 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); ptq->last_insn_cnt = ptq->state->tot_insn_cnt; @@ -1203,6 +1211,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); if (pt->synth_opts.callchain) { thread_stack__sample(ptq->thread, ptq->chain, diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 95f0884aae028..c9a941ef0f6dd 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -37,6 +37,10 @@ struct jit_buf_desc { bool needs_bswap; /* handles cross-endianess */ bool use_arch_timestamp; void *debug_data; + void *unwinding_data; + uint64_t unwinding_size; + uint64_t unwinding_mapped_size; + uint64_t eh_frame_hdr_size; size_t nr_debug_entries; uint32_t code_load_count; u64 bytes_written; @@ -68,7 +72,10 @@ jit_emit_elf(char *filename, const void *code, int csize, void *debug, - int nr_debug_entries) + int nr_debug_entries, + void *unwinding, + uint32_t unwinding_header_size, + uint32_t unwinding_size) { int ret, fd; @@ -81,7 +88,8 @@ jit_emit_elf(char *filename, return -1; } - ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); + ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries, + unwinding, unwinding_header_size, unwinding_size); close(fd); @@ -172,6 +180,12 @@ jit_open(struct jit_buf_desc *jd, const char *name) header.elf_mach, jd->use_arch_timestamp); + if (header.version > JITHEADER_VERSION) { + pr_err("wrong jitdump version %u, expected " STR(JITHEADER_VERSION), + header.version); + goto error; + } + if (header.flags & JITDUMP_FLAGS_RESERVED) { pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); @@ -263,8 +277,7 @@ jit_get_next_entry(struct jit_buf_desc *jd) return NULL; if (id >= JIT_CODE_MAX) { - pr_warning("next_entry: unknown prefix %d, skipping\n", id); - return NULL; + pr_warning("next_entry: unknown record type %d, skipping\n", id); } if (bs > jd->bufsize) { void *n; @@ -296,6 +309,13 @@ jit_get_next_entry(struct jit_buf_desc *jd) } } break; + case JIT_CODE_UNWINDING_INFO: + if (jd->needs_bswap) { + jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size); + jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size); + jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size); + } + break; case JIT_CODE_CLOSE: break; case JIT_CODE_LOAD: @@ -322,7 +342,8 @@ jit_get_next_entry(struct jit_buf_desc *jd) break; case JIT_CODE_MAX: default: - return NULL; + /* skip unknown record (we have read them) */ + break; } return jr; } @@ -370,7 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) u16 idr_size; const char *sym; uint32_t count; - int ret, csize; + int ret, csize, usize; pid_t pid, tid; struct { u32 pid, tid; @@ -380,6 +401,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) pid = jr->load.pid; tid = jr->load.tid; csize = jr->load.code_size; + usize = jd->unwinding_mapped_size; addr = jr->load.code_addr; sym = (void *)((unsigned long)jr + sizeof(jr->load)); code = (unsigned long)jr + jr->load.p.total_size - csize; @@ -400,7 +422,8 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) size = PERF_ALIGN(size, sizeof(u64)); uaddr = (uintptr_t)code; - ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); + ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries, + jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size); if (jd->debug_data && jd->nr_debug_entries) { free(jd->debug_data); @@ -408,6 +431,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) jd->nr_debug_entries = 0; } + if (jd->unwinding_data && jd->eh_frame_hdr_size) { + free(jd->unwinding_data); + jd->unwinding_data = NULL; + jd->eh_frame_hdr_size = 0; + jd->unwinding_mapped_size = 0; + jd->unwinding_size = 0; + } + if (ret) { free(event); return -1; @@ -422,7 +453,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.start = addr; - event->mmap2.len = csize; + event->mmap2.len = usize ? ALIGN_8(csize) + usize : csize; event->mmap2.pid = pid; event->mmap2.tid = tid; event->mmap2.ino = st.st_ino; @@ -473,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) char *filename; size_t size; struct stat st; + int usize; u16 idr_size; int ret; pid_t pid, tid; @@ -483,6 +515,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) pid = jr->move.pid; tid = jr->move.tid; + usize = jd->unwinding_mapped_size; idr_size = jd->machine->id_hdr_size; /* @@ -511,7 +544,8 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) (sizeof(event->mmap2.filename) - size) + idr_size); event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.start = jr->move.new_code_addr; - event->mmap2.len = jr->move.code_size; + event->mmap2.len = usize ? ALIGN_8(jr->move.code_size) + usize + : jr->move.code_size; event->mmap2.pid = pid; event->mmap2.tid = tid; event->mmap2.ino = st.st_ino; @@ -577,11 +611,36 @@ static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) return 0; } +static int +jit_repipe_unwinding_info(struct jit_buf_desc *jd, union jr_entry *jr) +{ + void *unwinding_data; + uint32_t unwinding_data_size; + + if (!(jd && jr)) + return -1; + + unwinding_data_size = jr->prefix.total_size - sizeof(jr->unwinding); + unwinding_data = malloc(unwinding_data_size); + if (!unwinding_data) + return -1; + + memcpy(unwinding_data, &jr->unwinding.unwinding_data, + unwinding_data_size); + + jd->eh_frame_hdr_size = jr->unwinding.eh_frame_hdr_size; + jd->unwinding_size = jr->unwinding.unwinding_size; + jd->unwinding_mapped_size = jr->unwinding.mapped_size; + jd->unwinding_data = unwinding_data; + + return 0; +} + static int jit_process_dump(struct jit_buf_desc *jd) { union jr_entry *jr; - int ret; + int ret = 0; while ((jr = jit_get_next_entry(jd))) { switch(jr->prefix.id) { @@ -594,6 +653,9 @@ jit_process_dump(struct jit_buf_desc *jd) case JIT_CODE_DEBUG_INFO: ret = jit_repipe_debug_info(jd, jr); break; + case JIT_CODE_UNWINDING_INFO: + ret = jit_repipe_unwinding_info(jd, jr); + break; default: ret = 0; continue; diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index bcacd20d0c1c7..c6b9b67f43bfb 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h @@ -19,6 +19,7 @@ #define JITHEADER_MAGIC_SW 0x4454694A #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) +#define ALIGN_8(x) (((x) + 7) & (~7)) #define JITHEADER_VERSION 1 @@ -48,6 +49,7 @@ enum jit_record_type { JIT_CODE_MOVE = 1, JIT_CODE_DEBUG_INFO = 2, JIT_CODE_CLOSE = 3, + JIT_CODE_UNWINDING_INFO = 4, JIT_CODE_MAX, }; @@ -101,12 +103,22 @@ struct jr_code_debug_info { struct debug_entry entries[0]; }; +struct jr_code_unwinding_info { + struct jr_prefix p; + + uint64_t unwinding_size; + uint64_t eh_frame_hdr_size; + uint64_t mapped_size; + const char unwinding_data[0]; +}; + union jr_entry { struct jr_code_debug_info info; struct jr_code_close close; struct jr_code_load load; struct jr_code_move move; struct jr_prefix prefix; + struct jr_code_unwinding_info unwinding; }; static inline struct debug_entry * diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index bf7216b8731dd..27b6f303720a4 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -339,7 +339,7 @@ dump_obj(const char *path, void *obj_buf, size_t size) char *p; if (!obj_path) { - pr_warning("WARNING: No enough memory, skip object dumping\n"); + pr_warning("WARNING: Not enough memory, skip object dumping\n"); return; } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c662fef95d144..4f9a71c63026d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -682,9 +682,16 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp continue; if (verbose >= 2) { - fputs("overlapping maps:\n", fp); - map__fprintf(map, fp); - map__fprintf(pos, fp); + + if (use_browser) { + pr_warning("overlapping maps in %s " + "(disable tui for more info)\n", + map->dso->name); + } else { + fputs("overlapping maps:\n", fp); + map__fprintf(map, fp); + map__fprintf(pos, fp); + } } rb_erase_init(&pos->rb_node, root); @@ -702,7 +709,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp before->end = map->start; __map_groups__insert(pos->groups, before); - if (verbose >= 2) + if (verbose >= 2 && !use_browser) map__fprintf(before, fp); map__put(before); } @@ -717,7 +724,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp after->start = map->end; __map_groups__insert(pos->groups, after); - if (verbose >= 2) + if (verbose >= 2 && !use_browser) map__fprintf(after, fp); map__put(after); } diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index afc088dd7d209..3634d6974300c 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c @@ -31,59 +31,51 @@ static const struct branch_mode branch_modes[] = { BRANCH_END }; -int -parse_branch_stack(const struct option *opt, const char *str, int unset) +int parse_branch_str(const char *str, __u64 *mode) { #define ONLY_PLM \ (PERF_SAMPLE_BRANCH_USER |\ PERF_SAMPLE_BRANCH_KERNEL |\ PERF_SAMPLE_BRANCH_HV) - uint64_t *mode = (uint64_t *)opt->value; + int ret = 0; + char *p, *s; + char *os = NULL; const struct branch_mode *br; - char *s, *os = NULL, *p; - int ret = -1; - if (unset) + if (str == NULL) { + *mode = PERF_SAMPLE_BRANCH_ANY; return 0; + } - /* - * cannot set it twice, -b + --branch-filter for instance - */ - if (*mode) + /* because str is read-only */ + s = os = strdup(str); + if (!s) return -1; - /* str may be NULL in case no arg is passed to -b */ - if (str) { - /* because str is read-only */ - s = os = strdup(str); - if (!s) - return -1; - - for (;;) { - p = strchr(s, ','); - if (p) - *p = '\0'; - - for (br = branch_modes; br->name; br++) { - if (!strcasecmp(s, br->name)) - break; - } - if (!br->name) { - ui__warning("unknown branch filter %s," - " check man page\n", s); - goto error; - } - - *mode |= br->mode; - - if (!p) - break; + for (;;) { + p = strchr(s, ','); + if (p) + *p = '\0'; - s = p + 1; + for (br = branch_modes; br->name; br++) { + if (!strcasecmp(s, br->name)) + break; + } + if (!br->name) { + ret = -1; + ui__warning("unknown branch filter %s," + " check man page\n", s); + goto error; } + + *mode |= br->mode; + + if (!p) + break; + + s = p + 1; } - ret = 0; /* default to any branch */ if ((*mode & ~ONLY_PLM) == 0) { @@ -93,3 +85,20 @@ parse_branch_stack(const struct option *opt, const char *str, int unset) free(os); return ret; } + +int +parse_branch_stack(const struct option *opt, const char *str, int unset) +{ + __u64 *mode = (__u64 *)opt->value; + + if (unset) + return 0; + + /* + * cannot set it twice, -b + --branch-filter for instance + */ + if (*mode) + return -1; + + return parse_branch_str(str, mode); +} diff --git a/tools/perf/util/parse-branch-options.h b/tools/perf/util/parse-branch-options.h index b9d9470c2e82d..6086fd90eb23a 100644 --- a/tools/perf/util/parse-branch-options.h +++ b/tools/perf/util/parse-branch-options.h @@ -1,5 +1,6 @@ #ifndef _PERF_PARSE_BRANCH_OPTIONS_H #define _PERF_PARSE_BRANCH_OPTIONS_H 1 -struct option; +#include int parse_branch_stack(const struct option *opt, const char *str, int unset); +int parse_branch_str(const char *str, __u64 *mode); #endif /* _PERF_PARSE_BRANCH_OPTIONS_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4e778eae15104..3c876b8ba4de6 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -22,6 +22,7 @@ #include "cpumap.h" #include "probe-file.h" #include "asm/bug.h" +#include "util/parse-branch-options.h" #define MAX_NAME_LEN 100 @@ -973,10 +974,13 @@ do { \ CHECK_TYPE_VAL(NUM); break; case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: - /* - * TODO uncomment when the field is available - * attr->branch_sample_type = term->val.num; - */ + CHECK_TYPE_VAL(STR); + if (strcmp(term->val.str, "no") && + parse_branch_str(term->val.str, &attr->branch_sample_type)) { + err->str = strdup("invalid branch sample type"); + err->idx = term->err_val; + return -EINVAL; + } break; case PARSE_EVENTS__TERM_TYPE_TIME: CHECK_TYPE_VAL(NUM); @@ -1119,6 +1123,9 @@ do { \ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str); break; + case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + ADD_CONFIG_TERM(BRANCH, branch, term->val.str); + break; case PARSE_EVENTS__TERM_TYPE_STACKSIZE: ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num); break; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b1474dcadfa2b..31b845ec32e24 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -504,6 +504,7 @@ static void pmu_add_cpu_aliases(struct list_head *head) struct pmu_events_map *map; struct pmu_event *pe; char *cpuid; + static bool printed; cpuid = getenv("PERF_CPUID"); if (cpuid) @@ -513,7 +514,10 @@ static void pmu_add_cpu_aliases(struct list_head *head) if (!cpuid) return; - pr_debug("Using CPUID %s\n", cpuid); + if (!printed) { + pr_debug("Using CPUID %s\n", cpuid); + printed = true; + } i = 0; while (1) { @@ -1135,8 +1139,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, bool is_cpu = !strcmp(pmu->name, "cpu"); if (event_glob != NULL && - !(strglobmatch(name, event_glob) || - (!is_cpu && strglobmatch(alias->name, + !(strglobmatch_nocase(name, event_glob) || + (!is_cpu && strglobmatch_nocase(alias->name, event_glob)))) continue; diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index 639d1da2f9786..293534c1a4743 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c @@ -54,7 +54,7 @@ int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) break; ret = sq_quote_buf(dst, argv[i]); if (maxlen && dst->len > maxlen) - die("Too many or long arguments"); + return -ENOSPC; } return ret; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d61242a6e648..f268201048a0b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2025,20 +2025,10 @@ int perf_session__cpu_bitmap(struct perf_session *session, void perf_session__fprintf_info(struct perf_session *session, FILE *fp, bool full) { - struct stat st; - int fd, ret; - if (session == NULL || fp == NULL) return; - fd = perf_data_file__fd(session->file); - - ret = fstat(fd, &st); - if (ret == -1) - return; - fprintf(fp, "# ========\n"); - fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); perf_header__fprintf_info(session, fp, full); fprintf(fp, "# ========\n#\n"); } diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 7f7e072be746f..d8dfaf64b32e1 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -193,7 +193,8 @@ static bool __match_charclass(const char *pat, char c, const char **npat) } /* Glob/lazy pattern matching */ -static bool __match_glob(const char *str, const char *pat, bool ignore_space) +static bool __match_glob(const char *str, const char *pat, bool ignore_space, + bool case_ins) { while (*str && *pat && *pat != '*') { if (ignore_space) { @@ -219,8 +220,13 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) return false; else if (*pat == '\\') /* Escaped char match as normal char */ pat++; - if (*str++ != *pat++) + if (case_ins) { + if (tolower(*str) != tolower(*pat)) + return false; + } else if (*str != *pat) return false; + str++; + pat++; } /* Check wild card */ if (*pat == '*') { @@ -229,7 +235,7 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) if (!*pat) /* Tail wild card matches all */ return true; while (*str) - if (__match_glob(str++, pat, ignore_space)) + if (__match_glob(str++, pat, ignore_space, case_ins)) return true; } return !*str && !*pat; @@ -249,7 +255,12 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) */ bool strglobmatch(const char *str, const char *pat) { - return __match_glob(str, pat, false); + return __match_glob(str, pat, false, false); +} + +bool strglobmatch_nocase(const char *str, const char *pat) +{ + return __match_glob(str, pat, false, true); } /** @@ -262,7 +273,7 @@ bool strglobmatch(const char *str, const char *pat) */ bool strlazymatch(const char *str, const char *pat) { - return __match_glob(str, pat, true); + return __match_glob(str, pat, true, false); } /** diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 20c2e57439038..6fec84dff3f77 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -357,8 +357,8 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, di.format = UNW_INFO_FORMAT_REMOTE_TABLE; di.start_ip = map->start; di.end_ip = map->end; - di.u.rti.segbase = map->start + segbase; - di.u.rti.table_data = map->start + table_data; + di.u.rti.segbase = map->start + segbase - map->pgoff; + di.u.rti.table_data = map->start + table_data - map->pgoff; di.u.rti.table_len = fde_count * sizeof(struct table_entry) / sizeof(unw_word_t); ret = dwarf_search_unwind_table(as, ip, &di, pi, diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 43899e0d6fa14..71b6992f1d985 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -222,6 +222,7 @@ s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); +bool strglobmatch_nocase(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); static inline bool strisglob(const char *str) { diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 0fb3c1fcd3e62..5074be4ed467b 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -2,15 +2,18 @@ #include "util.h" #include "values.h" +#include "debug.h" -void perf_read_values_init(struct perf_read_values *values) +int perf_read_values_init(struct perf_read_values *values) { values->threads_max = 16; values->pid = malloc(values->threads_max * sizeof(*values->pid)); values->tid = malloc(values->threads_max * sizeof(*values->tid)); values->value = malloc(values->threads_max * sizeof(*values->value)); - if (!values->pid || !values->tid || !values->value) - die("failed to allocate read_values threads arrays"); + if (!values->pid || !values->tid || !values->value) { + pr_debug("failed to allocate read_values threads arrays"); + goto out_free_pid; + } values->threads = 0; values->counters_max = 16; @@ -18,9 +21,22 @@ void perf_read_values_init(struct perf_read_values *values) * sizeof(*values->counterrawid)); values->countername = malloc(values->counters_max * sizeof(*values->countername)); - if (!values->counterrawid || !values->countername) - die("failed to allocate read_values counters arrays"); + if (!values->counterrawid || !values->countername) { + pr_debug("failed to allocate read_values counters arrays"); + goto out_free_counter; + } values->counters = 0; + + return 0; + +out_free_counter: + zfree(&values->counterrawid); + zfree(&values->countername); +out_free_pid: + zfree(&values->pid); + zfree(&values->tid); + zfree(&values->value); + return -ENOMEM; } void perf_read_values_destroy(struct perf_read_values *values) @@ -41,17 +57,27 @@ void perf_read_values_destroy(struct perf_read_values *values) zfree(&values->countername); } -static void perf_read_values__enlarge_threads(struct perf_read_values *values) +static int perf_read_values__enlarge_threads(struct perf_read_values *values) { - values->threads_max *= 2; - values->pid = realloc(values->pid, - values->threads_max * sizeof(*values->pid)); - values->tid = realloc(values->tid, - values->threads_max * sizeof(*values->tid)); - values->value = realloc(values->value, - values->threads_max * sizeof(*values->value)); - if (!values->pid || !values->tid || !values->value) - die("failed to enlarge read_values threads arrays"); + int nthreads_max = values->threads_max * 2; + void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)), + *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)), + *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value)); + + if (!npid || !ntid || !nvalue) + goto out_err; + + values->threads_max = nthreads_max; + values->pid = npid; + values->tid = ntid; + values->value = nvalue; + return 0; +out_err: + free(npid); + free(ntid); + free(nvalue); + pr_debug("failed to enlarge read_values threads arrays"); + return -ENOMEM; } static int perf_read_values__findnew_thread(struct perf_read_values *values, @@ -63,15 +89,21 @@ static int perf_read_values__findnew_thread(struct perf_read_values *values, if (values->pid[i] == pid && values->tid[i] == tid) return i; - if (values->threads == values->threads_max) - perf_read_values__enlarge_threads(values); + if (values->threads == values->threads_max) { + i = perf_read_values__enlarge_threads(values); + if (i < 0) + return i; + } - i = values->threads++; + i = values->threads + 1; + values->value[i] = malloc(values->counters_max * sizeof(**values->value)); + if (!values->value[i]) { + pr_debug("failed to allocate read_values counters array"); + return -ENOMEM; + } values->pid[i] = pid; values->tid[i] = tid; - values->value[i] = malloc(values->counters_max * sizeof(**values->value)); - if (!values->value[i]) - die("failed to allocate read_values counters array"); + values->threads = i; return i; } @@ -115,16 +147,21 @@ static int perf_read_values__findnew_counter(struct perf_read_values *values, return i; } -void perf_read_values_add_value(struct perf_read_values *values, +int perf_read_values_add_value(struct perf_read_values *values, u32 pid, u32 tid, u64 rawid, const char *name, u64 value) { int tindex, cindex; tindex = perf_read_values__findnew_thread(values, pid, tid); + if (tindex < 0) + return tindex; cindex = perf_read_values__findnew_counter(values, rawid, name); + if (cindex < 0) + return cindex; values->value[tindex][cindex] = value; + return 0; } static void perf_read_values__display_pretty(FILE *fp, diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index b21a80c6cf8de..808ff9c73bf5d 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h @@ -14,10 +14,10 @@ struct perf_read_values { u64 **value; }; -void perf_read_values_init(struct perf_read_values *values); +int perf_read_values_init(struct perf_read_values *values); void perf_read_values_destroy(struct perf_read_values *values); -void perf_read_values_add_value(struct perf_read_values *values, +int perf_read_values_add_value(struct perf_read_values *values, u32 pid, u32 tid, u64 rawid, const char *name, u64 value);