diff options
author | Jani Nikula <jani.nikula@intel.com> | 2018-11-20 06:14:08 -0500 |
---|---|---|
committer | Jani Nikula <jani.nikula@intel.com> | 2018-11-20 06:14:08 -0500 |
commit | 2ac5e38ea4203852d6e99edd3cf11f044b0a409f (patch) | |
tree | 1ef02da98d56309368ad2b6a4e492bafe5bb4faf /tools/perf | |
parent | f48cc647f3e196a3179d695d3c2d56c13e9dec98 (diff) | |
parent | 9235dd441af43599b9cdcce599a3da4083fcad3c (diff) |
Merge drm/drm-next into drm-intel-next-queued
Pull in v4.20-rc3 via drm-next.
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'tools/perf')
163 files changed, 7641 insertions, 2645 deletions
diff --git a/tools/perf/Documentation/build-xed.txt b/tools/perf/Documentation/build-xed.txt new file mode 100644 index 000000000000..6222c1e7231f --- /dev/null +++ b/tools/perf/Documentation/build-xed.txt | |||
@@ -0,0 +1,19 @@ | |||
1 | |||
2 | For --xed the xed tool is needed. Here is how to install it: | ||
3 | |||
4 | $ git clone https://github.com/intelxed/mbuild.git mbuild | ||
5 | $ git clone https://github.com/intelxed/xed | ||
6 | $ cd xed | ||
7 | $ ./mfile.py --share | ||
8 | $ ./mfile.py examples | ||
9 | $ sudo ./mfile.py --prefix=/usr/local install | ||
10 | $ sudo ldconfig | ||
11 | $ sudo cp obj/examples/xed /usr/local/bin | ||
12 | |||
13 | Basic xed testing: | ||
14 | |||
15 | $ xed | head -3 | ||
16 | ERROR: required argument(s) were missing | ||
17 | Copyright (C) 2017, Intel Corporation. All rights reserved. | ||
18 | XED version: [v10.0-328-g7d62c8c49b7b] | ||
19 | $ | ||
diff --git a/tools/perf/Documentation/intel-pt.txt b/tools/perf/Documentation/intel-pt.txt index 76971d2e4164..115eaacc455f 100644 --- a/tools/perf/Documentation/intel-pt.txt +++ b/tools/perf/Documentation/intel-pt.txt | |||
@@ -106,7 +106,7 @@ in transaction, respectively. | |||
106 | While it is possible to create scripts to analyze the data, an alternative | 106 | While it is possible to create scripts to analyze the data, an alternative |
107 | approach is available to export the data to a sqlite or postgresql database. | 107 | approach is available to export the data to a sqlite or postgresql database. |
108 | Refer to script export-to-sqlite.py or export-to-postgresql.py for more details, | 108 | Refer to script export-to-sqlite.py or export-to-postgresql.py for more details, |
109 | and to script call-graph-from-sql.py for an example of using the database. | 109 | and to script exported-sql-viewer.py for an example of using the database. |
110 | 110 | ||
111 | There is also script intel-pt-events.py which provides an example of how to | 111 | There is also script intel-pt-events.py which provides an example of how to |
112 | unpack the raw data for power events and PTWRITE. | 112 | unpack the raw data for power events and PTWRITE. |
diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt index a3abe04c779d..c2182cbabde3 100644 --- a/tools/perf/Documentation/itrace.txt +++ b/tools/perf/Documentation/itrace.txt | |||
@@ -11,10 +11,11 @@ | |||
11 | l synthesize last branch entries (use with i or x) | 11 | l synthesize last branch entries (use with i or x) |
12 | s skip initial number of events | 12 | s skip initial number of events |
13 | 13 | ||
14 | The default is all events i.e. the same as --itrace=ibxwpe | 14 | The default is all events i.e. the same as --itrace=ibxwpe, |
15 | except for perf script where it is --itrace=ce | ||
15 | 16 | ||
16 | In addition, the period (default 100000) for instructions events | 17 | In addition, the period (default 100000, except for perf script where it is 1) |
17 | can be specified in units of: | 18 | for instructions events can be specified in units of: |
18 | 19 | ||
19 | i instructions | 20 | i instructions |
20 | t ticks | 21 | t ticks |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 236b9b97dfdb..667c14e56031 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -55,7 +55,6 @@ counted. The following modifiers exist: | |||
55 | S - read sample value (PERF_SAMPLE_READ) | 55 | S - read sample value (PERF_SAMPLE_READ) |
56 | D - pin the event to the PMU | 56 | D - pin the event to the PMU |
57 | W - group is weak and will fallback to non-group if not schedulable, | 57 | W - group is weak and will fallback to non-group if not schedulable, |
58 | only supported in 'perf stat' for now. | ||
59 | 58 | ||
60 | The 'p' modifier can be used for specifying how precise the instruction | 59 | The 'p' modifier can be used for specifying how precise the instruction |
61 | address should be. The 'p' modifier can be specified multiple times: | 60 | address should be. The 'p' modifier can be specified multiple times: |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index afdafe2110a1..a2b37ce48094 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -383,6 +383,24 @@ include::itrace.txt[] | |||
383 | will be printed. Each entry has function name and file/line. Enabled by | 383 | will be printed. Each entry has function name and file/line. Enabled by |
384 | default, disable with --no-inline. | 384 | default, disable with --no-inline. |
385 | 385 | ||
386 | --insn-trace:: | ||
387 | Show instruction stream for intel_pt traces. Combine with --xed to | ||
388 | show disassembly. | ||
389 | |||
390 | --xed:: | ||
391 | Run xed disassembler on output. Requires installing the xed disassembler. | ||
392 | |||
393 | --call-trace:: | ||
394 | Show call stream for intel_pt traces. The CPUs are interleaved, but | ||
395 | can be filtered with -C. | ||
396 | |||
397 | --call-ret-trace:: | ||
398 | Show call and return stream for intel_pt traces. | ||
399 | |||
400 | --graph-function:: | ||
401 | For itrace only show specified functions and their callees for | ||
402 | itrace. Multiple functions can be separated by comma. | ||
403 | |||
386 | SEE ALSO | 404 | SEE ALSO |
387 | -------- | 405 | -------- |
388 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 406 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 114fda12aa49..808b664343c9 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -242,6 +242,16 @@ Default is to monitor all CPUS. | |||
242 | --hierarchy:: | 242 | --hierarchy:: |
243 | Enable hierarchy output. | 243 | Enable hierarchy output. |
244 | 244 | ||
245 | --overwrite:: | ||
246 | Enable this to use just the most recent records, which helps in high core count | ||
247 | machines such as Knights Landing/Mill, but right now is disabled by default as | ||
248 | the pausing used in this technique is leading to loss of metadata events such | ||
249 | as PERF_RECORD_MMAP which makes 'perf top' unable to resolve samples, leading | ||
250 | to lots of unknown samples appearing on the UI. Enable this if you are in such | ||
251 | machines and profiling a workload that doesn't creates short lived threads and/or | ||
252 | doesn't uses many executable mmap operations. Work is being planed to solve | ||
253 | this situation, till then, this will remain disabled by default. | ||
254 | |||
245 | --force:: | 255 | --force:: |
246 | Don't do ownership validation. | 256 | Don't do ownership validation. |
247 | 257 | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 115db9e06ecd..e113450503d2 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -171,6 +171,11 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
171 | --kernel-syscall-graph:: | 171 | --kernel-syscall-graph:: |
172 | Show the kernel callchains on the syscall exit path. | 172 | Show the kernel callchains on the syscall exit path. |
173 | 173 | ||
174 | --max-events=N:: | ||
175 | Stop after processing N events. Note that strace-like events are considered | ||
176 | only at exit time or when a syscall is interrupted, i.e. in those cases this | ||
177 | option is equivalent to the number of lines printed. | ||
178 | |||
174 | --max-stack:: | 179 | --max-stack:: |
175 | Set the stack depth limit when parsing the callchain, anything | 180 | Set the stack depth limit when parsing the callchain, anything |
176 | beyond the specified depth will be ignored. Note that at this point | 181 | beyond the specified depth will be ignored. Note that at this point |
@@ -238,6 +243,68 @@ Trace syscalls, major and minor pagefaults: | |||
238 | As you can see, there was major pagefault in python process, from | 243 | As you can see, there was major pagefault in python process, from |
239 | CRYPTO_push_info_ routine which faulted somewhere in libcrypto.so. | 244 | CRYPTO_push_info_ routine which faulted somewhere in libcrypto.so. |
240 | 245 | ||
246 | Trace the first 4 open, openat or open_by_handle_at syscalls (in the future more syscalls may match here): | ||
247 | |||
248 | $ perf trace -e open* --max-events 4 | ||
249 | [root@jouet perf]# trace -e open* --max-events 4 | ||
250 | 2272.992 ( 0.037 ms): gnome-shell/1370 openat(dfd: CWD, filename: /proc/self/stat) = 31 | ||
251 | 2277.481 ( 0.139 ms): gnome-shell/3039 openat(dfd: CWD, filename: /proc/self/stat) = 65 | ||
252 | 3026.398 ( 0.076 ms): gnome-shell/3039 openat(dfd: CWD, filename: /proc/self/stat) = 65 | ||
253 | 4294.665 ( 0.015 ms): sed/15879 openat(dfd: CWD, filename: /etc/ld.so.cache, flags: CLOEXEC) = 3 | ||
254 | $ | ||
255 | |||
256 | Trace the first minor page fault when running a workload: | ||
257 | |||
258 | # perf trace -F min --max-stack=7 --max-events 1 sleep 1 | ||
259 | 0.000 ( 0.000 ms): sleep/18006 minfault [__clear_user+0x1a] => 0x5626efa56080 (?k) | ||
260 | __clear_user ([kernel.kallsyms]) | ||
261 | load_elf_binary ([kernel.kallsyms]) | ||
262 | search_binary_handler ([kernel.kallsyms]) | ||
263 | __do_execve_file.isra.33 ([kernel.kallsyms]) | ||
264 | __x64_sys_execve ([kernel.kallsyms]) | ||
265 | do_syscall_64 ([kernel.kallsyms]) | ||
266 | entry_SYSCALL_64 ([kernel.kallsyms]) | ||
267 | # | ||
268 | |||
269 | Trace the next min page page fault to take place on the first CPU: | ||
270 | |||
271 | # perf trace -F min --call-graph=dwarf --max-events 1 --cpu 0 | ||
272 | 0.000 ( 0.000 ms): Web Content/17136 minfault [js::gc::Chunk::fetchNextDecommittedArena+0x4b] => 0x7fbe6181b000 (?.) | ||
273 | js::gc::FreeSpan::initAsEmpty (inlined) | ||
274 | js::gc::Arena::setAsNotAllocated (inlined) | ||
275 | js::gc::Chunk::fetchNextDecommittedArena (/usr/lib64/firefox/libxul.so) | ||
276 | js::gc::Chunk::allocateArena (/usr/lib64/firefox/libxul.so) | ||
277 | js::gc::GCRuntime::allocateArena (/usr/lib64/firefox/libxul.so) | ||
278 | js::gc::ArenaLists::allocateFromArena (/usr/lib64/firefox/libxul.so) | ||
279 | js::gc::GCRuntime::tryNewTenuredThing<JSString, (js::AllowGC)1> (inlined) | ||
280 | js::AllocateString<JSString, (js::AllowGC)1> (/usr/lib64/firefox/libxul.so) | ||
281 | js::Allocate<JSThinInlineString, (js::AllowGC)1> (inlined) | ||
282 | JSThinInlineString::new_<(js::AllowGC)1> (inlined) | ||
283 | AllocateInlineString<(js::AllowGC)1, unsigned char> (inlined) | ||
284 | js::ConcatStrings<(js::AllowGC)1> (/usr/lib64/firefox/libxul.so) | ||
285 | [0x18b26e6bc2bd] (/tmp/perf-17136.map) | ||
286 | # | ||
287 | |||
288 | Trace the next two sched:sched_switch events, four block:*_plug events, the | ||
289 | next block:*_unplug and the next three net:*dev_queue events, this last one | ||
290 | with a backtrace of at most 16 entries, system wide: | ||
291 | |||
292 | # perf trace -e sched:*switch/nr=2/,block:*_plug/nr=4/,block:*_unplug/nr=1/,net:*dev_queue/nr=3,max-stack=16/ | ||
293 | 0.000 :0/0 sched:sched_switch:swapper/2:0 [120] S ==> rcu_sched:10 [120] | ||
294 | 0.015 rcu_sched/10 sched:sched_switch:rcu_sched:10 [120] R ==> swapper/2:0 [120] | ||
295 | 254.198 irq/50-iwlwifi/680 net:net_dev_queue:dev=wlp3s0 skbaddr=0xffff93498051f600 len=66 | ||
296 | __dev_queue_xmit ([kernel.kallsyms]) | ||
297 | 273.977 :0/0 net:net_dev_queue:dev=wlp3s0 skbaddr=0xffff93498051f600 len=78 | ||
298 | __dev_queue_xmit ([kernel.kallsyms]) | ||
299 | 274.007 :0/0 net:net_dev_queue:dev=wlp3s0 skbaddr=0xffff93498051ff00 len=78 | ||
300 | __dev_queue_xmit ([kernel.kallsyms]) | ||
301 | 2930.140 kworker/u16:58/2722 block:block_plug:[kworker/u16:58] | ||
302 | 2930.162 kworker/u16:58/2722 block:block_unplug:[kworker/u16:58] 1 | ||
303 | 4466.094 jbd2/dm-2-8/748 block:block_plug:[jbd2/dm-2-8] | ||
304 | 8050.123 kworker/u16:30/2694 block:block_plug:[kworker/u16:30] | ||
305 | 8050.271 kworker/u16:30/2694 block:block_plug:[kworker/u16:30] | ||
306 | # | ||
307 | |||
241 | SEE ALSO | 308 | SEE ALSO |
242 | -------- | 309 | -------- |
243 | linkperf:perf-record[1], linkperf:perf-script[1] | 310 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index f6d1a03c7523..e30d20fb482d 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config | |||
@@ -833,7 +833,7 @@ ifndef NO_JVMTI | |||
833 | JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | awk '{print $$3}') | 833 | JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | awk '{print $$3}') |
834 | else | 834 | else |
835 | ifneq (,$(wildcard /usr/sbin/alternatives)) | 835 | ifneq (,$(wildcard /usr/sbin/alternatives)) |
836 | JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') | 836 | JDIR=$(shell /usr/sbin/alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') |
837 | endif | 837 | endif |
838 | endif | 838 | endif |
839 | ifndef JDIR | 839 | ifndef JDIR |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5224ade3d5af..d95655489f7e 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -1,4 +1,5 @@ | |||
1 | include ../scripts/Makefile.include | 1 | include ../scripts/Makefile.include |
2 | include ../scripts/Makefile.arch | ||
2 | 3 | ||
3 | # The default target of this Makefile is... | 4 | # The default target of this Makefile is... |
4 | all: | 5 | all: |
@@ -385,6 +386,8 @@ export INSTALL SHELL_PATH | |||
385 | SHELL = $(SHELL_PATH) | 386 | SHELL = $(SHELL_PATH) |
386 | 387 | ||
387 | linux_uapi_dir := $(srctree)/tools/include/uapi/linux | 388 | linux_uapi_dir := $(srctree)/tools/include/uapi/linux |
389 | asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic | ||
390 | arch_asm_uapi_dir := $(srctree)/tools/arch/$(SRCARCH)/include/uapi/asm/ | ||
388 | 391 | ||
389 | beauty_outdir := $(OUTPUT)trace/beauty/generated | 392 | beauty_outdir := $(OUTPUT)trace/beauty/generated |
390 | beauty_ioctl_outdir := $(beauty_outdir)/ioctl | 393 | beauty_ioctl_outdir := $(beauty_outdir)/ioctl |
@@ -460,6 +463,18 @@ madvise_behavior_tbl := $(srctree)/tools/perf/trace/beauty/madvise_behavior.sh | |||
460 | $(madvise_behavior_array): $(madvise_hdr_dir)/mman-common.h $(madvise_behavior_tbl) | 463 | $(madvise_behavior_array): $(madvise_hdr_dir)/mman-common.h $(madvise_behavior_tbl) |
461 | $(Q)$(SHELL) '$(madvise_behavior_tbl)' $(madvise_hdr_dir) > $@ | 464 | $(Q)$(SHELL) '$(madvise_behavior_tbl)' $(madvise_hdr_dir) > $@ |
462 | 465 | ||
466 | mmap_flags_array := $(beauty_outdir)/mmap_flags_array.c | ||
467 | mmap_flags_tbl := $(srctree)/tools/perf/trace/beauty/mmap_flags.sh | ||
468 | |||
469 | $(mmap_flags_array): $(asm_generic_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman-common.h $(arch_asm_uapi_dir)/mman.h $(mmap_flags_tbl) | ||
470 | $(Q)$(SHELL) '$(mmap_flags_tbl)' $(asm_generic_uapi_dir) $(arch_asm_uapi_dir) > $@ | ||
471 | |||
472 | mount_flags_array := $(beauty_outdir)/mount_flags_array.c | ||
473 | mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/mount_flags.sh | ||
474 | |||
475 | $(mount_flags_array): $(linux_uapi_dir)/fs.h $(mount_flags_tbl) | ||
476 | $(Q)$(SHELL) '$(mount_flags_tbl)' $(linux_uapi_dir) > $@ | ||
477 | |||
463 | prctl_option_array := $(beauty_outdir)/prctl_option_array.c | 478 | prctl_option_array := $(beauty_outdir)/prctl_option_array.c |
464 | prctl_hdr_dir := $(srctree)/tools/include/uapi/linux/ | 479 | prctl_hdr_dir := $(srctree)/tools/include/uapi/linux/ |
465 | prctl_option_tbl := $(srctree)/tools/perf/trace/beauty/prctl_option.sh | 480 | prctl_option_tbl := $(srctree)/tools/perf/trace/beauty/prctl_option.sh |
@@ -577,6 +592,8 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc | |||
577 | $(socket_ipproto_array) \ | 592 | $(socket_ipproto_array) \ |
578 | $(vhost_virtio_ioctl_array) \ | 593 | $(vhost_virtio_ioctl_array) \ |
579 | $(madvise_behavior_array) \ | 594 | $(madvise_behavior_array) \ |
595 | $(mmap_flags_array) \ | ||
596 | $(mount_flags_array) \ | ||
580 | $(perf_ioctl_array) \ | 597 | $(perf_ioctl_array) \ |
581 | $(prctl_option_array) \ | 598 | $(prctl_option_array) \ |
582 | $(arch_errno_name_array) | 599 | $(arch_errno_name_array) |
@@ -635,7 +652,7 @@ $(LIBPERF_IN): prepare FORCE | |||
635 | $(LIB_FILE): $(LIBPERF_IN) | 652 | $(LIB_FILE): $(LIBPERF_IN) |
636 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN) $(LIB_OBJS) | 653 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN) $(LIB_OBJS) |
637 | 654 | ||
638 | LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ) | 655 | LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ) 'EXTRA_CFLAGS=$(EXTRA_CFLAGS)' 'LDFLAGS=$(LDFLAGS)' |
639 | 656 | ||
640 | $(LIBTRACEEVENT): FORCE | 657 | $(LIBTRACEEVENT): FORCE |
641 | $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a | 658 | $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a |
@@ -779,7 +796,9 @@ endif | |||
779 | ifndef NO_LIBBPF | 796 | ifndef NO_LIBBPF |
780 | $(call QUIET_INSTALL, bpf-headers) \ | 797 | $(call QUIET_INSTALL, bpf-headers) \ |
781 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \ | 798 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \ |
782 | $(INSTALL) include/bpf/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf' | 799 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf/linux'; \ |
800 | $(INSTALL) include/bpf/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \ | ||
801 | $(INSTALL) include/bpf/linux/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf/linux' | ||
783 | $(call QUIET_INSTALL, bpf-examples) \ | 802 | $(call QUIET_INSTALL, bpf-examples) \ |
784 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'; \ | 803 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'; \ |
785 | $(INSTALL) examples/bpf/*.c -t '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' | 804 | $(INSTALL) examples/bpf/*.c -t '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' |
@@ -861,6 +880,8 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea | |||
861 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ | 880 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ |
862 | $(OUTPUT)pmu-events/pmu-events.c \ | 881 | $(OUTPUT)pmu-events/pmu-events.c \ |
863 | $(OUTPUT)$(madvise_behavior_array) \ | 882 | $(OUTPUT)$(madvise_behavior_array) \ |
883 | $(OUTPUT)$(mmap_flags_array) \ | ||
884 | $(OUTPUT)$(mount_flags_array) \ | ||
864 | $(OUTPUT)$(drm_ioctl_array) \ | 885 | $(OUTPUT)$(drm_ioctl_array) \ |
865 | $(OUTPUT)$(pkey_alloc_access_rights_array) \ | 886 | $(OUTPUT)$(pkey_alloc_access_rights_array) \ |
866 | $(OUTPUT)$(sndrv_ctl_ioctl_array) \ | 887 | $(OUTPUT)$(sndrv_ctl_ioctl_array) \ |
diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index 6688977e4ac7..76c6345a57d5 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c | |||
@@ -8,6 +8,63 @@ struct arm64_annotate { | |||
8 | jump_insn; | 8 | jump_insn; |
9 | }; | 9 | }; |
10 | 10 | ||
11 | static int arm64_mov__parse(struct arch *arch __maybe_unused, | ||
12 | struct ins_operands *ops, | ||
13 | struct map_symbol *ms __maybe_unused) | ||
14 | { | ||
15 | char *s = strchr(ops->raw, ','), *target, *endptr; | ||
16 | |||
17 | if (s == NULL) | ||
18 | return -1; | ||
19 | |||
20 | *s = '\0'; | ||
21 | ops->source.raw = strdup(ops->raw); | ||
22 | *s = ','; | ||
23 | |||
24 | if (ops->source.raw == NULL) | ||
25 | return -1; | ||
26 | |||
27 | target = ++s; | ||
28 | ops->target.raw = strdup(target); | ||
29 | if (ops->target.raw == NULL) | ||
30 | goto out_free_source; | ||
31 | |||
32 | ops->target.addr = strtoull(target, &endptr, 16); | ||
33 | if (endptr == target) | ||
34 | goto out_free_target; | ||
35 | |||
36 | s = strchr(endptr, '<'); | ||
37 | if (s == NULL) | ||
38 | goto out_free_target; | ||
39 | endptr = strchr(s + 1, '>'); | ||
40 | if (endptr == NULL) | ||
41 | goto out_free_target; | ||
42 | |||
43 | *endptr = '\0'; | ||
44 | *s = ' '; | ||
45 | ops->target.name = strdup(s); | ||
46 | *s = '<'; | ||
47 | *endptr = '>'; | ||
48 | if (ops->target.name == NULL) | ||
49 | goto out_free_target; | ||
50 | |||
51 | return 0; | ||
52 | |||
53 | out_free_target: | ||
54 | zfree(&ops->target.raw); | ||
55 | out_free_source: | ||
56 | zfree(&ops->source.raw); | ||
57 | return -1; | ||
58 | } | ||
59 | |||
60 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | ||
61 | struct ins_operands *ops); | ||
62 | |||
63 | static struct ins_ops arm64_mov_ops = { | ||
64 | .parse = arm64_mov__parse, | ||
65 | .scnprintf = mov__scnprintf, | ||
66 | }; | ||
67 | |||
11 | static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) | 68 | static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) |
12 | { | 69 | { |
13 | struct arm64_annotate *arm = arch->priv; | 70 | struct arm64_annotate *arm = arch->priv; |
@@ -21,7 +78,7 @@ static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const | |||
21 | else if (!strcmp(name, "ret")) | 78 | else if (!strcmp(name, "ret")) |
22 | ops = &ret_ops; | 79 | ops = &ret_ops; |
23 | else | 80 | else |
24 | return NULL; | 81 | ops = &arm64_mov_ops; |
25 | 82 | ||
26 | arch__associate_ins_ops(arch, name, ops); | 83 | arch__associate_ins_ops(arch, name, ops); |
27 | return ops; | 84 | return ops; |
diff --git a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl index 2dbb8cade048..c88fd32563eb 100755 --- a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl +++ b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl | |||
@@ -23,7 +23,7 @@ create_table_from_c() | |||
23 | { | 23 | { |
24 | local sc nr last_sc | 24 | local sc nr last_sc |
25 | 25 | ||
26 | create_table_exe=`mktemp /tmp/create-table-XXXXXX` | 26 | create_table_exe=`mktemp ${TMPDIR:-/tmp}/create-table-XXXXXX` |
27 | 27 | ||
28 | { | 28 | { |
29 | 29 | ||
diff --git a/tools/perf/arch/powerpc/util/book3s_hv_exits.h b/tools/perf/arch/powerpc/util/book3s_hv_exits.h index 853b95d1e139..2011376c7ab5 100644 --- a/tools/perf/arch/powerpc/util/book3s_hv_exits.h +++ b/tools/perf/arch/powerpc/util/book3s_hv_exits.h | |||
@@ -15,7 +15,6 @@ | |||
15 | {0x400, "INST_STORAGE"}, \ | 15 | {0x400, "INST_STORAGE"}, \ |
16 | {0x480, "INST_SEGMENT"}, \ | 16 | {0x480, "INST_SEGMENT"}, \ |
17 | {0x500, "EXTERNAL"}, \ | 17 | {0x500, "EXTERNAL"}, \ |
18 | {0x501, "EXTERNAL_LEVEL"}, \ | ||
19 | {0x502, "EXTERNAL_HV"}, \ | 18 | {0x502, "EXTERNAL_HV"}, \ |
20 | {0x600, "ALIGNMENT"}, \ | 19 | {0x600, "ALIGNMENT"}, \ |
21 | {0x700, "PROGRAM"}, \ | 20 | {0x700, "PROGRAM"}, \ |
diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index cee4e2f7c057..de0dd66dbb48 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c | |||
@@ -100,8 +100,6 @@ out_free_source: | |||
100 | return -1; | 100 | return -1; |
101 | } | 101 | } |
102 | 102 | ||
103 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | ||
104 | struct ins_operands *ops); | ||
105 | 103 | ||
106 | static struct ins_ops s390_mov_ops = { | 104 | static struct ins_ops s390_mov_ops = { |
107 | .parse = s390_mov__parse, | 105 | .parse = s390_mov__parse, |
diff --git a/tools/perf/arch/sparc/Makefile b/tools/perf/arch/sparc/Makefile index 7fbca175099e..275dea7ff59a 100644 --- a/tools/perf/arch/sparc/Makefile +++ b/tools/perf/arch/sparc/Makefile | |||
@@ -1,3 +1,5 @@ | |||
1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | endif | 3 | endif |
4 | |||
5 | PERF_HAVE_JITDUMP := 1 | ||
diff --git a/tools/perf/arch/sparc/annotate/instructions.c b/tools/perf/arch/sparc/annotate/instructions.c new file mode 100644 index 000000000000..2614c010c235 --- /dev/null +++ b/tools/perf/arch/sparc/annotate/instructions.c | |||
@@ -0,0 +1,169 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | static int is_branch_cond(const char *cond) | ||
4 | { | ||
5 | if (cond[0] == '\0') | ||
6 | return 1; | ||
7 | |||
8 | if (cond[0] == 'a' && cond[1] == '\0') | ||
9 | return 1; | ||
10 | |||
11 | if (cond[0] == 'c' && | ||
12 | (cond[1] == 'c' || cond[1] == 's') && | ||
13 | cond[2] == '\0') | ||
14 | return 1; | ||
15 | |||
16 | if (cond[0] == 'e' && | ||
17 | (cond[1] == '\0' || | ||
18 | (cond[1] == 'q' && cond[2] == '\0'))) | ||
19 | return 1; | ||
20 | |||
21 | if (cond[0] == 'g' && | ||
22 | (cond[1] == '\0' || | ||
23 | (cond[1] == 't' && cond[2] == '\0') || | ||
24 | (cond[1] == 'e' && cond[2] == '\0') || | ||
25 | (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0'))) | ||
26 | return 1; | ||
27 | |||
28 | if (cond[0] == 'l' && | ||
29 | (cond[1] == '\0' || | ||
30 | (cond[1] == 't' && cond[2] == '\0') || | ||
31 | (cond[1] == 'u' && cond[2] == '\0') || | ||
32 | (cond[1] == 'e' && cond[2] == '\0') || | ||
33 | (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0'))) | ||
34 | return 1; | ||
35 | |||
36 | if (cond[0] == 'n' && | ||
37 | (cond[1] == '\0' || | ||
38 | (cond[1] == 'e' && cond[2] == '\0') || | ||
39 | (cond[1] == 'z' && cond[2] == '\0') || | ||
40 | (cond[1] == 'e' && cond[2] == 'g' && cond[3] == '\0'))) | ||
41 | return 1; | ||
42 | |||
43 | if (cond[0] == 'b' && | ||
44 | cond[1] == 'p' && | ||
45 | cond[2] == 'o' && | ||
46 | cond[3] == 's' && | ||
47 | cond[4] == '\0') | ||
48 | return 1; | ||
49 | |||
50 | if (cond[0] == 'v' && | ||
51 | (cond[1] == 'c' || cond[1] == 's') && | ||
52 | cond[2] == '\0') | ||
53 | return 1; | ||
54 | |||
55 | if (cond[0] == 'b' && | ||
56 | cond[1] == 'z' && | ||
57 | cond[2] == '\0') | ||
58 | return 1; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static int is_branch_reg_cond(const char *cond) | ||
64 | { | ||
65 | if ((cond[0] == 'n' || cond[0] == 'l') && | ||
66 | cond[1] == 'z' && | ||
67 | cond[2] == '\0') | ||
68 | return 1; | ||
69 | |||
70 | if (cond[0] == 'z' && | ||
71 | cond[1] == '\0') | ||
72 | return 1; | ||
73 | |||
74 | if ((cond[0] == 'g' || cond[0] == 'l') && | ||
75 | cond[1] == 'e' && | ||
76 | cond[2] == 'z' && | ||
77 | cond[3] == '\0') | ||
78 | return 1; | ||
79 | |||
80 | if (cond[0] == 'g' && | ||
81 | cond[1] == 'z' && | ||
82 | cond[2] == '\0') | ||
83 | return 1; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int is_branch_float_cond(const char *cond) | ||
89 | { | ||
90 | if (cond[0] == '\0') | ||
91 | return 1; | ||
92 | |||
93 | if ((cond[0] == 'a' || cond[0] == 'e' || | ||
94 | cond[0] == 'z' || cond[0] == 'g' || | ||
95 | cond[0] == 'l' || cond[0] == 'n' || | ||
96 | cond[0] == 'o' || cond[0] == 'u') && | ||
97 | cond[1] == '\0') | ||
98 | return 1; | ||
99 | |||
100 | if (((cond[0] == 'g' && cond[1] == 'e') || | ||
101 | (cond[0] == 'l' && (cond[1] == 'e' || | ||
102 | cond[1] == 'g')) || | ||
103 | (cond[0] == 'n' && (cond[1] == 'e' || | ||
104 | cond[1] == 'z')) || | ||
105 | (cond[0] == 'u' && (cond[1] == 'e' || | ||
106 | cond[1] == 'g' || | ||
107 | cond[1] == 'l'))) && | ||
108 | cond[2] == '\0') | ||
109 | return 1; | ||
110 | |||
111 | if (cond[0] == 'u' && | ||
112 | (cond[1] == 'g' || cond[1] == 'l') && | ||
113 | cond[2] == 'e' && | ||
114 | cond[3] == '\0') | ||
115 | return 1; | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static struct ins_ops *sparc__associate_instruction_ops(struct arch *arch, const char *name) | ||
121 | { | ||
122 | struct ins_ops *ops = NULL; | ||
123 | |||
124 | if (!strcmp(name, "call") || | ||
125 | !strcmp(name, "jmp") || | ||
126 | !strcmp(name, "jmpl")) { | ||
127 | ops = &call_ops; | ||
128 | } else if (!strcmp(name, "ret") || | ||
129 | !strcmp(name, "retl") || | ||
130 | !strcmp(name, "return")) { | ||
131 | ops = &ret_ops; | ||
132 | } else if (!strcmp(name, "mov")) { | ||
133 | ops = &mov_ops; | ||
134 | } else { | ||
135 | if (name[0] == 'c' && | ||
136 | (name[1] == 'w' || name[1] == 'x')) | ||
137 | name += 2; | ||
138 | |||
139 | if (name[0] == 'b') { | ||
140 | const char *cond = name + 1; | ||
141 | |||
142 | if (cond[0] == 'r') { | ||
143 | if (is_branch_reg_cond(cond + 1)) | ||
144 | ops = &jump_ops; | ||
145 | } else if (is_branch_cond(cond)) { | ||
146 | ops = &jump_ops; | ||
147 | } | ||
148 | } else if (name[0] == 'f' && name[1] == 'b') { | ||
149 | if (is_branch_float_cond(name + 2)) | ||
150 | ops = &jump_ops; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | if (ops) | ||
155 | arch__associate_ins_ops(arch, name, ops); | ||
156 | |||
157 | return ops; | ||
158 | } | ||
159 | |||
160 | static int sparc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) | ||
161 | { | ||
162 | if (!arch->initialized) { | ||
163 | arch->initialized = true; | ||
164 | arch->associate_instruction_ops = sparc__associate_instruction_ops; | ||
165 | arch->objdump.comment_char = '#'; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 830481b8db26..93d679eaf1f4 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -283,12 +283,11 @@ out_put: | |||
283 | return ret; | 283 | return ret; |
284 | } | 284 | } |
285 | 285 | ||
286 | static int process_feature_event(struct perf_tool *tool, | 286 | static int process_feature_event(struct perf_session *session, |
287 | union perf_event *event, | 287 | union perf_event *event) |
288 | struct perf_session *session) | ||
289 | { | 288 | { |
290 | if (event->feat.feat_id < HEADER_LAST_FEATURE) | 289 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
291 | return perf_event__process_feature(tool, event, session); | 290 | return perf_event__process_feature(session, event); |
292 | return 0; | 291 | return 0; |
293 | } | 292 | } |
294 | 293 | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a3b346359ba0..eda41673c4f3 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -86,12 +86,10 @@ static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, | |||
86 | } | 86 | } |
87 | #endif | 87 | #endif |
88 | 88 | ||
89 | static int perf_event__repipe_op2_synth(struct perf_tool *tool, | 89 | static int perf_event__repipe_op2_synth(struct perf_session *session, |
90 | union perf_event *event, | 90 | union perf_event *event) |
91 | struct perf_session *session | ||
92 | __maybe_unused) | ||
93 | { | 91 | { |
94 | return perf_event__repipe_synth(tool, event); | 92 | return perf_event__repipe_synth(session->tool, event); |
95 | } | 93 | } |
96 | 94 | ||
97 | static int perf_event__repipe_attr(struct perf_tool *tool, | 95 | static int perf_event__repipe_attr(struct perf_tool *tool, |
@@ -133,10 +131,10 @@ static int copy_bytes(struct perf_inject *inject, int fd, off_t size) | |||
133 | return 0; | 131 | return 0; |
134 | } | 132 | } |
135 | 133 | ||
136 | static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, | 134 | static s64 perf_event__repipe_auxtrace(struct perf_session *session, |
137 | union perf_event *event, | 135 | union perf_event *event) |
138 | struct perf_session *session) | ||
139 | { | 136 | { |
137 | struct perf_tool *tool = session->tool; | ||
140 | struct perf_inject *inject = container_of(tool, struct perf_inject, | 138 | struct perf_inject *inject = container_of(tool, struct perf_inject, |
141 | tool); | 139 | tool); |
142 | int ret; | 140 | int ret; |
@@ -174,9 +172,8 @@ static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, | |||
174 | #else | 172 | #else |
175 | 173 | ||
176 | static s64 | 174 | static s64 |
177 | perf_event__repipe_auxtrace(struct perf_tool *tool __maybe_unused, | 175 | perf_event__repipe_auxtrace(struct perf_session *session __maybe_unused, |
178 | union perf_event *event __maybe_unused, | 176 | union perf_event *event __maybe_unused) |
179 | struct perf_session *session __maybe_unused) | ||
180 | { | 177 | { |
181 | pr_err("AUX area tracing not supported\n"); | 178 | pr_err("AUX area tracing not supported\n"); |
182 | return -EINVAL; | 179 | return -EINVAL; |
@@ -362,26 +359,24 @@ static int perf_event__repipe_exit(struct perf_tool *tool, | |||
362 | return err; | 359 | return err; |
363 | } | 360 | } |
364 | 361 | ||
365 | static int perf_event__repipe_tracing_data(struct perf_tool *tool, | 362 | static int perf_event__repipe_tracing_data(struct perf_session *session, |
366 | union perf_event *event, | 363 | union perf_event *event) |
367 | struct perf_session *session) | ||
368 | { | 364 | { |
369 | int err; | 365 | int err; |
370 | 366 | ||
371 | perf_event__repipe_synth(tool, event); | 367 | perf_event__repipe_synth(session->tool, event); |
372 | err = perf_event__process_tracing_data(tool, event, session); | 368 | err = perf_event__process_tracing_data(session, event); |
373 | 369 | ||
374 | return err; | 370 | return err; |
375 | } | 371 | } |
376 | 372 | ||
377 | static int perf_event__repipe_id_index(struct perf_tool *tool, | 373 | static int perf_event__repipe_id_index(struct perf_session *session, |
378 | union perf_event *event, | 374 | union perf_event *event) |
379 | struct perf_session *session) | ||
380 | { | 375 | { |
381 | int err; | 376 | int err; |
382 | 377 | ||
383 | perf_event__repipe_synth(tool, event); | 378 | perf_event__repipe_synth(session->tool, event); |
384 | err = perf_event__process_id_index(tool, event, session); | 379 | err = perf_event__process_id_index(session, event); |
385 | 380 | ||
386 | return err; | 381 | return err; |
387 | } | 382 | } |
@@ -803,7 +798,8 @@ int cmd_inject(int argc, const char **argv) | |||
803 | "kallsyms pathname"), | 798 | "kallsyms pathname"), |
804 | OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"), | 799 | OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"), |
805 | OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts, | 800 | OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts, |
806 | NULL, "opts", "Instruction Tracing options", | 801 | NULL, "opts", "Instruction Tracing options\n" |
802 | ITRACE_HELP, | ||
807 | itrace_parse_synth_opts), | 803 | itrace_parse_synth_opts), |
808 | OPT_BOOLEAN(0, "strip", &inject.strip, | 804 | OPT_BOOLEAN(0, "strip", &inject.strip, |
809 | "strip non-synthesized events (use with --itrace)"), | 805 | "strip non-synthesized events (use with --itrace)"), |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 22ebeb92ac51..488779bc4c8d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -106,9 +106,12 @@ static bool switch_output_time(struct record *rec) | |||
106 | trigger_is_ready(&switch_output_trigger); | 106 | trigger_is_ready(&switch_output_trigger); |
107 | } | 107 | } |
108 | 108 | ||
109 | static int record__write(struct record *rec, void *bf, size_t size) | 109 | static int record__write(struct record *rec, struct perf_mmap *map __maybe_unused, |
110 | void *bf, size_t size) | ||
110 | { | 111 | { |
111 | if (perf_data__write(rec->session->data, bf, size) < 0) { | 112 | struct perf_data_file *file = &rec->session->data->file; |
113 | |||
114 | if (perf_data_file__write(file, bf, size) < 0) { | ||
112 | pr_err("failed to write perf data, error: %m\n"); | 115 | pr_err("failed to write perf data, error: %m\n"); |
113 | return -1; | 116 | return -1; |
114 | } | 117 | } |
@@ -127,15 +130,15 @@ static int process_synthesized_event(struct perf_tool *tool, | |||
127 | struct machine *machine __maybe_unused) | 130 | struct machine *machine __maybe_unused) |
128 | { | 131 | { |
129 | struct record *rec = container_of(tool, struct record, tool); | 132 | struct record *rec = container_of(tool, struct record, tool); |
130 | return record__write(rec, event, event->header.size); | 133 | return record__write(rec, NULL, event, event->header.size); |
131 | } | 134 | } |
132 | 135 | ||
133 | static int record__pushfn(void *to, void *bf, size_t size) | 136 | static int record__pushfn(struct perf_mmap *map, void *to, void *bf, size_t size) |
134 | { | 137 | { |
135 | struct record *rec = to; | 138 | struct record *rec = to; |
136 | 139 | ||
137 | rec->samples++; | 140 | rec->samples++; |
138 | return record__write(rec, bf, size); | 141 | return record__write(rec, map, bf, size); |
139 | } | 142 | } |
140 | 143 | ||
141 | static volatile int done; | 144 | static volatile int done; |
@@ -170,6 +173,7 @@ static void record__sig_exit(void) | |||
170 | #ifdef HAVE_AUXTRACE_SUPPORT | 173 | #ifdef HAVE_AUXTRACE_SUPPORT |
171 | 174 | ||
172 | static int record__process_auxtrace(struct perf_tool *tool, | 175 | static int record__process_auxtrace(struct perf_tool *tool, |
176 | struct perf_mmap *map, | ||
173 | union perf_event *event, void *data1, | 177 | union perf_event *event, void *data1, |
174 | size_t len1, void *data2, size_t len2) | 178 | size_t len1, void *data2, size_t len2) |
175 | { | 179 | { |
@@ -197,21 +201,21 @@ static int record__process_auxtrace(struct perf_tool *tool, | |||
197 | if (padding) | 201 | if (padding) |
198 | padding = 8 - padding; | 202 | padding = 8 - padding; |
199 | 203 | ||
200 | record__write(rec, event, event->header.size); | 204 | record__write(rec, map, event, event->header.size); |
201 | record__write(rec, data1, len1); | 205 | record__write(rec, map, data1, len1); |
202 | if (len2) | 206 | if (len2) |
203 | record__write(rec, data2, len2); | 207 | record__write(rec, map, data2, len2); |
204 | record__write(rec, &pad, padding); | 208 | record__write(rec, map, &pad, padding); |
205 | 209 | ||
206 | return 0; | 210 | return 0; |
207 | } | 211 | } |
208 | 212 | ||
209 | static int record__auxtrace_mmap_read(struct record *rec, | 213 | static int record__auxtrace_mmap_read(struct record *rec, |
210 | struct auxtrace_mmap *mm) | 214 | struct perf_mmap *map) |
211 | { | 215 | { |
212 | int ret; | 216 | int ret; |
213 | 217 | ||
214 | ret = auxtrace_mmap__read(mm, rec->itr, &rec->tool, | 218 | ret = auxtrace_mmap__read(map, rec->itr, &rec->tool, |
215 | record__process_auxtrace); | 219 | record__process_auxtrace); |
216 | if (ret < 0) | 220 | if (ret < 0) |
217 | return ret; | 221 | return ret; |
@@ -223,11 +227,11 @@ static int record__auxtrace_mmap_read(struct record *rec, | |||
223 | } | 227 | } |
224 | 228 | ||
225 | static int record__auxtrace_mmap_read_snapshot(struct record *rec, | 229 | static int record__auxtrace_mmap_read_snapshot(struct record *rec, |
226 | struct auxtrace_mmap *mm) | 230 | struct perf_mmap *map) |
227 | { | 231 | { |
228 | int ret; | 232 | int ret; |
229 | 233 | ||
230 | ret = auxtrace_mmap__read_snapshot(mm, rec->itr, &rec->tool, | 234 | ret = auxtrace_mmap__read_snapshot(map, rec->itr, &rec->tool, |
231 | record__process_auxtrace, | 235 | record__process_auxtrace, |
232 | rec->opts.auxtrace_snapshot_size); | 236 | rec->opts.auxtrace_snapshot_size); |
233 | if (ret < 0) | 237 | if (ret < 0) |
@@ -245,13 +249,12 @@ static int record__auxtrace_read_snapshot_all(struct record *rec) | |||
245 | int rc = 0; | 249 | int rc = 0; |
246 | 250 | ||
247 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 251 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
248 | struct auxtrace_mmap *mm = | 252 | struct perf_mmap *map = &rec->evlist->mmap[i]; |
249 | &rec->evlist->mmap[i].auxtrace_mmap; | ||
250 | 253 | ||
251 | if (!mm->base) | 254 | if (!map->auxtrace_mmap.base) |
252 | continue; | 255 | continue; |
253 | 256 | ||
254 | if (record__auxtrace_mmap_read_snapshot(rec, mm) != 0) { | 257 | if (record__auxtrace_mmap_read_snapshot(rec, map) != 0) { |
255 | rc = -1; | 258 | rc = -1; |
256 | goto out; | 259 | goto out; |
257 | } | 260 | } |
@@ -295,7 +298,7 @@ static int record__auxtrace_init(struct record *rec) | |||
295 | 298 | ||
296 | static inline | 299 | static inline |
297 | int record__auxtrace_mmap_read(struct record *rec __maybe_unused, | 300 | int record__auxtrace_mmap_read(struct record *rec __maybe_unused, |
298 | struct auxtrace_mmap *mm __maybe_unused) | 301 | struct perf_mmap *map __maybe_unused) |
299 | { | 302 | { |
300 | return 0; | 303 | return 0; |
301 | } | 304 | } |
@@ -388,7 +391,12 @@ try_again: | |||
388 | ui__warning("%s\n", msg); | 391 | ui__warning("%s\n", msg); |
389 | goto try_again; | 392 | goto try_again; |
390 | } | 393 | } |
391 | 394 | if ((errno == EINVAL || errno == EBADF) && | |
395 | pos->leader != pos && | ||
396 | pos->weak_group) { | ||
397 | pos = perf_evlist__reset_weak_group(evlist, pos); | ||
398 | goto try_again; | ||
399 | } | ||
392 | rc = -errno; | 400 | rc = -errno; |
393 | perf_evsel__open_strerror(pos, &opts->target, | 401 | perf_evsel__open_strerror(pos, &opts->target, |
394 | errno, msg, sizeof(msg)); | 402 | errno, msg, sizeof(msg)); |
@@ -529,17 +537,17 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli | |||
529 | return 0; | 537 | return 0; |
530 | 538 | ||
531 | for (i = 0; i < evlist->nr_mmaps; i++) { | 539 | for (i = 0; i < evlist->nr_mmaps; i++) { |
532 | struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap; | 540 | struct perf_mmap *map = &maps[i]; |
533 | 541 | ||
534 | if (maps[i].base) { | 542 | if (map->base) { |
535 | if (perf_mmap__push(&maps[i], rec, record__pushfn) != 0) { | 543 | if (perf_mmap__push(map, rec, record__pushfn) != 0) { |
536 | rc = -1; | 544 | rc = -1; |
537 | goto out; | 545 | goto out; |
538 | } | 546 | } |
539 | } | 547 | } |
540 | 548 | ||
541 | if (mm->base && !rec->opts.auxtrace_snapshot_mode && | 549 | if (map->auxtrace_mmap.base && !rec->opts.auxtrace_snapshot_mode && |
542 | record__auxtrace_mmap_read(rec, mm) != 0) { | 550 | record__auxtrace_mmap_read(rec, map) != 0) { |
543 | rc = -1; | 551 | rc = -1; |
544 | goto out; | 552 | goto out; |
545 | } | 553 | } |
@@ -550,7 +558,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli | |||
550 | * at least one event. | 558 | * at least one event. |
551 | */ | 559 | */ |
552 | if (bytes_written != rec->bytes_written) | 560 | if (bytes_written != rec->bytes_written) |
553 | rc = record__write(rec, &finished_round_event, sizeof(finished_round_event)); | 561 | rc = record__write(rec, NULL, &finished_round_event, sizeof(finished_round_event)); |
554 | 562 | ||
555 | if (overwrite) | 563 | if (overwrite) |
556 | perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_EMPTY); | 564 | perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_EMPTY); |
@@ -589,6 +597,9 @@ static void record__init_features(struct record *rec) | |||
589 | if (!rec->opts.full_auxtrace) | 597 | if (!rec->opts.full_auxtrace) |
590 | perf_header__clear_feat(&session->header, HEADER_AUXTRACE); | 598 | perf_header__clear_feat(&session->header, HEADER_AUXTRACE); |
591 | 599 | ||
600 | if (!(rec->opts.use_clockid && rec->opts.clockid_res_ns)) | ||
601 | perf_header__clear_feat(&session->header, HEADER_CLOCKID); | ||
602 | |||
592 | perf_header__clear_feat(&session->header, HEADER_STAT); | 603 | perf_header__clear_feat(&session->header, HEADER_STAT); |
593 | } | 604 | } |
594 | 605 | ||
@@ -758,7 +769,7 @@ static int record__synthesize(struct record *rec, bool tail) | |||
758 | * We need to synthesize events first, because some | 769 | * We need to synthesize events first, because some |
759 | * features works on top of them (on report side). | 770 | * features works on top of them (on report side). |
760 | */ | 771 | */ |
761 | err = perf_event__synthesize_attrs(tool, session, | 772 | err = perf_event__synthesize_attrs(tool, rec->evlist, |
762 | process_synthesized_event); | 773 | process_synthesized_event); |
763 | if (err < 0) { | 774 | if (err < 0) { |
764 | pr_err("Couldn't synthesize attrs.\n"); | 775 | pr_err("Couldn't synthesize attrs.\n"); |
@@ -894,6 +905,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
894 | 905 | ||
895 | record__init_features(rec); | 906 | record__init_features(rec); |
896 | 907 | ||
908 | if (rec->opts.use_clockid && rec->opts.clockid_res_ns) | ||
909 | session->header.env.clockid_res_ns = rec->opts.clockid_res_ns; | ||
910 | |||
897 | if (forks) { | 911 | if (forks) { |
898 | err = perf_evlist__prepare_workload(rec->evlist, &opts->target, | 912 | err = perf_evlist__prepare_workload(rec->evlist, &opts->target, |
899 | argv, data->is_pipe, | 913 | argv, data->is_pipe, |
@@ -1334,6 +1348,19 @@ static const struct clockid_map clockids[] = { | |||
1334 | CLOCKID_END, | 1348 | CLOCKID_END, |
1335 | }; | 1349 | }; |
1336 | 1350 | ||
1351 | static int get_clockid_res(clockid_t clk_id, u64 *res_ns) | ||
1352 | { | ||
1353 | struct timespec res; | ||
1354 | |||
1355 | *res_ns = 0; | ||
1356 | if (!clock_getres(clk_id, &res)) | ||
1357 | *res_ns = res.tv_nsec + res.tv_sec * NSEC_PER_SEC; | ||
1358 | else | ||
1359 | pr_warning("WARNING: Failed to determine specified clock resolution.\n"); | ||
1360 | |||
1361 | return 0; | ||
1362 | } | ||
1363 | |||
1337 | static int parse_clockid(const struct option *opt, const char *str, int unset) | 1364 | static int parse_clockid(const struct option *opt, const char *str, int unset) |
1338 | { | 1365 | { |
1339 | struct record_opts *opts = (struct record_opts *)opt->value; | 1366 | struct record_opts *opts = (struct record_opts *)opt->value; |
@@ -1357,7 +1384,7 @@ static int parse_clockid(const struct option *opt, const char *str, int unset) | |||
1357 | 1384 | ||
1358 | /* if its a number, we're done */ | 1385 | /* if its a number, we're done */ |
1359 | if (sscanf(str, "%d", &opts->clockid) == 1) | 1386 | if (sscanf(str, "%d", &opts->clockid) == 1) |
1360 | return 0; | 1387 | return get_clockid_res(opts->clockid, &opts->clockid_res_ns); |
1361 | 1388 | ||
1362 | /* allow a "CLOCK_" prefix to the name */ | 1389 | /* allow a "CLOCK_" prefix to the name */ |
1363 | if (!strncasecmp(str, "CLOCK_", 6)) | 1390 | if (!strncasecmp(str, "CLOCK_", 6)) |
@@ -1366,7 +1393,8 @@ static int parse_clockid(const struct option *opt, const char *str, int unset) | |||
1366 | for (cm = clockids; cm->name; cm++) { | 1393 | for (cm = clockids; cm->name; cm++) { |
1367 | if (!strcasecmp(str, cm->name)) { | 1394 | if (!strcasecmp(str, cm->name)) { |
1368 | opts->clockid = cm->clockid; | 1395 | opts->clockid = cm->clockid; |
1369 | return 0; | 1396 | return get_clockid_res(opts->clockid, |
1397 | &opts->clockid_res_ns); | ||
1370 | } | 1398 | } |
1371 | } | 1399 | } |
1372 | 1400 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 76e12bcd1765..257c9c18cb7e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -201,14 +201,13 @@ static void setup_forced_leader(struct report *report, | |||
201 | perf_evlist__force_leader(evlist); | 201 | perf_evlist__force_leader(evlist); |
202 | } | 202 | } |
203 | 203 | ||
204 | static int process_feature_event(struct perf_tool *tool, | 204 | static int process_feature_event(struct perf_session *session, |
205 | union perf_event *event, | 205 | union perf_event *event) |
206 | struct perf_session *session __maybe_unused) | ||
207 | { | 206 | { |
208 | struct report *rep = container_of(tool, struct report, tool); | 207 | struct report *rep = container_of(session->tool, struct report, tool); |
209 | 208 | ||
210 | if (event->feat.feat_id < HEADER_LAST_FEATURE) | 209 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
211 | return perf_event__process_feature(tool, event, session); | 210 | return perf_event__process_feature(session, event); |
212 | 211 | ||
213 | if (event->feat.feat_id != HEADER_LAST_FEATURE) { | 212 | if (event->feat.feat_id != HEADER_LAST_FEATURE) { |
214 | pr_err("failed: wrong feature ID: %" PRIu64 "\n", | 213 | pr_err("failed: wrong feature ID: %" PRIu64 "\n", |
@@ -981,6 +980,7 @@ int cmd_report(int argc, const char **argv) | |||
981 | .id_index = perf_event__process_id_index, | 980 | .id_index = perf_event__process_id_index, |
982 | .auxtrace_info = perf_event__process_auxtrace_info, | 981 | .auxtrace_info = perf_event__process_auxtrace_info, |
983 | .auxtrace = perf_event__process_auxtrace, | 982 | .auxtrace = perf_event__process_auxtrace, |
983 | .event_update = perf_event__process_event_update, | ||
984 | .feature = process_feature_event, | 984 | .feature = process_feature_event, |
985 | .ordered_events = true, | 985 | .ordered_events = true, |
986 | .ordering_requires_timestamps = true, | 986 | .ordering_requires_timestamps = true, |
@@ -1105,7 +1105,7 @@ int cmd_report(int argc, const char **argv) | |||
1105 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 1105 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
1106 | "how to display percentage of filtered entries", parse_filter_percentage), | 1106 | "how to display percentage of filtered entries", parse_filter_percentage), |
1107 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", | 1107 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", |
1108 | "Instruction Tracing options", | 1108 | "Instruction Tracing options\n" ITRACE_HELP, |
1109 | itrace_parse_synth_opts), | 1109 | itrace_parse_synth_opts), |
1110 | OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename, | 1110 | OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename, |
1111 | "Show full source file name path for source lines"), | 1111 | "Show full source file name path for source lines"), |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ba481d73f910..b5bc85bd0bbe 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <sys/stat.h> | 44 | #include <sys/stat.h> |
45 | #include <fcntl.h> | 45 | #include <fcntl.h> |
46 | #include <unistd.h> | 46 | #include <unistd.h> |
47 | #include <subcmd/pager.h> | ||
47 | 48 | ||
48 | #include "sane_ctype.h" | 49 | #include "sane_ctype.h" |
49 | 50 | ||
@@ -406,9 +407,10 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
406 | PERF_OUTPUT_WEIGHT)) | 407 | PERF_OUTPUT_WEIGHT)) |
407 | return -EINVAL; | 408 | return -EINVAL; |
408 | 409 | ||
409 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | 410 | if (PRINT_FIELD(SYM) && |
411 | !(evsel->attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { | ||
410 | pr_err("Display of symbols requested but neither sample IP nor " | 412 | pr_err("Display of symbols requested but neither sample IP nor " |
411 | "sample address\nis selected. Hence, no addresses to convert " | 413 | "sample address\navailable. Hence, no addresses to convert " |
412 | "to symbols.\n"); | 414 | "to symbols.\n"); |
413 | return -EINVAL; | 415 | return -EINVAL; |
414 | } | 416 | } |
@@ -417,10 +419,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
417 | "selected.\n"); | 419 | "selected.\n"); |
418 | return -EINVAL; | 420 | return -EINVAL; |
419 | } | 421 | } |
420 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) && | 422 | if (PRINT_FIELD(DSO) && |
421 | !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) { | 423 | !(evsel->attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { |
422 | pr_err("Display of DSO requested but no address to convert. Select\n" | 424 | pr_err("Display of DSO requested but no address to convert.\n"); |
423 | "sample IP, sample address, brstack, brstacksym, or brstackoff.\n"); | ||
424 | return -EINVAL; | 425 | return -EINVAL; |
425 | } | 426 | } |
426 | if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { | 427 | if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { |
@@ -912,7 +913,7 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, | |||
912 | 913 | ||
913 | static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, | 914 | static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, |
914 | struct perf_insn *x, u8 *inbuf, int len, | 915 | struct perf_insn *x, u8 *inbuf, int len, |
915 | int insn, FILE *fp) | 916 | int insn, FILE *fp, int *total_cycles) |
916 | { | 917 | { |
917 | int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t#%s%s%s%s", ip, | 918 | int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t#%s%s%s%s", ip, |
918 | dump_insn(x, ip, inbuf, len, NULL), | 919 | dump_insn(x, ip, inbuf, len, NULL), |
@@ -921,7 +922,8 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, | |||
921 | en->flags.in_tx ? " INTX" : "", | 922 | en->flags.in_tx ? " INTX" : "", |
922 | en->flags.abort ? " ABORT" : ""); | 923 | en->flags.abort ? " ABORT" : ""); |
923 | if (en->flags.cycles) { | 924 | if (en->flags.cycles) { |
924 | printed += fprintf(fp, " %d cycles", en->flags.cycles); | 925 | *total_cycles += en->flags.cycles; |
926 | printed += fprintf(fp, " %d cycles [%d]", en->flags.cycles, *total_cycles); | ||
925 | if (insn) | 927 | if (insn) |
926 | printed += fprintf(fp, " %.2f IPC", (float)insn / en->flags.cycles); | 928 | printed += fprintf(fp, " %.2f IPC", (float)insn / en->flags.cycles); |
927 | } | 929 | } |
@@ -978,6 +980,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
978 | u8 buffer[MAXBB]; | 980 | u8 buffer[MAXBB]; |
979 | unsigned off; | 981 | unsigned off; |
980 | struct symbol *lastsym = NULL; | 982 | struct symbol *lastsym = NULL; |
983 | int total_cycles = 0; | ||
981 | 984 | ||
982 | if (!(br && br->nr)) | 985 | if (!(br && br->nr)) |
983 | return 0; | 986 | return 0; |
@@ -998,7 +1001,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
998 | printed += ip__fprintf_sym(br->entries[nr - 1].from, thread, | 1001 | printed += ip__fprintf_sym(br->entries[nr - 1].from, thread, |
999 | x.cpumode, x.cpu, &lastsym, attr, fp); | 1002 | x.cpumode, x.cpu, &lastsym, attr, fp); |
1000 | printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], | 1003 | printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], |
1001 | &x, buffer, len, 0, fp); | 1004 | &x, buffer, len, 0, fp, &total_cycles); |
1002 | } | 1005 | } |
1003 | 1006 | ||
1004 | /* Print all blocks */ | 1007 | /* Print all blocks */ |
@@ -1026,7 +1029,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
1026 | 1029 | ||
1027 | printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); | 1030 | printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); |
1028 | if (ip == end) { | 1031 | if (ip == end) { |
1029 | printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp); | 1032 | printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp, |
1033 | &total_cycles); | ||
1030 | break; | 1034 | break; |
1031 | } else { | 1035 | } else { |
1032 | printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, | 1036 | printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, |
@@ -1104,6 +1108,35 @@ out: | |||
1104 | return printed; | 1108 | return printed; |
1105 | } | 1109 | } |
1106 | 1110 | ||
1111 | static const char *resolve_branch_sym(struct perf_sample *sample, | ||
1112 | struct perf_evsel *evsel, | ||
1113 | struct thread *thread, | ||
1114 | struct addr_location *al, | ||
1115 | u64 *ip) | ||
1116 | { | ||
1117 | struct addr_location addr_al; | ||
1118 | struct perf_event_attr *attr = &evsel->attr; | ||
1119 | const char *name = NULL; | ||
1120 | |||
1121 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | ||
1122 | if (sample_addr_correlates_sym(attr)) { | ||
1123 | thread__resolve(thread, &addr_al, sample); | ||
1124 | if (addr_al.sym) | ||
1125 | name = addr_al.sym->name; | ||
1126 | else | ||
1127 | *ip = sample->addr; | ||
1128 | } else { | ||
1129 | *ip = sample->addr; | ||
1130 | } | ||
1131 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
1132 | if (al->sym) | ||
1133 | name = al->sym->name; | ||
1134 | else | ||
1135 | *ip = sample->ip; | ||
1136 | } | ||
1137 | return name; | ||
1138 | } | ||
1139 | |||
1107 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, | 1140 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, |
1108 | struct perf_evsel *evsel, | 1141 | struct perf_evsel *evsel, |
1109 | struct thread *thread, | 1142 | struct thread *thread, |
@@ -1111,10 +1144,10 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
1111 | { | 1144 | { |
1112 | struct perf_event_attr *attr = &evsel->attr; | 1145 | struct perf_event_attr *attr = &evsel->attr; |
1113 | size_t depth = thread_stack__depth(thread); | 1146 | size_t depth = thread_stack__depth(thread); |
1114 | struct addr_location addr_al; | ||
1115 | const char *name = NULL; | 1147 | const char *name = NULL; |
1116 | static int spacing; | 1148 | static int spacing; |
1117 | int len = 0; | 1149 | int len = 0; |
1150 | int dlen = 0; | ||
1118 | u64 ip = 0; | 1151 | u64 ip = 0; |
1119 | 1152 | ||
1120 | /* | 1153 | /* |
@@ -1124,21 +1157,12 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
1124 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) | 1157 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) |
1125 | depth += 1; | 1158 | depth += 1; |
1126 | 1159 | ||
1127 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | 1160 | name = resolve_branch_sym(sample, evsel, thread, al, &ip); |
1128 | if (sample_addr_correlates_sym(attr)) { | 1161 | |
1129 | thread__resolve(thread, &addr_al, sample); | 1162 | if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { |
1130 | if (addr_al.sym) | 1163 | dlen += fprintf(fp, "("); |
1131 | name = addr_al.sym->name; | 1164 | dlen += map__fprintf_dsoname(al->map, fp); |
1132 | else | 1165 | dlen += fprintf(fp, ")\t"); |
1133 | ip = sample->addr; | ||
1134 | } else { | ||
1135 | ip = sample->addr; | ||
1136 | } | ||
1137 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
1138 | if (al->sym) | ||
1139 | name = al->sym->name; | ||
1140 | else | ||
1141 | ip = sample->ip; | ||
1142 | } | 1166 | } |
1143 | 1167 | ||
1144 | if (name) | 1168 | if (name) |
@@ -1159,7 +1183,7 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
1159 | if (len < spacing) | 1183 | if (len < spacing) |
1160 | len += fprintf(fp, "%*s", spacing - len, ""); | 1184 | len += fprintf(fp, "%*s", spacing - len, ""); |
1161 | 1185 | ||
1162 | return len; | 1186 | return len + dlen; |
1163 | } | 1187 | } |
1164 | 1188 | ||
1165 | static int perf_sample__fprintf_insn(struct perf_sample *sample, | 1189 | static int perf_sample__fprintf_insn(struct perf_sample *sample, |
@@ -1255,6 +1279,18 @@ static struct { | |||
1255 | {0, NULL} | 1279 | {0, NULL} |
1256 | }; | 1280 | }; |
1257 | 1281 | ||
1282 | static const char *sample_flags_to_name(u32 flags) | ||
1283 | { | ||
1284 | int i; | ||
1285 | |||
1286 | for (i = 0; sample_flags[i].name ; i++) { | ||
1287 | if (sample_flags[i].flags == flags) | ||
1288 | return sample_flags[i].name; | ||
1289 | } | ||
1290 | |||
1291 | return NULL; | ||
1292 | } | ||
1293 | |||
1258 | static int perf_sample__fprintf_flags(u32 flags, FILE *fp) | 1294 | static int perf_sample__fprintf_flags(u32 flags, FILE *fp) |
1259 | { | 1295 | { |
1260 | const char *chars = PERF_IP_FLAG_CHARS; | 1296 | const char *chars = PERF_IP_FLAG_CHARS; |
@@ -1264,11 +1300,20 @@ static int perf_sample__fprintf_flags(u32 flags, FILE *fp) | |||
1264 | char str[33]; | 1300 | char str[33]; |
1265 | int i, pos = 0; | 1301 | int i, pos = 0; |
1266 | 1302 | ||
1267 | for (i = 0; sample_flags[i].name ; i++) { | 1303 | name = sample_flags_to_name(flags & ~PERF_IP_FLAG_IN_TX); |
1268 | if (sample_flags[i].flags == (flags & ~PERF_IP_FLAG_IN_TX)) { | 1304 | if (name) |
1269 | name = sample_flags[i].name; | 1305 | return fprintf(fp, " %-15s%4s ", name, in_tx ? "(x)" : ""); |
1270 | break; | 1306 | |
1271 | } | 1307 | if (flags & PERF_IP_FLAG_TRACE_BEGIN) { |
1308 | name = sample_flags_to_name(flags & ~(PERF_IP_FLAG_IN_TX | PERF_IP_FLAG_TRACE_BEGIN)); | ||
1309 | if (name) | ||
1310 | return fprintf(fp, " tr strt %-7s%4s ", name, in_tx ? "(x)" : ""); | ||
1311 | } | ||
1312 | |||
1313 | if (flags & PERF_IP_FLAG_TRACE_END) { | ||
1314 | name = sample_flags_to_name(flags & ~(PERF_IP_FLAG_IN_TX | PERF_IP_FLAG_TRACE_END)); | ||
1315 | if (name) | ||
1316 | return fprintf(fp, " tr end %-7s%4s ", name, in_tx ? "(x)" : ""); | ||
1272 | } | 1317 | } |
1273 | 1318 | ||
1274 | for (i = 0; i < n; i++, flags >>= 1) { | 1319 | for (i = 0; i < n; i++, flags >>= 1) { |
@@ -1281,10 +1326,7 @@ static int perf_sample__fprintf_flags(u32 flags, FILE *fp) | |||
1281 | } | 1326 | } |
1282 | str[pos] = 0; | 1327 | str[pos] = 0; |
1283 | 1328 | ||
1284 | if (name) | 1329 | return fprintf(fp, " %-19s ", str); |
1285 | return fprintf(fp, " %-7s%4s ", name, in_tx ? "(x)" : ""); | ||
1286 | |||
1287 | return fprintf(fp, " %-11s ", str); | ||
1288 | } | 1330 | } |
1289 | 1331 | ||
1290 | struct printer_data { | 1332 | struct printer_data { |
@@ -1544,7 +1586,8 @@ struct metric_ctx { | |||
1544 | FILE *fp; | 1586 | FILE *fp; |
1545 | }; | 1587 | }; |
1546 | 1588 | ||
1547 | static void script_print_metric(void *ctx, const char *color, | 1589 | static void script_print_metric(struct perf_stat_config *config __maybe_unused, |
1590 | void *ctx, const char *color, | ||
1548 | const char *fmt, | 1591 | const char *fmt, |
1549 | const char *unit, double val) | 1592 | const char *unit, double val) |
1550 | { | 1593 | { |
@@ -1562,7 +1605,8 @@ static void script_print_metric(void *ctx, const char *color, | |||
1562 | fprintf(mctx->fp, " %s\n", unit); | 1605 | fprintf(mctx->fp, " %s\n", unit); |
1563 | } | 1606 | } |
1564 | 1607 | ||
1565 | static void script_new_line(void *ctx) | 1608 | static void script_new_line(struct perf_stat_config *config __maybe_unused, |
1609 | void *ctx) | ||
1566 | { | 1610 | { |
1567 | struct metric_ctx *mctx = ctx; | 1611 | struct metric_ctx *mctx = ctx; |
1568 | 1612 | ||
@@ -1608,7 +1652,7 @@ static void perf_sample__fprint_metric(struct perf_script *script, | |||
1608 | evsel_script(evsel)->val = val; | 1652 | evsel_script(evsel)->val = val; |
1609 | if (evsel_script(evsel->leader)->gnum == evsel->leader->nr_members) { | 1653 | if (evsel_script(evsel->leader)->gnum == evsel->leader->nr_members) { |
1610 | for_each_group_member (ev2, evsel->leader) { | 1654 | for_each_group_member (ev2, evsel->leader) { |
1611 | perf_stat__print_shadow_stats(ev2, | 1655 | perf_stat__print_shadow_stats(&stat_config, ev2, |
1612 | evsel_script(ev2)->val, | 1656 | evsel_script(ev2)->val, |
1613 | sample->cpu, | 1657 | sample->cpu, |
1614 | &ctx, | 1658 | &ctx, |
@@ -1619,6 +1663,47 @@ static void perf_sample__fprint_metric(struct perf_script *script, | |||
1619 | } | 1663 | } |
1620 | } | 1664 | } |
1621 | 1665 | ||
1666 | static bool show_event(struct perf_sample *sample, | ||
1667 | struct perf_evsel *evsel, | ||
1668 | struct thread *thread, | ||
1669 | struct addr_location *al) | ||
1670 | { | ||
1671 | int depth = thread_stack__depth(thread); | ||
1672 | |||
1673 | if (!symbol_conf.graph_function) | ||
1674 | return true; | ||
1675 | |||
1676 | if (thread->filter) { | ||
1677 | if (depth <= thread->filter_entry_depth) { | ||
1678 | thread->filter = false; | ||
1679 | return false; | ||
1680 | } | ||
1681 | return true; | ||
1682 | } else { | ||
1683 | const char *s = symbol_conf.graph_function; | ||
1684 | u64 ip; | ||
1685 | const char *name = resolve_branch_sym(sample, evsel, thread, al, | ||
1686 | &ip); | ||
1687 | unsigned nlen; | ||
1688 | |||
1689 | if (!name) | ||
1690 | return false; | ||
1691 | nlen = strlen(name); | ||
1692 | while (*s) { | ||
1693 | unsigned len = strcspn(s, ","); | ||
1694 | if (nlen == len && !strncmp(name, s, len)) { | ||
1695 | thread->filter = true; | ||
1696 | thread->filter_entry_depth = depth; | ||
1697 | return true; | ||
1698 | } | ||
1699 | s += len; | ||
1700 | if (*s == ',') | ||
1701 | s++; | ||
1702 | } | ||
1703 | return false; | ||
1704 | } | ||
1705 | } | ||
1706 | |||
1622 | static void process_event(struct perf_script *script, | 1707 | static void process_event(struct perf_script *script, |
1623 | struct perf_sample *sample, struct perf_evsel *evsel, | 1708 | struct perf_sample *sample, struct perf_evsel *evsel, |
1624 | struct addr_location *al, | 1709 | struct addr_location *al, |
@@ -1633,6 +1718,9 @@ static void process_event(struct perf_script *script, | |||
1633 | if (output[type].fields == 0) | 1718 | if (output[type].fields == 0) |
1634 | return; | 1719 | return; |
1635 | 1720 | ||
1721 | if (!show_event(sample, evsel, thread, al)) | ||
1722 | return; | ||
1723 | |||
1636 | ++es->samples; | 1724 | ++es->samples; |
1637 | 1725 | ||
1638 | perf_sample__fprintf_start(sample, thread, evsel, | 1726 | perf_sample__fprintf_start(sample, thread, evsel, |
@@ -1710,6 +1798,9 @@ static void process_event(struct perf_script *script, | |||
1710 | 1798 | ||
1711 | if (PRINT_FIELD(METRIC)) | 1799 | if (PRINT_FIELD(METRIC)) |
1712 | perf_sample__fprint_metric(script, thread, evsel, sample, fp); | 1800 | perf_sample__fprint_metric(script, thread, evsel, sample, fp); |
1801 | |||
1802 | if (verbose) | ||
1803 | fflush(fp); | ||
1713 | } | 1804 | } |
1714 | 1805 | ||
1715 | static struct scripting_ops *scripting_ops; | 1806 | static struct scripting_ops *scripting_ops; |
@@ -2489,6 +2580,8 @@ parse: | |||
2489 | output[j].fields &= ~all_output_options[i].field; | 2580 | output[j].fields &= ~all_output_options[i].field; |
2490 | else | 2581 | else |
2491 | output[j].fields |= all_output_options[i].field; | 2582 | output[j].fields |= all_output_options[i].field; |
2583 | output[j].user_set = true; | ||
2584 | output[j].wildcard_set = true; | ||
2492 | } | 2585 | } |
2493 | } | 2586 | } |
2494 | } else { | 2587 | } else { |
@@ -2499,7 +2592,8 @@ parse: | |||
2499 | rc = -EINVAL; | 2592 | rc = -EINVAL; |
2500 | goto out; | 2593 | goto out; |
2501 | } | 2594 | } |
2502 | output[type].fields |= all_output_options[i].field; | 2595 | output[type].user_set = true; |
2596 | output[type].wildcard_set = true; | ||
2503 | } | 2597 | } |
2504 | } | 2598 | } |
2505 | 2599 | ||
@@ -2963,9 +3057,8 @@ static void script__setup_sample_type(struct perf_script *script) | |||
2963 | } | 3057 | } |
2964 | } | 3058 | } |
2965 | 3059 | ||
2966 | static int process_stat_round_event(struct perf_tool *tool __maybe_unused, | 3060 | static int process_stat_round_event(struct perf_session *session, |
2967 | union perf_event *event, | 3061 | union perf_event *event) |
2968 | struct perf_session *session) | ||
2969 | { | 3062 | { |
2970 | struct stat_round_event *round = &event->stat_round; | 3063 | struct stat_round_event *round = &event->stat_round; |
2971 | struct perf_evsel *counter; | 3064 | struct perf_evsel *counter; |
@@ -2979,9 +3072,8 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused, | |||
2979 | return 0; | 3072 | return 0; |
2980 | } | 3073 | } |
2981 | 3074 | ||
2982 | static int process_stat_config_event(struct perf_tool *tool __maybe_unused, | 3075 | static int process_stat_config_event(struct perf_session *session __maybe_unused, |
2983 | union perf_event *event, | 3076 | union perf_event *event) |
2984 | struct perf_session *session __maybe_unused) | ||
2985 | { | 3077 | { |
2986 | perf_event__read_stat_config(&stat_config, &event->stat_config); | 3078 | perf_event__read_stat_config(&stat_config, &event->stat_config); |
2987 | return 0; | 3079 | return 0; |
@@ -3007,10 +3099,10 @@ static int set_maps(struct perf_script *script) | |||
3007 | } | 3099 | } |
3008 | 3100 | ||
3009 | static | 3101 | static |
3010 | int process_thread_map_event(struct perf_tool *tool, | 3102 | int process_thread_map_event(struct perf_session *session, |
3011 | union perf_event *event, | 3103 | union perf_event *event) |
3012 | struct perf_session *session __maybe_unused) | ||
3013 | { | 3104 | { |
3105 | struct perf_tool *tool = session->tool; | ||
3014 | struct perf_script *script = container_of(tool, struct perf_script, tool); | 3106 | struct perf_script *script = container_of(tool, struct perf_script, tool); |
3015 | 3107 | ||
3016 | if (script->threads) { | 3108 | if (script->threads) { |
@@ -3026,10 +3118,10 @@ int process_thread_map_event(struct perf_tool *tool, | |||
3026 | } | 3118 | } |
3027 | 3119 | ||
3028 | static | 3120 | static |
3029 | int process_cpu_map_event(struct perf_tool *tool __maybe_unused, | 3121 | int process_cpu_map_event(struct perf_session *session, |
3030 | union perf_event *event, | 3122 | union perf_event *event) |
3031 | struct perf_session *session __maybe_unused) | ||
3032 | { | 3123 | { |
3124 | struct perf_tool *tool = session->tool; | ||
3033 | struct perf_script *script = container_of(tool, struct perf_script, tool); | 3125 | struct perf_script *script = container_of(tool, struct perf_script, tool); |
3034 | 3126 | ||
3035 | if (script->cpus) { | 3127 | if (script->cpus) { |
@@ -3044,21 +3136,21 @@ int process_cpu_map_event(struct perf_tool *tool __maybe_unused, | |||
3044 | return set_maps(script); | 3136 | return set_maps(script); |
3045 | } | 3137 | } |
3046 | 3138 | ||
3047 | static int process_feature_event(struct perf_tool *tool, | 3139 | static int process_feature_event(struct perf_session *session, |
3048 | union perf_event *event, | 3140 | union perf_event *event) |
3049 | struct perf_session *session) | ||
3050 | { | 3141 | { |
3051 | if (event->feat.feat_id < HEADER_LAST_FEATURE) | 3142 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
3052 | return perf_event__process_feature(tool, event, session); | 3143 | return perf_event__process_feature(session, event); |
3053 | return 0; | 3144 | return 0; |
3054 | } | 3145 | } |
3055 | 3146 | ||
3056 | #ifdef HAVE_AUXTRACE_SUPPORT | 3147 | #ifdef HAVE_AUXTRACE_SUPPORT |
3057 | static int perf_script__process_auxtrace_info(struct perf_tool *tool, | 3148 | static int perf_script__process_auxtrace_info(struct perf_session *session, |
3058 | union perf_event *event, | 3149 | union perf_event *event) |
3059 | struct perf_session *session) | ||
3060 | { | 3150 | { |
3061 | int ret = perf_event__process_auxtrace_info(tool, event, session); | 3151 | struct perf_tool *tool = session->tool; |
3152 | |||
3153 | int ret = perf_event__process_auxtrace_info(session, event); | ||
3062 | 3154 | ||
3063 | if (ret == 0) { | 3155 | if (ret == 0) { |
3064 | struct perf_script *script = container_of(tool, struct perf_script, tool); | 3156 | struct perf_script *script = container_of(tool, struct perf_script, tool); |
@@ -3072,6 +3164,44 @@ static int perf_script__process_auxtrace_info(struct perf_tool *tool, | |||
3072 | #define perf_script__process_auxtrace_info 0 | 3164 | #define perf_script__process_auxtrace_info 0 |
3073 | #endif | 3165 | #endif |
3074 | 3166 | ||
3167 | static int parse_insn_trace(const struct option *opt __maybe_unused, | ||
3168 | const char *str __maybe_unused, | ||
3169 | int unset __maybe_unused) | ||
3170 | { | ||
3171 | parse_output_fields(NULL, "+insn,-event,-period", 0); | ||
3172 | itrace_parse_synth_opts(opt, "i0ns", 0); | ||
3173 | nanosecs = true; | ||
3174 | return 0; | ||
3175 | } | ||
3176 | |||
3177 | static int parse_xed(const struct option *opt __maybe_unused, | ||
3178 | const char *str __maybe_unused, | ||
3179 | int unset __maybe_unused) | ||
3180 | { | ||
3181 | force_pager("xed -F insn: -A -64 | less"); | ||
3182 | return 0; | ||
3183 | } | ||
3184 | |||
3185 | static int parse_call_trace(const struct option *opt __maybe_unused, | ||
3186 | const char *str __maybe_unused, | ||
3187 | int unset __maybe_unused) | ||
3188 | { | ||
3189 | parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent", 0); | ||
3190 | itrace_parse_synth_opts(opt, "cewp", 0); | ||
3191 | nanosecs = true; | ||
3192 | return 0; | ||
3193 | } | ||
3194 | |||
3195 | static int parse_callret_trace(const struct option *opt __maybe_unused, | ||
3196 | const char *str __maybe_unused, | ||
3197 | int unset __maybe_unused) | ||
3198 | { | ||
3199 | parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent,+flags", 0); | ||
3200 | itrace_parse_synth_opts(opt, "crewp", 0); | ||
3201 | nanosecs = true; | ||
3202 | return 0; | ||
3203 | } | ||
3204 | |||
3075 | int cmd_script(int argc, const char **argv) | 3205 | int cmd_script(int argc, const char **argv) |
3076 | { | 3206 | { |
3077 | bool show_full_info = false; | 3207 | bool show_full_info = false; |
@@ -3081,7 +3211,10 @@ int cmd_script(int argc, const char **argv) | |||
3081 | char *rec_script_path = NULL; | 3211 | char *rec_script_path = NULL; |
3082 | char *rep_script_path = NULL; | 3212 | char *rep_script_path = NULL; |
3083 | struct perf_session *session; | 3213 | struct perf_session *session; |
3084 | struct itrace_synth_opts itrace_synth_opts = { .set = false, }; | 3214 | struct itrace_synth_opts itrace_synth_opts = { |
3215 | .set = false, | ||
3216 | .default_no_sample = true, | ||
3217 | }; | ||
3085 | char *script_path = NULL; | 3218 | char *script_path = NULL; |
3086 | const char **__argv; | 3219 | const char **__argv; |
3087 | int i, j, err = 0; | 3220 | int i, j, err = 0; |
@@ -3156,6 +3289,16 @@ int cmd_script(int argc, const char **argv) | |||
3156 | "system-wide collection from all CPUs"), | 3289 | "system-wide collection from all CPUs"), |
3157 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 3290 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
3158 | "only consider these symbols"), | 3291 | "only consider these symbols"), |
3292 | OPT_CALLBACK_OPTARG(0, "insn-trace", &itrace_synth_opts, NULL, NULL, | ||
3293 | "Decode instructions from itrace", parse_insn_trace), | ||
3294 | OPT_CALLBACK_OPTARG(0, "xed", NULL, NULL, NULL, | ||
3295 | "Run xed disassembler on output", parse_xed), | ||
3296 | OPT_CALLBACK_OPTARG(0, "call-trace", &itrace_synth_opts, NULL, NULL, | ||
3297 | "Decode calls from from itrace", parse_call_trace), | ||
3298 | OPT_CALLBACK_OPTARG(0, "call-ret-trace", &itrace_synth_opts, NULL, NULL, | ||
3299 | "Decode calls and returns from itrace", parse_callret_trace), | ||
3300 | OPT_STRING(0, "graph-function", &symbol_conf.graph_function, "symbol[,symbol...]", | ||
3301 | "Only print symbols and callees with --call-trace/--call-ret-trace"), | ||
3159 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", | 3302 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", |
3160 | "Stop display of callgraph at these symbols"), | 3303 | "Stop display of callgraph at these symbols"), |
3161 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 3304 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
@@ -3193,7 +3336,7 @@ int cmd_script(int argc, const char **argv) | |||
3193 | OPT_BOOLEAN(0, "ns", &nanosecs, | 3336 | OPT_BOOLEAN(0, "ns", &nanosecs, |
3194 | "Use 9 decimal places when displaying time"), | 3337 | "Use 9 decimal places when displaying time"), |
3195 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", | 3338 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", |
3196 | "Instruction Tracing options", | 3339 | "Instruction Tracing options\n" ITRACE_HELP, |
3197 | itrace_parse_synth_opts), | 3340 | itrace_parse_synth_opts), |
3198 | OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename, | 3341 | OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename, |
3199 | "Show full source file name path for source lines"), | 3342 | "Show full source file name path for source lines"), |
@@ -3389,8 +3532,10 @@ int cmd_script(int argc, const char **argv) | |||
3389 | exit(-1); | 3532 | exit(-1); |
3390 | } | 3533 | } |
3391 | 3534 | ||
3392 | if (!script_name) | 3535 | if (!script_name) { |
3393 | setup_pager(); | 3536 | setup_pager(); |
3537 | use_browser = 0; | ||
3538 | } | ||
3394 | 3539 | ||
3395 | session = perf_session__new(&data, false, &script.tool); | 3540 | session = perf_session__new(&data, false, &script.tool); |
3396 | if (session == NULL) | 3541 | if (session == NULL) |
@@ -3411,7 +3556,8 @@ int cmd_script(int argc, const char **argv) | |||
3411 | script.session = session; | 3556 | script.session = session; |
3412 | script__setup_sample_type(&script); | 3557 | script__setup_sample_type(&script); |
3413 | 3558 | ||
3414 | if (output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) | 3559 | if ((output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) || |
3560 | symbol_conf.graph_function) | ||
3415 | itrace_synth_opts.thread_stack = true; | 3561 | itrace_synth_opts.thread_stack = true; |
3416 | 3562 | ||
3417 | session->itrace_synth_opts = &itrace_synth_opts; | 3563 | session->itrace_synth_opts = &itrace_synth_opts; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d097b5b47eb8..a635abfa77b6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -88,8 +88,6 @@ | |||
88 | #include "sane_ctype.h" | 88 | #include "sane_ctype.h" |
89 | 89 | ||
90 | #define DEFAULT_SEPARATOR " " | 90 | #define DEFAULT_SEPARATOR " " |
91 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
92 | #define CNTR_NOT_COUNTED "<not counted>" | ||
93 | #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi" | 91 | #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi" |
94 | 92 | ||
95 | static void print_counters(struct timespec *ts, int argc, const char **argv); | 93 | static void print_counters(struct timespec *ts, int argc, const char **argv); |
@@ -137,54 +135,30 @@ static const char *smi_cost_attrs = { | |||
137 | 135 | ||
138 | static struct perf_evlist *evsel_list; | 136 | static struct perf_evlist *evsel_list; |
139 | 137 | ||
140 | static struct rblist metric_events; | ||
141 | |||
142 | static struct target target = { | 138 | static struct target target = { |
143 | .uid = UINT_MAX, | 139 | .uid = UINT_MAX, |
144 | }; | 140 | }; |
145 | 141 | ||
146 | typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); | ||
147 | |||
148 | #define METRIC_ONLY_LEN 20 | 142 | #define METRIC_ONLY_LEN 20 |
149 | 143 | ||
150 | static int run_count = 1; | ||
151 | static bool no_inherit = false; | ||
152 | static volatile pid_t child_pid = -1; | 144 | static volatile pid_t child_pid = -1; |
153 | static bool null_run = false; | ||
154 | static int detailed_run = 0; | 145 | static int detailed_run = 0; |
155 | static bool transaction_run; | 146 | static bool transaction_run; |
156 | static bool topdown_run = false; | 147 | static bool topdown_run = false; |
157 | static bool smi_cost = false; | 148 | static bool smi_cost = false; |
158 | static bool smi_reset = false; | 149 | static bool smi_reset = false; |
159 | static bool big_num = true; | ||
160 | static int big_num_opt = -1; | 150 | static int big_num_opt = -1; |
161 | static const char *csv_sep = NULL; | ||
162 | static bool csv_output = false; | ||
163 | static bool group = false; | 151 | static bool group = false; |
164 | static const char *pre_cmd = NULL; | 152 | static const char *pre_cmd = NULL; |
165 | static const char *post_cmd = NULL; | 153 | static const char *post_cmd = NULL; |
166 | static bool sync_run = false; | 154 | static bool sync_run = false; |
167 | static unsigned int initial_delay = 0; | ||
168 | static unsigned int unit_width = 4; /* strlen("unit") */ | ||
169 | static bool forever = false; | 155 | static bool forever = false; |
170 | static bool metric_only = false; | ||
171 | static bool force_metric_only = false; | 156 | static bool force_metric_only = false; |
172 | static bool no_merge = false; | ||
173 | static bool walltime_run_table = false; | ||
174 | static struct timespec ref_time; | 157 | static struct timespec ref_time; |
175 | static struct cpu_map *aggr_map; | ||
176 | static aggr_get_id_t aggr_get_id; | ||
177 | static bool append_file; | 158 | static bool append_file; |
178 | static bool interval_count; | 159 | static bool interval_count; |
179 | static bool interval_clear; | ||
180 | static const char *output_name; | 160 | static const char *output_name; |
181 | static int output_fd; | 161 | static int output_fd; |
182 | static int print_free_counters_hint; | ||
183 | static int print_mixed_hw_group_error; | ||
184 | static u64 *walltime_run; | ||
185 | static bool ru_display = false; | ||
186 | static struct rusage ru_data; | ||
187 | static unsigned int metric_only_len = METRIC_ONLY_LEN; | ||
188 | 162 | ||
189 | struct perf_stat { | 163 | struct perf_stat { |
190 | bool record; | 164 | bool record; |
@@ -204,15 +178,15 @@ static struct perf_stat perf_stat; | |||
204 | static volatile int done = 0; | 178 | static volatile int done = 0; |
205 | 179 | ||
206 | static struct perf_stat_config stat_config = { | 180 | static struct perf_stat_config stat_config = { |
207 | .aggr_mode = AGGR_GLOBAL, | 181 | .aggr_mode = AGGR_GLOBAL, |
208 | .scale = true, | 182 | .scale = true, |
183 | .unit_width = 4, /* strlen("unit") */ | ||
184 | .run_count = 1, | ||
185 | .metric_only_len = METRIC_ONLY_LEN, | ||
186 | .walltime_nsecs_stats = &walltime_nsecs_stats, | ||
187 | .big_num = true, | ||
209 | }; | 188 | }; |
210 | 189 | ||
211 | static bool is_duration_time(struct perf_evsel *evsel) | ||
212 | { | ||
213 | return !strcmp(evsel->name, "duration_time"); | ||
214 | } | ||
215 | |||
216 | static inline void diff_timespec(struct timespec *r, struct timespec *a, | 190 | static inline void diff_timespec(struct timespec *r, struct timespec *a, |
217 | struct timespec *b) | 191 | struct timespec *b) |
218 | { | 192 | { |
@@ -236,66 +210,6 @@ static void perf_stat__reset_stats(void) | |||
236 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); | 210 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); |
237 | } | 211 | } |
238 | 212 | ||
239 | static int create_perf_stat_counter(struct perf_evsel *evsel) | ||
240 | { | ||
241 | struct perf_event_attr *attr = &evsel->attr; | ||
242 | struct perf_evsel *leader = evsel->leader; | ||
243 | |||
244 | if (stat_config.scale) { | ||
245 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
246 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * The event is part of non trivial group, let's enable | ||
251 | * the group read (for leader) and ID retrieval for all | ||
252 | * members. | ||
253 | */ | ||
254 | if (leader->nr_members > 1) | ||
255 | attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP; | ||
256 | |||
257 | attr->inherit = !no_inherit; | ||
258 | |||
259 | /* | ||
260 | * Some events get initialized with sample_(period/type) set, | ||
261 | * like tracepoints. Clear it up for counting. | ||
262 | */ | ||
263 | attr->sample_period = 0; | ||
264 | |||
265 | /* | ||
266 | * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless | ||
267 | * while avoiding that older tools show confusing messages. | ||
268 | * | ||
269 | * However for pipe sessions we need to keep it zero, | ||
270 | * because script's perf_evsel__check_attr is triggered | ||
271 | * by attr->sample_type != 0, and we can't run it on | ||
272 | * stat sessions. | ||
273 | */ | ||
274 | if (!(STAT_RECORD && perf_stat.data.is_pipe)) | ||
275 | attr->sample_type = PERF_SAMPLE_IDENTIFIER; | ||
276 | |||
277 | /* | ||
278 | * Disabling all counters initially, they will be enabled | ||
279 | * either manually by us or by kernel via enable_on_exec | ||
280 | * set later. | ||
281 | */ | ||
282 | if (perf_evsel__is_group_leader(evsel)) { | ||
283 | attr->disabled = 1; | ||
284 | |||
285 | /* | ||
286 | * In case of initial_delay we enable tracee | ||
287 | * events manually. | ||
288 | */ | ||
289 | if (target__none(&target) && !initial_delay) | ||
290 | attr->enable_on_exec = 1; | ||
291 | } | ||
292 | |||
293 | if (target__has_cpu(&target) && !target__has_per_thread(&target)) | ||
294 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
295 | |||
296 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); | ||
297 | } | ||
298 | |||
299 | static int process_synthesized_event(struct perf_tool *tool __maybe_unused, | 213 | static int process_synthesized_event(struct perf_tool *tool __maybe_unused, |
300 | union perf_event *event, | 214 | union perf_event *event, |
301 | struct perf_sample *sample __maybe_unused, | 215 | struct perf_sample *sample __maybe_unused, |
@@ -428,15 +342,15 @@ static void process_interval(void) | |||
428 | 342 | ||
429 | static void enable_counters(void) | 343 | static void enable_counters(void) |
430 | { | 344 | { |
431 | if (initial_delay) | 345 | if (stat_config.initial_delay) |
432 | usleep(initial_delay * USEC_PER_MSEC); | 346 | usleep(stat_config.initial_delay * USEC_PER_MSEC); |
433 | 347 | ||
434 | /* | 348 | /* |
435 | * We need to enable counters only if: | 349 | * We need to enable counters only if: |
436 | * - we don't have tracee (attaching to task or cpu) | 350 | * - we don't have tracee (attaching to task or cpu) |
437 | * - we have initial delay configured | 351 | * - we have initial delay configured |
438 | */ | 352 | */ |
439 | if (!target__none(&target) || initial_delay) | 353 | if (!target__none(&target) || stat_config.initial_delay) |
440 | perf_evlist__enable(evsel_list); | 354 | perf_evlist__enable(evsel_list); |
441 | } | 355 | } |
442 | 356 | ||
@@ -464,109 +378,31 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf | |||
464 | workload_exec_errno = info->si_value.sival_int; | 378 | workload_exec_errno = info->si_value.sival_int; |
465 | } | 379 | } |
466 | 380 | ||
467 | static int perf_stat_synthesize_config(bool is_pipe) | ||
468 | { | ||
469 | int err; | ||
470 | |||
471 | if (is_pipe) { | ||
472 | err = perf_event__synthesize_attrs(NULL, perf_stat.session, | ||
473 | process_synthesized_event); | ||
474 | if (err < 0) { | ||
475 | pr_err("Couldn't synthesize attrs.\n"); | ||
476 | return err; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | err = perf_event__synthesize_extra_attr(NULL, | ||
481 | evsel_list, | ||
482 | process_synthesized_event, | ||
483 | is_pipe); | ||
484 | |||
485 | err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, | ||
486 | process_synthesized_event, | ||
487 | NULL); | ||
488 | if (err < 0) { | ||
489 | pr_err("Couldn't synthesize thread map.\n"); | ||
490 | return err; | ||
491 | } | ||
492 | |||
493 | err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, | ||
494 | process_synthesized_event, NULL); | ||
495 | if (err < 0) { | ||
496 | pr_err("Couldn't synthesize thread map.\n"); | ||
497 | return err; | ||
498 | } | ||
499 | |||
500 | err = perf_event__synthesize_stat_config(NULL, &stat_config, | ||
501 | process_synthesized_event, NULL); | ||
502 | if (err < 0) { | ||
503 | pr_err("Couldn't synthesize config.\n"); | ||
504 | return err; | ||
505 | } | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
511 | |||
512 | static int __store_counter_ids(struct perf_evsel *counter) | ||
513 | { | ||
514 | int cpu, thread; | ||
515 | |||
516 | for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) { | ||
517 | for (thread = 0; thread < xyarray__max_y(counter->fd); | ||
518 | thread++) { | ||
519 | int fd = FD(counter, cpu, thread); | ||
520 | |||
521 | if (perf_evlist__id_add_fd(evsel_list, counter, | ||
522 | cpu, thread, fd) < 0) | ||
523 | return -1; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static int store_counter_ids(struct perf_evsel *counter) | ||
531 | { | ||
532 | struct cpu_map *cpus = counter->cpus; | ||
533 | struct thread_map *threads = counter->threads; | ||
534 | |||
535 | if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | return __store_counter_ids(counter); | ||
539 | } | ||
540 | |||
541 | static bool perf_evsel__should_store_id(struct perf_evsel *counter) | 381 | static bool perf_evsel__should_store_id(struct perf_evsel *counter) |
542 | { | 382 | { |
543 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; | 383 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; |
544 | } | 384 | } |
545 | 385 | ||
546 | static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel) | 386 | static bool is_target_alive(struct target *_target, |
387 | struct thread_map *threads) | ||
547 | { | 388 | { |
548 | struct perf_evsel *c2, *leader; | 389 | struct stat st; |
549 | bool is_open = true; | 390 | int i; |
550 | 391 | ||
551 | leader = evsel->leader; | 392 | if (!target__has_task(_target)) |
552 | pr_debug("Weak group for %s/%d failed\n", | 393 | return true; |
553 | leader->name, leader->nr_members); | ||
554 | 394 | ||
555 | /* | 395 | for (i = 0; i < threads->nr; i++) { |
556 | * for_each_group_member doesn't work here because it doesn't | 396 | char path[PATH_MAX]; |
557 | * include the first entry. | 397 | |
558 | */ | 398 | scnprintf(path, PATH_MAX, "%s/%d", procfs__mountpoint(), |
559 | evlist__for_each_entry(evsel_list, c2) { | 399 | threads->map[i].pid); |
560 | if (c2 == evsel) | 400 | |
561 | is_open = false; | 401 | if (!stat(path, &st)) |
562 | if (c2->leader == leader) { | 402 | return true; |
563 | if (is_open) | ||
564 | perf_evsel__close(c2); | ||
565 | c2->leader = c2; | ||
566 | c2->nr_members = 0; | ||
567 | } | ||
568 | } | 403 | } |
569 | return leader; | 404 | |
405 | return false; | ||
570 | } | 406 | } |
571 | 407 | ||
572 | static int __run_perf_stat(int argc, const char **argv, int run_idx) | 408 | static int __run_perf_stat(int argc, const char **argv, int run_idx) |
@@ -609,13 +445,13 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) | |||
609 | 445 | ||
610 | evlist__for_each_entry(evsel_list, counter) { | 446 | evlist__for_each_entry(evsel_list, counter) { |
611 | try_again: | 447 | try_again: |
612 | if (create_perf_stat_counter(counter) < 0) { | 448 | if (create_perf_stat_counter(counter, &stat_config, &target) < 0) { |
613 | 449 | ||
614 | /* Weak group failed. Reset the group. */ | 450 | /* Weak group failed. Reset the group. */ |
615 | if ((errno == EINVAL || errno == EBADF) && | 451 | if ((errno == EINVAL || errno == EBADF) && |
616 | counter->leader != counter && | 452 | counter->leader != counter && |
617 | counter->weak_group) { | 453 | counter->weak_group) { |
618 | counter = perf_evsel__reset_weak_group(counter); | 454 | counter = perf_evlist__reset_weak_group(evsel_list, counter); |
619 | goto try_again; | 455 | goto try_again; |
620 | } | 456 | } |
621 | 457 | ||
@@ -664,11 +500,11 @@ try_again: | |||
664 | counter->supported = true; | 500 | counter->supported = true; |
665 | 501 | ||
666 | l = strlen(counter->unit); | 502 | l = strlen(counter->unit); |
667 | if (l > unit_width) | 503 | if (l > stat_config.unit_width) |
668 | unit_width = l; | 504 | stat_config.unit_width = l; |
669 | 505 | ||
670 | if (perf_evsel__should_store_id(counter) && | 506 | if (perf_evsel__should_store_id(counter) && |
671 | store_counter_ids(counter)) | 507 | perf_evsel__store_ids(counter, evsel_list)) |
672 | return -1; | 508 | return -1; |
673 | } | 509 | } |
674 | 510 | ||
@@ -699,7 +535,8 @@ try_again: | |||
699 | if (err < 0) | 535 | if (err < 0) |
700 | return err; | 536 | return err; |
701 | 537 | ||
702 | err = perf_stat_synthesize_config(is_pipe); | 538 | err = perf_stat_synthesize_config(&stat_config, NULL, evsel_list, |
539 | process_synthesized_event, is_pipe); | ||
703 | if (err < 0) | 540 | if (err < 0) |
704 | return err; | 541 | return err; |
705 | } | 542 | } |
@@ -724,7 +561,7 @@ try_again: | |||
724 | break; | 561 | break; |
725 | } | 562 | } |
726 | } | 563 | } |
727 | wait4(child_pid, &status, 0, &ru_data); | 564 | wait4(child_pid, &status, 0, &stat_config.ru_data); |
728 | 565 | ||
729 | if (workload_exec_errno) { | 566 | if (workload_exec_errno) { |
730 | const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); | 567 | const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); |
@@ -738,6 +575,8 @@ try_again: | |||
738 | enable_counters(); | 575 | enable_counters(); |
739 | while (!done) { | 576 | while (!done) { |
740 | nanosleep(&ts, NULL); | 577 | nanosleep(&ts, NULL); |
578 | if (!is_target_alive(&target, evsel_list->threads)) | ||
579 | break; | ||
741 | if (timeout) | 580 | if (timeout) |
742 | break; | 581 | break; |
743 | if (interval) { | 582 | if (interval) { |
@@ -752,8 +591,8 @@ try_again: | |||
752 | 591 | ||
753 | t1 = rdclock(); | 592 | t1 = rdclock(); |
754 | 593 | ||
755 | if (walltime_run_table) | 594 | if (stat_config.walltime_run_table) |
756 | walltime_run[run_idx] = t1 - t0; | 595 | stat_config.walltime_run[run_idx] = t1 - t0; |
757 | 596 | ||
758 | update_stats(&walltime_nsecs_stats, t1 - t0); | 597 | update_stats(&walltime_nsecs_stats, t1 - t0); |
759 | 598 | ||
@@ -795,1105 +634,14 @@ static int run_perf_stat(int argc, const char **argv, int run_idx) | |||
795 | return ret; | 634 | return ret; |
796 | } | 635 | } |
797 | 636 | ||
798 | static void print_running(u64 run, u64 ena) | ||
799 | { | ||
800 | if (csv_output) { | ||
801 | fprintf(stat_config.output, "%s%" PRIu64 "%s%.2f", | ||
802 | csv_sep, | ||
803 | run, | ||
804 | csv_sep, | ||
805 | ena ? 100.0 * run / ena : 100.0); | ||
806 | } else if (run != ena) { | ||
807 | fprintf(stat_config.output, " (%.2f%%)", 100.0 * run / ena); | ||
808 | } | ||
809 | } | ||
810 | |||
811 | static void print_noise_pct(double total, double avg) | ||
812 | { | ||
813 | double pct = rel_stddev_stats(total, avg); | ||
814 | |||
815 | if (csv_output) | ||
816 | fprintf(stat_config.output, "%s%.2f%%", csv_sep, pct); | ||
817 | else if (pct) | ||
818 | fprintf(stat_config.output, " ( +-%6.2f%% )", pct); | ||
819 | } | ||
820 | |||
821 | static void print_noise(struct perf_evsel *evsel, double avg) | ||
822 | { | ||
823 | struct perf_stat_evsel *ps; | ||
824 | |||
825 | if (run_count == 1) | ||
826 | return; | ||
827 | |||
828 | ps = evsel->stats; | ||
829 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | ||
830 | } | ||
831 | |||
832 | static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | ||
833 | { | ||
834 | switch (stat_config.aggr_mode) { | ||
835 | case AGGR_CORE: | ||
836 | fprintf(stat_config.output, "S%d-C%*d%s%*d%s", | ||
837 | cpu_map__id_to_socket(id), | ||
838 | csv_output ? 0 : -8, | ||
839 | cpu_map__id_to_cpu(id), | ||
840 | csv_sep, | ||
841 | csv_output ? 0 : 4, | ||
842 | nr, | ||
843 | csv_sep); | ||
844 | break; | ||
845 | case AGGR_SOCKET: | ||
846 | fprintf(stat_config.output, "S%*d%s%*d%s", | ||
847 | csv_output ? 0 : -5, | ||
848 | id, | ||
849 | csv_sep, | ||
850 | csv_output ? 0 : 4, | ||
851 | nr, | ||
852 | csv_sep); | ||
853 | break; | ||
854 | case AGGR_NONE: | ||
855 | fprintf(stat_config.output, "CPU%*d%s", | ||
856 | csv_output ? 0 : -4, | ||
857 | perf_evsel__cpus(evsel)->map[id], csv_sep); | ||
858 | break; | ||
859 | case AGGR_THREAD: | ||
860 | fprintf(stat_config.output, "%*s-%*d%s", | ||
861 | csv_output ? 0 : 16, | ||
862 | thread_map__comm(evsel->threads, id), | ||
863 | csv_output ? 0 : -8, | ||
864 | thread_map__pid(evsel->threads, id), | ||
865 | csv_sep); | ||
866 | break; | ||
867 | case AGGR_GLOBAL: | ||
868 | case AGGR_UNSET: | ||
869 | default: | ||
870 | break; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | struct outstate { | ||
875 | FILE *fh; | ||
876 | bool newline; | ||
877 | const char *prefix; | ||
878 | int nfields; | ||
879 | int id, nr; | ||
880 | struct perf_evsel *evsel; | ||
881 | }; | ||
882 | |||
883 | #define METRIC_LEN 35 | ||
884 | |||
885 | static void new_line_std(void *ctx) | ||
886 | { | ||
887 | struct outstate *os = ctx; | ||
888 | |||
889 | os->newline = true; | ||
890 | } | ||
891 | |||
892 | static void do_new_line_std(struct outstate *os) | ||
893 | { | ||
894 | fputc('\n', os->fh); | ||
895 | fputs(os->prefix, os->fh); | ||
896 | aggr_printout(os->evsel, os->id, os->nr); | ||
897 | if (stat_config.aggr_mode == AGGR_NONE) | ||
898 | fprintf(os->fh, " "); | ||
899 | fprintf(os->fh, " "); | ||
900 | } | ||
901 | |||
902 | static void print_metric_std(void *ctx, const char *color, const char *fmt, | ||
903 | const char *unit, double val) | ||
904 | { | ||
905 | struct outstate *os = ctx; | ||
906 | FILE *out = os->fh; | ||
907 | int n; | ||
908 | bool newline = os->newline; | ||
909 | |||
910 | os->newline = false; | ||
911 | |||
912 | if (unit == NULL || fmt == NULL) { | ||
913 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
914 | return; | ||
915 | } | ||
916 | |||
917 | if (newline) | ||
918 | do_new_line_std(os); | ||
919 | |||
920 | n = fprintf(out, " # "); | ||
921 | if (color) | ||
922 | n += color_fprintf(out, color, fmt, val); | ||
923 | else | ||
924 | n += fprintf(out, fmt, val); | ||
925 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
926 | } | ||
927 | |||
928 | static void new_line_csv(void *ctx) | ||
929 | { | ||
930 | struct outstate *os = ctx; | ||
931 | int i; | ||
932 | |||
933 | fputc('\n', os->fh); | ||
934 | if (os->prefix) | ||
935 | fprintf(os->fh, "%s%s", os->prefix, csv_sep); | ||
936 | aggr_printout(os->evsel, os->id, os->nr); | ||
937 | for (i = 0; i < os->nfields; i++) | ||
938 | fputs(csv_sep, os->fh); | ||
939 | } | ||
940 | |||
941 | static void print_metric_csv(void *ctx, | ||
942 | const char *color __maybe_unused, | ||
943 | const char *fmt, const char *unit, double val) | ||
944 | { | ||
945 | struct outstate *os = ctx; | ||
946 | FILE *out = os->fh; | ||
947 | char buf[64], *vals, *ends; | ||
948 | |||
949 | if (unit == NULL || fmt == NULL) { | ||
950 | fprintf(out, "%s%s", csv_sep, csv_sep); | ||
951 | return; | ||
952 | } | ||
953 | snprintf(buf, sizeof(buf), fmt, val); | ||
954 | ends = vals = ltrim(buf); | ||
955 | while (isdigit(*ends) || *ends == '.') | ||
956 | ends++; | ||
957 | *ends = 0; | ||
958 | while (isspace(*unit)) | ||
959 | unit++; | ||
960 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); | ||
961 | } | ||
962 | |||
963 | /* Filter out some columns that don't work well in metrics only mode */ | ||
964 | |||
965 | static bool valid_only_metric(const char *unit) | ||
966 | { | ||
967 | if (!unit) | ||
968 | return false; | ||
969 | if (strstr(unit, "/sec") || | ||
970 | strstr(unit, "hz") || | ||
971 | strstr(unit, "Hz") || | ||
972 | strstr(unit, "CPUs utilized")) | ||
973 | return false; | ||
974 | return true; | ||
975 | } | ||
976 | |||
977 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
978 | const char *unit) | ||
979 | { | ||
980 | if (!strncmp(unit, "of all", 6)) { | ||
981 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
982 | unit); | ||
983 | return buf; | ||
984 | } | ||
985 | return unit; | ||
986 | } | ||
987 | |||
988 | static void print_metric_only(void *ctx, const char *color, const char *fmt, | ||
989 | const char *unit, double val) | ||
990 | { | ||
991 | struct outstate *os = ctx; | ||
992 | FILE *out = os->fh; | ||
993 | char buf[1024], str[1024]; | ||
994 | unsigned mlen = metric_only_len; | ||
995 | |||
996 | if (!valid_only_metric(unit)) | ||
997 | return; | ||
998 | unit = fixunit(buf, os->evsel, unit); | ||
999 | if (mlen < strlen(unit)) | ||
1000 | mlen = strlen(unit) + 1; | ||
1001 | |||
1002 | if (color) | ||
1003 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | ||
1004 | |||
1005 | color_snprintf(str, sizeof(str), color ?: "", fmt, val); | ||
1006 | fprintf(out, "%*s ", mlen, str); | ||
1007 | } | ||
1008 | |||
1009 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, | ||
1010 | const char *fmt, | ||
1011 | const char *unit, double val) | ||
1012 | { | ||
1013 | struct outstate *os = ctx; | ||
1014 | FILE *out = os->fh; | ||
1015 | char buf[64], *vals, *ends; | ||
1016 | char tbuf[1024]; | ||
1017 | |||
1018 | if (!valid_only_metric(unit)) | ||
1019 | return; | ||
1020 | unit = fixunit(tbuf, os->evsel, unit); | ||
1021 | snprintf(buf, sizeof buf, fmt, val); | ||
1022 | ends = vals = ltrim(buf); | ||
1023 | while (isdigit(*ends) || *ends == '.') | ||
1024 | ends++; | ||
1025 | *ends = 0; | ||
1026 | fprintf(out, "%s%s", vals, csv_sep); | ||
1027 | } | ||
1028 | |||
1029 | static void new_line_metric(void *ctx __maybe_unused) | ||
1030 | { | ||
1031 | } | ||
1032 | |||
1033 | static void print_metric_header(void *ctx, const char *color __maybe_unused, | ||
1034 | const char *fmt __maybe_unused, | ||
1035 | const char *unit, double val __maybe_unused) | ||
1036 | { | ||
1037 | struct outstate *os = ctx; | ||
1038 | char tbuf[1024]; | ||
1039 | |||
1040 | if (!valid_only_metric(unit)) | ||
1041 | return; | ||
1042 | unit = fixunit(tbuf, os->evsel, unit); | ||
1043 | if (csv_output) | ||
1044 | fprintf(os->fh, "%s%s", unit, csv_sep); | ||
1045 | else | ||
1046 | fprintf(os->fh, "%*s ", metric_only_len, unit); | ||
1047 | } | ||
1048 | |||
1049 | static int first_shadow_cpu(struct perf_evsel *evsel, int id) | ||
1050 | { | ||
1051 | int i; | ||
1052 | |||
1053 | if (!aggr_get_id) | ||
1054 | return 0; | ||
1055 | |||
1056 | if (stat_config.aggr_mode == AGGR_NONE) | ||
1057 | return id; | ||
1058 | |||
1059 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
1060 | return 0; | ||
1061 | |||
1062 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
1063 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
1064 | |||
1065 | if (aggr_get_id(evsel_list->cpus, cpu2) == id) | ||
1066 | return cpu2; | ||
1067 | } | ||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | ||
1072 | { | ||
1073 | FILE *output = stat_config.output; | ||
1074 | double sc = evsel->scale; | ||
1075 | const char *fmt; | ||
1076 | |||
1077 | if (csv_output) { | ||
1078 | fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; | ||
1079 | } else { | ||
1080 | if (big_num) | ||
1081 | fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s"; | ||
1082 | else | ||
1083 | fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s"; | ||
1084 | } | ||
1085 | |||
1086 | aggr_printout(evsel, id, nr); | ||
1087 | |||
1088 | fprintf(output, fmt, avg, csv_sep); | ||
1089 | |||
1090 | if (evsel->unit) | ||
1091 | fprintf(output, "%-*s%s", | ||
1092 | csv_output ? 0 : unit_width, | ||
1093 | evsel->unit, csv_sep); | ||
1094 | |||
1095 | fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel)); | ||
1096 | |||
1097 | if (evsel->cgrp) | ||
1098 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | ||
1099 | } | ||
1100 | |||
1101 | static bool is_mixed_hw_group(struct perf_evsel *counter) | ||
1102 | { | ||
1103 | struct perf_evlist *evlist = counter->evlist; | ||
1104 | u32 pmu_type = counter->attr.type; | ||
1105 | struct perf_evsel *pos; | ||
1106 | |||
1107 | if (counter->nr_members < 2) | ||
1108 | return false; | ||
1109 | |||
1110 | evlist__for_each_entry(evlist, pos) { | ||
1111 | /* software events can be part of any hardware group */ | ||
1112 | if (pos->attr.type == PERF_TYPE_SOFTWARE) | ||
1113 | continue; | ||
1114 | if (pmu_type == PERF_TYPE_SOFTWARE) { | ||
1115 | pmu_type = pos->attr.type; | ||
1116 | continue; | ||
1117 | } | ||
1118 | if (pmu_type != pos->attr.type) | ||
1119 | return true; | ||
1120 | } | ||
1121 | |||
1122 | return false; | ||
1123 | } | ||
1124 | |||
1125 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, | ||
1126 | char *prefix, u64 run, u64 ena, double noise, | ||
1127 | struct runtime_stat *st) | ||
1128 | { | ||
1129 | struct perf_stat_output_ctx out; | ||
1130 | struct outstate os = { | ||
1131 | .fh = stat_config.output, | ||
1132 | .prefix = prefix ? prefix : "", | ||
1133 | .id = id, | ||
1134 | .nr = nr, | ||
1135 | .evsel = counter, | ||
1136 | }; | ||
1137 | print_metric_t pm = print_metric_std; | ||
1138 | void (*nl)(void *); | ||
1139 | |||
1140 | if (metric_only) { | ||
1141 | nl = new_line_metric; | ||
1142 | if (csv_output) | ||
1143 | pm = print_metric_only_csv; | ||
1144 | else | ||
1145 | pm = print_metric_only; | ||
1146 | } else | ||
1147 | nl = new_line_std; | ||
1148 | |||
1149 | if (csv_output && !metric_only) { | ||
1150 | static int aggr_fields[] = { | ||
1151 | [AGGR_GLOBAL] = 0, | ||
1152 | [AGGR_THREAD] = 1, | ||
1153 | [AGGR_NONE] = 1, | ||
1154 | [AGGR_SOCKET] = 2, | ||
1155 | [AGGR_CORE] = 2, | ||
1156 | }; | ||
1157 | |||
1158 | pm = print_metric_csv; | ||
1159 | nl = new_line_csv; | ||
1160 | os.nfields = 3; | ||
1161 | os.nfields += aggr_fields[stat_config.aggr_mode]; | ||
1162 | if (counter->cgrp) | ||
1163 | os.nfields++; | ||
1164 | } | ||
1165 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
1166 | if (metric_only) { | ||
1167 | pm(&os, NULL, "", "", 0); | ||
1168 | return; | ||
1169 | } | ||
1170 | aggr_printout(counter, id, nr); | ||
1171 | |||
1172 | fprintf(stat_config.output, "%*s%s", | ||
1173 | csv_output ? 0 : 18, | ||
1174 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
1175 | csv_sep); | ||
1176 | |||
1177 | if (counter->supported) { | ||
1178 | print_free_counters_hint = 1; | ||
1179 | if (is_mixed_hw_group(counter)) | ||
1180 | print_mixed_hw_group_error = 1; | ||
1181 | } | ||
1182 | |||
1183 | fprintf(stat_config.output, "%-*s%s", | ||
1184 | csv_output ? 0 : unit_width, | ||
1185 | counter->unit, csv_sep); | ||
1186 | |||
1187 | fprintf(stat_config.output, "%*s", | ||
1188 | csv_output ? 0 : -25, | ||
1189 | perf_evsel__name(counter)); | ||
1190 | |||
1191 | if (counter->cgrp) | ||
1192 | fprintf(stat_config.output, "%s%s", | ||
1193 | csv_sep, counter->cgrp->name); | ||
1194 | |||
1195 | if (!csv_output) | ||
1196 | pm(&os, NULL, NULL, "", 0); | ||
1197 | print_noise(counter, noise); | ||
1198 | print_running(run, ena); | ||
1199 | if (csv_output) | ||
1200 | pm(&os, NULL, NULL, "", 0); | ||
1201 | return; | ||
1202 | } | ||
1203 | |||
1204 | if (!metric_only) | ||
1205 | abs_printout(id, nr, counter, uval); | ||
1206 | |||
1207 | out.print_metric = pm; | ||
1208 | out.new_line = nl; | ||
1209 | out.ctx = &os; | ||
1210 | out.force_header = false; | ||
1211 | |||
1212 | if (csv_output && !metric_only) { | ||
1213 | print_noise(counter, noise); | ||
1214 | print_running(run, ena); | ||
1215 | } | ||
1216 | |||
1217 | perf_stat__print_shadow_stats(counter, uval, | ||
1218 | first_shadow_cpu(counter, id), | ||
1219 | &out, &metric_events, st); | ||
1220 | if (!csv_output && !metric_only) { | ||
1221 | print_noise(counter, noise); | ||
1222 | print_running(run, ena); | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | static void aggr_update_shadow(void) | ||
1227 | { | ||
1228 | int cpu, s2, id, s; | ||
1229 | u64 val; | ||
1230 | struct perf_evsel *counter; | ||
1231 | |||
1232 | for (s = 0; s < aggr_map->nr; s++) { | ||
1233 | id = aggr_map->map[s]; | ||
1234 | evlist__for_each_entry(evsel_list, counter) { | ||
1235 | val = 0; | ||
1236 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
1237 | s2 = aggr_get_id(evsel_list->cpus, cpu); | ||
1238 | if (s2 != id) | ||
1239 | continue; | ||
1240 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
1241 | } | ||
1242 | perf_stat__update_shadow_stats(counter, val, | ||
1243 | first_shadow_cpu(counter, id), | ||
1244 | &rt_stat); | ||
1245 | } | ||
1246 | } | ||
1247 | } | ||
1248 | |||
1249 | static void uniquify_event_name(struct perf_evsel *counter) | ||
1250 | { | ||
1251 | char *new_name; | ||
1252 | char *config; | ||
1253 | |||
1254 | if (counter->uniquified_name || | ||
1255 | !counter->pmu_name || !strncmp(counter->name, counter->pmu_name, | ||
1256 | strlen(counter->pmu_name))) | ||
1257 | return; | ||
1258 | |||
1259 | config = strchr(counter->name, '/'); | ||
1260 | if (config) { | ||
1261 | if (asprintf(&new_name, | ||
1262 | "%s%s", counter->pmu_name, config) > 0) { | ||
1263 | free(counter->name); | ||
1264 | counter->name = new_name; | ||
1265 | } | ||
1266 | } else { | ||
1267 | if (asprintf(&new_name, | ||
1268 | "%s [%s]", counter->name, counter->pmu_name) > 0) { | ||
1269 | free(counter->name); | ||
1270 | counter->name = new_name; | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | counter->uniquified_name = true; | ||
1275 | } | ||
1276 | |||
1277 | static void collect_all_aliases(struct perf_evsel *counter, | ||
1278 | void (*cb)(struct perf_evsel *counter, void *data, | ||
1279 | bool first), | ||
1280 | void *data) | ||
1281 | { | ||
1282 | struct perf_evsel *alias; | ||
1283 | |||
1284 | alias = list_prepare_entry(counter, &(evsel_list->entries), node); | ||
1285 | list_for_each_entry_continue (alias, &evsel_list->entries, node) { | ||
1286 | if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || | ||
1287 | alias->scale != counter->scale || | ||
1288 | alias->cgrp != counter->cgrp || | ||
1289 | strcmp(alias->unit, counter->unit) || | ||
1290 | perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter)) | ||
1291 | break; | ||
1292 | alias->merged_stat = true; | ||
1293 | cb(alias, data, false); | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | static bool collect_data(struct perf_evsel *counter, | ||
1298 | void (*cb)(struct perf_evsel *counter, void *data, | ||
1299 | bool first), | ||
1300 | void *data) | ||
1301 | { | ||
1302 | if (counter->merged_stat) | ||
1303 | return false; | ||
1304 | cb(counter, data, true); | ||
1305 | if (no_merge) | ||
1306 | uniquify_event_name(counter); | ||
1307 | else if (counter->auto_merge_stats) | ||
1308 | collect_all_aliases(counter, cb, data); | ||
1309 | return true; | ||
1310 | } | ||
1311 | |||
1312 | struct aggr_data { | ||
1313 | u64 ena, run, val; | ||
1314 | int id; | ||
1315 | int nr; | ||
1316 | int cpu; | ||
1317 | }; | ||
1318 | |||
1319 | static void aggr_cb(struct perf_evsel *counter, void *data, bool first) | ||
1320 | { | ||
1321 | struct aggr_data *ad = data; | ||
1322 | int cpu, s2; | ||
1323 | |||
1324 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
1325 | struct perf_counts_values *counts; | ||
1326 | |||
1327 | s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); | ||
1328 | if (s2 != ad->id) | ||
1329 | continue; | ||
1330 | if (first) | ||
1331 | ad->nr++; | ||
1332 | counts = perf_counts(counter->counts, cpu, 0); | ||
1333 | /* | ||
1334 | * When any result is bad, make them all to give | ||
1335 | * consistent output in interval mode. | ||
1336 | */ | ||
1337 | if (counts->ena == 0 || counts->run == 0 || | ||
1338 | counter->counts->scaled == -1) { | ||
1339 | ad->ena = 0; | ||
1340 | ad->run = 0; | ||
1341 | break; | ||
1342 | } | ||
1343 | ad->val += counts->val; | ||
1344 | ad->ena += counts->ena; | ||
1345 | ad->run += counts->run; | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | static void print_aggr(char *prefix) | ||
1350 | { | ||
1351 | FILE *output = stat_config.output; | ||
1352 | struct perf_evsel *counter; | ||
1353 | int s, id, nr; | ||
1354 | double uval; | ||
1355 | u64 ena, run, val; | ||
1356 | bool first; | ||
1357 | |||
1358 | if (!(aggr_map || aggr_get_id)) | ||
1359 | return; | ||
1360 | |||
1361 | aggr_update_shadow(); | ||
1362 | |||
1363 | /* | ||
1364 | * With metric_only everything is on a single line. | ||
1365 | * Without each counter has its own line. | ||
1366 | */ | ||
1367 | for (s = 0; s < aggr_map->nr; s++) { | ||
1368 | struct aggr_data ad; | ||
1369 | if (prefix && metric_only) | ||
1370 | fprintf(output, "%s", prefix); | ||
1371 | |||
1372 | ad.id = id = aggr_map->map[s]; | ||
1373 | first = true; | ||
1374 | evlist__for_each_entry(evsel_list, counter) { | ||
1375 | if (is_duration_time(counter)) | ||
1376 | continue; | ||
1377 | |||
1378 | ad.val = ad.ena = ad.run = 0; | ||
1379 | ad.nr = 0; | ||
1380 | if (!collect_data(counter, aggr_cb, &ad)) | ||
1381 | continue; | ||
1382 | nr = ad.nr; | ||
1383 | ena = ad.ena; | ||
1384 | run = ad.run; | ||
1385 | val = ad.val; | ||
1386 | if (first && metric_only) { | ||
1387 | first = false; | ||
1388 | aggr_printout(counter, id, nr); | ||
1389 | } | ||
1390 | if (prefix && !metric_only) | ||
1391 | fprintf(output, "%s", prefix); | ||
1392 | |||
1393 | uval = val * counter->scale; | ||
1394 | printout(id, nr, counter, uval, prefix, run, ena, 1.0, | ||
1395 | &rt_stat); | ||
1396 | if (!metric_only) | ||
1397 | fputc('\n', output); | ||
1398 | } | ||
1399 | if (metric_only) | ||
1400 | fputc('\n', output); | ||
1401 | } | ||
1402 | } | ||
1403 | |||
1404 | static int cmp_val(const void *a, const void *b) | ||
1405 | { | ||
1406 | return ((struct perf_aggr_thread_value *)b)->val - | ||
1407 | ((struct perf_aggr_thread_value *)a)->val; | ||
1408 | } | ||
1409 | |||
1410 | static struct perf_aggr_thread_value *sort_aggr_thread( | ||
1411 | struct perf_evsel *counter, | ||
1412 | int nthreads, int ncpus, | ||
1413 | int *ret) | ||
1414 | { | ||
1415 | int cpu, thread, i = 0; | ||
1416 | double uval; | ||
1417 | struct perf_aggr_thread_value *buf; | ||
1418 | |||
1419 | buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); | ||
1420 | if (!buf) | ||
1421 | return NULL; | ||
1422 | |||
1423 | for (thread = 0; thread < nthreads; thread++) { | ||
1424 | u64 ena = 0, run = 0, val = 0; | ||
1425 | |||
1426 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
1427 | val += perf_counts(counter->counts, cpu, thread)->val; | ||
1428 | ena += perf_counts(counter->counts, cpu, thread)->ena; | ||
1429 | run += perf_counts(counter->counts, cpu, thread)->run; | ||
1430 | } | ||
1431 | |||
1432 | uval = val * counter->scale; | ||
1433 | |||
1434 | /* | ||
1435 | * Skip value 0 when enabling --per-thread globally, | ||
1436 | * otherwise too many 0 output. | ||
1437 | */ | ||
1438 | if (uval == 0.0 && target__has_per_thread(&target)) | ||
1439 | continue; | ||
1440 | |||
1441 | buf[i].counter = counter; | ||
1442 | buf[i].id = thread; | ||
1443 | buf[i].uval = uval; | ||
1444 | buf[i].val = val; | ||
1445 | buf[i].run = run; | ||
1446 | buf[i].ena = ena; | ||
1447 | i++; | ||
1448 | } | ||
1449 | |||
1450 | qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); | ||
1451 | |||
1452 | if (ret) | ||
1453 | *ret = i; | ||
1454 | |||
1455 | return buf; | ||
1456 | } | ||
1457 | |||
1458 | static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | ||
1459 | { | ||
1460 | FILE *output = stat_config.output; | ||
1461 | int nthreads = thread_map__nr(counter->threads); | ||
1462 | int ncpus = cpu_map__nr(counter->cpus); | ||
1463 | int thread, sorted_threads, id; | ||
1464 | struct perf_aggr_thread_value *buf; | ||
1465 | |||
1466 | buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads); | ||
1467 | if (!buf) { | ||
1468 | perror("cannot sort aggr thread"); | ||
1469 | return; | ||
1470 | } | ||
1471 | |||
1472 | for (thread = 0; thread < sorted_threads; thread++) { | ||
1473 | if (prefix) | ||
1474 | fprintf(output, "%s", prefix); | ||
1475 | |||
1476 | id = buf[thread].id; | ||
1477 | if (stat_config.stats) | ||
1478 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
1479 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
1480 | &stat_config.stats[id]); | ||
1481 | else | ||
1482 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
1483 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
1484 | &rt_stat); | ||
1485 | fputc('\n', output); | ||
1486 | } | ||
1487 | |||
1488 | free(buf); | ||
1489 | } | ||
1490 | |||
1491 | struct caggr_data { | ||
1492 | double avg, avg_enabled, avg_running; | ||
1493 | }; | ||
1494 | |||
1495 | static void counter_aggr_cb(struct perf_evsel *counter, void *data, | ||
1496 | bool first __maybe_unused) | ||
1497 | { | ||
1498 | struct caggr_data *cd = data; | ||
1499 | struct perf_stat_evsel *ps = counter->stats; | ||
1500 | |||
1501 | cd->avg += avg_stats(&ps->res_stats[0]); | ||
1502 | cd->avg_enabled += avg_stats(&ps->res_stats[1]); | ||
1503 | cd->avg_running += avg_stats(&ps->res_stats[2]); | ||
1504 | } | ||
1505 | |||
1506 | /* | ||
1507 | * Print out the results of a single counter: | ||
1508 | * aggregated counts in system-wide mode | ||
1509 | */ | ||
1510 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | ||
1511 | { | ||
1512 | FILE *output = stat_config.output; | ||
1513 | double uval; | ||
1514 | struct caggr_data cd = { .avg = 0.0 }; | ||
1515 | |||
1516 | if (!collect_data(counter, counter_aggr_cb, &cd)) | ||
1517 | return; | ||
1518 | |||
1519 | if (prefix && !metric_only) | ||
1520 | fprintf(output, "%s", prefix); | ||
1521 | |||
1522 | uval = cd.avg * counter->scale; | ||
1523 | printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, | ||
1524 | cd.avg, &rt_stat); | ||
1525 | if (!metric_only) | ||
1526 | fprintf(output, "\n"); | ||
1527 | } | ||
1528 | |||
1529 | static void counter_cb(struct perf_evsel *counter, void *data, | ||
1530 | bool first __maybe_unused) | ||
1531 | { | ||
1532 | struct aggr_data *ad = data; | ||
1533 | |||
1534 | ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; | ||
1535 | ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; | ||
1536 | ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; | ||
1537 | } | ||
1538 | |||
1539 | /* | ||
1540 | * Print out the results of a single counter: | ||
1541 | * does not use aggregated count in system-wide | ||
1542 | */ | ||
1543 | static void print_counter(struct perf_evsel *counter, char *prefix) | ||
1544 | { | ||
1545 | FILE *output = stat_config.output; | ||
1546 | u64 ena, run, val; | ||
1547 | double uval; | ||
1548 | int cpu; | ||
1549 | |||
1550 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
1551 | struct aggr_data ad = { .cpu = cpu }; | ||
1552 | |||
1553 | if (!collect_data(counter, counter_cb, &ad)) | ||
1554 | return; | ||
1555 | val = ad.val; | ||
1556 | ena = ad.ena; | ||
1557 | run = ad.run; | ||
1558 | |||
1559 | if (prefix) | ||
1560 | fprintf(output, "%s", prefix); | ||
1561 | |||
1562 | uval = val * counter->scale; | ||
1563 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
1564 | &rt_stat); | ||
1565 | |||
1566 | fputc('\n', output); | ||
1567 | } | ||
1568 | } | ||
1569 | |||
1570 | static void print_no_aggr_metric(char *prefix) | ||
1571 | { | ||
1572 | int cpu; | ||
1573 | int nrcpus = 0; | ||
1574 | struct perf_evsel *counter; | ||
1575 | u64 ena, run, val; | ||
1576 | double uval; | ||
1577 | |||
1578 | nrcpus = evsel_list->cpus->nr; | ||
1579 | for (cpu = 0; cpu < nrcpus; cpu++) { | ||
1580 | bool first = true; | ||
1581 | |||
1582 | if (prefix) | ||
1583 | fputs(prefix, stat_config.output); | ||
1584 | evlist__for_each_entry(evsel_list, counter) { | ||
1585 | if (is_duration_time(counter)) | ||
1586 | continue; | ||
1587 | if (first) { | ||
1588 | aggr_printout(counter, cpu, 0); | ||
1589 | first = false; | ||
1590 | } | ||
1591 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
1592 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
1593 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
1594 | |||
1595 | uval = val * counter->scale; | ||
1596 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
1597 | &rt_stat); | ||
1598 | } | ||
1599 | fputc('\n', stat_config.output); | ||
1600 | } | ||
1601 | } | ||
1602 | |||
1603 | static int aggr_header_lens[] = { | ||
1604 | [AGGR_CORE] = 18, | ||
1605 | [AGGR_SOCKET] = 12, | ||
1606 | [AGGR_NONE] = 6, | ||
1607 | [AGGR_THREAD] = 24, | ||
1608 | [AGGR_GLOBAL] = 0, | ||
1609 | }; | ||
1610 | |||
1611 | static const char *aggr_header_csv[] = { | ||
1612 | [AGGR_CORE] = "core,cpus,", | ||
1613 | [AGGR_SOCKET] = "socket,cpus", | ||
1614 | [AGGR_NONE] = "cpu,", | ||
1615 | [AGGR_THREAD] = "comm-pid,", | ||
1616 | [AGGR_GLOBAL] = "" | ||
1617 | }; | ||
1618 | |||
1619 | static void print_metric_headers(const char *prefix, bool no_indent) | ||
1620 | { | ||
1621 | struct perf_stat_output_ctx out; | ||
1622 | struct perf_evsel *counter; | ||
1623 | struct outstate os = { | ||
1624 | .fh = stat_config.output | ||
1625 | }; | ||
1626 | |||
1627 | if (prefix) | ||
1628 | fprintf(stat_config.output, "%s", prefix); | ||
1629 | |||
1630 | if (!csv_output && !no_indent) | ||
1631 | fprintf(stat_config.output, "%*s", | ||
1632 | aggr_header_lens[stat_config.aggr_mode], ""); | ||
1633 | if (csv_output) { | ||
1634 | if (stat_config.interval) | ||
1635 | fputs("time,", stat_config.output); | ||
1636 | fputs(aggr_header_csv[stat_config.aggr_mode], | ||
1637 | stat_config.output); | ||
1638 | } | ||
1639 | |||
1640 | /* Print metrics headers only */ | ||
1641 | evlist__for_each_entry(evsel_list, counter) { | ||
1642 | if (is_duration_time(counter)) | ||
1643 | continue; | ||
1644 | os.evsel = counter; | ||
1645 | out.ctx = &os; | ||
1646 | out.print_metric = print_metric_header; | ||
1647 | out.new_line = new_line_metric; | ||
1648 | out.force_header = true; | ||
1649 | os.evsel = counter; | ||
1650 | perf_stat__print_shadow_stats(counter, 0, | ||
1651 | 0, | ||
1652 | &out, | ||
1653 | &metric_events, | ||
1654 | &rt_stat); | ||
1655 | } | ||
1656 | fputc('\n', stat_config.output); | ||
1657 | } | ||
1658 | |||
1659 | static void print_interval(char *prefix, struct timespec *ts) | ||
1660 | { | ||
1661 | FILE *output = stat_config.output; | ||
1662 | static int num_print_interval; | ||
1663 | |||
1664 | if (interval_clear) | ||
1665 | puts(CONSOLE_CLEAR); | ||
1666 | |||
1667 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | ||
1668 | |||
1669 | if ((num_print_interval == 0 && !csv_output) || interval_clear) { | ||
1670 | switch (stat_config.aggr_mode) { | ||
1671 | case AGGR_SOCKET: | ||
1672 | fprintf(output, "# time socket cpus"); | ||
1673 | if (!metric_only) | ||
1674 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
1675 | break; | ||
1676 | case AGGR_CORE: | ||
1677 | fprintf(output, "# time core cpus"); | ||
1678 | if (!metric_only) | ||
1679 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
1680 | break; | ||
1681 | case AGGR_NONE: | ||
1682 | fprintf(output, "# time CPU "); | ||
1683 | if (!metric_only) | ||
1684 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
1685 | break; | ||
1686 | case AGGR_THREAD: | ||
1687 | fprintf(output, "# time comm-pid"); | ||
1688 | if (!metric_only) | ||
1689 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
1690 | break; | ||
1691 | case AGGR_GLOBAL: | ||
1692 | default: | ||
1693 | fprintf(output, "# time"); | ||
1694 | if (!metric_only) | ||
1695 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
1696 | case AGGR_UNSET: | ||
1697 | break; | ||
1698 | } | ||
1699 | } | ||
1700 | |||
1701 | if ((num_print_interval == 0 || interval_clear) && metric_only) | ||
1702 | print_metric_headers(" ", true); | ||
1703 | if (++num_print_interval == 25) | ||
1704 | num_print_interval = 0; | ||
1705 | } | ||
1706 | |||
1707 | static void print_header(int argc, const char **argv) | ||
1708 | { | ||
1709 | FILE *output = stat_config.output; | ||
1710 | int i; | ||
1711 | |||
1712 | fflush(stdout); | ||
1713 | |||
1714 | if (!csv_output) { | ||
1715 | fprintf(output, "\n"); | ||
1716 | fprintf(output, " Performance counter stats for "); | ||
1717 | if (target.system_wide) | ||
1718 | fprintf(output, "\'system wide"); | ||
1719 | else if (target.cpu_list) | ||
1720 | fprintf(output, "\'CPU(s) %s", target.cpu_list); | ||
1721 | else if (!target__has_task(&target)) { | ||
1722 | fprintf(output, "\'%s", argv ? argv[0] : "pipe"); | ||
1723 | for (i = 1; argv && (i < argc); i++) | ||
1724 | fprintf(output, " %s", argv[i]); | ||
1725 | } else if (target.pid) | ||
1726 | fprintf(output, "process id \'%s", target.pid); | ||
1727 | else | ||
1728 | fprintf(output, "thread id \'%s", target.tid); | ||
1729 | |||
1730 | fprintf(output, "\'"); | ||
1731 | if (run_count > 1) | ||
1732 | fprintf(output, " (%d runs)", run_count); | ||
1733 | fprintf(output, ":\n\n"); | ||
1734 | } | ||
1735 | } | ||
1736 | |||
1737 | static int get_precision(double num) | ||
1738 | { | ||
1739 | if (num > 1) | ||
1740 | return 0; | ||
1741 | |||
1742 | return lround(ceil(-log10(num))); | ||
1743 | } | ||
1744 | |||
1745 | static void print_table(FILE *output, int precision, double avg) | ||
1746 | { | ||
1747 | char tmp[64]; | ||
1748 | int idx, indent = 0; | ||
1749 | |||
1750 | scnprintf(tmp, 64, " %17.*f", precision, avg); | ||
1751 | while (tmp[indent] == ' ') | ||
1752 | indent++; | ||
1753 | |||
1754 | fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); | ||
1755 | |||
1756 | for (idx = 0; idx < run_count; idx++) { | ||
1757 | double run = (double) walltime_run[idx] / NSEC_PER_SEC; | ||
1758 | int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); | ||
1759 | |||
1760 | fprintf(output, " %17.*f (%+.*f) ", | ||
1761 | precision, run, precision, run - avg); | ||
1762 | |||
1763 | for (h = 0; h < n; h++) | ||
1764 | fprintf(output, "#"); | ||
1765 | |||
1766 | fprintf(output, "\n"); | ||
1767 | } | ||
1768 | |||
1769 | fprintf(output, "\n%*s# Final result:\n", indent, ""); | ||
1770 | } | ||
1771 | |||
1772 | static double timeval2double(struct timeval *t) | ||
1773 | { | ||
1774 | return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; | ||
1775 | } | ||
1776 | |||
1777 | static void print_footer(void) | ||
1778 | { | ||
1779 | double avg = avg_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; | ||
1780 | FILE *output = stat_config.output; | ||
1781 | int n; | ||
1782 | |||
1783 | if (!null_run) | ||
1784 | fprintf(output, "\n"); | ||
1785 | |||
1786 | if (run_count == 1) { | ||
1787 | fprintf(output, " %17.9f seconds time elapsed", avg); | ||
1788 | |||
1789 | if (ru_display) { | ||
1790 | double ru_utime = timeval2double(&ru_data.ru_utime); | ||
1791 | double ru_stime = timeval2double(&ru_data.ru_stime); | ||
1792 | |||
1793 | fprintf(output, "\n\n"); | ||
1794 | fprintf(output, " %17.9f seconds user\n", ru_utime); | ||
1795 | fprintf(output, " %17.9f seconds sys\n", ru_stime); | ||
1796 | } | ||
1797 | } else { | ||
1798 | double sd = stddev_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; | ||
1799 | /* | ||
1800 | * Display at most 2 more significant | ||
1801 | * digits than the stddev inaccuracy. | ||
1802 | */ | ||
1803 | int precision = get_precision(sd) + 2; | ||
1804 | |||
1805 | if (walltime_run_table) | ||
1806 | print_table(output, precision, avg); | ||
1807 | |||
1808 | fprintf(output, " %17.*f +- %.*f seconds time elapsed", | ||
1809 | precision, avg, precision, sd); | ||
1810 | |||
1811 | print_noise_pct(sd, avg); | ||
1812 | } | ||
1813 | fprintf(output, "\n\n"); | ||
1814 | |||
1815 | if (print_free_counters_hint && | ||
1816 | sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 && | ||
1817 | n > 0) | ||
1818 | fprintf(output, | ||
1819 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | ||
1820 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | ||
1821 | " perf stat ...\n" | ||
1822 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | ||
1823 | |||
1824 | if (print_mixed_hw_group_error) | ||
1825 | fprintf(output, | ||
1826 | "The events in group usually have to be from " | ||
1827 | "the same PMU. Try reorganizing the group.\n"); | ||
1828 | } | ||
1829 | |||
1830 | static void print_counters(struct timespec *ts, int argc, const char **argv) | 637 | static void print_counters(struct timespec *ts, int argc, const char **argv) |
1831 | { | 638 | { |
1832 | int interval = stat_config.interval; | ||
1833 | struct perf_evsel *counter; | ||
1834 | char buf[64], *prefix = NULL; | ||
1835 | |||
1836 | /* Do not print anything if we record to the pipe. */ | 639 | /* Do not print anything if we record to the pipe. */ |
1837 | if (STAT_RECORD && perf_stat.data.is_pipe) | 640 | if (STAT_RECORD && perf_stat.data.is_pipe) |
1838 | return; | 641 | return; |
1839 | 642 | ||
1840 | if (interval) | 643 | perf_evlist__print_counters(evsel_list, &stat_config, &target, |
1841 | print_interval(prefix = buf, ts); | 644 | ts, argc, argv); |
1842 | else | ||
1843 | print_header(argc, argv); | ||
1844 | |||
1845 | if (metric_only) { | ||
1846 | static int num_print_iv; | ||
1847 | |||
1848 | if (num_print_iv == 0 && !interval) | ||
1849 | print_metric_headers(prefix, false); | ||
1850 | if (num_print_iv++ == 25) | ||
1851 | num_print_iv = 0; | ||
1852 | if (stat_config.aggr_mode == AGGR_GLOBAL && prefix) | ||
1853 | fprintf(stat_config.output, "%s", prefix); | ||
1854 | } | ||
1855 | |||
1856 | switch (stat_config.aggr_mode) { | ||
1857 | case AGGR_CORE: | ||
1858 | case AGGR_SOCKET: | ||
1859 | print_aggr(prefix); | ||
1860 | break; | ||
1861 | case AGGR_THREAD: | ||
1862 | evlist__for_each_entry(evsel_list, counter) { | ||
1863 | if (is_duration_time(counter)) | ||
1864 | continue; | ||
1865 | print_aggr_thread(counter, prefix); | ||
1866 | } | ||
1867 | break; | ||
1868 | case AGGR_GLOBAL: | ||
1869 | evlist__for_each_entry(evsel_list, counter) { | ||
1870 | if (is_duration_time(counter)) | ||
1871 | continue; | ||
1872 | print_counter_aggr(counter, prefix); | ||
1873 | } | ||
1874 | if (metric_only) | ||
1875 | fputc('\n', stat_config.output); | ||
1876 | break; | ||
1877 | case AGGR_NONE: | ||
1878 | if (metric_only) | ||
1879 | print_no_aggr_metric(prefix); | ||
1880 | else { | ||
1881 | evlist__for_each_entry(evsel_list, counter) { | ||
1882 | if (is_duration_time(counter)) | ||
1883 | continue; | ||
1884 | print_counter(counter, prefix); | ||
1885 | } | ||
1886 | } | ||
1887 | break; | ||
1888 | case AGGR_UNSET: | ||
1889 | default: | ||
1890 | break; | ||
1891 | } | ||
1892 | |||
1893 | if (!interval && !csv_output) | ||
1894 | print_footer(); | ||
1895 | |||
1896 | fflush(stat_config.output); | ||
1897 | } | 645 | } |
1898 | 646 | ||
1899 | static volatile int signr = -1; | 647 | static volatile int signr = -1; |
@@ -1950,7 +698,7 @@ static int enable_metric_only(const struct option *opt __maybe_unused, | |||
1950 | const char *s __maybe_unused, int unset) | 698 | const char *s __maybe_unused, int unset) |
1951 | { | 699 | { |
1952 | force_metric_only = true; | 700 | force_metric_only = true; |
1953 | metric_only = !unset; | 701 | stat_config.metric_only = !unset; |
1954 | return 0; | 702 | return 0; |
1955 | } | 703 | } |
1956 | 704 | ||
@@ -1958,7 +706,7 @@ static int parse_metric_groups(const struct option *opt, | |||
1958 | const char *str, | 706 | const char *str, |
1959 | int unset __maybe_unused) | 707 | int unset __maybe_unused) |
1960 | { | 708 | { |
1961 | return metricgroup__parse_groups(opt, str, &metric_events); | 709 | return metricgroup__parse_groups(opt, str, &stat_config.metric_events); |
1962 | } | 710 | } |
1963 | 711 | ||
1964 | static const struct option stat_options[] = { | 712 | static const struct option stat_options[] = { |
@@ -1969,7 +717,7 @@ static const struct option stat_options[] = { | |||
1969 | parse_events_option), | 717 | parse_events_option), |
1970 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 718 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
1971 | "event filter", parse_filter), | 719 | "event filter", parse_filter), |
1972 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 720 | OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, |
1973 | "child tasks do not inherit counters"), | 721 | "child tasks do not inherit counters"), |
1974 | OPT_STRING('p', "pid", &target.pid, "pid", | 722 | OPT_STRING('p', "pid", &target.pid, "pid", |
1975 | "stat events on existing process id"), | 723 | "stat events on existing process id"), |
@@ -1982,11 +730,11 @@ static const struct option stat_options[] = { | |||
1982 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), | 730 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), |
1983 | OPT_INCR('v', "verbose", &verbose, | 731 | OPT_INCR('v', "verbose", &verbose, |
1984 | "be more verbose (show counter open errors, etc)"), | 732 | "be more verbose (show counter open errors, etc)"), |
1985 | OPT_INTEGER('r', "repeat", &run_count, | 733 | OPT_INTEGER('r', "repeat", &stat_config.run_count, |
1986 | "repeat command and print average + stddev (max: 100, forever: 0)"), | 734 | "repeat command and print average + stddev (max: 100, forever: 0)"), |
1987 | OPT_BOOLEAN(0, "table", &walltime_run_table, | 735 | OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, |
1988 | "display details about each run (only with -r option)"), | 736 | "display details about each run (only with -r option)"), |
1989 | OPT_BOOLEAN('n', "null", &null_run, | 737 | OPT_BOOLEAN('n', "null", &stat_config.null_run, |
1990 | "null run - dont start any counters"), | 738 | "null run - dont start any counters"), |
1991 | OPT_INCR('d', "detailed", &detailed_run, | 739 | OPT_INCR('d', "detailed", &detailed_run, |
1992 | "detailed run - start a lot of events"), | 740 | "detailed run - start a lot of events"), |
@@ -1999,8 +747,8 @@ static const struct option stat_options[] = { | |||
1999 | "list of cpus to monitor in system-wide"), | 747 | "list of cpus to monitor in system-wide"), |
2000 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, | 748 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, |
2001 | "disable CPU count aggregation", AGGR_NONE), | 749 | "disable CPU count aggregation", AGGR_NONE), |
2002 | OPT_BOOLEAN(0, "no-merge", &no_merge, "Do not merge identical named events"), | 750 | OPT_BOOLEAN(0, "no-merge", &stat_config.no_merge, "Do not merge identical named events"), |
2003 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 751 | OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", |
2004 | "print counts with custom separator"), | 752 | "print counts with custom separator"), |
2005 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 753 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
2006 | "monitor event in cgroup name only", parse_cgroups), | 754 | "monitor event in cgroup name only", parse_cgroups), |
@@ -2017,7 +765,7 @@ static const struct option stat_options[] = { | |||
2017 | "(overhead is possible for values <= 100ms)"), | 765 | "(overhead is possible for values <= 100ms)"), |
2018 | OPT_INTEGER(0, "interval-count", &stat_config.times, | 766 | OPT_INTEGER(0, "interval-count", &stat_config.times, |
2019 | "print counts for fixed number of times"), | 767 | "print counts for fixed number of times"), |
2020 | OPT_BOOLEAN(0, "interval-clear", &interval_clear, | 768 | OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, |
2021 | "clear screen in between new interval"), | 769 | "clear screen in between new interval"), |
2022 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, | 770 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, |
2023 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), | 771 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), |
@@ -2027,9 +775,9 @@ static const struct option stat_options[] = { | |||
2027 | "aggregate counts per physical processor core", AGGR_CORE), | 775 | "aggregate counts per physical processor core", AGGR_CORE), |
2028 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, | 776 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, |
2029 | "aggregate counts per thread", AGGR_THREAD), | 777 | "aggregate counts per thread", AGGR_THREAD), |
2030 | OPT_UINTEGER('D', "delay", &initial_delay, | 778 | OPT_UINTEGER('D', "delay", &stat_config.initial_delay, |
2031 | "ms to wait before starting measurement after program start"), | 779 | "ms to wait before starting measurement after program start"), |
2032 | OPT_CALLBACK_NOOPT(0, "metric-only", &metric_only, NULL, | 780 | OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, |
2033 | "Only print computed metrics. No raw values", enable_metric_only), | 781 | "Only print computed metrics. No raw values", enable_metric_only), |
2034 | OPT_BOOLEAN(0, "topdown", &topdown_run, | 782 | OPT_BOOLEAN(0, "topdown", &topdown_run, |
2035 | "measure topdown level 1 statistics"), | 783 | "measure topdown level 1 statistics"), |
@@ -2041,12 +789,14 @@ static const struct option stat_options[] = { | |||
2041 | OPT_END() | 789 | OPT_END() |
2042 | }; | 790 | }; |
2043 | 791 | ||
2044 | static int perf_stat__get_socket(struct cpu_map *map, int cpu) | 792 | static int perf_stat__get_socket(struct perf_stat_config *config __maybe_unused, |
793 | struct cpu_map *map, int cpu) | ||
2045 | { | 794 | { |
2046 | return cpu_map__get_socket(map, cpu, NULL); | 795 | return cpu_map__get_socket(map, cpu, NULL); |
2047 | } | 796 | } |
2048 | 797 | ||
2049 | static int perf_stat__get_core(struct cpu_map *map, int cpu) | 798 | static int perf_stat__get_core(struct perf_stat_config *config __maybe_unused, |
799 | struct cpu_map *map, int cpu) | ||
2050 | { | 800 | { |
2051 | return cpu_map__get_core(map, cpu, NULL); | 801 | return cpu_map__get_core(map, cpu, NULL); |
2052 | } | 802 | } |
@@ -2063,9 +813,8 @@ static int cpu_map__get_max(struct cpu_map *map) | |||
2063 | return max; | 813 | return max; |
2064 | } | 814 | } |
2065 | 815 | ||
2066 | static struct cpu_map *cpus_aggr_map; | 816 | static int perf_stat__get_aggr(struct perf_stat_config *config, |
2067 | 817 | aggr_get_id_t get_id, struct cpu_map *map, int idx) | |
2068 | static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int idx) | ||
2069 | { | 818 | { |
2070 | int cpu; | 819 | int cpu; |
2071 | 820 | ||
@@ -2074,20 +823,22 @@ static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int id | |||
2074 | 823 | ||
2075 | cpu = map->map[idx]; | 824 | cpu = map->map[idx]; |
2076 | 825 | ||
2077 | if (cpus_aggr_map->map[cpu] == -1) | 826 | if (config->cpus_aggr_map->map[cpu] == -1) |
2078 | cpus_aggr_map->map[cpu] = get_id(map, idx); | 827 | config->cpus_aggr_map->map[cpu] = get_id(config, map, idx); |
2079 | 828 | ||
2080 | return cpus_aggr_map->map[cpu]; | 829 | return config->cpus_aggr_map->map[cpu]; |
2081 | } | 830 | } |
2082 | 831 | ||
2083 | static int perf_stat__get_socket_cached(struct cpu_map *map, int idx) | 832 | static int perf_stat__get_socket_cached(struct perf_stat_config *config, |
833 | struct cpu_map *map, int idx) | ||
2084 | { | 834 | { |
2085 | return perf_stat__get_aggr(perf_stat__get_socket, map, idx); | 835 | return perf_stat__get_aggr(config, perf_stat__get_socket, map, idx); |
2086 | } | 836 | } |
2087 | 837 | ||
2088 | static int perf_stat__get_core_cached(struct cpu_map *map, int idx) | 838 | static int perf_stat__get_core_cached(struct perf_stat_config *config, |
839 | struct cpu_map *map, int idx) | ||
2089 | { | 840 | { |
2090 | return perf_stat__get_aggr(perf_stat__get_core, map, idx); | 841 | return perf_stat__get_aggr(config, perf_stat__get_core, map, idx); |
2091 | } | 842 | } |
2092 | 843 | ||
2093 | static int perf_stat_init_aggr_mode(void) | 844 | static int perf_stat_init_aggr_mode(void) |
@@ -2096,18 +847,18 @@ static int perf_stat_init_aggr_mode(void) | |||
2096 | 847 | ||
2097 | switch (stat_config.aggr_mode) { | 848 | switch (stat_config.aggr_mode) { |
2098 | case AGGR_SOCKET: | 849 | case AGGR_SOCKET: |
2099 | if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { | 850 | if (cpu_map__build_socket_map(evsel_list->cpus, &stat_config.aggr_map)) { |
2100 | perror("cannot build socket map"); | 851 | perror("cannot build socket map"); |
2101 | return -1; | 852 | return -1; |
2102 | } | 853 | } |
2103 | aggr_get_id = perf_stat__get_socket_cached; | 854 | stat_config.aggr_get_id = perf_stat__get_socket_cached; |
2104 | break; | 855 | break; |
2105 | case AGGR_CORE: | 856 | case AGGR_CORE: |
2106 | if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { | 857 | if (cpu_map__build_core_map(evsel_list->cpus, &stat_config.aggr_map)) { |
2107 | perror("cannot build core map"); | 858 | perror("cannot build core map"); |
2108 | return -1; | 859 | return -1; |
2109 | } | 860 | } |
2110 | aggr_get_id = perf_stat__get_core_cached; | 861 | stat_config.aggr_get_id = perf_stat__get_core_cached; |
2111 | break; | 862 | break; |
2112 | case AGGR_NONE: | 863 | case AGGR_NONE: |
2113 | case AGGR_GLOBAL: | 864 | case AGGR_GLOBAL: |
@@ -2123,16 +874,16 @@ static int perf_stat_init_aggr_mode(void) | |||
2123 | * the aggregation translate cpumap. | 874 | * the aggregation translate cpumap. |
2124 | */ | 875 | */ |
2125 | nr = cpu_map__get_max(evsel_list->cpus); | 876 | nr = cpu_map__get_max(evsel_list->cpus); |
2126 | cpus_aggr_map = cpu_map__empty_new(nr + 1); | 877 | stat_config.cpus_aggr_map = cpu_map__empty_new(nr + 1); |
2127 | return cpus_aggr_map ? 0 : -ENOMEM; | 878 | return stat_config.cpus_aggr_map ? 0 : -ENOMEM; |
2128 | } | 879 | } |
2129 | 880 | ||
2130 | static void perf_stat__exit_aggr_mode(void) | 881 | static void perf_stat__exit_aggr_mode(void) |
2131 | { | 882 | { |
2132 | cpu_map__put(aggr_map); | 883 | cpu_map__put(stat_config.aggr_map); |
2133 | cpu_map__put(cpus_aggr_map); | 884 | cpu_map__put(stat_config.cpus_aggr_map); |
2134 | aggr_map = NULL; | 885 | stat_config.aggr_map = NULL; |
2135 | cpus_aggr_map = NULL; | 886 | stat_config.cpus_aggr_map = NULL; |
2136 | } | 887 | } |
2137 | 888 | ||
2138 | static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) | 889 | static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) |
@@ -2190,12 +941,14 @@ static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, | |||
2190 | return cpu_map__build_map(cpus, corep, perf_env__get_core, env); | 941 | return cpu_map__build_map(cpus, corep, perf_env__get_core, env); |
2191 | } | 942 | } |
2192 | 943 | ||
2193 | static int perf_stat__get_socket_file(struct cpu_map *map, int idx) | 944 | static int perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused, |
945 | struct cpu_map *map, int idx) | ||
2194 | { | 946 | { |
2195 | return perf_env__get_socket(map, idx, &perf_stat.session->header.env); | 947 | return perf_env__get_socket(map, idx, &perf_stat.session->header.env); |
2196 | } | 948 | } |
2197 | 949 | ||
2198 | static int perf_stat__get_core_file(struct cpu_map *map, int idx) | 950 | static int perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused, |
951 | struct cpu_map *map, int idx) | ||
2199 | { | 952 | { |
2200 | return perf_env__get_core(map, idx, &perf_stat.session->header.env); | 953 | return perf_env__get_core(map, idx, &perf_stat.session->header.env); |
2201 | } | 954 | } |
@@ -2206,18 +959,18 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st) | |||
2206 | 959 | ||
2207 | switch (stat_config.aggr_mode) { | 960 | switch (stat_config.aggr_mode) { |
2208 | case AGGR_SOCKET: | 961 | case AGGR_SOCKET: |
2209 | if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { | 962 | if (perf_env__build_socket_map(env, evsel_list->cpus, &stat_config.aggr_map)) { |
2210 | perror("cannot build socket map"); | 963 | perror("cannot build socket map"); |
2211 | return -1; | 964 | return -1; |
2212 | } | 965 | } |
2213 | aggr_get_id = perf_stat__get_socket_file; | 966 | stat_config.aggr_get_id = perf_stat__get_socket_file; |
2214 | break; | 967 | break; |
2215 | case AGGR_CORE: | 968 | case AGGR_CORE: |
2216 | if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { | 969 | if (perf_env__build_core_map(env, evsel_list->cpus, &stat_config.aggr_map)) { |
2217 | perror("cannot build core map"); | 970 | perror("cannot build core map"); |
2218 | return -1; | 971 | return -1; |
2219 | } | 972 | } |
2220 | aggr_get_id = perf_stat__get_core_file; | 973 | stat_config.aggr_get_id = perf_stat__get_core_file; |
2221 | break; | 974 | break; |
2222 | case AGGR_NONE: | 975 | case AGGR_NONE: |
2223 | case AGGR_GLOBAL: | 976 | case AGGR_GLOBAL: |
@@ -2401,7 +1154,7 @@ static int add_default_attributes(void) | |||
2401 | struct parse_events_error errinfo; | 1154 | struct parse_events_error errinfo; |
2402 | 1155 | ||
2403 | /* Set attrs if no event is selected and !null_run: */ | 1156 | /* Set attrs if no event is selected and !null_run: */ |
2404 | if (null_run) | 1157 | if (stat_config.null_run) |
2405 | return 0; | 1158 | return 0; |
2406 | 1159 | ||
2407 | if (transaction_run) { | 1160 | if (transaction_run) { |
@@ -2414,7 +1167,7 @@ static int add_default_attributes(void) | |||
2414 | struct option opt = { .value = &evsel_list }; | 1167 | struct option opt = { .value = &evsel_list }; |
2415 | 1168 | ||
2416 | return metricgroup__parse_groups(&opt, "transaction", | 1169 | return metricgroup__parse_groups(&opt, "transaction", |
2417 | &metric_events); | 1170 | &stat_config.metric_events); |
2418 | } | 1171 | } |
2419 | 1172 | ||
2420 | if (pmu_have_event("cpu", "cycles-ct") && | 1173 | if (pmu_have_event("cpu", "cycles-ct") && |
@@ -2452,7 +1205,7 @@ static int add_default_attributes(void) | |||
2452 | if (pmu_have_event("msr", "aperf") && | 1205 | if (pmu_have_event("msr", "aperf") && |
2453 | pmu_have_event("msr", "smi")) { | 1206 | pmu_have_event("msr", "smi")) { |
2454 | if (!force_metric_only) | 1207 | if (!force_metric_only) |
2455 | metric_only = true; | 1208 | stat_config.metric_only = true; |
2456 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); | 1209 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); |
2457 | } else { | 1210 | } else { |
2458 | fprintf(stderr, "To measure SMI cost, it needs " | 1211 | fprintf(stderr, "To measure SMI cost, it needs " |
@@ -2483,7 +1236,7 @@ static int add_default_attributes(void) | |||
2483 | } | 1236 | } |
2484 | 1237 | ||
2485 | if (!force_metric_only) | 1238 | if (!force_metric_only) |
2486 | metric_only = true; | 1239 | stat_config.metric_only = true; |
2487 | if (topdown_filter_events(topdown_attrs, &str, | 1240 | if (topdown_filter_events(topdown_attrs, &str, |
2488 | arch_topdown_check_group(&warn)) < 0) { | 1241 | arch_topdown_check_group(&warn)) < 0) { |
2489 | pr_err("Out of memory\n"); | 1242 | pr_err("Out of memory\n"); |
@@ -2580,7 +1333,7 @@ static int __cmd_record(int argc, const char **argv) | |||
2580 | if (output_name) | 1333 | if (output_name) |
2581 | data->file.path = output_name; | 1334 | data->file.path = output_name; |
2582 | 1335 | ||
2583 | if (run_count != 1 || forever) { | 1336 | if (stat_config.run_count != 1 || forever) { |
2584 | pr_err("Cannot use -r option with perf stat record.\n"); | 1337 | pr_err("Cannot use -r option with perf stat record.\n"); |
2585 | return -1; | 1338 | return -1; |
2586 | } | 1339 | } |
@@ -2599,9 +1352,8 @@ static int __cmd_record(int argc, const char **argv) | |||
2599 | return argc; | 1352 | return argc; |
2600 | } | 1353 | } |
2601 | 1354 | ||
2602 | static int process_stat_round_event(struct perf_tool *tool __maybe_unused, | 1355 | static int process_stat_round_event(struct perf_session *session, |
2603 | union perf_event *event, | 1356 | union perf_event *event) |
2604 | struct perf_session *session) | ||
2605 | { | 1357 | { |
2606 | struct stat_round_event *stat_round = &event->stat_round; | 1358 | struct stat_round_event *stat_round = &event->stat_round; |
2607 | struct perf_evsel *counter; | 1359 | struct perf_evsel *counter; |
@@ -2626,10 +1378,10 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused, | |||
2626 | } | 1378 | } |
2627 | 1379 | ||
2628 | static | 1380 | static |
2629 | int process_stat_config_event(struct perf_tool *tool, | 1381 | int process_stat_config_event(struct perf_session *session, |
2630 | union perf_event *event, | 1382 | union perf_event *event) |
2631 | struct perf_session *session __maybe_unused) | ||
2632 | { | 1383 | { |
1384 | struct perf_tool *tool = session->tool; | ||
2633 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); | 1385 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
2634 | 1386 | ||
2635 | perf_event__read_stat_config(&stat_config, &event->stat_config); | 1387 | perf_event__read_stat_config(&stat_config, &event->stat_config); |
@@ -2669,10 +1421,10 @@ static int set_maps(struct perf_stat *st) | |||
2669 | } | 1421 | } |
2670 | 1422 | ||
2671 | static | 1423 | static |
2672 | int process_thread_map_event(struct perf_tool *tool, | 1424 | int process_thread_map_event(struct perf_session *session, |
2673 | union perf_event *event, | 1425 | union perf_event *event) |
2674 | struct perf_session *session __maybe_unused) | ||
2675 | { | 1426 | { |
1427 | struct perf_tool *tool = session->tool; | ||
2676 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); | 1428 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
2677 | 1429 | ||
2678 | if (st->threads) { | 1430 | if (st->threads) { |
@@ -2688,10 +1440,10 @@ int process_thread_map_event(struct perf_tool *tool, | |||
2688 | } | 1440 | } |
2689 | 1441 | ||
2690 | static | 1442 | static |
2691 | int process_cpu_map_event(struct perf_tool *tool, | 1443 | int process_cpu_map_event(struct perf_session *session, |
2692 | union perf_event *event, | 1444 | union perf_event *event) |
2693 | struct perf_session *session __maybe_unused) | ||
2694 | { | 1445 | { |
1446 | struct perf_tool *tool = session->tool; | ||
2695 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); | 1447 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
2696 | struct cpu_map *cpus; | 1448 | struct cpu_map *cpus; |
2697 | 1449 | ||
@@ -2853,12 +1605,12 @@ int cmd_stat(int argc, const char **argv) | |||
2853 | perf_stat__collect_metric_expr(evsel_list); | 1605 | perf_stat__collect_metric_expr(evsel_list); |
2854 | perf_stat__init_shadow_stats(); | 1606 | perf_stat__init_shadow_stats(); |
2855 | 1607 | ||
2856 | if (csv_sep) { | 1608 | if (stat_config.csv_sep) { |
2857 | csv_output = true; | 1609 | stat_config.csv_output = true; |
2858 | if (!strcmp(csv_sep, "\\t")) | 1610 | if (!strcmp(stat_config.csv_sep, "\\t")) |
2859 | csv_sep = "\t"; | 1611 | stat_config.csv_sep = "\t"; |
2860 | } else | 1612 | } else |
2861 | csv_sep = DEFAULT_SEPARATOR; | 1613 | stat_config.csv_sep = DEFAULT_SEPARATOR; |
2862 | 1614 | ||
2863 | if (argc && !strncmp(argv[0], "rec", 3)) { | 1615 | if (argc && !strncmp(argv[0], "rec", 3)) { |
2864 | argc = __cmd_record(argc, argv); | 1616 | argc = __cmd_record(argc, argv); |
@@ -2883,17 +1635,17 @@ int cmd_stat(int argc, const char **argv) | |||
2883 | goto out; | 1635 | goto out; |
2884 | } | 1636 | } |
2885 | 1637 | ||
2886 | if (metric_only && stat_config.aggr_mode == AGGR_THREAD) { | 1638 | if (stat_config.metric_only && stat_config.aggr_mode == AGGR_THREAD) { |
2887 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); | 1639 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); |
2888 | goto out; | 1640 | goto out; |
2889 | } | 1641 | } |
2890 | 1642 | ||
2891 | if (metric_only && run_count > 1) { | 1643 | if (stat_config.metric_only && stat_config.run_count > 1) { |
2892 | fprintf(stderr, "--metric-only is not supported with -r\n"); | 1644 | fprintf(stderr, "--metric-only is not supported with -r\n"); |
2893 | goto out; | 1645 | goto out; |
2894 | } | 1646 | } |
2895 | 1647 | ||
2896 | if (walltime_run_table && run_count <= 1) { | 1648 | if (stat_config.walltime_run_table && stat_config.run_count <= 1) { |
2897 | fprintf(stderr, "--table is only supported with -r\n"); | 1649 | fprintf(stderr, "--table is only supported with -r\n"); |
2898 | parse_options_usage(stat_usage, stat_options, "r", 1); | 1650 | parse_options_usage(stat_usage, stat_options, "r", 1); |
2899 | parse_options_usage(NULL, stat_options, "table", 0); | 1651 | parse_options_usage(NULL, stat_options, "table", 0); |
@@ -2931,7 +1683,7 @@ int cmd_stat(int argc, const char **argv) | |||
2931 | /* | 1683 | /* |
2932 | * let the spreadsheet do the pretty-printing | 1684 | * let the spreadsheet do the pretty-printing |
2933 | */ | 1685 | */ |
2934 | if (csv_output) { | 1686 | if (stat_config.csv_output) { |
2935 | /* User explicitly passed -B? */ | 1687 | /* User explicitly passed -B? */ |
2936 | if (big_num_opt == 1) { | 1688 | if (big_num_opt == 1) { |
2937 | fprintf(stderr, "-B option not supported with -x\n"); | 1689 | fprintf(stderr, "-B option not supported with -x\n"); |
@@ -2939,9 +1691,9 @@ int cmd_stat(int argc, const char **argv) | |||
2939 | parse_options_usage(NULL, stat_options, "x", 1); | 1691 | parse_options_usage(NULL, stat_options, "x", 1); |
2940 | goto out; | 1692 | goto out; |
2941 | } else /* Nope, so disable big number formatting */ | 1693 | } else /* Nope, so disable big number formatting */ |
2942 | big_num = false; | 1694 | stat_config.big_num = false; |
2943 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | 1695 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
2944 | big_num = false; | 1696 | stat_config.big_num = false; |
2945 | 1697 | ||
2946 | setup_system_wide(argc); | 1698 | setup_system_wide(argc); |
2947 | 1699 | ||
@@ -2949,21 +1701,21 @@ int cmd_stat(int argc, const char **argv) | |||
2949 | * Display user/system times only for single | 1701 | * Display user/system times only for single |
2950 | * run and when there's specified tracee. | 1702 | * run and when there's specified tracee. |
2951 | */ | 1703 | */ |
2952 | if ((run_count == 1) && target__none(&target)) | 1704 | if ((stat_config.run_count == 1) && target__none(&target)) |
2953 | ru_display = true; | 1705 | stat_config.ru_display = true; |
2954 | 1706 | ||
2955 | if (run_count < 0) { | 1707 | if (stat_config.run_count < 0) { |
2956 | pr_err("Run count must be a positive number\n"); | 1708 | pr_err("Run count must be a positive number\n"); |
2957 | parse_options_usage(stat_usage, stat_options, "r", 1); | 1709 | parse_options_usage(stat_usage, stat_options, "r", 1); |
2958 | goto out; | 1710 | goto out; |
2959 | } else if (run_count == 0) { | 1711 | } else if (stat_config.run_count == 0) { |
2960 | forever = true; | 1712 | forever = true; |
2961 | run_count = 1; | 1713 | stat_config.run_count = 1; |
2962 | } | 1714 | } |
2963 | 1715 | ||
2964 | if (walltime_run_table) { | 1716 | if (stat_config.walltime_run_table) { |
2965 | walltime_run = zalloc(run_count * sizeof(walltime_run[0])); | 1717 | stat_config.walltime_run = zalloc(stat_config.run_count * sizeof(stat_config.walltime_run[0])); |
2966 | if (!walltime_run) { | 1718 | if (!stat_config.walltime_run) { |
2967 | pr_err("failed to setup -r option"); | 1719 | pr_err("failed to setup -r option"); |
2968 | goto out; | 1720 | goto out; |
2969 | } | 1721 | } |
@@ -3066,6 +1818,17 @@ int cmd_stat(int argc, const char **argv) | |||
3066 | goto out; | 1818 | goto out; |
3067 | 1819 | ||
3068 | /* | 1820 | /* |
1821 | * Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless | ||
1822 | * while avoiding that older tools show confusing messages. | ||
1823 | * | ||
1824 | * However for pipe sessions we need to keep it zero, | ||
1825 | * because script's perf_evsel__check_attr is triggered | ||
1826 | * by attr->sample_type != 0, and we can't run it on | ||
1827 | * stat sessions. | ||
1828 | */ | ||
1829 | stat_config.identifier = !(STAT_RECORD && perf_stat.data.is_pipe); | ||
1830 | |||
1831 | /* | ||
3069 | * We dont want to block the signals - that would cause | 1832 | * We dont want to block the signals - that would cause |
3070 | * child tasks to inherit that and Ctrl-C would not work. | 1833 | * child tasks to inherit that and Ctrl-C would not work. |
3071 | * What we want is for Ctrl-C to work in the exec()-ed | 1834 | * What we want is for Ctrl-C to work in the exec()-ed |
@@ -3079,8 +1842,8 @@ int cmd_stat(int argc, const char **argv) | |||
3079 | signal(SIGABRT, skip_signal); | 1842 | signal(SIGABRT, skip_signal); |
3080 | 1843 | ||
3081 | status = 0; | 1844 | status = 0; |
3082 | for (run_idx = 0; forever || run_idx < run_count; run_idx++) { | 1845 | for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) { |
3083 | if (run_count != 1 && verbose > 0) | 1846 | if (stat_config.run_count != 1 && verbose > 0) |
3084 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1847 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
3085 | run_idx + 1); | 1848 | run_idx + 1); |
3086 | 1849 | ||
@@ -3132,7 +1895,7 @@ int cmd_stat(int argc, const char **argv) | |||
3132 | perf_stat__exit_aggr_mode(); | 1895 | perf_stat__exit_aggr_mode(); |
3133 | perf_evlist__free_stats(evsel_list); | 1896 | perf_evlist__free_stats(evsel_list); |
3134 | out: | 1897 | out: |
3135 | free(walltime_run); | 1898 | free(stat_config.walltime_run); |
3136 | 1899 | ||
3137 | if (smi_cost && smi_reset) | 1900 | if (smi_cost && smi_reset) |
3138 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); | 1901 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d21d8751e749..aa0c73e57924 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -1134,11 +1134,6 @@ static int __cmd_top(struct perf_top *top) | |||
1134 | if (!target__none(&opts->target)) | 1134 | if (!target__none(&opts->target)) |
1135 | perf_evlist__enable(top->evlist); | 1135 | perf_evlist__enable(top->evlist); |
1136 | 1136 | ||
1137 | /* Wait for a minimal set of events before starting the snapshot */ | ||
1138 | perf_evlist__poll(top->evlist, 100); | ||
1139 | |||
1140 | perf_top__mmap_read(top); | ||
1141 | |||
1142 | ret = -1; | 1137 | ret = -1; |
1143 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 1138 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
1144 | display_thread), top)) { | 1139 | display_thread), top)) { |
@@ -1156,6 +1151,11 @@ static int __cmd_top(struct perf_top *top) | |||
1156 | } | 1151 | } |
1157 | } | 1152 | } |
1158 | 1153 | ||
1154 | /* Wait for a minimal set of events before starting the snapshot */ | ||
1155 | perf_evlist__poll(top->evlist, 100); | ||
1156 | |||
1157 | perf_top__mmap_read(top); | ||
1158 | |||
1159 | while (!done) { | 1159 | while (!done) { |
1160 | u64 hits = top->samples; | 1160 | u64 hits = top->samples; |
1161 | 1161 | ||
@@ -1257,7 +1257,14 @@ int cmd_top(int argc, const char **argv) | |||
1257 | .uses_mmap = true, | 1257 | .uses_mmap = true, |
1258 | }, | 1258 | }, |
1259 | .proc_map_timeout = 500, | 1259 | .proc_map_timeout = 500, |
1260 | .overwrite = 1, | 1260 | /* |
1261 | * FIXME: This will lose PERF_RECORD_MMAP and other metadata | ||
1262 | * when we pause, fix that and reenable. Probably using a | ||
1263 | * separate evlist with a dummy event, i.e. a non-overwrite | ||
1264 | * ring buffer just for metadata events, while PERF_RECORD_SAMPLE | ||
1265 | * stays in overwrite mode. -acme | ||
1266 | * */ | ||
1267 | .overwrite = 0, | ||
1261 | }, | 1268 | }, |
1262 | .max_stack = sysctl__max_stack(), | 1269 | .max_stack = sysctl__max_stack(), |
1263 | .annotation_opts = annotation__default_options, | 1270 | .annotation_opts = annotation__default_options, |
@@ -1372,6 +1379,8 @@ int cmd_top(int argc, const char **argv) | |||
1372 | "Show raw trace event output (do not use print fmt or plugins)"), | 1379 | "Show raw trace event output (do not use print fmt or plugins)"), |
1373 | OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy, | 1380 | OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy, |
1374 | "Show entries in a hierarchy"), | 1381 | "Show entries in a hierarchy"), |
1382 | OPT_BOOLEAN(0, "overwrite", &top.record_opts.overwrite, | ||
1383 | "Use a backward ring buffer, default: no"), | ||
1375 | OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"), | 1384 | OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"), |
1376 | OPT_UINTEGER(0, "num-thread-synthesize", &top.nr_threads_synthesize, | 1385 | OPT_UINTEGER(0, "num-thread-synthesize", &top.nr_threads_synthesize, |
1377 | "number of thread to run event synthesize"), | 1386 | "number of thread to run event synthesize"), |
@@ -1420,6 +1429,9 @@ int cmd_top(int argc, const char **argv) | |||
1420 | } | 1429 | } |
1421 | } | 1430 | } |
1422 | 1431 | ||
1432 | if (opts->branch_stack && callchain_param.enabled) | ||
1433 | symbol_conf.show_branchflag_count = true; | ||
1434 | |||
1423 | sort__mode = SORT_MODE__TOP; | 1435 | sort__mode = SORT_MODE__TOP; |
1424 | /* display thread wants entries to be collapsed in a different tree */ | 1436 | /* display thread wants entries to be collapsed in a different tree */ |
1425 | perf_hpp_list.need_collapse = 1; | 1437 | perf_hpp_list.need_collapse = 1; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 22ab8e67c760..835619476370 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -89,6 +89,8 @@ struct trace { | |||
89 | u64 base_time; | 89 | u64 base_time; |
90 | FILE *output; | 90 | FILE *output; |
91 | unsigned long nr_events; | 91 | unsigned long nr_events; |
92 | unsigned long nr_events_printed; | ||
93 | unsigned long max_events; | ||
92 | struct strlist *ev_qualifier; | 94 | struct strlist *ev_qualifier; |
93 | struct { | 95 | struct { |
94 | size_t nr; | 96 | size_t nr; |
@@ -106,6 +108,7 @@ struct trace { | |||
106 | } stats; | 108 | } stats; |
107 | unsigned int max_stack; | 109 | unsigned int max_stack; |
108 | unsigned int min_stack; | 110 | unsigned int min_stack; |
111 | bool raw_augmented_syscalls; | ||
109 | bool not_ev_qualifier; | 112 | bool not_ev_qualifier; |
110 | bool live; | 113 | bool live; |
111 | bool full_time; | 114 | bool full_time; |
@@ -181,7 +184,7 @@ static int __tp_field__init_uint(struct tp_field *field, int size, int offset, b | |||
181 | return 0; | 184 | return 0; |
182 | } | 185 | } |
183 | 186 | ||
184 | static int tp_field__init_uint(struct tp_field *field, struct format_field *format_field, bool needs_swap) | 187 | static int tp_field__init_uint(struct tp_field *field, struct tep_format_field *format_field, bool needs_swap) |
185 | { | 188 | { |
186 | return __tp_field__init_uint(field, format_field->size, format_field->offset, needs_swap); | 189 | return __tp_field__init_uint(field, format_field->size, format_field->offset, needs_swap); |
187 | } | 190 | } |
@@ -198,7 +201,7 @@ static int __tp_field__init_ptr(struct tp_field *field, int offset) | |||
198 | return 0; | 201 | return 0; |
199 | } | 202 | } |
200 | 203 | ||
201 | static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field) | 204 | static int tp_field__init_ptr(struct tp_field *field, struct tep_format_field *format_field) |
202 | { | 205 | { |
203 | return __tp_field__init_ptr(field, format_field->offset); | 206 | return __tp_field__init_ptr(field, format_field->offset); |
204 | } | 207 | } |
@@ -214,7 +217,7 @@ static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel, | |||
214 | struct tp_field *field, | 217 | struct tp_field *field, |
215 | const char *name) | 218 | const char *name) |
216 | { | 219 | { |
217 | struct format_field *format_field = perf_evsel__field(evsel, name); | 220 | struct tep_format_field *format_field = perf_evsel__field(evsel, name); |
218 | 221 | ||
219 | if (format_field == NULL) | 222 | if (format_field == NULL) |
220 | return -1; | 223 | return -1; |
@@ -230,7 +233,7 @@ static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, | |||
230 | struct tp_field *field, | 233 | struct tp_field *field, |
231 | const char *name) | 234 | const char *name) |
232 | { | 235 | { |
233 | struct format_field *format_field = perf_evsel__field(evsel, name); | 236 | struct tep_format_field *format_field = perf_evsel__field(evsel, name); |
234 | 237 | ||
235 | if (format_field == NULL) | 238 | if (format_field == NULL) |
236 | return -1; | 239 | return -1; |
@@ -288,6 +291,13 @@ static int perf_evsel__init_augmented_syscall_tp_args(struct perf_evsel *evsel) | |||
288 | return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); | 291 | return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); |
289 | } | 292 | } |
290 | 293 | ||
294 | static int perf_evsel__init_augmented_syscall_tp_ret(struct perf_evsel *evsel) | ||
295 | { | ||
296 | struct syscall_tp *sc = evsel->priv; | ||
297 | |||
298 | return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap); | ||
299 | } | ||
300 | |||
291 | static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler) | 301 | static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler) |
292 | { | 302 | { |
293 | evsel->priv = malloc(sizeof(struct syscall_tp)); | 303 | evsel->priv = malloc(sizeof(struct syscall_tp)); |
@@ -498,16 +508,6 @@ static const char *clockid[] = { | |||
498 | }; | 508 | }; |
499 | static DEFINE_STRARRAY(clockid); | 509 | static DEFINE_STRARRAY(clockid); |
500 | 510 | ||
501 | static const char *socket_families[] = { | ||
502 | "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", | ||
503 | "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", | ||
504 | "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", | ||
505 | "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", | ||
506 | "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", | ||
507 | "ALG", "NFC", "VSOCK", | ||
508 | }; | ||
509 | static DEFINE_STRARRAY(socket_families); | ||
510 | |||
511 | static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, | 511 | static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, |
512 | struct syscall_arg *arg) | 512 | struct syscall_arg *arg) |
513 | { | 513 | { |
@@ -615,6 +615,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, | |||
615 | 615 | ||
616 | struct syscall_arg_fmt { | 616 | struct syscall_arg_fmt { |
617 | size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); | 617 | size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); |
618 | unsigned long (*mask_val)(struct syscall_arg *arg, unsigned long val); | ||
618 | void *parm; | 619 | void *parm; |
619 | const char *name; | 620 | const char *name; |
620 | bool show_zero; | 621 | bool show_zero; |
@@ -631,6 +632,8 @@ static struct syscall_fmt { | |||
631 | } syscall_fmts[] = { | 632 | } syscall_fmts[] = { |
632 | { .name = "access", | 633 | { .name = "access", |
633 | .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, | 634 | .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, |
635 | { .name = "bind", | ||
636 | .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ }, }, }, | ||
634 | { .name = "bpf", | 637 | { .name = "bpf", |
635 | .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, | 638 | .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, |
636 | { .name = "brk", .hexret = true, | 639 | { .name = "brk", .hexret = true, |
@@ -645,6 +648,8 @@ static struct syscall_fmt { | |||
645 | [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, | 648 | [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, |
646 | { .name = "close", | 649 | { .name = "close", |
647 | .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, | 650 | .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, |
651 | { .name = "connect", | ||
652 | .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ }, }, }, | ||
648 | { .name = "epoll_ctl", | 653 | { .name = "epoll_ctl", |
649 | .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, | 654 | .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, |
650 | { .name = "eventfd2", | 655 | { .name = "eventfd2", |
@@ -722,6 +727,10 @@ static struct syscall_fmt { | |||
722 | .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, | 727 | .arg = { [0] = { .scnprintf = SCA_HEX, /* addr */ }, |
723 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, | 728 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, |
724 | [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, }, }, | 729 | [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, }, }, |
730 | { .name = "mount", | ||
731 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ }, | ||
732 | [3] = { .scnprintf = SCA_MOUNT_FLAGS, /* flags */ | ||
733 | .mask_val = SCAMV_MOUNT_FLAGS, /* flags */ }, }, }, | ||
725 | { .name = "mprotect", | 734 | { .name = "mprotect", |
726 | .arg = { [0] = { .scnprintf = SCA_HEX, /* start */ }, | 735 | .arg = { [0] = { .scnprintf = SCA_HEX, /* start */ }, |
727 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, }, | 736 | [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, }, |
@@ -801,7 +810,8 @@ static struct syscall_fmt { | |||
801 | { .name = "sendmsg", | 810 | { .name = "sendmsg", |
802 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, | 811 | .arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, |
803 | { .name = "sendto", | 812 | { .name = "sendto", |
804 | .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, }, | 813 | .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, |
814 | [4] = { .scnprintf = SCA_SOCKADDR, /* addr */ }, }, }, | ||
805 | { .name = "set_tid_address", .errpid = true, }, | 815 | { .name = "set_tid_address", .errpid = true, }, |
806 | { .name = "setitimer", | 816 | { .name = "setitimer", |
807 | .arg = { [0] = STRARRAY(which, itimers), }, }, | 817 | .arg = { [0] = STRARRAY(which, itimers), }, }, |
@@ -830,6 +840,8 @@ static struct syscall_fmt { | |||
830 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 840 | .arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
831 | { .name = "tkill", | 841 | { .name = "tkill", |
832 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, | 842 | .arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, }, |
843 | { .name = "umount2", .alias = "umount", | ||
844 | .arg = { [0] = { .scnprintf = SCA_FILENAME, /* name */ }, }, }, | ||
833 | { .name = "uname", .alias = "newuname", }, | 845 | { .name = "uname", .alias = "newuname", }, |
834 | { .name = "unlinkat", | 846 | { .name = "unlinkat", |
835 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, | 847 | .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, }, |
@@ -853,16 +865,30 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) | |||
853 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); | 865 | return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); |
854 | } | 866 | } |
855 | 867 | ||
868 | static struct syscall_fmt *syscall_fmt__find_by_alias(const char *alias) | ||
869 | { | ||
870 | int i, nmemb = ARRAY_SIZE(syscall_fmts); | ||
871 | |||
872 | for (i = 0; i < nmemb; ++i) { | ||
873 | if (syscall_fmts[i].alias && strcmp(syscall_fmts[i].alias, alias) == 0) | ||
874 | return &syscall_fmts[i]; | ||
875 | } | ||
876 | |||
877 | return NULL; | ||
878 | } | ||
879 | |||
856 | /* | 880 | /* |
857 | * is_exit: is this "exit" or "exit_group"? | 881 | * is_exit: is this "exit" or "exit_group"? |
858 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. | 882 | * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. |
883 | * args_size: sum of the sizes of the syscall arguments, anything after that is augmented stuff: pathname for openat, etc. | ||
859 | */ | 884 | */ |
860 | struct syscall { | 885 | struct syscall { |
861 | struct event_format *tp_format; | 886 | struct tep_event_format *tp_format; |
862 | int nr_args; | 887 | int nr_args; |
888 | int args_size; | ||
863 | bool is_exit; | 889 | bool is_exit; |
864 | bool is_open; | 890 | bool is_open; |
865 | struct format_field *args; | 891 | struct tep_format_field *args; |
866 | const char *name; | 892 | const char *name; |
867 | struct syscall_fmt *fmt; | 893 | struct syscall_fmt *fmt; |
868 | struct syscall_arg_fmt *arg_fmt; | 894 | struct syscall_arg_fmt *arg_fmt; |
@@ -1095,11 +1121,21 @@ static void thread__set_filename_pos(struct thread *thread, const char *bf, | |||
1095 | ttrace->filename.entry_str_pos = bf - ttrace->entry_str; | 1121 | ttrace->filename.entry_str_pos = bf - ttrace->entry_str; |
1096 | } | 1122 | } |
1097 | 1123 | ||
1124 | static size_t syscall_arg__scnprintf_augmented_string(struct syscall_arg *arg, char *bf, size_t size) | ||
1125 | { | ||
1126 | struct augmented_arg *augmented_arg = arg->augmented.args; | ||
1127 | |||
1128 | return scnprintf(bf, size, "%.*s", augmented_arg->size, augmented_arg->value); | ||
1129 | } | ||
1130 | |||
1098 | static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, | 1131 | static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, |
1099 | struct syscall_arg *arg) | 1132 | struct syscall_arg *arg) |
1100 | { | 1133 | { |
1101 | unsigned long ptr = arg->val; | 1134 | unsigned long ptr = arg->val; |
1102 | 1135 | ||
1136 | if (arg->augmented.args) | ||
1137 | return syscall_arg__scnprintf_augmented_string(arg, bf, size); | ||
1138 | |||
1103 | if (!arg->trace->vfs_getname) | 1139 | if (!arg->trace->vfs_getname) |
1104 | return scnprintf(bf, size, "%#x", ptr); | 1140 | return scnprintf(bf, size, "%#x", ptr); |
1105 | 1141 | ||
@@ -1142,11 +1178,9 @@ static void sig_handler(int sig) | |||
1142 | interrupted = sig == SIGINT; | 1178 | interrupted = sig == SIGINT; |
1143 | } | 1179 | } |
1144 | 1180 | ||
1145 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | 1181 | static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread, FILE *fp) |
1146 | u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) | ||
1147 | { | 1182 | { |
1148 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | 1183 | size_t printed = 0; |
1149 | printed += fprintf_duration(duration, duration_calculated, fp); | ||
1150 | 1184 | ||
1151 | if (trace->multiple_threads) { | 1185 | if (trace->multiple_threads) { |
1152 | if (trace->show_comm) | 1186 | if (trace->show_comm) |
@@ -1157,6 +1191,14 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre | |||
1157 | return printed; | 1191 | return printed; |
1158 | } | 1192 | } |
1159 | 1193 | ||
1194 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
1195 | u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) | ||
1196 | { | ||
1197 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
1198 | printed += fprintf_duration(duration, duration_calculated, fp); | ||
1199 | return printed + trace__fprintf_comm_tid(trace, thread, fp); | ||
1200 | } | ||
1201 | |||
1160 | static int trace__process_event(struct trace *trace, struct machine *machine, | 1202 | static int trace__process_event(struct trace *trace, struct machine *machine, |
1161 | union perf_event *event, struct perf_sample *sample) | 1203 | union perf_event *event, struct perf_sample *sample) |
1162 | { | 1204 | { |
@@ -1258,10 +1300,12 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args) | |||
1258 | 1300 | ||
1259 | static int syscall__set_arg_fmts(struct syscall *sc) | 1301 | static int syscall__set_arg_fmts(struct syscall *sc) |
1260 | { | 1302 | { |
1261 | struct format_field *field; | 1303 | struct tep_format_field *field, *last_field = NULL; |
1262 | int idx = 0, len; | 1304 | int idx = 0, len; |
1263 | 1305 | ||
1264 | for (field = sc->args; field; field = field->next, ++idx) { | 1306 | for (field = sc->args; field; field = field->next, ++idx) { |
1307 | last_field = field; | ||
1308 | |||
1265 | if (sc->fmt && sc->fmt->arg[idx].scnprintf) | 1309 | if (sc->fmt && sc->fmt->arg[idx].scnprintf) |
1266 | continue; | 1310 | continue; |
1267 | 1311 | ||
@@ -1270,7 +1314,7 @@ static int syscall__set_arg_fmts(struct syscall *sc) | |||
1270 | strcmp(field->name, "path") == 0 || | 1314 | strcmp(field->name, "path") == 0 || |
1271 | strcmp(field->name, "pathname") == 0)) | 1315 | strcmp(field->name, "pathname") == 0)) |
1272 | sc->arg_fmt[idx].scnprintf = SCA_FILENAME; | 1316 | sc->arg_fmt[idx].scnprintf = SCA_FILENAME; |
1273 | else if (field->flags & FIELD_IS_POINTER) | 1317 | else if (field->flags & TEP_FIELD_IS_POINTER) |
1274 | sc->arg_fmt[idx].scnprintf = syscall_arg__scnprintf_hex; | 1318 | sc->arg_fmt[idx].scnprintf = syscall_arg__scnprintf_hex; |
1275 | else if (strcmp(field->type, "pid_t") == 0) | 1319 | else if (strcmp(field->type, "pid_t") == 0) |
1276 | sc->arg_fmt[idx].scnprintf = SCA_PID; | 1320 | sc->arg_fmt[idx].scnprintf = SCA_PID; |
@@ -1292,6 +1336,9 @@ static int syscall__set_arg_fmts(struct syscall *sc) | |||
1292 | } | 1336 | } |
1293 | } | 1337 | } |
1294 | 1338 | ||
1339 | if (last_field) | ||
1340 | sc->args_size = last_field->offset + last_field->size; | ||
1341 | |||
1295 | return 0; | 1342 | return 0; |
1296 | } | 1343 | } |
1297 | 1344 | ||
@@ -1459,6 +1506,19 @@ static size_t syscall__scnprintf_name(struct syscall *sc, char *bf, size_t size, | |||
1459 | return scnprintf(bf, size, "arg%d: ", arg->idx); | 1506 | return scnprintf(bf, size, "arg%d: ", arg->idx); |
1460 | } | 1507 | } |
1461 | 1508 | ||
1509 | /* | ||
1510 | * Check if the value is in fact zero, i.e. mask whatever needs masking, such | ||
1511 | * as mount 'flags' argument that needs ignoring some magic flag, see comment | ||
1512 | * in tools/perf/trace/beauty/mount_flags.c | ||
1513 | */ | ||
1514 | static unsigned long syscall__mask_val(struct syscall *sc, struct syscall_arg *arg, unsigned long val) | ||
1515 | { | ||
1516 | if (sc->arg_fmt && sc->arg_fmt[arg->idx].mask_val) | ||
1517 | return sc->arg_fmt[arg->idx].mask_val(arg, val); | ||
1518 | |||
1519 | return val; | ||
1520 | } | ||
1521 | |||
1462 | static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, | 1522 | static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, |
1463 | struct syscall_arg *arg, unsigned long val) | 1523 | struct syscall_arg *arg, unsigned long val) |
1464 | { | 1524 | { |
@@ -1472,14 +1532,18 @@ static size_t syscall__scnprintf_val(struct syscall *sc, char *bf, size_t size, | |||
1472 | } | 1532 | } |
1473 | 1533 | ||
1474 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | 1534 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
1475 | unsigned char *args, struct trace *trace, | 1535 | unsigned char *args, void *augmented_args, int augmented_args_size, |
1476 | struct thread *thread) | 1536 | struct trace *trace, struct thread *thread) |
1477 | { | 1537 | { |
1478 | size_t printed = 0; | 1538 | size_t printed = 0; |
1479 | unsigned long val; | 1539 | unsigned long val; |
1480 | u8 bit = 1; | 1540 | u8 bit = 1; |
1481 | struct syscall_arg arg = { | 1541 | struct syscall_arg arg = { |
1482 | .args = args, | 1542 | .args = args, |
1543 | .augmented = { | ||
1544 | .size = augmented_args_size, | ||
1545 | .args = augmented_args, | ||
1546 | }, | ||
1483 | .idx = 0, | 1547 | .idx = 0, |
1484 | .mask = 0, | 1548 | .mask = 0, |
1485 | .trace = trace, | 1549 | .trace = trace, |
@@ -1495,7 +1559,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1495 | ttrace->ret_scnprintf = NULL; | 1559 | ttrace->ret_scnprintf = NULL; |
1496 | 1560 | ||
1497 | if (sc->args != NULL) { | 1561 | if (sc->args != NULL) { |
1498 | struct format_field *field; | 1562 | struct tep_format_field *field; |
1499 | 1563 | ||
1500 | for (field = sc->args; field; | 1564 | for (field = sc->args; field; |
1501 | field = field->next, ++arg.idx, bit <<= 1) { | 1565 | field = field->next, ++arg.idx, bit <<= 1) { |
@@ -1503,6 +1567,11 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1503 | continue; | 1567 | continue; |
1504 | 1568 | ||
1505 | val = syscall_arg__val(&arg, arg.idx); | 1569 | val = syscall_arg__val(&arg, arg.idx); |
1570 | /* | ||
1571 | * Some syscall args need some mask, most don't and | ||
1572 | * return val untouched. | ||
1573 | */ | ||
1574 | val = syscall__mask_val(sc, &arg, val); | ||
1506 | 1575 | ||
1507 | /* | 1576 | /* |
1508 | * Suppress this argument if its value is zero and | 1577 | * Suppress this argument if its value is zero and |
@@ -1634,6 +1703,8 @@ static int trace__printf_interrupted_entry(struct trace *trace) | |||
1634 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); | 1703 | printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); |
1635 | ttrace->entry_pending = false; | 1704 | ttrace->entry_pending = false; |
1636 | 1705 | ||
1706 | ++trace->nr_events_printed; | ||
1707 | |||
1637 | return printed; | 1708 | return printed; |
1638 | } | 1709 | } |
1639 | 1710 | ||
@@ -1654,6 +1725,32 @@ static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, | |||
1654 | return printed; | 1725 | return printed; |
1655 | } | 1726 | } |
1656 | 1727 | ||
1728 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, bool raw_augmented) | ||
1729 | { | ||
1730 | void *augmented_args = NULL; | ||
1731 | /* | ||
1732 | * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter | ||
1733 | * and there we get all 6 syscall args plus the tracepoint common | ||
1734 | * fields (sizeof(long)) and the syscall_nr (another long). So we check | ||
1735 | * if that is the case and if so don't look after the sc->args_size, | ||
1736 | * but always after the full raw_syscalls:sys_enter payload, which is | ||
1737 | * fixed. | ||
1738 | * | ||
1739 | * We'll revisit this later to pass s->args_size to the BPF augmenter | ||
1740 | * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it | ||
1741 | * copies only what we need for each syscall, like what happens when we | ||
1742 | * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace | ||
1743 | * traffic to just what is needed for each syscall. | ||
1744 | */ | ||
1745 | int args_size = raw_augmented ? (8 * (int)sizeof(long)) : sc->args_size; | ||
1746 | |||
1747 | *augmented_args_size = sample->raw_size - args_size; | ||
1748 | if (*augmented_args_size > 0) | ||
1749 | augmented_args = sample->raw_data + args_size; | ||
1750 | |||
1751 | return augmented_args; | ||
1752 | } | ||
1753 | |||
1657 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 1754 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
1658 | union perf_event *event __maybe_unused, | 1755 | union perf_event *event __maybe_unused, |
1659 | struct perf_sample *sample) | 1756 | struct perf_sample *sample) |
@@ -1663,6 +1760,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1663 | size_t printed = 0; | 1760 | size_t printed = 0; |
1664 | struct thread *thread; | 1761 | struct thread *thread; |
1665 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; | 1762 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
1763 | int augmented_args_size = 0; | ||
1764 | void *augmented_args = NULL; | ||
1666 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | 1765 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
1667 | struct thread_trace *ttrace; | 1766 | struct thread_trace *ttrace; |
1668 | 1767 | ||
@@ -1686,13 +1785,24 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1686 | 1785 | ||
1687 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) | 1786 | if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) |
1688 | trace__printf_interrupted_entry(trace); | 1787 | trace__printf_interrupted_entry(trace); |
1689 | 1788 | /* | |
1789 | * If this is raw_syscalls.sys_enter, then it always comes with the 6 possible | ||
1790 | * arguments, even if the syscall being handled, say "openat", uses only 4 arguments | ||
1791 | * this breaks syscall__augmented_args() check for augmented args, as we calculate | ||
1792 | * syscall->args_size using each syscalls:sys_enter_NAME tracefs format file, | ||
1793 | * so when handling, say the openat syscall, we end up getting 6 args for the | ||
1794 | * raw_syscalls:sys_enter event, when we expected just 4, we end up mistakenly | ||
1795 | * thinking that the extra 2 u64 args are the augmented filename, so just check | ||
1796 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. | ||
1797 | */ | ||
1798 | if (evsel != trace->syscalls.events.sys_enter) | ||
1799 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); | ||
1690 | ttrace->entry_time = sample->time; | 1800 | ttrace->entry_time = sample->time; |
1691 | msg = ttrace->entry_str; | 1801 | msg = ttrace->entry_str; |
1692 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); | 1802 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); |
1693 | 1803 | ||
1694 | printed += syscall__scnprintf_args(sc, msg + printed, trace__entry_str_size - printed, | 1804 | printed += syscall__scnprintf_args(sc, msg + printed, trace__entry_str_size - printed, |
1695 | args, trace, thread); | 1805 | args, augmented_args, augmented_args_size, trace, thread); |
1696 | 1806 | ||
1697 | if (sc->is_exit) { | 1807 | if (sc->is_exit) { |
1698 | if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { | 1808 | if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { |
@@ -1723,7 +1833,8 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
1723 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; | 1833 | int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; |
1724 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | 1834 | struct syscall *sc = trace__syscall_info(trace, evsel, id); |
1725 | char msg[1024]; | 1835 | char msg[1024]; |
1726 | void *args; | 1836 | void *args, *augmented_args = NULL; |
1837 | int augmented_args_size; | ||
1727 | 1838 | ||
1728 | if (sc == NULL) | 1839 | if (sc == NULL) |
1729 | return -1; | 1840 | return -1; |
@@ -1738,7 +1849,8 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
1738 | goto out_put; | 1849 | goto out_put; |
1739 | 1850 | ||
1740 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1851 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
1741 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, trace, thread); | 1852 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); |
1853 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); | ||
1742 | fprintf(trace->output, "%s", msg); | 1854 | fprintf(trace->output, "%s", msg); |
1743 | err = 0; | 1855 | err = 0; |
1744 | out_put: | 1856 | out_put: |
@@ -1754,12 +1866,14 @@ static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evse | |||
1754 | int max_stack = evsel->attr.sample_max_stack ? | 1866 | int max_stack = evsel->attr.sample_max_stack ? |
1755 | evsel->attr.sample_max_stack : | 1867 | evsel->attr.sample_max_stack : |
1756 | trace->max_stack; | 1868 | trace->max_stack; |
1869 | int err; | ||
1757 | 1870 | ||
1758 | if (machine__resolve(trace->host, &al, sample) < 0 || | 1871 | if (machine__resolve(trace->host, &al, sample) < 0) |
1759 | thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack)) | ||
1760 | return -1; | 1872 | return -1; |
1761 | 1873 | ||
1762 | return 0; | 1874 | err = thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack); |
1875 | addr_location__put(&al); | ||
1876 | return err; | ||
1763 | } | 1877 | } |
1764 | 1878 | ||
1765 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) | 1879 | static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample) |
@@ -1884,6 +1998,13 @@ errno_print: { | |||
1884 | 1998 | ||
1885 | fputc('\n', trace->output); | 1999 | fputc('\n', trace->output); |
1886 | 2000 | ||
2001 | /* | ||
2002 | * We only consider an 'event' for the sake of --max-events a non-filtered | ||
2003 | * sys_enter + sys_exit and other tracepoint events. | ||
2004 | */ | ||
2005 | if (++trace->nr_events_printed == trace->max_events && trace->max_events != ULONG_MAX) | ||
2006 | interrupted = true; | ||
2007 | |||
1887 | if (callchain_ret > 0) | 2008 | if (callchain_ret > 0) |
1888 | trace__fprintf_callchain(trace, sample); | 2009 | trace__fprintf_callchain(trace, sample); |
1889 | else if (callchain_ret < 0) | 2010 | else if (callchain_ret < 0) |
@@ -2016,13 +2137,25 @@ static void bpf_output__fprintf(struct trace *trace, | |||
2016 | { | 2137 | { |
2017 | binary__fprintf(sample->raw_data, sample->raw_size, 8, | 2138 | binary__fprintf(sample->raw_data, sample->raw_size, 8, |
2018 | bpf_output__printer, NULL, trace->output); | 2139 | bpf_output__printer, NULL, trace->output); |
2140 | ++trace->nr_events_printed; | ||
2019 | } | 2141 | } |
2020 | 2142 | ||
2021 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | 2143 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, |
2022 | union perf_event *event __maybe_unused, | 2144 | union perf_event *event __maybe_unused, |
2023 | struct perf_sample *sample) | 2145 | struct perf_sample *sample) |
2024 | { | 2146 | { |
2147 | struct thread *thread; | ||
2025 | int callchain_ret = 0; | 2148 | int callchain_ret = 0; |
2149 | /* | ||
2150 | * Check if we called perf_evsel__disable(evsel) due to, for instance, | ||
2151 | * this event's max_events having been hit and this is an entry coming | ||
2152 | * from the ring buffer that we should discard, since the max events | ||
2153 | * have already been considered/printed. | ||
2154 | */ | ||
2155 | if (evsel->disabled) | ||
2156 | return 0; | ||
2157 | |||
2158 | thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); | ||
2026 | 2159 | ||
2027 | if (sample->callchain) { | 2160 | if (sample->callchain) { |
2028 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); | 2161 | callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor); |
@@ -2039,22 +2172,47 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
2039 | if (trace->trace_syscalls) | 2172 | if (trace->trace_syscalls) |
2040 | fprintf(trace->output, "( ): "); | 2173 | fprintf(trace->output, "( ): "); |
2041 | 2174 | ||
2175 | if (thread) | ||
2176 | trace__fprintf_comm_tid(trace, thread, trace->output); | ||
2177 | |||
2178 | if (evsel == trace->syscalls.events.augmented) { | ||
2179 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); | ||
2180 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | ||
2181 | |||
2182 | if (sc) { | ||
2183 | fprintf(trace->output, "%s(", sc->name); | ||
2184 | trace__fprintf_sys_enter(trace, evsel, sample); | ||
2185 | fputc(')', trace->output); | ||
2186 | goto newline; | ||
2187 | } | ||
2188 | |||
2189 | /* | ||
2190 | * XXX: Not having the associated syscall info or not finding/adding | ||
2191 | * the thread should never happen, but if it does... | ||
2192 | * fall thru and print it as a bpf_output event. | ||
2193 | */ | ||
2194 | } | ||
2195 | |||
2042 | fprintf(trace->output, "%s:", evsel->name); | 2196 | fprintf(trace->output, "%s:", evsel->name); |
2043 | 2197 | ||
2044 | if (perf_evsel__is_bpf_output(evsel)) { | 2198 | if (perf_evsel__is_bpf_output(evsel)) { |
2045 | if (evsel == trace->syscalls.events.augmented) | 2199 | bpf_output__fprintf(trace, sample); |
2046 | trace__fprintf_sys_enter(trace, evsel, sample); | ||
2047 | else | ||
2048 | bpf_output__fprintf(trace, sample); | ||
2049 | } else if (evsel->tp_format) { | 2200 | } else if (evsel->tp_format) { |
2050 | if (strncmp(evsel->tp_format->name, "sys_enter_", 10) || | 2201 | if (strncmp(evsel->tp_format->name, "sys_enter_", 10) || |
2051 | trace__fprintf_sys_enter(trace, evsel, sample)) { | 2202 | trace__fprintf_sys_enter(trace, evsel, sample)) { |
2052 | event_format__fprintf(evsel->tp_format, sample->cpu, | 2203 | event_format__fprintf(evsel->tp_format, sample->cpu, |
2053 | sample->raw_data, sample->raw_size, | 2204 | sample->raw_data, sample->raw_size, |
2054 | trace->output); | 2205 | trace->output); |
2206 | ++trace->nr_events_printed; | ||
2207 | |||
2208 | if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { | ||
2209 | perf_evsel__disable(evsel); | ||
2210 | perf_evsel__close(evsel); | ||
2211 | } | ||
2055 | } | 2212 | } |
2056 | } | 2213 | } |
2057 | 2214 | ||
2215 | newline: | ||
2058 | fprintf(trace->output, "\n"); | 2216 | fprintf(trace->output, "\n"); |
2059 | 2217 | ||
2060 | if (callchain_ret > 0) | 2218 | if (callchain_ret > 0) |
@@ -2062,6 +2220,7 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
2062 | else if (callchain_ret < 0) | 2220 | else if (callchain_ret < 0) |
2063 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | 2221 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
2064 | out: | 2222 | out: |
2223 | thread__put(thread); | ||
2065 | return 0; | 2224 | return 0; |
2066 | } | 2225 | } |
2067 | 2226 | ||
@@ -2148,6 +2307,8 @@ static int trace__pgfault(struct trace *trace, | |||
2148 | trace__fprintf_callchain(trace, sample); | 2307 | trace__fprintf_callchain(trace, sample); |
2149 | else if (callchain_ret < 0) | 2308 | else if (callchain_ret < 0) |
2150 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); | 2309 | pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); |
2310 | |||
2311 | ++trace->nr_events_printed; | ||
2151 | out: | 2312 | out: |
2152 | err = 0; | 2313 | err = 0; |
2153 | out_put: | 2314 | out_put: |
@@ -2325,6 +2486,9 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st | |||
2325 | tracepoint_handler handler = evsel->handler; | 2486 | tracepoint_handler handler = evsel->handler; |
2326 | handler(trace, evsel, event, sample); | 2487 | handler(trace, evsel, event, sample); |
2327 | } | 2488 | } |
2489 | |||
2490 | if (trace->nr_events_printed >= trace->max_events && trace->max_events != ULONG_MAX) | ||
2491 | interrupted = true; | ||
2328 | } | 2492 | } |
2329 | 2493 | ||
2330 | static int trace__add_syscall_newtp(struct trace *trace) | 2494 | static int trace__add_syscall_newtp(struct trace *trace) |
@@ -2629,7 +2793,7 @@ next_event: | |||
2629 | int timeout = done ? 100 : -1; | 2793 | int timeout = done ? 100 : -1; |
2630 | 2794 | ||
2631 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { | 2795 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { |
2632 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0) | 2796 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP | POLLNVAL) == 0) |
2633 | draining = true; | 2797 | draining = true; |
2634 | 2798 | ||
2635 | goto again; | 2799 | goto again; |
@@ -3061,6 +3225,7 @@ static int trace__parse_events_option(const struct option *opt, const char *str, | |||
3061 | int len = strlen(str) + 1, err = -1, list, idx; | 3225 | int len = strlen(str) + 1, err = -1, list, idx; |
3062 | char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); | 3226 | char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); |
3063 | char group_name[PATH_MAX]; | 3227 | char group_name[PATH_MAX]; |
3228 | struct syscall_fmt *fmt; | ||
3064 | 3229 | ||
3065 | if (strace_groups_dir == NULL) | 3230 | if (strace_groups_dir == NULL) |
3066 | return -1; | 3231 | return -1; |
@@ -3078,12 +3243,19 @@ static int trace__parse_events_option(const struct option *opt, const char *str, | |||
3078 | if (syscalltbl__id(trace->sctbl, s) >= 0 || | 3243 | if (syscalltbl__id(trace->sctbl, s) >= 0 || |
3079 | syscalltbl__strglobmatch_first(trace->sctbl, s, &idx) >= 0) { | 3244 | syscalltbl__strglobmatch_first(trace->sctbl, s, &idx) >= 0) { |
3080 | list = 1; | 3245 | list = 1; |
3246 | goto do_concat; | ||
3247 | } | ||
3248 | |||
3249 | fmt = syscall_fmt__find_by_alias(s); | ||
3250 | if (fmt != NULL) { | ||
3251 | list = 1; | ||
3252 | s = fmt->name; | ||
3081 | } else { | 3253 | } else { |
3082 | path__join(group_name, sizeof(group_name), strace_groups_dir, s); | 3254 | path__join(group_name, sizeof(group_name), strace_groups_dir, s); |
3083 | if (access(group_name, R_OK) == 0) | 3255 | if (access(group_name, R_OK) == 0) |
3084 | list = 1; | 3256 | list = 1; |
3085 | } | 3257 | } |
3086 | 3258 | do_concat: | |
3087 | if (lists[list]) { | 3259 | if (lists[list]) { |
3088 | sprintf(lists[list] + strlen(lists[list]), ",%s", s); | 3260 | sprintf(lists[list] + strlen(lists[list]), ",%s", s); |
3089 | } else { | 3261 | } else { |
@@ -3172,6 +3344,7 @@ int cmd_trace(int argc, const char **argv) | |||
3172 | .trace_syscalls = false, | 3344 | .trace_syscalls = false, |
3173 | .kernel_syscallchains = false, | 3345 | .kernel_syscallchains = false, |
3174 | .max_stack = UINT_MAX, | 3346 | .max_stack = UINT_MAX, |
3347 | .max_events = ULONG_MAX, | ||
3175 | }; | 3348 | }; |
3176 | const char *output_name = NULL; | 3349 | const char *output_name = NULL; |
3177 | const struct option trace_options[] = { | 3350 | const struct option trace_options[] = { |
@@ -3224,6 +3397,8 @@ int cmd_trace(int argc, const char **argv) | |||
3224 | &record_parse_callchain_opt), | 3397 | &record_parse_callchain_opt), |
3225 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, | 3398 | OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, |
3226 | "Show the kernel callchains on the syscall exit path"), | 3399 | "Show the kernel callchains on the syscall exit path"), |
3400 | OPT_ULONG(0, "max-events", &trace.max_events, | ||
3401 | "Set the maximum number of events to print, exit after that is reached. "), | ||
3227 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, | 3402 | OPT_UINTEGER(0, "min-stack", &trace.min_stack, |
3228 | "Set the minimum stack depth when parsing the callchain, " | 3403 | "Set the minimum stack depth when parsing the callchain, " |
3229 | "anything below the specified depth will be ignored."), | 3404 | "anything below the specified depth will be ignored."), |
@@ -3276,12 +3451,8 @@ int cmd_trace(int argc, const char **argv) | |||
3276 | goto out; | 3451 | goto out; |
3277 | } | 3452 | } |
3278 | 3453 | ||
3279 | if (evsel) { | 3454 | if (evsel) |
3280 | if (perf_evsel__init_augmented_syscall_tp(evsel) || | ||
3281 | perf_evsel__init_augmented_syscall_tp_args(evsel)) | ||
3282 | goto out; | ||
3283 | trace.syscalls.events.augmented = evsel; | 3455 | trace.syscalls.events.augmented = evsel; |
3284 | } | ||
3285 | 3456 | ||
3286 | err = bpf__setup_stdout(trace.evlist); | 3457 | err = bpf__setup_stdout(trace.evlist); |
3287 | if (err) { | 3458 | if (err) { |
@@ -3326,6 +3497,42 @@ int cmd_trace(int argc, const char **argv) | |||
3326 | } | 3497 | } |
3327 | } | 3498 | } |
3328 | 3499 | ||
3500 | /* | ||
3501 | * If we are augmenting syscalls, then combine what we put in the | ||
3502 | * __augmented_syscalls__ BPF map with what is in the | ||
3503 | * syscalls:sys_exit_FOO tracepoints, i.e. just like we do without BPF, | ||
3504 | * combining raw_syscalls:sys_enter with raw_syscalls:sys_exit. | ||
3505 | * | ||
3506 | * We'll switch to look at two BPF maps, one for sys_enter and the | ||
3507 | * other for sys_exit when we start augmenting the sys_exit paths with | ||
3508 | * buffers that are being copied from kernel to userspace, think 'read' | ||
3509 | * syscall. | ||
3510 | */ | ||
3511 | if (trace.syscalls.events.augmented) { | ||
3512 | evsel = trace.syscalls.events.augmented; | ||
3513 | |||
3514 | if (perf_evsel__init_augmented_syscall_tp(evsel) || | ||
3515 | perf_evsel__init_augmented_syscall_tp_args(evsel)) | ||
3516 | goto out; | ||
3517 | evsel->handler = trace__sys_enter; | ||
3518 | |||
3519 | evlist__for_each_entry(trace.evlist, evsel) { | ||
3520 | bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0; | ||
3521 | |||
3522 | if (raw_syscalls_sys_exit) { | ||
3523 | trace.raw_augmented_syscalls = true; | ||
3524 | goto init_augmented_syscall_tp; | ||
3525 | } | ||
3526 | |||
3527 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { | ||
3528 | init_augmented_syscall_tp: | ||
3529 | perf_evsel__init_augmented_syscall_tp(evsel); | ||
3530 | perf_evsel__init_augmented_syscall_tp_ret(evsel); | ||
3531 | evsel->handler = trace__sys_exit; | ||
3532 | } | ||
3533 | } | ||
3534 | } | ||
3535 | |||
3329 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) | 3536 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) |
3330 | return trace__record(&trace, argc-1, &argv[1]); | 3537 | return trace__record(&trace, argc-1, &argv[1]); |
3331 | 3538 | ||
diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 466540ee8ea7..9531f7bd7d9b 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh | |||
@@ -5,6 +5,7 @@ HEADERS=' | |||
5 | include/uapi/drm/drm.h | 5 | include/uapi/drm/drm.h |
6 | include/uapi/drm/i915_drm.h | 6 | include/uapi/drm/i915_drm.h |
7 | include/uapi/linux/fcntl.h | 7 | include/uapi/linux/fcntl.h |
8 | include/uapi/linux/fs.h | ||
8 | include/uapi/linux/kcmp.h | 9 | include/uapi/linux/kcmp.h |
9 | include/uapi/linux/kvm.h | 10 | include/uapi/linux/kvm.h |
10 | include/uapi/linux/in.h | 11 | include/uapi/linux/in.h |
@@ -14,6 +15,7 @@ include/uapi/linux/sched.h | |||
14 | include/uapi/linux/stat.h | 15 | include/uapi/linux/stat.h |
15 | include/uapi/linux/vhost.h | 16 | include/uapi/linux/vhost.h |
16 | include/uapi/sound/asound.h | 17 | include/uapi/sound/asound.h |
18 | include/linux/bits.h | ||
17 | include/linux/hash.h | 19 | include/linux/hash.h |
18 | include/uapi/linux/hw_breakpoint.h | 20 | include/uapi/linux/hw_breakpoint.h |
19 | arch/x86/include/asm/disabled-features.h | 21 | arch/x86/include/asm/disabled-features.h |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 2d0caf20ff3a..bc6c585f74fc 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -30,3 +30,4 @@ perf-test mainporcelain common | |||
30 | perf-timechart mainporcelain common | 30 | perf-timechart mainporcelain common |
31 | perf-top mainporcelain common | 31 | perf-top mainporcelain common |
32 | perf-trace mainporcelain audit | 32 | perf-trace mainporcelain audit |
33 | perf-version mainporcelain common | ||
diff --git a/tools/perf/examples/bpf/augmented_raw_syscalls.c b/tools/perf/examples/bpf/augmented_raw_syscalls.c new file mode 100644 index 000000000000..90a19336310b --- /dev/null +++ b/tools/perf/examples/bpf/augmented_raw_syscalls.c | |||
@@ -0,0 +1,131 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Augment the raw_syscalls tracepoints with the contents of the pointer arguments. | ||
4 | * | ||
5 | * Test it with: | ||
6 | * | ||
7 | * perf trace -e tools/perf/examples/bpf/augmented_raw_syscalls.c cat /etc/passwd > /dev/null | ||
8 | * | ||
9 | * This exactly matches what is marshalled into the raw_syscall:sys_enter | ||
10 | * payload expected by the 'perf trace' beautifiers. | ||
11 | * | ||
12 | * For now it just uses the existing tracepoint augmentation code in 'perf | ||
13 | * trace', in the next csets we'll hook up these with the sys_enter/sys_exit | ||
14 | * code that will combine entry/exit in a strace like way. | ||
15 | */ | ||
16 | |||
17 | #include <stdio.h> | ||
18 | #include <linux/socket.h> | ||
19 | |||
20 | /* bpf-output associated map */ | ||
21 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | ||
22 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | ||
23 | .key_size = sizeof(int), | ||
24 | .value_size = sizeof(u32), | ||
25 | .max_entries = __NR_CPUS__, | ||
26 | }; | ||
27 | |||
28 | struct syscall_enter_args { | ||
29 | unsigned long long common_tp_fields; | ||
30 | long syscall_nr; | ||
31 | unsigned long args[6]; | ||
32 | }; | ||
33 | |||
34 | struct syscall_exit_args { | ||
35 | unsigned long long common_tp_fields; | ||
36 | long syscall_nr; | ||
37 | long ret; | ||
38 | }; | ||
39 | |||
40 | struct augmented_filename { | ||
41 | unsigned int size; | ||
42 | int reserved; | ||
43 | char value[256]; | ||
44 | }; | ||
45 | |||
46 | #define SYS_OPEN 2 | ||
47 | #define SYS_OPENAT 257 | ||
48 | |||
49 | SEC("raw_syscalls:sys_enter") | ||
50 | int sys_enter(struct syscall_enter_args *args) | ||
51 | { | ||
52 | struct { | ||
53 | struct syscall_enter_args args; | ||
54 | struct augmented_filename filename; | ||
55 | } augmented_args; | ||
56 | unsigned int len = sizeof(augmented_args); | ||
57 | const void *filename_arg = NULL; | ||
58 | |||
59 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); | ||
60 | /* | ||
61 | * Yonghong and Edward Cree sayz: | ||
62 | * | ||
63 | * https://www.spinics.net/lists/netdev/msg531645.html | ||
64 | * | ||
65 | * >> R0=inv(id=0) R1=inv2 R6=ctx(id=0,off=0,imm=0) R7=inv64 R10=fp0,call_-1 | ||
66 | * >> 10: (bf) r1 = r6 | ||
67 | * >> 11: (07) r1 += 16 | ||
68 | * >> 12: (05) goto pc+2 | ||
69 | * >> 15: (79) r3 = *(u64 *)(r1 +0) | ||
70 | * >> dereference of modified ctx ptr R1 off=16 disallowed | ||
71 | * > Aha, we at least got a different error message this time. | ||
72 | * > And indeed llvm has done that optimisation, rather than the more obvious | ||
73 | * > 11: r3 = *(u64 *)(r1 +16) | ||
74 | * > because it wants to have lots of reads share a single insn. You may be able | ||
75 | * > to defeat that optimisation by adding compiler barriers, idk. Maybe someone | ||
76 | * > with llvm knowledge can figure out how to stop it (ideally, llvm would know | ||
77 | * > when it's generating for bpf backend and not do that). -O0? ¯\_(ツ)_/¯ | ||
78 | * | ||
79 | * The optimization mostly likes below: | ||
80 | * | ||
81 | * br1: | ||
82 | * ... | ||
83 | * r1 += 16 | ||
84 | * goto merge | ||
85 | * br2: | ||
86 | * ... | ||
87 | * r1 += 20 | ||
88 | * goto merge | ||
89 | * merge: | ||
90 | * *(u64 *)(r1 + 0) | ||
91 | * | ||
92 | * The compiler tries to merge common loads. There is no easy way to | ||
93 | * stop this compiler optimization without turning off a lot of other | ||
94 | * optimizations. The easiest way is to add barriers: | ||
95 | * | ||
96 | * __asm__ __volatile__("": : :"memory") | ||
97 | * | ||
98 | * after the ctx memory access to prevent their down stream merging. | ||
99 | */ | ||
100 | switch (augmented_args.args.syscall_nr) { | ||
101 | case SYS_OPEN: filename_arg = (const void *)args->args[0]; | ||
102 | __asm__ __volatile__("": : :"memory"); | ||
103 | break; | ||
104 | case SYS_OPENAT: filename_arg = (const void *)args->args[1]; | ||
105 | break; | ||
106 | } | ||
107 | |||
108 | if (filename_arg != NULL) { | ||
109 | augmented_args.filename.reserved = 0; | ||
110 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, | ||
111 | sizeof(augmented_args.filename.value), | ||
112 | filename_arg); | ||
113 | if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) { | ||
114 | len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size; | ||
115 | len &= sizeof(augmented_args.filename.value) - 1; | ||
116 | } | ||
117 | } else { | ||
118 | len = sizeof(augmented_args.args); | ||
119 | } | ||
120 | |||
121 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, &augmented_args, len); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | SEC("raw_syscalls:sys_exit") | ||
126 | int sys_exit(struct syscall_exit_args *args) | ||
127 | { | ||
128 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ | ||
129 | } | ||
130 | |||
131 | license(GPL); | ||
diff --git a/tools/perf/examples/bpf/augmented_syscalls.c b/tools/perf/examples/bpf/augmented_syscalls.c index 69a31386d8cd..2ae44813ef2d 100644 --- a/tools/perf/examples/bpf/augmented_syscalls.c +++ b/tools/perf/examples/bpf/augmented_syscalls.c | |||
@@ -1,6 +1,6 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | 2 | /* |
3 | * Augment the openat syscall with the contents of the filename pointer argument. | 3 | * Augment syscalls with the contents of the pointer arguments. |
4 | * | 4 | * |
5 | * Test it with: | 5 | * Test it with: |
6 | * | 6 | * |
@@ -10,15 +10,14 @@ | |||
10 | * the last one should be the one for '/etc/passwd'. | 10 | * the last one should be the one for '/etc/passwd'. |
11 | * | 11 | * |
12 | * This matches what is marshalled into the raw_syscall:sys_enter payload | 12 | * This matches what is marshalled into the raw_syscall:sys_enter payload |
13 | * expected by the 'perf trace' beautifiers, and can be used by them unmodified, | 13 | * expected by the 'perf trace' beautifiers, and can be used by them, that will |
14 | * which will be done as that feature is implemented in the next csets, for now | 14 | * check if perf_sample->raw_data is more than what is expected for each |
15 | * it will appear in a dump done by the default tracepoint handler in 'perf trace', | 15 | * syscalls:sys_{enter,exit}_SYSCALL tracepoint, uing the extra data as the |
16 | * that uses bpf_output__fprintf() to just dump those contents, as done with | 16 | * contents of pointer arguments. |
17 | * the bpf-output event associated with the __bpf_output__ map declared in | ||
18 | * tools/perf/include/bpf/stdio.h. | ||
19 | */ | 17 | */ |
20 | 18 | ||
21 | #include <stdio.h> | 19 | #include <stdio.h> |
20 | #include <linux/socket.h> | ||
22 | 21 | ||
23 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | 22 | struct bpf_map SEC("maps") __augmented_syscalls__ = { |
24 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | 23 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, |
@@ -27,6 +26,44 @@ struct bpf_map SEC("maps") __augmented_syscalls__ = { | |||
27 | .max_entries = __NR_CPUS__, | 26 | .max_entries = __NR_CPUS__, |
28 | }; | 27 | }; |
29 | 28 | ||
29 | struct syscall_exit_args { | ||
30 | unsigned long long common_tp_fields; | ||
31 | long syscall_nr; | ||
32 | long ret; | ||
33 | }; | ||
34 | |||
35 | struct augmented_filename { | ||
36 | unsigned int size; | ||
37 | int reserved; | ||
38 | char value[256]; | ||
39 | }; | ||
40 | |||
41 | #define augmented_filename_syscall(syscall) \ | ||
42 | struct augmented_enter_##syscall##_args { \ | ||
43 | struct syscall_enter_##syscall##_args args; \ | ||
44 | struct augmented_filename filename; \ | ||
45 | }; \ | ||
46 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
47 | { \ | ||
48 | struct augmented_enter_##syscall##_args augmented_args = { .filename.reserved = 0, }; \ | ||
49 | unsigned int len = sizeof(augmented_args); \ | ||
50 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
51 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, \ | ||
52 | sizeof(augmented_args.filename.value), \ | ||
53 | args->filename_ptr); \ | ||
54 | if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) { \ | ||
55 | len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size; \ | ||
56 | len &= sizeof(augmented_args.filename.value) - 1; \ | ||
57 | } \ | ||
58 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
59 | &augmented_args, len); \ | ||
60 | return 0; \ | ||
61 | } \ | ||
62 | int syscall_exit(syscall)(struct syscall_exit_args *args) \ | ||
63 | { \ | ||
64 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ \ | ||
65 | } | ||
66 | |||
30 | struct syscall_enter_openat_args { | 67 | struct syscall_enter_openat_args { |
31 | unsigned long long common_tp_fields; | 68 | unsigned long long common_tp_fields; |
32 | long syscall_nr; | 69 | long syscall_nr; |
@@ -36,20 +73,101 @@ struct syscall_enter_openat_args { | |||
36 | long mode; | 73 | long mode; |
37 | }; | 74 | }; |
38 | 75 | ||
39 | struct augmented_enter_openat_args { | 76 | augmented_filename_syscall(openat); |
40 | struct syscall_enter_openat_args args; | 77 | |
41 | char filename[64]; | 78 | struct syscall_enter_open_args { |
79 | unsigned long long common_tp_fields; | ||
80 | long syscall_nr; | ||
81 | char *filename_ptr; | ||
82 | long flags; | ||
83 | long mode; | ||
84 | }; | ||
85 | |||
86 | augmented_filename_syscall(open); | ||
87 | |||
88 | struct syscall_enter_inotify_add_watch_args { | ||
89 | unsigned long long common_tp_fields; | ||
90 | long syscall_nr; | ||
91 | long fd; | ||
92 | char *filename_ptr; | ||
93 | long mask; | ||
94 | }; | ||
95 | |||
96 | augmented_filename_syscall(inotify_add_watch); | ||
97 | |||
98 | struct statbuf; | ||
99 | |||
100 | struct syscall_enter_newstat_args { | ||
101 | unsigned long long common_tp_fields; | ||
102 | long syscall_nr; | ||
103 | char *filename_ptr; | ||
104 | struct stat *statbuf; | ||
42 | }; | 105 | }; |
43 | 106 | ||
44 | int syscall_enter(openat)(struct syscall_enter_openat_args *args) | 107 | augmented_filename_syscall(newstat); |
45 | { | 108 | |
46 | struct augmented_enter_openat_args augmented_args; | 109 | #ifndef _K_SS_MAXSIZE |
110 | #define _K_SS_MAXSIZE 128 | ||
111 | #endif | ||
47 | 112 | ||
48 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); | 113 | #define augmented_sockaddr_syscall(syscall) \ |
49 | probe_read_str(&augmented_args.filename, sizeof(augmented_args.filename), args->filename_ptr); | 114 | struct augmented_enter_##syscall##_args { \ |
50 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, | 115 | struct syscall_enter_##syscall##_args args; \ |
51 | &augmented_args, sizeof(augmented_args)); | 116 | struct sockaddr_storage addr; \ |
52 | return 1; | 117 | }; \ |
118 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
119 | { \ | ||
120 | struct augmented_enter_##syscall##_args augmented_args; \ | ||
121 | unsigned long addrlen = sizeof(augmented_args.addr); \ | ||
122 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
123 | /* FIXME_CLANG_OPTIMIZATION_THAT_ACCESSES_USER_CONTROLLED_ADDRLEN_DESPITE_THIS_CHECK */ \ | ||
124 | /* if (addrlen > augmented_args.args.addrlen) */ \ | ||
125 | /* addrlen = augmented_args.args.addrlen; */ \ | ||
126 | /* */ \ | ||
127 | probe_read(&augmented_args.addr, addrlen, args->addr_ptr); \ | ||
128 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
129 | &augmented_args, \ | ||
130 | sizeof(augmented_args) - sizeof(augmented_args.addr) + addrlen); \ | ||
131 | return 0; \ | ||
132 | } \ | ||
133 | int syscall_exit(syscall)(struct syscall_exit_args *args) \ | ||
134 | { \ | ||
135 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ \ | ||
53 | } | 136 | } |
54 | 137 | ||
138 | struct sockaddr; | ||
139 | |||
140 | struct syscall_enter_bind_args { | ||
141 | unsigned long long common_tp_fields; | ||
142 | long syscall_nr; | ||
143 | long fd; | ||
144 | struct sockaddr *addr_ptr; | ||
145 | unsigned long addrlen; | ||
146 | }; | ||
147 | |||
148 | augmented_sockaddr_syscall(bind); | ||
149 | |||
150 | struct syscall_enter_connect_args { | ||
151 | unsigned long long common_tp_fields; | ||
152 | long syscall_nr; | ||
153 | long fd; | ||
154 | struct sockaddr *addr_ptr; | ||
155 | unsigned long addrlen; | ||
156 | }; | ||
157 | |||
158 | augmented_sockaddr_syscall(connect); | ||
159 | |||
160 | struct syscall_enter_sendto_args { | ||
161 | unsigned long long common_tp_fields; | ||
162 | long syscall_nr; | ||
163 | long fd; | ||
164 | void *buff; | ||
165 | long len; | ||
166 | unsigned long flags; | ||
167 | struct sockaddr *addr_ptr; | ||
168 | long addr_len; | ||
169 | }; | ||
170 | |||
171 | augmented_sockaddr_syscall(sendto); | ||
172 | |||
55 | license(GPL); | 173 | license(GPL); |
diff --git a/tools/perf/examples/bpf/etcsnoop.c b/tools/perf/examples/bpf/etcsnoop.c new file mode 100644 index 000000000000..b59e8812ee8c --- /dev/null +++ b/tools/perf/examples/bpf/etcsnoop.c | |||
@@ -0,0 +1,80 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Augment the filename syscalls with the contents of the filename pointer argument | ||
4 | * filtering only those that do not start with /etc/. | ||
5 | * | ||
6 | * Test it with: | ||
7 | * | ||
8 | * perf trace -e tools/perf/examples/bpf/augmented_syscalls.c cat /etc/passwd > /dev/null | ||
9 | * | ||
10 | * It'll catch some openat syscalls related to the dynamic linked and | ||
11 | * the last one should be the one for '/etc/passwd'. | ||
12 | * | ||
13 | * This matches what is marshalled into the raw_syscall:sys_enter payload | ||
14 | * expected by the 'perf trace' beautifiers, and can be used by them unmodified, | ||
15 | * which will be done as that feature is implemented in the next csets, for now | ||
16 | * it will appear in a dump done by the default tracepoint handler in 'perf trace', | ||
17 | * that uses bpf_output__fprintf() to just dump those contents, as done with | ||
18 | * the bpf-output event associated with the __bpf_output__ map declared in | ||
19 | * tools/perf/include/bpf/stdio.h. | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | |||
24 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | ||
25 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | ||
26 | .key_size = sizeof(int), | ||
27 | .value_size = sizeof(u32), | ||
28 | .max_entries = __NR_CPUS__, | ||
29 | }; | ||
30 | |||
31 | struct augmented_filename { | ||
32 | int size; | ||
33 | int reserved; | ||
34 | char value[64]; | ||
35 | }; | ||
36 | |||
37 | #define augmented_filename_syscall_enter(syscall) \ | ||
38 | struct augmented_enter_##syscall##_args { \ | ||
39 | struct syscall_enter_##syscall##_args args; \ | ||
40 | struct augmented_filename filename; \ | ||
41 | }; \ | ||
42 | int syscall_enter(syscall)(struct syscall_enter_##syscall##_args *args) \ | ||
43 | { \ | ||
44 | char etc[6] = "/etc/"; \ | ||
45 | struct augmented_enter_##syscall##_args augmented_args = { .filename.reserved = 0, }; \ | ||
46 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); \ | ||
47 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, \ | ||
48 | sizeof(augmented_args.filename.value), \ | ||
49 | args->filename_ptr); \ | ||
50 | if (__builtin_memcmp(augmented_args.filename.value, etc, 4) != 0) \ | ||
51 | return 0; \ | ||
52 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, \ | ||
53 | &augmented_args, \ | ||
54 | (sizeof(augmented_args) - sizeof(augmented_args.filename.value) + \ | ||
55 | augmented_args.filename.size)); \ | ||
56 | return 0; \ | ||
57 | } | ||
58 | |||
59 | struct syscall_enter_openat_args { | ||
60 | unsigned long long common_tp_fields; | ||
61 | long syscall_nr; | ||
62 | long dfd; | ||
63 | char *filename_ptr; | ||
64 | long flags; | ||
65 | long mode; | ||
66 | }; | ||
67 | |||
68 | augmented_filename_syscall_enter(openat); | ||
69 | |||
70 | struct syscall_enter_open_args { | ||
71 | unsigned long long common_tp_fields; | ||
72 | long syscall_nr; | ||
73 | char *filename_ptr; | ||
74 | long flags; | ||
75 | long mode; | ||
76 | }; | ||
77 | |||
78 | augmented_filename_syscall_enter(open); | ||
79 | |||
80 | license(GPL); | ||
diff --git a/tools/perf/include/bpf/bpf.h b/tools/perf/include/bpf/bpf.h index 47897d65e799..52b6d87fe822 100644 --- a/tools/perf/include/bpf/bpf.h +++ b/tools/perf/include/bpf/bpf.h | |||
@@ -26,6 +26,9 @@ struct bpf_map { | |||
26 | #define syscall_enter(name) \ | 26 | #define syscall_enter(name) \ |
27 | SEC("syscalls:sys_enter_" #name) syscall_enter_ ## name | 27 | SEC("syscalls:sys_enter_" #name) syscall_enter_ ## name |
28 | 28 | ||
29 | #define syscall_exit(name) \ | ||
30 | SEC("syscalls:sys_exit_" #name) syscall_exit_ ## name | ||
31 | |||
29 | #define license(name) \ | 32 | #define license(name) \ |
30 | char _license[] SEC("license") = #name; \ | 33 | char _license[] SEC("license") = #name; \ |
31 | int _version SEC("version") = LINUX_VERSION_CODE; | 34 | int _version SEC("version") = LINUX_VERSION_CODE; |
diff --git a/tools/perf/include/bpf/linux/socket.h b/tools/perf/include/bpf/linux/socket.h new file mode 100644 index 000000000000..7f844568dab8 --- /dev/null +++ b/tools/perf/include/bpf/linux/socket.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | #ifndef _UAPI_LINUX_SOCKET_H | ||
3 | #define _UAPI_LINUX_SOCKET_H | ||
4 | |||
5 | /* | ||
6 | * Desired design of maximum size and alignment (see RFC2553) | ||
7 | */ | ||
8 | #define _K_SS_MAXSIZE 128 /* Implementation specific max size */ | ||
9 | #define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *)) | ||
10 | /* Implementation specific desired alignment */ | ||
11 | |||
12 | typedef unsigned short __kernel_sa_family_t; | ||
13 | |||
14 | struct __kernel_sockaddr_storage { | ||
15 | __kernel_sa_family_t ss_family; /* address family */ | ||
16 | /* Following field(s) are implementation specific */ | ||
17 | char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; | ||
18 | /* space to achieve desired size, */ | ||
19 | /* _SS_MAXSIZE value minus size of ss_family */ | ||
20 | } __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ | ||
21 | |||
22 | #define sockaddr_storage __kernel_sockaddr_storage | ||
23 | |||
24 | #endif /* _UAPI_LINUX_SOCKET_H */ | ||
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c index ac1bcdc17dae..f7eb63cbbc65 100644 --- a/tools/perf/jvmti/jvmti_agent.c +++ b/tools/perf/jvmti/jvmti_agent.c | |||
@@ -125,7 +125,7 @@ perf_get_timestamp(void) | |||
125 | } | 125 | } |
126 | 126 | ||
127 | static int | 127 | static int |
128 | debug_cache_init(void) | 128 | create_jit_cache_dir(void) |
129 | { | 129 | { |
130 | char str[32]; | 130 | char str[32]; |
131 | char *base, *p; | 131 | char *base, *p; |
@@ -144,8 +144,13 @@ debug_cache_init(void) | |||
144 | 144 | ||
145 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); | 145 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); |
146 | 146 | ||
147 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base); | 147 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/", base); |
148 | 148 | if (ret >= PATH_MAX) { | |
149 | warnx("jvmti: cannot generate jit cache dir because %s/.debug/" | ||
150 | " is too long, please check the cwd, JITDUMPDIR, and" | ||
151 | " HOME variables", base); | ||
152 | return -1; | ||
153 | } | ||
149 | ret = mkdir(jit_path, 0755); | 154 | ret = mkdir(jit_path, 0755); |
150 | if (ret == -1) { | 155 | if (ret == -1) { |
151 | if (errno != EEXIST) { | 156 | if (errno != EEXIST) { |
@@ -154,20 +159,32 @@ debug_cache_init(void) | |||
154 | } | 159 | } |
155 | } | 160 | } |
156 | 161 | ||
157 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base); | 162 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit", base); |
163 | if (ret >= PATH_MAX) { | ||
164 | warnx("jvmti: cannot generate jit cache dir because" | ||
165 | " %s/.debug/jit is too long, please check the cwd," | ||
166 | " JITDUMPDIR, and HOME variables", base); | ||
167 | return -1; | ||
168 | } | ||
158 | ret = mkdir(jit_path, 0755); | 169 | ret = mkdir(jit_path, 0755); |
159 | if (ret == -1) { | 170 | if (ret == -1) { |
160 | if (errno != EEXIST) { | 171 | if (errno != EEXIST) { |
161 | warn("cannot create jit cache dir %s", jit_path); | 172 | warn("jvmti: cannot create jit cache dir %s", jit_path); |
162 | return -1; | 173 | return -1; |
163 | } | 174 | } |
164 | } | 175 | } |
165 | 176 | ||
166 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str); | 177 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit/%s.XXXXXXXX", base, str); |
167 | 178 | if (ret >= PATH_MAX) { | |
179 | warnx("jvmti: cannot generate jit cache dir because" | ||
180 | " %s/.debug/jit/%s.XXXXXXXX is too long, please check" | ||
181 | " the cwd, JITDUMPDIR, and HOME variables", | ||
182 | base, str); | ||
183 | return -1; | ||
184 | } | ||
168 | p = mkdtemp(jit_path); | 185 | p = mkdtemp(jit_path); |
169 | if (p != jit_path) { | 186 | if (p != jit_path) { |
170 | warn("cannot create jit cache dir %s", jit_path); | 187 | warn("jvmti: cannot create jit cache dir %s", jit_path); |
171 | return -1; | 188 | return -1; |
172 | } | 189 | } |
173 | 190 | ||
@@ -228,7 +245,7 @@ void *jvmti_open(void) | |||
228 | { | 245 | { |
229 | char dump_path[PATH_MAX]; | 246 | char dump_path[PATH_MAX]; |
230 | struct jitheader header; | 247 | struct jitheader header; |
231 | int fd; | 248 | int fd, ret; |
232 | FILE *fp; | 249 | FILE *fp; |
233 | 250 | ||
234 | init_arch_timestamp(); | 251 | init_arch_timestamp(); |
@@ -245,12 +262,22 @@ void *jvmti_open(void) | |||
245 | 262 | ||
246 | memset(&header, 0, sizeof(header)); | 263 | memset(&header, 0, sizeof(header)); |
247 | 264 | ||
248 | debug_cache_init(); | 265 | /* |
266 | * jitdump file dir | ||
267 | */ | ||
268 | if (create_jit_cache_dir() < 0) | ||
269 | return NULL; | ||
249 | 270 | ||
250 | /* | 271 | /* |
251 | * jitdump file name | 272 | * jitdump file name |
252 | */ | 273 | */ |
253 | scnprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); | 274 | ret = snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); |
275 | if (ret >= PATH_MAX) { | ||
276 | warnx("jvmti: cannot generate jitdump file full path because" | ||
277 | " %s/jit-%i.dump is too long, please check the cwd," | ||
278 | " JITDUMPDIR, and HOME variables", jit_path, getpid()); | ||
279 | return NULL; | ||
280 | } | ||
254 | 281 | ||
255 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); | 282 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); |
256 | if (fd == -1) | 283 | if (fd == -1) |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 21bf7f5a3cf5..0ed4a34c74c4 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -81,6 +81,7 @@ struct record_opts { | |||
81 | unsigned initial_delay; | 81 | unsigned initial_delay; |
82 | bool use_clockid; | 82 | bool use_clockid; |
83 | clockid_t clockid; | 83 | clockid_t clockid; |
84 | u64 clockid_res_ns; | ||
84 | unsigned int proc_map_timeout; | 85 | unsigned int proc_map_timeout; |
85 | }; | 86 | }; |
86 | 87 | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/branch.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/branch.json new file mode 100644 index 000000000000..abc98b018446 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/branch.json | |||
@@ -0,0 +1,23 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "BR_IMMED_SPEC", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "BR_RETURN_SPEC", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "BR_INDIRECT_SPEC", | ||
10 | }, | ||
11 | { | ||
12 | "PublicDescription": "Mispredicted or not predicted branch speculatively executed", | ||
13 | "EventCode": "0x10", | ||
14 | "EventName": "BR_MIS_PRED", | ||
15 | "BriefDescription": "Branch mispredicted" | ||
16 | }, | ||
17 | { | ||
18 | "PublicDescription": "Predictable branch speculatively executed", | ||
19 | "EventCode": "0x12", | ||
20 | "EventName": "BR_PRED", | ||
21 | "BriefDescription": "Predictable branch" | ||
22 | }, | ||
23 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/bus.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/bus.json new file mode 100644 index 000000000000..687b2629e1d1 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/bus.json | |||
@@ -0,0 +1,26 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "BUS_ACCESS_RD", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "BUS_ACCESS_WR", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "BUS_ACCESS_SHARED", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "BUS_ACCESS_NOT_SHARED", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "BUS_ACCESS_NORMAL", | ||
16 | }, | ||
17 | { | ||
18 | "ArchStdEvent": "BUS_ACCESS_PERIPH", | ||
19 | }, | ||
20 | { | ||
21 | "PublicDescription": "Bus access", | ||
22 | "EventCode": "0x19", | ||
23 | "EventName": "BUS_ACCESS", | ||
24 | "BriefDescription": "Bus access" | ||
25 | }, | ||
26 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/cache.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/cache.json new file mode 100644 index 000000000000..df9201434cb6 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/cache.json | |||
@@ -0,0 +1,191 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "L1D_CACHE_RD", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "L1D_CACHE_WR", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "L1D_CACHE_REFILL_RD", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "L1D_CACHE_INVAL", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "L1D_TLB_REFILL_RD", | ||
16 | }, | ||
17 | { | ||
18 | "ArchStdEvent": "L1D_TLB_REFILL_WR", | ||
19 | }, | ||
20 | { | ||
21 | "ArchStdEvent": "L2D_CACHE_RD", | ||
22 | }, | ||
23 | { | ||
24 | "ArchStdEvent": "L2D_CACHE_WR", | ||
25 | }, | ||
26 | { | ||
27 | "ArchStdEvent": "L2D_CACHE_REFILL_RD", | ||
28 | }, | ||
29 | { | ||
30 | "ArchStdEvent": "L2D_CACHE_REFILL_WR", | ||
31 | }, | ||
32 | { | ||
33 | "ArchStdEvent": "L2D_CACHE_WB_VICTIM", | ||
34 | }, | ||
35 | { | ||
36 | "ArchStdEvent": "L2D_CACHE_WB_CLEAN", | ||
37 | }, | ||
38 | { | ||
39 | "ArchStdEvent": "L2D_CACHE_INVAL", | ||
40 | }, | ||
41 | { | ||
42 | "PublicDescription": "Level 1 instruction cache refill", | ||
43 | "EventCode": "0x01", | ||
44 | "EventName": "L1I_CACHE_REFILL", | ||
45 | "BriefDescription": "L1I cache refill" | ||
46 | }, | ||
47 | { | ||
48 | "PublicDescription": "Level 1 instruction TLB refill", | ||
49 | "EventCode": "0x02", | ||
50 | "EventName": "L1I_TLB_REFILL", | ||
51 | "BriefDescription": "L1I TLB refill" | ||
52 | }, | ||
53 | { | ||
54 | "PublicDescription": "Level 1 data cache refill", | ||
55 | "EventCode": "0x03", | ||
56 | "EventName": "L1D_CACHE_REFILL", | ||
57 | "BriefDescription": "L1D cache refill" | ||
58 | }, | ||
59 | { | ||
60 | "PublicDescription": "Level 1 data cache access", | ||
61 | "EventCode": "0x04", | ||
62 | "EventName": "L1D_CACHE_ACCESS", | ||
63 | "BriefDescription": "L1D cache access" | ||
64 | }, | ||
65 | { | ||
66 | "PublicDescription": "Level 1 data TLB refill", | ||
67 | "EventCode": "0x05", | ||
68 | "EventName": "L1D_TLB_REFILL", | ||
69 | "BriefDescription": "L1D TLB refill" | ||
70 | }, | ||
71 | { | ||
72 | "PublicDescription": "Level 1 instruction cache access", | ||
73 | "EventCode": "0x14", | ||
74 | "EventName": "L1I_CACHE_ACCESS", | ||
75 | "BriefDescription": "L1I cache access" | ||
76 | }, | ||
77 | { | ||
78 | "PublicDescription": "Level 2 data cache access", | ||
79 | "EventCode": "0x16", | ||
80 | "EventName": "L2D_CACHE_ACCESS", | ||
81 | "BriefDescription": "L2D cache access" | ||
82 | }, | ||
83 | { | ||
84 | "PublicDescription": "Level 2 data refill", | ||
85 | "EventCode": "0x17", | ||
86 | "EventName": "L2D_CACHE_REFILL", | ||
87 | "BriefDescription": "L2D cache refill" | ||
88 | }, | ||
89 | { | ||
90 | "PublicDescription": "Level 2 data cache, Write-Back", | ||
91 | "EventCode": "0x18", | ||
92 | "EventName": "L2D_CACHE_WB", | ||
93 | "BriefDescription": "L2D cache Write-Back" | ||
94 | }, | ||
95 | { | ||
96 | "PublicDescription": "Level 1 data TLB access. This event counts any load or store operation which accesses the data L1 TLB", | ||
97 | "EventCode": "0x25", | ||
98 | "EventName": "L1D_TLB_ACCESS", | ||
99 | "BriefDescription": "L1D TLB access" | ||
100 | }, | ||
101 | { | ||
102 | "PublicDescription": "Level 1 instruction TLB access. This event counts any instruction fetch which accesses the instruction L1 TLB", | ||
103 | "EventCode": "0x26", | ||
104 | "EventName": "L1I_TLB_ACCESS", | ||
105 | "BriefDescription": "L1I TLB access" | ||
106 | }, | ||
107 | { | ||
108 | "PublicDescription": "Level 2 access to data TLB that caused a page table walk. This event counts on any data access which causes L2D_TLB_REFILL to count", | ||
109 | "EventCode": "0x34", | ||
110 | "EventName": "L2D_TLB_ACCESS", | ||
111 | "BriefDescription": "L2D TLB access" | ||
112 | }, | ||
113 | { | ||
114 | "PublicDescription": "Level 2 access to instruciton TLB that caused a page table walk. This event counts on any instruciton access which causes L2I_TLB_REFILL to count", | ||
115 | "EventCode": "0x35", | ||
116 | "EventName": "L2I_TLB_ACCESS", | ||
117 | "BriefDescription": "L2D TLB access" | ||
118 | }, | ||
119 | { | ||
120 | "PublicDescription": "Branch target buffer misprediction", | ||
121 | "EventCode": "0x102", | ||
122 | "EventName": "BTB_MIS_PRED", | ||
123 | "BriefDescription": "BTB misprediction" | ||
124 | }, | ||
125 | { | ||
126 | "PublicDescription": "ITB miss", | ||
127 | "EventCode": "0x103", | ||
128 | "EventName": "ITB_MISS", | ||
129 | "BriefDescription": "ITB miss" | ||
130 | }, | ||
131 | { | ||
132 | "PublicDescription": "DTB miss", | ||
133 | "EventCode": "0x104", | ||
134 | "EventName": "DTB_MISS", | ||
135 | "BriefDescription": "DTB miss" | ||
136 | }, | ||
137 | { | ||
138 | "PublicDescription": "Level 1 data cache late miss", | ||
139 | "EventCode": "0x105", | ||
140 | "EventName": "L1D_CACHE_LATE_MISS", | ||
141 | "BriefDescription": "L1D cache late miss" | ||
142 | }, | ||
143 | { | ||
144 | "PublicDescription": "Level 1 data cache prefetch request", | ||
145 | "EventCode": "0x106", | ||
146 | "EventName": "L1D_CACHE_PREFETCH", | ||
147 | "BriefDescription": "L1D cache prefetch" | ||
148 | }, | ||
149 | { | ||
150 | "PublicDescription": "Level 2 data cache prefetch request", | ||
151 | "EventCode": "0x107", | ||
152 | "EventName": "L2D_CACHE_PREFETCH", | ||
153 | "BriefDescription": "L2D cache prefetch" | ||
154 | }, | ||
155 | { | ||
156 | "PublicDescription": "Level 1 stage 2 TLB refill", | ||
157 | "EventCode": "0x111", | ||
158 | "EventName": "L1_STAGE2_TLB_REFILL", | ||
159 | "BriefDescription": "L1 stage 2 TLB refill" | ||
160 | }, | ||
161 | { | ||
162 | "PublicDescription": "Page walk cache level-0 stage-1 hit", | ||
163 | "EventCode": "0x112", | ||
164 | "EventName": "PAGE_WALK_L0_STAGE1_HIT", | ||
165 | "BriefDescription": "Page walk, L0 stage-1 hit" | ||
166 | }, | ||
167 | { | ||
168 | "PublicDescription": "Page walk cache level-1 stage-1 hit", | ||
169 | "EventCode": "0x113", | ||
170 | "EventName": "PAGE_WALK_L1_STAGE1_HIT", | ||
171 | "BriefDescription": "Page walk, L1 stage-1 hit" | ||
172 | }, | ||
173 | { | ||
174 | "PublicDescription": "Page walk cache level-2 stage-1 hit", | ||
175 | "EventCode": "0x114", | ||
176 | "EventName": "PAGE_WALK_L2_STAGE1_HIT", | ||
177 | "BriefDescription": "Page walk, L2 stage-1 hit" | ||
178 | }, | ||
179 | { | ||
180 | "PublicDescription": "Page walk cache level-1 stage-2 hit", | ||
181 | "EventCode": "0x115", | ||
182 | "EventName": "PAGE_WALK_L1_STAGE2_HIT", | ||
183 | "BriefDescription": "Page walk, L1 stage-2 hit" | ||
184 | }, | ||
185 | { | ||
186 | "PublicDescription": "Page walk cache level-2 stage-2 hit", | ||
187 | "EventCode": "0x116", | ||
188 | "EventName": "PAGE_WALK_L2_STAGE2_HIT", | ||
189 | "BriefDescription": "Page walk, L2 stage-2 hit" | ||
190 | }, | ||
191 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/clock.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/clock.json new file mode 100644 index 000000000000..38cd1f1a70dc --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/clock.json | |||
@@ -0,0 +1,20 @@ | |||
1 | [ | ||
2 | { | ||
3 | "PublicDescription": "The number of core clock cycles", | ||
4 | "EventCode": "0x11", | ||
5 | "EventName": "CPU_CYCLES", | ||
6 | "BriefDescription": "Clock cycles" | ||
7 | }, | ||
8 | { | ||
9 | "PublicDescription": "FSU clocking gated off cycle", | ||
10 | "EventCode": "0x101", | ||
11 | "EventName": "FSU_CLOCK_OFF_CYCLES", | ||
12 | "BriefDescription": "FSU clocking gated off cycle" | ||
13 | }, | ||
14 | { | ||
15 | "PublicDescription": "Wait state cycle", | ||
16 | "EventCode": "0x110", | ||
17 | "EventName": "Wait_CYCLES", | ||
18 | "BriefDescription": "Wait state cycle" | ||
19 | }, | ||
20 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/core-imp-def.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/core-imp-def.json deleted file mode 100644 index bc03c06c3918..000000000000 --- a/tools/perf/pmu-events/arch/arm64/ampere/emag/core-imp-def.json +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "L1D_CACHE_RD", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "L1D_CACHE_WR", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "L1D_CACHE_REFILL_RD", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "L1D_CACHE_REFILL_WR", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "L1D_TLB_REFILL_RD", | ||
16 | }, | ||
17 | { | ||
18 | "ArchStdEvent": "L1D_TLB_REFILL_WR", | ||
19 | }, | ||
20 | { | ||
21 | "ArchStdEvent": "L1D_TLB_RD", | ||
22 | }, | ||
23 | { | ||
24 | "ArchStdEvent": "L1D_TLB_WR", | ||
25 | }, | ||
26 | { | ||
27 | "ArchStdEvent": "BUS_ACCESS_RD", | ||
28 | }, | ||
29 | { | ||
30 | "ArchStdEvent": "BUS_ACCESS_WR", | ||
31 | } | ||
32 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/exception.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/exception.json new file mode 100644 index 000000000000..3720dc28a15f --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/exception.json | |||
@@ -0,0 +1,50 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "EXC_UNDEF", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "EXC_SVC", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "EXC_PABORT", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "EXC_DABORT", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "EXC_IRQ", | ||
16 | }, | ||
17 | { | ||
18 | "ArchStdEvent": "EXC_FIQ", | ||
19 | }, | ||
20 | { | ||
21 | "ArchStdEvent": "EXC_HVC", | ||
22 | }, | ||
23 | { | ||
24 | "ArchStdEvent": "EXC_TRAP_PABORT", | ||
25 | }, | ||
26 | { | ||
27 | "ArchStdEvent": "EXC_TRAP_DABORT", | ||
28 | }, | ||
29 | { | ||
30 | "ArchStdEvent": "EXC_TRAP_OTHER", | ||
31 | }, | ||
32 | { | ||
33 | "ArchStdEvent": "EXC_TRAP_IRQ", | ||
34 | }, | ||
35 | { | ||
36 | "ArchStdEvent": "EXC_TRAP_FIQ", | ||
37 | }, | ||
38 | { | ||
39 | "PublicDescription": "Exception taken", | ||
40 | "EventCode": "0x09", | ||
41 | "EventName": "EXC_TAKEN", | ||
42 | "BriefDescription": "Exception taken" | ||
43 | }, | ||
44 | { | ||
45 | "PublicDescription": "Instruction architecturally executed, condition check pass, exception return", | ||
46 | "EventCode": "0x0a", | ||
47 | "EventName": "EXC_RETURN", | ||
48 | "BriefDescription": "Exception return" | ||
49 | }, | ||
50 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/instruction.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/instruction.json new file mode 100644 index 000000000000..82cf753e6472 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/instruction.json | |||
@@ -0,0 +1,89 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "LD_SPEC", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "ST_SPEC", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "LDST_SPEC", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "DP_SPEC", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "ASE_SPEC", | ||
16 | }, | ||
17 | { | ||
18 | "ArchStdEvent": "VFP_SPEC", | ||
19 | }, | ||
20 | { | ||
21 | "ArchStdEvent": "PC_WRITE_SPEC", | ||
22 | }, | ||
23 | { | ||
24 | "ArchStdEvent": "CRYPTO_SPEC", | ||
25 | }, | ||
26 | { | ||
27 | "ArchStdEvent": "ISB_SPEC", | ||
28 | }, | ||
29 | { | ||
30 | "ArchStdEvent": "DSB_SPEC", | ||
31 | }, | ||
32 | { | ||
33 | "ArchStdEvent": "DMB_SPEC", | ||
34 | }, | ||
35 | { | ||
36 | "ArchStdEvent": "RC_LD_SPEC", | ||
37 | }, | ||
38 | { | ||
39 | "ArchStdEvent": "RC_ST_SPEC", | ||
40 | }, | ||
41 | { | ||
42 | "PublicDescription": "Instruction architecturally executed, software increment", | ||
43 | "EventCode": "0x00", | ||
44 | "EventName": "SW_INCR", | ||
45 | "BriefDescription": "Software increment" | ||
46 | }, | ||
47 | { | ||
48 | "PublicDescription": "Instruction architecturally executed", | ||
49 | "EventCode": "0x08", | ||
50 | "EventName": "INST_RETIRED", | ||
51 | "BriefDescription": "Instruction retired" | ||
52 | }, | ||
53 | { | ||
54 | "PublicDescription": "Instruction architecturally executed, condition code check pass, write to CONTEXTIDR", | ||
55 | "EventCode": "0x0b", | ||
56 | "EventName": "CID_WRITE_RETIRED", | ||
57 | "BriefDescription": "Write to CONTEXTIDR" | ||
58 | }, | ||
59 | { | ||
60 | "PublicDescription": "Operation speculatively executed", | ||
61 | "EventCode": "0x1b", | ||
62 | "EventName": "INST_SPEC", | ||
63 | "BriefDescription": "Speculatively executed" | ||
64 | }, | ||
65 | { | ||
66 | "PublicDescription": "Instruction architecturally executed (condition check pass), write to TTBR", | ||
67 | "EventCode": "0x1c", | ||
68 | "EventName": "TTBR_WRITE_RETIRED", | ||
69 | "BriefDescription": "Instruction executed, TTBR write" | ||
70 | }, | ||
71 | { | ||
72 | "PublicDescription": "Instruction architecturally executed, branch. This event counts all branches, taken or not. This excludes exception entries, debug entries and CCFAIL branches", | ||
73 | "EventCode": "0x21", | ||
74 | "EventName": "BR_RETIRED", | ||
75 | "BriefDescription": "Branch retired" | ||
76 | }, | ||
77 | { | ||
78 | "PublicDescription": "Instruction architecturally executed, mispredicted branch. This event counts any branch counted by BR_RETIRED which is not correctly predicted and causes a pipeline flush", | ||
79 | "EventCode": "0x22", | ||
80 | "EventName": "BR_MISPRED_RETIRED", | ||
81 | "BriefDescription": "Mispredicted branch retired" | ||
82 | }, | ||
83 | { | ||
84 | "PublicDescription": "Operation speculatively executed, NOP", | ||
85 | "EventCode": "0x100", | ||
86 | "EventName": "NOP_SPEC", | ||
87 | "BriefDescription": "Speculatively executed, NOP" | ||
88 | }, | ||
89 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/intrinsic.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/intrinsic.json new file mode 100644 index 000000000000..2aecc5c2347d --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/intrinsic.json | |||
@@ -0,0 +1,14 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "LDREX_SPEC", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "STREX_PASS_SPEC", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "STREX_FAIL_SPEC", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "STREX_SPEC", | ||
13 | }, | ||
14 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/memory.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/memory.json new file mode 100644 index 000000000000..08508697b318 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/memory.json | |||
@@ -0,0 +1,29 @@ | |||
1 | [ | ||
2 | { | ||
3 | "ArchStdEvent": "MEM_ACCESS_RD", | ||
4 | }, | ||
5 | { | ||
6 | "ArchStdEvent": "MEM_ACCESS_WR", | ||
7 | }, | ||
8 | { | ||
9 | "ArchStdEvent": "UNALIGNED_LD_SPEC", | ||
10 | }, | ||
11 | { | ||
12 | "ArchStdEvent": "UNALIGNED_ST_SPEC", | ||
13 | }, | ||
14 | { | ||
15 | "ArchStdEvent": "UNALIGNED_LDST_SPEC", | ||
16 | }, | ||
17 | { | ||
18 | "PublicDescription": "Data memory access", | ||
19 | "EventCode": "0x13", | ||
20 | "EventName": "MEM_ACCESS", | ||
21 | "BriefDescription": "Memory access" | ||
22 | }, | ||
23 | { | ||
24 | "PublicDescription": "Local memory error. This event counts any correctable or uncorrectable memory error (ECC or parity) in the protected core RAMs", | ||
25 | "EventCode": "0x1a", | ||
26 | "EventName": "MEM_ERROR", | ||
27 | "BriefDescription": "Memory error" | ||
28 | }, | ||
29 | ] | ||
diff --git a/tools/perf/pmu-events/arch/arm64/ampere/emag/pipeline.json b/tools/perf/pmu-events/arch/arm64/ampere/emag/pipeline.json new file mode 100644 index 000000000000..e2087de586bf --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/emag/pipeline.json | |||
@@ -0,0 +1,50 @@ | |||
1 | [ | ||
2 | { | ||
3 | "PublicDescription": "Decode starved for instruction cycle", | ||
4 | "EventCode": "0x108", | ||
5 | "EventName": "DECODE_STALL", | ||
6 | "BriefDescription": "Decode starved" | ||
7 | }, | ||
8 | { | ||
9 | "PublicDescription": "Op dispatch stalled cycle", | ||
10 | "EventCode": "0x109", | ||
11 | "EventName": "DISPATCH_STALL", | ||
12 | "BriefDescription": "Dispatch stalled" | ||
13 | }, | ||
14 | { | ||
15 | "PublicDescription": "IXA Op non-issue", | ||
16 | "EventCode": "0x10a", | ||
17 | "EventName": "IXA_STALL", | ||
18 | "BriefDescription": "IXA stalled" | ||
19 | }, | ||
20 | { | ||
21 | "PublicDescription": "IXB Op non-issue", | ||
22 | "EventCode": "0x10b", | ||
23 | "EventName": "IXB_STALL", | ||
24 | "BriefDescription": "IXB stalled" | ||
25 | }, | ||
26 | { | ||
27 | "PublicDescription": "BX Op non-issue", | ||
28 | "EventCode": "0x10c", | ||
29 | "EventName": "BX_STALL", | ||
30 | "BriefDescription": "BX stalled" | ||
31 | }, | ||
32 | { | ||
33 | "PublicDescription": "LX Op non-issue", | ||
34 | "EventCode": "0x10d", | ||
35 | "EventName": "LX_STALL", | ||
36 | "BriefDescription": "LX stalled" | ||
37 | }, | ||
38 | { | ||
39 | "PublicDescription": "SX Op non-issue", | ||
40 | "EventCode": "0x10e", | ||
41 | "EventName": "SX_STALL", | ||
42 | "BriefDescription": "SX stalled" | ||
43 | }, | ||
44 | { | ||
45 | "PublicDescription": "FX Op non-issue", | ||
46 | "EventCode": "0x10f", | ||
47 | "EventName": "FX_STALL", | ||
48 | "BriefDescription": "FX stalled" | ||
49 | }, | ||
50 | ] | ||
diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json index d40498f2cb1e..635c09fda1d9 100644 --- a/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json +++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json | |||
@@ -188,7 +188,7 @@ | |||
188 | "Counter": "0,1,2,3", | 188 | "Counter": "0,1,2,3", |
189 | "EventCode": "0xb", | 189 | "EventCode": "0xb", |
190 | "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", | 190 | "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", |
191 | "Filter": "filter_band0=1200", | 191 | "Filter": "filter_band0=12", |
192 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 192 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
193 | "MetricName": "freq_ge_1200mhz_cycles %", | 193 | "MetricName": "freq_ge_1200mhz_cycles %", |
194 | "PerPkg": "1", | 194 | "PerPkg": "1", |
@@ -199,7 +199,7 @@ | |||
199 | "Counter": "0,1,2,3", | 199 | "Counter": "0,1,2,3", |
200 | "EventCode": "0xc", | 200 | "EventCode": "0xc", |
201 | "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", | 201 | "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", |
202 | "Filter": "filter_band1=2000", | 202 | "Filter": "filter_band1=20", |
203 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 203 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
204 | "MetricName": "freq_ge_2000mhz_cycles %", | 204 | "MetricName": "freq_ge_2000mhz_cycles %", |
205 | "PerPkg": "1", | 205 | "PerPkg": "1", |
@@ -210,7 +210,7 @@ | |||
210 | "Counter": "0,1,2,3", | 210 | "Counter": "0,1,2,3", |
211 | "EventCode": "0xd", | 211 | "EventCode": "0xd", |
212 | "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", | 212 | "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", |
213 | "Filter": "filter_band2=3000", | 213 | "Filter": "filter_band2=30", |
214 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 214 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
215 | "MetricName": "freq_ge_3000mhz_cycles %", | 215 | "MetricName": "freq_ge_3000mhz_cycles %", |
216 | "PerPkg": "1", | 216 | "PerPkg": "1", |
@@ -221,7 +221,7 @@ | |||
221 | "Counter": "0,1,2,3", | 221 | "Counter": "0,1,2,3", |
222 | "EventCode": "0xe", | 222 | "EventCode": "0xe", |
223 | "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", | 223 | "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", |
224 | "Filter": "filter_band3=4000", | 224 | "Filter": "filter_band3=40", |
225 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 225 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
226 | "MetricName": "freq_ge_4000mhz_cycles %", | 226 | "MetricName": "freq_ge_4000mhz_cycles %", |
227 | "PerPkg": "1", | 227 | "PerPkg": "1", |
@@ -232,7 +232,7 @@ | |||
232 | "Counter": "0,1,2,3", | 232 | "Counter": "0,1,2,3", |
233 | "EventCode": "0xb", | 233 | "EventCode": "0xb", |
234 | "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", | 234 | "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", |
235 | "Filter": "edge=1,filter_band0=1200", | 235 | "Filter": "edge=1,filter_band0=12", |
236 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 236 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
237 | "MetricName": "freq_ge_1200mhz_cycles %", | 237 | "MetricName": "freq_ge_1200mhz_cycles %", |
238 | "PerPkg": "1", | 238 | "PerPkg": "1", |
@@ -243,7 +243,7 @@ | |||
243 | "Counter": "0,1,2,3", | 243 | "Counter": "0,1,2,3", |
244 | "EventCode": "0xc", | 244 | "EventCode": "0xc", |
245 | "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", | 245 | "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", |
246 | "Filter": "edge=1,filter_band1=2000", | 246 | "Filter": "edge=1,filter_band1=20", |
247 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 247 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
248 | "MetricName": "freq_ge_2000mhz_cycles %", | 248 | "MetricName": "freq_ge_2000mhz_cycles %", |
249 | "PerPkg": "1", | 249 | "PerPkg": "1", |
@@ -254,7 +254,7 @@ | |||
254 | "Counter": "0,1,2,3", | 254 | "Counter": "0,1,2,3", |
255 | "EventCode": "0xd", | 255 | "EventCode": "0xd", |
256 | "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", | 256 | "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", |
257 | "Filter": "edge=1,filter_band2=4000", | 257 | "Filter": "edge=1,filter_band2=30", |
258 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 258 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
259 | "MetricName": "freq_ge_3000mhz_cycles %", | 259 | "MetricName": "freq_ge_3000mhz_cycles %", |
260 | "PerPkg": "1", | 260 | "PerPkg": "1", |
@@ -265,7 +265,7 @@ | |||
265 | "Counter": "0,1,2,3", | 265 | "Counter": "0,1,2,3", |
266 | "EventCode": "0xe", | 266 | "EventCode": "0xe", |
267 | "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", | 267 | "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", |
268 | "Filter": "edge=1,filter_band3=4000", | 268 | "Filter": "edge=1,filter_band3=40", |
269 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 269 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
270 | "MetricName": "freq_ge_4000mhz_cycles %", | 270 | "MetricName": "freq_ge_4000mhz_cycles %", |
271 | "PerPkg": "1", | 271 | "PerPkg": "1", |
diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json index 16034bfd06dd..8755693d86c6 100644 --- a/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json +++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json | |||
@@ -187,7 +187,7 @@ | |||
187 | "Counter": "0,1,2,3", | 187 | "Counter": "0,1,2,3", |
188 | "EventCode": "0xb", | 188 | "EventCode": "0xb", |
189 | "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", | 189 | "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", |
190 | "Filter": "filter_band0=1200", | 190 | "Filter": "filter_band0=12", |
191 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 191 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
192 | "MetricName": "freq_ge_1200mhz_cycles %", | 192 | "MetricName": "freq_ge_1200mhz_cycles %", |
193 | "PerPkg": "1", | 193 | "PerPkg": "1", |
@@ -198,7 +198,7 @@ | |||
198 | "Counter": "0,1,2,3", | 198 | "Counter": "0,1,2,3", |
199 | "EventCode": "0xc", | 199 | "EventCode": "0xc", |
200 | "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", | 200 | "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", |
201 | "Filter": "filter_band1=2000", | 201 | "Filter": "filter_band1=20", |
202 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 202 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
203 | "MetricName": "freq_ge_2000mhz_cycles %", | 203 | "MetricName": "freq_ge_2000mhz_cycles %", |
204 | "PerPkg": "1", | 204 | "PerPkg": "1", |
@@ -209,7 +209,7 @@ | |||
209 | "Counter": "0,1,2,3", | 209 | "Counter": "0,1,2,3", |
210 | "EventCode": "0xd", | 210 | "EventCode": "0xd", |
211 | "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", | 211 | "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", |
212 | "Filter": "filter_band2=3000", | 212 | "Filter": "filter_band2=30", |
213 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 213 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
214 | "MetricName": "freq_ge_3000mhz_cycles %", | 214 | "MetricName": "freq_ge_3000mhz_cycles %", |
215 | "PerPkg": "1", | 215 | "PerPkg": "1", |
@@ -220,7 +220,7 @@ | |||
220 | "Counter": "0,1,2,3", | 220 | "Counter": "0,1,2,3", |
221 | "EventCode": "0xe", | 221 | "EventCode": "0xe", |
222 | "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", | 222 | "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", |
223 | "Filter": "filter_band3=4000", | 223 | "Filter": "filter_band3=40", |
224 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 224 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
225 | "MetricName": "freq_ge_4000mhz_cycles %", | 225 | "MetricName": "freq_ge_4000mhz_cycles %", |
226 | "PerPkg": "1", | 226 | "PerPkg": "1", |
@@ -231,7 +231,7 @@ | |||
231 | "Counter": "0,1,2,3", | 231 | "Counter": "0,1,2,3", |
232 | "EventCode": "0xb", | 232 | "EventCode": "0xb", |
233 | "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", | 233 | "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", |
234 | "Filter": "edge=1,filter_band0=1200", | 234 | "Filter": "edge=1,filter_band0=12", |
235 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 235 | "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
236 | "MetricName": "freq_ge_1200mhz_cycles %", | 236 | "MetricName": "freq_ge_1200mhz_cycles %", |
237 | "PerPkg": "1", | 237 | "PerPkg": "1", |
@@ -242,7 +242,7 @@ | |||
242 | "Counter": "0,1,2,3", | 242 | "Counter": "0,1,2,3", |
243 | "EventCode": "0xc", | 243 | "EventCode": "0xc", |
244 | "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", | 244 | "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", |
245 | "Filter": "edge=1,filter_band1=2000", | 245 | "Filter": "edge=1,filter_band1=20", |
246 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 246 | "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
247 | "MetricName": "freq_ge_2000mhz_cycles %", | 247 | "MetricName": "freq_ge_2000mhz_cycles %", |
248 | "PerPkg": "1", | 248 | "PerPkg": "1", |
@@ -253,7 +253,7 @@ | |||
253 | "Counter": "0,1,2,3", | 253 | "Counter": "0,1,2,3", |
254 | "EventCode": "0xd", | 254 | "EventCode": "0xd", |
255 | "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", | 255 | "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", |
256 | "Filter": "edge=1,filter_band2=4000", | 256 | "Filter": "edge=1,filter_band2=30", |
257 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 257 | "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
258 | "MetricName": "freq_ge_3000mhz_cycles %", | 258 | "MetricName": "freq_ge_3000mhz_cycles %", |
259 | "PerPkg": "1", | 259 | "PerPkg": "1", |
@@ -264,7 +264,7 @@ | |||
264 | "Counter": "0,1,2,3", | 264 | "Counter": "0,1,2,3", |
265 | "EventCode": "0xe", | 265 | "EventCode": "0xe", |
266 | "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", | 266 | "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", |
267 | "Filter": "edge=1,filter_band3=4000", | 267 | "Filter": "edge=1,filter_band3=40", |
268 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", | 268 | "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", |
269 | "MetricName": "freq_ge_4000mhz_cycles %", | 269 | "MetricName": "freq_ge_4000mhz_cycles %", |
270 | "PerPkg": "1", | 270 | "PerPkg": "1", |
diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py deleted file mode 100644 index b494a67a1c67..000000000000 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ /dev/null | |||
@@ -1,339 +0,0 @@ | |||
1 | #!/usr/bin/python2 | ||
2 | # call-graph-from-sql.py: create call-graph from sql database | ||
3 | # Copyright (c) 2014-2017, Intel Corporation. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms and conditions of the GNU General Public License, | ||
7 | # version 2, as published by the Free Software Foundation. | ||
8 | # | ||
9 | # This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | # more details. | ||
13 | |||
14 | # To use this script you will need to have exported data using either the | ||
15 | # export-to-sqlite.py or the export-to-postgresql.py script. Refer to those | ||
16 | # scripts for details. | ||
17 | # | ||
18 | # Following on from the example in the export scripts, a | ||
19 | # call-graph can be displayed for the pt_example database like this: | ||
20 | # | ||
21 | # python tools/perf/scripts/python/call-graph-from-sql.py pt_example | ||
22 | # | ||
23 | # Note that for PostgreSQL, this script supports connecting to remote databases | ||
24 | # by setting hostname, port, username, password, and dbname e.g. | ||
25 | # | ||
26 | # python tools/perf/scripts/python/call-graph-from-sql.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" | ||
27 | # | ||
28 | # The result is a GUI window with a tree representing a context-sensitive | ||
29 | # call-graph. Expanding a couple of levels of the tree and adjusting column | ||
30 | # widths to suit will display something like: | ||
31 | # | ||
32 | # Call Graph: pt_example | ||
33 | # Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) | ||
34 | # v- ls | ||
35 | # v- 2638:2638 | ||
36 | # v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 | ||
37 | # |- unknown unknown 1 13198 0.1 1 0.0 | ||
38 | # >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 | ||
39 | # >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 | ||
40 | # v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 | ||
41 | # >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 | ||
42 | # >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 | ||
43 | # >- __libc_csu_init ls 1 10354 0.1 10 0.0 | ||
44 | # |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 | ||
45 | # v- main ls 1 8182043 99.6 180254 99.9 | ||
46 | # | ||
47 | # Points to note: | ||
48 | # The top level is a command name (comm) | ||
49 | # The next level is a thread (pid:tid) | ||
50 | # Subsequent levels are functions | ||
51 | # 'Count' is the number of calls | ||
52 | # 'Time' is the elapsed time until the function returns | ||
53 | # Percentages are relative to the level above | ||
54 | # 'Branch Count' is the total number of branches for that function and all | ||
55 | # functions that it calls | ||
56 | |||
57 | import sys | ||
58 | from PySide.QtCore import * | ||
59 | from PySide.QtGui import * | ||
60 | from PySide.QtSql import * | ||
61 | from decimal import * | ||
62 | |||
63 | class TreeItem(): | ||
64 | |||
65 | def __init__(self, db, row, parent_item): | ||
66 | self.db = db | ||
67 | self.row = row | ||
68 | self.parent_item = parent_item | ||
69 | self.query_done = False; | ||
70 | self.child_count = 0 | ||
71 | self.child_items = [] | ||
72 | self.data = ["", "", "", "", "", "", ""] | ||
73 | self.comm_id = 0 | ||
74 | self.thread_id = 0 | ||
75 | self.call_path_id = 1 | ||
76 | self.branch_count = 0 | ||
77 | self.time = 0 | ||
78 | if not parent_item: | ||
79 | self.setUpRoot() | ||
80 | |||
81 | def setUpRoot(self): | ||
82 | self.query_done = True | ||
83 | query = QSqlQuery(self.db) | ||
84 | ret = query.exec_('SELECT id, comm FROM comms') | ||
85 | if not ret: | ||
86 | raise Exception("Query failed: " + query.lastError().text()) | ||
87 | while query.next(): | ||
88 | if not query.value(0): | ||
89 | continue | ||
90 | child_item = TreeItem(self.db, self.child_count, self) | ||
91 | self.child_items.append(child_item) | ||
92 | self.child_count += 1 | ||
93 | child_item.setUpLevel1(query.value(0), query.value(1)) | ||
94 | |||
95 | def setUpLevel1(self, comm_id, comm): | ||
96 | self.query_done = True; | ||
97 | self.comm_id = comm_id | ||
98 | self.data[0] = comm | ||
99 | self.child_items = [] | ||
100 | self.child_count = 0 | ||
101 | query = QSqlQuery(self.db) | ||
102 | ret = query.exec_('SELECT thread_id, ( SELECT pid FROM threads WHERE id = thread_id ), ( SELECT tid FROM threads WHERE id = thread_id ) FROM comm_threads WHERE comm_id = ' + str(comm_id)) | ||
103 | if not ret: | ||
104 | raise Exception("Query failed: " + query.lastError().text()) | ||
105 | while query.next(): | ||
106 | child_item = TreeItem(self.db, self.child_count, self) | ||
107 | self.child_items.append(child_item) | ||
108 | self.child_count += 1 | ||
109 | child_item.setUpLevel2(comm_id, query.value(0), query.value(1), query.value(2)) | ||
110 | |||
111 | def setUpLevel2(self, comm_id, thread_id, pid, tid): | ||
112 | self.comm_id = comm_id | ||
113 | self.thread_id = thread_id | ||
114 | self.data[0] = str(pid) + ":" + str(tid) | ||
115 | |||
116 | def getChildItem(self, row): | ||
117 | return self.child_items[row] | ||
118 | |||
119 | def getParentItem(self): | ||
120 | return self.parent_item | ||
121 | |||
122 | def getRow(self): | ||
123 | return self.row | ||
124 | |||
125 | def timePercent(self, b): | ||
126 | if not self.time: | ||
127 | return "0.0" | ||
128 | x = (b * Decimal(100)) / self.time | ||
129 | return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP)) | ||
130 | |||
131 | def branchPercent(self, b): | ||
132 | if not self.branch_count: | ||
133 | return "0.0" | ||
134 | x = (b * Decimal(100)) / self.branch_count | ||
135 | return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP)) | ||
136 | |||
137 | def addChild(self, call_path_id, name, dso, count, time, branch_count): | ||
138 | child_item = TreeItem(self.db, self.child_count, self) | ||
139 | child_item.comm_id = self.comm_id | ||
140 | child_item.thread_id = self.thread_id | ||
141 | child_item.call_path_id = call_path_id | ||
142 | child_item.branch_count = branch_count | ||
143 | child_item.time = time | ||
144 | child_item.data[0] = name | ||
145 | if dso == "[kernel.kallsyms]": | ||
146 | dso = "[kernel]" | ||
147 | child_item.data[1] = dso | ||
148 | child_item.data[2] = str(count) | ||
149 | child_item.data[3] = str(time) | ||
150 | child_item.data[4] = self.timePercent(time) | ||
151 | child_item.data[5] = str(branch_count) | ||
152 | child_item.data[6] = self.branchPercent(branch_count) | ||
153 | self.child_items.append(child_item) | ||
154 | self.child_count += 1 | ||
155 | |||
156 | def selectCalls(self): | ||
157 | self.query_done = True; | ||
158 | query = QSqlQuery(self.db) | ||
159 | ret = query.exec_('SELECT id, call_path_id, branch_count, call_time, return_time, ' | ||
160 | '( SELECT name FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ), ' | ||
161 | '( SELECT short_name FROM dsos WHERE id = ( SELECT dso_id FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ) ), ' | ||
162 | '( SELECT ip FROM call_paths where id = call_path_id ) ' | ||
163 | 'FROM calls WHERE parent_call_path_id = ' + str(self.call_path_id) + ' AND comm_id = ' + str(self.comm_id) + ' AND thread_id = ' + str(self.thread_id) + | ||
164 | ' ORDER BY call_path_id') | ||
165 | if not ret: | ||
166 | raise Exception("Query failed: " + query.lastError().text()) | ||
167 | last_call_path_id = 0 | ||
168 | name = "" | ||
169 | dso = "" | ||
170 | count = 0 | ||
171 | branch_count = 0 | ||
172 | total_branch_count = 0 | ||
173 | time = 0 | ||
174 | total_time = 0 | ||
175 | while query.next(): | ||
176 | if query.value(1) == last_call_path_id: | ||
177 | count += 1 | ||
178 | branch_count += query.value(2) | ||
179 | time += query.value(4) - query.value(3) | ||
180 | else: | ||
181 | if count: | ||
182 | self.addChild(last_call_path_id, name, dso, count, time, branch_count) | ||
183 | last_call_path_id = query.value(1) | ||
184 | name = query.value(5) | ||
185 | dso = query.value(6) | ||
186 | count = 1 | ||
187 | total_branch_count += branch_count | ||
188 | total_time += time | ||
189 | branch_count = query.value(2) | ||
190 | time = query.value(4) - query.value(3) | ||
191 | if count: | ||
192 | self.addChild(last_call_path_id, name, dso, count, time, branch_count) | ||
193 | total_branch_count += branch_count | ||
194 | total_time += time | ||
195 | # Top level does not have time or branch count, so fix that here | ||
196 | if total_branch_count > self.branch_count: | ||
197 | self.branch_count = total_branch_count | ||
198 | if self.branch_count: | ||
199 | for child_item in self.child_items: | ||
200 | child_item.data[6] = self.branchPercent(child_item.branch_count) | ||
201 | if total_time > self.time: | ||
202 | self.time = total_time | ||
203 | if self.time: | ||
204 | for child_item in self.child_items: | ||
205 | child_item.data[4] = self.timePercent(child_item.time) | ||
206 | |||
207 | def childCount(self): | ||
208 | if not self.query_done: | ||
209 | self.selectCalls() | ||
210 | return self.child_count | ||
211 | |||
212 | def columnCount(self): | ||
213 | return 7 | ||
214 | |||
215 | def columnHeader(self, column): | ||
216 | headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] | ||
217 | return headers[column] | ||
218 | |||
219 | def getData(self, column): | ||
220 | return self.data[column] | ||
221 | |||
222 | class TreeModel(QAbstractItemModel): | ||
223 | |||
224 | def __init__(self, db, parent=None): | ||
225 | super(TreeModel, self).__init__(parent) | ||
226 | self.db = db | ||
227 | self.root = TreeItem(db, 0, None) | ||
228 | |||
229 | def columnCount(self, parent): | ||
230 | return self.root.columnCount() | ||
231 | |||
232 | def rowCount(self, parent): | ||
233 | if parent.isValid(): | ||
234 | parent_item = parent.internalPointer() | ||
235 | else: | ||
236 | parent_item = self.root | ||
237 | return parent_item.childCount() | ||
238 | |||
239 | def headerData(self, section, orientation, role): | ||
240 | if role == Qt.TextAlignmentRole: | ||
241 | if section > 1: | ||
242 | return Qt.AlignRight | ||
243 | if role != Qt.DisplayRole: | ||
244 | return None | ||
245 | if orientation != Qt.Horizontal: | ||
246 | return None | ||
247 | return self.root.columnHeader(section) | ||
248 | |||
249 | def parent(self, child): | ||
250 | child_item = child.internalPointer() | ||
251 | if child_item is self.root: | ||
252 | return QModelIndex() | ||
253 | parent_item = child_item.getParentItem() | ||
254 | return self.createIndex(parent_item.getRow(), 0, parent_item) | ||
255 | |||
256 | def index(self, row, column, parent): | ||
257 | if parent.isValid(): | ||
258 | parent_item = parent.internalPointer() | ||
259 | else: | ||
260 | parent_item = self.root | ||
261 | child_item = parent_item.getChildItem(row) | ||
262 | return self.createIndex(row, column, child_item) | ||
263 | |||
264 | def data(self, index, role): | ||
265 | if role == Qt.TextAlignmentRole: | ||
266 | if index.column() > 1: | ||
267 | return Qt.AlignRight | ||
268 | if role != Qt.DisplayRole: | ||
269 | return None | ||
270 | index_item = index.internalPointer() | ||
271 | return index_item.getData(index.column()) | ||
272 | |||
273 | class MainWindow(QMainWindow): | ||
274 | |||
275 | def __init__(self, db, dbname, parent=None): | ||
276 | super(MainWindow, self).__init__(parent) | ||
277 | |||
278 | self.setObjectName("MainWindow") | ||
279 | self.setWindowTitle("Call Graph: " + dbname) | ||
280 | self.move(100, 100) | ||
281 | self.resize(800, 600) | ||
282 | style = self.style() | ||
283 | icon = style.standardIcon(QStyle.SP_MessageBoxInformation) | ||
284 | self.setWindowIcon(icon); | ||
285 | |||
286 | self.model = TreeModel(db) | ||
287 | |||
288 | self.view = QTreeView() | ||
289 | self.view.setModel(self.model) | ||
290 | |||
291 | self.setCentralWidget(self.view) | ||
292 | |||
293 | if __name__ == '__main__': | ||
294 | if (len(sys.argv) < 2): | ||
295 | print >> sys.stderr, "Usage is: call-graph-from-sql.py <database name>" | ||
296 | raise Exception("Too few arguments") | ||
297 | |||
298 | dbname = sys.argv[1] | ||
299 | |||
300 | is_sqlite3 = False | ||
301 | try: | ||
302 | f = open(dbname) | ||
303 | if f.read(15) == "SQLite format 3": | ||
304 | is_sqlite3 = True | ||
305 | f.close() | ||
306 | except: | ||
307 | pass | ||
308 | |||
309 | if is_sqlite3: | ||
310 | db = QSqlDatabase.addDatabase('QSQLITE') | ||
311 | else: | ||
312 | db = QSqlDatabase.addDatabase('QPSQL') | ||
313 | opts = dbname.split() | ||
314 | for opt in opts: | ||
315 | if '=' in opt: | ||
316 | opt = opt.split('=') | ||
317 | if opt[0] == 'hostname': | ||
318 | db.setHostName(opt[1]) | ||
319 | elif opt[0] == 'port': | ||
320 | db.setPort(int(opt[1])) | ||
321 | elif opt[0] == 'username': | ||
322 | db.setUserName(opt[1]) | ||
323 | elif opt[0] == 'password': | ||
324 | db.setPassword(opt[1]) | ||
325 | elif opt[0] == 'dbname': | ||
326 | dbname = opt[1] | ||
327 | else: | ||
328 | dbname = opt | ||
329 | |||
330 | db.setDatabaseName(dbname) | ||
331 | if not db.open(): | ||
332 | raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) | ||
333 | |||
334 | app = QApplication(sys.argv) | ||
335 | window = MainWindow(db, dbname) | ||
336 | window.show() | ||
337 | err = app.exec_() | ||
338 | db.close() | ||
339 | sys.exit(err) | ||
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index efcaf6cac2eb..0564dd7377f2 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
@@ -59,7 +59,7 @@ import datetime | |||
59 | # pt_example=# \q | 59 | # pt_example=# \q |
60 | # | 60 | # |
61 | # An example of using the database is provided by the script | 61 | # An example of using the database is provided by the script |
62 | # call-graph-from-sql.py. Refer to that script for details. | 62 | # exported-sql-viewer.py. Refer to that script for details. |
63 | # | 63 | # |
64 | # Tables: | 64 | # Tables: |
65 | # | 65 | # |
@@ -204,14 +204,23 @@ from ctypes import * | |||
204 | libpq = CDLL("libpq.so.5") | 204 | libpq = CDLL("libpq.so.5") |
205 | PQconnectdb = libpq.PQconnectdb | 205 | PQconnectdb = libpq.PQconnectdb |
206 | PQconnectdb.restype = c_void_p | 206 | PQconnectdb.restype = c_void_p |
207 | PQconnectdb.argtypes = [ c_char_p ] | ||
207 | PQfinish = libpq.PQfinish | 208 | PQfinish = libpq.PQfinish |
209 | PQfinish.argtypes = [ c_void_p ] | ||
208 | PQstatus = libpq.PQstatus | 210 | PQstatus = libpq.PQstatus |
211 | PQstatus.restype = c_int | ||
212 | PQstatus.argtypes = [ c_void_p ] | ||
209 | PQexec = libpq.PQexec | 213 | PQexec = libpq.PQexec |
210 | PQexec.restype = c_void_p | 214 | PQexec.restype = c_void_p |
215 | PQexec.argtypes = [ c_void_p, c_char_p ] | ||
211 | PQresultStatus = libpq.PQresultStatus | 216 | PQresultStatus = libpq.PQresultStatus |
217 | PQresultStatus.restype = c_int | ||
218 | PQresultStatus.argtypes = [ c_void_p ] | ||
212 | PQputCopyData = libpq.PQputCopyData | 219 | PQputCopyData = libpq.PQputCopyData |
220 | PQputCopyData.restype = c_int | ||
213 | PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ] | 221 | PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ] |
214 | PQputCopyEnd = libpq.PQputCopyEnd | 222 | PQputCopyEnd = libpq.PQputCopyEnd |
223 | PQputCopyEnd.restype = c_int | ||
215 | PQputCopyEnd.argtypes = [ c_void_p, c_void_p ] | 224 | PQputCopyEnd.argtypes = [ c_void_p, c_void_p ] |
216 | 225 | ||
217 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | 226 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ |
diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py index f827bf77e9d2..245caf2643ed 100644 --- a/tools/perf/scripts/python/export-to-sqlite.py +++ b/tools/perf/scripts/python/export-to-sqlite.py | |||
@@ -40,7 +40,7 @@ import datetime | |||
40 | # sqlite> .quit | 40 | # sqlite> .quit |
41 | # | 41 | # |
42 | # An example of using the database is provided by the script | 42 | # An example of using the database is provided by the script |
43 | # call-graph-from-sql.py. Refer to that script for details. | 43 | # exported-sql-viewer.py. Refer to that script for details. |
44 | # | 44 | # |
45 | # The database structure is practically the same as created by the script | 45 | # The database structure is practically the same as created by the script |
46 | # export-to-postgresql.py. Refer to that script for details. A notable | 46 | # export-to-postgresql.py. Refer to that script for details. A notable |
@@ -440,7 +440,11 @@ def branch_type_table(*x): | |||
440 | 440 | ||
441 | def sample_table(*x): | 441 | def sample_table(*x): |
442 | if branches: | 442 | if branches: |
443 | bind_exec(sample_query, 18, x) | 443 | for xx in x[0:15]: |
444 | sample_query.addBindValue(str(xx)) | ||
445 | for xx in x[19:22]: | ||
446 | sample_query.addBindValue(str(xx)) | ||
447 | do_query_(sample_query) | ||
444 | else: | 448 | else: |
445 | bind_exec(sample_query, 22, x) | 449 | bind_exec(sample_query, 22, x) |
446 | 450 | ||
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py new file mode 100755 index 000000000000..f278ce5ebab7 --- /dev/null +++ b/tools/perf/scripts/python/exported-sql-viewer.py | |||
@@ -0,0 +1,2615 @@ | |||
1 | #!/usr/bin/python2 | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # exported-sql-viewer.py: view data from sql database | ||
4 | # Copyright (c) 2014-2018, Intel Corporation. | ||
5 | |||
6 | # To use this script you will need to have exported data using either the | ||
7 | # export-to-sqlite.py or the export-to-postgresql.py script. Refer to those | ||
8 | # scripts for details. | ||
9 | # | ||
10 | # Following on from the example in the export scripts, a | ||
11 | # call-graph can be displayed for the pt_example database like this: | ||
12 | # | ||
13 | # python tools/perf/scripts/python/exported-sql-viewer.py pt_example | ||
14 | # | ||
15 | # Note that for PostgreSQL, this script supports connecting to remote databases | ||
16 | # by setting hostname, port, username, password, and dbname e.g. | ||
17 | # | ||
18 | # python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" | ||
19 | # | ||
20 | # The result is a GUI window with a tree representing a context-sensitive | ||
21 | # call-graph. Expanding a couple of levels of the tree and adjusting column | ||
22 | # widths to suit will display something like: | ||
23 | # | ||
24 | # Call Graph: pt_example | ||
25 | # Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) | ||
26 | # v- ls | ||
27 | # v- 2638:2638 | ||
28 | # v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 | ||
29 | # |- unknown unknown 1 13198 0.1 1 0.0 | ||
30 | # >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 | ||
31 | # >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 | ||
32 | # v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 | ||
33 | # >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 | ||
34 | # >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 | ||
35 | # >- __libc_csu_init ls 1 10354 0.1 10 0.0 | ||
36 | # |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 | ||
37 | # v- main ls 1 8182043 99.6 180254 99.9 | ||
38 | # | ||
39 | # Points to note: | ||
40 | # The top level is a command name (comm) | ||
41 | # The next level is a thread (pid:tid) | ||
42 | # Subsequent levels are functions | ||
43 | # 'Count' is the number of calls | ||
44 | # 'Time' is the elapsed time until the function returns | ||
45 | # Percentages are relative to the level above | ||
46 | # 'Branch Count' is the total number of branches for that function and all | ||
47 | # functions that it calls | ||
48 | |||
49 | # There is also a "All branches" report, which displays branches and | ||
50 | # possibly disassembly. However, presently, the only supported disassembler is | ||
51 | # Intel XED, and additionally the object code must be present in perf build ID | ||
52 | # cache. To use Intel XED, libxed.so must be present. To build and install | ||
53 | # libxed.so: | ||
54 | # git clone https://github.com/intelxed/mbuild.git mbuild | ||
55 | # git clone https://github.com/intelxed/xed | ||
56 | # cd xed | ||
57 | # ./mfile.py --share | ||
58 | # sudo ./mfile.py --prefix=/usr/local install | ||
59 | # sudo ldconfig | ||
60 | # | ||
61 | # Example report: | ||
62 | # | ||
63 | # Time CPU Command PID TID Branch Type In Tx Branch | ||
64 | # 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) | ||
65 | # 7fab593ea260 48 89 e7 mov %rsp, %rdi | ||
66 | # 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
67 | # 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) | ||
68 | # 7fab593ea260 48 89 e7 mov %rsp, %rdi | ||
69 | # 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930 | ||
70 | # 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so) | ||
71 | # 7fab593ea930 55 pushq %rbp | ||
72 | # 7fab593ea931 48 89 e5 mov %rsp, %rbp | ||
73 | # 7fab593ea934 41 57 pushq %r15 | ||
74 | # 7fab593ea936 41 56 pushq %r14 | ||
75 | # 7fab593ea938 41 55 pushq %r13 | ||
76 | # 7fab593ea93a 41 54 pushq %r12 | ||
77 | # 7fab593ea93c 53 pushq %rbx | ||
78 | # 7fab593ea93d 48 89 fb mov %rdi, %rbx | ||
79 | # 7fab593ea940 48 83 ec 68 sub $0x68, %rsp | ||
80 | # 7fab593ea944 0f 31 rdtsc | ||
81 | # 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx | ||
82 | # 7fab593ea94a 89 c0 mov %eax, %eax | ||
83 | # 7fab593ea94c 48 09 c2 or %rax, %rdx | ||
84 | # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax | ||
85 | # 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
86 | # 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so) | ||
87 | # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax | ||
88 | # 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) | ||
89 | # 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
90 | |||
91 | import sys | ||
92 | import weakref | ||
93 | import threading | ||
94 | import string | ||
95 | import cPickle | ||
96 | import re | ||
97 | import os | ||
98 | from PySide.QtCore import * | ||
99 | from PySide.QtGui import * | ||
100 | from PySide.QtSql import * | ||
101 | from decimal import * | ||
102 | from ctypes import * | ||
103 | from multiprocessing import Process, Array, Value, Event | ||
104 | |||
105 | # Data formatting helpers | ||
106 | |||
107 | def tohex(ip): | ||
108 | if ip < 0: | ||
109 | ip += 1 << 64 | ||
110 | return "%x" % ip | ||
111 | |||
112 | def offstr(offset): | ||
113 | if offset: | ||
114 | return "+0x%x" % offset | ||
115 | return "" | ||
116 | |||
117 | def dsoname(name): | ||
118 | if name == "[kernel.kallsyms]": | ||
119 | return "[kernel]" | ||
120 | return name | ||
121 | |||
122 | def findnth(s, sub, n, offs=0): | ||
123 | pos = s.find(sub) | ||
124 | if pos < 0: | ||
125 | return pos | ||
126 | if n <= 1: | ||
127 | return offs + pos | ||
128 | return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) | ||
129 | |||
130 | # Percent to one decimal place | ||
131 | |||
132 | def PercentToOneDP(n, d): | ||
133 | if not d: | ||
134 | return "0.0" | ||
135 | x = (n * Decimal(100)) / d | ||
136 | return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) | ||
137 | |||
138 | # Helper for queries that must not fail | ||
139 | |||
140 | def QueryExec(query, stmt): | ||
141 | ret = query.exec_(stmt) | ||
142 | if not ret: | ||
143 | raise Exception("Query failed: " + query.lastError().text()) | ||
144 | |||
145 | # Background thread | ||
146 | |||
147 | class Thread(QThread): | ||
148 | |||
149 | done = Signal(object) | ||
150 | |||
151 | def __init__(self, task, param=None, parent=None): | ||
152 | super(Thread, self).__init__(parent) | ||
153 | self.task = task | ||
154 | self.param = param | ||
155 | |||
156 | def run(self): | ||
157 | while True: | ||
158 | if self.param is None: | ||
159 | done, result = self.task() | ||
160 | else: | ||
161 | done, result = self.task(self.param) | ||
162 | self.done.emit(result) | ||
163 | if done: | ||
164 | break | ||
165 | |||
166 | # Tree data model | ||
167 | |||
168 | class TreeModel(QAbstractItemModel): | ||
169 | |||
170 | def __init__(self, root, parent=None): | ||
171 | super(TreeModel, self).__init__(parent) | ||
172 | self.root = root | ||
173 | self.last_row_read = 0 | ||
174 | |||
175 | def Item(self, parent): | ||
176 | if parent.isValid(): | ||
177 | return parent.internalPointer() | ||
178 | else: | ||
179 | return self.root | ||
180 | |||
181 | def rowCount(self, parent): | ||
182 | result = self.Item(parent).childCount() | ||
183 | if result < 0: | ||
184 | result = 0 | ||
185 | self.dataChanged.emit(parent, parent) | ||
186 | return result | ||
187 | |||
188 | def hasChildren(self, parent): | ||
189 | return self.Item(parent).hasChildren() | ||
190 | |||
191 | def headerData(self, section, orientation, role): | ||
192 | if role == Qt.TextAlignmentRole: | ||
193 | return self.columnAlignment(section) | ||
194 | if role != Qt.DisplayRole: | ||
195 | return None | ||
196 | if orientation != Qt.Horizontal: | ||
197 | return None | ||
198 | return self.columnHeader(section) | ||
199 | |||
200 | def parent(self, child): | ||
201 | child_item = child.internalPointer() | ||
202 | if child_item is self.root: | ||
203 | return QModelIndex() | ||
204 | parent_item = child_item.getParentItem() | ||
205 | return self.createIndex(parent_item.getRow(), 0, parent_item) | ||
206 | |||
207 | def index(self, row, column, parent): | ||
208 | child_item = self.Item(parent).getChildItem(row) | ||
209 | return self.createIndex(row, column, child_item) | ||
210 | |||
211 | def DisplayData(self, item, index): | ||
212 | return item.getData(index.column()) | ||
213 | |||
214 | def FetchIfNeeded(self, row): | ||
215 | if row > self.last_row_read: | ||
216 | self.last_row_read = row | ||
217 | if row + 10 >= self.root.child_count: | ||
218 | self.fetcher.Fetch(glb_chunk_sz) | ||
219 | |||
220 | def columnAlignment(self, column): | ||
221 | return Qt.AlignLeft | ||
222 | |||
223 | def columnFont(self, column): | ||
224 | return None | ||
225 | |||
226 | def data(self, index, role): | ||
227 | if role == Qt.TextAlignmentRole: | ||
228 | return self.columnAlignment(index.column()) | ||
229 | if role == Qt.FontRole: | ||
230 | return self.columnFont(index.column()) | ||
231 | if role != Qt.DisplayRole: | ||
232 | return None | ||
233 | item = index.internalPointer() | ||
234 | return self.DisplayData(item, index) | ||
235 | |||
236 | # Table data model | ||
237 | |||
238 | class TableModel(QAbstractTableModel): | ||
239 | |||
240 | def __init__(self, parent=None): | ||
241 | super(TableModel, self).__init__(parent) | ||
242 | self.child_count = 0 | ||
243 | self.child_items = [] | ||
244 | self.last_row_read = 0 | ||
245 | |||
246 | def Item(self, parent): | ||
247 | if parent.isValid(): | ||
248 | return parent.internalPointer() | ||
249 | else: | ||
250 | return self | ||
251 | |||
252 | def rowCount(self, parent): | ||
253 | return self.child_count | ||
254 | |||
255 | def headerData(self, section, orientation, role): | ||
256 | if role == Qt.TextAlignmentRole: | ||
257 | return self.columnAlignment(section) | ||
258 | if role != Qt.DisplayRole: | ||
259 | return None | ||
260 | if orientation != Qt.Horizontal: | ||
261 | return None | ||
262 | return self.columnHeader(section) | ||
263 | |||
264 | def index(self, row, column, parent): | ||
265 | return self.createIndex(row, column, self.child_items[row]) | ||
266 | |||
267 | def DisplayData(self, item, index): | ||
268 | return item.getData(index.column()) | ||
269 | |||
270 | def FetchIfNeeded(self, row): | ||
271 | if row > self.last_row_read: | ||
272 | self.last_row_read = row | ||
273 | if row + 10 >= self.child_count: | ||
274 | self.fetcher.Fetch(glb_chunk_sz) | ||
275 | |||
276 | def columnAlignment(self, column): | ||
277 | return Qt.AlignLeft | ||
278 | |||
279 | def columnFont(self, column): | ||
280 | return None | ||
281 | |||
282 | def data(self, index, role): | ||
283 | if role == Qt.TextAlignmentRole: | ||
284 | return self.columnAlignment(index.column()) | ||
285 | if role == Qt.FontRole: | ||
286 | return self.columnFont(index.column()) | ||
287 | if role != Qt.DisplayRole: | ||
288 | return None | ||
289 | item = index.internalPointer() | ||
290 | return self.DisplayData(item, index) | ||
291 | |||
292 | # Model cache | ||
293 | |||
294 | model_cache = weakref.WeakValueDictionary() | ||
295 | model_cache_lock = threading.Lock() | ||
296 | |||
297 | def LookupCreateModel(model_name, create_fn): | ||
298 | model_cache_lock.acquire() | ||
299 | try: | ||
300 | model = model_cache[model_name] | ||
301 | except: | ||
302 | model = None | ||
303 | if model is None: | ||
304 | model = create_fn() | ||
305 | model_cache[model_name] = model | ||
306 | model_cache_lock.release() | ||
307 | return model | ||
308 | |||
309 | # Find bar | ||
310 | |||
311 | class FindBar(): | ||
312 | |||
313 | def __init__(self, parent, finder, is_reg_expr=False): | ||
314 | self.finder = finder | ||
315 | self.context = [] | ||
316 | self.last_value = None | ||
317 | self.last_pattern = None | ||
318 | |||
319 | label = QLabel("Find:") | ||
320 | label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
321 | |||
322 | self.textbox = QComboBox() | ||
323 | self.textbox.setEditable(True) | ||
324 | self.textbox.currentIndexChanged.connect(self.ValueChanged) | ||
325 | |||
326 | self.progress = QProgressBar() | ||
327 | self.progress.setRange(0, 0) | ||
328 | self.progress.hide() | ||
329 | |||
330 | if is_reg_expr: | ||
331 | self.pattern = QCheckBox("Regular Expression") | ||
332 | else: | ||
333 | self.pattern = QCheckBox("Pattern") | ||
334 | self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
335 | |||
336 | self.next_button = QToolButton() | ||
337 | self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) | ||
338 | self.next_button.released.connect(lambda: self.NextPrev(1)) | ||
339 | |||
340 | self.prev_button = QToolButton() | ||
341 | self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) | ||
342 | self.prev_button.released.connect(lambda: self.NextPrev(-1)) | ||
343 | |||
344 | self.close_button = QToolButton() | ||
345 | self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) | ||
346 | self.close_button.released.connect(self.Deactivate) | ||
347 | |||
348 | self.hbox = QHBoxLayout() | ||
349 | self.hbox.setContentsMargins(0, 0, 0, 0) | ||
350 | |||
351 | self.hbox.addWidget(label) | ||
352 | self.hbox.addWidget(self.textbox) | ||
353 | self.hbox.addWidget(self.progress) | ||
354 | self.hbox.addWidget(self.pattern) | ||
355 | self.hbox.addWidget(self.next_button) | ||
356 | self.hbox.addWidget(self.prev_button) | ||
357 | self.hbox.addWidget(self.close_button) | ||
358 | |||
359 | self.bar = QWidget() | ||
360 | self.bar.setLayout(self.hbox); | ||
361 | self.bar.hide() | ||
362 | |||
363 | def Widget(self): | ||
364 | return self.bar | ||
365 | |||
366 | def Activate(self): | ||
367 | self.bar.show() | ||
368 | self.textbox.setFocus() | ||
369 | |||
370 | def Deactivate(self): | ||
371 | self.bar.hide() | ||
372 | |||
373 | def Busy(self): | ||
374 | self.textbox.setEnabled(False) | ||
375 | self.pattern.hide() | ||
376 | self.next_button.hide() | ||
377 | self.prev_button.hide() | ||
378 | self.progress.show() | ||
379 | |||
380 | def Idle(self): | ||
381 | self.textbox.setEnabled(True) | ||
382 | self.progress.hide() | ||
383 | self.pattern.show() | ||
384 | self.next_button.show() | ||
385 | self.prev_button.show() | ||
386 | |||
387 | def Find(self, direction): | ||
388 | value = self.textbox.currentText() | ||
389 | pattern = self.pattern.isChecked() | ||
390 | self.last_value = value | ||
391 | self.last_pattern = pattern | ||
392 | self.finder.Find(value, direction, pattern, self.context) | ||
393 | |||
394 | def ValueChanged(self): | ||
395 | value = self.textbox.currentText() | ||
396 | pattern = self.pattern.isChecked() | ||
397 | index = self.textbox.currentIndex() | ||
398 | data = self.textbox.itemData(index) | ||
399 | # Store the pattern in the combo box to keep it with the text value | ||
400 | if data == None: | ||
401 | self.textbox.setItemData(index, pattern) | ||
402 | else: | ||
403 | self.pattern.setChecked(data) | ||
404 | self.Find(0) | ||
405 | |||
406 | def NextPrev(self, direction): | ||
407 | value = self.textbox.currentText() | ||
408 | pattern = self.pattern.isChecked() | ||
409 | if value != self.last_value: | ||
410 | index = self.textbox.findText(value) | ||
411 | # Allow for a button press before the value has been added to the combo box | ||
412 | if index < 0: | ||
413 | index = self.textbox.count() | ||
414 | self.textbox.addItem(value, pattern) | ||
415 | self.textbox.setCurrentIndex(index) | ||
416 | return | ||
417 | else: | ||
418 | self.textbox.setItemData(index, pattern) | ||
419 | elif pattern != self.last_pattern: | ||
420 | # Keep the pattern recorded in the combo box up to date | ||
421 | index = self.textbox.currentIndex() | ||
422 | self.textbox.setItemData(index, pattern) | ||
423 | self.Find(direction) | ||
424 | |||
425 | def NotFound(self): | ||
426 | QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") | ||
427 | |||
428 | # Context-sensitive call graph data model item base | ||
429 | |||
430 | class CallGraphLevelItemBase(object): | ||
431 | |||
432 | def __init__(self, glb, row, parent_item): | ||
433 | self.glb = glb | ||
434 | self.row = row | ||
435 | self.parent_item = parent_item | ||
436 | self.query_done = False; | ||
437 | self.child_count = 0 | ||
438 | self.child_items = [] | ||
439 | |||
440 | def getChildItem(self, row): | ||
441 | return self.child_items[row] | ||
442 | |||
443 | def getParentItem(self): | ||
444 | return self.parent_item | ||
445 | |||
446 | def getRow(self): | ||
447 | return self.row | ||
448 | |||
449 | def childCount(self): | ||
450 | if not self.query_done: | ||
451 | self.Select() | ||
452 | if not self.child_count: | ||
453 | return -1 | ||
454 | return self.child_count | ||
455 | |||
456 | def hasChildren(self): | ||
457 | if not self.query_done: | ||
458 | return True | ||
459 | return self.child_count > 0 | ||
460 | |||
461 | def getData(self, column): | ||
462 | return self.data[column] | ||
463 | |||
464 | # Context-sensitive call graph data model level 2+ item base | ||
465 | |||
466 | class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): | ||
467 | |||
468 | def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): | ||
469 | super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) | ||
470 | self.comm_id = comm_id | ||
471 | self.thread_id = thread_id | ||
472 | self.call_path_id = call_path_id | ||
473 | self.branch_count = branch_count | ||
474 | self.time = time | ||
475 | |||
476 | def Select(self): | ||
477 | self.query_done = True; | ||
478 | query = QSqlQuery(self.glb.db) | ||
479 | QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" | ||
480 | " FROM calls" | ||
481 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
482 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
483 | " INNER JOIN dsos ON symbols.dso_id = dsos.id" | ||
484 | " WHERE parent_call_path_id = " + str(self.call_path_id) + | ||
485 | " AND comm_id = " + str(self.comm_id) + | ||
486 | " AND thread_id = " + str(self.thread_id) + | ||
487 | " GROUP BY call_path_id, name, short_name" | ||
488 | " ORDER BY call_path_id") | ||
489 | while query.next(): | ||
490 | child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) | ||
491 | self.child_items.append(child_item) | ||
492 | self.child_count += 1 | ||
493 | |||
494 | # Context-sensitive call graph data model level three item | ||
495 | |||
496 | class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): | ||
497 | |||
498 | def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): | ||
499 | super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) | ||
500 | dso = dsoname(dso) | ||
501 | self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] | ||
502 | self.dbid = call_path_id | ||
503 | |||
504 | # Context-sensitive call graph data model level two item | ||
505 | |||
506 | class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): | ||
507 | |||
508 | def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): | ||
509 | super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) | ||
510 | self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] | ||
511 | self.dbid = thread_id | ||
512 | |||
513 | def Select(self): | ||
514 | super(CallGraphLevelTwoItem, self).Select() | ||
515 | for child_item in self.child_items: | ||
516 | self.time += child_item.time | ||
517 | self.branch_count += child_item.branch_count | ||
518 | for child_item in self.child_items: | ||
519 | child_item.data[4] = PercentToOneDP(child_item.time, self.time) | ||
520 | child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) | ||
521 | |||
522 | # Context-sensitive call graph data model level one item | ||
523 | |||
524 | class CallGraphLevelOneItem(CallGraphLevelItemBase): | ||
525 | |||
526 | def __init__(self, glb, row, comm_id, comm, parent_item): | ||
527 | super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) | ||
528 | self.data = [comm, "", "", "", "", "", ""] | ||
529 | self.dbid = comm_id | ||
530 | |||
531 | def Select(self): | ||
532 | self.query_done = True; | ||
533 | query = QSqlQuery(self.glb.db) | ||
534 | QueryExec(query, "SELECT thread_id, pid, tid" | ||
535 | " FROM comm_threads" | ||
536 | " INNER JOIN threads ON thread_id = threads.id" | ||
537 | " WHERE comm_id = " + str(self.dbid)) | ||
538 | while query.next(): | ||
539 | child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) | ||
540 | self.child_items.append(child_item) | ||
541 | self.child_count += 1 | ||
542 | |||
543 | # Context-sensitive call graph data model root item | ||
544 | |||
545 | class CallGraphRootItem(CallGraphLevelItemBase): | ||
546 | |||
547 | def __init__(self, glb): | ||
548 | super(CallGraphRootItem, self).__init__(glb, 0, None) | ||
549 | self.dbid = 0 | ||
550 | self.query_done = True; | ||
551 | query = QSqlQuery(glb.db) | ||
552 | QueryExec(query, "SELECT id, comm FROM comms") | ||
553 | while query.next(): | ||
554 | if not query.value(0): | ||
555 | continue | ||
556 | child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) | ||
557 | self.child_items.append(child_item) | ||
558 | self.child_count += 1 | ||
559 | |||
560 | # Context-sensitive call graph data model | ||
561 | |||
562 | class CallGraphModel(TreeModel): | ||
563 | |||
564 | def __init__(self, glb, parent=None): | ||
565 | super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) | ||
566 | self.glb = glb | ||
567 | |||
568 | def columnCount(self, parent=None): | ||
569 | return 7 | ||
570 | |||
571 | def columnHeader(self, column): | ||
572 | headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] | ||
573 | return headers[column] | ||
574 | |||
575 | def columnAlignment(self, column): | ||
576 | alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] | ||
577 | return alignment[column] | ||
578 | |||
579 | def FindSelect(self, value, pattern, query): | ||
580 | if pattern: | ||
581 | # postgresql and sqlite pattern patching differences: | ||
582 | # postgresql LIKE is case sensitive but sqlite LIKE is not | ||
583 | # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not | ||
584 | # postgresql supports ILIKE which is case insensitive | ||
585 | # sqlite supports GLOB (text only) which uses * and ? and is case sensitive | ||
586 | if not self.glb.dbref.is_sqlite3: | ||
587 | # Escape % and _ | ||
588 | s = value.replace("%", "\%") | ||
589 | s = s.replace("_", "\_") | ||
590 | # Translate * and ? into SQL LIKE pattern characters % and _ | ||
591 | trans = string.maketrans("*?", "%_") | ||
592 | match = " LIKE '" + str(s).translate(trans) + "'" | ||
593 | else: | ||
594 | match = " GLOB '" + str(value) + "'" | ||
595 | else: | ||
596 | match = " = '" + str(value) + "'" | ||
597 | QueryExec(query, "SELECT call_path_id, comm_id, thread_id" | ||
598 | " FROM calls" | ||
599 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
600 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
601 | " WHERE symbols.name" + match + | ||
602 | " GROUP BY comm_id, thread_id, call_path_id" | ||
603 | " ORDER BY comm_id, thread_id, call_path_id") | ||
604 | |||
605 | def FindPath(self, query): | ||
606 | # Turn the query result into a list of ids that the tree view can walk | ||
607 | # to open the tree at the right place. | ||
608 | ids = [] | ||
609 | parent_id = query.value(0) | ||
610 | while parent_id: | ||
611 | ids.insert(0, parent_id) | ||
612 | q2 = QSqlQuery(self.glb.db) | ||
613 | QueryExec(q2, "SELECT parent_id" | ||
614 | " FROM call_paths" | ||
615 | " WHERE id = " + str(parent_id)) | ||
616 | if not q2.next(): | ||
617 | break | ||
618 | parent_id = q2.value(0) | ||
619 | # The call path root is not used | ||
620 | if ids[0] == 1: | ||
621 | del ids[0] | ||
622 | ids.insert(0, query.value(2)) | ||
623 | ids.insert(0, query.value(1)) | ||
624 | return ids | ||
625 | |||
626 | def Found(self, query, found): | ||
627 | if found: | ||
628 | return self.FindPath(query) | ||
629 | return [] | ||
630 | |||
631 | def FindValue(self, value, pattern, query, last_value, last_pattern): | ||
632 | if last_value == value and pattern == last_pattern: | ||
633 | found = query.first() | ||
634 | else: | ||
635 | self.FindSelect(value, pattern, query) | ||
636 | found = query.next() | ||
637 | return self.Found(query, found) | ||
638 | |||
639 | def FindNext(self, query): | ||
640 | found = query.next() | ||
641 | if not found: | ||
642 | found = query.first() | ||
643 | return self.Found(query, found) | ||
644 | |||
645 | def FindPrev(self, query): | ||
646 | found = query.previous() | ||
647 | if not found: | ||
648 | found = query.last() | ||
649 | return self.Found(query, found) | ||
650 | |||
651 | def FindThread(self, c): | ||
652 | if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: | ||
653 | ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) | ||
654 | elif c.direction > 0: | ||
655 | ids = self.FindNext(c.query) | ||
656 | else: | ||
657 | ids = self.FindPrev(c.query) | ||
658 | return (True, ids) | ||
659 | |||
660 | def Find(self, value, direction, pattern, context, callback): | ||
661 | class Context(): | ||
662 | def __init__(self, *x): | ||
663 | self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x | ||
664 | def Update(self, *x): | ||
665 | self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) | ||
666 | if len(context): | ||
667 | context[0].Update(value, direction, pattern) | ||
668 | else: | ||
669 | context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) | ||
670 | # Use a thread so the UI is not blocked during the SELECT | ||
671 | thread = Thread(self.FindThread, context[0]) | ||
672 | thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) | ||
673 | thread.start() | ||
674 | |||
675 | def FindDone(self, thread, callback, ids): | ||
676 | callback(ids) | ||
677 | |||
678 | # Vertical widget layout | ||
679 | |||
680 | class VBox(): | ||
681 | |||
682 | def __init__(self, w1, w2, w3=None): | ||
683 | self.vbox = QWidget() | ||
684 | self.vbox.setLayout(QVBoxLayout()); | ||
685 | |||
686 | self.vbox.layout().setContentsMargins(0, 0, 0, 0) | ||
687 | |||
688 | self.vbox.layout().addWidget(w1) | ||
689 | self.vbox.layout().addWidget(w2) | ||
690 | if w3: | ||
691 | self.vbox.layout().addWidget(w3) | ||
692 | |||
693 | def Widget(self): | ||
694 | return self.vbox | ||
695 | |||
696 | # Context-sensitive call graph window | ||
697 | |||
698 | class CallGraphWindow(QMdiSubWindow): | ||
699 | |||
700 | def __init__(self, glb, parent=None): | ||
701 | super(CallGraphWindow, self).__init__(parent) | ||
702 | |||
703 | self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) | ||
704 | |||
705 | self.view = QTreeView() | ||
706 | self.view.setModel(self.model) | ||
707 | |||
708 | for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): | ||
709 | self.view.setColumnWidth(c, w) | ||
710 | |||
711 | self.find_bar = FindBar(self, self) | ||
712 | |||
713 | self.vbox = VBox(self.view, self.find_bar.Widget()) | ||
714 | |||
715 | self.setWidget(self.vbox.Widget()) | ||
716 | |||
717 | AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") | ||
718 | |||
719 | def DisplayFound(self, ids): | ||
720 | if not len(ids): | ||
721 | return False | ||
722 | parent = QModelIndex() | ||
723 | for dbid in ids: | ||
724 | found = False | ||
725 | n = self.model.rowCount(parent) | ||
726 | for row in xrange(n): | ||
727 | child = self.model.index(row, 0, parent) | ||
728 | if child.internalPointer().dbid == dbid: | ||
729 | found = True | ||
730 | self.view.setCurrentIndex(child) | ||
731 | parent = child | ||
732 | break | ||
733 | if not found: | ||
734 | break | ||
735 | return found | ||
736 | |||
737 | def Find(self, value, direction, pattern, context): | ||
738 | self.view.setFocus() | ||
739 | self.find_bar.Busy() | ||
740 | self.model.Find(value, direction, pattern, context, self.FindDone) | ||
741 | |||
742 | def FindDone(self, ids): | ||
743 | found = True | ||
744 | if not self.DisplayFound(ids): | ||
745 | found = False | ||
746 | self.find_bar.Idle() | ||
747 | if not found: | ||
748 | self.find_bar.NotFound() | ||
749 | |||
750 | # Child data item finder | ||
751 | |||
752 | class ChildDataItemFinder(): | ||
753 | |||
754 | def __init__(self, root): | ||
755 | self.root = root | ||
756 | self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 | ||
757 | self.rows = [] | ||
758 | self.pos = 0 | ||
759 | |||
760 | def FindSelect(self): | ||
761 | self.rows = [] | ||
762 | if self.pattern: | ||
763 | pattern = re.compile(self.value) | ||
764 | for child in self.root.child_items: | ||
765 | for column_data in child.data: | ||
766 | if re.search(pattern, str(column_data)) is not None: | ||
767 | self.rows.append(child.row) | ||
768 | break | ||
769 | else: | ||
770 | for child in self.root.child_items: | ||
771 | for column_data in child.data: | ||
772 | if self.value in str(column_data): | ||
773 | self.rows.append(child.row) | ||
774 | break | ||
775 | |||
776 | def FindValue(self): | ||
777 | self.pos = 0 | ||
778 | if self.last_value != self.value or self.pattern != self.last_pattern: | ||
779 | self.FindSelect() | ||
780 | if not len(self.rows): | ||
781 | return -1 | ||
782 | return self.rows[self.pos] | ||
783 | |||
784 | def FindThread(self): | ||
785 | if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: | ||
786 | row = self.FindValue() | ||
787 | elif len(self.rows): | ||
788 | if self.direction > 0: | ||
789 | self.pos += 1 | ||
790 | if self.pos >= len(self.rows): | ||
791 | self.pos = 0 | ||
792 | else: | ||
793 | self.pos -= 1 | ||
794 | if self.pos < 0: | ||
795 | self.pos = len(self.rows) - 1 | ||
796 | row = self.rows[self.pos] | ||
797 | else: | ||
798 | row = -1 | ||
799 | return (True, row) | ||
800 | |||
801 | def Find(self, value, direction, pattern, context, callback): | ||
802 | self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) | ||
803 | # Use a thread so the UI is not blocked | ||
804 | thread = Thread(self.FindThread) | ||
805 | thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) | ||
806 | thread.start() | ||
807 | |||
808 | def FindDone(self, thread, callback, row): | ||
809 | callback(row) | ||
810 | |||
811 | # Number of database records to fetch in one go | ||
812 | |||
813 | glb_chunk_sz = 10000 | ||
814 | |||
815 | # size of pickled integer big enough for record size | ||
816 | |||
817 | glb_nsz = 8 | ||
818 | |||
819 | # Background process for SQL data fetcher | ||
820 | |||
821 | class SQLFetcherProcess(): | ||
822 | |||
823 | def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): | ||
824 | # Need a unique connection name | ||
825 | conn_name = "SQLFetcher" + str(os.getpid()) | ||
826 | self.db, dbname = dbref.Open(conn_name) | ||
827 | self.sql = sql | ||
828 | self.buffer = buffer | ||
829 | self.head = head | ||
830 | self.tail = tail | ||
831 | self.fetch_count = fetch_count | ||
832 | self.fetching_done = fetching_done | ||
833 | self.process_target = process_target | ||
834 | self.wait_event = wait_event | ||
835 | self.fetched_event = fetched_event | ||
836 | self.prep = prep | ||
837 | self.query = QSqlQuery(self.db) | ||
838 | self.query_limit = 0 if "$$last_id$$" in sql else 2 | ||
839 | self.last_id = -1 | ||
840 | self.fetched = 0 | ||
841 | self.more = True | ||
842 | self.local_head = self.head.value | ||
843 | self.local_tail = self.tail.value | ||
844 | |||
845 | def Select(self): | ||
846 | if self.query_limit: | ||
847 | if self.query_limit == 1: | ||
848 | return | ||
849 | self.query_limit -= 1 | ||
850 | stmt = self.sql.replace("$$last_id$$", str(self.last_id)) | ||
851 | QueryExec(self.query, stmt) | ||
852 | |||
853 | def Next(self): | ||
854 | if not self.query.next(): | ||
855 | self.Select() | ||
856 | if not self.query.next(): | ||
857 | return None | ||
858 | self.last_id = self.query.value(0) | ||
859 | return self.prep(self.query) | ||
860 | |||
861 | def WaitForTarget(self): | ||
862 | while True: | ||
863 | self.wait_event.clear() | ||
864 | target = self.process_target.value | ||
865 | if target > self.fetched or target < 0: | ||
866 | break | ||
867 | self.wait_event.wait() | ||
868 | return target | ||
869 | |||
870 | def HasSpace(self, sz): | ||
871 | if self.local_tail <= self.local_head: | ||
872 | space = len(self.buffer) - self.local_head | ||
873 | if space > sz: | ||
874 | return True | ||
875 | if space >= glb_nsz: | ||
876 | # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer | ||
877 | nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL) | ||
878 | self.buffer[self.local_head : self.local_head + len(nd)] = nd | ||
879 | self.local_head = 0 | ||
880 | if self.local_tail - self.local_head > sz: | ||
881 | return True | ||
882 | return False | ||
883 | |||
884 | def WaitForSpace(self, sz): | ||
885 | if self.HasSpace(sz): | ||
886 | return | ||
887 | while True: | ||
888 | self.wait_event.clear() | ||
889 | self.local_tail = self.tail.value | ||
890 | if self.HasSpace(sz): | ||
891 | return | ||
892 | self.wait_event.wait() | ||
893 | |||
894 | def AddToBuffer(self, obj): | ||
895 | d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL) | ||
896 | n = len(d) | ||
897 | nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL) | ||
898 | sz = n + glb_nsz | ||
899 | self.WaitForSpace(sz) | ||
900 | pos = self.local_head | ||
901 | self.buffer[pos : pos + len(nd)] = nd | ||
902 | self.buffer[pos + glb_nsz : pos + sz] = d | ||
903 | self.local_head += sz | ||
904 | |||
905 | def FetchBatch(self, batch_size): | ||
906 | fetched = 0 | ||
907 | while batch_size > fetched: | ||
908 | obj = self.Next() | ||
909 | if obj is None: | ||
910 | self.more = False | ||
911 | break | ||
912 | self.AddToBuffer(obj) | ||
913 | fetched += 1 | ||
914 | if fetched: | ||
915 | self.fetched += fetched | ||
916 | with self.fetch_count.get_lock(): | ||
917 | self.fetch_count.value += fetched | ||
918 | self.head.value = self.local_head | ||
919 | self.fetched_event.set() | ||
920 | |||
921 | def Run(self): | ||
922 | while self.more: | ||
923 | target = self.WaitForTarget() | ||
924 | if target < 0: | ||
925 | break | ||
926 | batch_size = min(glb_chunk_sz, target - self.fetched) | ||
927 | self.FetchBatch(batch_size) | ||
928 | self.fetching_done.value = True | ||
929 | self.fetched_event.set() | ||
930 | |||
931 | def SQLFetcherFn(*x): | ||
932 | process = SQLFetcherProcess(*x) | ||
933 | process.Run() | ||
934 | |||
935 | # SQL data fetcher | ||
936 | |||
937 | class SQLFetcher(QObject): | ||
938 | |||
939 | done = Signal(object) | ||
940 | |||
941 | def __init__(self, glb, sql, prep, process_data, parent=None): | ||
942 | super(SQLFetcher, self).__init__(parent) | ||
943 | self.process_data = process_data | ||
944 | self.more = True | ||
945 | self.target = 0 | ||
946 | self.last_target = 0 | ||
947 | self.fetched = 0 | ||
948 | self.buffer_size = 16 * 1024 * 1024 | ||
949 | self.buffer = Array(c_char, self.buffer_size, lock=False) | ||
950 | self.head = Value(c_longlong) | ||
951 | self.tail = Value(c_longlong) | ||
952 | self.local_tail = 0 | ||
953 | self.fetch_count = Value(c_longlong) | ||
954 | self.fetching_done = Value(c_bool) | ||
955 | self.last_count = 0 | ||
956 | self.process_target = Value(c_longlong) | ||
957 | self.wait_event = Event() | ||
958 | self.fetched_event = Event() | ||
959 | glb.AddInstanceToShutdownOnExit(self) | ||
960 | self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep)) | ||
961 | self.process.start() | ||
962 | self.thread = Thread(self.Thread) | ||
963 | self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) | ||
964 | self.thread.start() | ||
965 | |||
966 | def Shutdown(self): | ||
967 | # Tell the thread and process to exit | ||
968 | self.process_target.value = -1 | ||
969 | self.wait_event.set() | ||
970 | self.more = False | ||
971 | self.fetching_done.value = True | ||
972 | self.fetched_event.set() | ||
973 | |||
974 | def Thread(self): | ||
975 | if not self.more: | ||
976 | return True, 0 | ||
977 | while True: | ||
978 | self.fetched_event.clear() | ||
979 | fetch_count = self.fetch_count.value | ||
980 | if fetch_count != self.last_count: | ||
981 | break | ||
982 | if self.fetching_done.value: | ||
983 | self.more = False | ||
984 | return True, 0 | ||
985 | self.fetched_event.wait() | ||
986 | count = fetch_count - self.last_count | ||
987 | self.last_count = fetch_count | ||
988 | self.fetched += count | ||
989 | return False, count | ||
990 | |||
991 | def Fetch(self, nr): | ||
992 | if not self.more: | ||
993 | # -1 inidcates there are no more | ||
994 | return -1 | ||
995 | result = self.fetched | ||
996 | extra = result + nr - self.target | ||
997 | if extra > 0: | ||
998 | self.target += extra | ||
999 | # process_target < 0 indicates shutting down | ||
1000 | if self.process_target.value >= 0: | ||
1001 | self.process_target.value = self.target | ||
1002 | self.wait_event.set() | ||
1003 | return result | ||
1004 | |||
1005 | def RemoveFromBuffer(self): | ||
1006 | pos = self.local_tail | ||
1007 | if len(self.buffer) - pos < glb_nsz: | ||
1008 | pos = 0 | ||
1009 | n = cPickle.loads(self.buffer[pos : pos + glb_nsz]) | ||
1010 | if n == 0: | ||
1011 | pos = 0 | ||
1012 | n = cPickle.loads(self.buffer[0 : glb_nsz]) | ||
1013 | pos += glb_nsz | ||
1014 | obj = cPickle.loads(self.buffer[pos : pos + n]) | ||
1015 | self.local_tail = pos + n | ||
1016 | return obj | ||
1017 | |||
1018 | def ProcessData(self, count): | ||
1019 | for i in xrange(count): | ||
1020 | obj = self.RemoveFromBuffer() | ||
1021 | self.process_data(obj) | ||
1022 | self.tail.value = self.local_tail | ||
1023 | self.wait_event.set() | ||
1024 | self.done.emit(count) | ||
1025 | |||
1026 | # Fetch more records bar | ||
1027 | |||
1028 | class FetchMoreRecordsBar(): | ||
1029 | |||
1030 | def __init__(self, model, parent): | ||
1031 | self.model = model | ||
1032 | |||
1033 | self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") | ||
1034 | self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
1035 | |||
1036 | self.fetch_count = QSpinBox() | ||
1037 | self.fetch_count.setRange(1, 1000000) | ||
1038 | self.fetch_count.setValue(10) | ||
1039 | self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
1040 | |||
1041 | self.fetch = QPushButton("Go!") | ||
1042 | self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
1043 | self.fetch.released.connect(self.FetchMoreRecords) | ||
1044 | |||
1045 | self.progress = QProgressBar() | ||
1046 | self.progress.setRange(0, 100) | ||
1047 | self.progress.hide() | ||
1048 | |||
1049 | self.done_label = QLabel("All records fetched") | ||
1050 | self.done_label.hide() | ||
1051 | |||
1052 | self.spacer = QLabel("") | ||
1053 | |||
1054 | self.close_button = QToolButton() | ||
1055 | self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) | ||
1056 | self.close_button.released.connect(self.Deactivate) | ||
1057 | |||
1058 | self.hbox = QHBoxLayout() | ||
1059 | self.hbox.setContentsMargins(0, 0, 0, 0) | ||
1060 | |||
1061 | self.hbox.addWidget(self.label) | ||
1062 | self.hbox.addWidget(self.fetch_count) | ||
1063 | self.hbox.addWidget(self.fetch) | ||
1064 | self.hbox.addWidget(self.spacer) | ||
1065 | self.hbox.addWidget(self.progress) | ||
1066 | self.hbox.addWidget(self.done_label) | ||
1067 | self.hbox.addWidget(self.close_button) | ||
1068 | |||
1069 | self.bar = QWidget() | ||
1070 | self.bar.setLayout(self.hbox); | ||
1071 | self.bar.show() | ||
1072 | |||
1073 | self.in_progress = False | ||
1074 | self.model.progress.connect(self.Progress) | ||
1075 | |||
1076 | self.done = False | ||
1077 | |||
1078 | if not model.HasMoreRecords(): | ||
1079 | self.Done() | ||
1080 | |||
1081 | def Widget(self): | ||
1082 | return self.bar | ||
1083 | |||
1084 | def Activate(self): | ||
1085 | self.bar.show() | ||
1086 | self.fetch.setFocus() | ||
1087 | |||
1088 | def Deactivate(self): | ||
1089 | self.bar.hide() | ||
1090 | |||
1091 | def Enable(self, enable): | ||
1092 | self.fetch.setEnabled(enable) | ||
1093 | self.fetch_count.setEnabled(enable) | ||
1094 | |||
1095 | def Busy(self): | ||
1096 | self.Enable(False) | ||
1097 | self.fetch.hide() | ||
1098 | self.spacer.hide() | ||
1099 | self.progress.show() | ||
1100 | |||
1101 | def Idle(self): | ||
1102 | self.in_progress = False | ||
1103 | self.Enable(True) | ||
1104 | self.progress.hide() | ||
1105 | self.fetch.show() | ||
1106 | self.spacer.show() | ||
1107 | |||
1108 | def Target(self): | ||
1109 | return self.fetch_count.value() * glb_chunk_sz | ||
1110 | |||
1111 | def Done(self): | ||
1112 | self.done = True | ||
1113 | self.Idle() | ||
1114 | self.label.hide() | ||
1115 | self.fetch_count.hide() | ||
1116 | self.fetch.hide() | ||
1117 | self.spacer.hide() | ||
1118 | self.done_label.show() | ||
1119 | |||
1120 | def Progress(self, count): | ||
1121 | if self.in_progress: | ||
1122 | if count: | ||
1123 | percent = ((count - self.start) * 100) / self.Target() | ||
1124 | if percent >= 100: | ||
1125 | self.Idle() | ||
1126 | else: | ||
1127 | self.progress.setValue(percent) | ||
1128 | if not count: | ||
1129 | # Count value of zero means no more records | ||
1130 | self.Done() | ||
1131 | |||
1132 | def FetchMoreRecords(self): | ||
1133 | if self.done: | ||
1134 | return | ||
1135 | self.progress.setValue(0) | ||
1136 | self.Busy() | ||
1137 | self.in_progress = True | ||
1138 | self.start = self.model.FetchMoreRecords(self.Target()) | ||
1139 | |||
1140 | # Brance data model level two item | ||
1141 | |||
1142 | class BranchLevelTwoItem(): | ||
1143 | |||
1144 | def __init__(self, row, text, parent_item): | ||
1145 | self.row = row | ||
1146 | self.parent_item = parent_item | ||
1147 | self.data = [""] * 8 | ||
1148 | self.data[7] = text | ||
1149 | self.level = 2 | ||
1150 | |||
1151 | def getParentItem(self): | ||
1152 | return self.parent_item | ||
1153 | |||
1154 | def getRow(self): | ||
1155 | return self.row | ||
1156 | |||
1157 | def childCount(self): | ||
1158 | return 0 | ||
1159 | |||
1160 | def hasChildren(self): | ||
1161 | return False | ||
1162 | |||
1163 | def getData(self, column): | ||
1164 | return self.data[column] | ||
1165 | |||
1166 | # Brance data model level one item | ||
1167 | |||
1168 | class BranchLevelOneItem(): | ||
1169 | |||
1170 | def __init__(self, glb, row, data, parent_item): | ||
1171 | self.glb = glb | ||
1172 | self.row = row | ||
1173 | self.parent_item = parent_item | ||
1174 | self.child_count = 0 | ||
1175 | self.child_items = [] | ||
1176 | self.data = data[1:] | ||
1177 | self.dbid = data[0] | ||
1178 | self.level = 1 | ||
1179 | self.query_done = False | ||
1180 | |||
1181 | def getChildItem(self, row): | ||
1182 | return self.child_items[row] | ||
1183 | |||
1184 | def getParentItem(self): | ||
1185 | return self.parent_item | ||
1186 | |||
1187 | def getRow(self): | ||
1188 | return self.row | ||
1189 | |||
1190 | def Select(self): | ||
1191 | self.query_done = True | ||
1192 | |||
1193 | if not self.glb.have_disassembler: | ||
1194 | return | ||
1195 | |||
1196 | query = QSqlQuery(self.glb.db) | ||
1197 | |||
1198 | QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" | ||
1199 | " FROM samples" | ||
1200 | " INNER JOIN dsos ON samples.to_dso_id = dsos.id" | ||
1201 | " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" | ||
1202 | " WHERE samples.id = " + str(self.dbid)) | ||
1203 | if not query.next(): | ||
1204 | return | ||
1205 | cpu = query.value(0) | ||
1206 | dso = query.value(1) | ||
1207 | sym = query.value(2) | ||
1208 | if dso == 0 or sym == 0: | ||
1209 | return | ||
1210 | off = query.value(3) | ||
1211 | short_name = query.value(4) | ||
1212 | long_name = query.value(5) | ||
1213 | build_id = query.value(6) | ||
1214 | sym_start = query.value(7) | ||
1215 | ip = query.value(8) | ||
1216 | |||
1217 | QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" | ||
1218 | " FROM samples" | ||
1219 | " INNER JOIN symbols ON samples.symbol_id = symbols.id" | ||
1220 | " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + | ||
1221 | " ORDER BY samples.id" | ||
1222 | " LIMIT 1") | ||
1223 | if not query.next(): | ||
1224 | return | ||
1225 | if query.value(0) != dso: | ||
1226 | # Cannot disassemble from one dso to another | ||
1227 | return | ||
1228 | bsym = query.value(1) | ||
1229 | boff = query.value(2) | ||
1230 | bsym_start = query.value(3) | ||
1231 | if bsym == 0: | ||
1232 | return | ||
1233 | tot = bsym_start + boff + 1 - sym_start - off | ||
1234 | if tot <= 0 or tot > 16384: | ||
1235 | return | ||
1236 | |||
1237 | inst = self.glb.disassembler.Instruction() | ||
1238 | f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) | ||
1239 | if not f: | ||
1240 | return | ||
1241 | mode = 0 if Is64Bit(f) else 1 | ||
1242 | self.glb.disassembler.SetMode(inst, mode) | ||
1243 | |||
1244 | buf_sz = tot + 16 | ||
1245 | buf = create_string_buffer(tot + 16) | ||
1246 | f.seek(sym_start + off) | ||
1247 | buf.value = f.read(buf_sz) | ||
1248 | buf_ptr = addressof(buf) | ||
1249 | i = 0 | ||
1250 | while tot > 0: | ||
1251 | cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) | ||
1252 | if cnt: | ||
1253 | byte_str = tohex(ip).rjust(16) | ||
1254 | for k in xrange(cnt): | ||
1255 | byte_str += " %02x" % ord(buf[i]) | ||
1256 | i += 1 | ||
1257 | while k < 15: | ||
1258 | byte_str += " " | ||
1259 | k += 1 | ||
1260 | self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) | ||
1261 | self.child_count += 1 | ||
1262 | else: | ||
1263 | return | ||
1264 | buf_ptr += cnt | ||
1265 | tot -= cnt | ||
1266 | buf_sz -= cnt | ||
1267 | ip += cnt | ||
1268 | |||
1269 | def childCount(self): | ||
1270 | if not self.query_done: | ||
1271 | self.Select() | ||
1272 | if not self.child_count: | ||
1273 | return -1 | ||
1274 | return self.child_count | ||
1275 | |||
1276 | def hasChildren(self): | ||
1277 | if not self.query_done: | ||
1278 | return True | ||
1279 | return self.child_count > 0 | ||
1280 | |||
1281 | def getData(self, column): | ||
1282 | return self.data[column] | ||
1283 | |||
1284 | # Brance data model root item | ||
1285 | |||
1286 | class BranchRootItem(): | ||
1287 | |||
1288 | def __init__(self): | ||
1289 | self.child_count = 0 | ||
1290 | self.child_items = [] | ||
1291 | self.level = 0 | ||
1292 | |||
1293 | def getChildItem(self, row): | ||
1294 | return self.child_items[row] | ||
1295 | |||
1296 | def getParentItem(self): | ||
1297 | return None | ||
1298 | |||
1299 | def getRow(self): | ||
1300 | return 0 | ||
1301 | |||
1302 | def childCount(self): | ||
1303 | return self.child_count | ||
1304 | |||
1305 | def hasChildren(self): | ||
1306 | return self.child_count > 0 | ||
1307 | |||
1308 | def getData(self, column): | ||
1309 | return "" | ||
1310 | |||
1311 | # Branch data preparation | ||
1312 | |||
1313 | def BranchDataPrep(query): | ||
1314 | data = [] | ||
1315 | for i in xrange(0, 8): | ||
1316 | data.append(query.value(i)) | ||
1317 | data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + | ||
1318 | " (" + dsoname(query.value(11)) + ")" + " -> " + | ||
1319 | tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + | ||
1320 | " (" + dsoname(query.value(15)) + ")") | ||
1321 | return data | ||
1322 | |||
1323 | # Branch data model | ||
1324 | |||
1325 | class BranchModel(TreeModel): | ||
1326 | |||
1327 | progress = Signal(object) | ||
1328 | |||
1329 | def __init__(self, glb, event_id, where_clause, parent=None): | ||
1330 | super(BranchModel, self).__init__(BranchRootItem(), parent) | ||
1331 | self.glb = glb | ||
1332 | self.event_id = event_id | ||
1333 | self.more = True | ||
1334 | self.populated = 0 | ||
1335 | sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," | ||
1336 | " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," | ||
1337 | " ip, symbols.name, sym_offset, dsos.short_name," | ||
1338 | " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" | ||
1339 | " FROM samples" | ||
1340 | " INNER JOIN comms ON comm_id = comms.id" | ||
1341 | " INNER JOIN threads ON thread_id = threads.id" | ||
1342 | " INNER JOIN branch_types ON branch_type = branch_types.id" | ||
1343 | " INNER JOIN symbols ON symbol_id = symbols.id" | ||
1344 | " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" | ||
1345 | " INNER JOIN dsos ON samples.dso_id = dsos.id" | ||
1346 | " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" | ||
1347 | " WHERE samples.id > $$last_id$$" + where_clause + | ||
1348 | " AND evsel_id = " + str(self.event_id) + | ||
1349 | " ORDER BY samples.id" | ||
1350 | " LIMIT " + str(glb_chunk_sz)) | ||
1351 | self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample) | ||
1352 | self.fetcher.done.connect(self.Update) | ||
1353 | self.fetcher.Fetch(glb_chunk_sz) | ||
1354 | |||
1355 | def columnCount(self, parent=None): | ||
1356 | return 8 | ||
1357 | |||
1358 | def columnHeader(self, column): | ||
1359 | return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] | ||
1360 | |||
1361 | def columnFont(self, column): | ||
1362 | if column != 7: | ||
1363 | return None | ||
1364 | return QFont("Monospace") | ||
1365 | |||
1366 | def DisplayData(self, item, index): | ||
1367 | if item.level == 1: | ||
1368 | self.FetchIfNeeded(item.row) | ||
1369 | return item.getData(index.column()) | ||
1370 | |||
1371 | def AddSample(self, data): | ||
1372 | child = BranchLevelOneItem(self.glb, self.populated, data, self.root) | ||
1373 | self.root.child_items.append(child) | ||
1374 | self.populated += 1 | ||
1375 | |||
1376 | def Update(self, fetched): | ||
1377 | if not fetched: | ||
1378 | self.more = False | ||
1379 | self.progress.emit(0) | ||
1380 | child_count = self.root.child_count | ||
1381 | count = self.populated - child_count | ||
1382 | if count > 0: | ||
1383 | parent = QModelIndex() | ||
1384 | self.beginInsertRows(parent, child_count, child_count + count - 1) | ||
1385 | self.insertRows(child_count, count, parent) | ||
1386 | self.root.child_count += count | ||
1387 | self.endInsertRows() | ||
1388 | self.progress.emit(self.root.child_count) | ||
1389 | |||
1390 | def FetchMoreRecords(self, count): | ||
1391 | current = self.root.child_count | ||
1392 | if self.more: | ||
1393 | self.fetcher.Fetch(count) | ||
1394 | else: | ||
1395 | self.progress.emit(0) | ||
1396 | return current | ||
1397 | |||
1398 | def HasMoreRecords(self): | ||
1399 | return self.more | ||
1400 | |||
1401 | # Branch window | ||
1402 | |||
1403 | class BranchWindow(QMdiSubWindow): | ||
1404 | |||
1405 | def __init__(self, glb, event_id, name, where_clause, parent=None): | ||
1406 | super(BranchWindow, self).__init__(parent) | ||
1407 | |||
1408 | model_name = "Branch Events " + str(event_id) | ||
1409 | if len(where_clause): | ||
1410 | model_name = where_clause + " " + model_name | ||
1411 | |||
1412 | self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) | ||
1413 | |||
1414 | self.view = QTreeView() | ||
1415 | self.view.setUniformRowHeights(True) | ||
1416 | self.view.setModel(self.model) | ||
1417 | |||
1418 | self.ResizeColumnsToContents() | ||
1419 | |||
1420 | self.find_bar = FindBar(self, self, True) | ||
1421 | |||
1422 | self.finder = ChildDataItemFinder(self.model.root) | ||
1423 | |||
1424 | self.fetch_bar = FetchMoreRecordsBar(self.model, self) | ||
1425 | |||
1426 | self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) | ||
1427 | |||
1428 | self.setWidget(self.vbox.Widget()) | ||
1429 | |||
1430 | AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") | ||
1431 | |||
1432 | def ResizeColumnToContents(self, column, n): | ||
1433 | # Using the view's resizeColumnToContents() here is extrememly slow | ||
1434 | # so implement a crude alternative | ||
1435 | mm = "MM" if column else "MMMM" | ||
1436 | font = self.view.font() | ||
1437 | metrics = QFontMetrics(font) | ||
1438 | max = 0 | ||
1439 | for row in xrange(n): | ||
1440 | val = self.model.root.child_items[row].data[column] | ||
1441 | len = metrics.width(str(val) + mm) | ||
1442 | max = len if len > max else max | ||
1443 | val = self.model.columnHeader(column) | ||
1444 | len = metrics.width(str(val) + mm) | ||
1445 | max = len if len > max else max | ||
1446 | self.view.setColumnWidth(column, max) | ||
1447 | |||
1448 | def ResizeColumnsToContents(self): | ||
1449 | n = min(self.model.root.child_count, 100) | ||
1450 | if n < 1: | ||
1451 | # No data yet, so connect a signal to notify when there is | ||
1452 | self.model.rowsInserted.connect(self.UpdateColumnWidths) | ||
1453 | return | ||
1454 | columns = self.model.columnCount() | ||
1455 | for i in xrange(columns): | ||
1456 | self.ResizeColumnToContents(i, n) | ||
1457 | |||
1458 | def UpdateColumnWidths(self, *x): | ||
1459 | # This only needs to be done once, so disconnect the signal now | ||
1460 | self.model.rowsInserted.disconnect(self.UpdateColumnWidths) | ||
1461 | self.ResizeColumnsToContents() | ||
1462 | |||
1463 | def Find(self, value, direction, pattern, context): | ||
1464 | self.view.setFocus() | ||
1465 | self.find_bar.Busy() | ||
1466 | self.finder.Find(value, direction, pattern, context, self.FindDone) | ||
1467 | |||
1468 | def FindDone(self, row): | ||
1469 | self.find_bar.Idle() | ||
1470 | if row >= 0: | ||
1471 | self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) | ||
1472 | else: | ||
1473 | self.find_bar.NotFound() | ||
1474 | |||
1475 | # Dialog data item converted and validated using a SQL table | ||
1476 | |||
1477 | class SQLTableDialogDataItem(): | ||
1478 | |||
1479 | def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): | ||
1480 | self.glb = glb | ||
1481 | self.label = label | ||
1482 | self.placeholder_text = placeholder_text | ||
1483 | self.table_name = table_name | ||
1484 | self.match_column = match_column | ||
1485 | self.column_name1 = column_name1 | ||
1486 | self.column_name2 = column_name2 | ||
1487 | self.parent = parent | ||
1488 | |||
1489 | self.value = "" | ||
1490 | |||
1491 | self.widget = QLineEdit() | ||
1492 | self.widget.editingFinished.connect(self.Validate) | ||
1493 | self.widget.textChanged.connect(self.Invalidate) | ||
1494 | self.red = False | ||
1495 | self.error = "" | ||
1496 | self.validated = True | ||
1497 | |||
1498 | self.last_id = 0 | ||
1499 | self.first_time = 0 | ||
1500 | self.last_time = 2 ** 64 | ||
1501 | if self.table_name == "<timeranges>": | ||
1502 | query = QSqlQuery(self.glb.db) | ||
1503 | QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") | ||
1504 | if query.next(): | ||
1505 | self.last_id = int(query.value(0)) | ||
1506 | self.last_time = int(query.value(1)) | ||
1507 | QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") | ||
1508 | if query.next(): | ||
1509 | self.first_time = int(query.value(0)) | ||
1510 | if placeholder_text: | ||
1511 | placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) | ||
1512 | |||
1513 | if placeholder_text: | ||
1514 | self.widget.setPlaceholderText(placeholder_text) | ||
1515 | |||
1516 | def ValueToIds(self, value): | ||
1517 | ids = [] | ||
1518 | query = QSqlQuery(self.glb.db) | ||
1519 | stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" | ||
1520 | ret = query.exec_(stmt) | ||
1521 | if ret: | ||
1522 | while query.next(): | ||
1523 | ids.append(str(query.value(0))) | ||
1524 | return ids | ||
1525 | |||
1526 | def IdBetween(self, query, lower_id, higher_id, order): | ||
1527 | QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") | ||
1528 | if query.next(): | ||
1529 | return True, int(query.value(0)) | ||
1530 | else: | ||
1531 | return False, 0 | ||
1532 | |||
1533 | def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): | ||
1534 | query = QSqlQuery(self.glb.db) | ||
1535 | while True: | ||
1536 | next_id = int((lower_id + higher_id) / 2) | ||
1537 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
1538 | if not query.next(): | ||
1539 | ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") | ||
1540 | if not ok: | ||
1541 | ok, dbid = self.IdBetween(query, next_id, higher_id, "") | ||
1542 | if not ok: | ||
1543 | return str(higher_id) | ||
1544 | next_id = dbid | ||
1545 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
1546 | next_time = int(query.value(0)) | ||
1547 | if get_floor: | ||
1548 | if target_time > next_time: | ||
1549 | lower_id = next_id | ||
1550 | else: | ||
1551 | higher_id = next_id | ||
1552 | if higher_id <= lower_id + 1: | ||
1553 | return str(higher_id) | ||
1554 | else: | ||
1555 | if target_time >= next_time: | ||
1556 | lower_id = next_id | ||
1557 | else: | ||
1558 | higher_id = next_id | ||
1559 | if higher_id <= lower_id + 1: | ||
1560 | return str(lower_id) | ||
1561 | |||
1562 | def ConvertRelativeTime(self, val): | ||
1563 | print "val ", val | ||
1564 | mult = 1 | ||
1565 | suffix = val[-2:] | ||
1566 | if suffix == "ms": | ||
1567 | mult = 1000000 | ||
1568 | elif suffix == "us": | ||
1569 | mult = 1000 | ||
1570 | elif suffix == "ns": | ||
1571 | mult = 1 | ||
1572 | else: | ||
1573 | return val | ||
1574 | val = val[:-2].strip() | ||
1575 | if not self.IsNumber(val): | ||
1576 | return val | ||
1577 | val = int(val) * mult | ||
1578 | if val >= 0: | ||
1579 | val += self.first_time | ||
1580 | else: | ||
1581 | val += self.last_time | ||
1582 | return str(val) | ||
1583 | |||
1584 | def ConvertTimeRange(self, vrange): | ||
1585 | print "vrange ", vrange | ||
1586 | if vrange[0] == "": | ||
1587 | vrange[0] = str(self.first_time) | ||
1588 | if vrange[1] == "": | ||
1589 | vrange[1] = str(self.last_time) | ||
1590 | vrange[0] = self.ConvertRelativeTime(vrange[0]) | ||
1591 | vrange[1] = self.ConvertRelativeTime(vrange[1]) | ||
1592 | print "vrange2 ", vrange | ||
1593 | if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
1594 | return False | ||
1595 | print "ok1" | ||
1596 | beg_range = max(int(vrange[0]), self.first_time) | ||
1597 | end_range = min(int(vrange[1]), self.last_time) | ||
1598 | if beg_range > self.last_time or end_range < self.first_time: | ||
1599 | return False | ||
1600 | print "ok2" | ||
1601 | vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) | ||
1602 | vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) | ||
1603 | print "vrange3 ", vrange | ||
1604 | return True | ||
1605 | |||
1606 | def AddTimeRange(self, value, ranges): | ||
1607 | print "value ", value | ||
1608 | n = value.count("-") | ||
1609 | if n == 1: | ||
1610 | pass | ||
1611 | elif n == 2: | ||
1612 | if value.split("-")[1].strip() == "": | ||
1613 | n = 1 | ||
1614 | elif n == 3: | ||
1615 | n = 2 | ||
1616 | else: | ||
1617 | return False | ||
1618 | pos = findnth(value, "-", n) | ||
1619 | vrange = [value[:pos].strip() ,value[pos+1:].strip()] | ||
1620 | if self.ConvertTimeRange(vrange): | ||
1621 | ranges.append(vrange) | ||
1622 | return True | ||
1623 | return False | ||
1624 | |||
1625 | def InvalidValue(self, value): | ||
1626 | self.value = "" | ||
1627 | palette = QPalette() | ||
1628 | palette.setColor(QPalette.Text,Qt.red) | ||
1629 | self.widget.setPalette(palette) | ||
1630 | self.red = True | ||
1631 | self.error = self.label + " invalid value '" + value + "'" | ||
1632 | self.parent.ShowMessage(self.error) | ||
1633 | |||
1634 | def IsNumber(self, value): | ||
1635 | try: | ||
1636 | x = int(value) | ||
1637 | except: | ||
1638 | x = 0 | ||
1639 | return str(x) == value | ||
1640 | |||
1641 | def Invalidate(self): | ||
1642 | self.validated = False | ||
1643 | |||
1644 | def Validate(self): | ||
1645 | input_string = self.widget.text() | ||
1646 | self.validated = True | ||
1647 | if self.red: | ||
1648 | palette = QPalette() | ||
1649 | self.widget.setPalette(palette) | ||
1650 | self.red = False | ||
1651 | if not len(input_string.strip()): | ||
1652 | self.error = "" | ||
1653 | self.value = "" | ||
1654 | return | ||
1655 | if self.table_name == "<timeranges>": | ||
1656 | ranges = [] | ||
1657 | for value in [x.strip() for x in input_string.split(",")]: | ||
1658 | if not self.AddTimeRange(value, ranges): | ||
1659 | return self.InvalidValue(value) | ||
1660 | ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] | ||
1661 | self.value = " OR ".join(ranges) | ||
1662 | elif self.table_name == "<ranges>": | ||
1663 | singles = [] | ||
1664 | ranges = [] | ||
1665 | for value in [x.strip() for x in input_string.split(",")]: | ||
1666 | if "-" in value: | ||
1667 | vrange = value.split("-") | ||
1668 | if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
1669 | return self.InvalidValue(value) | ||
1670 | ranges.append(vrange) | ||
1671 | else: | ||
1672 | if not self.IsNumber(value): | ||
1673 | return self.InvalidValue(value) | ||
1674 | singles.append(value) | ||
1675 | ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] | ||
1676 | if len(singles): | ||
1677 | ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")") | ||
1678 | self.value = " OR ".join(ranges) | ||
1679 | elif self.table_name: | ||
1680 | all_ids = [] | ||
1681 | for value in [x.strip() for x in input_string.split(",")]: | ||
1682 | ids = self.ValueToIds(value) | ||
1683 | if len(ids): | ||
1684 | all_ids.extend(ids) | ||
1685 | else: | ||
1686 | return self.InvalidValue(value) | ||
1687 | self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" | ||
1688 | if self.column_name2: | ||
1689 | self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" | ||
1690 | else: | ||
1691 | self.value = input_string.strip() | ||
1692 | self.error = "" | ||
1693 | self.parent.ClearMessage() | ||
1694 | |||
1695 | def IsValid(self): | ||
1696 | if not self.validated: | ||
1697 | self.Validate() | ||
1698 | if len(self.error): | ||
1699 | self.parent.ShowMessage(self.error) | ||
1700 | return False | ||
1701 | return True | ||
1702 | |||
1703 | # Selected branch report creation dialog | ||
1704 | |||
1705 | class SelectedBranchDialog(QDialog): | ||
1706 | |||
1707 | def __init__(self, glb, parent=None): | ||
1708 | super(SelectedBranchDialog, self).__init__(parent) | ||
1709 | |||
1710 | self.glb = glb | ||
1711 | |||
1712 | self.name = "" | ||
1713 | self.where_clause = "" | ||
1714 | |||
1715 | self.setWindowTitle("Selected Branches") | ||
1716 | self.setMinimumWidth(600) | ||
1717 | |||
1718 | items = ( | ||
1719 | ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), | ||
1720 | ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), | ||
1721 | ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""), | ||
1722 | ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""), | ||
1723 | ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""), | ||
1724 | ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""), | ||
1725 | ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"), | ||
1726 | ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"), | ||
1727 | ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""), | ||
1728 | ) | ||
1729 | self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items] | ||
1730 | |||
1731 | self.grid = QGridLayout() | ||
1732 | |||
1733 | for row in xrange(len(self.data_items)): | ||
1734 | self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) | ||
1735 | self.grid.addWidget(self.data_items[row].widget, row, 1) | ||
1736 | |||
1737 | self.status = QLabel() | ||
1738 | |||
1739 | self.ok_button = QPushButton("Ok", self) | ||
1740 | self.ok_button.setDefault(True) | ||
1741 | self.ok_button.released.connect(self.Ok) | ||
1742 | self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
1743 | |||
1744 | self.cancel_button = QPushButton("Cancel", self) | ||
1745 | self.cancel_button.released.connect(self.reject) | ||
1746 | self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
1747 | |||
1748 | self.hbox = QHBoxLayout() | ||
1749 | #self.hbox.addStretch() | ||
1750 | self.hbox.addWidget(self.status) | ||
1751 | self.hbox.addWidget(self.ok_button) | ||
1752 | self.hbox.addWidget(self.cancel_button) | ||
1753 | |||
1754 | self.vbox = QVBoxLayout() | ||
1755 | self.vbox.addLayout(self.grid) | ||
1756 | self.vbox.addLayout(self.hbox) | ||
1757 | |||
1758 | self.setLayout(self.vbox); | ||
1759 | |||
1760 | def Ok(self): | ||
1761 | self.name = self.data_items[0].value | ||
1762 | if not self.name: | ||
1763 | self.ShowMessage("Report name is required") | ||
1764 | return | ||
1765 | for d in self.data_items: | ||
1766 | if not d.IsValid(): | ||
1767 | return | ||
1768 | for d in self.data_items[1:]: | ||
1769 | if len(d.value): | ||
1770 | if len(self.where_clause): | ||
1771 | self.where_clause += " AND " | ||
1772 | self.where_clause += d.value | ||
1773 | if len(self.where_clause): | ||
1774 | self.where_clause = " AND ( " + self.where_clause + " ) " | ||
1775 | else: | ||
1776 | self.ShowMessage("No selection") | ||
1777 | return | ||
1778 | self.accept() | ||
1779 | |||
1780 | def ShowMessage(self, msg): | ||
1781 | self.status.setText("<font color=#FF0000>" + msg) | ||
1782 | |||
1783 | def ClearMessage(self): | ||
1784 | self.status.setText("") | ||
1785 | |||
1786 | # Event list | ||
1787 | |||
1788 | def GetEventList(db): | ||
1789 | events = [] | ||
1790 | query = QSqlQuery(db) | ||
1791 | QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") | ||
1792 | while query.next(): | ||
1793 | events.append(query.value(0)) | ||
1794 | return events | ||
1795 | |||
1796 | # SQL data preparation | ||
1797 | |||
1798 | def SQLTableDataPrep(query, count): | ||
1799 | data = [] | ||
1800 | for i in xrange(count): | ||
1801 | data.append(query.value(i)) | ||
1802 | return data | ||
1803 | |||
1804 | # SQL table data model item | ||
1805 | |||
1806 | class SQLTableItem(): | ||
1807 | |||
1808 | def __init__(self, row, data): | ||
1809 | self.row = row | ||
1810 | self.data = data | ||
1811 | |||
1812 | def getData(self, column): | ||
1813 | return self.data[column] | ||
1814 | |||
1815 | # SQL table data model | ||
1816 | |||
1817 | class SQLTableModel(TableModel): | ||
1818 | |||
1819 | progress = Signal(object) | ||
1820 | |||
1821 | def __init__(self, glb, sql, column_count, parent=None): | ||
1822 | super(SQLTableModel, self).__init__(parent) | ||
1823 | self.glb = glb | ||
1824 | self.more = True | ||
1825 | self.populated = 0 | ||
1826 | self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample) | ||
1827 | self.fetcher.done.connect(self.Update) | ||
1828 | self.fetcher.Fetch(glb_chunk_sz) | ||
1829 | |||
1830 | def DisplayData(self, item, index): | ||
1831 | self.FetchIfNeeded(item.row) | ||
1832 | return item.getData(index.column()) | ||
1833 | |||
1834 | def AddSample(self, data): | ||
1835 | child = SQLTableItem(self.populated, data) | ||
1836 | self.child_items.append(child) | ||
1837 | self.populated += 1 | ||
1838 | |||
1839 | def Update(self, fetched): | ||
1840 | if not fetched: | ||
1841 | self.more = False | ||
1842 | self.progress.emit(0) | ||
1843 | child_count = self.child_count | ||
1844 | count = self.populated - child_count | ||
1845 | if count > 0: | ||
1846 | parent = QModelIndex() | ||
1847 | self.beginInsertRows(parent, child_count, child_count + count - 1) | ||
1848 | self.insertRows(child_count, count, parent) | ||
1849 | self.child_count += count | ||
1850 | self.endInsertRows() | ||
1851 | self.progress.emit(self.child_count) | ||
1852 | |||
1853 | def FetchMoreRecords(self, count): | ||
1854 | current = self.child_count | ||
1855 | if self.more: | ||
1856 | self.fetcher.Fetch(count) | ||
1857 | else: | ||
1858 | self.progress.emit(0) | ||
1859 | return current | ||
1860 | |||
1861 | def HasMoreRecords(self): | ||
1862 | return self.more | ||
1863 | |||
1864 | # SQL automatic table data model | ||
1865 | |||
1866 | class SQLAutoTableModel(SQLTableModel): | ||
1867 | |||
1868 | def __init__(self, glb, table_name, parent=None): | ||
1869 | sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) | ||
1870 | if table_name == "comm_threads_view": | ||
1871 | # For now, comm_threads_view has no id column | ||
1872 | sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) | ||
1873 | self.column_headers = [] | ||
1874 | query = QSqlQuery(glb.db) | ||
1875 | if glb.dbref.is_sqlite3: | ||
1876 | QueryExec(query, "PRAGMA table_info(" + table_name + ")") | ||
1877 | while query.next(): | ||
1878 | self.column_headers.append(query.value(1)) | ||
1879 | if table_name == "sqlite_master": | ||
1880 | sql = "SELECT * FROM " + table_name | ||
1881 | else: | ||
1882 | if table_name[:19] == "information_schema.": | ||
1883 | sql = "SELECT * FROM " + table_name | ||
1884 | select_table_name = table_name[19:] | ||
1885 | schema = "information_schema" | ||
1886 | else: | ||
1887 | select_table_name = table_name | ||
1888 | schema = "public" | ||
1889 | QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") | ||
1890 | while query.next(): | ||
1891 | self.column_headers.append(query.value(0)) | ||
1892 | super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent) | ||
1893 | |||
1894 | def columnCount(self, parent=None): | ||
1895 | return len(self.column_headers) | ||
1896 | |||
1897 | def columnHeader(self, column): | ||
1898 | return self.column_headers[column] | ||
1899 | |||
1900 | # Base class for custom ResizeColumnsToContents | ||
1901 | |||
1902 | class ResizeColumnsToContentsBase(QObject): | ||
1903 | |||
1904 | def __init__(self, parent=None): | ||
1905 | super(ResizeColumnsToContentsBase, self).__init__(parent) | ||
1906 | |||
1907 | def ResizeColumnToContents(self, column, n): | ||
1908 | # Using the view's resizeColumnToContents() here is extrememly slow | ||
1909 | # so implement a crude alternative | ||
1910 | font = self.view.font() | ||
1911 | metrics = QFontMetrics(font) | ||
1912 | max = 0 | ||
1913 | for row in xrange(n): | ||
1914 | val = self.data_model.child_items[row].data[column] | ||
1915 | len = metrics.width(str(val) + "MM") | ||
1916 | max = len if len > max else max | ||
1917 | val = self.data_model.columnHeader(column) | ||
1918 | len = metrics.width(str(val) + "MM") | ||
1919 | max = len if len > max else max | ||
1920 | self.view.setColumnWidth(column, max) | ||
1921 | |||
1922 | def ResizeColumnsToContents(self): | ||
1923 | n = min(self.data_model.child_count, 100) | ||
1924 | if n < 1: | ||
1925 | # No data yet, so connect a signal to notify when there is | ||
1926 | self.data_model.rowsInserted.connect(self.UpdateColumnWidths) | ||
1927 | return | ||
1928 | columns = self.data_model.columnCount() | ||
1929 | for i in xrange(columns): | ||
1930 | self.ResizeColumnToContents(i, n) | ||
1931 | |||
1932 | def UpdateColumnWidths(self, *x): | ||
1933 | # This only needs to be done once, so disconnect the signal now | ||
1934 | self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) | ||
1935 | self.ResizeColumnsToContents() | ||
1936 | |||
1937 | # Table window | ||
1938 | |||
1939 | class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): | ||
1940 | |||
1941 | def __init__(self, glb, table_name, parent=None): | ||
1942 | super(TableWindow, self).__init__(parent) | ||
1943 | |||
1944 | self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) | ||
1945 | |||
1946 | self.model = QSortFilterProxyModel() | ||
1947 | self.model.setSourceModel(self.data_model) | ||
1948 | |||
1949 | self.view = QTableView() | ||
1950 | self.view.setModel(self.model) | ||
1951 | self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) | ||
1952 | self.view.verticalHeader().setVisible(False) | ||
1953 | self.view.sortByColumn(-1, Qt.AscendingOrder) | ||
1954 | self.view.setSortingEnabled(True) | ||
1955 | |||
1956 | self.ResizeColumnsToContents() | ||
1957 | |||
1958 | self.find_bar = FindBar(self, self, True) | ||
1959 | |||
1960 | self.finder = ChildDataItemFinder(self.data_model) | ||
1961 | |||
1962 | self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) | ||
1963 | |||
1964 | self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) | ||
1965 | |||
1966 | self.setWidget(self.vbox.Widget()) | ||
1967 | |||
1968 | AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") | ||
1969 | |||
1970 | def Find(self, value, direction, pattern, context): | ||
1971 | self.view.setFocus() | ||
1972 | self.find_bar.Busy() | ||
1973 | self.finder.Find(value, direction, pattern, context, self.FindDone) | ||
1974 | |||
1975 | def FindDone(self, row): | ||
1976 | self.find_bar.Idle() | ||
1977 | if row >= 0: | ||
1978 | self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) | ||
1979 | else: | ||
1980 | self.find_bar.NotFound() | ||
1981 | |||
1982 | # Table list | ||
1983 | |||
1984 | def GetTableList(glb): | ||
1985 | tables = [] | ||
1986 | query = QSqlQuery(glb.db) | ||
1987 | if glb.dbref.is_sqlite3: | ||
1988 | QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") | ||
1989 | else: | ||
1990 | QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name") | ||
1991 | while query.next(): | ||
1992 | tables.append(query.value(0)) | ||
1993 | if glb.dbref.is_sqlite3: | ||
1994 | tables.append("sqlite_master") | ||
1995 | else: | ||
1996 | tables.append("information_schema.tables") | ||
1997 | tables.append("information_schema.views") | ||
1998 | tables.append("information_schema.columns") | ||
1999 | return tables | ||
2000 | |||
2001 | # Action Definition | ||
2002 | |||
2003 | def CreateAction(label, tip, callback, parent=None, shortcut=None): | ||
2004 | action = QAction(label, parent) | ||
2005 | if shortcut != None: | ||
2006 | action.setShortcuts(shortcut) | ||
2007 | action.setStatusTip(tip) | ||
2008 | action.triggered.connect(callback) | ||
2009 | return action | ||
2010 | |||
2011 | # Typical application actions | ||
2012 | |||
2013 | def CreateExitAction(app, parent=None): | ||
2014 | return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) | ||
2015 | |||
2016 | # Typical MDI actions | ||
2017 | |||
2018 | def CreateCloseActiveWindowAction(mdi_area): | ||
2019 | return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) | ||
2020 | |||
2021 | def CreateCloseAllWindowsAction(mdi_area): | ||
2022 | return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) | ||
2023 | |||
2024 | def CreateTileWindowsAction(mdi_area): | ||
2025 | return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) | ||
2026 | |||
2027 | def CreateCascadeWindowsAction(mdi_area): | ||
2028 | return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) | ||
2029 | |||
2030 | def CreateNextWindowAction(mdi_area): | ||
2031 | return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) | ||
2032 | |||
2033 | def CreatePreviousWindowAction(mdi_area): | ||
2034 | return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) | ||
2035 | |||
2036 | # Typical MDI window menu | ||
2037 | |||
2038 | class WindowMenu(): | ||
2039 | |||
2040 | def __init__(self, mdi_area, menu): | ||
2041 | self.mdi_area = mdi_area | ||
2042 | self.window_menu = menu.addMenu("&Windows") | ||
2043 | self.close_active_window = CreateCloseActiveWindowAction(mdi_area) | ||
2044 | self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) | ||
2045 | self.tile_windows = CreateTileWindowsAction(mdi_area) | ||
2046 | self.cascade_windows = CreateCascadeWindowsAction(mdi_area) | ||
2047 | self.next_window = CreateNextWindowAction(mdi_area) | ||
2048 | self.previous_window = CreatePreviousWindowAction(mdi_area) | ||
2049 | self.window_menu.aboutToShow.connect(self.Update) | ||
2050 | |||
2051 | def Update(self): | ||
2052 | self.window_menu.clear() | ||
2053 | sub_window_count = len(self.mdi_area.subWindowList()) | ||
2054 | have_sub_windows = sub_window_count != 0 | ||
2055 | self.close_active_window.setEnabled(have_sub_windows) | ||
2056 | self.close_all_windows.setEnabled(have_sub_windows) | ||
2057 | self.tile_windows.setEnabled(have_sub_windows) | ||
2058 | self.cascade_windows.setEnabled(have_sub_windows) | ||
2059 | self.next_window.setEnabled(have_sub_windows) | ||
2060 | self.previous_window.setEnabled(have_sub_windows) | ||
2061 | self.window_menu.addAction(self.close_active_window) | ||
2062 | self.window_menu.addAction(self.close_all_windows) | ||
2063 | self.window_menu.addSeparator() | ||
2064 | self.window_menu.addAction(self.tile_windows) | ||
2065 | self.window_menu.addAction(self.cascade_windows) | ||
2066 | self.window_menu.addSeparator() | ||
2067 | self.window_menu.addAction(self.next_window) | ||
2068 | self.window_menu.addAction(self.previous_window) | ||
2069 | if sub_window_count == 0: | ||
2070 | return | ||
2071 | self.window_menu.addSeparator() | ||
2072 | nr = 1 | ||
2073 | for sub_window in self.mdi_area.subWindowList(): | ||
2074 | label = str(nr) + " " + sub_window.name | ||
2075 | if nr < 10: | ||
2076 | label = "&" + label | ||
2077 | action = self.window_menu.addAction(label) | ||
2078 | action.setCheckable(True) | ||
2079 | action.setChecked(sub_window == self.mdi_area.activeSubWindow()) | ||
2080 | action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) | ||
2081 | self.window_menu.addAction(action) | ||
2082 | nr += 1 | ||
2083 | |||
2084 | def setActiveSubWindow(self, nr): | ||
2085 | self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) | ||
2086 | |||
2087 | # Help text | ||
2088 | |||
2089 | glb_help_text = """ | ||
2090 | <h1>Contents</h1> | ||
2091 | <style> | ||
2092 | p.c1 { | ||
2093 | text-indent: 40px; | ||
2094 | } | ||
2095 | p.c2 { | ||
2096 | text-indent: 80px; | ||
2097 | } | ||
2098 | } | ||
2099 | </style> | ||
2100 | <p class=c1><a href=#reports>1. Reports</a></p> | ||
2101 | <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> | ||
2102 | <p class=c2><a href=#allbranches>1.2 All branches</a></p> | ||
2103 | <p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> | ||
2104 | <p class=c1><a href=#tables>2. Tables</a></p> | ||
2105 | <h1 id=reports>1. Reports</h1> | ||
2106 | <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> | ||
2107 | The result is a GUI window with a tree representing a context-sensitive | ||
2108 | call-graph. Expanding a couple of levels of the tree and adjusting column | ||
2109 | widths to suit will display something like: | ||
2110 | <pre> | ||
2111 | Call Graph: pt_example | ||
2112 | Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) | ||
2113 | v- ls | ||
2114 | v- 2638:2638 | ||
2115 | v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 | ||
2116 | |- unknown unknown 1 13198 0.1 1 0.0 | ||
2117 | >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 | ||
2118 | >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 | ||
2119 | v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 | ||
2120 | >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 | ||
2121 | >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 | ||
2122 | >- __libc_csu_init ls 1 10354 0.1 10 0.0 | ||
2123 | |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 | ||
2124 | v- main ls 1 8182043 99.6 180254 99.9 | ||
2125 | </pre> | ||
2126 | <h3>Points to note:</h3> | ||
2127 | <ul> | ||
2128 | <li>The top level is a command name (comm)</li> | ||
2129 | <li>The next level is a thread (pid:tid)</li> | ||
2130 | <li>Subsequent levels are functions</li> | ||
2131 | <li>'Count' is the number of calls</li> | ||
2132 | <li>'Time' is the elapsed time until the function returns</li> | ||
2133 | <li>Percentages are relative to the level above</li> | ||
2134 | <li>'Branch Count' is the total number of branches for that function and all functions that it calls | ||
2135 | </ul> | ||
2136 | <h3>Find</h3> | ||
2137 | Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match. | ||
2138 | The pattern matching symbols are ? for any character and * for zero or more characters. | ||
2139 | <h2 id=allbranches>1.2 All branches</h2> | ||
2140 | The All branches report displays all branches in chronological order. | ||
2141 | Not all data is fetched immediately. More records can be fetched using the Fetch bar provided. | ||
2142 | <h3>Disassembly</h3> | ||
2143 | Open a branch to display disassembly. This only works if: | ||
2144 | <ol> | ||
2145 | <li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> | ||
2146 | <li>The object code is available. Currently, only the perf build ID cache is searched for object code. | ||
2147 | The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. | ||
2148 | One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), | ||
2149 | or alternatively, set environment variable PERF_KCORE to the kcore file name.</li> | ||
2150 | </ol> | ||
2151 | <h4 id=xed>Intel XED Setup</h4> | ||
2152 | To use Intel XED, libxed.so must be present. To build and install libxed.so: | ||
2153 | <pre> | ||
2154 | git clone https://github.com/intelxed/mbuild.git mbuild | ||
2155 | git clone https://github.com/intelxed/xed | ||
2156 | cd xed | ||
2157 | ./mfile.py --share | ||
2158 | sudo ./mfile.py --prefix=/usr/local install | ||
2159 | sudo ldconfig | ||
2160 | </pre> | ||
2161 | <h3>Find</h3> | ||
2162 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. | ||
2163 | Refer to Python documentation for the regular expression syntax. | ||
2164 | All columns are searched, but only currently fetched rows are searched. | ||
2165 | <h2 id=selectedbranches>1.3 Selected branches</h2> | ||
2166 | This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced | ||
2167 | by various selection criteria. A dialog box displays available criteria which are AND'ed together. | ||
2168 | <h3>1.3.1 Time ranges</h3> | ||
2169 | The time ranges hint text shows the total time range. Relative time ranges can also be entered in | ||
2170 | ms, us or ns. Also, negative values are relative to the end of trace. Examples: | ||
2171 | <pre> | ||
2172 | 81073085947329-81073085958238 From 81073085947329 to 81073085958238 | ||
2173 | 100us-200us From 100us to 200us | ||
2174 | 10ms- From 10ms to the end | ||
2175 | -100ns The first 100ns | ||
2176 | -10ms- The last 10ms | ||
2177 | </pre> | ||
2178 | N.B. Due to the granularity of timestamps, there could be no branches in any given time range. | ||
2179 | <h1 id=tables>2. Tables</h1> | ||
2180 | The Tables menu shows all tables and views in the database. Most tables have an associated view | ||
2181 | which displays the information in a more friendly way. Not all data for large tables is fetched | ||
2182 | immediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, | ||
2183 | but that can be slow for large tables. | ||
2184 | <p>There are also tables of database meta-information. | ||
2185 | For SQLite3 databases, the sqlite_master table is included. | ||
2186 | For PostgreSQL databases, information_schema.tables/views/columns are included. | ||
2187 | <h3>Find</h3> | ||
2188 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. | ||
2189 | Refer to Python documentation for the regular expression syntax. | ||
2190 | All columns are searched, but only currently fetched rows are searched. | ||
2191 | <p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous | ||
2192 | will go to the next/previous result in id order, instead of display order. | ||
2193 | """ | ||
2194 | |||
2195 | # Help window | ||
2196 | |||
2197 | class HelpWindow(QMdiSubWindow): | ||
2198 | |||
2199 | def __init__(self, glb, parent=None): | ||
2200 | super(HelpWindow, self).__init__(parent) | ||
2201 | |||
2202 | self.text = QTextBrowser() | ||
2203 | self.text.setHtml(glb_help_text) | ||
2204 | self.text.setReadOnly(True) | ||
2205 | self.text.setOpenExternalLinks(True) | ||
2206 | |||
2207 | self.setWidget(self.text) | ||
2208 | |||
2209 | AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") | ||
2210 | |||
2211 | # Main window that only displays the help text | ||
2212 | |||
2213 | class HelpOnlyWindow(QMainWindow): | ||
2214 | |||
2215 | def __init__(self, parent=None): | ||
2216 | super(HelpOnlyWindow, self).__init__(parent) | ||
2217 | |||
2218 | self.setMinimumSize(200, 100) | ||
2219 | self.resize(800, 600) | ||
2220 | self.setWindowTitle("Exported SQL Viewer Help") | ||
2221 | self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) | ||
2222 | |||
2223 | self.text = QTextBrowser() | ||
2224 | self.text.setHtml(glb_help_text) | ||
2225 | self.text.setReadOnly(True) | ||
2226 | self.text.setOpenExternalLinks(True) | ||
2227 | |||
2228 | self.setCentralWidget(self.text) | ||
2229 | |||
2230 | # Font resize | ||
2231 | |||
2232 | def ResizeFont(widget, diff): | ||
2233 | font = widget.font() | ||
2234 | sz = font.pointSize() | ||
2235 | font.setPointSize(sz + diff) | ||
2236 | widget.setFont(font) | ||
2237 | |||
2238 | def ShrinkFont(widget): | ||
2239 | ResizeFont(widget, -1) | ||
2240 | |||
2241 | def EnlargeFont(widget): | ||
2242 | ResizeFont(widget, 1) | ||
2243 | |||
2244 | # Unique name for sub-windows | ||
2245 | |||
2246 | def NumberedWindowName(name, nr): | ||
2247 | if nr > 1: | ||
2248 | name += " <" + str(nr) + ">" | ||
2249 | return name | ||
2250 | |||
2251 | def UniqueSubWindowName(mdi_area, name): | ||
2252 | nr = 1 | ||
2253 | while True: | ||
2254 | unique_name = NumberedWindowName(name, nr) | ||
2255 | ok = True | ||
2256 | for sub_window in mdi_area.subWindowList(): | ||
2257 | if sub_window.name == unique_name: | ||
2258 | ok = False | ||
2259 | break | ||
2260 | if ok: | ||
2261 | return unique_name | ||
2262 | nr += 1 | ||
2263 | |||
2264 | # Add a sub-window | ||
2265 | |||
2266 | def AddSubWindow(mdi_area, sub_window, name): | ||
2267 | unique_name = UniqueSubWindowName(mdi_area, name) | ||
2268 | sub_window.setMinimumSize(200, 100) | ||
2269 | sub_window.resize(800, 600) | ||
2270 | sub_window.setWindowTitle(unique_name) | ||
2271 | sub_window.setAttribute(Qt.WA_DeleteOnClose) | ||
2272 | sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) | ||
2273 | sub_window.name = unique_name | ||
2274 | mdi_area.addSubWindow(sub_window) | ||
2275 | sub_window.show() | ||
2276 | |||
2277 | # Main window | ||
2278 | |||
2279 | class MainWindow(QMainWindow): | ||
2280 | |||
2281 | def __init__(self, glb, parent=None): | ||
2282 | super(MainWindow, self).__init__(parent) | ||
2283 | |||
2284 | self.glb = glb | ||
2285 | |||
2286 | self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) | ||
2287 | self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) | ||
2288 | self.setMinimumSize(200, 100) | ||
2289 | |||
2290 | self.mdi_area = QMdiArea() | ||
2291 | self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) | ||
2292 | self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) | ||
2293 | |||
2294 | self.setCentralWidget(self.mdi_area) | ||
2295 | |||
2296 | menu = self.menuBar() | ||
2297 | |||
2298 | file_menu = menu.addMenu("&File") | ||
2299 | file_menu.addAction(CreateExitAction(glb.app, self)) | ||
2300 | |||
2301 | edit_menu = menu.addMenu("&Edit") | ||
2302 | edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) | ||
2303 | edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) | ||
2304 | edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) | ||
2305 | edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) | ||
2306 | |||
2307 | reports_menu = menu.addMenu("&Reports") | ||
2308 | reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) | ||
2309 | |||
2310 | self.EventMenu(GetEventList(glb.db), reports_menu) | ||
2311 | |||
2312 | self.TableMenu(GetTableList(glb), menu) | ||
2313 | |||
2314 | self.window_menu = WindowMenu(self.mdi_area, menu) | ||
2315 | |||
2316 | help_menu = menu.addMenu("&Help") | ||
2317 | help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) | ||
2318 | |||
2319 | def Find(self): | ||
2320 | win = self.mdi_area.activeSubWindow() | ||
2321 | if win: | ||
2322 | try: | ||
2323 | win.find_bar.Activate() | ||
2324 | except: | ||
2325 | pass | ||
2326 | |||
2327 | def FetchMoreRecords(self): | ||
2328 | win = self.mdi_area.activeSubWindow() | ||
2329 | if win: | ||
2330 | try: | ||
2331 | win.fetch_bar.Activate() | ||
2332 | except: | ||
2333 | pass | ||
2334 | |||
2335 | def ShrinkFont(self): | ||
2336 | win = self.mdi_area.activeSubWindow() | ||
2337 | ShrinkFont(win.view) | ||
2338 | |||
2339 | def EnlargeFont(self): | ||
2340 | win = self.mdi_area.activeSubWindow() | ||
2341 | EnlargeFont(win.view) | ||
2342 | |||
2343 | def EventMenu(self, events, reports_menu): | ||
2344 | branches_events = 0 | ||
2345 | for event in events: | ||
2346 | event = event.split(":")[0] | ||
2347 | if event == "branches": | ||
2348 | branches_events += 1 | ||
2349 | dbid = 0 | ||
2350 | for event in events: | ||
2351 | dbid += 1 | ||
2352 | event = event.split(":")[0] | ||
2353 | if event == "branches": | ||
2354 | label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" | ||
2355 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) | ||
2356 | label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" | ||
2357 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) | ||
2358 | |||
2359 | def TableMenu(self, tables, menu): | ||
2360 | table_menu = menu.addMenu("&Tables") | ||
2361 | for table in tables: | ||
2362 | table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) | ||
2363 | |||
2364 | def NewCallGraph(self): | ||
2365 | CallGraphWindow(self.glb, self) | ||
2366 | |||
2367 | def NewBranchView(self, event_id): | ||
2368 | BranchWindow(self.glb, event_id, "", "", self) | ||
2369 | |||
2370 | def NewSelectedBranchView(self, event_id): | ||
2371 | dialog = SelectedBranchDialog(self.glb, self) | ||
2372 | ret = dialog.exec_() | ||
2373 | if ret: | ||
2374 | BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self) | ||
2375 | |||
2376 | def NewTableView(self, table_name): | ||
2377 | TableWindow(self.glb, table_name, self) | ||
2378 | |||
2379 | def Help(self): | ||
2380 | HelpWindow(self.glb, self) | ||
2381 | |||
2382 | # XED Disassembler | ||
2383 | |||
2384 | class xed_state_t(Structure): | ||
2385 | |||
2386 | _fields_ = [ | ||
2387 | ("mode", c_int), | ||
2388 | ("width", c_int) | ||
2389 | ] | ||
2390 | |||
2391 | class XEDInstruction(): | ||
2392 | |||
2393 | def __init__(self, libxed): | ||
2394 | # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion | ||
2395 | xedd_t = c_byte * 512 | ||
2396 | self.xedd = xedd_t() | ||
2397 | self.xedp = addressof(self.xedd) | ||
2398 | libxed.xed_decoded_inst_zero(self.xedp) | ||
2399 | self.state = xed_state_t() | ||
2400 | self.statep = addressof(self.state) | ||
2401 | # Buffer for disassembled instruction text | ||
2402 | self.buffer = create_string_buffer(256) | ||
2403 | self.bufferp = addressof(self.buffer) | ||
2404 | |||
2405 | class LibXED(): | ||
2406 | |||
2407 | def __init__(self): | ||
2408 | try: | ||
2409 | self.libxed = CDLL("libxed.so") | ||
2410 | except: | ||
2411 | self.libxed = None | ||
2412 | if not self.libxed: | ||
2413 | self.libxed = CDLL("/usr/local/lib/libxed.so") | ||
2414 | |||
2415 | self.xed_tables_init = self.libxed.xed_tables_init | ||
2416 | self.xed_tables_init.restype = None | ||
2417 | self.xed_tables_init.argtypes = [] | ||
2418 | |||
2419 | self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero | ||
2420 | self.xed_decoded_inst_zero.restype = None | ||
2421 | self.xed_decoded_inst_zero.argtypes = [ c_void_p ] | ||
2422 | |||
2423 | self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode | ||
2424 | self.xed_operand_values_set_mode.restype = None | ||
2425 | self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] | ||
2426 | |||
2427 | self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode | ||
2428 | self.xed_decoded_inst_zero_keep_mode.restype = None | ||
2429 | self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] | ||
2430 | |||
2431 | self.xed_decode = self.libxed.xed_decode | ||
2432 | self.xed_decode.restype = c_int | ||
2433 | self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] | ||
2434 | |||
2435 | self.xed_format_context = self.libxed.xed_format_context | ||
2436 | self.xed_format_context.restype = c_uint | ||
2437 | self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] | ||
2438 | |||
2439 | self.xed_tables_init() | ||
2440 | |||
2441 | def Instruction(self): | ||
2442 | return XEDInstruction(self) | ||
2443 | |||
2444 | def SetMode(self, inst, mode): | ||
2445 | if mode: | ||
2446 | inst.state.mode = 4 # 32-bit | ||
2447 | inst.state.width = 4 # 4 bytes | ||
2448 | else: | ||
2449 | inst.state.mode = 1 # 64-bit | ||
2450 | inst.state.width = 8 # 8 bytes | ||
2451 | self.xed_operand_values_set_mode(inst.xedp, inst.statep) | ||
2452 | |||
2453 | def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): | ||
2454 | self.xed_decoded_inst_zero_keep_mode(inst.xedp) | ||
2455 | err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) | ||
2456 | if err: | ||
2457 | return 0, "" | ||
2458 | # Use AT&T mode (2), alternative is Intel (3) | ||
2459 | ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) | ||
2460 | if not ok: | ||
2461 | return 0, "" | ||
2462 | # Return instruction length and the disassembled instruction text | ||
2463 | # For now, assume the length is in byte 166 | ||
2464 | return inst.xedd[166], inst.buffer.value | ||
2465 | |||
2466 | def TryOpen(file_name): | ||
2467 | try: | ||
2468 | return open(file_name, "rb") | ||
2469 | except: | ||
2470 | return None | ||
2471 | |||
2472 | def Is64Bit(f): | ||
2473 | result = sizeof(c_void_p) | ||
2474 | # ELF support only | ||
2475 | pos = f.tell() | ||
2476 | f.seek(0) | ||
2477 | header = f.read(7) | ||
2478 | f.seek(pos) | ||
2479 | magic = header[0:4] | ||
2480 | eclass = ord(header[4]) | ||
2481 | encoding = ord(header[5]) | ||
2482 | version = ord(header[6]) | ||
2483 | if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: | ||
2484 | result = True if eclass == 2 else False | ||
2485 | return result | ||
2486 | |||
2487 | # Global data | ||
2488 | |||
2489 | class Glb(): | ||
2490 | |||
2491 | def __init__(self, dbref, db, dbname): | ||
2492 | self.dbref = dbref | ||
2493 | self.db = db | ||
2494 | self.dbname = dbname | ||
2495 | self.home_dir = os.path.expanduser("~") | ||
2496 | self.buildid_dir = os.getenv("PERF_BUILDID_DIR") | ||
2497 | if self.buildid_dir: | ||
2498 | self.buildid_dir += "/.build-id/" | ||
2499 | else: | ||
2500 | self.buildid_dir = self.home_dir + "/.debug/.build-id/" | ||
2501 | self.app = None | ||
2502 | self.mainwindow = None | ||
2503 | self.instances_to_shutdown_on_exit = weakref.WeakSet() | ||
2504 | try: | ||
2505 | self.disassembler = LibXED() | ||
2506 | self.have_disassembler = True | ||
2507 | except: | ||
2508 | self.have_disassembler = False | ||
2509 | |||
2510 | def FileFromBuildId(self, build_id): | ||
2511 | file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" | ||
2512 | return TryOpen(file_name) | ||
2513 | |||
2514 | def FileFromNamesAndBuildId(self, short_name, long_name, build_id): | ||
2515 | # Assume current machine i.e. no support for virtualization | ||
2516 | if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": | ||
2517 | file_name = os.getenv("PERF_KCORE") | ||
2518 | f = TryOpen(file_name) if file_name else None | ||
2519 | if f: | ||
2520 | return f | ||
2521 | # For now, no special handling if long_name is /proc/kcore | ||
2522 | f = TryOpen(long_name) | ||
2523 | if f: | ||
2524 | return f | ||
2525 | f = self.FileFromBuildId(build_id) | ||
2526 | if f: | ||
2527 | return f | ||
2528 | return None | ||
2529 | |||
2530 | def AddInstanceToShutdownOnExit(self, instance): | ||
2531 | self.instances_to_shutdown_on_exit.add(instance) | ||
2532 | |||
2533 | # Shutdown any background processes or threads | ||
2534 | def ShutdownInstances(self): | ||
2535 | for x in self.instances_to_shutdown_on_exit: | ||
2536 | try: | ||
2537 | x.Shutdown() | ||
2538 | except: | ||
2539 | pass | ||
2540 | |||
2541 | # Database reference | ||
2542 | |||
2543 | class DBRef(): | ||
2544 | |||
2545 | def __init__(self, is_sqlite3, dbname): | ||
2546 | self.is_sqlite3 = is_sqlite3 | ||
2547 | self.dbname = dbname | ||
2548 | |||
2549 | def Open(self, connection_name): | ||
2550 | dbname = self.dbname | ||
2551 | if self.is_sqlite3: | ||
2552 | db = QSqlDatabase.addDatabase("QSQLITE", connection_name) | ||
2553 | else: | ||
2554 | db = QSqlDatabase.addDatabase("QPSQL", connection_name) | ||
2555 | opts = dbname.split() | ||
2556 | for opt in opts: | ||
2557 | if "=" in opt: | ||
2558 | opt = opt.split("=") | ||
2559 | if opt[0] == "hostname": | ||
2560 | db.setHostName(opt[1]) | ||
2561 | elif opt[0] == "port": | ||
2562 | db.setPort(int(opt[1])) | ||
2563 | elif opt[0] == "username": | ||
2564 | db.setUserName(opt[1]) | ||
2565 | elif opt[0] == "password": | ||
2566 | db.setPassword(opt[1]) | ||
2567 | elif opt[0] == "dbname": | ||
2568 | dbname = opt[1] | ||
2569 | else: | ||
2570 | dbname = opt | ||
2571 | |||
2572 | db.setDatabaseName(dbname) | ||
2573 | if not db.open(): | ||
2574 | raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) | ||
2575 | return db, dbname | ||
2576 | |||
2577 | # Main | ||
2578 | |||
2579 | def Main(): | ||
2580 | if (len(sys.argv) < 2): | ||
2581 | print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}" | ||
2582 | raise Exception("Too few arguments") | ||
2583 | |||
2584 | dbname = sys.argv[1] | ||
2585 | if dbname == "--help-only": | ||
2586 | app = QApplication(sys.argv) | ||
2587 | mainwindow = HelpOnlyWindow() | ||
2588 | mainwindow.show() | ||
2589 | err = app.exec_() | ||
2590 | sys.exit(err) | ||
2591 | |||
2592 | is_sqlite3 = False | ||
2593 | try: | ||
2594 | f = open(dbname) | ||
2595 | if f.read(15) == "SQLite format 3": | ||
2596 | is_sqlite3 = True | ||
2597 | f.close() | ||
2598 | except: | ||
2599 | pass | ||
2600 | |||
2601 | dbref = DBRef(is_sqlite3, dbname) | ||
2602 | db, dbname = dbref.Open("main") | ||
2603 | glb = Glb(dbref, db, dbname) | ||
2604 | app = QApplication(sys.argv) | ||
2605 | glb.app = app | ||
2606 | mainwindow = MainWindow(glb) | ||
2607 | glb.mainwindow = mainwindow | ||
2608 | mainwindow.show() | ||
2609 | err = app.exec_() | ||
2610 | glb.ShutdownInstances() | ||
2611 | db.close() | ||
2612 | sys.exit(err) | ||
2613 | |||
2614 | if __name__ == "__main__": | ||
2615 | Main() | ||
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 6c108fa79ae3..0b2b8305c965 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -21,6 +21,7 @@ perf-y += python-use.o | |||
21 | perf-y += bp_signal.o | 21 | perf-y += bp_signal.o |
22 | perf-y += bp_signal_overflow.o | 22 | perf-y += bp_signal_overflow.o |
23 | perf-y += bp_account.o | 23 | perf-y += bp_account.o |
24 | perf-y += wp.o | ||
24 | perf-y += task-exit.o | 25 | perf-y += task-exit.o |
25 | perf-y += sw-clock.o | 26 | perf-y += sw-clock.o |
26 | perf-y += mmap-thread-lookup.o | 27 | perf-y += mmap-thread-lookup.o |
diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/attr/test-record-group-sampling index 8a33ca4f9e1f..f0729c454f16 100644 --- a/tools/perf/tests/attr/test-record-group-sampling +++ b/tools/perf/tests/attr/test-record-group-sampling | |||
@@ -37,4 +37,3 @@ sample_freq=0 | |||
37 | sample_period=0 | 37 | sample_period=0 |
38 | freq=0 | 38 | freq=0 |
39 | write_backward=0 | 39 | write_backward=0 |
40 | sample_id_all=0 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index d7a5e1b9aa6f..12c09e0ece71 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -121,6 +121,16 @@ static struct test generic_tests[] = { | |||
121 | .is_supported = test__bp_signal_is_supported, | 121 | .is_supported = test__bp_signal_is_supported, |
122 | }, | 122 | }, |
123 | { | 123 | { |
124 | .desc = "Watchpoint", | ||
125 | .func = test__wp, | ||
126 | .is_supported = test__wp_is_supported, | ||
127 | .subtest = { | ||
128 | .skip_if_fail = false, | ||
129 | .get_nr = test__wp_subtest_get_nr, | ||
130 | .get_desc = test__wp_subtest_get_desc, | ||
131 | }, | ||
132 | }, | ||
133 | { | ||
124 | .desc = "Number of exit events of a simple workload", | 134 | .desc = "Number of exit events of a simple workload", |
125 | .func = test__task_exit, | 135 | .func = test__task_exit, |
126 | }, | 136 | }, |
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 699561fa512c..5f8501c68da4 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -8,7 +8,7 @@ | |||
8 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | 8 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, |
9 | int size, bool should_be_signed) | 9 | int size, bool should_be_signed) |
10 | { | 10 | { |
11 | struct format_field *field = perf_evsel__field(evsel, name); | 11 | struct tep_format_field *field = perf_evsel__field(evsel, name); |
12 | int is_signed; | 12 | int is_signed; |
13 | int ret = 0; | 13 | int ret = 0; |
14 | 14 | ||
@@ -17,7 +17,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | |||
17 | return -1; | 17 | return -1; |
18 | } | 18 | } |
19 | 19 | ||
20 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | 20 | is_signed = !!(field->flags | TEP_FIELD_IS_SIGNED); |
21 | if (should_be_signed && !is_signed) { | 21 | if (should_be_signed && !is_signed) { |
22 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | 22 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", |
23 | evsel->name, name, is_signed, should_be_signed); | 23 | evsel->name, name, is_signed, should_be_signed); |
diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 3013ac8f83d0..cab7b0aea6ea 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh | |||
@@ -48,7 +48,7 @@ trace_libc_inet_pton_backtrace() { | |||
48 | *) | 48 | *) |
49 | eventattr='max-stack=3' | 49 | eventattr='max-stack=3' |
50 | echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected | 50 | echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected |
51 | echo ".*\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected | 51 | echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected |
52 | ;; | 52 | ;; |
53 | esac | 53 | esac |
54 | 54 | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a9760e790563..b82f55fcc294 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -59,6 +59,9 @@ int test__python_use(struct test *test, int subtest); | |||
59 | int test__bp_signal(struct test *test, int subtest); | 59 | int test__bp_signal(struct test *test, int subtest); |
60 | int test__bp_signal_overflow(struct test *test, int subtest); | 60 | int test__bp_signal_overflow(struct test *test, int subtest); |
61 | int test__bp_accounting(struct test *test, int subtest); | 61 | int test__bp_accounting(struct test *test, int subtest); |
62 | int test__wp(struct test *test, int subtest); | ||
63 | const char *test__wp_subtest_get_desc(int subtest); | ||
64 | int test__wp_subtest_get_nr(void); | ||
62 | int test__task_exit(struct test *test, int subtest); | 65 | int test__task_exit(struct test *test, int subtest); |
63 | int test__mem(struct test *test, int subtest); | 66 | int test__mem(struct test *test, int subtest); |
64 | int test__sw_clock_freq(struct test *test, int subtest); | 67 | int test__sw_clock_freq(struct test *test, int subtest); |
@@ -106,6 +109,7 @@ int test__unit_number__scnprint(struct test *test, int subtest); | |||
106 | int test__mem2node(struct test *t, int subtest); | 109 | int test__mem2node(struct test *t, int subtest); |
107 | 110 | ||
108 | bool test__bp_signal_is_supported(void); | 111 | bool test__bp_signal_is_supported(void); |
112 | bool test__wp_is_supported(void); | ||
109 | 113 | ||
110 | #if defined(__arm__) || defined(__aarch64__) | 114 | #if defined(__arm__) || defined(__aarch64__) |
111 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 115 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/tests/wp.c b/tools/perf/tests/wp.c new file mode 100644 index 000000000000..f89e6806557b --- /dev/null +++ b/tools/perf/tests/wp.c | |||
@@ -0,0 +1,241 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <stdlib.h> | ||
3 | #include <sys/ioctl.h> | ||
4 | #include <linux/hw_breakpoint.h> | ||
5 | #include "tests.h" | ||
6 | #include "debug.h" | ||
7 | #include "cloexec.h" | ||
8 | |||
9 | #define WP_TEST_ASSERT_VAL(fd, text, val) \ | ||
10 | do { \ | ||
11 | long long count; \ | ||
12 | wp_read(fd, &count, sizeof(long long)); \ | ||
13 | TEST_ASSERT_VAL(text, count == val); \ | ||
14 | } while (0) | ||
15 | |||
16 | volatile u64 data1; | ||
17 | volatile u8 data2[3]; | ||
18 | |||
19 | static int wp_read(int fd, long long *count, int size) | ||
20 | { | ||
21 | int ret = read(fd, count, size); | ||
22 | |||
23 | if (ret != size) { | ||
24 | pr_debug("failed to read: %d\n", ret); | ||
25 | return -1; | ||
26 | } | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static void get__perf_event_attr(struct perf_event_attr *attr, int wp_type, | ||
31 | void *wp_addr, unsigned long wp_len) | ||
32 | { | ||
33 | memset(attr, 0, sizeof(struct perf_event_attr)); | ||
34 | attr->type = PERF_TYPE_BREAKPOINT; | ||
35 | attr->size = sizeof(struct perf_event_attr); | ||
36 | attr->config = 0; | ||
37 | attr->bp_type = wp_type; | ||
38 | attr->bp_addr = (unsigned long)wp_addr; | ||
39 | attr->bp_len = wp_len; | ||
40 | attr->sample_period = 1; | ||
41 | attr->sample_type = PERF_SAMPLE_IP; | ||
42 | attr->exclude_kernel = 1; | ||
43 | attr->exclude_hv = 1; | ||
44 | } | ||
45 | |||
46 | static int __event(int wp_type, void *wp_addr, unsigned long wp_len) | ||
47 | { | ||
48 | int fd; | ||
49 | struct perf_event_attr attr; | ||
50 | |||
51 | get__perf_event_attr(&attr, wp_type, wp_addr, wp_len); | ||
52 | fd = sys_perf_event_open(&attr, 0, -1, -1, | ||
53 | perf_event_open_cloexec_flag()); | ||
54 | if (fd < 0) | ||
55 | pr_debug("failed opening event %x\n", attr.bp_type); | ||
56 | |||
57 | return fd; | ||
58 | } | ||
59 | |||
60 | static int wp_ro_test(void) | ||
61 | { | ||
62 | int fd; | ||
63 | unsigned long tmp, tmp1 = rand(); | ||
64 | |||
65 | fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1)); | ||
66 | if (fd < 0) | ||
67 | return -1; | ||
68 | |||
69 | tmp = data1; | ||
70 | WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1); | ||
71 | |||
72 | data1 = tmp1 + tmp; | ||
73 | WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1); | ||
74 | |||
75 | close(fd); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int wp_wo_test(void) | ||
80 | { | ||
81 | int fd; | ||
82 | unsigned long tmp, tmp1 = rand(); | ||
83 | |||
84 | fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1)); | ||
85 | if (fd < 0) | ||
86 | return -1; | ||
87 | |||
88 | tmp = data1; | ||
89 | WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0); | ||
90 | |||
91 | data1 = tmp1 + tmp; | ||
92 | WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1); | ||
93 | |||
94 | close(fd); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int wp_rw_test(void) | ||
99 | { | ||
100 | int fd; | ||
101 | unsigned long tmp, tmp1 = rand(); | ||
102 | |||
103 | fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1, | ||
104 | sizeof(data1)); | ||
105 | if (fd < 0) | ||
106 | return -1; | ||
107 | |||
108 | tmp = data1; | ||
109 | WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1); | ||
110 | |||
111 | data1 = tmp1 + tmp; | ||
112 | WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2); | ||
113 | |||
114 | close(fd); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int wp_modify_test(void) | ||
119 | { | ||
120 | int fd, ret; | ||
121 | unsigned long tmp = rand(); | ||
122 | struct perf_event_attr new_attr; | ||
123 | |||
124 | fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1)); | ||
125 | if (fd < 0) | ||
126 | return -1; | ||
127 | |||
128 | data1 = tmp; | ||
129 | WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1); | ||
130 | |||
131 | /* Modify watchpoint with disabled = 1 */ | ||
132 | get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0], | ||
133 | sizeof(u8) * 2); | ||
134 | new_attr.disabled = 1; | ||
135 | ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr); | ||
136 | if (ret < 0) { | ||
137 | pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n"); | ||
138 | close(fd); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | data2[1] = tmp; /* Not Counted */ | ||
143 | WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1); | ||
144 | |||
145 | /* Enable the event */ | ||
146 | ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); | ||
147 | if (ret < 0) { | ||
148 | pr_debug("Failed to enable event\n"); | ||
149 | close(fd); | ||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | data2[1] = tmp; /* Counted */ | ||
154 | WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2); | ||
155 | |||
156 | data2[2] = tmp; /* Not Counted */ | ||
157 | WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2); | ||
158 | |||
159 | close(fd); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static bool wp_ro_supported(void) | ||
164 | { | ||
165 | #if defined (__x86_64__) || defined (__i386__) | ||
166 | return false; | ||
167 | #else | ||
168 | return true; | ||
169 | #endif | ||
170 | } | ||
171 | |||
172 | static void wp_ro_skip_msg(void) | ||
173 | { | ||
174 | #if defined (__x86_64__) || defined (__i386__) | ||
175 | pr_debug("Hardware does not support read only watchpoints.\n"); | ||
176 | #endif | ||
177 | } | ||
178 | |||
179 | static struct { | ||
180 | const char *desc; | ||
181 | int (*target_func)(void); | ||
182 | bool (*is_supported)(void); | ||
183 | void (*skip_msg)(void); | ||
184 | } wp_testcase_table[] = { | ||
185 | { | ||
186 | .desc = "Read Only Watchpoint", | ||
187 | .target_func = &wp_ro_test, | ||
188 | .is_supported = &wp_ro_supported, | ||
189 | .skip_msg = &wp_ro_skip_msg, | ||
190 | }, | ||
191 | { | ||
192 | .desc = "Write Only Watchpoint", | ||
193 | .target_func = &wp_wo_test, | ||
194 | }, | ||
195 | { | ||
196 | .desc = "Read / Write Watchpoint", | ||
197 | .target_func = &wp_rw_test, | ||
198 | }, | ||
199 | { | ||
200 | .desc = "Modify Watchpoint", | ||
201 | .target_func = &wp_modify_test, | ||
202 | }, | ||
203 | }; | ||
204 | |||
205 | int test__wp_subtest_get_nr(void) | ||
206 | { | ||
207 | return (int)ARRAY_SIZE(wp_testcase_table); | ||
208 | } | ||
209 | |||
210 | const char *test__wp_subtest_get_desc(int i) | ||
211 | { | ||
212 | if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table)) | ||
213 | return NULL; | ||
214 | return wp_testcase_table[i].desc; | ||
215 | } | ||
216 | |||
217 | int test__wp(struct test *test __maybe_unused, int i) | ||
218 | { | ||
219 | if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table)) | ||
220 | return TEST_FAIL; | ||
221 | |||
222 | if (wp_testcase_table[i].is_supported && | ||
223 | !wp_testcase_table[i].is_supported()) { | ||
224 | wp_testcase_table[i].skip_msg(); | ||
225 | return TEST_SKIP; | ||
226 | } | ||
227 | |||
228 | return !wp_testcase_table[i].target_func() ? TEST_OK : TEST_FAIL; | ||
229 | } | ||
230 | |||
231 | /* The s390 so far does not have support for | ||
232 | * instruction breakpoint using the perf_event_open() system call. | ||
233 | */ | ||
234 | bool test__wp_is_supported(void) | ||
235 | { | ||
236 | #if defined(__s390x__) | ||
237 | return false; | ||
238 | #else | ||
239 | return true; | ||
240 | #endif | ||
241 | } | ||
diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index f528ba35e140..304313073242 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build | |||
@@ -5,7 +5,9 @@ ifeq ($(SRCARCH),$(filter $(SRCARCH),x86)) | |||
5 | libperf-y += ioctl.o | 5 | libperf-y += ioctl.o |
6 | endif | 6 | endif |
7 | libperf-y += kcmp.o | 7 | libperf-y += kcmp.o |
8 | libperf-y += mount_flags.o | ||
8 | libperf-y += pkey_alloc.o | 9 | libperf-y += pkey_alloc.o |
9 | libperf-y += prctl.o | 10 | libperf-y += prctl.o |
11 | libperf-y += sockaddr.o | ||
10 | libperf-y += socket.o | 12 | libperf-y += socket.o |
11 | libperf-y += statx.o | 13 | libperf-y += statx.o |
diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 9615af5d412b..039c29039b2c 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h | |||
@@ -24,15 +24,43 @@ struct strarray { | |||
24 | } | 24 | } |
25 | 25 | ||
26 | size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, int val); | 26 | size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, int val); |
27 | size_t strarray__scnprintf_flags(struct strarray *sa, char *bf, size_t size, unsigned long flags); | ||
27 | 28 | ||
28 | struct trace; | 29 | struct trace; |
29 | struct thread; | 30 | struct thread; |
30 | 31 | ||
31 | size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_t size); | 32 | size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_t size); |
32 | 33 | ||
34 | extern struct strarray strarray__socket_families; | ||
35 | |||
36 | /** | ||
37 | * augmented_arg: extra payload for syscall pointer arguments | ||
38 | |||
39 | * If perf_sample->raw_size is more than what a syscall sys_enter_FOO puts, | ||
40 | * then its the arguments contents, so that we can show more than just a | ||
41 | * pointer. This will be done initially with eBPF, the start of that is at the | ||
42 | * tools/perf/examples/bpf/augmented_syscalls.c example for the openat, but | ||
43 | * will eventually be done automagically caching the running kernel tracefs | ||
44 | * events data into an eBPF C script, that then gets compiled and its .o file | ||
45 | * cached for subsequent use. For char pointers like the ones for 'open' like | ||
46 | * syscalls its easy, for the rest we should use DWARF or better, BTF, much | ||
47 | * more compact. | ||
48 | * | ||
49 | * @size: 8 if all we need is an integer, otherwise all of the augmented arg. | ||
50 | * @int_arg: will be used for integer like pointer contents, like 'accept's 'upeer_addrlen' | ||
51 | * @value: u64 aligned, for structs, pathnames | ||
52 | */ | ||
53 | struct augmented_arg { | ||
54 | int size; | ||
55 | int int_arg; | ||
56 | u64 value[]; | ||
57 | }; | ||
58 | |||
33 | /** | 59 | /** |
34 | * @val: value of syscall argument being formatted | 60 | * @val: value of syscall argument being formatted |
35 | * @args: All the args, use syscall_args__val(arg, nth) to access one | 61 | * @args: All the args, use syscall_args__val(arg, nth) to access one |
62 | * @augmented_args: Extra data that can be collected, for instance, with eBPF for expanding the pathname for open, etc | ||
63 | * @augmented_args_size: augmented_args total payload size | ||
36 | * @thread: tid state (maps, pid, tid, etc) | 64 | * @thread: tid state (maps, pid, tid, etc) |
37 | * @trace: 'perf trace' internals: all threads, etc | 65 | * @trace: 'perf trace' internals: all threads, etc |
38 | * @parm: private area, may be an strarray, for instance | 66 | * @parm: private area, may be an strarray, for instance |
@@ -43,6 +71,10 @@ size_t pid__scnprintf_fd(struct trace *trace, pid_t pid, int fd, char *bf, size_ | |||
43 | struct syscall_arg { | 71 | struct syscall_arg { |
44 | unsigned long val; | 72 | unsigned long val; |
45 | unsigned char *args; | 73 | unsigned char *args; |
74 | struct { | ||
75 | struct augmented_arg *args; | ||
76 | int size; | ||
77 | } augmented; | ||
46 | struct thread *thread; | 78 | struct thread *thread; |
47 | struct trace *trace; | 79 | struct trace *trace; |
48 | void *parm; | 80 | void *parm; |
@@ -91,6 +123,12 @@ size_t syscall_arg__scnprintf_kcmp_type(char *bf, size_t size, struct syscall_ar | |||
91 | size_t syscall_arg__scnprintf_kcmp_idx(char *bf, size_t size, struct syscall_arg *arg); | 123 | size_t syscall_arg__scnprintf_kcmp_idx(char *bf, size_t size, struct syscall_arg *arg); |
92 | #define SCA_KCMP_IDX syscall_arg__scnprintf_kcmp_idx | 124 | #define SCA_KCMP_IDX syscall_arg__scnprintf_kcmp_idx |
93 | 125 | ||
126 | unsigned long syscall_arg__mask_val_mount_flags(struct syscall_arg *arg, unsigned long flags); | ||
127 | #define SCAMV_MOUNT_FLAGS syscall_arg__mask_val_mount_flags | ||
128 | |||
129 | size_t syscall_arg__scnprintf_mount_flags(char *bf, size_t size, struct syscall_arg *arg); | ||
130 | #define SCA_MOUNT_FLAGS syscall_arg__scnprintf_mount_flags | ||
131 | |||
94 | size_t syscall_arg__scnprintf_pkey_alloc_access_rights(char *bf, size_t size, struct syscall_arg *arg); | 132 | size_t syscall_arg__scnprintf_pkey_alloc_access_rights(char *bf, size_t size, struct syscall_arg *arg); |
95 | #define SCA_PKEY_ALLOC_ACCESS_RIGHTS syscall_arg__scnprintf_pkey_alloc_access_rights | 133 | #define SCA_PKEY_ALLOC_ACCESS_RIGHTS syscall_arg__scnprintf_pkey_alloc_access_rights |
96 | 134 | ||
@@ -106,6 +144,9 @@ size_t syscall_arg__scnprintf_prctl_arg2(char *bf, size_t size, struct syscall_a | |||
106 | size_t syscall_arg__scnprintf_prctl_arg3(char *bf, size_t size, struct syscall_arg *arg); | 144 | size_t syscall_arg__scnprintf_prctl_arg3(char *bf, size_t size, struct syscall_arg *arg); |
107 | #define SCA_PRCTL_ARG3 syscall_arg__scnprintf_prctl_arg3 | 145 | #define SCA_PRCTL_ARG3 syscall_arg__scnprintf_prctl_arg3 |
108 | 146 | ||
147 | size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg *arg); | ||
148 | #define SCA_SOCKADDR syscall_arg__scnprintf_sockaddr | ||
149 | |||
109 | size_t syscall_arg__scnprintf_socket_protocol(char *bf, size_t size, struct syscall_arg *arg); | 150 | size_t syscall_arg__scnprintf_socket_protocol(char *bf, size_t size, struct syscall_arg *arg); |
110 | #define SCA_SK_PROTO syscall_arg__scnprintf_socket_protocol | 151 | #define SCA_SK_PROTO syscall_arg__scnprintf_socket_protocol |
111 | 152 | ||
diff --git a/tools/perf/trace/beauty/clone.c b/tools/perf/trace/beauty/clone.c index d64d049ab991..010406500c30 100644 --- a/tools/perf/trace/beauty/clone.c +++ b/tools/perf/trace/beauty/clone.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/cone.c | 3 | * trace/beauty/cone.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/drm_ioctl.sh b/tools/perf/trace/beauty/drm_ioctl.sh index 9d3816815e60..9aa94fd523a9 100755 --- a/tools/perf/trace/beauty/drm_ioctl.sh +++ b/tools/perf/trace/beauty/drm_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/drm/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/drm/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/eventfd.c b/tools/perf/trace/beauty/eventfd.c index 5d6a477a6400..db5b9b492113 100644 --- a/tools/perf/trace/beauty/eventfd.c +++ b/tools/perf/trace/beauty/eventfd.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #ifndef EFD_SEMAPHORE | 2 | #ifndef EFD_SEMAPHORE |
3 | #define EFD_SEMAPHORE 1 | 3 | #define EFD_SEMAPHORE 1 |
4 | #endif | 4 | #endif |
diff --git a/tools/perf/trace/beauty/fcntl.c b/tools/perf/trace/beauty/fcntl.c index 9e8900c13cb1..e6de31674e24 100644 --- a/tools/perf/trace/beauty/fcntl.c +++ b/tools/perf/trace/beauty/fcntl.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/fcntl.c | 3 | * trace/beauty/fcntl.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/flock.c b/tools/perf/trace/beauty/flock.c index c4ff6ad30b06..cf02ae5f0ba6 100644 --- a/tools/perf/trace/beauty/flock.c +++ b/tools/perf/trace/beauty/flock.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | 2 | ||
3 | #include "trace/beauty/beauty.h" | 3 | #include "trace/beauty/beauty.h" |
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
diff --git a/tools/perf/trace/beauty/futex_op.c b/tools/perf/trace/beauty/futex_op.c index 61850fbc85ff..1136bde56406 100644 --- a/tools/perf/trace/beauty/futex_op.c +++ b/tools/perf/trace/beauty/futex_op.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <linux/futex.h> | 2 | #include <linux/futex.h> |
3 | 3 | ||
4 | #ifndef FUTEX_WAIT_BITSET | 4 | #ifndef FUTEX_WAIT_BITSET |
diff --git a/tools/perf/trace/beauty/futex_val3.c b/tools/perf/trace/beauty/futex_val3.c index 26f6b3253511..138b7d588a70 100644 --- a/tools/perf/trace/beauty/futex_val3.c +++ b/tools/perf/trace/beauty/futex_val3.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <linux/futex.h> | 2 | #include <linux/futex.h> |
3 | 3 | ||
4 | #ifndef FUTEX_BITSET_MATCH_ANY | 4 | #ifndef FUTEX_BITSET_MATCH_ANY |
diff --git a/tools/perf/trace/beauty/ioctl.c b/tools/perf/trace/beauty/ioctl.c index 1be3b4cf0827..5d2a7fd8d407 100644 --- a/tools/perf/trace/beauty/ioctl.c +++ b/tools/perf/trace/beauty/ioctl.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/ioctl.c | 3 | * trace/beauty/ioctl.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/kcmp.c b/tools/perf/trace/beauty/kcmp.c index f62040eb9d5c..b276a274f203 100644 --- a/tools/perf/trace/beauty/kcmp.c +++ b/tools/perf/trace/beauty/kcmp.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/kcmp.c | 3 | * trace/beauty/kcmp.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/kcmp_type.sh b/tools/perf/trace/beauty/kcmp_type.sh index a3c304caa336..df8b17486d57 100755 --- a/tools/perf/trace/beauty/kcmp_type.sh +++ b/tools/perf/trace/beauty/kcmp_type.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/kvm_ioctl.sh b/tools/perf/trace/beauty/kvm_ioctl.sh index c4699fd46bb6..4ce54f5bf756 100755 --- a/tools/perf/trace/beauty/kvm_ioctl.sh +++ b/tools/perf/trace/beauty/kvm_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/madvise_behavior.sh b/tools/perf/trace/beauty/madvise_behavior.sh index 431639eb4d29..4527d290cdfc 100755 --- a/tools/perf/trace/beauty/madvise_behavior.sh +++ b/tools/perf/trace/beauty/madvise_behavior.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/asm-generic/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/asm-generic/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/mmap.c b/tools/perf/trace/beauty/mmap.c index 9f68077b241b..c534bd96ef5c 100644 --- a/tools/perf/trace/beauty/mmap.c +++ b/tools/perf/trace/beauty/mmap.c | |||
@@ -1,5 +1,6 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <uapi/linux/mman.h> | 2 | #include <uapi/linux/mman.h> |
3 | #include <linux/log2.h> | ||
3 | 4 | ||
4 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, | 5 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, |
5 | struct syscall_arg *arg) | 6 | struct syscall_arg *arg) |
@@ -30,50 +31,23 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, | |||
30 | 31 | ||
31 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot | 32 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot |
32 | 33 | ||
34 | static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size) | ||
35 | { | ||
36 | #include "trace/beauty/generated/mmap_flags_array.c" | ||
37 | static DEFINE_STRARRAY(mmap_flags); | ||
38 | |||
39 | return strarray__scnprintf_flags(&strarray__mmap_flags, bf, size, flags); | ||
40 | } | ||
41 | |||
33 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, | 42 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, |
34 | struct syscall_arg *arg) | 43 | struct syscall_arg *arg) |
35 | { | 44 | { |
36 | int printed = 0, flags = arg->val; | 45 | unsigned long flags = arg->val; |
37 | 46 | ||
38 | if (flags & MAP_ANONYMOUS) | 47 | if (flags & MAP_ANONYMOUS) |
39 | arg->mask |= (1 << 4) | (1 << 5); /* Mask 4th ('fd') and 5th ('offset') args, ignored */ | 48 | arg->mask |= (1 << 4) | (1 << 5); /* Mask 4th ('fd') and 5th ('offset') args, ignored */ |
40 | 49 | ||
41 | #define P_MMAP_FLAG(n) \ | 50 | return mmap__scnprintf_flags(flags, bf, size); |
42 | if (flags & MAP_##n) { \ | ||
43 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
44 | flags &= ~MAP_##n; \ | ||
45 | } | ||
46 | |||
47 | P_MMAP_FLAG(SHARED); | ||
48 | P_MMAP_FLAG(PRIVATE); | ||
49 | #ifdef MAP_32BIT | ||
50 | P_MMAP_FLAG(32BIT); | ||
51 | #endif | ||
52 | P_MMAP_FLAG(ANONYMOUS); | ||
53 | P_MMAP_FLAG(DENYWRITE); | ||
54 | P_MMAP_FLAG(EXECUTABLE); | ||
55 | P_MMAP_FLAG(FILE); | ||
56 | P_MMAP_FLAG(FIXED); | ||
57 | #ifdef MAP_FIXED_NOREPLACE | ||
58 | P_MMAP_FLAG(FIXED_NOREPLACE); | ||
59 | #endif | ||
60 | P_MMAP_FLAG(GROWSDOWN); | ||
61 | P_MMAP_FLAG(HUGETLB); | ||
62 | P_MMAP_FLAG(LOCKED); | ||
63 | P_MMAP_FLAG(NONBLOCK); | ||
64 | P_MMAP_FLAG(NORESERVE); | ||
65 | P_MMAP_FLAG(POPULATE); | ||
66 | P_MMAP_FLAG(STACK); | ||
67 | P_MMAP_FLAG(UNINITIALIZED); | ||
68 | #ifdef MAP_SYNC | ||
69 | P_MMAP_FLAG(SYNC); | ||
70 | #endif | ||
71 | #undef P_MMAP_FLAG | ||
72 | |||
73 | if (flags) | ||
74 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
75 | |||
76 | return printed; | ||
77 | } | 51 | } |
78 | 52 | ||
79 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | 53 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags |
diff --git a/tools/perf/trace/beauty/mmap_flags.sh b/tools/perf/trace/beauty/mmap_flags.sh new file mode 100755 index 000000000000..22c3fdca8975 --- /dev/null +++ b/tools/perf/trace/beauty/mmap_flags.sh | |||
@@ -0,0 +1,32 @@ | |||
1 | #!/bin/sh | ||
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
3 | |||
4 | if [ $# -ne 2 ] ; then | ||
5 | [ $# -eq 1 ] && hostarch=$1 || hostarch=`uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/` | ||
6 | header_dir=tools/include/uapi/asm-generic | ||
7 | arch_header_dir=tools/arch/${hostarch}/include/uapi/asm | ||
8 | else | ||
9 | header_dir=$1 | ||
10 | arch_header_dir=$2 | ||
11 | fi | ||
12 | |||
13 | arch_mman=${arch_header_dir}/mman.h | ||
14 | |||
15 | # those in egrep -vw are flags, we want just the bits | ||
16 | |||
17 | printf "static const char *mmap_flags[] = {\n" | ||
18 | regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MAP_([[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*' | ||
19 | egrep -q $regex ${arch_mman} && \ | ||
20 | (egrep $regex ${arch_mman} | \ | ||
21 | sed -r "s/$regex/\2 \1/g" | \ | ||
22 | xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n") | ||
23 | egrep -q '#[[:space:]]*include[[:space:]]+<uapi/asm-generic/mman.*' ${arch_mman} && | ||
24 | (egrep $regex ${header_dir}/mman-common.h | \ | ||
25 | egrep -vw 'MAP_(UNINITIALIZED|TYPE|SHARED_VALIDATE)' | \ | ||
26 | sed -r "s/$regex/\2 \1/g" | \ | ||
27 | xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n") | ||
28 | egrep -q '#[[:space:]]*include[[:space:]]+<uapi/asm-generic/mman.h>.*' ${arch_mman} && | ||
29 | (egrep $regex ${header_dir}/mman.h | \ | ||
30 | sed -r "s/$regex/\2 \1/g" | \ | ||
31 | xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n") | ||
32 | printf "};\n" | ||
diff --git a/tools/perf/trace/beauty/mode_t.c b/tools/perf/trace/beauty/mode_t.c index d929ad7dd97b..6879d36d3004 100644 --- a/tools/perf/trace/beauty/mode_t.c +++ b/tools/perf/trace/beauty/mode_t.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
4 | #include <unistd.h> | 4 | #include <unistd.h> |
diff --git a/tools/perf/trace/beauty/mount_flags.c b/tools/perf/trace/beauty/mount_flags.c new file mode 100644 index 000000000000..712935c6620a --- /dev/null +++ b/tools/perf/trace/beauty/mount_flags.c | |||
@@ -0,0 +1,43 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
2 | /* | ||
3 | * trace/beauty/mount_flags.c | ||
4 | * | ||
5 | * Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
6 | */ | ||
7 | |||
8 | #include "trace/beauty/beauty.h" | ||
9 | #include <linux/compiler.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/log2.h> | ||
12 | #include <sys/mount.h> | ||
13 | |||
14 | static size_t mount__scnprintf_flags(unsigned long flags, char *bf, size_t size) | ||
15 | { | ||
16 | #include "trace/beauty/generated/mount_flags_array.c" | ||
17 | static DEFINE_STRARRAY(mount_flags); | ||
18 | |||
19 | return strarray__scnprintf_flags(&strarray__mount_flags, bf, size, flags); | ||
20 | } | ||
21 | |||
22 | unsigned long syscall_arg__mask_val_mount_flags(struct syscall_arg *arg __maybe_unused, unsigned long flags) | ||
23 | { | ||
24 | // do_mount in fs/namespace.c: | ||
25 | /* | ||
26 | * Pre-0.97 versions of mount() didn't have a flags word. When the | ||
27 | * flags word was introduced its top half was required to have the | ||
28 | * magic value 0xC0ED, and this remained so until 2.4.0-test9. | ||
29 | * Therefore, if this magic number is present, it carries no | ||
30 | * information and must be discarded. | ||
31 | */ | ||
32 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
33 | flags &= ~MS_MGC_MSK; | ||
34 | |||
35 | return flags; | ||
36 | } | ||
37 | |||
38 | size_t syscall_arg__scnprintf_mount_flags(char *bf, size_t size, struct syscall_arg *arg) | ||
39 | { | ||
40 | unsigned long flags = arg->val; | ||
41 | |||
42 | return mount__scnprintf_flags(flags, bf, size); | ||
43 | } | ||
diff --git a/tools/perf/trace/beauty/mount_flags.sh b/tools/perf/trace/beauty/mount_flags.sh new file mode 100755 index 000000000000..45547573a1db --- /dev/null +++ b/tools/perf/trace/beauty/mount_flags.sh | |||
@@ -0,0 +1,15 @@ | |||
1 | #!/bin/sh | ||
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
3 | |||
4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | ||
5 | |||
6 | printf "static const char *mount_flags[] = {\n" | ||
7 | regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MS_([[:alnum:]_]+)[[:space:]]+([[:digit:]]+)[[:space:]]*.*' | ||
8 | egrep $regex ${header_dir}/fs.h | egrep -v '(MSK|VERBOSE|MGC_VAL)\>' | \ | ||
9 | sed -r "s/$regex/\2 \2 \1/g" | sort -n | \ | ||
10 | xargs printf "\t[%s ? (ilog2(%s) + 1) : 0] = \"%s\",\n" | ||
11 | regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MS_([[:alnum:]_]+)[[:space:]]+\(1<<([[:digit:]]+)\)[[:space:]]*.*' | ||
12 | egrep $regex ${header_dir}/fs.h | \ | ||
13 | sed -r "s/$regex/\2 \1/g" | \ | ||
14 | xargs printf "\t[%s + 1] = \"%s\",\n" | ||
15 | printf "};\n" | ||
diff --git a/tools/perf/trace/beauty/msg_flags.c b/tools/perf/trace/beauty/msg_flags.c index c064d6aae659..1b9d6306d274 100644 --- a/tools/perf/trace/beauty/msg_flags.c +++ b/tools/perf/trace/beauty/msg_flags.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/socket.h> | 3 | #include <sys/socket.h> |
4 | 4 | ||
diff --git a/tools/perf/trace/beauty/open_flags.c b/tools/perf/trace/beauty/open_flags.c index 6aec6178a99d..cc673fec9184 100644 --- a/tools/perf/trace/beauty/open_flags.c +++ b/tools/perf/trace/beauty/open_flags.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
4 | #include <fcntl.h> | 4 | #include <fcntl.h> |
diff --git a/tools/perf/trace/beauty/perf_event_open.c b/tools/perf/trace/beauty/perf_event_open.c index 2bafd7c995ff..981185c1974b 100644 --- a/tools/perf/trace/beauty/perf_event_open.c +++ b/tools/perf/trace/beauty/perf_event_open.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #ifndef PERF_FLAG_FD_NO_GROUP | 2 | #ifndef PERF_FLAG_FD_NO_GROUP |
3 | # define PERF_FLAG_FD_NO_GROUP (1UL << 0) | 3 | # define PERF_FLAG_FD_NO_GROUP (1UL << 0) |
4 | #endif | 4 | #endif |
diff --git a/tools/perf/trace/beauty/perf_ioctl.sh b/tools/perf/trace/beauty/perf_ioctl.sh index 6492c74df928..9aabd9743ef6 100755 --- a/tools/perf/trace/beauty/perf_ioctl.sh +++ b/tools/perf/trace/beauty/perf_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/pid.c b/tools/perf/trace/beauty/pid.c index 0313df342830..1a6acc46807b 100644 --- a/tools/perf/trace/beauty/pid.c +++ b/tools/perf/trace/beauty/pid.c | |||
@@ -1,4 +1,5 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | |||
2 | size_t syscall_arg__scnprintf_pid(char *bf, size_t size, struct syscall_arg *arg) | 3 | size_t syscall_arg__scnprintf_pid(char *bf, size_t size, struct syscall_arg *arg) |
3 | { | 4 | { |
4 | int pid = arg->val; | 5 | int pid = arg->val; |
diff --git a/tools/perf/trace/beauty/pkey_alloc.c b/tools/perf/trace/beauty/pkey_alloc.c index 2ba784a3734a..1b8ed4cac815 100644 --- a/tools/perf/trace/beauty/pkey_alloc.c +++ b/tools/perf/trace/beauty/pkey_alloc.c | |||
@@ -1,40 +1,36 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/pkey_alloc.c | 3 | * trace/beauty/pkey_alloc.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
10 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
11 | #include <linux/log2.h> | 10 | #include <linux/log2.h> |
12 | 11 | ||
13 | static size_t pkey_alloc__scnprintf_access_rights(int access_rights, char *bf, size_t size) | 12 | size_t strarray__scnprintf_flags(struct strarray *sa, char *bf, size_t size, unsigned long flags) |
14 | { | 13 | { |
15 | int i, printed = 0; | 14 | int i, printed = 0; |
16 | 15 | ||
17 | #include "trace/beauty/generated/pkey_alloc_access_rights_array.c" | 16 | if (flags == 0) { |
18 | static DEFINE_STRARRAY(pkey_alloc_access_rights); | 17 | const char *s = sa->entries[0]; |
19 | |||
20 | if (access_rights == 0) { | ||
21 | const char *s = strarray__pkey_alloc_access_rights.entries[0]; | ||
22 | if (s) | 18 | if (s) |
23 | return scnprintf(bf, size, "%s", s); | 19 | return scnprintf(bf, size, "%s", s); |
24 | return scnprintf(bf, size, "%d", 0); | 20 | return scnprintf(bf, size, "%d", 0); |
25 | } | 21 | } |
26 | 22 | ||
27 | for (i = 1; i < strarray__pkey_alloc_access_rights.nr_entries; ++i) { | 23 | for (i = 1; i < sa->nr_entries; ++i) { |
28 | int bit = 1 << (i - 1); | 24 | unsigned long bit = 1UL << (i - 1); |
29 | 25 | ||
30 | if (!(access_rights & bit)) | 26 | if (!(flags & bit)) |
31 | continue; | 27 | continue; |
32 | 28 | ||
33 | if (printed != 0) | 29 | if (printed != 0) |
34 | printed += scnprintf(bf + printed, size - printed, "|"); | 30 | printed += scnprintf(bf + printed, size - printed, "|"); |
35 | 31 | ||
36 | if (strarray__pkey_alloc_access_rights.entries[i] != NULL) | 32 | if (sa->entries[i] != NULL) |
37 | printed += scnprintf(bf + printed, size - printed, "%s", strarray__pkey_alloc_access_rights.entries[i]); | 33 | printed += scnprintf(bf + printed, size - printed, "%s", sa->entries[i]); |
38 | else | 34 | else |
39 | printed += scnprintf(bf + printed, size - printed, "0x%#", bit); | 35 | printed += scnprintf(bf + printed, size - printed, "0x%#", bit); |
40 | } | 36 | } |
@@ -42,6 +38,14 @@ static size_t pkey_alloc__scnprintf_access_rights(int access_rights, char *bf, s | |||
42 | return printed; | 38 | return printed; |
43 | } | 39 | } |
44 | 40 | ||
41 | static size_t pkey_alloc__scnprintf_access_rights(int access_rights, char *bf, size_t size) | ||
42 | { | ||
43 | #include "trace/beauty/generated/pkey_alloc_access_rights_array.c" | ||
44 | static DEFINE_STRARRAY(pkey_alloc_access_rights); | ||
45 | |||
46 | return strarray__scnprintf_flags(&strarray__pkey_alloc_access_rights, bf, size, access_rights); | ||
47 | } | ||
48 | |||
45 | size_t syscall_arg__scnprintf_pkey_alloc_access_rights(char *bf, size_t size, struct syscall_arg *arg) | 49 | size_t syscall_arg__scnprintf_pkey_alloc_access_rights(char *bf, size_t size, struct syscall_arg *arg) |
46 | { | 50 | { |
47 | unsigned long cmd = arg->val; | 51 | unsigned long cmd = arg->val; |
diff --git a/tools/perf/trace/beauty/pkey_alloc_access_rights.sh b/tools/perf/trace/beauty/pkey_alloc_access_rights.sh index e0a51aeb20b2..f8f1b560cf8a 100755 --- a/tools/perf/trace/beauty/pkey_alloc_access_rights.sh +++ b/tools/perf/trace/beauty/pkey_alloc_access_rights.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/asm-generic/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/asm-generic/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/prctl.c b/tools/perf/trace/beauty/prctl.c index 246130dad6c4..be7a5d395975 100644 --- a/tools/perf/trace/beauty/prctl.c +++ b/tools/perf/trace/beauty/prctl.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/prctl.c | 3 | * trace/beauty/prctl.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/prctl_option.sh b/tools/perf/trace/beauty/prctl_option.sh index f24722146ebe..d32f8f1124af 100755 --- a/tools/perf/trace/beauty/prctl_option.sh +++ b/tools/perf/trace/beauty/prctl_option.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/sched_policy.c b/tools/perf/trace/beauty/sched_policy.c index ba5096ae76b6..48f2b5c9aa3e 100644 --- a/tools/perf/trace/beauty/sched_policy.c +++ b/tools/perf/trace/beauty/sched_policy.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sched.h> | 2 | #include <sched.h> |
3 | 3 | ||
4 | /* | 4 | /* |
diff --git a/tools/perf/trace/beauty/seccomp.c b/tools/perf/trace/beauty/seccomp.c index b7097fd5fed9..e36156b19c70 100644 --- a/tools/perf/trace/beauty/seccomp.c +++ b/tools/perf/trace/beauty/seccomp.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #ifndef SECCOMP_SET_MODE_STRICT | 2 | #ifndef SECCOMP_SET_MODE_STRICT |
3 | #define SECCOMP_SET_MODE_STRICT 0 | 3 | #define SECCOMP_SET_MODE_STRICT 0 |
4 | #endif | 4 | #endif |
diff --git a/tools/perf/trace/beauty/signum.c b/tools/perf/trace/beauty/signum.c index bde18a53f090..587fec545b8a 100644 --- a/tools/perf/trace/beauty/signum.c +++ b/tools/perf/trace/beauty/signum.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <signal.h> | 2 | #include <signal.h> |
3 | 3 | ||
4 | static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) | 4 | static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) |
diff --git a/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh b/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh index eb511bb5fbd3..e0803b957593 100755 --- a/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh +++ b/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/sound/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/sound/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh b/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh index 6818392968b2..7a464a7bf913 100755 --- a/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh +++ b/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/sound/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/sound/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/sockaddr.c b/tools/perf/trace/beauty/sockaddr.c new file mode 100644 index 000000000000..9410ad230f10 --- /dev/null +++ b/tools/perf/trace/beauty/sockaddr.c | |||
@@ -0,0 +1,76 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
2 | // Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | |||
4 | #include "trace/beauty/beauty.h" | ||
5 | #include <sys/socket.h> | ||
6 | #include <sys/types.h> | ||
7 | #include <sys/un.h> | ||
8 | #include <arpa/inet.h> | ||
9 | |||
10 | static const char *socket_families[] = { | ||
11 | "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", | ||
12 | "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", | ||
13 | "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", | ||
14 | "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", | ||
15 | "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", | ||
16 | "ALG", "NFC", "VSOCK", | ||
17 | }; | ||
18 | DEFINE_STRARRAY(socket_families); | ||
19 | |||
20 | static size_t af_inet__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
21 | { | ||
22 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; | ||
23 | char tmp[16]; | ||
24 | return scnprintf(bf, size, ", port: %d, addr: %s", ntohs(sin->sin_port), | ||
25 | inet_ntop(sin->sin_family, &sin->sin_addr, tmp, sizeof(tmp))); | ||
26 | } | ||
27 | |||
28 | static size_t af_inet6__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
29 | { | ||
30 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; | ||
31 | u32 flowinfo = ntohl(sin6->sin6_flowinfo); | ||
32 | char tmp[512]; | ||
33 | size_t printed = scnprintf(bf, size, ", port: %d, addr: %s", ntohs(sin6->sin6_port), | ||
34 | inet_ntop(sin6->sin6_family, &sin6->sin6_addr, tmp, sizeof(tmp))); | ||
35 | if (flowinfo != 0) | ||
36 | printed += scnprintf(bf + printed, size - printed, ", flowinfo: %lu", flowinfo); | ||
37 | if (sin6->sin6_scope_id != 0) | ||
38 | printed += scnprintf(bf + printed, size - printed, ", scope_id: %lu", sin6->sin6_scope_id); | ||
39 | |||
40 | return printed; | ||
41 | } | ||
42 | |||
43 | static size_t af_local__scnprintf(struct sockaddr *sa, char *bf, size_t size) | ||
44 | { | ||
45 | struct sockaddr_un *sun = (struct sockaddr_un *)sa; | ||
46 | return scnprintf(bf, size, ", path: %s", sun->sun_path); | ||
47 | } | ||
48 | |||
49 | static size_t (*af_scnprintfs[])(struct sockaddr *sa, char *bf, size_t size) = { | ||
50 | [AF_LOCAL] = af_local__scnprintf, | ||
51 | [AF_INET] = af_inet__scnprintf, | ||
52 | [AF_INET6] = af_inet6__scnprintf, | ||
53 | }; | ||
54 | |||
55 | static size_t syscall_arg__scnprintf_augmented_sockaddr(struct syscall_arg *arg, char *bf, size_t size) | ||
56 | { | ||
57 | struct sockaddr *sa = (struct sockaddr *)arg->augmented.args; | ||
58 | char family[32]; | ||
59 | size_t printed; | ||
60 | |||
61 | strarray__scnprintf(&strarray__socket_families, family, sizeof(family), "%d", sa->sa_family); | ||
62 | printed = scnprintf(bf, size, "{ .family: %s", family); | ||
63 | |||
64 | if (sa->sa_family < ARRAY_SIZE(af_scnprintfs) && af_scnprintfs[sa->sa_family]) | ||
65 | printed += af_scnprintfs[sa->sa_family](sa, bf + printed, size - printed); | ||
66 | |||
67 | return printed + scnprintf(bf + printed, size - printed, " }"); | ||
68 | } | ||
69 | |||
70 | size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg *arg) | ||
71 | { | ||
72 | if (arg->augmented.args) | ||
73 | return syscall_arg__scnprintf_augmented_sockaddr(arg, bf, size); | ||
74 | |||
75 | return scnprintf(bf, size, "%#x", arg->val); | ||
76 | } | ||
diff --git a/tools/perf/trace/beauty/socket.c b/tools/perf/trace/beauty/socket.c index 65227269384b..d971a2596417 100644 --- a/tools/perf/trace/beauty/socket.c +++ b/tools/perf/trace/beauty/socket.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | /* | 2 | /* |
3 | * trace/beauty/socket.c | 3 | * trace/beauty/socket.c |
4 | * | 4 | * |
diff --git a/tools/perf/trace/beauty/socket_ipproto.sh b/tools/perf/trace/beauty/socket_ipproto.sh index a3cc24633bec..de0f2f29017f 100755 --- a/tools/perf/trace/beauty/socket_ipproto.sh +++ b/tools/perf/trace/beauty/socket_ipproto.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/socket_type.c b/tools/perf/trace/beauty/socket_type.c index bca26aef4a77..a63a9a332aa0 100644 --- a/tools/perf/trace/beauty/socket_type.c +++ b/tools/perf/trace/beauty/socket_type.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/socket.h> | 3 | #include <sys/socket.h> |
4 | 4 | ||
diff --git a/tools/perf/trace/beauty/statx.c b/tools/perf/trace/beauty/statx.c index 5643b692af4c..630f2760dd66 100644 --- a/tools/perf/trace/beauty/statx.c +++ b/tools/perf/trace/beauty/statx.c | |||
@@ -1,9 +1,8 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
1 | /* | 2 | /* |
2 | * trace/beauty/statx.c | 3 | * trace/beauty/statx.c |
3 | * | 4 | * |
4 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include "trace/beauty/beauty.h" | 8 | #include "trace/beauty/beauty.h" |
diff --git a/tools/perf/trace/beauty/vhost_virtio_ioctl.sh b/tools/perf/trace/beauty/vhost_virtio_ioctl.sh index 0f6a5197d0be..439773daaf77 100755 --- a/tools/perf/trace/beauty/vhost_virtio_ioctl.sh +++ b/tools/perf/trace/beauty/vhost_virtio_ioctl.sh | |||
@@ -1,4 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # SPDX-License-Identifier: LGPL-2.1 | ||
2 | 3 | ||
3 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ | 4 | [ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ |
4 | 5 | ||
diff --git a/tools/perf/trace/beauty/waitid_options.c b/tools/perf/trace/beauty/waitid_options.c index 8465281a093d..42ff58ad613b 100644 --- a/tools/perf/trace/beauty/waitid_options.c +++ b/tools/perf/trace/beauty/waitid_options.c | |||
@@ -1,4 +1,4 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/wait.h> | 3 | #include <sys/wait.h> |
4 | 4 | ||
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 7efe15b9618d..ecd9f9ceda77 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -73,6 +73,7 @@ libperf-y += vdso.o | |||
73 | libperf-y += counts.o | 73 | libperf-y += counts.o |
74 | libperf-y += stat.o | 74 | libperf-y += stat.o |
75 | libperf-y += stat-shadow.o | 75 | libperf-y += stat-shadow.o |
76 | libperf-y += stat-display.o | ||
76 | libperf-y += record.o | 77 | libperf-y += record.o |
77 | libperf-y += srcline.o | 78 | libperf-y += srcline.o |
78 | libperf-y += data.o | 79 | libperf-y += data.o |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 28cd6a17491b..6936daf89ddd 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -139,6 +139,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i | |||
139 | #include "arch/x86/annotate/instructions.c" | 139 | #include "arch/x86/annotate/instructions.c" |
140 | #include "arch/powerpc/annotate/instructions.c" | 140 | #include "arch/powerpc/annotate/instructions.c" |
141 | #include "arch/s390/annotate/instructions.c" | 141 | #include "arch/s390/annotate/instructions.c" |
142 | #include "arch/sparc/annotate/instructions.c" | ||
142 | 143 | ||
143 | static struct arch architectures[] = { | 144 | static struct arch architectures[] = { |
144 | { | 145 | { |
@@ -170,6 +171,13 @@ static struct arch architectures[] = { | |||
170 | .comment_char = '#', | 171 | .comment_char = '#', |
171 | }, | 172 | }, |
172 | }, | 173 | }, |
174 | { | ||
175 | .name = "sparc", | ||
176 | .init = sparc__annotate_init, | ||
177 | .objdump = { | ||
178 | .comment_char = '#', | ||
179 | }, | ||
180 | }, | ||
173 | }; | 181 | }; |
174 | 182 | ||
175 | static void ins__delete(struct ins_operands *ops) | 183 | static void ins__delete(struct ins_operands *ops) |
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index db1511359c5e..72d5ba2479bf 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -906,9 +906,8 @@ out_free: | |||
906 | return err; | 906 | return err; |
907 | } | 907 | } |
908 | 908 | ||
909 | int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused, | 909 | int perf_event__process_auxtrace_info(struct perf_session *session, |
910 | union perf_event *event, | 910 | union perf_event *event) |
911 | struct perf_session *session) | ||
912 | { | 911 | { |
913 | enum auxtrace_type type = event->auxtrace_info.type; | 912 | enum auxtrace_type type = event->auxtrace_info.type; |
914 | 913 | ||
@@ -932,9 +931,8 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused, | |||
932 | } | 931 | } |
933 | } | 932 | } |
934 | 933 | ||
935 | s64 perf_event__process_auxtrace(struct perf_tool *tool, | 934 | s64 perf_event__process_auxtrace(struct perf_session *session, |
936 | union perf_event *event, | 935 | union perf_event *event) |
937 | struct perf_session *session) | ||
938 | { | 936 | { |
939 | s64 err; | 937 | s64 err; |
940 | 938 | ||
@@ -950,7 +948,7 @@ s64 perf_event__process_auxtrace(struct perf_tool *tool, | |||
950 | if (!session->auxtrace || event->header.type != PERF_RECORD_AUXTRACE) | 948 | if (!session->auxtrace || event->header.type != PERF_RECORD_AUXTRACE) |
951 | return -EINVAL; | 949 | return -EINVAL; |
952 | 950 | ||
953 | err = session->auxtrace->process_auxtrace_event(session, event, tool); | 951 | err = session->auxtrace->process_auxtrace_event(session, event, session->tool); |
954 | if (err < 0) | 952 | if (err < 0) |
955 | return err; | 953 | return err; |
956 | 954 | ||
@@ -964,16 +962,23 @@ s64 perf_event__process_auxtrace(struct perf_tool *tool, | |||
964 | #define PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ 64 | 962 | #define PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ 64 |
965 | #define PERF_ITRACE_MAX_LAST_BRANCH_SZ 1024 | 963 | #define PERF_ITRACE_MAX_LAST_BRANCH_SZ 1024 |
966 | 964 | ||
967 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts) | 965 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts, |
966 | bool no_sample) | ||
968 | { | 967 | { |
969 | synth_opts->instructions = true; | ||
970 | synth_opts->branches = true; | 968 | synth_opts->branches = true; |
971 | synth_opts->transactions = true; | 969 | synth_opts->transactions = true; |
972 | synth_opts->ptwrites = true; | 970 | synth_opts->ptwrites = true; |
973 | synth_opts->pwr_events = true; | 971 | synth_opts->pwr_events = true; |
974 | synth_opts->errors = true; | 972 | synth_opts->errors = true; |
975 | synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; | 973 | if (no_sample) { |
976 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; | 974 | synth_opts->period_type = PERF_ITRACE_PERIOD_INSTRUCTIONS; |
975 | synth_opts->period = 1; | ||
976 | synth_opts->calls = true; | ||
977 | } else { | ||
978 | synth_opts->instructions = true; | ||
979 | synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; | ||
980 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; | ||
981 | } | ||
977 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; | 982 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; |
978 | synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ; | 983 | synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ; |
979 | synth_opts->initial_skip = 0; | 984 | synth_opts->initial_skip = 0; |
@@ -1001,7 +1006,7 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, | |||
1001 | } | 1006 | } |
1002 | 1007 | ||
1003 | if (!str) { | 1008 | if (!str) { |
1004 | itrace_synth_opts__set_default(synth_opts); | 1009 | itrace_synth_opts__set_default(synth_opts, false); |
1005 | return 0; | 1010 | return 0; |
1006 | } | 1011 | } |
1007 | 1012 | ||
@@ -1185,9 +1190,8 @@ void events_stats__auxtrace_error_warn(const struct events_stats *stats) | |||
1185 | } | 1190 | } |
1186 | } | 1191 | } |
1187 | 1192 | ||
1188 | int perf_event__process_auxtrace_error(struct perf_tool *tool __maybe_unused, | 1193 | int perf_event__process_auxtrace_error(struct perf_session *session, |
1189 | union perf_event *event, | 1194 | union perf_event *event) |
1190 | struct perf_session *session) | ||
1191 | { | 1195 | { |
1192 | if (auxtrace__dont_decode(session)) | 1196 | if (auxtrace__dont_decode(session)) |
1193 | return 0; | 1197 | return 0; |
@@ -1196,11 +1200,12 @@ int perf_event__process_auxtrace_error(struct perf_tool *tool __maybe_unused, | |||
1196 | return 0; | 1200 | return 0; |
1197 | } | 1201 | } |
1198 | 1202 | ||
1199 | static int __auxtrace_mmap__read(struct auxtrace_mmap *mm, | 1203 | static int __auxtrace_mmap__read(struct perf_mmap *map, |
1200 | struct auxtrace_record *itr, | 1204 | struct auxtrace_record *itr, |
1201 | struct perf_tool *tool, process_auxtrace_t fn, | 1205 | struct perf_tool *tool, process_auxtrace_t fn, |
1202 | bool snapshot, size_t snapshot_size) | 1206 | bool snapshot, size_t snapshot_size) |
1203 | { | 1207 | { |
1208 | struct auxtrace_mmap *mm = &map->auxtrace_mmap; | ||
1204 | u64 head, old = mm->prev, offset, ref; | 1209 | u64 head, old = mm->prev, offset, ref; |
1205 | unsigned char *data = mm->base; | 1210 | unsigned char *data = mm->base; |
1206 | size_t size, head_off, old_off, len1, len2, padding; | 1211 | size_t size, head_off, old_off, len1, len2, padding; |
@@ -1287,7 +1292,7 @@ static int __auxtrace_mmap__read(struct auxtrace_mmap *mm, | |||
1287 | ev.auxtrace.tid = mm->tid; | 1292 | ev.auxtrace.tid = mm->tid; |
1288 | ev.auxtrace.cpu = mm->cpu; | 1293 | ev.auxtrace.cpu = mm->cpu; |
1289 | 1294 | ||
1290 | if (fn(tool, &ev, data1, len1, data2, len2)) | 1295 | if (fn(tool, map, &ev, data1, len1, data2, len2)) |
1291 | return -1; | 1296 | return -1; |
1292 | 1297 | ||
1293 | mm->prev = head; | 1298 | mm->prev = head; |
@@ -1306,18 +1311,18 @@ static int __auxtrace_mmap__read(struct auxtrace_mmap *mm, | |||
1306 | return 1; | 1311 | return 1; |
1307 | } | 1312 | } |
1308 | 1313 | ||
1309 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, | 1314 | int auxtrace_mmap__read(struct perf_mmap *map, struct auxtrace_record *itr, |
1310 | struct perf_tool *tool, process_auxtrace_t fn) | 1315 | struct perf_tool *tool, process_auxtrace_t fn) |
1311 | { | 1316 | { |
1312 | return __auxtrace_mmap__read(mm, itr, tool, fn, false, 0); | 1317 | return __auxtrace_mmap__read(map, itr, tool, fn, false, 0); |
1313 | } | 1318 | } |
1314 | 1319 | ||
1315 | int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm, | 1320 | int auxtrace_mmap__read_snapshot(struct perf_mmap *map, |
1316 | struct auxtrace_record *itr, | 1321 | struct auxtrace_record *itr, |
1317 | struct perf_tool *tool, process_auxtrace_t fn, | 1322 | struct perf_tool *tool, process_auxtrace_t fn, |
1318 | size_t snapshot_size) | 1323 | size_t snapshot_size) |
1319 | { | 1324 | { |
1320 | return __auxtrace_mmap__read(mm, itr, tool, fn, true, snapshot_size); | 1325 | return __auxtrace_mmap__read(map, itr, tool, fn, true, snapshot_size); |
1321 | } | 1326 | } |
1322 | 1327 | ||
1323 | /** | 1328 | /** |
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 71fc3bd74299..8e50f96d4b23 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
24 | #include <linux/perf_event.h> | 24 | #include <linux/perf_event.h> |
25 | #include <linux/types.h> | 25 | #include <linux/types.h> |
26 | #include <asm/bitsperlong.h> | ||
26 | 27 | ||
27 | #include "../perf.h" | 28 | #include "../perf.h" |
28 | #include "event.h" | 29 | #include "event.h" |
@@ -33,6 +34,7 @@ union perf_event; | |||
33 | struct perf_session; | 34 | struct perf_session; |
34 | struct perf_evlist; | 35 | struct perf_evlist; |
35 | struct perf_tool; | 36 | struct perf_tool; |
37 | struct perf_mmap; | ||
36 | struct option; | 38 | struct option; |
37 | struct record_opts; | 39 | struct record_opts; |
38 | struct auxtrace_info_event; | 40 | struct auxtrace_info_event; |
@@ -56,6 +58,7 @@ enum itrace_period_type { | |||
56 | /** | 58 | /** |
57 | * struct itrace_synth_opts - AUX area tracing synthesis options. | 59 | * struct itrace_synth_opts - AUX area tracing synthesis options. |
58 | * @set: indicates whether or not options have been set | 60 | * @set: indicates whether or not options have been set |
61 | * @default_no_sample: Default to no sampling. | ||
59 | * @inject: indicates the event (not just the sample) must be fully synthesized | 62 | * @inject: indicates the event (not just the sample) must be fully synthesized |
60 | * because 'perf inject' will write it out | 63 | * because 'perf inject' will write it out |
61 | * @instructions: whether to synthesize 'instructions' events | 64 | * @instructions: whether to synthesize 'instructions' events |
@@ -80,6 +83,7 @@ enum itrace_period_type { | |||
80 | */ | 83 | */ |
81 | struct itrace_synth_opts { | 84 | struct itrace_synth_opts { |
82 | bool set; | 85 | bool set; |
86 | bool default_no_sample; | ||
83 | bool inject; | 87 | bool inject; |
84 | bool instructions; | 88 | bool instructions; |
85 | bool branches; | 89 | bool branches; |
@@ -434,13 +438,14 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | |||
434 | bool per_cpu); | 438 | bool per_cpu); |
435 | 439 | ||
436 | typedef int (*process_auxtrace_t)(struct perf_tool *tool, | 440 | typedef int (*process_auxtrace_t)(struct perf_tool *tool, |
441 | struct perf_mmap *map, | ||
437 | union perf_event *event, void *data1, | 442 | union perf_event *event, void *data1, |
438 | size_t len1, void *data2, size_t len2); | 443 | size_t len1, void *data2, size_t len2); |
439 | 444 | ||
440 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, | 445 | int auxtrace_mmap__read(struct perf_mmap *map, struct auxtrace_record *itr, |
441 | struct perf_tool *tool, process_auxtrace_t fn); | 446 | struct perf_tool *tool, process_auxtrace_t fn); |
442 | 447 | ||
443 | int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm, | 448 | int auxtrace_mmap__read_snapshot(struct perf_mmap *map, |
444 | struct auxtrace_record *itr, | 449 | struct auxtrace_record *itr, |
445 | struct perf_tool *tool, process_auxtrace_t fn, | 450 | struct perf_tool *tool, process_auxtrace_t fn, |
446 | size_t snapshot_size); | 451 | size_t snapshot_size); |
@@ -517,18 +522,16 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, | |||
517 | struct perf_tool *tool, | 522 | struct perf_tool *tool, |
518 | struct perf_session *session, | 523 | struct perf_session *session, |
519 | perf_event__handler_t process); | 524 | perf_event__handler_t process); |
520 | int perf_event__process_auxtrace_info(struct perf_tool *tool, | 525 | int perf_event__process_auxtrace_info(struct perf_session *session, |
521 | union perf_event *event, | 526 | union perf_event *event); |
522 | struct perf_session *session); | 527 | s64 perf_event__process_auxtrace(struct perf_session *session, |
523 | s64 perf_event__process_auxtrace(struct perf_tool *tool, | 528 | union perf_event *event); |
524 | union perf_event *event, | 529 | int perf_event__process_auxtrace_error(struct perf_session *session, |
525 | struct perf_session *session); | 530 | union perf_event *event); |
526 | int perf_event__process_auxtrace_error(struct perf_tool *tool, | ||
527 | union perf_event *event, | ||
528 | struct perf_session *session); | ||
529 | int itrace_parse_synth_opts(const struct option *opt, const char *str, | 531 | int itrace_parse_synth_opts(const struct option *opt, const char *str, |
530 | int unset); | 532 | int unset); |
531 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts); | 533 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts, |
534 | bool no_sample); | ||
532 | 535 | ||
533 | size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp); | 536 | size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp); |
534 | void perf_session__auxtrace_error_inc(struct perf_session *session, | 537 | void perf_session__auxtrace_error_inc(struct perf_session *session, |
@@ -577,6 +580,23 @@ static inline void auxtrace__free(struct perf_session *session) | |||
577 | return session->auxtrace->free(session); | 580 | return session->auxtrace->free(session); |
578 | } | 581 | } |
579 | 582 | ||
583 | #define ITRACE_HELP \ | ||
584 | " i: synthesize instructions events\n" \ | ||
585 | " b: synthesize branches events\n" \ | ||
586 | " c: synthesize branches events (calls only)\n" \ | ||
587 | " r: synthesize branches events (returns only)\n" \ | ||
588 | " x: synthesize transactions events\n" \ | ||
589 | " w: synthesize ptwrite events\n" \ | ||
590 | " p: synthesize power events\n" \ | ||
591 | " e: synthesize error events\n" \ | ||
592 | " d: create a debug log\n" \ | ||
593 | " g[len]: synthesize a call chain (use with i or x)\n" \ | ||
594 | " l[len]: synthesize last branch entries (use with i or x)\n" \ | ||
595 | " sNUMBER: skip initial number of events\n" \ | ||
596 | " PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \ | ||
597 | " concatenate multiple options. Default is ibxwpe or cewp\n" | ||
598 | |||
599 | |||
580 | #else | 600 | #else |
581 | 601 | ||
582 | static inline struct auxtrace_record * | 602 | static inline struct auxtrace_record * |
@@ -717,6 +737,8 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | |||
717 | struct perf_evlist *evlist, int idx, | 737 | struct perf_evlist *evlist, int idx, |
718 | bool per_cpu); | 738 | bool per_cpu); |
719 | 739 | ||
740 | #define ITRACE_HELP "" | ||
741 | |||
720 | #endif | 742 | #endif |
721 | 743 | ||
722 | #endif | 744 | #endif |
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 47aac41349a2..f9ae1a993806 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
@@ -1615,7 +1615,7 @@ struct perf_evsel *bpf__setup_output_event(struct perf_evlist *evlist, const cha | |||
1615 | int bpf__setup_stdout(struct perf_evlist *evlist) | 1615 | int bpf__setup_stdout(struct perf_evlist *evlist) |
1616 | { | 1616 | { |
1617 | struct perf_evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__"); | 1617 | struct perf_evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__"); |
1618 | return IS_ERR(evsel) ? PTR_ERR(evsel) : 0; | 1618 | return PTR_ERR_OR_ZERO(evsel); |
1619 | } | 1619 | } |
1620 | 1620 | ||
1621 | #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) | 1621 | #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) |
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 2ae640257fdb..73430b73570d 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c | |||
@@ -244,6 +244,27 @@ static void cs_etm__free(struct perf_session *session) | |||
244 | zfree(&aux); | 244 | zfree(&aux); |
245 | } | 245 | } |
246 | 246 | ||
247 | static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address) | ||
248 | { | ||
249 | struct machine *machine; | ||
250 | |||
251 | machine = etmq->etm->machine; | ||
252 | |||
253 | if (address >= etmq->etm->kernel_start) { | ||
254 | if (machine__is_host(machine)) | ||
255 | return PERF_RECORD_MISC_KERNEL; | ||
256 | else | ||
257 | return PERF_RECORD_MISC_GUEST_KERNEL; | ||
258 | } else { | ||
259 | if (machine__is_host(machine)) | ||
260 | return PERF_RECORD_MISC_USER; | ||
261 | else if (perf_guest) | ||
262 | return PERF_RECORD_MISC_GUEST_USER; | ||
263 | else | ||
264 | return PERF_RECORD_MISC_HYPERVISOR; | ||
265 | } | ||
266 | } | ||
267 | |||
247 | static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address, | 268 | static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address, |
248 | size_t size, u8 *buffer) | 269 | size_t size, u8 *buffer) |
249 | { | 270 | { |
@@ -258,10 +279,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address, | |||
258 | return -1; | 279 | return -1; |
259 | 280 | ||
260 | machine = etmq->etm->machine; | 281 | machine = etmq->etm->machine; |
261 | if (address >= etmq->etm->kernel_start) | 282 | cpumode = cs_etm__cpu_mode(etmq, address); |
262 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
263 | else | ||
264 | cpumode = PERF_RECORD_MISC_USER; | ||
265 | 283 | ||
266 | thread = etmq->thread; | 284 | thread = etmq->thread; |
267 | if (!thread) { | 285 | if (!thread) { |
@@ -653,7 +671,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, | |||
653 | struct perf_sample sample = {.ip = 0,}; | 671 | struct perf_sample sample = {.ip = 0,}; |
654 | 672 | ||
655 | event->sample.header.type = PERF_RECORD_SAMPLE; | 673 | event->sample.header.type = PERF_RECORD_SAMPLE; |
656 | event->sample.header.misc = PERF_RECORD_MISC_USER; | 674 | event->sample.header.misc = cs_etm__cpu_mode(etmq, addr); |
657 | event->sample.header.size = sizeof(struct perf_event_header); | 675 | event->sample.header.size = sizeof(struct perf_event_header); |
658 | 676 | ||
659 | sample.ip = addr; | 677 | sample.ip = addr; |
@@ -665,7 +683,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, | |||
665 | sample.cpu = etmq->packet->cpu; | 683 | sample.cpu = etmq->packet->cpu; |
666 | sample.flags = 0; | 684 | sample.flags = 0; |
667 | sample.insn_len = 1; | 685 | sample.insn_len = 1; |
668 | sample.cpumode = event->header.misc; | 686 | sample.cpumode = event->sample.header.misc; |
669 | 687 | ||
670 | if (etm->synth_opts.last_branch) { | 688 | if (etm->synth_opts.last_branch) { |
671 | cs_etm__copy_last_branch_rb(etmq); | 689 | cs_etm__copy_last_branch_rb(etmq); |
@@ -706,12 +724,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq) | |||
706 | u64 nr; | 724 | u64 nr; |
707 | struct branch_entry entries; | 725 | struct branch_entry entries; |
708 | } dummy_bs; | 726 | } dummy_bs; |
727 | u64 ip; | ||
728 | |||
729 | ip = cs_etm__last_executed_instr(etmq->prev_packet); | ||
709 | 730 | ||
710 | event->sample.header.type = PERF_RECORD_SAMPLE; | 731 | event->sample.header.type = PERF_RECORD_SAMPLE; |
711 | event->sample.header.misc = PERF_RECORD_MISC_USER; | 732 | event->sample.header.misc = cs_etm__cpu_mode(etmq, ip); |
712 | event->sample.header.size = sizeof(struct perf_event_header); | 733 | event->sample.header.size = sizeof(struct perf_event_header); |
713 | 734 | ||
714 | sample.ip = cs_etm__last_executed_instr(etmq->prev_packet); | 735 | sample.ip = ip; |
715 | sample.pid = etmq->pid; | 736 | sample.pid = etmq->pid; |
716 | sample.tid = etmq->tid; | 737 | sample.tid = etmq->tid; |
717 | sample.addr = cs_etm__first_executed_instr(etmq->packet); | 738 | sample.addr = cs_etm__first_executed_instr(etmq->packet); |
@@ -720,7 +741,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq) | |||
720 | sample.period = 1; | 741 | sample.period = 1; |
721 | sample.cpu = etmq->packet->cpu; | 742 | sample.cpu = etmq->packet->cpu; |
722 | sample.flags = 0; | 743 | sample.flags = 0; |
723 | sample.cpumode = PERF_RECORD_MISC_USER; | 744 | sample.cpumode = event->sample.header.misc; |
724 | 745 | ||
725 | /* | 746 | /* |
726 | * perf report cannot handle events without a branch stack | 747 | * perf report cannot handle events without a branch stack |
@@ -1432,7 +1453,8 @@ int cs_etm__process_auxtrace_info(union perf_event *event, | |||
1432 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { | 1453 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { |
1433 | etm->synth_opts = *session->itrace_synth_opts; | 1454 | etm->synth_opts = *session->itrace_synth_opts; |
1434 | } else { | 1455 | } else { |
1435 | itrace_synth_opts__set_default(&etm->synth_opts); | 1456 | itrace_synth_opts__set_default(&etm->synth_opts, |
1457 | session->itrace_synth_opts->default_no_sample); | ||
1436 | etm->synth_opts.callchain = false; | 1458 | etm->synth_opts.callchain = false; |
1437 | } | 1459 | } |
1438 | 1460 | ||
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index abd38abf1d91..2a36fab76994 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c | |||
@@ -182,20 +182,20 @@ err_put_field: | |||
182 | } | 182 | } |
183 | 183 | ||
184 | static struct bt_ctf_field_type* | 184 | static struct bt_ctf_field_type* |
185 | get_tracepoint_field_type(struct ctf_writer *cw, struct format_field *field) | 185 | get_tracepoint_field_type(struct ctf_writer *cw, struct tep_format_field *field) |
186 | { | 186 | { |
187 | unsigned long flags = field->flags; | 187 | unsigned long flags = field->flags; |
188 | 188 | ||
189 | if (flags & FIELD_IS_STRING) | 189 | if (flags & TEP_FIELD_IS_STRING) |
190 | return cw->data.string; | 190 | return cw->data.string; |
191 | 191 | ||
192 | if (!(flags & FIELD_IS_SIGNED)) { | 192 | if (!(flags & TEP_FIELD_IS_SIGNED)) { |
193 | /* unsigned long are mostly pointers */ | 193 | /* unsigned long are mostly pointers */ |
194 | if (flags & FIELD_IS_LONG || flags & FIELD_IS_POINTER) | 194 | if (flags & TEP_FIELD_IS_LONG || flags & TEP_FIELD_IS_POINTER) |
195 | return cw->data.u64_hex; | 195 | return cw->data.u64_hex; |
196 | } | 196 | } |
197 | 197 | ||
198 | if (flags & FIELD_IS_SIGNED) { | 198 | if (flags & TEP_FIELD_IS_SIGNED) { |
199 | if (field->size == 8) | 199 | if (field->size == 8) |
200 | return cw->data.s64; | 200 | return cw->data.s64; |
201 | else | 201 | else |
@@ -287,7 +287,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
287 | struct bt_ctf_event_class *event_class, | 287 | struct bt_ctf_event_class *event_class, |
288 | struct bt_ctf_event *event, | 288 | struct bt_ctf_event *event, |
289 | struct perf_sample *sample, | 289 | struct perf_sample *sample, |
290 | struct format_field *fmtf) | 290 | struct tep_format_field *fmtf) |
291 | { | 291 | { |
292 | struct bt_ctf_field_type *type; | 292 | struct bt_ctf_field_type *type; |
293 | struct bt_ctf_field *array_field; | 293 | struct bt_ctf_field *array_field; |
@@ -304,10 +304,10 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
304 | name = fmtf->alias; | 304 | name = fmtf->alias; |
305 | offset = fmtf->offset; | 305 | offset = fmtf->offset; |
306 | len = fmtf->size; | 306 | len = fmtf->size; |
307 | if (flags & FIELD_IS_STRING) | 307 | if (flags & TEP_FIELD_IS_STRING) |
308 | flags &= ~FIELD_IS_ARRAY; | 308 | flags &= ~TEP_FIELD_IS_ARRAY; |
309 | 309 | ||
310 | if (flags & FIELD_IS_DYNAMIC) { | 310 | if (flags & TEP_FIELD_IS_DYNAMIC) { |
311 | unsigned long long tmp_val; | 311 | unsigned long long tmp_val; |
312 | 312 | ||
313 | tmp_val = tep_read_number(fmtf->event->pevent, | 313 | tmp_val = tep_read_number(fmtf->event->pevent, |
@@ -317,7 +317,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
317 | offset &= 0xffff; | 317 | offset &= 0xffff; |
318 | } | 318 | } |
319 | 319 | ||
320 | if (flags & FIELD_IS_ARRAY) { | 320 | if (flags & TEP_FIELD_IS_ARRAY) { |
321 | 321 | ||
322 | type = bt_ctf_event_class_get_field_by_name( | 322 | type = bt_ctf_event_class_get_field_by_name( |
323 | event_class, name); | 323 | event_class, name); |
@@ -338,7 +338,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
338 | type = get_tracepoint_field_type(cw, fmtf); | 338 | type = get_tracepoint_field_type(cw, fmtf); |
339 | 339 | ||
340 | for (i = 0; i < n_items; i++) { | 340 | for (i = 0; i < n_items; i++) { |
341 | if (flags & FIELD_IS_ARRAY) | 341 | if (flags & TEP_FIELD_IS_ARRAY) |
342 | field = bt_ctf_field_array_get_field(array_field, i); | 342 | field = bt_ctf_field_array_get_field(array_field, i); |
343 | else | 343 | else |
344 | field = bt_ctf_field_create(type); | 344 | field = bt_ctf_field_create(type); |
@@ -348,7 +348,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
348 | return -1; | 348 | return -1; |
349 | } | 349 | } |
350 | 350 | ||
351 | if (flags & FIELD_IS_STRING) | 351 | if (flags & TEP_FIELD_IS_STRING) |
352 | ret = string_set_value(field, data + offset + i * len); | 352 | ret = string_set_value(field, data + offset + i * len); |
353 | else { | 353 | else { |
354 | unsigned long long value_int; | 354 | unsigned long long value_int; |
@@ -357,7 +357,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
357 | fmtf->event->pevent, | 357 | fmtf->event->pevent, |
358 | data + offset + i * len, len); | 358 | data + offset + i * len, len); |
359 | 359 | ||
360 | if (!(flags & FIELD_IS_SIGNED)) | 360 | if (!(flags & TEP_FIELD_IS_SIGNED)) |
361 | ret = bt_ctf_field_unsigned_integer_set_value( | 361 | ret = bt_ctf_field_unsigned_integer_set_value( |
362 | field, value_int); | 362 | field, value_int); |
363 | else | 363 | else |
@@ -369,7 +369,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
369 | pr_err("failed to set file value %s\n", name); | 369 | pr_err("failed to set file value %s\n", name); |
370 | goto err_put_field; | 370 | goto err_put_field; |
371 | } | 371 | } |
372 | if (!(flags & FIELD_IS_ARRAY)) { | 372 | if (!(flags & TEP_FIELD_IS_ARRAY)) { |
373 | ret = bt_ctf_event_set_payload(event, name, field); | 373 | ret = bt_ctf_event_set_payload(event, name, field); |
374 | if (ret) { | 374 | if (ret) { |
375 | pr_err("failed to set payload %s\n", name); | 375 | pr_err("failed to set payload %s\n", name); |
@@ -378,7 +378,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
378 | } | 378 | } |
379 | bt_ctf_field_put(field); | 379 | bt_ctf_field_put(field); |
380 | } | 380 | } |
381 | if (flags & FIELD_IS_ARRAY) { | 381 | if (flags & TEP_FIELD_IS_ARRAY) { |
382 | ret = bt_ctf_event_set_payload(event, name, array_field); | 382 | ret = bt_ctf_event_set_payload(event, name, array_field); |
383 | if (ret) { | 383 | if (ret) { |
384 | pr_err("Failed add payload array %s\n", name); | 384 | pr_err("Failed add payload array %s\n", name); |
@@ -396,10 +396,10 @@ err_put_field: | |||
396 | static int add_tracepoint_fields_values(struct ctf_writer *cw, | 396 | static int add_tracepoint_fields_values(struct ctf_writer *cw, |
397 | struct bt_ctf_event_class *event_class, | 397 | struct bt_ctf_event_class *event_class, |
398 | struct bt_ctf_event *event, | 398 | struct bt_ctf_event *event, |
399 | struct format_field *fields, | 399 | struct tep_format_field *fields, |
400 | struct perf_sample *sample) | 400 | struct perf_sample *sample) |
401 | { | 401 | { |
402 | struct format_field *field; | 402 | struct tep_format_field *field; |
403 | int ret; | 403 | int ret; |
404 | 404 | ||
405 | for (field = fields; field; field = field->next) { | 405 | for (field = fields; field; field = field->next) { |
@@ -417,8 +417,8 @@ static int add_tracepoint_values(struct ctf_writer *cw, | |||
417 | struct perf_evsel *evsel, | 417 | struct perf_evsel *evsel, |
418 | struct perf_sample *sample) | 418 | struct perf_sample *sample) |
419 | { | 419 | { |
420 | struct format_field *common_fields = evsel->tp_format->format.common_fields; | 420 | struct tep_format_field *common_fields = evsel->tp_format->format.common_fields; |
421 | struct format_field *fields = evsel->tp_format->format.fields; | 421 | struct tep_format_field *fields = evsel->tp_format->format.fields; |
422 | int ret; | 422 | int ret; |
423 | 423 | ||
424 | ret = add_tracepoint_fields_values(cw, event_class, event, | 424 | ret = add_tracepoint_fields_values(cw, event_class, event, |
@@ -970,7 +970,7 @@ out: | |||
970 | 970 | ||
971 | static int event_class_add_field(struct bt_ctf_event_class *event_class, | 971 | static int event_class_add_field(struct bt_ctf_event_class *event_class, |
972 | struct bt_ctf_field_type *type, | 972 | struct bt_ctf_field_type *type, |
973 | struct format_field *field) | 973 | struct tep_format_field *field) |
974 | { | 974 | { |
975 | struct bt_ctf_field_type *t = NULL; | 975 | struct bt_ctf_field_type *t = NULL; |
976 | char *name; | 976 | char *name; |
@@ -1009,10 +1009,10 @@ static int event_class_add_field(struct bt_ctf_event_class *event_class, | |||
1009 | } | 1009 | } |
1010 | 1010 | ||
1011 | static int add_tracepoint_fields_types(struct ctf_writer *cw, | 1011 | static int add_tracepoint_fields_types(struct ctf_writer *cw, |
1012 | struct format_field *fields, | 1012 | struct tep_format_field *fields, |
1013 | struct bt_ctf_event_class *event_class) | 1013 | struct bt_ctf_event_class *event_class) |
1014 | { | 1014 | { |
1015 | struct format_field *field; | 1015 | struct tep_format_field *field; |
1016 | int ret; | 1016 | int ret; |
1017 | 1017 | ||
1018 | for (field = fields; field; field = field->next) { | 1018 | for (field = fields; field; field = field->next) { |
@@ -1030,15 +1030,15 @@ static int add_tracepoint_fields_types(struct ctf_writer *cw, | |||
1030 | * type and don't care that it is an array. What we don't | 1030 | * type and don't care that it is an array. What we don't |
1031 | * support is an array of strings. | 1031 | * support is an array of strings. |
1032 | */ | 1032 | */ |
1033 | if (flags & FIELD_IS_STRING) | 1033 | if (flags & TEP_FIELD_IS_STRING) |
1034 | flags &= ~FIELD_IS_ARRAY; | 1034 | flags &= ~TEP_FIELD_IS_ARRAY; |
1035 | 1035 | ||
1036 | if (flags & FIELD_IS_ARRAY) | 1036 | if (flags & TEP_FIELD_IS_ARRAY) |
1037 | type = bt_ctf_field_type_array_create(type, field->arraylen); | 1037 | type = bt_ctf_field_type_array_create(type, field->arraylen); |
1038 | 1038 | ||
1039 | ret = event_class_add_field(event_class, type, field); | 1039 | ret = event_class_add_field(event_class, type, field); |
1040 | 1040 | ||
1041 | if (flags & FIELD_IS_ARRAY) | 1041 | if (flags & TEP_FIELD_IS_ARRAY) |
1042 | bt_ctf_field_type_put(type); | 1042 | bt_ctf_field_type_put(type); |
1043 | 1043 | ||
1044 | if (ret) { | 1044 | if (ret) { |
@@ -1055,8 +1055,8 @@ static int add_tracepoint_types(struct ctf_writer *cw, | |||
1055 | struct perf_evsel *evsel, | 1055 | struct perf_evsel *evsel, |
1056 | struct bt_ctf_event_class *class) | 1056 | struct bt_ctf_event_class *class) |
1057 | { | 1057 | { |
1058 | struct format_field *common_fields = evsel->tp_format->format.common_fields; | 1058 | struct tep_format_field *common_fields = evsel->tp_format->format.common_fields; |
1059 | struct format_field *fields = evsel->tp_format->format.fields; | 1059 | struct tep_format_field *fields = evsel->tp_format->format.fields; |
1060 | int ret; | 1060 | int ret; |
1061 | 1061 | ||
1062 | ret = add_tracepoint_fields_types(cw, common_fields, class); | 1062 | ret = add_tracepoint_fields_types(cw, common_fields, class); |
@@ -1578,7 +1578,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, | |||
1578 | { | 1578 | { |
1579 | struct perf_session *session; | 1579 | struct perf_session *session; |
1580 | struct perf_data data = { | 1580 | struct perf_data data = { |
1581 | .file.path = input, | 1581 | .file = { .path = input, .fd = -1 }, |
1582 | .mode = PERF_DATA_MODE_READ, | 1582 | .mode = PERF_DATA_MODE_READ, |
1583 | .force = opts->force, | 1583 | .force = opts->force, |
1584 | }; | 1584 | }; |
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 7123746edcf4..69fbb0a72d0c 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c | |||
@@ -463,6 +463,28 @@ int db_export__branch_types(struct db_export *dbe) | |||
463 | if (err) | 463 | if (err) |
464 | break; | 464 | break; |
465 | } | 465 | } |
466 | |||
467 | /* Add trace begin / end variants */ | ||
468 | for (i = 0; branch_types[i].name ; i++) { | ||
469 | const char *name = branch_types[i].name; | ||
470 | u32 type = branch_types[i].branch_type; | ||
471 | char buf[64]; | ||
472 | |||
473 | if (type == PERF_IP_FLAG_BRANCH || | ||
474 | (type & (PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END))) | ||
475 | continue; | ||
476 | |||
477 | snprintf(buf, sizeof(buf), "trace begin / %s", name); | ||
478 | err = db_export__branch_type(dbe, type | PERF_IP_FLAG_TRACE_BEGIN, buf); | ||
479 | if (err) | ||
480 | break; | ||
481 | |||
482 | snprintf(buf, sizeof(buf), "%s / trace end", name); | ||
483 | err = db_export__branch_type(dbe, type | PERF_IP_FLAG_TRACE_END, buf); | ||
484 | if (err) | ||
485 | break; | ||
486 | } | ||
487 | |||
466 | return err; | 488 | return err; |
467 | } | 489 | } |
468 | 490 | ||
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 1f3ccc368530..d01b8355f4ca 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h | |||
@@ -63,6 +63,7 @@ struct perf_env { | |||
63 | struct numa_node *numa_nodes; | 63 | struct numa_node *numa_nodes; |
64 | struct memory_node *memory_nodes; | 64 | struct memory_node *memory_nodes; |
65 | unsigned long long memory_bsize; | 65 | unsigned long long memory_bsize; |
66 | u64 clockid_res_ns; | ||
66 | }; | 67 | }; |
67 | 68 | ||
68 | extern struct perf_env perf_env; | 69 | extern struct perf_env perf_env; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 0cd42150f712..e9c108a6b1c3 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -308,6 +308,7 @@ static int perf_event__synthesize_fork(struct perf_tool *tool, | |||
308 | event->fork.pid = tgid; | 308 | event->fork.pid = tgid; |
309 | event->fork.tid = pid; | 309 | event->fork.tid = pid; |
310 | event->fork.header.type = PERF_RECORD_FORK; | 310 | event->fork.header.type = PERF_RECORD_FORK; |
311 | event->fork.header.misc = PERF_RECORD_MISC_FORK_EXEC; | ||
311 | 312 | ||
312 | event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); | 313 | event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); |
313 | 314 | ||
@@ -1081,6 +1082,7 @@ void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max | |||
1081 | } | 1082 | } |
1082 | 1083 | ||
1083 | *size += sizeof(struct cpu_map_data); | 1084 | *size += sizeof(struct cpu_map_data); |
1085 | *size = PERF_ALIGN(*size, sizeof(u64)); | ||
1084 | return zalloc(*size); | 1086 | return zalloc(*size); |
1085 | } | 1087 | } |
1086 | 1088 | ||
@@ -1560,26 +1562,9 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, | |||
1560 | 1562 | ||
1561 | return NULL; | 1563 | return NULL; |
1562 | } | 1564 | } |
1563 | try_again: | 1565 | |
1564 | al->map = map_groups__find(mg, al->addr); | 1566 | al->map = map_groups__find(mg, al->addr); |
1565 | if (al->map == NULL) { | 1567 | if (al->map != NULL) { |
1566 | /* | ||
1567 | * If this is outside of all known maps, and is a negative | ||
1568 | * address, try to look it up in the kernel dso, as it might be | ||
1569 | * a vsyscall or vdso (which executes in user-mode). | ||
1570 | * | ||
1571 | * XXX This is nasty, we should have a symbol list in the | ||
1572 | * "[vdso]" dso, but for now lets use the old trick of looking | ||
1573 | * in the whole kernel symbol list. | ||
1574 | */ | ||
1575 | if (cpumode == PERF_RECORD_MISC_USER && machine && | ||
1576 | mg != &machine->kmaps && | ||
1577 | machine__kernel_ip(machine, al->addr)) { | ||
1578 | mg = &machine->kmaps; | ||
1579 | load_map = true; | ||
1580 | goto try_again; | ||
1581 | } | ||
1582 | } else { | ||
1583 | /* | 1568 | /* |
1584 | * Kernel maps might be changed when loading symbols so loading | 1569 | * Kernel maps might be changed when loading symbols so loading |
1585 | * must be done prior to using kernel maps. | 1570 | * must be done prior to using kernel maps. |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index be440df29615..668d2a9ef0f4 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -358,7 +358,7 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
358 | struct perf_evsel *pos; | 358 | struct perf_evsel *pos; |
359 | 359 | ||
360 | evlist__for_each_entry(evlist, pos) { | 360 | evlist__for_each_entry(evlist, pos) { |
361 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 361 | if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->fd) |
362 | continue; | 362 | continue; |
363 | perf_evsel__disable(pos); | 363 | perf_evsel__disable(pos); |
364 | } | 364 | } |
@@ -1810,3 +1810,30 @@ void perf_evlist__force_leader(struct perf_evlist *evlist) | |||
1810 | leader->forced_leader = true; | 1810 | leader->forced_leader = true; |
1811 | } | 1811 | } |
1812 | } | 1812 | } |
1813 | |||
1814 | struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list, | ||
1815 | struct perf_evsel *evsel) | ||
1816 | { | ||
1817 | struct perf_evsel *c2, *leader; | ||
1818 | bool is_open = true; | ||
1819 | |||
1820 | leader = evsel->leader; | ||
1821 | pr_debug("Weak group for %s/%d failed\n", | ||
1822 | leader->name, leader->nr_members); | ||
1823 | |||
1824 | /* | ||
1825 | * for_each_group_member doesn't work here because it doesn't | ||
1826 | * include the first entry. | ||
1827 | */ | ||
1828 | evlist__for_each_entry(evsel_list, c2) { | ||
1829 | if (c2 == evsel) | ||
1830 | is_open = false; | ||
1831 | if (c2->leader == leader) { | ||
1832 | if (is_open) | ||
1833 | perf_evsel__close(c2); | ||
1834 | c2->leader = c2; | ||
1835 | c2->nr_members = 0; | ||
1836 | } | ||
1837 | } | ||
1838 | return leader; | ||
1839 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index dc66436add98..9919eed6d15b 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -312,4 +312,7 @@ bool perf_evlist__exclude_kernel(struct perf_evlist *evlist); | |||
312 | 312 | ||
313 | void perf_evlist__force_leader(struct perf_evlist *evlist); | 313 | void perf_evlist__force_leader(struct perf_evlist *evlist); |
314 | 314 | ||
315 | struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evlist, | ||
316 | struct perf_evsel *evsel); | ||
317 | |||
315 | #endif /* __PERF_EVLIST_H */ | 318 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1a61628a1c12..d37bb1566cd9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -232,6 +232,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
232 | evsel->leader = evsel; | 232 | evsel->leader = evsel; |
233 | evsel->unit = ""; | 233 | evsel->unit = ""; |
234 | evsel->scale = 1.0; | 234 | evsel->scale = 1.0; |
235 | evsel->max_events = ULONG_MAX; | ||
235 | evsel->evlist = NULL; | 236 | evsel->evlist = NULL; |
236 | evsel->bpf_fd = -1; | 237 | evsel->bpf_fd = -1; |
237 | INIT_LIST_HEAD(&evsel->node); | 238 | INIT_LIST_HEAD(&evsel->node); |
@@ -793,6 +794,9 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
793 | case PERF_EVSEL__CONFIG_TERM_MAX_STACK: | 794 | case PERF_EVSEL__CONFIG_TERM_MAX_STACK: |
794 | max_stack = term->val.max_stack; | 795 | max_stack = term->val.max_stack; |
795 | break; | 796 | break; |
797 | case PERF_EVSEL__CONFIG_TERM_MAX_EVENTS: | ||
798 | evsel->max_events = term->val.max_events; | ||
799 | break; | ||
796 | case PERF_EVSEL__CONFIG_TERM_INHERIT: | 800 | case PERF_EVSEL__CONFIG_TERM_INHERIT: |
797 | /* | 801 | /* |
798 | * attr->inherit should has already been set by | 802 | * attr->inherit should has already been set by |
@@ -952,7 +956,6 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
952 | attr->sample_freq = 0; | 956 | attr->sample_freq = 0; |
953 | attr->sample_period = 0; | 957 | attr->sample_period = 0; |
954 | attr->write_backward = 0; | 958 | attr->write_backward = 0; |
955 | attr->sample_id_all = 0; | ||
956 | } | 959 | } |
957 | 960 | ||
958 | if (opts->no_samples) | 961 | if (opts->no_samples) |
@@ -1089,6 +1092,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
1089 | attr->exclude_user = 1; | 1092 | attr->exclude_user = 1; |
1090 | } | 1093 | } |
1091 | 1094 | ||
1095 | if (evsel->own_cpus) | ||
1096 | evsel->attr.read_format |= PERF_FORMAT_ID; | ||
1097 | |||
1092 | /* | 1098 | /* |
1093 | * Apply event specific term settings, | 1099 | * Apply event specific term settings, |
1094 | * it overloads any global configuration. | 1100 | * it overloads any global configuration. |
@@ -1200,16 +1206,27 @@ int perf_evsel__append_addr_filter(struct perf_evsel *evsel, const char *filter) | |||
1200 | 1206 | ||
1201 | int perf_evsel__enable(struct perf_evsel *evsel) | 1207 | int perf_evsel__enable(struct perf_evsel *evsel) |
1202 | { | 1208 | { |
1203 | return perf_evsel__run_ioctl(evsel, | 1209 | int err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, 0); |
1204 | PERF_EVENT_IOC_ENABLE, | 1210 | |
1205 | 0); | 1211 | if (!err) |
1212 | evsel->disabled = false; | ||
1213 | |||
1214 | return err; | ||
1206 | } | 1215 | } |
1207 | 1216 | ||
1208 | int perf_evsel__disable(struct perf_evsel *evsel) | 1217 | int perf_evsel__disable(struct perf_evsel *evsel) |
1209 | { | 1218 | { |
1210 | return perf_evsel__run_ioctl(evsel, | 1219 | int err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, 0); |
1211 | PERF_EVENT_IOC_DISABLE, | 1220 | /* |
1212 | 0); | 1221 | * We mark it disabled here so that tools that disable a event can |
1222 | * ignore events after they disable it. I.e. the ring buffer may have | ||
1223 | * already a few more events queued up before the kernel got the stop | ||
1224 | * request. | ||
1225 | */ | ||
1226 | if (!err) | ||
1227 | evsel->disabled = true; | ||
1228 | |||
1229 | return err; | ||
1213 | } | 1230 | } |
1214 | 1231 | ||
1215 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 1232 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
@@ -2682,7 +2699,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
2682 | return 0; | 2699 | return 0; |
2683 | } | 2700 | } |
2684 | 2701 | ||
2685 | struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name) | 2702 | struct tep_format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name) |
2686 | { | 2703 | { |
2687 | return tep_find_field(evsel->tp_format, name); | 2704 | return tep_find_field(evsel->tp_format, name); |
2688 | } | 2705 | } |
@@ -2690,7 +2707,7 @@ struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *nam | |||
2690 | void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | 2707 | void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, |
2691 | const char *name) | 2708 | const char *name) |
2692 | { | 2709 | { |
2693 | struct format_field *field = perf_evsel__field(evsel, name); | 2710 | struct tep_format_field *field = perf_evsel__field(evsel, name); |
2694 | int offset; | 2711 | int offset; |
2695 | 2712 | ||
2696 | if (!field) | 2713 | if (!field) |
@@ -2698,7 +2715,7 @@ void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | |||
2698 | 2715 | ||
2699 | offset = field->offset; | 2716 | offset = field->offset; |
2700 | 2717 | ||
2701 | if (field->flags & FIELD_IS_DYNAMIC) { | 2718 | if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
2702 | offset = *(int *)(sample->raw_data + field->offset); | 2719 | offset = *(int *)(sample->raw_data + field->offset); |
2703 | offset &= 0xffff; | 2720 | offset &= 0xffff; |
2704 | } | 2721 | } |
@@ -2706,7 +2723,7 @@ void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | |||
2706 | return sample->raw_data + offset; | 2723 | return sample->raw_data + offset; |
2707 | } | 2724 | } |
2708 | 2725 | ||
2709 | u64 format_field__intval(struct format_field *field, struct perf_sample *sample, | 2726 | u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, |
2710 | bool needs_swap) | 2727 | bool needs_swap) |
2711 | { | 2728 | { |
2712 | u64 value; | 2729 | u64 value; |
@@ -2748,7 +2765,7 @@ u64 format_field__intval(struct format_field *field, struct perf_sample *sample, | |||
2748 | u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | 2765 | u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, |
2749 | const char *name) | 2766 | const char *name) |
2750 | { | 2767 | { |
2751 | struct format_field *field = perf_evsel__field(evsel, name); | 2768 | struct tep_format_field *field = perf_evsel__field(evsel, name); |
2752 | 2769 | ||
2753 | if (!field) | 2770 | if (!field) |
2754 | return 0; | 2771 | return 0; |
@@ -2940,3 +2957,32 @@ struct perf_env *perf_evsel__env(struct perf_evsel *evsel) | |||
2940 | return evsel->evlist->env; | 2957 | return evsel->evlist->env; |
2941 | return NULL; | 2958 | return NULL; |
2942 | } | 2959 | } |
2960 | |||
2961 | static int store_evsel_ids(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
2962 | { | ||
2963 | int cpu, thread; | ||
2964 | |||
2965 | for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { | ||
2966 | for (thread = 0; thread < xyarray__max_y(evsel->fd); | ||
2967 | thread++) { | ||
2968 | int fd = FD(evsel, cpu, thread); | ||
2969 | |||
2970 | if (perf_evlist__id_add_fd(evlist, evsel, | ||
2971 | cpu, thread, fd) < 0) | ||
2972 | return -1; | ||
2973 | } | ||
2974 | } | ||
2975 | |||
2976 | return 0; | ||
2977 | } | ||
2978 | |||
2979 | int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
2980 | { | ||
2981 | struct cpu_map *cpus = evsel->cpus; | ||
2982 | struct thread_map *threads = evsel->threads; | ||
2983 | |||
2984 | if (perf_evsel__alloc_id(evsel, cpus->nr, threads->nr)) | ||
2985 | return -ENOMEM; | ||
2986 | |||
2987 | return store_evsel_ids(evsel, evlist); | ||
2988 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 163c960614d3..3147ca76c6fc 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -46,6 +46,7 @@ enum term_type { | |||
46 | PERF_EVSEL__CONFIG_TERM_STACK_USER, | 46 | PERF_EVSEL__CONFIG_TERM_STACK_USER, |
47 | PERF_EVSEL__CONFIG_TERM_INHERIT, | 47 | PERF_EVSEL__CONFIG_TERM_INHERIT, |
48 | PERF_EVSEL__CONFIG_TERM_MAX_STACK, | 48 | PERF_EVSEL__CONFIG_TERM_MAX_STACK, |
49 | PERF_EVSEL__CONFIG_TERM_MAX_EVENTS, | ||
49 | PERF_EVSEL__CONFIG_TERM_OVERWRITE, | 50 | PERF_EVSEL__CONFIG_TERM_OVERWRITE, |
50 | PERF_EVSEL__CONFIG_TERM_DRV_CFG, | 51 | PERF_EVSEL__CONFIG_TERM_DRV_CFG, |
51 | PERF_EVSEL__CONFIG_TERM_BRANCH, | 52 | PERF_EVSEL__CONFIG_TERM_BRANCH, |
@@ -65,6 +66,7 @@ struct perf_evsel_config_term { | |||
65 | bool inherit; | 66 | bool inherit; |
66 | bool overwrite; | 67 | bool overwrite; |
67 | char *branch; | 68 | char *branch; |
69 | unsigned long max_events; | ||
68 | } val; | 70 | } val; |
69 | bool weak; | 71 | bool weak; |
70 | }; | 72 | }; |
@@ -99,10 +101,12 @@ struct perf_evsel { | |||
99 | struct perf_counts *prev_raw_counts; | 101 | struct perf_counts *prev_raw_counts; |
100 | int idx; | 102 | int idx; |
101 | u32 ids; | 103 | u32 ids; |
104 | unsigned long max_events; | ||
105 | unsigned long nr_events_printed; | ||
102 | char *name; | 106 | char *name; |
103 | double scale; | 107 | double scale; |
104 | const char *unit; | 108 | const char *unit; |
105 | struct event_format *tp_format; | 109 | struct tep_event_format *tp_format; |
106 | off_t id_offset; | 110 | off_t id_offset; |
107 | struct perf_stat_evsel *stats; | 111 | struct perf_stat_evsel *stats; |
108 | void *priv; | 112 | void *priv; |
@@ -119,6 +123,7 @@ struct perf_evsel { | |||
119 | bool snapshot; | 123 | bool snapshot; |
120 | bool supported; | 124 | bool supported; |
121 | bool needs_swap; | 125 | bool needs_swap; |
126 | bool disabled; | ||
122 | bool no_aux_samples; | 127 | bool no_aux_samples; |
123 | bool immediate; | 128 | bool immediate; |
124 | bool system_wide; | 129 | bool system_wide; |
@@ -211,7 +216,7 @@ static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char * | |||
211 | 216 | ||
212 | struct perf_evsel *perf_evsel__new_cycles(bool precise); | 217 | struct perf_evsel *perf_evsel__new_cycles(bool precise); |
213 | 218 | ||
214 | struct event_format *event_format__new(const char *sys, const char *name); | 219 | struct tep_event_format *event_format__new(const char *sys, const char *name); |
215 | 220 | ||
216 | void perf_evsel__init(struct perf_evsel *evsel, | 221 | void perf_evsel__init(struct perf_evsel *evsel, |
217 | struct perf_event_attr *attr, int idx); | 222 | struct perf_event_attr *attr, int idx); |
@@ -296,11 +301,11 @@ static inline char *perf_evsel__strval(struct perf_evsel *evsel, | |||
296 | return perf_evsel__rawptr(evsel, sample, name); | 301 | return perf_evsel__rawptr(evsel, sample, name); |
297 | } | 302 | } |
298 | 303 | ||
299 | struct format_field; | 304 | struct tep_format_field; |
300 | 305 | ||
301 | u64 format_field__intval(struct format_field *field, struct perf_sample *sample, bool needs_swap); | 306 | u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, bool needs_swap); |
302 | 307 | ||
303 | struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name); | 308 | struct tep_format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name); |
304 | 309 | ||
305 | #define perf_evsel__match(evsel, t, c) \ | 310 | #define perf_evsel__match(evsel, t, c) \ |
306 | (evsel->attr.type == PERF_TYPE_##t && \ | 311 | (evsel->attr.type == PERF_TYPE_##t && \ |
@@ -481,4 +486,5 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, | |||
481 | 486 | ||
482 | struct perf_env *perf_evsel__env(struct perf_evsel *evsel); | 487 | struct perf_env *perf_evsel__env(struct perf_evsel *evsel); |
483 | 488 | ||
489 | int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist); | ||
484 | #endif /* __PERF_EVSEL_H */ | 490 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 06dfb027879d..0d0a4c6f368b 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -73,7 +73,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
73 | } | 73 | } |
74 | 74 | ||
75 | if (details->trace_fields) { | 75 | if (details->trace_fields) { |
76 | struct format_field *field; | 76 | struct tep_format_field *field; |
77 | 77 | ||
78 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { | 78 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { |
79 | printed += comma_fprintf(fp, &first, " (not a tracepoint)"); | 79 | printed += comma_fprintf(fp, &first, " (not a tracepoint)"); |
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index de322d51c7fe..b72440bf9a79 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h | |||
@@ -29,6 +29,12 @@ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_ent | |||
29 | #elif defined(__powerpc__) | 29 | #elif defined(__powerpc__) |
30 | #define GEN_ELF_ARCH EM_PPC | 30 | #define GEN_ELF_ARCH EM_PPC |
31 | #define GEN_ELF_CLASS ELFCLASS32 | 31 | #define GEN_ELF_CLASS ELFCLASS32 |
32 | #elif defined(__sparc__) && defined(__arch64__) | ||
33 | #define GEN_ELF_ARCH EM_SPARCV9 | ||
34 | #define GEN_ELF_CLASS ELFCLASS64 | ||
35 | #elif defined(__sparc__) | ||
36 | #define GEN_ELF_ARCH EM_SPARC | ||
37 | #define GEN_ELF_CLASS ELFCLASS32 | ||
32 | #else | 38 | #else |
33 | #error "unsupported architecture" | 39 | #error "unsupported architecture" |
34 | #endif | 40 | #endif |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3cadc252dd89..4fd45be95a43 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1034,6 +1034,13 @@ static int write_auxtrace(struct feat_fd *ff, | |||
1034 | return err; | 1034 | return err; |
1035 | } | 1035 | } |
1036 | 1036 | ||
1037 | static int write_clockid(struct feat_fd *ff, | ||
1038 | struct perf_evlist *evlist __maybe_unused) | ||
1039 | { | ||
1040 | return do_write(ff, &ff->ph->env.clockid_res_ns, | ||
1041 | sizeof(ff->ph->env.clockid_res_ns)); | ||
1042 | } | ||
1043 | |||
1037 | static int cpu_cache_level__sort(const void *a, const void *b) | 1044 | static int cpu_cache_level__sort(const void *a, const void *b) |
1038 | { | 1045 | { |
1039 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; | 1046 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; |
@@ -1508,6 +1515,12 @@ static void print_cpu_topology(struct feat_fd *ff, FILE *fp) | |||
1508 | fprintf(fp, "# Core ID and Socket ID information is not available\n"); | 1515 | fprintf(fp, "# Core ID and Socket ID information is not available\n"); |
1509 | } | 1516 | } |
1510 | 1517 | ||
1518 | static void print_clockid(struct feat_fd *ff, FILE *fp) | ||
1519 | { | ||
1520 | fprintf(fp, "# clockid frequency: %"PRIu64" MHz\n", | ||
1521 | ff->ph->env.clockid_res_ns * 1000); | ||
1522 | } | ||
1523 | |||
1511 | static void free_event_desc(struct perf_evsel *events) | 1524 | static void free_event_desc(struct perf_evsel *events) |
1512 | { | 1525 | { |
1513 | struct perf_evsel *evsel; | 1526 | struct perf_evsel *evsel; |
@@ -2531,6 +2544,15 @@ out: | |||
2531 | return ret; | 2544 | return ret; |
2532 | } | 2545 | } |
2533 | 2546 | ||
2547 | static int process_clockid(struct feat_fd *ff, | ||
2548 | void *data __maybe_unused) | ||
2549 | { | ||
2550 | if (do_read_u64(ff, &ff->ph->env.clockid_res_ns)) | ||
2551 | return -1; | ||
2552 | |||
2553 | return 0; | ||
2554 | } | ||
2555 | |||
2534 | struct feature_ops { | 2556 | struct feature_ops { |
2535 | int (*write)(struct feat_fd *ff, struct perf_evlist *evlist); | 2557 | int (*write)(struct feat_fd *ff, struct perf_evlist *evlist); |
2536 | void (*print)(struct feat_fd *ff, FILE *fp); | 2558 | void (*print)(struct feat_fd *ff, FILE *fp); |
@@ -2590,6 +2612,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
2590 | FEAT_OPN(CACHE, cache, true), | 2612 | FEAT_OPN(CACHE, cache, true), |
2591 | FEAT_OPR(SAMPLE_TIME, sample_time, false), | 2613 | FEAT_OPR(SAMPLE_TIME, sample_time, false), |
2592 | FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), | 2614 | FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), |
2615 | FEAT_OPR(CLOCKID, clockid, false) | ||
2593 | }; | 2616 | }; |
2594 | 2617 | ||
2595 | struct header_print_data { | 2618 | struct header_print_data { |
@@ -3206,7 +3229,7 @@ static int read_attr(int fd, struct perf_header *ph, | |||
3206 | static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, | 3229 | static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, |
3207 | struct tep_handle *pevent) | 3230 | struct tep_handle *pevent) |
3208 | { | 3231 | { |
3209 | struct event_format *event; | 3232 | struct tep_event_format *event; |
3210 | char bf[128]; | 3233 | char bf[128]; |
3211 | 3234 | ||
3212 | /* already prepared */ | 3235 | /* already prepared */ |
@@ -3448,10 +3471,10 @@ int perf_event__synthesize_features(struct perf_tool *tool, | |||
3448 | return ret; | 3471 | return ret; |
3449 | } | 3472 | } |
3450 | 3473 | ||
3451 | int perf_event__process_feature(struct perf_tool *tool, | 3474 | int perf_event__process_feature(struct perf_session *session, |
3452 | union perf_event *event, | 3475 | union perf_event *event) |
3453 | struct perf_session *session __maybe_unused) | ||
3454 | { | 3476 | { |
3477 | struct perf_tool *tool = session->tool; | ||
3455 | struct feat_fd ff = { .fd = 0 }; | 3478 | struct feat_fd ff = { .fd = 0 }; |
3456 | struct feature_event *fe = (struct feature_event *)event; | 3479 | struct feature_event *fe = (struct feature_event *)event; |
3457 | int type = fe->header.type; | 3480 | int type = fe->header.type; |
@@ -3637,13 +3660,13 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) | |||
3637 | } | 3660 | } |
3638 | 3661 | ||
3639 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 3662 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
3640 | struct perf_session *session, | 3663 | struct perf_evlist *evlist, |
3641 | perf_event__handler_t process) | 3664 | perf_event__handler_t process) |
3642 | { | 3665 | { |
3643 | struct perf_evsel *evsel; | 3666 | struct perf_evsel *evsel; |
3644 | int err = 0; | 3667 | int err = 0; |
3645 | 3668 | ||
3646 | evlist__for_each_entry(session->evlist, evsel) { | 3669 | evlist__for_each_entry(evlist, evsel) { |
3647 | err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, | 3670 | err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, |
3648 | evsel->id, process); | 3671 | evsel->id, process); |
3649 | if (err) { | 3672 | if (err) { |
@@ -3856,9 +3879,8 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, | |||
3856 | return aligned_size; | 3879 | return aligned_size; |
3857 | } | 3880 | } |
3858 | 3881 | ||
3859 | int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, | 3882 | int perf_event__process_tracing_data(struct perf_session *session, |
3860 | union perf_event *event, | 3883 | union perf_event *event) |
3861 | struct perf_session *session) | ||
3862 | { | 3884 | { |
3863 | ssize_t size_read, padding, size = event->tracing_data.size; | 3885 | ssize_t size_read, padding, size = event->tracing_data.size; |
3864 | int fd = perf_data__fd(session->data); | 3886 | int fd = perf_data__fd(session->data); |
@@ -3924,9 +3946,8 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, | |||
3924 | return err; | 3946 | return err; |
3925 | } | 3947 | } |
3926 | 3948 | ||
3927 | int perf_event__process_build_id(struct perf_tool *tool __maybe_unused, | 3949 | int perf_event__process_build_id(struct perf_session *session, |
3928 | union perf_event *event, | 3950 | union perf_event *event) |
3929 | struct perf_session *session) | ||
3930 | { | 3951 | { |
3931 | __event_process_build_id(&event->build_id, | 3952 | __event_process_build_id(&event->build_id, |
3932 | event->build_id.filename, | 3953 | event->build_id.filename, |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 6d7fe44aadc0..0d553ddca0a3 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -38,6 +38,7 @@ enum { | |||
38 | HEADER_CACHE, | 38 | HEADER_CACHE, |
39 | HEADER_SAMPLE_TIME, | 39 | HEADER_SAMPLE_TIME, |
40 | HEADER_MEM_TOPOLOGY, | 40 | HEADER_MEM_TOPOLOGY, |
41 | HEADER_CLOCKID, | ||
41 | HEADER_LAST_FEATURE, | 42 | HEADER_LAST_FEATURE, |
42 | HEADER_FEAT_BITS = 256, | 43 | HEADER_FEAT_BITS = 256, |
43 | }; | 44 | }; |
@@ -116,15 +117,14 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool, | |||
116 | perf_event__handler_t process, | 117 | perf_event__handler_t process, |
117 | bool is_pipe); | 118 | bool is_pipe); |
118 | 119 | ||
119 | int perf_event__process_feature(struct perf_tool *tool, | 120 | int perf_event__process_feature(struct perf_session *session, |
120 | union perf_event *event, | 121 | union perf_event *event); |
121 | struct perf_session *session); | ||
122 | 122 | ||
123 | int perf_event__synthesize_attr(struct perf_tool *tool, | 123 | int perf_event__synthesize_attr(struct perf_tool *tool, |
124 | struct perf_event_attr *attr, u32 ids, u64 *id, | 124 | struct perf_event_attr *attr, u32 ids, u64 *id, |
125 | perf_event__handler_t process); | 125 | perf_event__handler_t process); |
126 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 126 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
127 | struct perf_session *session, | 127 | struct perf_evlist *evlist, |
128 | perf_event__handler_t process); | 128 | perf_event__handler_t process); |
129 | int perf_event__synthesize_event_update_unit(struct perf_tool *tool, | 129 | int perf_event__synthesize_event_update_unit(struct perf_tool *tool, |
130 | struct perf_evsel *evsel, | 130 | struct perf_evsel *evsel, |
@@ -148,17 +148,15 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); | |||
148 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, | 148 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, |
149 | int fd, struct perf_evlist *evlist, | 149 | int fd, struct perf_evlist *evlist, |
150 | perf_event__handler_t process); | 150 | perf_event__handler_t process); |
151 | int perf_event__process_tracing_data(struct perf_tool *tool, | 151 | int perf_event__process_tracing_data(struct perf_session *session, |
152 | union perf_event *event, | 152 | union perf_event *event); |
153 | struct perf_session *session); | ||
154 | 153 | ||
155 | int perf_event__synthesize_build_id(struct perf_tool *tool, | 154 | int perf_event__synthesize_build_id(struct perf_tool *tool, |
156 | struct dso *pos, u16 misc, | 155 | struct dso *pos, u16 misc, |
157 | perf_event__handler_t process, | 156 | perf_event__handler_t process, |
158 | struct machine *machine); | 157 | struct machine *machine); |
159 | int perf_event__process_build_id(struct perf_tool *tool, | 158 | int perf_event__process_build_id(struct perf_session *session, |
160 | union perf_event *event, | 159 | union perf_event *event); |
161 | struct perf_session *session); | ||
162 | bool is_perf_magic(u64 magic); | 160 | bool is_perf_magic(u64 magic); |
163 | 161 | ||
164 | #define NAME_ALIGN 64 | 162 | #define NAME_ALIGN 64 |
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 7f0c83b6332b..7b27d77306c2 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c | |||
@@ -269,6 +269,13 @@ static int intel_bts_do_fix_overlap(struct auxtrace_queue *queue, | |||
269 | return 0; | 269 | return 0; |
270 | } | 270 | } |
271 | 271 | ||
272 | static inline u8 intel_bts_cpumode(struct intel_bts *bts, uint64_t ip) | ||
273 | { | ||
274 | return machine__kernel_ip(bts->machine, ip) ? | ||
275 | PERF_RECORD_MISC_KERNEL : | ||
276 | PERF_RECORD_MISC_USER; | ||
277 | } | ||
278 | |||
272 | static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | 279 | static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, |
273 | struct branch *branch) | 280 | struct branch *branch) |
274 | { | 281 | { |
@@ -281,12 +288,8 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | |||
281 | bts->num_events++ <= bts->synth_opts.initial_skip) | 288 | bts->num_events++ <= bts->synth_opts.initial_skip) |
282 | return 0; | 289 | return 0; |
283 | 290 | ||
284 | event.sample.header.type = PERF_RECORD_SAMPLE; | ||
285 | event.sample.header.misc = PERF_RECORD_MISC_USER; | ||
286 | event.sample.header.size = sizeof(struct perf_event_header); | ||
287 | |||
288 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
289 | sample.ip = le64_to_cpu(branch->from); | 291 | sample.ip = le64_to_cpu(branch->from); |
292 | sample.cpumode = intel_bts_cpumode(bts, sample.ip); | ||
290 | sample.pid = btsq->pid; | 293 | sample.pid = btsq->pid; |
291 | sample.tid = btsq->tid; | 294 | sample.tid = btsq->tid; |
292 | sample.addr = le64_to_cpu(branch->to); | 295 | sample.addr = le64_to_cpu(branch->to); |
@@ -298,6 +301,10 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | |||
298 | sample.insn_len = btsq->intel_pt_insn.length; | 301 | sample.insn_len = btsq->intel_pt_insn.length; |
299 | memcpy(sample.insn, btsq->intel_pt_insn.buf, INTEL_PT_INSN_BUF_SZ); | 302 | memcpy(sample.insn, btsq->intel_pt_insn.buf, INTEL_PT_INSN_BUF_SZ); |
300 | 303 | ||
304 | event.sample.header.type = PERF_RECORD_SAMPLE; | ||
305 | event.sample.header.misc = sample.cpumode; | ||
306 | event.sample.header.size = sizeof(struct perf_event_header); | ||
307 | |||
301 | if (bts->synth_opts.inject) { | 308 | if (bts->synth_opts.inject) { |
302 | event.sample.header.size = bts->branches_event_size; | 309 | event.sample.header.size = bts->branches_event_size; |
303 | ret = perf_event__synthesize_sample(&event, | 310 | ret = perf_event__synthesize_sample(&event, |
@@ -910,7 +917,8 @@ int intel_bts_process_auxtrace_info(union perf_event *event, | |||
910 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { | 917 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { |
911 | bts->synth_opts = *session->itrace_synth_opts; | 918 | bts->synth_opts = *session->itrace_synth_opts; |
912 | } else { | 919 | } else { |
913 | itrace_synth_opts__set_default(&bts->synth_opts); | 920 | itrace_synth_opts__set_default(&bts->synth_opts, |
921 | session->itrace_synth_opts->default_no_sample); | ||
914 | if (session->itrace_synth_opts) | 922 | if (session->itrace_synth_opts) |
915 | bts->synth_opts.thread_stack = | 923 | bts->synth_opts.thread_stack = |
916 | session->itrace_synth_opts->thread_stack; | 924 | session->itrace_synth_opts->thread_stack; |
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 d404bed7003a..4503f3ca45ab 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
@@ -1165,7 +1165,7 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) | |||
1165 | decoder->pge = false; | 1165 | decoder->pge = false; |
1166 | decoder->continuous_period = false; | 1166 | decoder->continuous_period = false; |
1167 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | 1167 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; |
1168 | decoder->state.to_ip = 0; | 1168 | decoder->state.type |= INTEL_PT_TRACE_END; |
1169 | return 0; | 1169 | return 0; |
1170 | } | 1170 | } |
1171 | if (err == INTEL_PT_RETURN) | 1171 | if (err == INTEL_PT_RETURN) |
@@ -1179,9 +1179,13 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) | |||
1179 | decoder->continuous_period = false; | 1179 | decoder->continuous_period = false; |
1180 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | 1180 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; |
1181 | decoder->state.from_ip = decoder->ip; | 1181 | decoder->state.from_ip = decoder->ip; |
1182 | decoder->state.to_ip = 0; | 1182 | if (decoder->packet.count == 0) { |
1183 | if (decoder->packet.count != 0) | 1183 | decoder->state.to_ip = 0; |
1184 | } else { | ||
1185 | decoder->state.to_ip = decoder->last_ip; | ||
1184 | decoder->ip = decoder->last_ip; | 1186 | decoder->ip = decoder->last_ip; |
1187 | } | ||
1188 | decoder->state.type |= INTEL_PT_TRACE_END; | ||
1185 | } else { | 1189 | } else { |
1186 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | 1190 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; |
1187 | decoder->state.from_ip = decoder->ip; | 1191 | decoder->state.from_ip = decoder->ip; |
@@ -1208,7 +1212,8 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) | |||
1208 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | 1212 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; |
1209 | decoder->ip = to_ip; | 1213 | decoder->ip = to_ip; |
1210 | decoder->state.from_ip = decoder->ip; | 1214 | decoder->state.from_ip = decoder->ip; |
1211 | decoder->state.to_ip = 0; | 1215 | decoder->state.to_ip = to_ip; |
1216 | decoder->state.type |= INTEL_PT_TRACE_END; | ||
1212 | return 0; | 1217 | return 0; |
1213 | } | 1218 | } |
1214 | intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch", | 1219 | intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch", |
@@ -1469,6 +1474,8 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder) | |||
1469 | decoder->have_calc_cyc_to_tsc = false; | 1474 | decoder->have_calc_cyc_to_tsc = false; |
1470 | intel_pt_calc_cyc_to_tsc(decoder, true); | 1475 | intel_pt_calc_cyc_to_tsc(decoder, true); |
1471 | } | 1476 | } |
1477 | |||
1478 | intel_pt_log_to("Setting timestamp", decoder->timestamp); | ||
1472 | } | 1479 | } |
1473 | 1480 | ||
1474 | static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder) | 1481 | static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder) |
@@ -1509,6 +1516,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder) | |||
1509 | decoder->timestamp = timestamp; | 1516 | decoder->timestamp = timestamp; |
1510 | 1517 | ||
1511 | decoder->timestamp_insn_cnt = 0; | 1518 | decoder->timestamp_insn_cnt = 0; |
1519 | |||
1520 | intel_pt_log_to("Setting timestamp", decoder->timestamp); | ||
1512 | } | 1521 | } |
1513 | 1522 | ||
1514 | /* Walk PSB+ packets when already in sync. */ | 1523 | /* Walk PSB+ packets when already in sync. */ |
@@ -1640,14 +1649,15 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder) | |||
1640 | 1649 | ||
1641 | case INTEL_PT_TIP_PGD: | 1650 | case INTEL_PT_TIP_PGD: |
1642 | decoder->state.from_ip = decoder->ip; | 1651 | decoder->state.from_ip = decoder->ip; |
1643 | decoder->state.to_ip = 0; | 1652 | if (decoder->packet.count == 0) { |
1644 | if (decoder->packet.count != 0) { | 1653 | decoder->state.to_ip = 0; |
1654 | } else { | ||
1645 | intel_pt_set_ip(decoder); | 1655 | intel_pt_set_ip(decoder); |
1646 | intel_pt_log("Omitting PGD ip " x64_fmt "\n", | 1656 | decoder->state.to_ip = decoder->ip; |
1647 | decoder->ip); | ||
1648 | } | 1657 | } |
1649 | decoder->pge = false; | 1658 | decoder->pge = false; |
1650 | decoder->continuous_period = false; | 1659 | decoder->continuous_period = false; |
1660 | decoder->state.type |= INTEL_PT_TRACE_END; | ||
1651 | return 0; | 1661 | return 0; |
1652 | 1662 | ||
1653 | case INTEL_PT_TIP_PGE: | 1663 | case INTEL_PT_TIP_PGE: |
@@ -1661,6 +1671,7 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder) | |||
1661 | intel_pt_set_ip(decoder); | 1671 | intel_pt_set_ip(decoder); |
1662 | decoder->state.to_ip = decoder->ip; | 1672 | decoder->state.to_ip = decoder->ip; |
1663 | } | 1673 | } |
1674 | decoder->state.type |= INTEL_PT_TRACE_BEGIN; | ||
1664 | return 0; | 1675 | return 0; |
1665 | 1676 | ||
1666 | case INTEL_PT_TIP: | 1677 | case INTEL_PT_TIP: |
@@ -1739,6 +1750,7 @@ next: | |||
1739 | intel_pt_set_ip(decoder); | 1750 | intel_pt_set_ip(decoder); |
1740 | decoder->state.from_ip = 0; | 1751 | decoder->state.from_ip = 0; |
1741 | decoder->state.to_ip = decoder->ip; | 1752 | decoder->state.to_ip = decoder->ip; |
1753 | decoder->state.type |= INTEL_PT_TRACE_BEGIN; | ||
1742 | return 0; | 1754 | return 0; |
1743 | } | 1755 | } |
1744 | 1756 | ||
@@ -2077,9 +2089,13 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder) | |||
2077 | decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD; | 2089 | decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD; |
2078 | if (intel_pt_have_ip(decoder)) | 2090 | if (intel_pt_have_ip(decoder)) |
2079 | intel_pt_set_ip(decoder); | 2091 | intel_pt_set_ip(decoder); |
2080 | if (decoder->ip) | 2092 | if (!decoder->ip) |
2081 | return 0; | 2093 | break; |
2082 | break; | 2094 | if (decoder->packet.type == INTEL_PT_TIP_PGE) |
2095 | decoder->state.type |= INTEL_PT_TRACE_BEGIN; | ||
2096 | if (decoder->packet.type == INTEL_PT_TIP_PGD) | ||
2097 | decoder->state.type |= INTEL_PT_TRACE_END; | ||
2098 | return 0; | ||
2083 | 2099 | ||
2084 | case INTEL_PT_FUP: | 2100 | case INTEL_PT_FUP: |
2085 | if (intel_pt_have_ip(decoder)) | 2101 | if (intel_pt_have_ip(decoder)) |
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 51c18d67f4ca..ed088d4726ba 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h | |||
@@ -37,6 +37,8 @@ enum intel_pt_sample_type { | |||
37 | INTEL_PT_EX_STOP = 1 << 6, | 37 | INTEL_PT_EX_STOP = 1 << 6, |
38 | INTEL_PT_PWR_EXIT = 1 << 7, | 38 | INTEL_PT_PWR_EXIT = 1 << 7, |
39 | INTEL_PT_CBR_CHG = 1 << 8, | 39 | INTEL_PT_CBR_CHG = 1 << 8, |
40 | INTEL_PT_TRACE_BEGIN = 1 << 9, | ||
41 | INTEL_PT_TRACE_END = 1 << 10, | ||
40 | }; | 42 | }; |
41 | 43 | ||
42 | enum intel_pt_period_type { | 44 | enum intel_pt_period_type { |
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 e02bc7b166a0..5e64da270f97 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c | |||
@@ -31,6 +31,11 @@ static FILE *f; | |||
31 | static char log_name[MAX_LOG_NAME]; | 31 | static char log_name[MAX_LOG_NAME]; |
32 | bool intel_pt_enable_logging; | 32 | bool intel_pt_enable_logging; |
33 | 33 | ||
34 | void *intel_pt_log_fp(void) | ||
35 | { | ||
36 | return f; | ||
37 | } | ||
38 | |||
34 | void intel_pt_log_enable(void) | 39 | void intel_pt_log_enable(void) |
35 | { | 40 | { |
36 | intel_pt_enable_logging = true; | 41 | intel_pt_enable_logging = true; |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.h b/tools/perf/util/intel-pt-decoder/intel-pt-log.h index 45b64f93f358..cc084937f701 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.h | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | struct intel_pt_pkt; | 23 | struct intel_pt_pkt; |
24 | 24 | ||
25 | void *intel_pt_log_fp(void); | ||
25 | void intel_pt_log_enable(void); | 26 | void intel_pt_log_enable(void); |
26 | void intel_pt_log_disable(void); | 27 | void intel_pt_log_disable(void); |
27 | void intel_pt_log_set_name(const char *name); | 28 | void intel_pt_log_set_name(const char *name); |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index aec68908d604..149ff361ca78 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -206,6 +206,16 @@ static void intel_pt_dump_event(struct intel_pt *pt, unsigned char *buf, | |||
206 | intel_pt_dump(pt, buf, len); | 206 | intel_pt_dump(pt, buf, len); |
207 | } | 207 | } |
208 | 208 | ||
209 | static void intel_pt_log_event(union perf_event *event) | ||
210 | { | ||
211 | FILE *f = intel_pt_log_fp(); | ||
212 | |||
213 | if (!intel_pt_enable_logging || !f) | ||
214 | return; | ||
215 | |||
216 | perf_event__fprintf(event, f); | ||
217 | } | ||
218 | |||
209 | static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a, | 219 | static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a, |
210 | struct auxtrace_buffer *b) | 220 | struct auxtrace_buffer *b) |
211 | { | 221 | { |
@@ -407,6 +417,13 @@ intel_pt_cache_lookup(struct dso *dso, struct machine *machine, u64 offset) | |||
407 | return auxtrace_cache__lookup(dso->auxtrace_cache, offset); | 417 | return auxtrace_cache__lookup(dso->auxtrace_cache, offset); |
408 | } | 418 | } |
409 | 419 | ||
420 | static inline u8 intel_pt_cpumode(struct intel_pt *pt, uint64_t ip) | ||
421 | { | ||
422 | return ip >= pt->kernel_start ? | ||
423 | PERF_RECORD_MISC_KERNEL : | ||
424 | PERF_RECORD_MISC_USER; | ||
425 | } | ||
426 | |||
410 | static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | 427 | static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, |
411 | uint64_t *insn_cnt_ptr, uint64_t *ip, | 428 | uint64_t *insn_cnt_ptr, uint64_t *ip, |
412 | uint64_t to_ip, uint64_t max_insn_cnt, | 429 | uint64_t to_ip, uint64_t max_insn_cnt, |
@@ -429,10 +446,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
429 | if (to_ip && *ip == to_ip) | 446 | if (to_ip && *ip == to_ip) |
430 | goto out_no_cache; | 447 | goto out_no_cache; |
431 | 448 | ||
432 | if (*ip >= ptq->pt->kernel_start) | 449 | cpumode = intel_pt_cpumode(ptq->pt, *ip); |
433 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
434 | else | ||
435 | cpumode = PERF_RECORD_MISC_USER; | ||
436 | 450 | ||
437 | thread = ptq->thread; | 451 | thread = ptq->thread; |
438 | if (!thread) { | 452 | if (!thread) { |
@@ -759,7 +773,8 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, | |||
759 | if (pt->synth_opts.callchain) { | 773 | if (pt->synth_opts.callchain) { |
760 | size_t sz = sizeof(struct ip_callchain); | 774 | size_t sz = sizeof(struct ip_callchain); |
761 | 775 | ||
762 | sz += pt->synth_opts.callchain_sz * sizeof(u64); | 776 | /* Add 1 to callchain_sz for callchain context */ |
777 | sz += (pt->synth_opts.callchain_sz + 1) * sizeof(u64); | ||
763 | ptq->chain = zalloc(sz); | 778 | ptq->chain = zalloc(sz); |
764 | if (!ptq->chain) | 779 | if (!ptq->chain) |
765 | goto out_free; | 780 | goto out_free; |
@@ -908,6 +923,11 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq) | |||
908 | ptq->insn_len = ptq->state->insn_len; | 923 | ptq->insn_len = ptq->state->insn_len; |
909 | memcpy(ptq->insn, ptq->state->insn, INTEL_PT_INSN_BUF_SZ); | 924 | memcpy(ptq->insn, ptq->state->insn, INTEL_PT_INSN_BUF_SZ); |
910 | } | 925 | } |
926 | |||
927 | if (ptq->state->type & INTEL_PT_TRACE_BEGIN) | ||
928 | ptq->flags |= PERF_IP_FLAG_TRACE_BEGIN; | ||
929 | if (ptq->state->type & INTEL_PT_TRACE_END) | ||
930 | ptq->flags |= PERF_IP_FLAG_TRACE_END; | ||
911 | } | 931 | } |
912 | 932 | ||
913 | static int intel_pt_setup_queue(struct intel_pt *pt, | 933 | static int intel_pt_setup_queue(struct intel_pt *pt, |
@@ -1053,15 +1073,11 @@ static void intel_pt_prep_b_sample(struct intel_pt *pt, | |||
1053 | union perf_event *event, | 1073 | union perf_event *event, |
1054 | struct perf_sample *sample) | 1074 | struct perf_sample *sample) |
1055 | { | 1075 | { |
1056 | event->sample.header.type = PERF_RECORD_SAMPLE; | ||
1057 | event->sample.header.misc = PERF_RECORD_MISC_USER; | ||
1058 | event->sample.header.size = sizeof(struct perf_event_header); | ||
1059 | |||
1060 | if (!pt->timeless_decoding) | 1076 | if (!pt->timeless_decoding) |
1061 | sample->time = tsc_to_perf_time(ptq->timestamp, &pt->tc); | 1077 | sample->time = tsc_to_perf_time(ptq->timestamp, &pt->tc); |
1062 | 1078 | ||
1063 | sample->cpumode = PERF_RECORD_MISC_USER; | ||
1064 | sample->ip = ptq->state->from_ip; | 1079 | sample->ip = ptq->state->from_ip; |
1080 | sample->cpumode = intel_pt_cpumode(pt, sample->ip); | ||
1065 | sample->pid = ptq->pid; | 1081 | sample->pid = ptq->pid; |
1066 | sample->tid = ptq->tid; | 1082 | sample->tid = ptq->tid; |
1067 | sample->addr = ptq->state->to_ip; | 1083 | sample->addr = ptq->state->to_ip; |
@@ -1070,6 +1086,10 @@ static void intel_pt_prep_b_sample(struct intel_pt *pt, | |||
1070 | sample->flags = ptq->flags; | 1086 | sample->flags = ptq->flags; |
1071 | sample->insn_len = ptq->insn_len; | 1087 | sample->insn_len = ptq->insn_len; |
1072 | memcpy(sample->insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); | 1088 | memcpy(sample->insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); |
1089 | |||
1090 | event->sample.header.type = PERF_RECORD_SAMPLE; | ||
1091 | event->sample.header.misc = sample->cpumode; | ||
1092 | event->sample.header.size = sizeof(struct perf_event_header); | ||
1073 | } | 1093 | } |
1074 | 1094 | ||
1075 | static int intel_pt_inject_event(union perf_event *event, | 1095 | static int intel_pt_inject_event(union perf_event *event, |
@@ -1155,7 +1175,8 @@ static void intel_pt_prep_sample(struct intel_pt *pt, | |||
1155 | 1175 | ||
1156 | if (pt->synth_opts.callchain) { | 1176 | if (pt->synth_opts.callchain) { |
1157 | thread_stack__sample(ptq->thread, ptq->chain, | 1177 | thread_stack__sample(ptq->thread, ptq->chain, |
1158 | pt->synth_opts.callchain_sz, sample->ip); | 1178 | pt->synth_opts.callchain_sz + 1, |
1179 | sample->ip, pt->kernel_start); | ||
1159 | sample->callchain = ptq->chain; | 1180 | sample->callchain = ptq->chain; |
1160 | } | 1181 | } |
1161 | 1182 | ||
@@ -1999,9 +2020,9 @@ static int intel_pt_process_event(struct perf_session *session, | |||
1999 | event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) | 2020 | event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) |
2000 | err = intel_pt_context_switch(pt, event, sample); | 2021 | err = intel_pt_context_switch(pt, event, sample); |
2001 | 2022 | ||
2002 | intel_pt_log("event %s (%u): cpu %d time %"PRIu64" tsc %#"PRIx64"\n", | 2023 | intel_pt_log("event %u: cpu %d time %"PRIu64" tsc %#"PRIx64" ", |
2003 | perf_event__name(event->header.type), event->header.type, | 2024 | event->header.type, sample->cpu, sample->time, timestamp); |
2004 | sample->cpu, sample->time, timestamp); | 2025 | intel_pt_log_event(event); |
2005 | 2026 | ||
2006 | return err; | 2027 | return err; |
2007 | } | 2028 | } |
@@ -2554,7 +2575,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2554 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { | 2575 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { |
2555 | pt->synth_opts = *session->itrace_synth_opts; | 2576 | pt->synth_opts = *session->itrace_synth_opts; |
2556 | } else { | 2577 | } else { |
2557 | itrace_synth_opts__set_default(&pt->synth_opts); | 2578 | itrace_synth_opts__set_default(&pt->synth_opts, |
2579 | session->itrace_synth_opts->default_no_sample); | ||
2558 | if (use_browser != -1) { | 2580 | if (use_browser != -1) { |
2559 | pt->synth_opts.branches = false; | 2581 | pt->synth_opts.branches = false; |
2560 | pt->synth_opts.callchain = true; | 2582 | pt->synth_opts.callchain = true; |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 19262f98cd4e..5b0b60f00275 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | 19 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ |
20 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ | 20 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ |
21 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ | 21 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ |
22 | "$CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS " \ | 22 | "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ |
23 | "-Wno-unused-value -Wno-pointer-sign " \ | 23 | "-Wno-unused-value -Wno-pointer-sign " \ |
24 | "-working-directory $WORKING_DIR " \ | 24 | "-working-directory $WORKING_DIR " \ |
25 | "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" | 25 | "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index c4acd2001db0..8f36ce813bc5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1708,6 +1708,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1708 | struct thread *parent = machine__findnew_thread(machine, | 1708 | struct thread *parent = machine__findnew_thread(machine, |
1709 | event->fork.ppid, | 1709 | event->fork.ppid, |
1710 | event->fork.ptid); | 1710 | event->fork.ptid); |
1711 | bool do_maps_clone = true; | ||
1711 | int err = 0; | 1712 | int err = 0; |
1712 | 1713 | ||
1713 | if (dump_trace) | 1714 | if (dump_trace) |
@@ -1736,9 +1737,25 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1736 | 1737 | ||
1737 | thread = machine__findnew_thread(machine, event->fork.pid, | 1738 | thread = machine__findnew_thread(machine, event->fork.pid, |
1738 | event->fork.tid); | 1739 | event->fork.tid); |
1740 | /* | ||
1741 | * When synthesizing FORK events, we are trying to create thread | ||
1742 | * objects for the already running tasks on the machine. | ||
1743 | * | ||
1744 | * Normally, for a kernel FORK event, we want to clone the parent's | ||
1745 | * maps because that is what the kernel just did. | ||
1746 | * | ||
1747 | * But when synthesizing, this should not be done. If we do, we end up | ||
1748 | * with overlapping maps as we process the sythesized MMAP2 events that | ||
1749 | * get delivered shortly thereafter. | ||
1750 | * | ||
1751 | * Use the FORK event misc flags in an internal way to signal this | ||
1752 | * situation, so we can elide the map clone when appropriate. | ||
1753 | */ | ||
1754 | if (event->fork.header.misc & PERF_RECORD_MISC_FORK_EXEC) | ||
1755 | do_maps_clone = false; | ||
1739 | 1756 | ||
1740 | if (thread == NULL || parent == NULL || | 1757 | if (thread == NULL || parent == NULL || |
1741 | thread__fork(thread, parent, sample->time) < 0) { | 1758 | thread__fork(thread, parent, sample->time, do_maps_clone) < 0) { |
1742 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | 1759 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); |
1743 | err = -1; | 1760 | err = -1; |
1744 | } | 1761 | } |
@@ -2140,6 +2157,27 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
2140 | return 0; | 2157 | return 0; |
2141 | } | 2158 | } |
2142 | 2159 | ||
2160 | static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, | ||
2161 | struct callchain_cursor *cursor, | ||
2162 | struct symbol **parent, | ||
2163 | struct addr_location *root_al, | ||
2164 | u8 *cpumode, int ent) | ||
2165 | { | ||
2166 | int err = 0; | ||
2167 | |||
2168 | while (--ent >= 0) { | ||
2169 | u64 ip = chain->ips[ent]; | ||
2170 | |||
2171 | if (ip >= PERF_CONTEXT_MAX) { | ||
2172 | err = add_callchain_ip(thread, cursor, parent, | ||
2173 | root_al, cpumode, ip, | ||
2174 | false, NULL, NULL, 0); | ||
2175 | break; | ||
2176 | } | ||
2177 | } | ||
2178 | return err; | ||
2179 | } | ||
2180 | |||
2143 | static int thread__resolve_callchain_sample(struct thread *thread, | 2181 | static int thread__resolve_callchain_sample(struct thread *thread, |
2144 | struct callchain_cursor *cursor, | 2182 | struct callchain_cursor *cursor, |
2145 | struct perf_evsel *evsel, | 2183 | struct perf_evsel *evsel, |
@@ -2246,6 +2284,12 @@ static int thread__resolve_callchain_sample(struct thread *thread, | |||
2246 | } | 2284 | } |
2247 | 2285 | ||
2248 | check_calls: | 2286 | check_calls: |
2287 | if (callchain_param.order != ORDER_CALLEE) { | ||
2288 | err = find_prev_cpumode(chain, thread, cursor, parent, root_al, | ||
2289 | &cpumode, chain->nr - first_call); | ||
2290 | if (err) | ||
2291 | return (err < 0) ? err : 0; | ||
2292 | } | ||
2249 | for (i = first_call, nr_entries = 0; | 2293 | for (i = first_call, nr_entries = 0; |
2250 | i < chain_nr && nr_entries < max_stack; i++) { | 2294 | i < chain_nr && nr_entries < max_stack; i++) { |
2251 | u64 ip; | 2295 | u64 ip; |
@@ -2260,9 +2304,15 @@ check_calls: | |||
2260 | continue; | 2304 | continue; |
2261 | #endif | 2305 | #endif |
2262 | ip = chain->ips[j]; | 2306 | ip = chain->ips[j]; |
2263 | |||
2264 | if (ip < PERF_CONTEXT_MAX) | 2307 | if (ip < PERF_CONTEXT_MAX) |
2265 | ++nr_entries; | 2308 | ++nr_entries; |
2309 | else if (callchain_param.order != ORDER_CALLEE) { | ||
2310 | err = find_prev_cpumode(chain, thread, cursor, parent, | ||
2311 | root_al, &cpumode, j); | ||
2312 | if (err) | ||
2313 | return (err < 0) ? err : 0; | ||
2314 | continue; | ||
2315 | } | ||
2266 | 2316 | ||
2267 | err = add_callchain_ip(thread, cursor, parent, | 2317 | err = add_callchain_ip(thread, cursor, parent, |
2268 | root_al, &cpumode, ip, | 2318 | root_al, &cpumode, ip, |
@@ -2286,7 +2336,8 @@ static int append_inlines(struct callchain_cursor *cursor, | |||
2286 | if (!symbol_conf.inline_name || !map || !sym) | 2336 | if (!symbol_conf.inline_name || !map || !sym) |
2287 | return ret; | 2337 | return ret; |
2288 | 2338 | ||
2289 | addr = map__rip_2objdump(map, ip); | 2339 | addr = map__map_ip(map, ip); |
2340 | addr = map__rip_2objdump(map, addr); | ||
2290 | 2341 | ||
2291 | inline_node = inlines__tree_find(&map->dso->inlined_nodes, addr); | 2342 | inline_node = inlines__tree_find(&map->dso->inlined_nodes, addr); |
2292 | if (!inline_node) { | 2343 | if (!inline_node) { |
@@ -2312,7 +2363,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
2312 | { | 2363 | { |
2313 | struct callchain_cursor *cursor = arg; | 2364 | struct callchain_cursor *cursor = arg; |
2314 | const char *srcline = NULL; | 2365 | const char *srcline = NULL; |
2315 | u64 addr; | 2366 | u64 addr = entry->ip; |
2316 | 2367 | ||
2317 | if (symbol_conf.hide_unresolved && entry->sym == NULL) | 2368 | if (symbol_conf.hide_unresolved && entry->sym == NULL) |
2318 | return 0; | 2369 | return 0; |
@@ -2324,7 +2375,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
2324 | * Convert entry->ip from a virtual address to an offset in | 2375 | * Convert entry->ip from a virtual address to an offset in |
2325 | * its corresponding binary. | 2376 | * its corresponding binary. |
2326 | */ | 2377 | */ |
2327 | addr = map__map_ip(entry->map, entry->ip); | 2378 | if (entry->map) |
2379 | addr = map__map_ip(entry->map, entry->ip); | ||
2328 | 2380 | ||
2329 | srcline = callchain_srcline(entry->map, entry->sym, addr); | 2381 | srcline = callchain_srcline(entry->map, entry->sym, addr); |
2330 | return callchain_cursor_append(cursor, entry->ip, | 2382 | return callchain_cursor_append(cursor, entry->ip, |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 6a6929f208b4..354e54550d2b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -320,12 +320,11 @@ int map__load(struct map *map) | |||
320 | build_id__sprintf(map->dso->build_id, | 320 | build_id__sprintf(map->dso->build_id, |
321 | sizeof(map->dso->build_id), | 321 | sizeof(map->dso->build_id), |
322 | sbuild_id); | 322 | sbuild_id); |
323 | pr_warning("%s with build id %s not found", | 323 | pr_debug("%s with build id %s not found", name, sbuild_id); |
324 | name, sbuild_id); | ||
325 | } else | 324 | } else |
326 | pr_warning("Failed to open %s", name); | 325 | pr_debug("Failed to open %s", name); |
327 | 326 | ||
328 | pr_warning(", continuing without symbols\n"); | 327 | pr_debug(", continuing without symbols\n"); |
329 | return -1; | 328 | return -1; |
330 | } else if (nr == 0) { | 329 | } else if (nr == 0) { |
331 | #ifdef HAVE_LIBELF_SUPPORT | 330 | #ifdef HAVE_LIBELF_SUPPORT |
@@ -334,12 +333,11 @@ int map__load(struct map *map) | |||
334 | 333 | ||
335 | if (len > sizeof(DSO__DELETED) && | 334 | if (len > sizeof(DSO__DELETED) && |
336 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 335 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
337 | pr_warning("%.*s was updated (is prelink enabled?). " | 336 | pr_debug("%.*s was updated (is prelink enabled?). " |
338 | "Restart the long running apps that use it!\n", | 337 | "Restart the long running apps that use it!\n", |
339 | (int)real_len, name); | 338 | (int)real_len, name); |
340 | } else { | 339 | } else { |
341 | pr_warning("no symbols found in %s, maybe install " | 340 | pr_debug("no symbols found in %s, maybe install a debug package?\n", name); |
342 | "a debug package?\n", name); | ||
343 | } | 341 | } |
344 | #endif | 342 | #endif |
345 | return -1; | 343 | return -1; |
@@ -712,8 +710,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
712 | if (verbose >= 2) { | 710 | if (verbose >= 2) { |
713 | 711 | ||
714 | if (use_browser) { | 712 | if (use_browser) { |
715 | pr_warning("overlapping maps in %s " | 713 | pr_debug("overlapping maps in %s (disable tui for more info)\n", |
716 | "(disable tui for more info)\n", | ||
717 | map->dso->name); | 714 | map->dso->name); |
718 | } else { | 715 | } else { |
719 | fputs("overlapping maps:\n", fp); | 716 | fputs("overlapping maps:\n", fp); |
diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 215f69f41672..cdb95b3a1213 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c | |||
@@ -281,7 +281,7 @@ int perf_mmap__read_init(struct perf_mmap *map) | |||
281 | } | 281 | } |
282 | 282 | ||
283 | int perf_mmap__push(struct perf_mmap *md, void *to, | 283 | int perf_mmap__push(struct perf_mmap *md, void *to, |
284 | int push(void *to, void *buf, size_t size)) | 284 | int push(struct perf_mmap *map, void *to, void *buf, size_t size)) |
285 | { | 285 | { |
286 | u64 head = perf_mmap__read_head(md); | 286 | u64 head = perf_mmap__read_head(md); |
287 | unsigned char *data = md->base + page_size; | 287 | unsigned char *data = md->base + page_size; |
@@ -300,7 +300,7 @@ int perf_mmap__push(struct perf_mmap *md, void *to, | |||
300 | size = md->mask + 1 - (md->start & md->mask); | 300 | size = md->mask + 1 - (md->start & md->mask); |
301 | md->start += size; | 301 | md->start += size; |
302 | 302 | ||
303 | if (push(to, buf, size) < 0) { | 303 | if (push(md, to, buf, size) < 0) { |
304 | rc = -1; | 304 | rc = -1; |
305 | goto out; | 305 | goto out; |
306 | } | 306 | } |
@@ -310,7 +310,7 @@ int perf_mmap__push(struct perf_mmap *md, void *to, | |||
310 | size = md->end - md->start; | 310 | size = md->end - md->start; |
311 | md->start += size; | 311 | md->start += size; |
312 | 312 | ||
313 | if (push(to, buf, size) < 0) { | 313 | if (push(md, to, buf, size) < 0) { |
314 | rc = -1; | 314 | rc = -1; |
315 | goto out; | 315 | goto out; |
316 | } | 316 | } |
diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h index 05a6d47c7956..cc5e2d6d17a9 100644 --- a/tools/perf/util/mmap.h +++ b/tools/perf/util/mmap.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/compiler.h> | 4 | #include <linux/compiler.h> |
5 | #include <linux/refcount.h> | 5 | #include <linux/refcount.h> |
6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
7 | #include <asm/barrier.h> | 7 | #include <linux/ring_buffer.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include "auxtrace.h" | 9 | #include "auxtrace.h" |
10 | #include "event.h" | 10 | #include "event.h" |
@@ -71,21 +71,12 @@ void perf_mmap__consume(struct perf_mmap *map); | |||
71 | 71 | ||
72 | static inline u64 perf_mmap__read_head(struct perf_mmap *mm) | 72 | static inline u64 perf_mmap__read_head(struct perf_mmap *mm) |
73 | { | 73 | { |
74 | struct perf_event_mmap_page *pc = mm->base; | 74 | return ring_buffer_read_head(mm->base); |
75 | u64 head = READ_ONCE(pc->data_head); | ||
76 | rmb(); | ||
77 | return head; | ||
78 | } | 75 | } |
79 | 76 | ||
80 | static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) | 77 | static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) |
81 | { | 78 | { |
82 | struct perf_event_mmap_page *pc = md->base; | 79 | ring_buffer_write_tail(md->base, tail); |
83 | |||
84 | /* | ||
85 | * ensure all reads are done before we write the tail out. | ||
86 | */ | ||
87 | mb(); | ||
88 | pc->data_tail = tail; | ||
89 | } | 80 | } |
90 | 81 | ||
91 | union perf_event *perf_mmap__read_forward(struct perf_mmap *map); | 82 | union perf_event *perf_mmap__read_forward(struct perf_mmap *map); |
@@ -93,7 +84,7 @@ union perf_event *perf_mmap__read_forward(struct perf_mmap *map); | |||
93 | union perf_event *perf_mmap__read_event(struct perf_mmap *map); | 84 | union perf_event *perf_mmap__read_event(struct perf_mmap *map); |
94 | 85 | ||
95 | int perf_mmap__push(struct perf_mmap *md, void *to, | 86 | int perf_mmap__push(struct perf_mmap *md, void *to, |
96 | int push(void *to, void *buf, size_t size)); | 87 | int push(struct perf_mmap *map, void *to, void *buf, size_t size)); |
97 | 88 | ||
98 | size_t perf_mmap__mmap_len(struct perf_mmap *map); | 89 | size_t perf_mmap__mmap_len(struct perf_mmap *map); |
99 | 90 | ||
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c index bad9e0296e9a..1904e7f6ec84 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c | |||
@@ -80,14 +80,20 @@ static union perf_event *dup_event(struct ordered_events *oe, | |||
80 | return oe->copy_on_queue ? __dup_event(oe, event) : event; | 80 | return oe->copy_on_queue ? __dup_event(oe, event) : event; |
81 | } | 81 | } |
82 | 82 | ||
83 | static void free_dup_event(struct ordered_events *oe, union perf_event *event) | 83 | static void __free_dup_event(struct ordered_events *oe, union perf_event *event) |
84 | { | 84 | { |
85 | if (event && oe->copy_on_queue) { | 85 | if (event) { |
86 | oe->cur_alloc_size -= event->header.size; | 86 | oe->cur_alloc_size -= event->header.size; |
87 | free(event); | 87 | free(event); |
88 | } | 88 | } |
89 | } | 89 | } |
90 | 90 | ||
91 | static void free_dup_event(struct ordered_events *oe, union perf_event *event) | ||
92 | { | ||
93 | if (oe->copy_on_queue) | ||
94 | __free_dup_event(oe, event); | ||
95 | } | ||
96 | |||
91 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) | 97 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) |
92 | static struct ordered_event *alloc_event(struct ordered_events *oe, | 98 | static struct ordered_event *alloc_event(struct ordered_events *oe, |
93 | union perf_event *event) | 99 | union perf_event *event) |
@@ -95,21 +101,49 @@ static struct ordered_event *alloc_event(struct ordered_events *oe, | |||
95 | struct list_head *cache = &oe->cache; | 101 | struct list_head *cache = &oe->cache; |
96 | struct ordered_event *new = NULL; | 102 | struct ordered_event *new = NULL; |
97 | union perf_event *new_event; | 103 | union perf_event *new_event; |
104 | size_t size; | ||
98 | 105 | ||
99 | new_event = dup_event(oe, event); | 106 | new_event = dup_event(oe, event); |
100 | if (!new_event) | 107 | if (!new_event) |
101 | return NULL; | 108 | return NULL; |
102 | 109 | ||
110 | /* | ||
111 | * We maintain the following scheme of buffers for ordered | ||
112 | * event allocation: | ||
113 | * | ||
114 | * to_free list -> buffer1 (64K) | ||
115 | * buffer2 (64K) | ||
116 | * ... | ||
117 | * | ||
118 | * Each buffer keeps an array of ordered events objects: | ||
119 | * buffer -> event[0] | ||
120 | * event[1] | ||
121 | * ... | ||
122 | * | ||
123 | * Each allocated ordered event is linked to one of | ||
124 | * following lists: | ||
125 | * - time ordered list 'events' | ||
126 | * - list of currently removed events 'cache' | ||
127 | * | ||
128 | * Allocation of the ordered event uses the following order | ||
129 | * to get the memory: | ||
130 | * - use recently removed object from 'cache' list | ||
131 | * - use available object in current allocation buffer | ||
132 | * - allocate new buffer if the current buffer is full | ||
133 | * | ||
134 | * Removal of ordered event object moves it from events to | ||
135 | * the cache list. | ||
136 | */ | ||
137 | size = sizeof(*oe->buffer) + MAX_SAMPLE_BUFFER * sizeof(*new); | ||
138 | |||
103 | if (!list_empty(cache)) { | 139 | if (!list_empty(cache)) { |
104 | new = list_entry(cache->next, struct ordered_event, list); | 140 | new = list_entry(cache->next, struct ordered_event, list); |
105 | list_del(&new->list); | 141 | list_del(&new->list); |
106 | } else if (oe->buffer) { | 142 | } else if (oe->buffer) { |
107 | new = oe->buffer + oe->buffer_idx; | 143 | new = &oe->buffer->event[oe->buffer_idx]; |
108 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) | 144 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) |
109 | oe->buffer = NULL; | 145 | oe->buffer = NULL; |
110 | } else if (oe->cur_alloc_size < oe->max_alloc_size) { | 146 | } else if ((oe->cur_alloc_size + size) < oe->max_alloc_size) { |
111 | size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); | ||
112 | |||
113 | oe->buffer = malloc(size); | 147 | oe->buffer = malloc(size); |
114 | if (!oe->buffer) { | 148 | if (!oe->buffer) { |
115 | free_dup_event(oe, new_event); | 149 | free_dup_event(oe, new_event); |
@@ -122,11 +156,11 @@ static struct ordered_event *alloc_event(struct ordered_events *oe, | |||
122 | oe->cur_alloc_size += size; | 156 | oe->cur_alloc_size += size; |
123 | list_add(&oe->buffer->list, &oe->to_free); | 157 | list_add(&oe->buffer->list, &oe->to_free); |
124 | 158 | ||
125 | /* First entry is abused to maintain the to_free list. */ | 159 | oe->buffer_idx = 1; |
126 | oe->buffer_idx = 2; | 160 | new = &oe->buffer->event[0]; |
127 | new = oe->buffer + 1; | ||
128 | } else { | 161 | } else { |
129 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); | 162 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); |
163 | return NULL; | ||
130 | } | 164 | } |
131 | 165 | ||
132 | new->event = new_event; | 166 | new->event = new_event; |
@@ -300,15 +334,38 @@ void ordered_events__init(struct ordered_events *oe, ordered_events__deliver_t d | |||
300 | oe->deliver = deliver; | 334 | oe->deliver = deliver; |
301 | } | 335 | } |
302 | 336 | ||
337 | static void | ||
338 | ordered_events_buffer__free(struct ordered_events_buffer *buffer, | ||
339 | unsigned int max, struct ordered_events *oe) | ||
340 | { | ||
341 | if (oe->copy_on_queue) { | ||
342 | unsigned int i; | ||
343 | |||
344 | for (i = 0; i < max; i++) | ||
345 | __free_dup_event(oe, buffer->event[i].event); | ||
346 | } | ||
347 | |||
348 | free(buffer); | ||
349 | } | ||
350 | |||
303 | void ordered_events__free(struct ordered_events *oe) | 351 | void ordered_events__free(struct ordered_events *oe) |
304 | { | 352 | { |
305 | while (!list_empty(&oe->to_free)) { | 353 | struct ordered_events_buffer *buffer, *tmp; |
306 | struct ordered_event *event; | ||
307 | 354 | ||
308 | event = list_entry(oe->to_free.next, struct ordered_event, list); | 355 | if (list_empty(&oe->to_free)) |
309 | list_del(&event->list); | 356 | return; |
310 | free_dup_event(oe, event->event); | 357 | |
311 | free(event); | 358 | /* |
359 | * Current buffer might not have all the events allocated | ||
360 | * yet, we need to free only allocated ones ... | ||
361 | */ | ||
362 | list_del(&oe->buffer->list); | ||
363 | ordered_events_buffer__free(oe->buffer, oe->buffer_idx, oe); | ||
364 | |||
365 | /* ... and continue with the rest */ | ||
366 | list_for_each_entry_safe(buffer, tmp, &oe->to_free, list) { | ||
367 | list_del(&buffer->list); | ||
368 | ordered_events_buffer__free(buffer, MAX_SAMPLE_BUFFER, oe); | ||
312 | } | 369 | } |
313 | } | 370 | } |
314 | 371 | ||
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h index 8c7a2948593e..1338d5c345dc 100644 --- a/tools/perf/util/ordered-events.h +++ b/tools/perf/util/ordered-events.h | |||
@@ -25,23 +25,28 @@ struct ordered_events; | |||
25 | typedef int (*ordered_events__deliver_t)(struct ordered_events *oe, | 25 | typedef int (*ordered_events__deliver_t)(struct ordered_events *oe, |
26 | struct ordered_event *event); | 26 | struct ordered_event *event); |
27 | 27 | ||
28 | struct ordered_events_buffer { | ||
29 | struct list_head list; | ||
30 | struct ordered_event event[0]; | ||
31 | }; | ||
32 | |||
28 | struct ordered_events { | 33 | struct ordered_events { |
29 | u64 last_flush; | 34 | u64 last_flush; |
30 | u64 next_flush; | 35 | u64 next_flush; |
31 | u64 max_timestamp; | 36 | u64 max_timestamp; |
32 | u64 max_alloc_size; | 37 | u64 max_alloc_size; |
33 | u64 cur_alloc_size; | 38 | u64 cur_alloc_size; |
34 | struct list_head events; | 39 | struct list_head events; |
35 | struct list_head cache; | 40 | struct list_head cache; |
36 | struct list_head to_free; | 41 | struct list_head to_free; |
37 | struct ordered_event *buffer; | 42 | struct ordered_events_buffer *buffer; |
38 | struct ordered_event *last; | 43 | struct ordered_event *last; |
39 | ordered_events__deliver_t deliver; | 44 | ordered_events__deliver_t deliver; |
40 | int buffer_idx; | 45 | int buffer_idx; |
41 | unsigned int nr_events; | 46 | unsigned int nr_events; |
42 | enum oe_flush last_flush_type; | 47 | enum oe_flush last_flush_type; |
43 | u32 nr_unordered_events; | 48 | u32 nr_unordered_events; |
44 | bool copy_on_queue; | 49 | bool copy_on_queue; |
45 | }; | 50 | }; |
46 | 51 | ||
47 | int ordered_events__queue(struct ordered_events *oe, union perf_event *event, | 52 | int ordered_events__queue(struct ordered_events *oe, union perf_event *event, |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index f8cd3e7c9186..59be3466d64d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -926,6 +926,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { | |||
926 | [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", | 926 | [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", |
927 | [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", | 927 | [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", |
928 | [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", | 928 | [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", |
929 | [PARSE_EVENTS__TERM_TYPE_MAX_EVENTS] = "nr", | ||
929 | [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite", | 930 | [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite", |
930 | [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite", | 931 | [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite", |
931 | [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config", | 932 | [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config", |
@@ -1037,6 +1038,9 @@ do { \ | |||
1037 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: | 1038 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: |
1038 | CHECK_TYPE_VAL(NUM); | 1039 | CHECK_TYPE_VAL(NUM); |
1039 | break; | 1040 | break; |
1041 | case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: | ||
1042 | CHECK_TYPE_VAL(NUM); | ||
1043 | break; | ||
1040 | default: | 1044 | default: |
1041 | err->str = strdup("unknown term"); | 1045 | err->str = strdup("unknown term"); |
1042 | err->idx = term->err_term; | 1046 | err->idx = term->err_term; |
@@ -1084,6 +1088,7 @@ static int config_term_tracepoint(struct perf_event_attr *attr, | |||
1084 | case PARSE_EVENTS__TERM_TYPE_INHERIT: | 1088 | case PARSE_EVENTS__TERM_TYPE_INHERIT: |
1085 | case PARSE_EVENTS__TERM_TYPE_NOINHERIT: | 1089 | case PARSE_EVENTS__TERM_TYPE_NOINHERIT: |
1086 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: | 1090 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: |
1091 | case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: | ||
1087 | case PARSE_EVENTS__TERM_TYPE_OVERWRITE: | 1092 | case PARSE_EVENTS__TERM_TYPE_OVERWRITE: |
1088 | case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE: | 1093 | case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE: |
1089 | return config_term_common(attr, term, err); | 1094 | return config_term_common(attr, term, err); |
@@ -1162,6 +1167,9 @@ do { \ | |||
1162 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: | 1167 | case PARSE_EVENTS__TERM_TYPE_MAX_STACK: |
1163 | ADD_CONFIG_TERM(MAX_STACK, max_stack, term->val.num); | 1168 | ADD_CONFIG_TERM(MAX_STACK, max_stack, term->val.num); |
1164 | break; | 1169 | break; |
1170 | case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: | ||
1171 | ADD_CONFIG_TERM(MAX_EVENTS, max_events, term->val.num); | ||
1172 | break; | ||
1165 | case PARSE_EVENTS__TERM_TYPE_OVERWRITE: | 1173 | case PARSE_EVENTS__TERM_TYPE_OVERWRITE: |
1166 | ADD_CONFIG_TERM(OVERWRITE, overwrite, term->val.num ? 1 : 0); | 1174 | ADD_CONFIG_TERM(OVERWRITE, overwrite, term->val.num ? 1 : 0); |
1167 | break; | 1175 | break; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 4473dac27aee..5ed035cbcbb7 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -71,6 +71,7 @@ enum { | |||
71 | PARSE_EVENTS__TERM_TYPE_NOINHERIT, | 71 | PARSE_EVENTS__TERM_TYPE_NOINHERIT, |
72 | PARSE_EVENTS__TERM_TYPE_INHERIT, | 72 | PARSE_EVENTS__TERM_TYPE_INHERIT, |
73 | PARSE_EVENTS__TERM_TYPE_MAX_STACK, | 73 | PARSE_EVENTS__TERM_TYPE_MAX_STACK, |
74 | PARSE_EVENTS__TERM_TYPE_MAX_EVENTS, | ||
74 | PARSE_EVENTS__TERM_TYPE_NOOVERWRITE, | 75 | PARSE_EVENTS__TERM_TYPE_NOOVERWRITE, |
75 | PARSE_EVENTS__TERM_TYPE_OVERWRITE, | 76 | PARSE_EVENTS__TERM_TYPE_OVERWRITE, |
76 | PARSE_EVENTS__TERM_TYPE_DRV_CFG, | 77 | PARSE_EVENTS__TERM_TYPE_DRV_CFG, |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 5f761f3ed0f3..7805c71aaae2 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -269,6 +269,7 @@ time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); } | |||
269 | call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); } | 269 | call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); } |
270 | stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); } | 270 | stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); } |
271 | max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); } | 271 | max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); } |
272 | nr { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_EVENTS); } | ||
272 | inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); } | 273 | inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); } |
273 | no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } | 274 | no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } |
274 | overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); } | 275 | overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); } |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index afd68524ffa9..7e49baad304d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -773,7 +773,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) | |||
773 | 773 | ||
774 | if (!is_arm_pmu_core(name)) { | 774 | if (!is_arm_pmu_core(name)) { |
775 | pname = pe->pmu ? pe->pmu : "cpu"; | 775 | pname = pe->pmu ? pe->pmu : "cpu"; |
776 | if (strncmp(pname, name, strlen(pname))) | 776 | if (strcmp(pname, name)) |
777 | continue; | 777 | continue; |
778 | } | 778 | } |
779 | 779 | ||
@@ -930,13 +930,14 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, | |||
930 | 930 | ||
931 | static __u64 pmu_format_max_value(const unsigned long *format) | 931 | static __u64 pmu_format_max_value(const unsigned long *format) |
932 | { | 932 | { |
933 | __u64 w = 0; | 933 | int w; |
934 | int fbit; | ||
935 | |||
936 | for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS) | ||
937 | w |= (1ULL << fbit); | ||
938 | 934 | ||
939 | return w; | 935 | w = bitmap_weight(format, PERF_PMU_FORMAT_BITS); |
936 | if (!w) | ||
937 | return 0; | ||
938 | if (w < 64) | ||
939 | return (1ULL << w) - 1; | ||
940 | return -1; | ||
940 | } | 941 | } |
941 | 942 | ||
942 | /* | 943 | /* |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f119eb628dbb..e86f8be89157 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -1819,6 +1819,12 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev) | |||
1819 | tp->offset = strtoul(fmt2_str, NULL, 10); | 1819 | tp->offset = strtoul(fmt2_str, NULL, 10); |
1820 | } | 1820 | } |
1821 | 1821 | ||
1822 | if (tev->uprobes) { | ||
1823 | fmt2_str = strchr(p, '('); | ||
1824 | if (fmt2_str) | ||
1825 | tp->ref_ctr_offset = strtoul(fmt2_str + 1, NULL, 0); | ||
1826 | } | ||
1827 | |||
1822 | tev->nargs = argc - 2; | 1828 | tev->nargs = argc - 2; |
1823 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1829 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
1824 | if (tev->args == NULL) { | 1830 | if (tev->args == NULL) { |
@@ -2012,6 +2018,22 @@ static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, | |||
2012 | return err; | 2018 | return err; |
2013 | } | 2019 | } |
2014 | 2020 | ||
2021 | static int | ||
2022 | synthesize_uprobe_trace_def(struct probe_trace_event *tev, struct strbuf *buf) | ||
2023 | { | ||
2024 | struct probe_trace_point *tp = &tev->point; | ||
2025 | int err; | ||
2026 | |||
2027 | err = strbuf_addf(buf, "%s:0x%lx", tp->module, tp->address); | ||
2028 | |||
2029 | if (err >= 0 && tp->ref_ctr_offset) { | ||
2030 | if (!uprobe_ref_ctr_is_supported()) | ||
2031 | return -1; | ||
2032 | err = strbuf_addf(buf, "(0x%lx)", tp->ref_ctr_offset); | ||
2033 | } | ||
2034 | return err >= 0 ? 0 : -1; | ||
2035 | } | ||
2036 | |||
2015 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) | 2037 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) |
2016 | { | 2038 | { |
2017 | struct probe_trace_point *tp = &tev->point; | 2039 | struct probe_trace_point *tp = &tev->point; |
@@ -2041,15 +2063,17 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) | |||
2041 | } | 2063 | } |
2042 | 2064 | ||
2043 | /* Use the tp->address for uprobes */ | 2065 | /* Use the tp->address for uprobes */ |
2044 | if (tev->uprobes) | 2066 | if (tev->uprobes) { |
2045 | err = strbuf_addf(&buf, "%s:0x%lx", tp->module, tp->address); | 2067 | err = synthesize_uprobe_trace_def(tev, &buf); |
2046 | else if (!strncmp(tp->symbol, "0x", 2)) | 2068 | } else if (!strncmp(tp->symbol, "0x", 2)) { |
2047 | /* Absolute address. See try_to_find_absolute_address() */ | 2069 | /* Absolute address. See try_to_find_absolute_address() */ |
2048 | err = strbuf_addf(&buf, "%s%s0x%lx", tp->module ?: "", | 2070 | err = strbuf_addf(&buf, "%s%s0x%lx", tp->module ?: "", |
2049 | tp->module ? ":" : "", tp->address); | 2071 | tp->module ? ":" : "", tp->address); |
2050 | else | 2072 | } else { |
2051 | err = strbuf_addf(&buf, "%s%s%s+%lu", tp->module ?: "", | 2073 | err = strbuf_addf(&buf, "%s%s%s+%lu", tp->module ?: "", |
2052 | tp->module ? ":" : "", tp->symbol, tp->offset); | 2074 | tp->module ? ":" : "", tp->symbol, tp->offset); |
2075 | } | ||
2076 | |||
2053 | if (err) | 2077 | if (err) |
2054 | goto error; | 2078 | goto error; |
2055 | 2079 | ||
@@ -2633,6 +2657,13 @@ static void warn_uprobe_event_compat(struct probe_trace_event *tev) | |||
2633 | { | 2657 | { |
2634 | int i; | 2658 | int i; |
2635 | char *buf = synthesize_probe_trace_command(tev); | 2659 | char *buf = synthesize_probe_trace_command(tev); |
2660 | struct probe_trace_point *tp = &tev->point; | ||
2661 | |||
2662 | if (tp->ref_ctr_offset && !uprobe_ref_ctr_is_supported()) { | ||
2663 | pr_warning("A semaphore is associated with %s:%s and " | ||
2664 | "seems your kernel doesn't support it.\n", | ||
2665 | tev->group, tev->event); | ||
2666 | } | ||
2636 | 2667 | ||
2637 | /* Old uprobe event doesn't support memory dereference */ | 2668 | /* Old uprobe event doesn't support memory dereference */ |
2638 | if (!tev->uprobes || tev->nargs == 0 || !buf) | 2669 | if (!tev->uprobes || tev->nargs == 0 || !buf) |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 45b14f020558..15a98c3a2a2f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -27,6 +27,7 @@ struct probe_trace_point { | |||
27 | char *symbol; /* Base symbol */ | 27 | char *symbol; /* Base symbol */ |
28 | char *module; /* Module name */ | 28 | char *module; /* Module name */ |
29 | unsigned long offset; /* Offset from symbol */ | 29 | unsigned long offset; /* Offset from symbol */ |
30 | unsigned long ref_ctr_offset; /* SDT reference counter offset */ | ||
30 | unsigned long address; /* Actual address of the trace point */ | 31 | unsigned long address; /* Actual address of the trace point */ |
31 | bool retprobe; /* Return probe flag */ | 32 | bool retprobe; /* Return probe flag */ |
32 | }; | 33 | }; |
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index b76088fadf3d..aac7817d9e14 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
@@ -696,8 +696,16 @@ out_err: | |||
696 | #ifdef HAVE_GELF_GETNOTE_SUPPORT | 696 | #ifdef HAVE_GELF_GETNOTE_SUPPORT |
697 | static unsigned long long sdt_note__get_addr(struct sdt_note *note) | 697 | static unsigned long long sdt_note__get_addr(struct sdt_note *note) |
698 | { | 698 | { |
699 | return note->bit32 ? (unsigned long long)note->addr.a32[0] | 699 | return note->bit32 ? |
700 | : (unsigned long long)note->addr.a64[0]; | 700 | (unsigned long long)note->addr.a32[SDT_NOTE_IDX_LOC] : |
701 | (unsigned long long)note->addr.a64[SDT_NOTE_IDX_LOC]; | ||
702 | } | ||
703 | |||
704 | static unsigned long long sdt_note__get_ref_ctr_offset(struct sdt_note *note) | ||
705 | { | ||
706 | return note->bit32 ? | ||
707 | (unsigned long long)note->addr.a32[SDT_NOTE_IDX_REFCTR] : | ||
708 | (unsigned long long)note->addr.a64[SDT_NOTE_IDX_REFCTR]; | ||
701 | } | 709 | } |
702 | 710 | ||
703 | static const char * const type_to_suffix[] = { | 711 | static const char * const type_to_suffix[] = { |
@@ -775,14 +783,21 @@ static char *synthesize_sdt_probe_command(struct sdt_note *note, | |||
775 | { | 783 | { |
776 | struct strbuf buf; | 784 | struct strbuf buf; |
777 | char *ret = NULL, **args; | 785 | char *ret = NULL, **args; |
778 | int i, args_count; | 786 | int i, args_count, err; |
787 | unsigned long long ref_ctr_offset; | ||
779 | 788 | ||
780 | if (strbuf_init(&buf, 32) < 0) | 789 | if (strbuf_init(&buf, 32) < 0) |
781 | return NULL; | 790 | return NULL; |
782 | 791 | ||
783 | if (strbuf_addf(&buf, "p:%s/%s %s:0x%llx", | 792 | err = strbuf_addf(&buf, "p:%s/%s %s:0x%llx", |
784 | sdtgrp, note->name, pathname, | 793 | sdtgrp, note->name, pathname, |
785 | sdt_note__get_addr(note)) < 0) | 794 | sdt_note__get_addr(note)); |
795 | |||
796 | ref_ctr_offset = sdt_note__get_ref_ctr_offset(note); | ||
797 | if (ref_ctr_offset && err >= 0) | ||
798 | err = strbuf_addf(&buf, "(0x%llx)", ref_ctr_offset); | ||
799 | |||
800 | if (err < 0) | ||
786 | goto error; | 801 | goto error; |
787 | 802 | ||
788 | if (!note->args) | 803 | if (!note->args) |
@@ -998,6 +1013,7 @@ int probe_cache__show_all_caches(struct strfilter *filter) | |||
998 | enum ftrace_readme { | 1013 | enum ftrace_readme { |
999 | FTRACE_README_PROBE_TYPE_X = 0, | 1014 | FTRACE_README_PROBE_TYPE_X = 0, |
1000 | FTRACE_README_KRETPROBE_OFFSET, | 1015 | FTRACE_README_KRETPROBE_OFFSET, |
1016 | FTRACE_README_UPROBE_REF_CTR, | ||
1001 | FTRACE_README_END, | 1017 | FTRACE_README_END, |
1002 | }; | 1018 | }; |
1003 | 1019 | ||
@@ -1009,6 +1025,7 @@ static struct { | |||
1009 | [idx] = {.pattern = pat, .avail = false} | 1025 | [idx] = {.pattern = pat, .avail = false} |
1010 | DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"), | 1026 | DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"), |
1011 | DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), | 1027 | DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), |
1028 | DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"), | ||
1012 | }; | 1029 | }; |
1013 | 1030 | ||
1014 | static bool scan_ftrace_readme(enum ftrace_readme type) | 1031 | static bool scan_ftrace_readme(enum ftrace_readme type) |
@@ -1064,3 +1081,8 @@ bool kretprobe_offset_is_supported(void) | |||
1064 | { | 1081 | { |
1065 | return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET); | 1082 | return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET); |
1066 | } | 1083 | } |
1084 | |||
1085 | bool uprobe_ref_ctr_is_supported(void) | ||
1086 | { | ||
1087 | return scan_ftrace_readme(FTRACE_README_UPROBE_REF_CTR); | ||
1088 | } | ||
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 63f29b1d22c1..2a249182f2a6 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h | |||
@@ -69,6 +69,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache, | |||
69 | int probe_cache__show_all_caches(struct strfilter *filter); | 69 | int probe_cache__show_all_caches(struct strfilter *filter); |
70 | bool probe_type_is_available(enum probe_type type); | 70 | bool probe_type_is_available(enum probe_type type); |
71 | bool kretprobe_offset_is_supported(void); | 71 | bool kretprobe_offset_is_supported(void); |
72 | bool uprobe_ref_ctr_is_supported(void); | ||
72 | #else /* ! HAVE_LIBELF_SUPPORT */ | 73 | #else /* ! HAVE_LIBELF_SUPPORT */ |
73 | static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused) | 74 | static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused) |
74 | { | 75 | { |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index ce501ba14b08..50150dfc0cdf 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -340,7 +340,7 @@ static bool is_tracepoint(struct pyrf_event *pevent) | |||
340 | } | 340 | } |
341 | 341 | ||
342 | static PyObject* | 342 | static PyObject* |
343 | tracepoint_field(struct pyrf_event *pe, struct format_field *field) | 343 | tracepoint_field(struct pyrf_event *pe, struct tep_format_field *field) |
344 | { | 344 | { |
345 | struct tep_handle *pevent = field->event->pevent; | 345 | struct tep_handle *pevent = field->event->pevent; |
346 | void *data = pe->sample.raw_data; | 346 | void *data = pe->sample.raw_data; |
@@ -348,28 +348,28 @@ tracepoint_field(struct pyrf_event *pe, struct format_field *field) | |||
348 | unsigned long long val; | 348 | unsigned long long val; |
349 | unsigned int offset, len; | 349 | unsigned int offset, len; |
350 | 350 | ||
351 | if (field->flags & FIELD_IS_ARRAY) { | 351 | if (field->flags & TEP_FIELD_IS_ARRAY) { |
352 | offset = field->offset; | 352 | offset = field->offset; |
353 | len = field->size; | 353 | len = field->size; |
354 | if (field->flags & FIELD_IS_DYNAMIC) { | 354 | if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
355 | val = tep_read_number(pevent, data + offset, len); | 355 | val = tep_read_number(pevent, data + offset, len); |
356 | offset = val; | 356 | offset = val; |
357 | len = offset >> 16; | 357 | len = offset >> 16; |
358 | offset &= 0xffff; | 358 | offset &= 0xffff; |
359 | } | 359 | } |
360 | if (field->flags & FIELD_IS_STRING && | 360 | if (field->flags & TEP_FIELD_IS_STRING && |
361 | is_printable_array(data + offset, len)) { | 361 | is_printable_array(data + offset, len)) { |
362 | ret = _PyUnicode_FromString((char *)data + offset); | 362 | ret = _PyUnicode_FromString((char *)data + offset); |
363 | } else { | 363 | } else { |
364 | ret = PyByteArray_FromStringAndSize((const char *) data + offset, len); | 364 | ret = PyByteArray_FromStringAndSize((const char *) data + offset, len); |
365 | field->flags &= ~FIELD_IS_STRING; | 365 | field->flags &= ~TEP_FIELD_IS_STRING; |
366 | } | 366 | } |
367 | } else { | 367 | } else { |
368 | val = tep_read_number(pevent, data + field->offset, | 368 | val = tep_read_number(pevent, data + field->offset, |
369 | field->size); | 369 | field->size); |
370 | if (field->flags & FIELD_IS_POINTER) | 370 | if (field->flags & TEP_FIELD_IS_POINTER) |
371 | ret = PyLong_FromUnsignedLong((unsigned long) val); | 371 | ret = PyLong_FromUnsignedLong((unsigned long) val); |
372 | else if (field->flags & FIELD_IS_SIGNED) | 372 | else if (field->flags & TEP_FIELD_IS_SIGNED) |
373 | ret = PyLong_FromLong((long) val); | 373 | ret = PyLong_FromLong((long) val); |
374 | else | 374 | else |
375 | ret = PyLong_FromUnsignedLong((unsigned long) val); | 375 | ret = PyLong_FromUnsignedLong((unsigned long) val); |
@@ -383,10 +383,10 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) | |||
383 | { | 383 | { |
384 | const char *str = _PyUnicode_AsString(PyObject_Str(attr_name)); | 384 | const char *str = _PyUnicode_AsString(PyObject_Str(attr_name)); |
385 | struct perf_evsel *evsel = pevent->evsel; | 385 | struct perf_evsel *evsel = pevent->evsel; |
386 | struct format_field *field; | 386 | struct tep_format_field *field; |
387 | 387 | ||
388 | if (!evsel->tp_format) { | 388 | if (!evsel->tp_format) { |
389 | struct event_format *tp_format; | 389 | struct tep_event_format *tp_format; |
390 | 390 | ||
391 | tp_format = trace_event__tp_format_id(evsel->attr.config); | 391 | tp_format = trace_event__tp_format_id(evsel->attr.config); |
392 | if (!tp_format) | 392 | if (!tp_format) |
@@ -1240,7 +1240,7 @@ static struct { | |||
1240 | static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel, | 1240 | static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel, |
1241 | PyObject *args, PyObject *kwargs) | 1241 | PyObject *args, PyObject *kwargs) |
1242 | { | 1242 | { |
1243 | struct event_format *tp_format; | 1243 | struct tep_event_format *tp_format; |
1244 | static char *kwlist[] = { "sys", "name", NULL }; | 1244 | static char *kwlist[] = { "sys", "name", NULL }; |
1245 | char *sys = NULL; | 1245 | char *sys = NULL; |
1246 | char *name = NULL; | 1246 | char *name = NULL; |
diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index d2c78ffd9fee..a2eeebbfb25f 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c | |||
@@ -147,6 +147,9 @@ | |||
147 | #include <linux/bitops.h> | 147 | #include <linux/bitops.h> |
148 | #include <linux/log2.h> | 148 | #include <linux/log2.h> |
149 | 149 | ||
150 | #include <sys/stat.h> | ||
151 | #include <sys/types.h> | ||
152 | |||
150 | #include "cpumap.h" | 153 | #include "cpumap.h" |
151 | #include "color.h" | 154 | #include "color.h" |
152 | #include "evsel.h" | 155 | #include "evsel.h" |
@@ -159,6 +162,7 @@ | |||
159 | #include "auxtrace.h" | 162 | #include "auxtrace.h" |
160 | #include "s390-cpumsf.h" | 163 | #include "s390-cpumsf.h" |
161 | #include "s390-cpumsf-kernel.h" | 164 | #include "s390-cpumsf-kernel.h" |
165 | #include "config.h" | ||
162 | 166 | ||
163 | struct s390_cpumsf { | 167 | struct s390_cpumsf { |
164 | struct auxtrace auxtrace; | 168 | struct auxtrace auxtrace; |
@@ -170,6 +174,8 @@ struct s390_cpumsf { | |||
170 | u32 pmu_type; | 174 | u32 pmu_type; |
171 | u16 machine_type; | 175 | u16 machine_type; |
172 | bool data_queued; | 176 | bool data_queued; |
177 | bool use_logfile; | ||
178 | char *logdir; | ||
173 | }; | 179 | }; |
174 | 180 | ||
175 | struct s390_cpumsf_queue { | 181 | struct s390_cpumsf_queue { |
@@ -177,6 +183,7 @@ struct s390_cpumsf_queue { | |||
177 | unsigned int queue_nr; | 183 | unsigned int queue_nr; |
178 | struct auxtrace_buffer *buffer; | 184 | struct auxtrace_buffer *buffer; |
179 | int cpu; | 185 | int cpu; |
186 | FILE *logfile; | ||
180 | }; | 187 | }; |
181 | 188 | ||
182 | /* Display s390 CPU measurement facility basic-sampling data entry */ | 189 | /* Display s390 CPU measurement facility basic-sampling data entry */ |
@@ -595,6 +602,12 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq, | |||
595 | buffer->use_size = buffer->size; | 602 | buffer->use_size = buffer->size; |
596 | buffer->use_data = buffer->data; | 603 | buffer->use_data = buffer->data; |
597 | } | 604 | } |
605 | if (sfq->logfile) { /* Write into log file */ | ||
606 | size_t rc = fwrite(buffer->data, buffer->size, 1, | ||
607 | sfq->logfile); | ||
608 | if (rc != 1) | ||
609 | pr_err("Failed to write auxiliary data\n"); | ||
610 | } | ||
598 | } else | 611 | } else |
599 | buffer = sfq->buffer; | 612 | buffer = sfq->buffer; |
600 | 613 | ||
@@ -606,6 +619,13 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq, | |||
606 | return -ENOMEM; | 619 | return -ENOMEM; |
607 | buffer->use_size = buffer->size; | 620 | buffer->use_size = buffer->size; |
608 | buffer->use_data = buffer->data; | 621 | buffer->use_data = buffer->data; |
622 | |||
623 | if (sfq->logfile) { /* Write into log file */ | ||
624 | size_t rc = fwrite(buffer->data, buffer->size, 1, | ||
625 | sfq->logfile); | ||
626 | if (rc != 1) | ||
627 | pr_err("Failed to write auxiliary data\n"); | ||
628 | } | ||
609 | } | 629 | } |
610 | pr_debug4("%s queue_nr:%d buffer:%" PRId64 " offset:%#" PRIx64 " size:%#zx rest:%#zx\n", | 630 | pr_debug4("%s queue_nr:%d buffer:%" PRId64 " offset:%#" PRIx64 " size:%#zx rest:%#zx\n", |
611 | __func__, sfq->queue_nr, buffer->buffer_nr, buffer->offset, | 631 | __func__, sfq->queue_nr, buffer->buffer_nr, buffer->offset, |
@@ -640,6 +660,23 @@ s390_cpumsf_alloc_queue(struct s390_cpumsf *sf, unsigned int queue_nr) | |||
640 | sfq->sf = sf; | 660 | sfq->sf = sf; |
641 | sfq->queue_nr = queue_nr; | 661 | sfq->queue_nr = queue_nr; |
642 | sfq->cpu = -1; | 662 | sfq->cpu = -1; |
663 | if (sf->use_logfile) { | ||
664 | char *name; | ||
665 | int rc; | ||
666 | |||
667 | rc = (sf->logdir) | ||
668 | ? asprintf(&name, "%s/aux.smp.%02x", | ||
669 | sf->logdir, queue_nr) | ||
670 | : asprintf(&name, "aux.smp.%02x", queue_nr); | ||
671 | if (rc > 0) | ||
672 | sfq->logfile = fopen(name, "w"); | ||
673 | if (sfq->logfile == NULL) { | ||
674 | pr_err("Failed to open auxiliary log file %s," | ||
675 | "continue...\n", name); | ||
676 | sf->use_logfile = false; | ||
677 | } | ||
678 | free(name); | ||
679 | } | ||
643 | return sfq; | 680 | return sfq; |
644 | } | 681 | } |
645 | 682 | ||
@@ -850,8 +887,16 @@ static void s390_cpumsf_free_queues(struct perf_session *session) | |||
850 | struct auxtrace_queues *queues = &sf->queues; | 887 | struct auxtrace_queues *queues = &sf->queues; |
851 | unsigned int i; | 888 | unsigned int i; |
852 | 889 | ||
853 | for (i = 0; i < queues->nr_queues; i++) | 890 | for (i = 0; i < queues->nr_queues; i++) { |
891 | struct s390_cpumsf_queue *sfq = (struct s390_cpumsf_queue *) | ||
892 | queues->queue_array[i].priv; | ||
893 | |||
894 | if (sfq != NULL && sfq->logfile) { | ||
895 | fclose(sfq->logfile); | ||
896 | sfq->logfile = NULL; | ||
897 | } | ||
854 | zfree(&queues->queue_array[i].priv); | 898 | zfree(&queues->queue_array[i].priv); |
899 | } | ||
855 | auxtrace_queues__free(queues); | 900 | auxtrace_queues__free(queues); |
856 | } | 901 | } |
857 | 902 | ||
@@ -864,6 +909,7 @@ static void s390_cpumsf_free(struct perf_session *session) | |||
864 | auxtrace_heap__free(&sf->heap); | 909 | auxtrace_heap__free(&sf->heap); |
865 | s390_cpumsf_free_queues(session); | 910 | s390_cpumsf_free_queues(session); |
866 | session->auxtrace = NULL; | 911 | session->auxtrace = NULL; |
912 | free(sf->logdir); | ||
867 | free(sf); | 913 | free(sf); |
868 | } | 914 | } |
869 | 915 | ||
@@ -877,17 +923,55 @@ static int s390_cpumsf_get_type(const char *cpuid) | |||
877 | 923 | ||
878 | /* Check itrace options set on perf report command. | 924 | /* Check itrace options set on perf report command. |
879 | * Return true, if none are set or all options specified can be | 925 | * Return true, if none are set or all options specified can be |
880 | * handled on s390. | 926 | * handled on s390 (currently only option 'd' for logging. |
881 | * Return false otherwise. | 927 | * Return false otherwise. |
882 | */ | 928 | */ |
883 | static bool check_auxtrace_itrace(struct itrace_synth_opts *itops) | 929 | static bool check_auxtrace_itrace(struct itrace_synth_opts *itops) |
884 | { | 930 | { |
931 | bool ison = false; | ||
932 | |||
885 | if (!itops || !itops->set) | 933 | if (!itops || !itops->set) |
886 | return true; | 934 | return true; |
887 | pr_err("No --itrace options supported\n"); | 935 | ison = itops->inject || itops->instructions || itops->branches || |
936 | itops->transactions || itops->ptwrites || | ||
937 | itops->pwr_events || itops->errors || | ||
938 | itops->dont_decode || itops->calls || itops->returns || | ||
939 | itops->callchain || itops->thread_stack || | ||
940 | itops->last_branch; | ||
941 | if (!ison) | ||
942 | return true; | ||
943 | pr_err("Unsupported --itrace options specified\n"); | ||
888 | return false; | 944 | return false; |
889 | } | 945 | } |
890 | 946 | ||
947 | /* Check for AUXTRACE dump directory if it is needed. | ||
948 | * On failure print an error message but continue. | ||
949 | * Return 0 on wrong keyword in config file and 1 otherwise. | ||
950 | */ | ||
951 | static int s390_cpumsf__config(const char *var, const char *value, void *cb) | ||
952 | { | ||
953 | struct s390_cpumsf *sf = cb; | ||
954 | struct stat stbuf; | ||
955 | int rc; | ||
956 | |||
957 | if (strcmp(var, "auxtrace.dumpdir")) | ||
958 | return 0; | ||
959 | sf->logdir = strdup(value); | ||
960 | if (sf->logdir == NULL) { | ||
961 | pr_err("Failed to find auxtrace log directory %s," | ||
962 | " continue with current directory...\n", value); | ||
963 | return 1; | ||
964 | } | ||
965 | rc = stat(sf->logdir, &stbuf); | ||
966 | if (rc == -1 || !S_ISDIR(stbuf.st_mode)) { | ||
967 | pr_err("Missing auxtrace log directory %s," | ||
968 | " continue with current directory...\n", value); | ||
969 | free(sf->logdir); | ||
970 | sf->logdir = NULL; | ||
971 | } | ||
972 | return 1; | ||
973 | } | ||
974 | |||
891 | int s390_cpumsf_process_auxtrace_info(union perf_event *event, | 975 | int s390_cpumsf_process_auxtrace_info(union perf_event *event, |
892 | struct perf_session *session) | 976 | struct perf_session *session) |
893 | { | 977 | { |
@@ -906,6 +990,9 @@ int s390_cpumsf_process_auxtrace_info(union perf_event *event, | |||
906 | err = -EINVAL; | 990 | err = -EINVAL; |
907 | goto err_free; | 991 | goto err_free; |
908 | } | 992 | } |
993 | sf->use_logfile = session->itrace_synth_opts->log; | ||
994 | if (sf->use_logfile) | ||
995 | perf_config(s390_cpumsf__config, sf); | ||
909 | 996 | ||
910 | err = auxtrace_queues__init(&sf->queues); | 997 | err = auxtrace_queues__init(&sf->queues); |
911 | if (err) | 998 | if (err) |
@@ -940,6 +1027,7 @@ err_free_queues: | |||
940 | auxtrace_queues__free(&sf->queues); | 1027 | auxtrace_queues__free(&sf->queues); |
941 | session->auxtrace = NULL; | 1028 | session->auxtrace = NULL; |
942 | err_free: | 1029 | err_free: |
1030 | free(sf->logdir); | ||
943 | free(sf); | 1031 | free(sf); |
944 | return err; | 1032 | return err; |
945 | } | 1033 | } |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 45484f0f7292..89cb887648f9 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -99,7 +99,7 @@ static void define_symbolic_value(const char *ev_name, | |||
99 | LEAVE; | 99 | LEAVE; |
100 | } | 100 | } |
101 | 101 | ||
102 | static void define_symbolic_values(struct print_flag_sym *field, | 102 | static void define_symbolic_values(struct tep_print_flag_sym *field, |
103 | const char *ev_name, | 103 | const char *ev_name, |
104 | const char *field_name) | 104 | const char *field_name) |
105 | { | 105 | { |
@@ -157,7 +157,7 @@ static void define_flag_value(const char *ev_name, | |||
157 | LEAVE; | 157 | LEAVE; |
158 | } | 158 | } |
159 | 159 | ||
160 | static void define_flag_values(struct print_flag_sym *field, | 160 | static void define_flag_values(struct tep_print_flag_sym *field, |
161 | const char *ev_name, | 161 | const char *ev_name, |
162 | const char *field_name) | 162 | const char *field_name) |
163 | { | 163 | { |
@@ -189,62 +189,62 @@ static void define_flag_field(const char *ev_name, | |||
189 | LEAVE; | 189 | LEAVE; |
190 | } | 190 | } |
191 | 191 | ||
192 | static void define_event_symbols(struct event_format *event, | 192 | static void define_event_symbols(struct tep_event_format *event, |
193 | const char *ev_name, | 193 | const char *ev_name, |
194 | struct print_arg *args) | 194 | struct tep_print_arg *args) |
195 | { | 195 | { |
196 | if (args == NULL) | 196 | if (args == NULL) |
197 | return; | 197 | return; |
198 | 198 | ||
199 | switch (args->type) { | 199 | switch (args->type) { |
200 | case PRINT_NULL: | 200 | case TEP_PRINT_NULL: |
201 | break; | 201 | break; |
202 | case PRINT_ATOM: | 202 | case TEP_PRINT_ATOM: |
203 | define_flag_value(ev_name, cur_field_name, "0", | 203 | define_flag_value(ev_name, cur_field_name, "0", |
204 | args->atom.atom); | 204 | args->atom.atom); |
205 | zero_flag_atom = 0; | 205 | zero_flag_atom = 0; |
206 | break; | 206 | break; |
207 | case PRINT_FIELD: | 207 | case TEP_PRINT_FIELD: |
208 | free(cur_field_name); | 208 | free(cur_field_name); |
209 | cur_field_name = strdup(args->field.name); | 209 | cur_field_name = strdup(args->field.name); |
210 | break; | 210 | break; |
211 | case PRINT_FLAGS: | 211 | case TEP_PRINT_FLAGS: |
212 | define_event_symbols(event, ev_name, args->flags.field); | 212 | define_event_symbols(event, ev_name, args->flags.field); |
213 | define_flag_field(ev_name, cur_field_name, args->flags.delim); | 213 | define_flag_field(ev_name, cur_field_name, args->flags.delim); |
214 | define_flag_values(args->flags.flags, ev_name, cur_field_name); | 214 | define_flag_values(args->flags.flags, ev_name, cur_field_name); |
215 | break; | 215 | break; |
216 | case PRINT_SYMBOL: | 216 | case TEP_PRINT_SYMBOL: |
217 | define_event_symbols(event, ev_name, args->symbol.field); | 217 | define_event_symbols(event, ev_name, args->symbol.field); |
218 | define_symbolic_field(ev_name, cur_field_name); | 218 | define_symbolic_field(ev_name, cur_field_name); |
219 | define_symbolic_values(args->symbol.symbols, ev_name, | 219 | define_symbolic_values(args->symbol.symbols, ev_name, |
220 | cur_field_name); | 220 | cur_field_name); |
221 | break; | 221 | break; |
222 | case PRINT_HEX: | 222 | case TEP_PRINT_HEX: |
223 | case PRINT_HEX_STR: | 223 | case TEP_PRINT_HEX_STR: |
224 | define_event_symbols(event, ev_name, args->hex.field); | 224 | define_event_symbols(event, ev_name, args->hex.field); |
225 | define_event_symbols(event, ev_name, args->hex.size); | 225 | define_event_symbols(event, ev_name, args->hex.size); |
226 | break; | 226 | break; |
227 | case PRINT_INT_ARRAY: | 227 | case TEP_PRINT_INT_ARRAY: |
228 | define_event_symbols(event, ev_name, args->int_array.field); | 228 | define_event_symbols(event, ev_name, args->int_array.field); |
229 | define_event_symbols(event, ev_name, args->int_array.count); | 229 | define_event_symbols(event, ev_name, args->int_array.count); |
230 | define_event_symbols(event, ev_name, args->int_array.el_size); | 230 | define_event_symbols(event, ev_name, args->int_array.el_size); |
231 | break; | 231 | break; |
232 | case PRINT_BSTRING: | 232 | case TEP_PRINT_BSTRING: |
233 | case PRINT_DYNAMIC_ARRAY: | 233 | case TEP_PRINT_DYNAMIC_ARRAY: |
234 | case PRINT_DYNAMIC_ARRAY_LEN: | 234 | case TEP_PRINT_DYNAMIC_ARRAY_LEN: |
235 | case PRINT_STRING: | 235 | case TEP_PRINT_STRING: |
236 | case PRINT_BITMASK: | 236 | case TEP_PRINT_BITMASK: |
237 | break; | 237 | break; |
238 | case PRINT_TYPE: | 238 | case TEP_PRINT_TYPE: |
239 | define_event_symbols(event, ev_name, args->typecast.item); | 239 | define_event_symbols(event, ev_name, args->typecast.item); |
240 | break; | 240 | break; |
241 | case PRINT_OP: | 241 | case TEP_PRINT_OP: |
242 | if (strcmp(args->op.op, ":") == 0) | 242 | if (strcmp(args->op.op, ":") == 0) |
243 | zero_flag_atom = 1; | 243 | zero_flag_atom = 1; |
244 | define_event_symbols(event, ev_name, args->op.left); | 244 | define_event_symbols(event, ev_name, args->op.left); |
245 | define_event_symbols(event, ev_name, args->op.right); | 245 | define_event_symbols(event, ev_name, args->op.right); |
246 | break; | 246 | break; |
247 | case PRINT_FUNC: | 247 | case TEP_PRINT_FUNC: |
248 | default: | 248 | default: |
249 | pr_err("Unsupported print arg type\n"); | 249 | pr_err("Unsupported print arg type\n"); |
250 | /* we should warn... */ | 250 | /* we should warn... */ |
@@ -338,8 +338,8 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
338 | struct addr_location *al) | 338 | struct addr_location *al) |
339 | { | 339 | { |
340 | struct thread *thread = al->thread; | 340 | struct thread *thread = al->thread; |
341 | struct event_format *event = evsel->tp_format; | 341 | struct tep_event_format *event = evsel->tp_format; |
342 | struct format_field *field; | 342 | struct tep_format_field *field; |
343 | static char handler[256]; | 343 | static char handler[256]; |
344 | unsigned long long val; | 344 | unsigned long long val; |
345 | unsigned long s, ns; | 345 | unsigned long s, ns; |
@@ -388,9 +388,9 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
388 | /* common fields other than pid can be accessed via xsub fns */ | 388 | /* common fields other than pid can be accessed via xsub fns */ |
389 | 389 | ||
390 | for (field = event->format.fields; field; field = field->next) { | 390 | for (field = event->format.fields; field; field = field->next) { |
391 | if (field->flags & FIELD_IS_STRING) { | 391 | if (field->flags & TEP_FIELD_IS_STRING) { |
392 | int offset; | 392 | int offset; |
393 | if (field->flags & FIELD_IS_DYNAMIC) { | 393 | if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
394 | offset = *(int *)(data + field->offset); | 394 | offset = *(int *)(data + field->offset); |
395 | offset &= 0xffff; | 395 | offset &= 0xffff; |
396 | } else | 396 | } else |
@@ -399,7 +399,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
399 | } else { /* FIELD_IS_NUMERIC */ | 399 | } else { /* FIELD_IS_NUMERIC */ |
400 | val = read_size(event, data + field->offset, | 400 | val = read_size(event, data + field->offset, |
401 | field->size); | 401 | field->size); |
402 | if (field->flags & FIELD_IS_SIGNED) { | 402 | if (field->flags & TEP_FIELD_IS_SIGNED) { |
403 | XPUSHs(sv_2mortal(newSViv(val))); | 403 | XPUSHs(sv_2mortal(newSViv(val))); |
404 | } else { | 404 | } else { |
405 | XPUSHs(sv_2mortal(newSVuv(val))); | 405 | XPUSHs(sv_2mortal(newSVuv(val))); |
@@ -537,8 +537,8 @@ static int perl_stop_script(void) | |||
537 | 537 | ||
538 | static int perl_generate_script(struct tep_handle *pevent, const char *outfile) | 538 | static int perl_generate_script(struct tep_handle *pevent, const char *outfile) |
539 | { | 539 | { |
540 | struct event_format *event = NULL; | 540 | struct tep_event_format *event = NULL; |
541 | struct format_field *f; | 541 | struct tep_format_field *f; |
542 | char fname[PATH_MAX]; | 542 | char fname[PATH_MAX]; |
543 | int not_first, count; | 543 | int not_first, count; |
544 | FILE *ofp; | 544 | FILE *ofp; |
@@ -646,11 +646,11 @@ sub print_backtrace\n\ | |||
646 | count++; | 646 | count++; |
647 | 647 | ||
648 | fprintf(ofp, "%s=", f->name); | 648 | fprintf(ofp, "%s=", f->name); |
649 | if (f->flags & FIELD_IS_STRING || | 649 | if (f->flags & TEP_FIELD_IS_STRING || |
650 | f->flags & FIELD_IS_FLAG || | 650 | f->flags & TEP_FIELD_IS_FLAG || |
651 | f->flags & FIELD_IS_SYMBOLIC) | 651 | f->flags & TEP_FIELD_IS_SYMBOLIC) |
652 | fprintf(ofp, "%%s"); | 652 | fprintf(ofp, "%%s"); |
653 | else if (f->flags & FIELD_IS_SIGNED) | 653 | else if (f->flags & TEP_FIELD_IS_SIGNED) |
654 | fprintf(ofp, "%%d"); | 654 | fprintf(ofp, "%%d"); |
655 | else | 655 | else |
656 | fprintf(ofp, "%%u"); | 656 | fprintf(ofp, "%%u"); |
@@ -668,7 +668,7 @@ sub print_backtrace\n\ | |||
668 | if (++count % 5 == 0) | 668 | if (++count % 5 == 0) |
669 | fprintf(ofp, "\n\t "); | 669 | fprintf(ofp, "\n\t "); |
670 | 670 | ||
671 | if (f->flags & FIELD_IS_FLAG) { | 671 | if (f->flags & TEP_FIELD_IS_FLAG) { |
672 | if ((count - 1) % 5 != 0) { | 672 | if ((count - 1) % 5 != 0) { |
673 | fprintf(ofp, "\n\t "); | 673 | fprintf(ofp, "\n\t "); |
674 | count = 4; | 674 | count = 4; |
@@ -678,7 +678,7 @@ sub print_backtrace\n\ | |||
678 | event->name); | 678 | event->name); |
679 | fprintf(ofp, "\"%s\", $%s)", f->name, | 679 | fprintf(ofp, "\"%s\", $%s)", f->name, |
680 | f->name); | 680 | f->name); |
681 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | 681 | } else if (f->flags & TEP_FIELD_IS_SYMBOLIC) { |
682 | if ((count - 1) % 5 != 0) { | 682 | if ((count - 1) % 5 != 0) { |
683 | fprintf(ofp, "\n\t "); | 683 | fprintf(ofp, "\n\t "); |
684 | count = 4; | 684 | count = 4; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index dfc6093f118c..69aa93d4ee99 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -193,7 +193,7 @@ static void try_call_object(const char *handler_name, PyObject *args) | |||
193 | call_object(handler, args, handler_name); | 193 | call_object(handler, args, handler_name); |
194 | } | 194 | } |
195 | 195 | ||
196 | static void define_value(enum print_arg_type field_type, | 196 | static void define_value(enum tep_print_arg_type field_type, |
197 | const char *ev_name, | 197 | const char *ev_name, |
198 | const char *field_name, | 198 | const char *field_name, |
199 | const char *field_value, | 199 | const char *field_value, |
@@ -204,7 +204,7 @@ static void define_value(enum print_arg_type field_type, | |||
204 | unsigned long long value; | 204 | unsigned long long value; |
205 | unsigned n = 0; | 205 | unsigned n = 0; |
206 | 206 | ||
207 | if (field_type == PRINT_SYMBOL) | 207 | if (field_type == TEP_PRINT_SYMBOL) |
208 | handler_name = "define_symbolic_value"; | 208 | handler_name = "define_symbolic_value"; |
209 | 209 | ||
210 | t = PyTuple_New(4); | 210 | t = PyTuple_New(4); |
@@ -223,8 +223,8 @@ static void define_value(enum print_arg_type field_type, | |||
223 | Py_DECREF(t); | 223 | Py_DECREF(t); |
224 | } | 224 | } |
225 | 225 | ||
226 | static void define_values(enum print_arg_type field_type, | 226 | static void define_values(enum tep_print_arg_type field_type, |
227 | struct print_flag_sym *field, | 227 | struct tep_print_flag_sym *field, |
228 | const char *ev_name, | 228 | const char *ev_name, |
229 | const char *field_name) | 229 | const char *field_name) |
230 | { | 230 | { |
@@ -235,7 +235,7 @@ static void define_values(enum print_arg_type field_type, | |||
235 | define_values(field_type, field->next, ev_name, field_name); | 235 | define_values(field_type, field->next, ev_name, field_name); |
236 | } | 236 | } |
237 | 237 | ||
238 | static void define_field(enum print_arg_type field_type, | 238 | static void define_field(enum tep_print_arg_type field_type, |
239 | const char *ev_name, | 239 | const char *ev_name, |
240 | const char *field_name, | 240 | const char *field_name, |
241 | const char *delim) | 241 | const char *delim) |
@@ -244,10 +244,10 @@ static void define_field(enum print_arg_type field_type, | |||
244 | PyObject *t; | 244 | PyObject *t; |
245 | unsigned n = 0; | 245 | unsigned n = 0; |
246 | 246 | ||
247 | if (field_type == PRINT_SYMBOL) | 247 | if (field_type == TEP_PRINT_SYMBOL) |
248 | handler_name = "define_symbolic_field"; | 248 | handler_name = "define_symbolic_field"; |
249 | 249 | ||
250 | if (field_type == PRINT_FLAGS) | 250 | if (field_type == TEP_PRINT_FLAGS) |
251 | t = PyTuple_New(3); | 251 | t = PyTuple_New(3); |
252 | else | 252 | else |
253 | t = PyTuple_New(2); | 253 | t = PyTuple_New(2); |
@@ -256,7 +256,7 @@ static void define_field(enum print_arg_type field_type, | |||
256 | 256 | ||
257 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(ev_name)); | 257 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(ev_name)); |
258 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(field_name)); | 258 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(field_name)); |
259 | if (field_type == PRINT_FLAGS) | 259 | if (field_type == TEP_PRINT_FLAGS) |
260 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(delim)); | 260 | PyTuple_SetItem(t, n++, _PyUnicode_FromString(delim)); |
261 | 261 | ||
262 | try_call_object(handler_name, t); | 262 | try_call_object(handler_name, t); |
@@ -264,54 +264,54 @@ static void define_field(enum print_arg_type field_type, | |||
264 | Py_DECREF(t); | 264 | Py_DECREF(t); |
265 | } | 265 | } |
266 | 266 | ||
267 | static void define_event_symbols(struct event_format *event, | 267 | static void define_event_symbols(struct tep_event_format *event, |
268 | const char *ev_name, | 268 | const char *ev_name, |
269 | struct print_arg *args) | 269 | struct tep_print_arg *args) |
270 | { | 270 | { |
271 | if (args == NULL) | 271 | if (args == NULL) |
272 | return; | 272 | return; |
273 | 273 | ||
274 | switch (args->type) { | 274 | switch (args->type) { |
275 | case PRINT_NULL: | 275 | case TEP_PRINT_NULL: |
276 | break; | 276 | break; |
277 | case PRINT_ATOM: | 277 | case TEP_PRINT_ATOM: |
278 | define_value(PRINT_FLAGS, ev_name, cur_field_name, "0", | 278 | define_value(TEP_PRINT_FLAGS, ev_name, cur_field_name, "0", |
279 | args->atom.atom); | 279 | args->atom.atom); |
280 | zero_flag_atom = 0; | 280 | zero_flag_atom = 0; |
281 | break; | 281 | break; |
282 | case PRINT_FIELD: | 282 | case TEP_PRINT_FIELD: |
283 | free(cur_field_name); | 283 | free(cur_field_name); |
284 | cur_field_name = strdup(args->field.name); | 284 | cur_field_name = strdup(args->field.name); |
285 | break; | 285 | break; |
286 | case PRINT_FLAGS: | 286 | case TEP_PRINT_FLAGS: |
287 | define_event_symbols(event, ev_name, args->flags.field); | 287 | define_event_symbols(event, ev_name, args->flags.field); |
288 | define_field(PRINT_FLAGS, ev_name, cur_field_name, | 288 | define_field(TEP_PRINT_FLAGS, ev_name, cur_field_name, |
289 | args->flags.delim); | 289 | args->flags.delim); |
290 | define_values(PRINT_FLAGS, args->flags.flags, ev_name, | 290 | define_values(TEP_PRINT_FLAGS, args->flags.flags, ev_name, |
291 | cur_field_name); | 291 | cur_field_name); |
292 | break; | 292 | break; |
293 | case PRINT_SYMBOL: | 293 | case TEP_PRINT_SYMBOL: |
294 | define_event_symbols(event, ev_name, args->symbol.field); | 294 | define_event_symbols(event, ev_name, args->symbol.field); |
295 | define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL); | 295 | define_field(TEP_PRINT_SYMBOL, ev_name, cur_field_name, NULL); |
296 | define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, | 296 | define_values(TEP_PRINT_SYMBOL, args->symbol.symbols, ev_name, |
297 | cur_field_name); | 297 | cur_field_name); |
298 | break; | 298 | break; |
299 | case PRINT_HEX: | 299 | case TEP_PRINT_HEX: |
300 | case PRINT_HEX_STR: | 300 | case TEP_PRINT_HEX_STR: |
301 | define_event_symbols(event, ev_name, args->hex.field); | 301 | define_event_symbols(event, ev_name, args->hex.field); |
302 | define_event_symbols(event, ev_name, args->hex.size); | 302 | define_event_symbols(event, ev_name, args->hex.size); |
303 | break; | 303 | break; |
304 | case PRINT_INT_ARRAY: | 304 | case TEP_PRINT_INT_ARRAY: |
305 | define_event_symbols(event, ev_name, args->int_array.field); | 305 | define_event_symbols(event, ev_name, args->int_array.field); |
306 | define_event_symbols(event, ev_name, args->int_array.count); | 306 | define_event_symbols(event, ev_name, args->int_array.count); |
307 | define_event_symbols(event, ev_name, args->int_array.el_size); | 307 | define_event_symbols(event, ev_name, args->int_array.el_size); |
308 | break; | 308 | break; |
309 | case PRINT_STRING: | 309 | case TEP_PRINT_STRING: |
310 | break; | 310 | break; |
311 | case PRINT_TYPE: | 311 | case TEP_PRINT_TYPE: |
312 | define_event_symbols(event, ev_name, args->typecast.item); | 312 | define_event_symbols(event, ev_name, args->typecast.item); |
313 | break; | 313 | break; |
314 | case PRINT_OP: | 314 | case TEP_PRINT_OP: |
315 | if (strcmp(args->op.op, ":") == 0) | 315 | if (strcmp(args->op.op, ":") == 0) |
316 | zero_flag_atom = 1; | 316 | zero_flag_atom = 1; |
317 | define_event_symbols(event, ev_name, args->op.left); | 317 | define_event_symbols(event, ev_name, args->op.left); |
@@ -319,11 +319,11 @@ static void define_event_symbols(struct event_format *event, | |||
319 | break; | 319 | break; |
320 | default: | 320 | default: |
321 | /* gcc warns for these? */ | 321 | /* gcc warns for these? */ |
322 | case PRINT_BSTRING: | 322 | case TEP_PRINT_BSTRING: |
323 | case PRINT_DYNAMIC_ARRAY: | 323 | case TEP_PRINT_DYNAMIC_ARRAY: |
324 | case PRINT_DYNAMIC_ARRAY_LEN: | 324 | case TEP_PRINT_DYNAMIC_ARRAY_LEN: |
325 | case PRINT_FUNC: | 325 | case TEP_PRINT_FUNC: |
326 | case PRINT_BITMASK: | 326 | case TEP_PRINT_BITMASK: |
327 | /* we should warn... */ | 327 | /* we should warn... */ |
328 | return; | 328 | return; |
329 | } | 329 | } |
@@ -332,10 +332,10 @@ static void define_event_symbols(struct event_format *event, | |||
332 | define_event_symbols(event, ev_name, args->next); | 332 | define_event_symbols(event, ev_name, args->next); |
333 | } | 333 | } |
334 | 334 | ||
335 | static PyObject *get_field_numeric_entry(struct event_format *event, | 335 | static PyObject *get_field_numeric_entry(struct tep_event_format *event, |
336 | struct format_field *field, void *data) | 336 | struct tep_format_field *field, void *data) |
337 | { | 337 | { |
338 | bool is_array = field->flags & FIELD_IS_ARRAY; | 338 | bool is_array = field->flags & TEP_FIELD_IS_ARRAY; |
339 | PyObject *obj = NULL, *list = NULL; | 339 | PyObject *obj = NULL, *list = NULL; |
340 | unsigned long long val; | 340 | unsigned long long val; |
341 | unsigned int item_size, n_items, i; | 341 | unsigned int item_size, n_items, i; |
@@ -353,7 +353,7 @@ static PyObject *get_field_numeric_entry(struct event_format *event, | |||
353 | 353 | ||
354 | val = read_size(event, data + field->offset + i * item_size, | 354 | val = read_size(event, data + field->offset + i * item_size, |
355 | item_size); | 355 | item_size); |
356 | if (field->flags & FIELD_IS_SIGNED) { | 356 | if (field->flags & TEP_FIELD_IS_SIGNED) { |
357 | if ((long long)val >= LONG_MIN && | 357 | if ((long long)val >= LONG_MIN && |
358 | (long long)val <= LONG_MAX) | 358 | (long long)val <= LONG_MAX) |
359 | obj = _PyLong_FromLong(val); | 359 | obj = _PyLong_FromLong(val); |
@@ -790,11 +790,11 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
790 | struct perf_evsel *evsel, | 790 | struct perf_evsel *evsel, |
791 | struct addr_location *al) | 791 | struct addr_location *al) |
792 | { | 792 | { |
793 | struct event_format *event = evsel->tp_format; | 793 | struct tep_event_format *event = evsel->tp_format; |
794 | PyObject *handler, *context, *t, *obj = NULL, *callchain; | 794 | PyObject *handler, *context, *t, *obj = NULL, *callchain; |
795 | PyObject *dict = NULL, *all_entries_dict = NULL; | 795 | PyObject *dict = NULL, *all_entries_dict = NULL; |
796 | static char handler_name[256]; | 796 | static char handler_name[256]; |
797 | struct format_field *field; | 797 | struct tep_format_field *field; |
798 | unsigned long s, ns; | 798 | unsigned long s, ns; |
799 | unsigned n = 0; | 799 | unsigned n = 0; |
800 | int pid; | 800 | int pid; |
@@ -867,22 +867,22 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
867 | unsigned int offset, len; | 867 | unsigned int offset, len; |
868 | unsigned long long val; | 868 | unsigned long long val; |
869 | 869 | ||
870 | if (field->flags & FIELD_IS_ARRAY) { | 870 | if (field->flags & TEP_FIELD_IS_ARRAY) { |
871 | offset = field->offset; | 871 | offset = field->offset; |
872 | len = field->size; | 872 | len = field->size; |
873 | if (field->flags & FIELD_IS_DYNAMIC) { | 873 | if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
874 | val = tep_read_number(scripting_context->pevent, | 874 | val = tep_read_number(scripting_context->pevent, |
875 | data + offset, len); | 875 | data + offset, len); |
876 | offset = val; | 876 | offset = val; |
877 | len = offset >> 16; | 877 | len = offset >> 16; |
878 | offset &= 0xffff; | 878 | offset &= 0xffff; |
879 | } | 879 | } |
880 | if (field->flags & FIELD_IS_STRING && | 880 | if (field->flags & TEP_FIELD_IS_STRING && |
881 | is_printable_array(data + offset, len)) { | 881 | is_printable_array(data + offset, len)) { |
882 | obj = _PyUnicode_FromString((char *) data + offset); | 882 | obj = _PyUnicode_FromString((char *) data + offset); |
883 | } else { | 883 | } else { |
884 | obj = PyByteArray_FromStringAndSize((const char *) data + offset, len); | 884 | obj = PyByteArray_FromStringAndSize((const char *) data + offset, len); |
885 | field->flags &= ~FIELD_IS_STRING; | 885 | field->flags &= ~TEP_FIELD_IS_STRING; |
886 | } | 886 | } |
887 | } else { /* FIELD_IS_NUMERIC */ | 887 | } else { /* FIELD_IS_NUMERIC */ |
888 | obj = get_field_numeric_entry(event, field, data); | 888 | obj = get_field_numeric_entry(event, field, data); |
@@ -1590,8 +1590,8 @@ static int python_stop_script(void) | |||
1590 | 1590 | ||
1591 | static int python_generate_script(struct tep_handle *pevent, const char *outfile) | 1591 | static int python_generate_script(struct tep_handle *pevent, const char *outfile) |
1592 | { | 1592 | { |
1593 | struct event_format *event = NULL; | 1593 | struct tep_event_format *event = NULL; |
1594 | struct format_field *f; | 1594 | struct tep_format_field *f; |
1595 | char fname[PATH_MAX]; | 1595 | char fname[PATH_MAX]; |
1596 | int not_first, count; | 1596 | int not_first, count; |
1597 | FILE *ofp; | 1597 | FILE *ofp; |
@@ -1686,12 +1686,12 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile | |||
1686 | count++; | 1686 | count++; |
1687 | 1687 | ||
1688 | fprintf(ofp, "%s=", f->name); | 1688 | fprintf(ofp, "%s=", f->name); |
1689 | if (f->flags & FIELD_IS_STRING || | 1689 | if (f->flags & TEP_FIELD_IS_STRING || |
1690 | f->flags & FIELD_IS_FLAG || | 1690 | f->flags & TEP_FIELD_IS_FLAG || |
1691 | f->flags & FIELD_IS_ARRAY || | 1691 | f->flags & TEP_FIELD_IS_ARRAY || |
1692 | f->flags & FIELD_IS_SYMBOLIC) | 1692 | f->flags & TEP_FIELD_IS_SYMBOLIC) |
1693 | fprintf(ofp, "%%s"); | 1693 | fprintf(ofp, "%%s"); |
1694 | else if (f->flags & FIELD_IS_SIGNED) | 1694 | else if (f->flags & TEP_FIELD_IS_SIGNED) |
1695 | fprintf(ofp, "%%d"); | 1695 | fprintf(ofp, "%%d"); |
1696 | else | 1696 | else |
1697 | fprintf(ofp, "%%u"); | 1697 | fprintf(ofp, "%%u"); |
@@ -1709,7 +1709,7 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile | |||
1709 | if (++count % 5 == 0) | 1709 | if (++count % 5 == 0) |
1710 | fprintf(ofp, "\n\t\t"); | 1710 | fprintf(ofp, "\n\t\t"); |
1711 | 1711 | ||
1712 | if (f->flags & FIELD_IS_FLAG) { | 1712 | if (f->flags & TEP_FIELD_IS_FLAG) { |
1713 | if ((count - 1) % 5 != 0) { | 1713 | if ((count - 1) % 5 != 0) { |
1714 | fprintf(ofp, "\n\t\t"); | 1714 | fprintf(ofp, "\n\t\t"); |
1715 | count = 4; | 1715 | count = 4; |
@@ -1719,7 +1719,7 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile | |||
1719 | event->name); | 1719 | event->name); |
1720 | fprintf(ofp, "\"%s\", %s)", f->name, | 1720 | fprintf(ofp, "\"%s\", %s)", f->name, |
1721 | f->name); | 1721 | f->name); |
1722 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | 1722 | } else if (f->flags & TEP_FIELD_IS_SYMBOLIC) { |
1723 | if ((count - 1) % 5 != 0) { | 1723 | if ((count - 1) % 5 != 0) { |
1724 | fprintf(ofp, "\n\t\t"); | 1724 | fprintf(ofp, "\n\t\t"); |
1725 | count = 4; | 1725 | count = 4; |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8b9369303561..7d2c8ce6cfad 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -199,12 +199,10 @@ void perf_session__delete(struct perf_session *session) | |||
199 | free(session); | 199 | free(session); |
200 | } | 200 | } |
201 | 201 | ||
202 | static int process_event_synth_tracing_data_stub(struct perf_tool *tool | 202 | static int process_event_synth_tracing_data_stub(struct perf_session *session |
203 | __maybe_unused, | 203 | __maybe_unused, |
204 | union perf_event *event | 204 | union perf_event *event |
205 | __maybe_unused, | 205 | __maybe_unused) |
206 | struct perf_session *session | ||
207 | __maybe_unused) | ||
208 | { | 206 | { |
209 | dump_printf(": unhandled!\n"); | 207 | dump_printf(": unhandled!\n"); |
210 | return 0; | 208 | return 0; |
@@ -277,10 +275,8 @@ static int skipn(int fd, off_t n) | |||
277 | return 0; | 275 | return 0; |
278 | } | 276 | } |
279 | 277 | ||
280 | static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, | 278 | static s64 process_event_auxtrace_stub(struct perf_session *session __maybe_unused, |
281 | union perf_event *event, | 279 | union perf_event *event) |
282 | struct perf_session *session | ||
283 | __maybe_unused) | ||
284 | { | 280 | { |
285 | dump_printf(": unhandled!\n"); | 281 | dump_printf(": unhandled!\n"); |
286 | if (perf_data__is_pipe(session->data)) | 282 | if (perf_data__is_pipe(session->data)) |
@@ -288,9 +284,8 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, | |||
288 | return event->auxtrace.size; | 284 | return event->auxtrace.size; |
289 | } | 285 | } |
290 | 286 | ||
291 | static int process_event_op2_stub(struct perf_tool *tool __maybe_unused, | 287 | static int process_event_op2_stub(struct perf_session *session __maybe_unused, |
292 | union perf_event *event __maybe_unused, | 288 | union perf_event *event __maybe_unused) |
293 | struct perf_session *session __maybe_unused) | ||
294 | { | 289 | { |
295 | dump_printf(": unhandled!\n"); | 290 | dump_printf(": unhandled!\n"); |
296 | return 0; | 291 | return 0; |
@@ -298,9 +293,8 @@ static int process_event_op2_stub(struct perf_tool *tool __maybe_unused, | |||
298 | 293 | ||
299 | 294 | ||
300 | static | 295 | static |
301 | int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, | 296 | int process_event_thread_map_stub(struct perf_session *session __maybe_unused, |
302 | union perf_event *event __maybe_unused, | 297 | union perf_event *event __maybe_unused) |
303 | struct perf_session *session __maybe_unused) | ||
304 | { | 298 | { |
305 | if (dump_trace) | 299 | if (dump_trace) |
306 | perf_event__fprintf_thread_map(event, stdout); | 300 | perf_event__fprintf_thread_map(event, stdout); |
@@ -310,9 +304,8 @@ int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, | |||
310 | } | 304 | } |
311 | 305 | ||
312 | static | 306 | static |
313 | int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, | 307 | int process_event_cpu_map_stub(struct perf_session *session __maybe_unused, |
314 | union perf_event *event __maybe_unused, | 308 | union perf_event *event __maybe_unused) |
315 | struct perf_session *session __maybe_unused) | ||
316 | { | 309 | { |
317 | if (dump_trace) | 310 | if (dump_trace) |
318 | perf_event__fprintf_cpu_map(event, stdout); | 311 | perf_event__fprintf_cpu_map(event, stdout); |
@@ -322,9 +315,8 @@ int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, | |||
322 | } | 315 | } |
323 | 316 | ||
324 | static | 317 | static |
325 | int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, | 318 | int process_event_stat_config_stub(struct perf_session *session __maybe_unused, |
326 | union perf_event *event __maybe_unused, | 319 | union perf_event *event __maybe_unused) |
327 | struct perf_session *session __maybe_unused) | ||
328 | { | 320 | { |
329 | if (dump_trace) | 321 | if (dump_trace) |
330 | perf_event__fprintf_stat_config(event, stdout); | 322 | perf_event__fprintf_stat_config(event, stdout); |
@@ -333,10 +325,8 @@ int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, | |||
333 | return 0; | 325 | return 0; |
334 | } | 326 | } |
335 | 327 | ||
336 | static int process_stat_stub(struct perf_tool *tool __maybe_unused, | 328 | static int process_stat_stub(struct perf_session *perf_session __maybe_unused, |
337 | union perf_event *event __maybe_unused, | 329 | union perf_event *event) |
338 | struct perf_session *perf_session | ||
339 | __maybe_unused) | ||
340 | { | 330 | { |
341 | if (dump_trace) | 331 | if (dump_trace) |
342 | perf_event__fprintf_stat(event, stdout); | 332 | perf_event__fprintf_stat(event, stdout); |
@@ -345,10 +335,8 @@ static int process_stat_stub(struct perf_tool *tool __maybe_unused, | |||
345 | return 0; | 335 | return 0; |
346 | } | 336 | } |
347 | 337 | ||
348 | static int process_stat_round_stub(struct perf_tool *tool __maybe_unused, | 338 | static int process_stat_round_stub(struct perf_session *perf_session __maybe_unused, |
349 | union perf_event *event __maybe_unused, | 339 | union perf_event *event) |
350 | struct perf_session *perf_session | ||
351 | __maybe_unused) | ||
352 | { | 340 | { |
353 | if (dump_trace) | 341 | if (dump_trace) |
354 | perf_event__fprintf_stat_round(event, stdout); | 342 | perf_event__fprintf_stat_round(event, stdout); |
@@ -1374,37 +1362,37 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1374 | case PERF_RECORD_HEADER_TRACING_DATA: | 1362 | case PERF_RECORD_HEADER_TRACING_DATA: |
1375 | /* setup for reading amidst mmap */ | 1363 | /* setup for reading amidst mmap */ |
1376 | lseek(fd, file_offset, SEEK_SET); | 1364 | lseek(fd, file_offset, SEEK_SET); |
1377 | return tool->tracing_data(tool, event, session); | 1365 | return tool->tracing_data(session, event); |
1378 | case PERF_RECORD_HEADER_BUILD_ID: | 1366 | case PERF_RECORD_HEADER_BUILD_ID: |
1379 | return tool->build_id(tool, event, session); | 1367 | return tool->build_id(session, event); |
1380 | case PERF_RECORD_FINISHED_ROUND: | 1368 | case PERF_RECORD_FINISHED_ROUND: |
1381 | return tool->finished_round(tool, event, oe); | 1369 | return tool->finished_round(tool, event, oe); |
1382 | case PERF_RECORD_ID_INDEX: | 1370 | case PERF_RECORD_ID_INDEX: |
1383 | return tool->id_index(tool, event, session); | 1371 | return tool->id_index(session, event); |
1384 | case PERF_RECORD_AUXTRACE_INFO: | 1372 | case PERF_RECORD_AUXTRACE_INFO: |
1385 | return tool->auxtrace_info(tool, event, session); | 1373 | return tool->auxtrace_info(session, event); |
1386 | case PERF_RECORD_AUXTRACE: | 1374 | case PERF_RECORD_AUXTRACE: |
1387 | /* setup for reading amidst mmap */ | 1375 | /* setup for reading amidst mmap */ |
1388 | lseek(fd, file_offset + event->header.size, SEEK_SET); | 1376 | lseek(fd, file_offset + event->header.size, SEEK_SET); |
1389 | return tool->auxtrace(tool, event, session); | 1377 | return tool->auxtrace(session, event); |
1390 | case PERF_RECORD_AUXTRACE_ERROR: | 1378 | case PERF_RECORD_AUXTRACE_ERROR: |
1391 | perf_session__auxtrace_error_inc(session, event); | 1379 | perf_session__auxtrace_error_inc(session, event); |
1392 | return tool->auxtrace_error(tool, event, session); | 1380 | return tool->auxtrace_error(session, event); |
1393 | case PERF_RECORD_THREAD_MAP: | 1381 | case PERF_RECORD_THREAD_MAP: |
1394 | return tool->thread_map(tool, event, session); | 1382 | return tool->thread_map(session, event); |
1395 | case PERF_RECORD_CPU_MAP: | 1383 | case PERF_RECORD_CPU_MAP: |
1396 | return tool->cpu_map(tool, event, session); | 1384 | return tool->cpu_map(session, event); |
1397 | case PERF_RECORD_STAT_CONFIG: | 1385 | case PERF_RECORD_STAT_CONFIG: |
1398 | return tool->stat_config(tool, event, session); | 1386 | return tool->stat_config(session, event); |
1399 | case PERF_RECORD_STAT: | 1387 | case PERF_RECORD_STAT: |
1400 | return tool->stat(tool, event, session); | 1388 | return tool->stat(session, event); |
1401 | case PERF_RECORD_STAT_ROUND: | 1389 | case PERF_RECORD_STAT_ROUND: |
1402 | return tool->stat_round(tool, event, session); | 1390 | return tool->stat_round(session, event); |
1403 | case PERF_RECORD_TIME_CONV: | 1391 | case PERF_RECORD_TIME_CONV: |
1404 | session->time_conv = event->time_conv; | 1392 | session->time_conv = event->time_conv; |
1405 | return tool->time_conv(tool, event, session); | 1393 | return tool->time_conv(session, event); |
1406 | case PERF_RECORD_HEADER_FEATURE: | 1394 | case PERF_RECORD_HEADER_FEATURE: |
1407 | return tool->feature(tool, event, session); | 1395 | return tool->feature(session, event); |
1408 | default: | 1396 | default: |
1409 | return -EINVAL; | 1397 | return -EINVAL; |
1410 | } | 1398 | } |
@@ -2133,9 +2121,8 @@ out: | |||
2133 | return err; | 2121 | return err; |
2134 | } | 2122 | } |
2135 | 2123 | ||
2136 | int perf_event__process_id_index(struct perf_tool *tool __maybe_unused, | 2124 | int perf_event__process_id_index(struct perf_session *session, |
2137 | union perf_event *event, | 2125 | union perf_event *event) |
2138 | struct perf_session *session) | ||
2139 | { | 2126 | { |
2140 | struct perf_evlist *evlist = session->evlist; | 2127 | struct perf_evlist *evlist = session->evlist; |
2141 | struct id_index_event *ie = &event->id_index; | 2128 | struct id_index_event *ie = &event->id_index; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index da40b4b380ca..d96eccd7d27f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -120,9 +120,8 @@ int perf_session__deliver_synth_event(struct perf_session *session, | |||
120 | union perf_event *event, | 120 | union perf_event *event, |
121 | struct perf_sample *sample); | 121 | struct perf_sample *sample); |
122 | 122 | ||
123 | int perf_event__process_id_index(struct perf_tool *tool, | 123 | int perf_event__process_id_index(struct perf_session *session, |
124 | union perf_event *event, | 124 | union perf_event *event); |
125 | struct perf_session *session); | ||
126 | 125 | ||
127 | int perf_event__synthesize_id_index(struct perf_tool *tool, | 126 | int perf_event__synthesize_id_index(struct perf_tool *tool, |
128 | perf_event__handler_t process, | 127 | perf_event__handler_t process, |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 97efbcad076e..63f758c655d5 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -5,16 +5,18 @@ from subprocess import Popen, PIPE | |||
5 | from re import sub | 5 | from re import sub |
6 | 6 | ||
7 | def clang_has_option(option): | 7 | def clang_has_option(option): |
8 | return [o for o in Popen(['clang', option], stderr=PIPE).stderr.readlines() if "unknown argument" in o] == [ ] | 8 | return [o for o in Popen(['clang', option], stderr=PIPE).stderr.readlines() if b"unknown argument" in o] == [ ] |
9 | 9 | ||
10 | cc = getenv("CC") | 10 | cc = getenv("CC") |
11 | if cc == "clang": | 11 | if cc == "clang": |
12 | from _sysconfigdata import build_time_vars | 12 | from distutils.sysconfig import get_config_vars |
13 | build_time_vars["CFLAGS"] = sub("-specs=[^ ]+", "", build_time_vars["CFLAGS"]) | 13 | vars = get_config_vars() |
14 | if not clang_has_option("-mcet"): | 14 | for var in ('CFLAGS', 'OPT'): |
15 | build_time_vars["CFLAGS"] = sub("-mcet", "", build_time_vars["CFLAGS"]) | 15 | vars[var] = sub("-specs=[^ ]+", "", vars[var]) |
16 | if not clang_has_option("-fcf-protection"): | 16 | if not clang_has_option("-mcet"): |
17 | build_time_vars["CFLAGS"] = sub("-fcf-protection", "", build_time_vars["CFLAGS"]) | 17 | vars[var] = sub("-mcet", "", vars[var]) |
18 | if not clang_has_option("-fcf-protection"): | ||
19 | vars[var] = sub("-fcf-protection", "", vars[var]) | ||
18 | 20 | ||
19 | from distutils.core import setup, Extension | 21 | from distutils.core import setup, Extension |
20 | 22 | ||
@@ -35,7 +37,7 @@ class install_lib(_install_lib): | |||
35 | 37 | ||
36 | cflags = getenv('CFLAGS', '').split() | 38 | cflags = getenv('CFLAGS', '').split() |
37 | # switch off several checks (need to be at the end of cflags list) | 39 | # switch off several checks (need to be at the end of cflags list) |
38 | cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ] | 40 | cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter', '-Wno-redundant-decls' ] |
39 | if cc != "clang": | 41 | if cc != "clang": |
40 | cflags += ['-Wno-cast-function-type' ] | 42 | cflags += ['-Wno-cast-function-type' ] |
41 | 43 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b284276ec963..f96c005b3c41 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1884,7 +1884,7 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd, | |||
1884 | struct hpp_dynamic_entry { | 1884 | struct hpp_dynamic_entry { |
1885 | struct perf_hpp_fmt hpp; | 1885 | struct perf_hpp_fmt hpp; |
1886 | struct perf_evsel *evsel; | 1886 | struct perf_evsel *evsel; |
1887 | struct format_field *field; | 1887 | struct tep_format_field *field; |
1888 | unsigned dynamic_len; | 1888 | unsigned dynamic_len; |
1889 | bool raw_trace; | 1889 | bool raw_trace; |
1890 | }; | 1890 | }; |
@@ -1899,7 +1899,7 @@ static int hde_width(struct hpp_dynamic_entry *hde) | |||
1899 | if (namelen > len) | 1899 | if (namelen > len) |
1900 | len = namelen; | 1900 | len = namelen; |
1901 | 1901 | ||
1902 | if (!(hde->field->flags & FIELD_IS_STRING)) { | 1902 | if (!(hde->field->flags & TEP_FIELD_IS_STRING)) { |
1903 | /* length for print hex numbers */ | 1903 | /* length for print hex numbers */ |
1904 | fieldlen = hde->field->size * 2 + 2; | 1904 | fieldlen = hde->field->size * 2 + 2; |
1905 | } | 1905 | } |
@@ -1915,7 +1915,7 @@ static void update_dynamic_len(struct hpp_dynamic_entry *hde, | |||
1915 | struct hist_entry *he) | 1915 | struct hist_entry *he) |
1916 | { | 1916 | { |
1917 | char *str, *pos; | 1917 | char *str, *pos; |
1918 | struct format_field *field = hde->field; | 1918 | struct tep_format_field *field = hde->field; |
1919 | size_t namelen; | 1919 | size_t namelen; |
1920 | bool last = false; | 1920 | bool last = false; |
1921 | 1921 | ||
@@ -2000,7 +2000,7 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | |||
2000 | struct hpp_dynamic_entry *hde; | 2000 | struct hpp_dynamic_entry *hde; |
2001 | size_t len = fmt->user_len; | 2001 | size_t len = fmt->user_len; |
2002 | char *str, *pos; | 2002 | char *str, *pos; |
2003 | struct format_field *field; | 2003 | struct tep_format_field *field; |
2004 | size_t namelen; | 2004 | size_t namelen; |
2005 | bool last = false; | 2005 | bool last = false; |
2006 | int ret; | 2006 | int ret; |
@@ -2060,7 +2060,7 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, | |||
2060 | struct hist_entry *a, struct hist_entry *b) | 2060 | struct hist_entry *a, struct hist_entry *b) |
2061 | { | 2061 | { |
2062 | struct hpp_dynamic_entry *hde; | 2062 | struct hpp_dynamic_entry *hde; |
2063 | struct format_field *field; | 2063 | struct tep_format_field *field; |
2064 | unsigned offset, size; | 2064 | unsigned offset, size; |
2065 | 2065 | ||
2066 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | 2066 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); |
@@ -2071,7 +2071,7 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, | |||
2071 | } | 2071 | } |
2072 | 2072 | ||
2073 | field = hde->field; | 2073 | field = hde->field; |
2074 | if (field->flags & FIELD_IS_DYNAMIC) { | 2074 | if (field->flags & TEP_FIELD_IS_DYNAMIC) { |
2075 | unsigned long long dyn; | 2075 | unsigned long long dyn; |
2076 | 2076 | ||
2077 | tep_read_number_field(field, a->raw_data, &dyn); | 2077 | tep_read_number_field(field, a->raw_data, &dyn); |
@@ -2117,7 +2117,7 @@ static void hde_free(struct perf_hpp_fmt *fmt) | |||
2117 | } | 2117 | } |
2118 | 2118 | ||
2119 | static struct hpp_dynamic_entry * | 2119 | static struct hpp_dynamic_entry * |
2120 | __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, | 2120 | __alloc_dynamic_entry(struct perf_evsel *evsel, struct tep_format_field *field, |
2121 | int level) | 2121 | int level) |
2122 | { | 2122 | { |
2123 | struct hpp_dynamic_entry *hde; | 2123 | struct hpp_dynamic_entry *hde; |
@@ -2252,7 +2252,7 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam | |||
2252 | } | 2252 | } |
2253 | 2253 | ||
2254 | static int __dynamic_dimension__add(struct perf_evsel *evsel, | 2254 | static int __dynamic_dimension__add(struct perf_evsel *evsel, |
2255 | struct format_field *field, | 2255 | struct tep_format_field *field, |
2256 | bool raw_trace, int level) | 2256 | bool raw_trace, int level) |
2257 | { | 2257 | { |
2258 | struct hpp_dynamic_entry *hde; | 2258 | struct hpp_dynamic_entry *hde; |
@@ -2270,7 +2270,7 @@ static int __dynamic_dimension__add(struct perf_evsel *evsel, | |||
2270 | static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level) | 2270 | static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level) |
2271 | { | 2271 | { |
2272 | int ret; | 2272 | int ret; |
2273 | struct format_field *field; | 2273 | struct tep_format_field *field; |
2274 | 2274 | ||
2275 | field = evsel->tp_format->format.fields; | 2275 | field = evsel->tp_format->format.fields; |
2276 | while (field) { | 2276 | while (field) { |
@@ -2305,7 +2305,7 @@ static int add_all_matching_fields(struct perf_evlist *evlist, | |||
2305 | { | 2305 | { |
2306 | int ret = -ESRCH; | 2306 | int ret = -ESRCH; |
2307 | struct perf_evsel *evsel; | 2307 | struct perf_evsel *evsel; |
2308 | struct format_field *field; | 2308 | struct tep_format_field *field; |
2309 | 2309 | ||
2310 | evlist__for_each_entry(evlist, evsel) { | 2310 | evlist__for_each_entry(evlist, evsel) { |
2311 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 2311 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
@@ -2327,7 +2327,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok, | |||
2327 | { | 2327 | { |
2328 | char *str, *event_name, *field_name, *opt_name; | 2328 | char *str, *event_name, *field_name, *opt_name; |
2329 | struct perf_evsel *evsel; | 2329 | struct perf_evsel *evsel; |
2330 | struct format_field *field; | 2330 | struct tep_format_field *field; |
2331 | bool raw_trace = symbol_conf.raw_trace; | 2331 | bool raw_trace = symbol_conf.raw_trace; |
2332 | int ret = 0; | 2332 | int ret = 0; |
2333 | 2333 | ||
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 09d6746e6ec8..e767c4a9d4d2 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c | |||
@@ -85,6 +85,9 @@ static struct symbol *new_inline_sym(struct dso *dso, | |||
85 | struct symbol *inline_sym; | 85 | struct symbol *inline_sym; |
86 | char *demangled = NULL; | 86 | char *demangled = NULL; |
87 | 87 | ||
88 | if (!funcname) | ||
89 | funcname = "??"; | ||
90 | |||
88 | if (dso) { | 91 | if (dso) { |
89 | demangled = dso__demangle_sym(dso, 0, funcname); | 92 | demangled = dso__demangle_sym(dso, 0, funcname); |
90 | if (demangled) | 93 | if (demangled) |
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c new file mode 100644 index 000000000000..e7b4c44ebb62 --- /dev/null +++ b/tools/perf/util/stat-display.c | |||
@@ -0,0 +1,1166 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <inttypes.h> | ||
3 | #include <linux/time64.h> | ||
4 | #include <math.h> | ||
5 | #include "evlist.h" | ||
6 | #include "evsel.h" | ||
7 | #include "stat.h" | ||
8 | #include "top.h" | ||
9 | #include "thread_map.h" | ||
10 | #include "cpumap.h" | ||
11 | #include "string2.h" | ||
12 | #include "sane_ctype.h" | ||
13 | #include "cgroup.h" | ||
14 | #include <math.h> | ||
15 | #include <api/fs/fs.h> | ||
16 | |||
17 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
18 | #define CNTR_NOT_COUNTED "<not counted>" | ||
19 | |||
20 | static bool is_duration_time(struct perf_evsel *evsel) | ||
21 | { | ||
22 | return !strcmp(evsel->name, "duration_time"); | ||
23 | } | ||
24 | |||
25 | static void print_running(struct perf_stat_config *config, | ||
26 | u64 run, u64 ena) | ||
27 | { | ||
28 | if (config->csv_output) { | ||
29 | fprintf(config->output, "%s%" PRIu64 "%s%.2f", | ||
30 | config->csv_sep, | ||
31 | run, | ||
32 | config->csv_sep, | ||
33 | ena ? 100.0 * run / ena : 100.0); | ||
34 | } else if (run != ena) { | ||
35 | fprintf(config->output, " (%.2f%%)", 100.0 * run / ena); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | static void print_noise_pct(struct perf_stat_config *config, | ||
40 | double total, double avg) | ||
41 | { | ||
42 | double pct = rel_stddev_stats(total, avg); | ||
43 | |||
44 | if (config->csv_output) | ||
45 | fprintf(config->output, "%s%.2f%%", config->csv_sep, pct); | ||
46 | else if (pct) | ||
47 | fprintf(config->output, " ( +-%6.2f%% )", pct); | ||
48 | } | ||
49 | |||
50 | static void print_noise(struct perf_stat_config *config, | ||
51 | struct perf_evsel *evsel, double avg) | ||
52 | { | ||
53 | struct perf_stat_evsel *ps; | ||
54 | |||
55 | if (config->run_count == 1) | ||
56 | return; | ||
57 | |||
58 | ps = evsel->stats; | ||
59 | print_noise_pct(config, stddev_stats(&ps->res_stats[0]), avg); | ||
60 | } | ||
61 | |||
62 | static void aggr_printout(struct perf_stat_config *config, | ||
63 | struct perf_evsel *evsel, int id, int nr) | ||
64 | { | ||
65 | switch (config->aggr_mode) { | ||
66 | case AGGR_CORE: | ||
67 | fprintf(config->output, "S%d-C%*d%s%*d%s", | ||
68 | cpu_map__id_to_socket(id), | ||
69 | config->csv_output ? 0 : -8, | ||
70 | cpu_map__id_to_cpu(id), | ||
71 | config->csv_sep, | ||
72 | config->csv_output ? 0 : 4, | ||
73 | nr, | ||
74 | config->csv_sep); | ||
75 | break; | ||
76 | case AGGR_SOCKET: | ||
77 | fprintf(config->output, "S%*d%s%*d%s", | ||
78 | config->csv_output ? 0 : -5, | ||
79 | id, | ||
80 | config->csv_sep, | ||
81 | config->csv_output ? 0 : 4, | ||
82 | nr, | ||
83 | config->csv_sep); | ||
84 | break; | ||
85 | case AGGR_NONE: | ||
86 | fprintf(config->output, "CPU%*d%s", | ||
87 | config->csv_output ? 0 : -4, | ||
88 | perf_evsel__cpus(evsel)->map[id], config->csv_sep); | ||
89 | break; | ||
90 | case AGGR_THREAD: | ||
91 | fprintf(config->output, "%*s-%*d%s", | ||
92 | config->csv_output ? 0 : 16, | ||
93 | thread_map__comm(evsel->threads, id), | ||
94 | config->csv_output ? 0 : -8, | ||
95 | thread_map__pid(evsel->threads, id), | ||
96 | config->csv_sep); | ||
97 | break; | ||
98 | case AGGR_GLOBAL: | ||
99 | case AGGR_UNSET: | ||
100 | default: | ||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | struct outstate { | ||
106 | FILE *fh; | ||
107 | bool newline; | ||
108 | const char *prefix; | ||
109 | int nfields; | ||
110 | int id, nr; | ||
111 | struct perf_evsel *evsel; | ||
112 | }; | ||
113 | |||
114 | #define METRIC_LEN 35 | ||
115 | |||
116 | static void new_line_std(struct perf_stat_config *config __maybe_unused, | ||
117 | void *ctx) | ||
118 | { | ||
119 | struct outstate *os = ctx; | ||
120 | |||
121 | os->newline = true; | ||
122 | } | ||
123 | |||
124 | static void do_new_line_std(struct perf_stat_config *config, | ||
125 | struct outstate *os) | ||
126 | { | ||
127 | fputc('\n', os->fh); | ||
128 | fputs(os->prefix, os->fh); | ||
129 | aggr_printout(config, os->evsel, os->id, os->nr); | ||
130 | if (config->aggr_mode == AGGR_NONE) | ||
131 | fprintf(os->fh, " "); | ||
132 | fprintf(os->fh, " "); | ||
133 | } | ||
134 | |||
135 | static void print_metric_std(struct perf_stat_config *config, | ||
136 | void *ctx, const char *color, const char *fmt, | ||
137 | const char *unit, double val) | ||
138 | { | ||
139 | struct outstate *os = ctx; | ||
140 | FILE *out = os->fh; | ||
141 | int n; | ||
142 | bool newline = os->newline; | ||
143 | |||
144 | os->newline = false; | ||
145 | |||
146 | if (unit == NULL || fmt == NULL) { | ||
147 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
148 | return; | ||
149 | } | ||
150 | |||
151 | if (newline) | ||
152 | do_new_line_std(config, os); | ||
153 | |||
154 | n = fprintf(out, " # "); | ||
155 | if (color) | ||
156 | n += color_fprintf(out, color, fmt, val); | ||
157 | else | ||
158 | n += fprintf(out, fmt, val); | ||
159 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
160 | } | ||
161 | |||
162 | static void new_line_csv(struct perf_stat_config *config, void *ctx) | ||
163 | { | ||
164 | struct outstate *os = ctx; | ||
165 | int i; | ||
166 | |||
167 | fputc('\n', os->fh); | ||
168 | if (os->prefix) | ||
169 | fprintf(os->fh, "%s%s", os->prefix, config->csv_sep); | ||
170 | aggr_printout(config, os->evsel, os->id, os->nr); | ||
171 | for (i = 0; i < os->nfields; i++) | ||
172 | fputs(config->csv_sep, os->fh); | ||
173 | } | ||
174 | |||
175 | static void print_metric_csv(struct perf_stat_config *config __maybe_unused, | ||
176 | void *ctx, | ||
177 | const char *color __maybe_unused, | ||
178 | const char *fmt, const char *unit, double val) | ||
179 | { | ||
180 | struct outstate *os = ctx; | ||
181 | FILE *out = os->fh; | ||
182 | char buf[64], *vals, *ends; | ||
183 | |||
184 | if (unit == NULL || fmt == NULL) { | ||
185 | fprintf(out, "%s%s", config->csv_sep, config->csv_sep); | ||
186 | return; | ||
187 | } | ||
188 | snprintf(buf, sizeof(buf), fmt, val); | ||
189 | ends = vals = ltrim(buf); | ||
190 | while (isdigit(*ends) || *ends == '.') | ||
191 | ends++; | ||
192 | *ends = 0; | ||
193 | while (isspace(*unit)) | ||
194 | unit++; | ||
195 | fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, unit); | ||
196 | } | ||
197 | |||
198 | /* Filter out some columns that don't work well in metrics only mode */ | ||
199 | |||
200 | static bool valid_only_metric(const char *unit) | ||
201 | { | ||
202 | if (!unit) | ||
203 | return false; | ||
204 | if (strstr(unit, "/sec") || | ||
205 | strstr(unit, "hz") || | ||
206 | strstr(unit, "Hz") || | ||
207 | strstr(unit, "CPUs utilized")) | ||
208 | return false; | ||
209 | return true; | ||
210 | } | ||
211 | |||
212 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
213 | const char *unit) | ||
214 | { | ||
215 | if (!strncmp(unit, "of all", 6)) { | ||
216 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
217 | unit); | ||
218 | return buf; | ||
219 | } | ||
220 | return unit; | ||
221 | } | ||
222 | |||
223 | static void print_metric_only(struct perf_stat_config *config, | ||
224 | void *ctx, const char *color, const char *fmt, | ||
225 | const char *unit, double val) | ||
226 | { | ||
227 | struct outstate *os = ctx; | ||
228 | FILE *out = os->fh; | ||
229 | char buf[1024], str[1024]; | ||
230 | unsigned mlen = config->metric_only_len; | ||
231 | |||
232 | if (!valid_only_metric(unit)) | ||
233 | return; | ||
234 | unit = fixunit(buf, os->evsel, unit); | ||
235 | if (mlen < strlen(unit)) | ||
236 | mlen = strlen(unit) + 1; | ||
237 | |||
238 | if (color) | ||
239 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | ||
240 | |||
241 | color_snprintf(str, sizeof(str), color ?: "", fmt, val); | ||
242 | fprintf(out, "%*s ", mlen, str); | ||
243 | } | ||
244 | |||
245 | static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused, | ||
246 | void *ctx, const char *color __maybe_unused, | ||
247 | const char *fmt, | ||
248 | const char *unit, double val) | ||
249 | { | ||
250 | struct outstate *os = ctx; | ||
251 | FILE *out = os->fh; | ||
252 | char buf[64], *vals, *ends; | ||
253 | char tbuf[1024]; | ||
254 | |||
255 | if (!valid_only_metric(unit)) | ||
256 | return; | ||
257 | unit = fixunit(tbuf, os->evsel, unit); | ||
258 | snprintf(buf, sizeof buf, fmt, val); | ||
259 | ends = vals = ltrim(buf); | ||
260 | while (isdigit(*ends) || *ends == '.') | ||
261 | ends++; | ||
262 | *ends = 0; | ||
263 | fprintf(out, "%s%s", vals, config->csv_sep); | ||
264 | } | ||
265 | |||
266 | static void new_line_metric(struct perf_stat_config *config __maybe_unused, | ||
267 | void *ctx __maybe_unused) | ||
268 | { | ||
269 | } | ||
270 | |||
271 | static void print_metric_header(struct perf_stat_config *config, | ||
272 | void *ctx, const char *color __maybe_unused, | ||
273 | const char *fmt __maybe_unused, | ||
274 | const char *unit, double val __maybe_unused) | ||
275 | { | ||
276 | struct outstate *os = ctx; | ||
277 | char tbuf[1024]; | ||
278 | |||
279 | if (!valid_only_metric(unit)) | ||
280 | return; | ||
281 | unit = fixunit(tbuf, os->evsel, unit); | ||
282 | if (config->csv_output) | ||
283 | fprintf(os->fh, "%s%s", unit, config->csv_sep); | ||
284 | else | ||
285 | fprintf(os->fh, "%*s ", config->metric_only_len, unit); | ||
286 | } | ||
287 | |||
288 | static int first_shadow_cpu(struct perf_stat_config *config, | ||
289 | struct perf_evsel *evsel, int id) | ||
290 | { | ||
291 | struct perf_evlist *evlist = evsel->evlist; | ||
292 | int i; | ||
293 | |||
294 | if (!config->aggr_get_id) | ||
295 | return 0; | ||
296 | |||
297 | if (config->aggr_mode == AGGR_NONE) | ||
298 | return id; | ||
299 | |||
300 | if (config->aggr_mode == AGGR_GLOBAL) | ||
301 | return 0; | ||
302 | |||
303 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
304 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
305 | |||
306 | if (config->aggr_get_id(config, evlist->cpus, cpu2) == id) | ||
307 | return cpu2; | ||
308 | } | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static void abs_printout(struct perf_stat_config *config, | ||
313 | int id, int nr, struct perf_evsel *evsel, double avg) | ||
314 | { | ||
315 | FILE *output = config->output; | ||
316 | double sc = evsel->scale; | ||
317 | const char *fmt; | ||
318 | |||
319 | if (config->csv_output) { | ||
320 | fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; | ||
321 | } else { | ||
322 | if (config->big_num) | ||
323 | fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s"; | ||
324 | else | ||
325 | fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s"; | ||
326 | } | ||
327 | |||
328 | aggr_printout(config, evsel, id, nr); | ||
329 | |||
330 | fprintf(output, fmt, avg, config->csv_sep); | ||
331 | |||
332 | if (evsel->unit) | ||
333 | fprintf(output, "%-*s%s", | ||
334 | config->csv_output ? 0 : config->unit_width, | ||
335 | evsel->unit, config->csv_sep); | ||
336 | |||
337 | fprintf(output, "%-*s", config->csv_output ? 0 : 25, perf_evsel__name(evsel)); | ||
338 | |||
339 | if (evsel->cgrp) | ||
340 | fprintf(output, "%s%s", config->csv_sep, evsel->cgrp->name); | ||
341 | } | ||
342 | |||
343 | static bool is_mixed_hw_group(struct perf_evsel *counter) | ||
344 | { | ||
345 | struct perf_evlist *evlist = counter->evlist; | ||
346 | u32 pmu_type = counter->attr.type; | ||
347 | struct perf_evsel *pos; | ||
348 | |||
349 | if (counter->nr_members < 2) | ||
350 | return false; | ||
351 | |||
352 | evlist__for_each_entry(evlist, pos) { | ||
353 | /* software events can be part of any hardware group */ | ||
354 | if (pos->attr.type == PERF_TYPE_SOFTWARE) | ||
355 | continue; | ||
356 | if (pmu_type == PERF_TYPE_SOFTWARE) { | ||
357 | pmu_type = pos->attr.type; | ||
358 | continue; | ||
359 | } | ||
360 | if (pmu_type != pos->attr.type) | ||
361 | return true; | ||
362 | } | ||
363 | |||
364 | return false; | ||
365 | } | ||
366 | |||
367 | static void printout(struct perf_stat_config *config, int id, int nr, | ||
368 | struct perf_evsel *counter, double uval, | ||
369 | char *prefix, u64 run, u64 ena, double noise, | ||
370 | struct runtime_stat *st) | ||
371 | { | ||
372 | struct perf_stat_output_ctx out; | ||
373 | struct outstate os = { | ||
374 | .fh = config->output, | ||
375 | .prefix = prefix ? prefix : "", | ||
376 | .id = id, | ||
377 | .nr = nr, | ||
378 | .evsel = counter, | ||
379 | }; | ||
380 | print_metric_t pm = print_metric_std; | ||
381 | new_line_t nl; | ||
382 | |||
383 | if (config->metric_only) { | ||
384 | nl = new_line_metric; | ||
385 | if (config->csv_output) | ||
386 | pm = print_metric_only_csv; | ||
387 | else | ||
388 | pm = print_metric_only; | ||
389 | } else | ||
390 | nl = new_line_std; | ||
391 | |||
392 | if (config->csv_output && !config->metric_only) { | ||
393 | static int aggr_fields[] = { | ||
394 | [AGGR_GLOBAL] = 0, | ||
395 | [AGGR_THREAD] = 1, | ||
396 | [AGGR_NONE] = 1, | ||
397 | [AGGR_SOCKET] = 2, | ||
398 | [AGGR_CORE] = 2, | ||
399 | }; | ||
400 | |||
401 | pm = print_metric_csv; | ||
402 | nl = new_line_csv; | ||
403 | os.nfields = 3; | ||
404 | os.nfields += aggr_fields[config->aggr_mode]; | ||
405 | if (counter->cgrp) | ||
406 | os.nfields++; | ||
407 | } | ||
408 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
409 | if (config->metric_only) { | ||
410 | pm(config, &os, NULL, "", "", 0); | ||
411 | return; | ||
412 | } | ||
413 | aggr_printout(config, counter, id, nr); | ||
414 | |||
415 | fprintf(config->output, "%*s%s", | ||
416 | config->csv_output ? 0 : 18, | ||
417 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
418 | config->csv_sep); | ||
419 | |||
420 | if (counter->supported) { | ||
421 | config->print_free_counters_hint = 1; | ||
422 | if (is_mixed_hw_group(counter)) | ||
423 | config->print_mixed_hw_group_error = 1; | ||
424 | } | ||
425 | |||
426 | fprintf(config->output, "%-*s%s", | ||
427 | config->csv_output ? 0 : config->unit_width, | ||
428 | counter->unit, config->csv_sep); | ||
429 | |||
430 | fprintf(config->output, "%*s", | ||
431 | config->csv_output ? 0 : -25, | ||
432 | perf_evsel__name(counter)); | ||
433 | |||
434 | if (counter->cgrp) | ||
435 | fprintf(config->output, "%s%s", | ||
436 | config->csv_sep, counter->cgrp->name); | ||
437 | |||
438 | if (!config->csv_output) | ||
439 | pm(config, &os, NULL, NULL, "", 0); | ||
440 | print_noise(config, counter, noise); | ||
441 | print_running(config, run, ena); | ||
442 | if (config->csv_output) | ||
443 | pm(config, &os, NULL, NULL, "", 0); | ||
444 | return; | ||
445 | } | ||
446 | |||
447 | if (!config->metric_only) | ||
448 | abs_printout(config, id, nr, counter, uval); | ||
449 | |||
450 | out.print_metric = pm; | ||
451 | out.new_line = nl; | ||
452 | out.ctx = &os; | ||
453 | out.force_header = false; | ||
454 | |||
455 | if (config->csv_output && !config->metric_only) { | ||
456 | print_noise(config, counter, noise); | ||
457 | print_running(config, run, ena); | ||
458 | } | ||
459 | |||
460 | perf_stat__print_shadow_stats(config, counter, uval, | ||
461 | first_shadow_cpu(config, counter, id), | ||
462 | &out, &config->metric_events, st); | ||
463 | if (!config->csv_output && !config->metric_only) { | ||
464 | print_noise(config, counter, noise); | ||
465 | print_running(config, run, ena); | ||
466 | } | ||
467 | } | ||
468 | |||
469 | static void aggr_update_shadow(struct perf_stat_config *config, | ||
470 | struct perf_evlist *evlist) | ||
471 | { | ||
472 | int cpu, s2, id, s; | ||
473 | u64 val; | ||
474 | struct perf_evsel *counter; | ||
475 | |||
476 | for (s = 0; s < config->aggr_map->nr; s++) { | ||
477 | id = config->aggr_map->map[s]; | ||
478 | evlist__for_each_entry(evlist, counter) { | ||
479 | val = 0; | ||
480 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
481 | s2 = config->aggr_get_id(config, evlist->cpus, cpu); | ||
482 | if (s2 != id) | ||
483 | continue; | ||
484 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
485 | } | ||
486 | perf_stat__update_shadow_stats(counter, val, | ||
487 | first_shadow_cpu(config, counter, id), | ||
488 | &rt_stat); | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | |||
493 | static void uniquify_event_name(struct perf_evsel *counter) | ||
494 | { | ||
495 | char *new_name; | ||
496 | char *config; | ||
497 | |||
498 | if (counter->uniquified_name || | ||
499 | !counter->pmu_name || !strncmp(counter->name, counter->pmu_name, | ||
500 | strlen(counter->pmu_name))) | ||
501 | return; | ||
502 | |||
503 | config = strchr(counter->name, '/'); | ||
504 | if (config) { | ||
505 | if (asprintf(&new_name, | ||
506 | "%s%s", counter->pmu_name, config) > 0) { | ||
507 | free(counter->name); | ||
508 | counter->name = new_name; | ||
509 | } | ||
510 | } else { | ||
511 | if (asprintf(&new_name, | ||
512 | "%s [%s]", counter->name, counter->pmu_name) > 0) { | ||
513 | free(counter->name); | ||
514 | counter->name = new_name; | ||
515 | } | ||
516 | } | ||
517 | |||
518 | counter->uniquified_name = true; | ||
519 | } | ||
520 | |||
521 | static void collect_all_aliases(struct perf_stat_config *config, struct perf_evsel *counter, | ||
522 | void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data, | ||
523 | bool first), | ||
524 | void *data) | ||
525 | { | ||
526 | struct perf_evlist *evlist = counter->evlist; | ||
527 | struct perf_evsel *alias; | ||
528 | |||
529 | alias = list_prepare_entry(counter, &(evlist->entries), node); | ||
530 | list_for_each_entry_continue (alias, &evlist->entries, node) { | ||
531 | if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || | ||
532 | alias->scale != counter->scale || | ||
533 | alias->cgrp != counter->cgrp || | ||
534 | strcmp(alias->unit, counter->unit) || | ||
535 | perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter)) | ||
536 | break; | ||
537 | alias->merged_stat = true; | ||
538 | cb(config, alias, data, false); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | static bool collect_data(struct perf_stat_config *config, struct perf_evsel *counter, | ||
543 | void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data, | ||
544 | bool first), | ||
545 | void *data) | ||
546 | { | ||
547 | if (counter->merged_stat) | ||
548 | return false; | ||
549 | cb(config, counter, data, true); | ||
550 | if (config->no_merge) | ||
551 | uniquify_event_name(counter); | ||
552 | else if (counter->auto_merge_stats) | ||
553 | collect_all_aliases(config, counter, cb, data); | ||
554 | return true; | ||
555 | } | ||
556 | |||
557 | struct aggr_data { | ||
558 | u64 ena, run, val; | ||
559 | int id; | ||
560 | int nr; | ||
561 | int cpu; | ||
562 | }; | ||
563 | |||
564 | static void aggr_cb(struct perf_stat_config *config, | ||
565 | struct perf_evsel *counter, void *data, bool first) | ||
566 | { | ||
567 | struct aggr_data *ad = data; | ||
568 | int cpu, s2; | ||
569 | |||
570 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
571 | struct perf_counts_values *counts; | ||
572 | |||
573 | s2 = config->aggr_get_id(config, perf_evsel__cpus(counter), cpu); | ||
574 | if (s2 != ad->id) | ||
575 | continue; | ||
576 | if (first) | ||
577 | ad->nr++; | ||
578 | counts = perf_counts(counter->counts, cpu, 0); | ||
579 | /* | ||
580 | * When any result is bad, make them all to give | ||
581 | * consistent output in interval mode. | ||
582 | */ | ||
583 | if (counts->ena == 0 || counts->run == 0 || | ||
584 | counter->counts->scaled == -1) { | ||
585 | ad->ena = 0; | ||
586 | ad->run = 0; | ||
587 | break; | ||
588 | } | ||
589 | ad->val += counts->val; | ||
590 | ad->ena += counts->ena; | ||
591 | ad->run += counts->run; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static void print_aggr(struct perf_stat_config *config, | ||
596 | struct perf_evlist *evlist, | ||
597 | char *prefix) | ||
598 | { | ||
599 | bool metric_only = config->metric_only; | ||
600 | FILE *output = config->output; | ||
601 | struct perf_evsel *counter; | ||
602 | int s, id, nr; | ||
603 | double uval; | ||
604 | u64 ena, run, val; | ||
605 | bool first; | ||
606 | |||
607 | if (!(config->aggr_map || config->aggr_get_id)) | ||
608 | return; | ||
609 | |||
610 | aggr_update_shadow(config, evlist); | ||
611 | |||
612 | /* | ||
613 | * With metric_only everything is on a single line. | ||
614 | * Without each counter has its own line. | ||
615 | */ | ||
616 | for (s = 0; s < config->aggr_map->nr; s++) { | ||
617 | struct aggr_data ad; | ||
618 | if (prefix && metric_only) | ||
619 | fprintf(output, "%s", prefix); | ||
620 | |||
621 | ad.id = id = config->aggr_map->map[s]; | ||
622 | first = true; | ||
623 | evlist__for_each_entry(evlist, counter) { | ||
624 | if (is_duration_time(counter)) | ||
625 | continue; | ||
626 | |||
627 | ad.val = ad.ena = ad.run = 0; | ||
628 | ad.nr = 0; | ||
629 | if (!collect_data(config, counter, aggr_cb, &ad)) | ||
630 | continue; | ||
631 | nr = ad.nr; | ||
632 | ena = ad.ena; | ||
633 | run = ad.run; | ||
634 | val = ad.val; | ||
635 | if (first && metric_only) { | ||
636 | first = false; | ||
637 | aggr_printout(config, counter, id, nr); | ||
638 | } | ||
639 | if (prefix && !metric_only) | ||
640 | fprintf(output, "%s", prefix); | ||
641 | |||
642 | uval = val * counter->scale; | ||
643 | printout(config, id, nr, counter, uval, prefix, | ||
644 | run, ena, 1.0, &rt_stat); | ||
645 | if (!metric_only) | ||
646 | fputc('\n', output); | ||
647 | } | ||
648 | if (metric_only) | ||
649 | fputc('\n', output); | ||
650 | } | ||
651 | } | ||
652 | |||
653 | static int cmp_val(const void *a, const void *b) | ||
654 | { | ||
655 | return ((struct perf_aggr_thread_value *)b)->val - | ||
656 | ((struct perf_aggr_thread_value *)a)->val; | ||
657 | } | ||
658 | |||
659 | static struct perf_aggr_thread_value *sort_aggr_thread( | ||
660 | struct perf_evsel *counter, | ||
661 | int nthreads, int ncpus, | ||
662 | int *ret, | ||
663 | struct target *_target) | ||
664 | { | ||
665 | int cpu, thread, i = 0; | ||
666 | double uval; | ||
667 | struct perf_aggr_thread_value *buf; | ||
668 | |||
669 | buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); | ||
670 | if (!buf) | ||
671 | return NULL; | ||
672 | |||
673 | for (thread = 0; thread < nthreads; thread++) { | ||
674 | u64 ena = 0, run = 0, val = 0; | ||
675 | |||
676 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
677 | val += perf_counts(counter->counts, cpu, thread)->val; | ||
678 | ena += perf_counts(counter->counts, cpu, thread)->ena; | ||
679 | run += perf_counts(counter->counts, cpu, thread)->run; | ||
680 | } | ||
681 | |||
682 | uval = val * counter->scale; | ||
683 | |||
684 | /* | ||
685 | * Skip value 0 when enabling --per-thread globally, | ||
686 | * otherwise too many 0 output. | ||
687 | */ | ||
688 | if (uval == 0.0 && target__has_per_thread(_target)) | ||
689 | continue; | ||
690 | |||
691 | buf[i].counter = counter; | ||
692 | buf[i].id = thread; | ||
693 | buf[i].uval = uval; | ||
694 | buf[i].val = val; | ||
695 | buf[i].run = run; | ||
696 | buf[i].ena = ena; | ||
697 | i++; | ||
698 | } | ||
699 | |||
700 | qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); | ||
701 | |||
702 | if (ret) | ||
703 | *ret = i; | ||
704 | |||
705 | return buf; | ||
706 | } | ||
707 | |||
708 | static void print_aggr_thread(struct perf_stat_config *config, | ||
709 | struct target *_target, | ||
710 | struct perf_evsel *counter, char *prefix) | ||
711 | { | ||
712 | FILE *output = config->output; | ||
713 | int nthreads = thread_map__nr(counter->threads); | ||
714 | int ncpus = cpu_map__nr(counter->cpus); | ||
715 | int thread, sorted_threads, id; | ||
716 | struct perf_aggr_thread_value *buf; | ||
717 | |||
718 | buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads, _target); | ||
719 | if (!buf) { | ||
720 | perror("cannot sort aggr thread"); | ||
721 | return; | ||
722 | } | ||
723 | |||
724 | for (thread = 0; thread < sorted_threads; thread++) { | ||
725 | if (prefix) | ||
726 | fprintf(output, "%s", prefix); | ||
727 | |||
728 | id = buf[thread].id; | ||
729 | if (config->stats) | ||
730 | printout(config, id, 0, buf[thread].counter, buf[thread].uval, | ||
731 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
732 | &config->stats[id]); | ||
733 | else | ||
734 | printout(config, id, 0, buf[thread].counter, buf[thread].uval, | ||
735 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
736 | &rt_stat); | ||
737 | fputc('\n', output); | ||
738 | } | ||
739 | |||
740 | free(buf); | ||
741 | } | ||
742 | |||
743 | struct caggr_data { | ||
744 | double avg, avg_enabled, avg_running; | ||
745 | }; | ||
746 | |||
747 | static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused, | ||
748 | struct perf_evsel *counter, void *data, | ||
749 | bool first __maybe_unused) | ||
750 | { | ||
751 | struct caggr_data *cd = data; | ||
752 | struct perf_stat_evsel *ps = counter->stats; | ||
753 | |||
754 | cd->avg += avg_stats(&ps->res_stats[0]); | ||
755 | cd->avg_enabled += avg_stats(&ps->res_stats[1]); | ||
756 | cd->avg_running += avg_stats(&ps->res_stats[2]); | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * Print out the results of a single counter: | ||
761 | * aggregated counts in system-wide mode | ||
762 | */ | ||
763 | static void print_counter_aggr(struct perf_stat_config *config, | ||
764 | struct perf_evsel *counter, char *prefix) | ||
765 | { | ||
766 | bool metric_only = config->metric_only; | ||
767 | FILE *output = config->output; | ||
768 | double uval; | ||
769 | struct caggr_data cd = { .avg = 0.0 }; | ||
770 | |||
771 | if (!collect_data(config, counter, counter_aggr_cb, &cd)) | ||
772 | return; | ||
773 | |||
774 | if (prefix && !metric_only) | ||
775 | fprintf(output, "%s", prefix); | ||
776 | |||
777 | uval = cd.avg * counter->scale; | ||
778 | printout(config, -1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, | ||
779 | cd.avg, &rt_stat); | ||
780 | if (!metric_only) | ||
781 | fprintf(output, "\n"); | ||
782 | } | ||
783 | |||
784 | static void counter_cb(struct perf_stat_config *config __maybe_unused, | ||
785 | struct perf_evsel *counter, void *data, | ||
786 | bool first __maybe_unused) | ||
787 | { | ||
788 | struct aggr_data *ad = data; | ||
789 | |||
790 | ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; | ||
791 | ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; | ||
792 | ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; | ||
793 | } | ||
794 | |||
795 | /* | ||
796 | * Print out the results of a single counter: | ||
797 | * does not use aggregated count in system-wide | ||
798 | */ | ||
799 | static void print_counter(struct perf_stat_config *config, | ||
800 | struct perf_evsel *counter, char *prefix) | ||
801 | { | ||
802 | FILE *output = config->output; | ||
803 | u64 ena, run, val; | ||
804 | double uval; | ||
805 | int cpu; | ||
806 | |||
807 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
808 | struct aggr_data ad = { .cpu = cpu }; | ||
809 | |||
810 | if (!collect_data(config, counter, counter_cb, &ad)) | ||
811 | return; | ||
812 | val = ad.val; | ||
813 | ena = ad.ena; | ||
814 | run = ad.run; | ||
815 | |||
816 | if (prefix) | ||
817 | fprintf(output, "%s", prefix); | ||
818 | |||
819 | uval = val * counter->scale; | ||
820 | printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
821 | &rt_stat); | ||
822 | |||
823 | fputc('\n', output); | ||
824 | } | ||
825 | } | ||
826 | |||
827 | static void print_no_aggr_metric(struct perf_stat_config *config, | ||
828 | struct perf_evlist *evlist, | ||
829 | char *prefix) | ||
830 | { | ||
831 | int cpu; | ||
832 | int nrcpus = 0; | ||
833 | struct perf_evsel *counter; | ||
834 | u64 ena, run, val; | ||
835 | double uval; | ||
836 | |||
837 | nrcpus = evlist->cpus->nr; | ||
838 | for (cpu = 0; cpu < nrcpus; cpu++) { | ||
839 | bool first = true; | ||
840 | |||
841 | if (prefix) | ||
842 | fputs(prefix, config->output); | ||
843 | evlist__for_each_entry(evlist, counter) { | ||
844 | if (is_duration_time(counter)) | ||
845 | continue; | ||
846 | if (first) { | ||
847 | aggr_printout(config, counter, cpu, 0); | ||
848 | first = false; | ||
849 | } | ||
850 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
851 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
852 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
853 | |||
854 | uval = val * counter->scale; | ||
855 | printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0, | ||
856 | &rt_stat); | ||
857 | } | ||
858 | fputc('\n', config->output); | ||
859 | } | ||
860 | } | ||
861 | |||
862 | static int aggr_header_lens[] = { | ||
863 | [AGGR_CORE] = 18, | ||
864 | [AGGR_SOCKET] = 12, | ||
865 | [AGGR_NONE] = 6, | ||
866 | [AGGR_THREAD] = 24, | ||
867 | [AGGR_GLOBAL] = 0, | ||
868 | }; | ||
869 | |||
870 | static const char *aggr_header_csv[] = { | ||
871 | [AGGR_CORE] = "core,cpus,", | ||
872 | [AGGR_SOCKET] = "socket,cpus", | ||
873 | [AGGR_NONE] = "cpu,", | ||
874 | [AGGR_THREAD] = "comm-pid,", | ||
875 | [AGGR_GLOBAL] = "" | ||
876 | }; | ||
877 | |||
878 | static void print_metric_headers(struct perf_stat_config *config, | ||
879 | struct perf_evlist *evlist, | ||
880 | const char *prefix, bool no_indent) | ||
881 | { | ||
882 | struct perf_stat_output_ctx out; | ||
883 | struct perf_evsel *counter; | ||
884 | struct outstate os = { | ||
885 | .fh = config->output | ||
886 | }; | ||
887 | |||
888 | if (prefix) | ||
889 | fprintf(config->output, "%s", prefix); | ||
890 | |||
891 | if (!config->csv_output && !no_indent) | ||
892 | fprintf(config->output, "%*s", | ||
893 | aggr_header_lens[config->aggr_mode], ""); | ||
894 | if (config->csv_output) { | ||
895 | if (config->interval) | ||
896 | fputs("time,", config->output); | ||
897 | fputs(aggr_header_csv[config->aggr_mode], config->output); | ||
898 | } | ||
899 | |||
900 | /* Print metrics headers only */ | ||
901 | evlist__for_each_entry(evlist, counter) { | ||
902 | if (is_duration_time(counter)) | ||
903 | continue; | ||
904 | os.evsel = counter; | ||
905 | out.ctx = &os; | ||
906 | out.print_metric = print_metric_header; | ||
907 | out.new_line = new_line_metric; | ||
908 | out.force_header = true; | ||
909 | os.evsel = counter; | ||
910 | perf_stat__print_shadow_stats(config, counter, 0, | ||
911 | 0, | ||
912 | &out, | ||
913 | &config->metric_events, | ||
914 | &rt_stat); | ||
915 | } | ||
916 | fputc('\n', config->output); | ||
917 | } | ||
918 | |||
919 | static void print_interval(struct perf_stat_config *config, | ||
920 | struct perf_evlist *evlist, | ||
921 | char *prefix, struct timespec *ts) | ||
922 | { | ||
923 | bool metric_only = config->metric_only; | ||
924 | unsigned int unit_width = config->unit_width; | ||
925 | FILE *output = config->output; | ||
926 | static int num_print_interval; | ||
927 | |||
928 | if (config->interval_clear) | ||
929 | puts(CONSOLE_CLEAR); | ||
930 | |||
931 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, config->csv_sep); | ||
932 | |||
933 | if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) { | ||
934 | switch (config->aggr_mode) { | ||
935 | case AGGR_SOCKET: | ||
936 | fprintf(output, "# time socket cpus"); | ||
937 | if (!metric_only) | ||
938 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
939 | break; | ||
940 | case AGGR_CORE: | ||
941 | fprintf(output, "# time core cpus"); | ||
942 | if (!metric_only) | ||
943 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
944 | break; | ||
945 | case AGGR_NONE: | ||
946 | fprintf(output, "# time CPU "); | ||
947 | if (!metric_only) | ||
948 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
949 | break; | ||
950 | case AGGR_THREAD: | ||
951 | fprintf(output, "# time comm-pid"); | ||
952 | if (!metric_only) | ||
953 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
954 | break; | ||
955 | case AGGR_GLOBAL: | ||
956 | default: | ||
957 | fprintf(output, "# time"); | ||
958 | if (!metric_only) | ||
959 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | ||
960 | case AGGR_UNSET: | ||
961 | break; | ||
962 | } | ||
963 | } | ||
964 | |||
965 | if ((num_print_interval == 0 || config->interval_clear) && metric_only) | ||
966 | print_metric_headers(config, evlist, " ", true); | ||
967 | if (++num_print_interval == 25) | ||
968 | num_print_interval = 0; | ||
969 | } | ||
970 | |||
971 | static void print_header(struct perf_stat_config *config, | ||
972 | struct target *_target, | ||
973 | int argc, const char **argv) | ||
974 | { | ||
975 | FILE *output = config->output; | ||
976 | int i; | ||
977 | |||
978 | fflush(stdout); | ||
979 | |||
980 | if (!config->csv_output) { | ||
981 | fprintf(output, "\n"); | ||
982 | fprintf(output, " Performance counter stats for "); | ||
983 | if (_target->system_wide) | ||
984 | fprintf(output, "\'system wide"); | ||
985 | else if (_target->cpu_list) | ||
986 | fprintf(output, "\'CPU(s) %s", _target->cpu_list); | ||
987 | else if (!target__has_task(_target)) { | ||
988 | fprintf(output, "\'%s", argv ? argv[0] : "pipe"); | ||
989 | for (i = 1; argv && (i < argc); i++) | ||
990 | fprintf(output, " %s", argv[i]); | ||
991 | } else if (_target->pid) | ||
992 | fprintf(output, "process id \'%s", _target->pid); | ||
993 | else | ||
994 | fprintf(output, "thread id \'%s", _target->tid); | ||
995 | |||
996 | fprintf(output, "\'"); | ||
997 | if (config->run_count > 1) | ||
998 | fprintf(output, " (%d runs)", config->run_count); | ||
999 | fprintf(output, ":\n\n"); | ||
1000 | } | ||
1001 | } | ||
1002 | |||
1003 | static int get_precision(double num) | ||
1004 | { | ||
1005 | if (num > 1) | ||
1006 | return 0; | ||
1007 | |||
1008 | return lround(ceil(-log10(num))); | ||
1009 | } | ||
1010 | |||
1011 | static void print_table(struct perf_stat_config *config, | ||
1012 | FILE *output, int precision, double avg) | ||
1013 | { | ||
1014 | char tmp[64]; | ||
1015 | int idx, indent = 0; | ||
1016 | |||
1017 | scnprintf(tmp, 64, " %17.*f", precision, avg); | ||
1018 | while (tmp[indent] == ' ') | ||
1019 | indent++; | ||
1020 | |||
1021 | fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); | ||
1022 | |||
1023 | for (idx = 0; idx < config->run_count; idx++) { | ||
1024 | double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; | ||
1025 | int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); | ||
1026 | |||
1027 | fprintf(output, " %17.*f (%+.*f) ", | ||
1028 | precision, run, precision, run - avg); | ||
1029 | |||
1030 | for (h = 0; h < n; h++) | ||
1031 | fprintf(output, "#"); | ||
1032 | |||
1033 | fprintf(output, "\n"); | ||
1034 | } | ||
1035 | |||
1036 | fprintf(output, "\n%*s# Final result:\n", indent, ""); | ||
1037 | } | ||
1038 | |||
1039 | static double timeval2double(struct timeval *t) | ||
1040 | { | ||
1041 | return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; | ||
1042 | } | ||
1043 | |||
1044 | static void print_footer(struct perf_stat_config *config) | ||
1045 | { | ||
1046 | double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | ||
1047 | FILE *output = config->output; | ||
1048 | int n; | ||
1049 | |||
1050 | if (!config->null_run) | ||
1051 | fprintf(output, "\n"); | ||
1052 | |||
1053 | if (config->run_count == 1) { | ||
1054 | fprintf(output, " %17.9f seconds time elapsed", avg); | ||
1055 | |||
1056 | if (config->ru_display) { | ||
1057 | double ru_utime = timeval2double(&config->ru_data.ru_utime); | ||
1058 | double ru_stime = timeval2double(&config->ru_data.ru_stime); | ||
1059 | |||
1060 | fprintf(output, "\n\n"); | ||
1061 | fprintf(output, " %17.9f seconds user\n", ru_utime); | ||
1062 | fprintf(output, " %17.9f seconds sys\n", ru_stime); | ||
1063 | } | ||
1064 | } else { | ||
1065 | double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | ||
1066 | /* | ||
1067 | * Display at most 2 more significant | ||
1068 | * digits than the stddev inaccuracy. | ||
1069 | */ | ||
1070 | int precision = get_precision(sd) + 2; | ||
1071 | |||
1072 | if (config->walltime_run_table) | ||
1073 | print_table(config, output, precision, avg); | ||
1074 | |||
1075 | fprintf(output, " %17.*f +- %.*f seconds time elapsed", | ||
1076 | precision, avg, precision, sd); | ||
1077 | |||
1078 | print_noise_pct(config, sd, avg); | ||
1079 | } | ||
1080 | fprintf(output, "\n\n"); | ||
1081 | |||
1082 | if (config->print_free_counters_hint && | ||
1083 | sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 && | ||
1084 | n > 0) | ||
1085 | fprintf(output, | ||
1086 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | ||
1087 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | ||
1088 | " perf stat ...\n" | ||
1089 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | ||
1090 | |||
1091 | if (config->print_mixed_hw_group_error) | ||
1092 | fprintf(output, | ||
1093 | "The events in group usually have to be from " | ||
1094 | "the same PMU. Try reorganizing the group.\n"); | ||
1095 | } | ||
1096 | |||
1097 | void | ||
1098 | perf_evlist__print_counters(struct perf_evlist *evlist, | ||
1099 | struct perf_stat_config *config, | ||
1100 | struct target *_target, | ||
1101 | struct timespec *ts, | ||
1102 | int argc, const char **argv) | ||
1103 | { | ||
1104 | bool metric_only = config->metric_only; | ||
1105 | int interval = config->interval; | ||
1106 | struct perf_evsel *counter; | ||
1107 | char buf[64], *prefix = NULL; | ||
1108 | |||
1109 | if (interval) | ||
1110 | print_interval(config, evlist, prefix = buf, ts); | ||
1111 | else | ||
1112 | print_header(config, _target, argc, argv); | ||
1113 | |||
1114 | if (metric_only) { | ||
1115 | static int num_print_iv; | ||
1116 | |||
1117 | if (num_print_iv == 0 && !interval) | ||
1118 | print_metric_headers(config, evlist, prefix, false); | ||
1119 | if (num_print_iv++ == 25) | ||
1120 | num_print_iv = 0; | ||
1121 | if (config->aggr_mode == AGGR_GLOBAL && prefix) | ||
1122 | fprintf(config->output, "%s", prefix); | ||
1123 | } | ||
1124 | |||
1125 | switch (config->aggr_mode) { | ||
1126 | case AGGR_CORE: | ||
1127 | case AGGR_SOCKET: | ||
1128 | print_aggr(config, evlist, prefix); | ||
1129 | break; | ||
1130 | case AGGR_THREAD: | ||
1131 | evlist__for_each_entry(evlist, counter) { | ||
1132 | if (is_duration_time(counter)) | ||
1133 | continue; | ||
1134 | print_aggr_thread(config, _target, counter, prefix); | ||
1135 | } | ||
1136 | break; | ||
1137 | case AGGR_GLOBAL: | ||
1138 | evlist__for_each_entry(evlist, counter) { | ||
1139 | if (is_duration_time(counter)) | ||
1140 | continue; | ||
1141 | print_counter_aggr(config, counter, prefix); | ||
1142 | } | ||
1143 | if (metric_only) | ||
1144 | fputc('\n', config->output); | ||
1145 | break; | ||
1146 | case AGGR_NONE: | ||
1147 | if (metric_only) | ||
1148 | print_no_aggr_metric(config, evlist, prefix); | ||
1149 | else { | ||
1150 | evlist__for_each_entry(evlist, counter) { | ||
1151 | if (is_duration_time(counter)) | ||
1152 | continue; | ||
1153 | print_counter(config, counter, prefix); | ||
1154 | } | ||
1155 | } | ||
1156 | break; | ||
1157 | case AGGR_UNSET: | ||
1158 | default: | ||
1159 | break; | ||
1160 | } | ||
1161 | |||
1162 | if (!interval && !config->csv_output) | ||
1163 | print_footer(config); | ||
1164 | |||
1165 | fflush(config->output); | ||
1166 | } | ||
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 99990f5f2512..8ad32763cfff 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c | |||
@@ -410,7 +410,8 @@ static double runtime_stat_n(struct runtime_stat *st, | |||
410 | return v->stats.n; | 410 | return v->stats.n; |
411 | } | 411 | } |
412 | 412 | ||
413 | static void print_stalled_cycles_frontend(int cpu, | 413 | static void print_stalled_cycles_frontend(struct perf_stat_config *config, |
414 | int cpu, | ||
414 | struct perf_evsel *evsel, double avg, | 415 | struct perf_evsel *evsel, double avg, |
415 | struct perf_stat_output_ctx *out, | 416 | struct perf_stat_output_ctx *out, |
416 | struct runtime_stat *st) | 417 | struct runtime_stat *st) |
@@ -427,13 +428,14 @@ static void print_stalled_cycles_frontend(int cpu, | |||
427 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); | 428 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); |
428 | 429 | ||
429 | if (ratio) | 430 | if (ratio) |
430 | out->print_metric(out->ctx, color, "%7.2f%%", "frontend cycles idle", | 431 | out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle", |
431 | ratio); | 432 | ratio); |
432 | else | 433 | else |
433 | out->print_metric(out->ctx, NULL, NULL, "frontend cycles idle", 0); | 434 | out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0); |
434 | } | 435 | } |
435 | 436 | ||
436 | static void print_stalled_cycles_backend(int cpu, | 437 | static void print_stalled_cycles_backend(struct perf_stat_config *config, |
438 | int cpu, | ||
437 | struct perf_evsel *evsel, double avg, | 439 | struct perf_evsel *evsel, double avg, |
438 | struct perf_stat_output_ctx *out, | 440 | struct perf_stat_output_ctx *out, |
439 | struct runtime_stat *st) | 441 | struct runtime_stat *st) |
@@ -449,10 +451,11 @@ static void print_stalled_cycles_backend(int cpu, | |||
449 | 451 | ||
450 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); | 452 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); |
451 | 453 | ||
452 | out->print_metric(out->ctx, color, "%7.2f%%", "backend cycles idle", ratio); | 454 | out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio); |
453 | } | 455 | } |
454 | 456 | ||
455 | static void print_branch_misses(int cpu, | 457 | static void print_branch_misses(struct perf_stat_config *config, |
458 | int cpu, | ||
456 | struct perf_evsel *evsel, | 459 | struct perf_evsel *evsel, |
457 | double avg, | 460 | double avg, |
458 | struct perf_stat_output_ctx *out, | 461 | struct perf_stat_output_ctx *out, |
@@ -469,10 +472,11 @@ static void print_branch_misses(int cpu, | |||
469 | 472 | ||
470 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 473 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
471 | 474 | ||
472 | out->print_metric(out->ctx, color, "%7.2f%%", "of all branches", ratio); | 475 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio); |
473 | } | 476 | } |
474 | 477 | ||
475 | static void print_l1_dcache_misses(int cpu, | 478 | static void print_l1_dcache_misses(struct perf_stat_config *config, |
479 | int cpu, | ||
476 | struct perf_evsel *evsel, | 480 | struct perf_evsel *evsel, |
477 | double avg, | 481 | double avg, |
478 | struct perf_stat_output_ctx *out, | 482 | struct perf_stat_output_ctx *out, |
@@ -490,10 +494,11 @@ static void print_l1_dcache_misses(int cpu, | |||
490 | 494 | ||
491 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 495 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
492 | 496 | ||
493 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio); | 497 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio); |
494 | } | 498 | } |
495 | 499 | ||
496 | static void print_l1_icache_misses(int cpu, | 500 | static void print_l1_icache_misses(struct perf_stat_config *config, |
501 | int cpu, | ||
497 | struct perf_evsel *evsel, | 502 | struct perf_evsel *evsel, |
498 | double avg, | 503 | double avg, |
499 | struct perf_stat_output_ctx *out, | 504 | struct perf_stat_output_ctx *out, |
@@ -510,10 +515,11 @@ static void print_l1_icache_misses(int cpu, | |||
510 | ratio = avg / total * 100.0; | 515 | ratio = avg / total * 100.0; |
511 | 516 | ||
512 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 517 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
513 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio); | 518 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio); |
514 | } | 519 | } |
515 | 520 | ||
516 | static void print_dtlb_cache_misses(int cpu, | 521 | static void print_dtlb_cache_misses(struct perf_stat_config *config, |
522 | int cpu, | ||
517 | struct perf_evsel *evsel, | 523 | struct perf_evsel *evsel, |
518 | double avg, | 524 | double avg, |
519 | struct perf_stat_output_ctx *out, | 525 | struct perf_stat_output_ctx *out, |
@@ -529,10 +535,11 @@ static void print_dtlb_cache_misses(int cpu, | |||
529 | ratio = avg / total * 100.0; | 535 | ratio = avg / total * 100.0; |
530 | 536 | ||
531 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 537 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
532 | out->print_metric(out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio); | 538 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio); |
533 | } | 539 | } |
534 | 540 | ||
535 | static void print_itlb_cache_misses(int cpu, | 541 | static void print_itlb_cache_misses(struct perf_stat_config *config, |
542 | int cpu, | ||
536 | struct perf_evsel *evsel, | 543 | struct perf_evsel *evsel, |
537 | double avg, | 544 | double avg, |
538 | struct perf_stat_output_ctx *out, | 545 | struct perf_stat_output_ctx *out, |
@@ -548,10 +555,11 @@ static void print_itlb_cache_misses(int cpu, | |||
548 | ratio = avg / total * 100.0; | 555 | ratio = avg / total * 100.0; |
549 | 556 | ||
550 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 557 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
551 | out->print_metric(out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio); | 558 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio); |
552 | } | 559 | } |
553 | 560 | ||
554 | static void print_ll_cache_misses(int cpu, | 561 | static void print_ll_cache_misses(struct perf_stat_config *config, |
562 | int cpu, | ||
555 | struct perf_evsel *evsel, | 563 | struct perf_evsel *evsel, |
556 | double avg, | 564 | double avg, |
557 | struct perf_stat_output_ctx *out, | 565 | struct perf_stat_output_ctx *out, |
@@ -567,7 +575,7 @@ static void print_ll_cache_misses(int cpu, | |||
567 | ratio = avg / total * 100.0; | 575 | ratio = avg / total * 100.0; |
568 | 576 | ||
569 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 577 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
570 | out->print_metric(out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio); | 578 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio); |
571 | } | 579 | } |
572 | 580 | ||
573 | /* | 581 | /* |
@@ -674,7 +682,8 @@ static double td_be_bound(int ctx, int cpu, struct runtime_stat *st) | |||
674 | return sanitize_val(1.0 - sum); | 682 | return sanitize_val(1.0 - sum); |
675 | } | 683 | } |
676 | 684 | ||
677 | static void print_smi_cost(int cpu, struct perf_evsel *evsel, | 685 | static void print_smi_cost(struct perf_stat_config *config, |
686 | int cpu, struct perf_evsel *evsel, | ||
678 | struct perf_stat_output_ctx *out, | 687 | struct perf_stat_output_ctx *out, |
679 | struct runtime_stat *st) | 688 | struct runtime_stat *st) |
680 | { | 689 | { |
@@ -694,11 +703,12 @@ static void print_smi_cost(int cpu, struct perf_evsel *evsel, | |||
694 | 703 | ||
695 | if (cost > 10) | 704 | if (cost > 10) |
696 | color = PERF_COLOR_RED; | 705 | color = PERF_COLOR_RED; |
697 | out->print_metric(out->ctx, color, "%8.1f%%", "SMI cycles%", cost); | 706 | out->print_metric(config, out->ctx, color, "%8.1f%%", "SMI cycles%", cost); |
698 | out->print_metric(out->ctx, NULL, "%4.0f", "SMI#", smi_num); | 707 | out->print_metric(config, out->ctx, NULL, "%4.0f", "SMI#", smi_num); |
699 | } | 708 | } |
700 | 709 | ||
701 | static void generic_metric(const char *metric_expr, | 710 | static void generic_metric(struct perf_stat_config *config, |
711 | const char *metric_expr, | ||
702 | struct perf_evsel **metric_events, | 712 | struct perf_evsel **metric_events, |
703 | char *name, | 713 | char *name, |
704 | const char *metric_name, | 714 | const char *metric_name, |
@@ -737,20 +747,21 @@ static void generic_metric(const char *metric_expr, | |||
737 | const char *p = metric_expr; | 747 | const char *p = metric_expr; |
738 | 748 | ||
739 | if (expr__parse(&ratio, &pctx, &p) == 0) | 749 | if (expr__parse(&ratio, &pctx, &p) == 0) |
740 | print_metric(ctxp, NULL, "%8.1f", | 750 | print_metric(config, ctxp, NULL, "%8.1f", |
741 | metric_name ? | 751 | metric_name ? |
742 | metric_name : | 752 | metric_name : |
743 | out->force_header ? name : "", | 753 | out->force_header ? name : "", |
744 | ratio); | 754 | ratio); |
745 | else | 755 | else |
746 | print_metric(ctxp, NULL, NULL, | 756 | print_metric(config, ctxp, NULL, NULL, |
747 | out->force_header ? | 757 | out->force_header ? |
748 | (metric_name ? metric_name : name) : "", 0); | 758 | (metric_name ? metric_name : name) : "", 0); |
749 | } else | 759 | } else |
750 | print_metric(ctxp, NULL, NULL, "", 0); | 760 | print_metric(config, ctxp, NULL, NULL, "", 0); |
751 | } | 761 | } |
752 | 762 | ||
753 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | 763 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
764 | struct perf_evsel *evsel, | ||
754 | double avg, int cpu, | 765 | double avg, int cpu, |
755 | struct perf_stat_output_ctx *out, | 766 | struct perf_stat_output_ctx *out, |
756 | struct rblist *metric_events, | 767 | struct rblist *metric_events, |
@@ -769,10 +780,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
769 | 780 | ||
770 | if (total) { | 781 | if (total) { |
771 | ratio = avg / total; | 782 | ratio = avg / total; |
772 | print_metric(ctxp, NULL, "%7.2f ", | 783 | print_metric(config, ctxp, NULL, "%7.2f ", |
773 | "insn per cycle", ratio); | 784 | "insn per cycle", ratio); |
774 | } else { | 785 | } else { |
775 | print_metric(ctxp, NULL, NULL, "insn per cycle", 0); | 786 | print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0); |
776 | } | 787 | } |
777 | 788 | ||
778 | total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, | 789 | total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, |
@@ -783,20 +794,20 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
783 | ctx, cpu)); | 794 | ctx, cpu)); |
784 | 795 | ||
785 | if (total && avg) { | 796 | if (total && avg) { |
786 | out->new_line(ctxp); | 797 | out->new_line(config, ctxp); |
787 | ratio = total / avg; | 798 | ratio = total / avg; |
788 | print_metric(ctxp, NULL, "%7.2f ", | 799 | print_metric(config, ctxp, NULL, "%7.2f ", |
789 | "stalled cycles per insn", | 800 | "stalled cycles per insn", |
790 | ratio); | 801 | ratio); |
791 | } else if (have_frontend_stalled) { | 802 | } else if (have_frontend_stalled) { |
792 | print_metric(ctxp, NULL, NULL, | 803 | print_metric(config, ctxp, NULL, NULL, |
793 | "stalled cycles per insn", 0); | 804 | "stalled cycles per insn", 0); |
794 | } | 805 | } |
795 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { | 806 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { |
796 | if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0) | 807 | if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0) |
797 | print_branch_misses(cpu, evsel, avg, out, st); | 808 | print_branch_misses(config, cpu, evsel, avg, out, st); |
798 | else | 809 | else |
799 | print_metric(ctxp, NULL, NULL, "of all branches", 0); | 810 | print_metric(config, ctxp, NULL, NULL, "of all branches", 0); |
800 | } else if ( | 811 | } else if ( |
801 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 812 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
802 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | | 813 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | |
@@ -804,9 +815,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
804 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 815 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
805 | 816 | ||
806 | if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0) | 817 | if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0) |
807 | print_l1_dcache_misses(cpu, evsel, avg, out, st); | 818 | print_l1_dcache_misses(config, cpu, evsel, avg, out, st); |
808 | else | 819 | else |
809 | print_metric(ctxp, NULL, NULL, "of all L1-dcache hits", 0); | 820 | print_metric(config, ctxp, NULL, NULL, "of all L1-dcache hits", 0); |
810 | } else if ( | 821 | } else if ( |
811 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 822 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
812 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | | 823 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | |
@@ -814,9 +825,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
814 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 825 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
815 | 826 | ||
816 | if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0) | 827 | if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0) |
817 | print_l1_icache_misses(cpu, evsel, avg, out, st); | 828 | print_l1_icache_misses(config, cpu, evsel, avg, out, st); |
818 | else | 829 | else |
819 | print_metric(ctxp, NULL, NULL, "of all L1-icache hits", 0); | 830 | print_metric(config, ctxp, NULL, NULL, "of all L1-icache hits", 0); |
820 | } else if ( | 831 | } else if ( |
821 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 832 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
822 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | | 833 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | |
@@ -824,9 +835,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
824 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 835 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
825 | 836 | ||
826 | if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0) | 837 | if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0) |
827 | print_dtlb_cache_misses(cpu, evsel, avg, out, st); | 838 | print_dtlb_cache_misses(config, cpu, evsel, avg, out, st); |
828 | else | 839 | else |
829 | print_metric(ctxp, NULL, NULL, "of all dTLB cache hits", 0); | 840 | print_metric(config, ctxp, NULL, NULL, "of all dTLB cache hits", 0); |
830 | } else if ( | 841 | } else if ( |
831 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 842 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
832 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | | 843 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | |
@@ -834,9 +845,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
834 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 845 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
835 | 846 | ||
836 | if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0) | 847 | if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0) |
837 | print_itlb_cache_misses(cpu, evsel, avg, out, st); | 848 | print_itlb_cache_misses(config, cpu, evsel, avg, out, st); |
838 | else | 849 | else |
839 | print_metric(ctxp, NULL, NULL, "of all iTLB cache hits", 0); | 850 | print_metric(config, ctxp, NULL, NULL, "of all iTLB cache hits", 0); |
840 | } else if ( | 851 | } else if ( |
841 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 852 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
842 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | | 853 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | |
@@ -844,9 +855,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
844 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { | 855 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
845 | 856 | ||
846 | if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0) | 857 | if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0) |
847 | print_ll_cache_misses(cpu, evsel, avg, out, st); | 858 | print_ll_cache_misses(config, cpu, evsel, avg, out, st); |
848 | else | 859 | else |
849 | print_metric(ctxp, NULL, NULL, "of all LL-cache hits", 0); | 860 | print_metric(config, ctxp, NULL, NULL, "of all LL-cache hits", 0); |
850 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { | 861 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { |
851 | total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu); | 862 | total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu); |
852 | 863 | ||
@@ -854,32 +865,32 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
854 | ratio = avg * 100 / total; | 865 | ratio = avg * 100 / total; |
855 | 866 | ||
856 | if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0) | 867 | if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0) |
857 | print_metric(ctxp, NULL, "%8.3f %%", | 868 | print_metric(config, ctxp, NULL, "%8.3f %%", |
858 | "of all cache refs", ratio); | 869 | "of all cache refs", ratio); |
859 | else | 870 | else |
860 | print_metric(ctxp, NULL, NULL, "of all cache refs", 0); | 871 | print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0); |
861 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { | 872 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
862 | print_stalled_cycles_frontend(cpu, evsel, avg, out, st); | 873 | print_stalled_cycles_frontend(config, cpu, evsel, avg, out, st); |
863 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { | 874 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { |
864 | print_stalled_cycles_backend(cpu, evsel, avg, out, st); | 875 | print_stalled_cycles_backend(config, cpu, evsel, avg, out, st); |
865 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { | 876 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { |
866 | total = runtime_stat_avg(st, STAT_NSECS, 0, cpu); | 877 | total = runtime_stat_avg(st, STAT_NSECS, 0, cpu); |
867 | 878 | ||
868 | if (total) { | 879 | if (total) { |
869 | ratio = avg / total; | 880 | ratio = avg / total; |
870 | print_metric(ctxp, NULL, "%8.3f", "GHz", ratio); | 881 | print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio); |
871 | } else { | 882 | } else { |
872 | print_metric(ctxp, NULL, NULL, "Ghz", 0); | 883 | print_metric(config, ctxp, NULL, NULL, "Ghz", 0); |
873 | } | 884 | } |
874 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { | 885 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { |
875 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); | 886 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); |
876 | 887 | ||
877 | if (total) | 888 | if (total) |
878 | print_metric(ctxp, NULL, | 889 | print_metric(config, ctxp, NULL, |
879 | "%7.2f%%", "transactional cycles", | 890 | "%7.2f%%", "transactional cycles", |
880 | 100.0 * (avg / total)); | 891 | 100.0 * (avg / total)); |
881 | else | 892 | else |
882 | print_metric(ctxp, NULL, NULL, "transactional cycles", | 893 | print_metric(config, ctxp, NULL, NULL, "transactional cycles", |
883 | 0); | 894 | 0); |
884 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { | 895 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { |
885 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); | 896 | total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu); |
@@ -888,10 +899,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
888 | if (total2 < avg) | 899 | if (total2 < avg) |
889 | total2 = avg; | 900 | total2 = avg; |
890 | if (total) | 901 | if (total) |
891 | print_metric(ctxp, NULL, "%7.2f%%", "aborted cycles", | 902 | print_metric(config, ctxp, NULL, "%7.2f%%", "aborted cycles", |
892 | 100.0 * ((total2-avg) / total)); | 903 | 100.0 * ((total2-avg) / total)); |
893 | else | 904 | else |
894 | print_metric(ctxp, NULL, NULL, "aborted cycles", 0); | 905 | print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0); |
895 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) { | 906 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) { |
896 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, | 907 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, |
897 | ctx, cpu); | 908 | ctx, cpu); |
@@ -900,10 +911,10 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
900 | ratio = total / avg; | 911 | ratio = total / avg; |
901 | 912 | ||
902 | if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0) | 913 | if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0) |
903 | print_metric(ctxp, NULL, "%8.0f", | 914 | print_metric(config, ctxp, NULL, "%8.0f", |
904 | "cycles / transaction", ratio); | 915 | "cycles / transaction", ratio); |
905 | else | 916 | else |
906 | print_metric(ctxp, NULL, NULL, "cycles / transaction", | 917 | print_metric(config, ctxp, NULL, NULL, "cycles / transaction", |
907 | 0); | 918 | 0); |
908 | } else if (perf_stat_evsel__is(evsel, ELISION_START)) { | 919 | } else if (perf_stat_evsel__is(evsel, ELISION_START)) { |
909 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, | 920 | total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, |
@@ -912,33 +923,33 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
912 | if (avg) | 923 | if (avg) |
913 | ratio = total / avg; | 924 | ratio = total / avg; |
914 | 925 | ||
915 | print_metric(ctxp, NULL, "%8.0f", "cycles / elision", ratio); | 926 | print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio); |
916 | } else if (perf_evsel__is_clock(evsel)) { | 927 | } else if (perf_evsel__is_clock(evsel)) { |
917 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) | 928 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) |
918 | print_metric(ctxp, NULL, "%8.3f", "CPUs utilized", | 929 | print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized", |
919 | avg / (ratio * evsel->scale)); | 930 | avg / (ratio * evsel->scale)); |
920 | else | 931 | else |
921 | print_metric(ctxp, NULL, NULL, "CPUs utilized", 0); | 932 | print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0); |
922 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) { | 933 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) { |
923 | double fe_bound = td_fe_bound(ctx, cpu, st); | 934 | double fe_bound = td_fe_bound(ctx, cpu, st); |
924 | 935 | ||
925 | if (fe_bound > 0.2) | 936 | if (fe_bound > 0.2) |
926 | color = PERF_COLOR_RED; | 937 | color = PERF_COLOR_RED; |
927 | print_metric(ctxp, color, "%8.1f%%", "frontend bound", | 938 | print_metric(config, ctxp, color, "%8.1f%%", "frontend bound", |
928 | fe_bound * 100.); | 939 | fe_bound * 100.); |
929 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) { | 940 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) { |
930 | double retiring = td_retiring(ctx, cpu, st); | 941 | double retiring = td_retiring(ctx, cpu, st); |
931 | 942 | ||
932 | if (retiring > 0.7) | 943 | if (retiring > 0.7) |
933 | color = PERF_COLOR_GREEN; | 944 | color = PERF_COLOR_GREEN; |
934 | print_metric(ctxp, color, "%8.1f%%", "retiring", | 945 | print_metric(config, ctxp, color, "%8.1f%%", "retiring", |
935 | retiring * 100.); | 946 | retiring * 100.); |
936 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) { | 947 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) { |
937 | double bad_spec = td_bad_spec(ctx, cpu, st); | 948 | double bad_spec = td_bad_spec(ctx, cpu, st); |
938 | 949 | ||
939 | if (bad_spec > 0.1) | 950 | if (bad_spec > 0.1) |
940 | color = PERF_COLOR_RED; | 951 | color = PERF_COLOR_RED; |
941 | print_metric(ctxp, color, "%8.1f%%", "bad speculation", | 952 | print_metric(config, ctxp, color, "%8.1f%%", "bad speculation", |
942 | bad_spec * 100.); | 953 | bad_spec * 100.); |
943 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) { | 954 | } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) { |
944 | double be_bound = td_be_bound(ctx, cpu, st); | 955 | double be_bound = td_be_bound(ctx, cpu, st); |
@@ -955,12 +966,12 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
955 | if (be_bound > 0.2) | 966 | if (be_bound > 0.2) |
956 | color = PERF_COLOR_RED; | 967 | color = PERF_COLOR_RED; |
957 | if (td_total_slots(ctx, cpu, st) > 0) | 968 | if (td_total_slots(ctx, cpu, st) > 0) |
958 | print_metric(ctxp, color, "%8.1f%%", name, | 969 | print_metric(config, ctxp, color, "%8.1f%%", name, |
959 | be_bound * 100.); | 970 | be_bound * 100.); |
960 | else | 971 | else |
961 | print_metric(ctxp, NULL, NULL, name, 0); | 972 | print_metric(config, ctxp, NULL, NULL, name, 0); |
962 | } else if (evsel->metric_expr) { | 973 | } else if (evsel->metric_expr) { |
963 | generic_metric(evsel->metric_expr, evsel->metric_events, evsel->name, | 974 | generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name, |
964 | evsel->metric_name, avg, cpu, out, st); | 975 | evsel->metric_name, avg, cpu, out, st); |
965 | } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) { | 976 | } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) { |
966 | char unit = 'M'; | 977 | char unit = 'M'; |
@@ -975,9 +986,9 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
975 | unit = 'K'; | 986 | unit = 'K'; |
976 | } | 987 | } |
977 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); | 988 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); |
978 | print_metric(ctxp, NULL, "%8.3f", unit_buf, ratio); | 989 | print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio); |
979 | } else if (perf_stat_evsel__is(evsel, SMI_NUM)) { | 990 | } else if (perf_stat_evsel__is(evsel, SMI_NUM)) { |
980 | print_smi_cost(cpu, evsel, out, st); | 991 | print_smi_cost(config, cpu, evsel, out, st); |
981 | } else { | 992 | } else { |
982 | num = 0; | 993 | num = 0; |
983 | } | 994 | } |
@@ -987,12 +998,12 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | |||
987 | 998 | ||
988 | list_for_each_entry (mexp, &me->head, nd) { | 999 | list_for_each_entry (mexp, &me->head, nd) { |
989 | if (num++ > 0) | 1000 | if (num++ > 0) |
990 | out->new_line(ctxp); | 1001 | out->new_line(config, ctxp); |
991 | generic_metric(mexp->metric_expr, mexp->metric_events, | 1002 | generic_metric(config, mexp->metric_expr, mexp->metric_events, |
992 | evsel->name, mexp->metric_name, | 1003 | evsel->name, mexp->metric_name, |
993 | avg, cpu, out, st); | 1004 | avg, cpu, out, st); |
994 | } | 1005 | } |
995 | } | 1006 | } |
996 | if (num == 0) | 1007 | if (num == 0) |
997 | print_metric(ctxp, NULL, NULL, NULL, 0); | 1008 | print_metric(config, ctxp, NULL, NULL, NULL, 0); |
998 | } | 1009 | } |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index a0061e0b0fad..4d40515307b8 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
@@ -374,9 +374,8 @@ int perf_stat_process_counter(struct perf_stat_config *config, | |||
374 | return 0; | 374 | return 0; |
375 | } | 375 | } |
376 | 376 | ||
377 | int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, | 377 | int perf_event__process_stat_event(struct perf_session *session, |
378 | union perf_event *event, | 378 | union perf_event *event) |
379 | struct perf_session *session) | ||
380 | { | 379 | { |
381 | struct perf_counts_values count; | 380 | struct perf_counts_values count; |
382 | struct stat_event *st = &event->stat; | 381 | struct stat_event *st = &event->stat; |
@@ -435,3 +434,98 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) | |||
435 | 434 | ||
436 | return ret; | 435 | return ret; |
437 | } | 436 | } |
437 | |||
438 | int create_perf_stat_counter(struct perf_evsel *evsel, | ||
439 | struct perf_stat_config *config, | ||
440 | struct target *target) | ||
441 | { | ||
442 | struct perf_event_attr *attr = &evsel->attr; | ||
443 | struct perf_evsel *leader = evsel->leader; | ||
444 | |||
445 | if (config->scale) { | ||
446 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
447 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * The event is part of non trivial group, let's enable | ||
452 | * the group read (for leader) and ID retrieval for all | ||
453 | * members. | ||
454 | */ | ||
455 | if (leader->nr_members > 1) | ||
456 | attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP; | ||
457 | |||
458 | attr->inherit = !config->no_inherit; | ||
459 | |||
460 | /* | ||
461 | * Some events get initialized with sample_(period/type) set, | ||
462 | * like tracepoints. Clear it up for counting. | ||
463 | */ | ||
464 | attr->sample_period = 0; | ||
465 | |||
466 | if (config->identifier) | ||
467 | attr->sample_type = PERF_SAMPLE_IDENTIFIER; | ||
468 | |||
469 | /* | ||
470 | * Disabling all counters initially, they will be enabled | ||
471 | * either manually by us or by kernel via enable_on_exec | ||
472 | * set later. | ||
473 | */ | ||
474 | if (perf_evsel__is_group_leader(evsel)) { | ||
475 | attr->disabled = 1; | ||
476 | |||
477 | /* | ||
478 | * In case of initial_delay we enable tracee | ||
479 | * events manually. | ||
480 | */ | ||
481 | if (target__none(target) && !config->initial_delay) | ||
482 | attr->enable_on_exec = 1; | ||
483 | } | ||
484 | |||
485 | if (target__has_cpu(target) && !target__has_per_thread(target)) | ||
486 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
487 | |||
488 | return perf_evsel__open_per_thread(evsel, evsel->threads); | ||
489 | } | ||
490 | |||
491 | int perf_stat_synthesize_config(struct perf_stat_config *config, | ||
492 | struct perf_tool *tool, | ||
493 | struct perf_evlist *evlist, | ||
494 | perf_event__handler_t process, | ||
495 | bool attrs) | ||
496 | { | ||
497 | int err; | ||
498 | |||
499 | if (attrs) { | ||
500 | err = perf_event__synthesize_attrs(tool, evlist, process); | ||
501 | if (err < 0) { | ||
502 | pr_err("Couldn't synthesize attrs.\n"); | ||
503 | return err; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | err = perf_event__synthesize_extra_attr(tool, evlist, process, | ||
508 | attrs); | ||
509 | |||
510 | err = perf_event__synthesize_thread_map2(tool, evlist->threads, | ||
511 | process, NULL); | ||
512 | if (err < 0) { | ||
513 | pr_err("Couldn't synthesize thread map.\n"); | ||
514 | return err; | ||
515 | } | ||
516 | |||
517 | err = perf_event__synthesize_cpu_map(tool, evlist->cpus, | ||
518 | process, NULL); | ||
519 | if (err < 0) { | ||
520 | pr_err("Couldn't synthesize thread map.\n"); | ||
521 | return err; | ||
522 | } | ||
523 | |||
524 | err = perf_event__synthesize_stat_config(tool, config, process, NULL); | ||
525 | if (err < 0) { | ||
526 | pr_err("Couldn't synthesize config.\n"); | ||
527 | return err; | ||
528 | } | ||
529 | |||
530 | return 0; | ||
531 | } | ||
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 36efb986f7fc..2f9c9159a364 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
@@ -4,8 +4,14 @@ | |||
4 | 4 | ||
5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <sys/types.h> | ||
8 | #include <sys/time.h> | ||
9 | #include <sys/resource.h> | ||
10 | #include <sys/wait.h> | ||
7 | #include "xyarray.h" | 11 | #include "xyarray.h" |
8 | #include "rblist.h" | 12 | #include "rblist.h" |
13 | #include "perf.h" | ||
14 | #include "event.h" | ||
9 | 15 | ||
10 | struct stats { | 16 | struct stats { |
11 | double n, mean, M2; | 17 | double n, mean, M2; |
@@ -84,15 +90,42 @@ struct runtime_stat { | |||
84 | struct rblist value_list; | 90 | struct rblist value_list; |
85 | }; | 91 | }; |
86 | 92 | ||
93 | typedef int (*aggr_get_id_t)(struct perf_stat_config *config, | ||
94 | struct cpu_map *m, int cpu); | ||
95 | |||
87 | struct perf_stat_config { | 96 | struct perf_stat_config { |
88 | enum aggr_mode aggr_mode; | 97 | enum aggr_mode aggr_mode; |
89 | bool scale; | 98 | bool scale; |
90 | FILE *output; | 99 | bool no_inherit; |
91 | unsigned int interval; | 100 | bool identifier; |
92 | unsigned int timeout; | 101 | bool csv_output; |
93 | int times; | 102 | bool interval_clear; |
94 | struct runtime_stat *stats; | 103 | bool metric_only; |
95 | int stats_num; | 104 | bool null_run; |
105 | bool ru_display; | ||
106 | bool big_num; | ||
107 | bool no_merge; | ||
108 | bool walltime_run_table; | ||
109 | FILE *output; | ||
110 | unsigned int interval; | ||
111 | unsigned int timeout; | ||
112 | unsigned int initial_delay; | ||
113 | unsigned int unit_width; | ||
114 | unsigned int metric_only_len; | ||
115 | int times; | ||
116 | int run_count; | ||
117 | int print_free_counters_hint; | ||
118 | int print_mixed_hw_group_error; | ||
119 | struct runtime_stat *stats; | ||
120 | int stats_num; | ||
121 | const char *csv_sep; | ||
122 | struct stats *walltime_nsecs_stats; | ||
123 | struct rusage ru_data; | ||
124 | struct cpu_map *aggr_map; | ||
125 | aggr_get_id_t aggr_get_id; | ||
126 | struct cpu_map *cpus_aggr_map; | ||
127 | u64 *walltime_run; | ||
128 | struct rblist metric_events; | ||
96 | }; | 129 | }; |
97 | 130 | ||
98 | void update_stats(struct stats *stats, u64 val); | 131 | void update_stats(struct stats *stats, u64 val); |
@@ -130,9 +163,10 @@ bool __perf_evsel_stat__is(struct perf_evsel *evsel, | |||
130 | extern struct runtime_stat rt_stat; | 163 | extern struct runtime_stat rt_stat; |
131 | extern struct stats walltime_nsecs_stats; | 164 | extern struct stats walltime_nsecs_stats; |
132 | 165 | ||
133 | typedef void (*print_metric_t)(void *ctx, const char *color, const char *unit, | 166 | typedef void (*print_metric_t)(struct perf_stat_config *config, |
167 | void *ctx, const char *color, const char *unit, | ||
134 | const char *fmt, double val); | 168 | const char *fmt, double val); |
135 | typedef void (*new_line_t )(void *ctx); | 169 | typedef void (*new_line_t)(struct perf_stat_config *config, void *ctx); |
136 | 170 | ||
137 | void runtime_stat__init(struct runtime_stat *st); | 171 | void runtime_stat__init(struct runtime_stat *st); |
138 | void runtime_stat__exit(struct runtime_stat *st); | 172 | void runtime_stat__exit(struct runtime_stat *st); |
@@ -148,7 +182,8 @@ struct perf_stat_output_ctx { | |||
148 | bool force_header; | 182 | bool force_header; |
149 | }; | 183 | }; |
150 | 184 | ||
151 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, | 185 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
186 | struct perf_evsel *evsel, | ||
152 | double avg, int cpu, | 187 | double avg, int cpu, |
153 | struct perf_stat_output_ctx *out, | 188 | struct perf_stat_output_ctx *out, |
154 | struct rblist *metric_events, | 189 | struct rblist *metric_events, |
@@ -164,11 +199,25 @@ int perf_stat_process_counter(struct perf_stat_config *config, | |||
164 | struct perf_tool; | 199 | struct perf_tool; |
165 | union perf_event; | 200 | union perf_event; |
166 | struct perf_session; | 201 | struct perf_session; |
167 | int perf_event__process_stat_event(struct perf_tool *tool, | 202 | int perf_event__process_stat_event(struct perf_session *session, |
168 | union perf_event *event, | 203 | union perf_event *event); |
169 | struct perf_session *session); | ||
170 | 204 | ||
171 | size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); | 205 | size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); |
172 | size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); | 206 | size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); |
173 | size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); | 207 | size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); |
208 | |||
209 | int create_perf_stat_counter(struct perf_evsel *evsel, | ||
210 | struct perf_stat_config *config, | ||
211 | struct target *target); | ||
212 | int perf_stat_synthesize_config(struct perf_stat_config *config, | ||
213 | struct perf_tool *tool, | ||
214 | struct perf_evlist *evlist, | ||
215 | perf_event__handler_t process, | ||
216 | bool attrs); | ||
217 | void | ||
218 | perf_evlist__print_counters(struct perf_evlist *evlist, | ||
219 | struct perf_stat_config *config, | ||
220 | struct target *_target, | ||
221 | struct timespec *ts, | ||
222 | int argc, const char **argv); | ||
174 | #endif | 223 | #endif |
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 3d1cf5bf7f18..9005fbe0780e 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c | |||
@@ -98,19 +98,25 @@ static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap) | |||
98 | 98 | ||
99 | va_copy(ap_saved, ap); | 99 | va_copy(ap_saved, ap); |
100 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 100 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
101 | if (len < 0) | 101 | if (len < 0) { |
102 | va_end(ap_saved); | ||
102 | return len; | 103 | return len; |
104 | } | ||
103 | if (len > strbuf_avail(sb)) { | 105 | if (len > strbuf_avail(sb)) { |
104 | ret = strbuf_grow(sb, len); | 106 | ret = strbuf_grow(sb, len); |
105 | if (ret) | 107 | if (ret) { |
108 | va_end(ap_saved); | ||
106 | return ret; | 109 | return ret; |
110 | } | ||
107 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved); | 111 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved); |
108 | va_end(ap_saved); | 112 | va_end(ap_saved); |
109 | if (len > strbuf_avail(sb)) { | 113 | if (len > strbuf_avail(sb)) { |
110 | pr_debug("this should not happen, your vsnprintf is broken"); | 114 | pr_debug("this should not happen, your vsnprintf is broken"); |
115 | va_end(ap_saved); | ||
111 | return -EINVAL; | 116 | return -EINVAL; |
112 | } | 117 | } |
113 | } | 118 | } |
119 | va_end(ap_saved); | ||
114 | return strbuf_setlen(sb, sb->len + len); | 120 | return strbuf_setlen(sb, sb->len + len); |
115 | } | 121 | } |
116 | 122 | ||
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 29770ea61768..66a84d5846c8 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -324,7 +324,17 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss) | |||
324 | plt_entry_size = 16; | 324 | plt_entry_size = 16; |
325 | break; | 325 | break; |
326 | 326 | ||
327 | default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/sparc/xtensa need to be checked */ | 327 | case EM_SPARC: |
328 | plt_header_size = 48; | ||
329 | plt_entry_size = 12; | ||
330 | break; | ||
331 | |||
332 | case EM_SPARCV9: | ||
333 | plt_header_size = 128; | ||
334 | plt_entry_size = 32; | ||
335 | break; | ||
336 | |||
337 | default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */ | ||
328 | plt_header_size = shdr_plt.sh_entsize; | 338 | plt_header_size = shdr_plt.sh_entsize; |
329 | plt_entry_size = shdr_plt.sh_entsize; | 339 | plt_entry_size = shdr_plt.sh_entsize; |
330 | break; | 340 | break; |
@@ -1947,6 +1957,34 @@ void kcore_extract__delete(struct kcore_extract *kce) | |||
1947 | } | 1957 | } |
1948 | 1958 | ||
1949 | #ifdef HAVE_GELF_GETNOTE_SUPPORT | 1959 | #ifdef HAVE_GELF_GETNOTE_SUPPORT |
1960 | |||
1961 | static void sdt_adjust_loc(struct sdt_note *tmp, GElf_Addr base_off) | ||
1962 | { | ||
1963 | if (!base_off) | ||
1964 | return; | ||
1965 | |||
1966 | if (tmp->bit32) | ||
1967 | tmp->addr.a32[SDT_NOTE_IDX_LOC] = | ||
1968 | tmp->addr.a32[SDT_NOTE_IDX_LOC] + base_off - | ||
1969 | tmp->addr.a32[SDT_NOTE_IDX_BASE]; | ||
1970 | else | ||
1971 | tmp->addr.a64[SDT_NOTE_IDX_LOC] = | ||
1972 | tmp->addr.a64[SDT_NOTE_IDX_LOC] + base_off - | ||
1973 | tmp->addr.a64[SDT_NOTE_IDX_BASE]; | ||
1974 | } | ||
1975 | |||
1976 | static void sdt_adjust_refctr(struct sdt_note *tmp, GElf_Addr base_addr, | ||
1977 | GElf_Addr base_off) | ||
1978 | { | ||
1979 | if (!base_off) | ||
1980 | return; | ||
1981 | |||
1982 | if (tmp->bit32 && tmp->addr.a32[SDT_NOTE_IDX_REFCTR]) | ||
1983 | tmp->addr.a32[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off); | ||
1984 | else if (tmp->addr.a64[SDT_NOTE_IDX_REFCTR]) | ||
1985 | tmp->addr.a64[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off); | ||
1986 | } | ||
1987 | |||
1950 | /** | 1988 | /** |
1951 | * populate_sdt_note : Parse raw data and identify SDT note | 1989 | * populate_sdt_note : Parse raw data and identify SDT note |
1952 | * @elf: elf of the opened file | 1990 | * @elf: elf of the opened file |
@@ -1964,7 +2002,6 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len, | |||
1964 | const char *provider, *name, *args; | 2002 | const char *provider, *name, *args; |
1965 | struct sdt_note *tmp = NULL; | 2003 | struct sdt_note *tmp = NULL; |
1966 | GElf_Ehdr ehdr; | 2004 | GElf_Ehdr ehdr; |
1967 | GElf_Addr base_off = 0; | ||
1968 | GElf_Shdr shdr; | 2005 | GElf_Shdr shdr; |
1969 | int ret = -EINVAL; | 2006 | int ret = -EINVAL; |
1970 | 2007 | ||
@@ -2060,17 +2097,12 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len, | |||
2060 | * base address in the description of the SDT note. If its different, | 2097 | * base address in the description of the SDT note. If its different, |
2061 | * then accordingly, adjust the note location. | 2098 | * then accordingly, adjust the note location. |
2062 | */ | 2099 | */ |
2063 | if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) { | 2100 | if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) |
2064 | base_off = shdr.sh_offset; | 2101 | sdt_adjust_loc(tmp, shdr.sh_offset); |
2065 | if (base_off) { | 2102 | |
2066 | if (tmp->bit32) | 2103 | /* Adjust reference counter offset */ |
2067 | tmp->addr.a32[0] = tmp->addr.a32[0] + base_off - | 2104 | if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_PROBES_SCN, NULL)) |
2068 | tmp->addr.a32[1]; | 2105 | sdt_adjust_refctr(tmp, shdr.sh_addr, shdr.sh_offset); |
2069 | else | ||
2070 | tmp->addr.a64[0] = tmp->addr.a64[0] + base_off - | ||
2071 | tmp->addr.a64[1]; | ||
2072 | } | ||
2073 | } | ||
2074 | 2106 | ||
2075 | list_add_tail(&tmp->note_list, sdt_notes); | 2107 | list_add_tail(&tmp->note_list, sdt_notes); |
2076 | return 0; | 2108 | return 0; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index f25fae4b5743..d026d215bdc6 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -123,7 +123,8 @@ struct symbol_conf { | |||
123 | const char *vmlinux_name, | 123 | const char *vmlinux_name, |
124 | *kallsyms_name, | 124 | *kallsyms_name, |
125 | *source_prefix, | 125 | *source_prefix, |
126 | *field_sep; | 126 | *field_sep, |
127 | *graph_function; | ||
127 | const char *default_guest_vmlinux_name, | 128 | const char *default_guest_vmlinux_name, |
128 | *default_guest_kallsyms, | 129 | *default_guest_kallsyms, |
129 | *default_guest_modules; | 130 | *default_guest_modules; |
@@ -379,12 +380,19 @@ int get_sdt_note_list(struct list_head *head, const char *target); | |||
379 | int cleanup_sdt_note_list(struct list_head *sdt_notes); | 380 | int cleanup_sdt_note_list(struct list_head *sdt_notes); |
380 | int sdt_notes__get_count(struct list_head *start); | 381 | int sdt_notes__get_count(struct list_head *start); |
381 | 382 | ||
383 | #define SDT_PROBES_SCN ".probes" | ||
382 | #define SDT_BASE_SCN ".stapsdt.base" | 384 | #define SDT_BASE_SCN ".stapsdt.base" |
383 | #define SDT_NOTE_SCN ".note.stapsdt" | 385 | #define SDT_NOTE_SCN ".note.stapsdt" |
384 | #define SDT_NOTE_TYPE 3 | 386 | #define SDT_NOTE_TYPE 3 |
385 | #define SDT_NOTE_NAME "stapsdt" | 387 | #define SDT_NOTE_NAME "stapsdt" |
386 | #define NR_ADDR 3 | 388 | #define NR_ADDR 3 |
387 | 389 | ||
390 | enum { | ||
391 | SDT_NOTE_IDX_LOC = 0, | ||
392 | SDT_NOTE_IDX_BASE, | ||
393 | SDT_NOTE_IDX_REFCTR, | ||
394 | }; | ||
395 | |||
388 | struct mem_info *mem_info__new(void); | 396 | struct mem_info *mem_info__new(void); |
389 | struct mem_info *mem_info__get(struct mem_info *mi); | 397 | struct mem_info *mem_info__get(struct mem_info *mi); |
390 | void mem_info__put(struct mem_info *mi); | 398 | void mem_info__put(struct mem_info *mi); |
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c index dd17d6a38d3a..61a4286a74dc 100644 --- a/tools/perf/util/thread-stack.c +++ b/tools/perf/util/thread-stack.c | |||
@@ -36,6 +36,7 @@ | |||
36 | * @branch_count: the branch count when the entry was created | 36 | * @branch_count: the branch count when the entry was created |
37 | * @cp: call path | 37 | * @cp: call path |
38 | * @no_call: a 'call' was not seen | 38 | * @no_call: a 'call' was not seen |
39 | * @trace_end: a 'call' but trace ended | ||
39 | */ | 40 | */ |
40 | struct thread_stack_entry { | 41 | struct thread_stack_entry { |
41 | u64 ret_addr; | 42 | u64 ret_addr; |
@@ -44,6 +45,7 @@ struct thread_stack_entry { | |||
44 | u64 branch_count; | 45 | u64 branch_count; |
45 | struct call_path *cp; | 46 | struct call_path *cp; |
46 | bool no_call; | 47 | bool no_call; |
48 | bool trace_end; | ||
47 | }; | 49 | }; |
48 | 50 | ||
49 | /** | 51 | /** |
@@ -112,7 +114,8 @@ static struct thread_stack *thread_stack__new(struct thread *thread, | |||
112 | return ts; | 114 | return ts; |
113 | } | 115 | } |
114 | 116 | ||
115 | static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | 117 | static int thread_stack__push(struct thread_stack *ts, u64 ret_addr, |
118 | bool trace_end) | ||
116 | { | 119 | { |
117 | int err = 0; | 120 | int err = 0; |
118 | 121 | ||
@@ -124,6 +127,7 @@ static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | |||
124 | } | 127 | } |
125 | } | 128 | } |
126 | 129 | ||
130 | ts->stack[ts->cnt].trace_end = trace_end; | ||
127 | ts->stack[ts->cnt++].ret_addr = ret_addr; | 131 | ts->stack[ts->cnt++].ret_addr = ret_addr; |
128 | 132 | ||
129 | return err; | 133 | return err; |
@@ -150,6 +154,18 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) | |||
150 | } | 154 | } |
151 | } | 155 | } |
152 | 156 | ||
157 | static void thread_stack__pop_trace_end(struct thread_stack *ts) | ||
158 | { | ||
159 | size_t i; | ||
160 | |||
161 | for (i = ts->cnt; i; ) { | ||
162 | if (ts->stack[--i].trace_end) | ||
163 | ts->cnt = i; | ||
164 | else | ||
165 | return; | ||
166 | } | ||
167 | } | ||
168 | |||
153 | static bool thread_stack__in_kernel(struct thread_stack *ts) | 169 | static bool thread_stack__in_kernel(struct thread_stack *ts) |
154 | { | 170 | { |
155 | if (!ts->cnt) | 171 | if (!ts->cnt) |
@@ -254,10 +270,19 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | |||
254 | ret_addr = from_ip + insn_len; | 270 | ret_addr = from_ip + insn_len; |
255 | if (ret_addr == to_ip) | 271 | if (ret_addr == to_ip) |
256 | return 0; /* Zero-length calls are excluded */ | 272 | return 0; /* Zero-length calls are excluded */ |
257 | return thread_stack__push(thread->ts, ret_addr); | 273 | return thread_stack__push(thread->ts, ret_addr, |
258 | } else if (flags & PERF_IP_FLAG_RETURN) { | 274 | flags & PERF_IP_FLAG_TRACE_END); |
259 | if (!from_ip) | 275 | } else if (flags & PERF_IP_FLAG_TRACE_BEGIN) { |
260 | return 0; | 276 | /* |
277 | * If the caller did not change the trace number (which would | ||
278 | * have flushed the stack) then try to make sense of the stack. | ||
279 | * Possibly, tracing began after returning to the current | ||
280 | * address, so try to pop that. Also, do not expect a call made | ||
281 | * when the trace ended, to return, so pop that. | ||
282 | */ | ||
283 | thread_stack__pop(thread->ts, to_ip); | ||
284 | thread_stack__pop_trace_end(thread->ts); | ||
285 | } else if ((flags & PERF_IP_FLAG_RETURN) && from_ip) { | ||
261 | thread_stack__pop(thread->ts, to_ip); | 286 | thread_stack__pop(thread->ts, to_ip); |
262 | } | 287 | } |
263 | 288 | ||
@@ -285,20 +310,46 @@ void thread_stack__free(struct thread *thread) | |||
285 | } | 310 | } |
286 | } | 311 | } |
287 | 312 | ||
313 | static inline u64 callchain_context(u64 ip, u64 kernel_start) | ||
314 | { | ||
315 | return ip < kernel_start ? PERF_CONTEXT_USER : PERF_CONTEXT_KERNEL; | ||
316 | } | ||
317 | |||
288 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | 318 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, |
289 | size_t sz, u64 ip) | 319 | size_t sz, u64 ip, u64 kernel_start) |
290 | { | 320 | { |
291 | size_t i; | 321 | u64 context = callchain_context(ip, kernel_start); |
322 | u64 last_context; | ||
323 | size_t i, j; | ||
292 | 324 | ||
293 | if (!thread || !thread->ts) | 325 | if (sz < 2) { |
294 | chain->nr = 1; | 326 | chain->nr = 0; |
295 | else | 327 | return; |
296 | chain->nr = min(sz, thread->ts->cnt + 1); | 328 | } |
329 | |||
330 | chain->ips[0] = context; | ||
331 | chain->ips[1] = ip; | ||
297 | 332 | ||
298 | chain->ips[0] = ip; | 333 | if (!thread || !thread->ts) { |
334 | chain->nr = 2; | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | last_context = context; | ||
339 | |||
340 | for (i = 2, j = 1; i < sz && j <= thread->ts->cnt; i++, j++) { | ||
341 | ip = thread->ts->stack[thread->ts->cnt - j].ret_addr; | ||
342 | context = callchain_context(ip, kernel_start); | ||
343 | if (context != last_context) { | ||
344 | if (i >= sz - 1) | ||
345 | break; | ||
346 | chain->ips[i++] = context; | ||
347 | last_context = context; | ||
348 | } | ||
349 | chain->ips[i] = ip; | ||
350 | } | ||
299 | 351 | ||
300 | for (i = 1; i < chain->nr; i++) | 352 | chain->nr = i; |
301 | chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; | ||
302 | } | 353 | } |
303 | 354 | ||
304 | struct call_return_processor * | 355 | struct call_return_processor * |
@@ -332,7 +383,7 @@ void call_return_processor__free(struct call_return_processor *crp) | |||
332 | 383 | ||
333 | static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, | 384 | static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, |
334 | u64 timestamp, u64 ref, struct call_path *cp, | 385 | u64 timestamp, u64 ref, struct call_path *cp, |
335 | bool no_call) | 386 | bool no_call, bool trace_end) |
336 | { | 387 | { |
337 | struct thread_stack_entry *tse; | 388 | struct thread_stack_entry *tse; |
338 | int err; | 389 | int err; |
@@ -350,6 +401,7 @@ static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, | |||
350 | tse->branch_count = ts->branch_count; | 401 | tse->branch_count = ts->branch_count; |
351 | tse->cp = cp; | 402 | tse->cp = cp; |
352 | tse->no_call = no_call; | 403 | tse->no_call = no_call; |
404 | tse->trace_end = trace_end; | ||
353 | 405 | ||
354 | return 0; | 406 | return 0; |
355 | } | 407 | } |
@@ -423,7 +475,7 @@ static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts, | |||
423 | return -ENOMEM; | 475 | return -ENOMEM; |
424 | 476 | ||
425 | return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp, | 477 | return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp, |
426 | true); | 478 | true, false); |
427 | } | 479 | } |
428 | 480 | ||
429 | static int thread_stack__no_call_return(struct thread *thread, | 481 | static int thread_stack__no_call_return(struct thread *thread, |
@@ -455,7 +507,7 @@ static int thread_stack__no_call_return(struct thread *thread, | |||
455 | if (!cp) | 507 | if (!cp) |
456 | return -ENOMEM; | 508 | return -ENOMEM; |
457 | return thread_stack__push_cp(ts, 0, sample->time, ref, | 509 | return thread_stack__push_cp(ts, 0, sample->time, ref, |
458 | cp, true); | 510 | cp, true, false); |
459 | } | 511 | } |
460 | } else if (thread_stack__in_kernel(ts) && sample->ip < ks) { | 512 | } else if (thread_stack__in_kernel(ts) && sample->ip < ks) { |
461 | /* Return to userspace, so pop all kernel addresses */ | 513 | /* Return to userspace, so pop all kernel addresses */ |
@@ -480,7 +532,7 @@ static int thread_stack__no_call_return(struct thread *thread, | |||
480 | return -ENOMEM; | 532 | return -ENOMEM; |
481 | 533 | ||
482 | err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp, | 534 | err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp, |
483 | true); | 535 | true, false); |
484 | if (err) | 536 | if (err) |
485 | return err; | 537 | return err; |
486 | 538 | ||
@@ -500,7 +552,7 @@ static int thread_stack__trace_begin(struct thread *thread, | |||
500 | 552 | ||
501 | /* Pop trace end */ | 553 | /* Pop trace end */ |
502 | tse = &ts->stack[ts->cnt - 1]; | 554 | tse = &ts->stack[ts->cnt - 1]; |
503 | if (tse->cp->sym == NULL && tse->cp->ip == 0) { | 555 | if (tse->trace_end) { |
504 | err = thread_stack__call_return(thread, ts, --ts->cnt, | 556 | err = thread_stack__call_return(thread, ts, --ts->cnt, |
505 | timestamp, ref, false); | 557 | timestamp, ref, false); |
506 | if (err) | 558 | if (err) |
@@ -529,7 +581,7 @@ static int thread_stack__trace_end(struct thread_stack *ts, | |||
529 | ret_addr = sample->ip + sample->insn_len; | 581 | ret_addr = sample->ip + sample->insn_len; |
530 | 582 | ||
531 | return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, | 583 | return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, |
532 | false); | 584 | false, true); |
533 | } | 585 | } |
534 | 586 | ||
535 | int thread_stack__process(struct thread *thread, struct comm *comm, | 587 | int thread_stack__process(struct thread *thread, struct comm *comm, |
@@ -579,6 +631,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm, | |||
579 | ts->last_time = sample->time; | 631 | ts->last_time = sample->time; |
580 | 632 | ||
581 | if (sample->flags & PERF_IP_FLAG_CALL) { | 633 | if (sample->flags & PERF_IP_FLAG_CALL) { |
634 | bool trace_end = sample->flags & PERF_IP_FLAG_TRACE_END; | ||
582 | struct call_path_root *cpr = ts->crp->cpr; | 635 | struct call_path_root *cpr = ts->crp->cpr; |
583 | struct call_path *cp; | 636 | struct call_path *cp; |
584 | u64 ret_addr; | 637 | u64 ret_addr; |
@@ -596,7 +649,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm, | |||
596 | if (!cp) | 649 | if (!cp) |
597 | return -ENOMEM; | 650 | return -ENOMEM; |
598 | err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, | 651 | err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, |
599 | cp, false); | 652 | cp, false, trace_end); |
600 | } else if (sample->flags & PERF_IP_FLAG_RETURN) { | 653 | } else if (sample->flags & PERF_IP_FLAG_RETURN) { |
601 | if (!sample->ip || !sample->addr) | 654 | if (!sample->ip || !sample->addr) |
602 | return 0; | 655 | return 0; |
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h index b7e41c4ebfdd..f97c00a8c251 100644 --- a/tools/perf/util/thread-stack.h +++ b/tools/perf/util/thread-stack.h | |||
@@ -84,7 +84,7 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | |||
84 | u64 to_ip, u16 insn_len, u64 trace_nr); | 84 | u64 to_ip, u16 insn_len, u64 trace_nr); |
85 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr); | 85 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr); |
86 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | 86 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, |
87 | size_t sz, u64 ip); | 87 | size_t sz, u64 ip, u64 kernel_start); |
88 | int thread_stack__flush(struct thread *thread); | 88 | int thread_stack__flush(struct thread *thread); |
89 | void thread_stack__free(struct thread *thread); | 89 | void thread_stack__free(struct thread *thread); |
90 | size_t thread_stack__depth(struct thread *thread); | 90 | size_t thread_stack__depth(struct thread *thread); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 2048d393ece6..3d9ed7d0e281 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -330,7 +330,8 @@ static int thread__prepare_access(struct thread *thread) | |||
330 | } | 330 | } |
331 | 331 | ||
332 | static int thread__clone_map_groups(struct thread *thread, | 332 | static int thread__clone_map_groups(struct thread *thread, |
333 | struct thread *parent) | 333 | struct thread *parent, |
334 | bool do_maps_clone) | ||
334 | { | 335 | { |
335 | /* This is new thread, we share map groups for process. */ | 336 | /* This is new thread, we share map groups for process. */ |
336 | if (thread->pid_ == parent->pid_) | 337 | if (thread->pid_ == parent->pid_) |
@@ -341,15 +342,11 @@ static int thread__clone_map_groups(struct thread *thread, | |||
341 | thread->pid_, thread->tid, parent->pid_, parent->tid); | 342 | thread->pid_, thread->tid, parent->pid_, parent->tid); |
342 | return 0; | 343 | return 0; |
343 | } | 344 | } |
344 | |||
345 | /* But this one is new process, copy maps. */ | 345 | /* But this one is new process, copy maps. */ |
346 | if (map_groups__clone(thread, parent->mg) < 0) | 346 | return do_maps_clone ? map_groups__clone(thread, parent->mg) : 0; |
347 | return -ENOMEM; | ||
348 | |||
349 | return 0; | ||
350 | } | 347 | } |
351 | 348 | ||
352 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | 349 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone) |
353 | { | 350 | { |
354 | if (parent->comm_set) { | 351 | if (parent->comm_set) { |
355 | const char *comm = thread__comm_str(parent); | 352 | const char *comm = thread__comm_str(parent); |
@@ -362,7 +359,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
362 | } | 359 | } |
363 | 360 | ||
364 | thread->ppid = parent->tid; | 361 | thread->ppid = parent->tid; |
365 | return thread__clone_map_groups(thread, parent); | 362 | return thread__clone_map_groups(thread, parent, do_maps_clone); |
366 | } | 363 | } |
367 | 364 | ||
368 | void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, | 365 | void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 07606aa6998d..30e2b4c165fe 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -42,6 +42,8 @@ struct thread { | |||
42 | void *addr_space; | 42 | void *addr_space; |
43 | struct unwind_libunwind_ops *unwind_libunwind_ops; | 43 | struct unwind_libunwind_ops *unwind_libunwind_ops; |
44 | #endif | 44 | #endif |
45 | bool filter; | ||
46 | int filter_entry_depth; | ||
45 | }; | 47 | }; |
46 | 48 | ||
47 | struct machine; | 49 | struct machine; |
@@ -87,7 +89,7 @@ struct comm *thread__comm(const struct thread *thread); | |||
87 | struct comm *thread__exec_comm(const struct thread *thread); | 89 | struct comm *thread__exec_comm(const struct thread *thread); |
88 | const char *thread__comm_str(const struct thread *thread); | 90 | const char *thread__comm_str(const struct thread *thread); |
89 | int thread__insert_map(struct thread *thread, struct map *map); | 91 | int thread__insert_map(struct thread *thread, struct map *map); |
90 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 92 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone); |
91 | size_t thread__fprintf(struct thread *thread, FILE *fp); | 93 | size_t thread__fprintf(struct thread *thread, FILE *fp); |
92 | 94 | ||
93 | struct thread *thread__main_thread(struct machine *machine, struct thread *thread); | 95 | struct thread *thread__main_thread(struct machine *machine, struct thread *thread); |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 183c91453522..56e4ca54020a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -26,15 +26,12 @@ typedef int (*event_attr_op)(struct perf_tool *tool, | |||
26 | union perf_event *event, | 26 | union perf_event *event, |
27 | struct perf_evlist **pevlist); | 27 | struct perf_evlist **pevlist); |
28 | 28 | ||
29 | typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event, | 29 | typedef int (*event_op2)(struct perf_session *session, union perf_event *event); |
30 | struct perf_session *session); | 30 | typedef s64 (*event_op3)(struct perf_session *session, union perf_event *event); |
31 | 31 | ||
32 | typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, | 32 | typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, |
33 | struct ordered_events *oe); | 33 | struct ordered_events *oe); |
34 | 34 | ||
35 | typedef s64 (*event_op3)(struct perf_tool *tool, union perf_event *event, | ||
36 | struct perf_session *session); | ||
37 | |||
38 | enum show_feature_header { | 35 | enum show_feature_header { |
39 | SHOW_FEAT_NO_HEADER = 0, | 36 | SHOW_FEAT_NO_HEADER = 0, |
40 | SHOW_FEAT_HEADER, | 37 | SHOW_FEAT_HEADER, |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 7b0ca7cbb7de..8ad8e755127b 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -531,12 +531,14 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
531 | "/tmp/perf-XXXXXX"); | 531 | "/tmp/perf-XXXXXX"); |
532 | if (!mkstemp(tdata->temp_file)) { | 532 | if (!mkstemp(tdata->temp_file)) { |
533 | pr_debug("Can't make temp file"); | 533 | pr_debug("Can't make temp file"); |
534 | free(tdata); | ||
534 | return NULL; | 535 | return NULL; |
535 | } | 536 | } |
536 | 537 | ||
537 | temp_fd = open(tdata->temp_file, O_RDWR); | 538 | temp_fd = open(tdata->temp_file, O_RDWR); |
538 | if (temp_fd < 0) { | 539 | if (temp_fd < 0) { |
539 | pr_debug("Can't read '%s'", tdata->temp_file); | 540 | pr_debug("Can't read '%s'", tdata->temp_file); |
541 | free(tdata); | ||
540 | return NULL; | 542 | return NULL; |
541 | } | 543 | } |
542 | 544 | ||
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index e76214f8d596..32e558a65af3 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -33,14 +33,15 @@ static int get_common_field(struct scripting_context *context, | |||
33 | int *offset, int *size, const char *type) | 33 | int *offset, int *size, const char *type) |
34 | { | 34 | { |
35 | struct tep_handle *pevent = context->pevent; | 35 | struct tep_handle *pevent = context->pevent; |
36 | struct event_format *event; | 36 | struct tep_event_format *event; |
37 | struct format_field *field; | 37 | struct tep_format_field *field; |
38 | 38 | ||
39 | if (!*size) { | 39 | if (!*size) { |
40 | if (!pevent->events) | 40 | |
41 | event = tep_get_first_event(pevent); | ||
42 | if (!event) | ||
41 | return 0; | 43 | return 0; |
42 | 44 | ||
43 | event = pevent->events[0]; | ||
44 | field = tep_find_common_field(event, type); | 45 | field = tep_find_common_field(event, type); |
45 | if (!field) | 46 | if (!field) |
46 | return 0; | 47 | return 0; |
@@ -94,9 +95,9 @@ int common_pc(struct scripting_context *context) | |||
94 | } | 95 | } |
95 | 96 | ||
96 | unsigned long long | 97 | unsigned long long |
97 | raw_field_value(struct event_format *event, const char *name, void *data) | 98 | raw_field_value(struct tep_event_format *event, const char *name, void *data) |
98 | { | 99 | { |
99 | struct format_field *field; | 100 | struct tep_format_field *field; |
100 | unsigned long long val; | 101 | unsigned long long val; |
101 | 102 | ||
102 | field = tep_find_any_field(event, name); | 103 | field = tep_find_any_field(event, name); |
@@ -108,12 +109,12 @@ raw_field_value(struct event_format *event, const char *name, void *data) | |||
108 | return val; | 109 | return val; |
109 | } | 110 | } |
110 | 111 | ||
111 | unsigned long long read_size(struct event_format *event, void *ptr, int size) | 112 | unsigned long long read_size(struct tep_event_format *event, void *ptr, int size) |
112 | { | 113 | { |
113 | return tep_read_number(event->pevent, ptr, size); | 114 | return tep_read_number(event->pevent, ptr, size); |
114 | } | 115 | } |
115 | 116 | ||
116 | void event_format__fprintf(struct event_format *event, | 117 | void event_format__fprintf(struct tep_event_format *event, |
117 | int cpu, void *data, int size, FILE *fp) | 118 | int cpu, void *data, int size, FILE *fp) |
118 | { | 119 | { |
119 | struct tep_record record; | 120 | struct tep_record record; |
@@ -130,7 +131,7 @@ void event_format__fprintf(struct event_format *event, | |||
130 | trace_seq_destroy(&s); | 131 | trace_seq_destroy(&s); |
131 | } | 132 | } |
132 | 133 | ||
133 | void event_format__print(struct event_format *event, | 134 | void event_format__print(struct tep_event_format *event, |
134 | int cpu, void *data, int size) | 135 | int cpu, void *data, int size) |
135 | { | 136 | { |
136 | return event_format__fprintf(event, cpu, data, size, stdout); | 137 | return event_format__fprintf(event, cpu, data, size, stdout); |
@@ -158,6 +159,7 @@ void parse_ftrace_printk(struct tep_handle *pevent, | |||
158 | printk = strdup(fmt+1); | 159 | printk = strdup(fmt+1); |
159 | line = strtok_r(NULL, "\n", &next); | 160 | line = strtok_r(NULL, "\n", &next); |
160 | tep_register_print_string(pevent, printk, addr); | 161 | tep_register_print_string(pevent, printk, addr); |
162 | free(printk); | ||
161 | } | 163 | } |
162 | } | 164 | } |
163 | 165 | ||
@@ -188,29 +190,33 @@ int parse_event_file(struct tep_handle *pevent, | |||
188 | return tep_parse_event(pevent, buf, size, sys); | 190 | return tep_parse_event(pevent, buf, size, sys); |
189 | } | 191 | } |
190 | 192 | ||
191 | struct event_format *trace_find_next_event(struct tep_handle *pevent, | 193 | struct tep_event_format *trace_find_next_event(struct tep_handle *pevent, |
192 | struct event_format *event) | 194 | struct tep_event_format *event) |
193 | { | 195 | { |
194 | static int idx; | 196 | static int idx; |
197 | int events_count; | ||
198 | struct tep_event_format *all_events; | ||
195 | 199 | ||
196 | if (!pevent || !pevent->events) | 200 | all_events = tep_get_first_event(pevent); |
201 | events_count = tep_get_events_count(pevent); | ||
202 | if (!pevent || !all_events || events_count < 1) | ||
197 | return NULL; | 203 | return NULL; |
198 | 204 | ||
199 | if (!event) { | 205 | if (!event) { |
200 | idx = 0; | 206 | idx = 0; |
201 | return pevent->events[0]; | 207 | return all_events; |
202 | } | 208 | } |
203 | 209 | ||
204 | if (idx < pevent->nr_events && event == pevent->events[idx]) { | 210 | if (idx < events_count && event == (all_events + idx)) { |
205 | idx++; | 211 | idx++; |
206 | if (idx == pevent->nr_events) | 212 | if (idx == events_count) |
207 | return NULL; | 213 | return NULL; |
208 | return pevent->events[idx]; | 214 | return (all_events + idx); |
209 | } | 215 | } |
210 | 216 | ||
211 | for (idx = 1; idx < pevent->nr_events; idx++) { | 217 | for (idx = 1; idx < events_count; idx++) { |
212 | if (event == pevent->events[idx - 1]) | 218 | if (event == (all_events + (idx - 1))) |
213 | return pevent->events[idx]; | 219 | return (all_events + idx); |
214 | } | 220 | } |
215 | return NULL; | 221 | return NULL; |
216 | } | 222 | } |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 3dfc1db6b25b..76f12c705ef9 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -102,7 +102,7 @@ static unsigned int read4(struct tep_handle *pevent) | |||
102 | 102 | ||
103 | if (do_read(&data, 4) < 0) | 103 | if (do_read(&data, 4) < 0) |
104 | return 0; | 104 | return 0; |
105 | return __data2host4(pevent, data); | 105 | return __tep_data2host4(pevent, data); |
106 | } | 106 | } |
107 | 107 | ||
108 | static unsigned long long read8(struct tep_handle *pevent) | 108 | static unsigned long long read8(struct tep_handle *pevent) |
@@ -111,7 +111,7 @@ static unsigned long long read8(struct tep_handle *pevent) | |||
111 | 111 | ||
112 | if (do_read(&data, 8) < 0) | 112 | if (do_read(&data, 8) < 0) |
113 | return 0; | 113 | return 0; |
114 | return __data2host8(pevent, data); | 114 | return __tep_data2host8(pevent, data); |
115 | } | 115 | } |
116 | 116 | ||
117 | static char *read_string(void) | 117 | static char *read_string(void) |
@@ -241,7 +241,7 @@ static int read_header_files(struct tep_handle *pevent) | |||
241 | * The commit field in the page is of type long, | 241 | * The commit field in the page is of type long, |
242 | * use that instead, since it represents the kernel. | 242 | * use that instead, since it represents the kernel. |
243 | */ | 243 | */ |
244 | tep_set_long_size(pevent, pevent->header_page_size_size); | 244 | tep_set_long_size(pevent, tep_get_header_page_size(pevent)); |
245 | } | 245 | } |
246 | free(header_page); | 246 | free(header_page); |
247 | 247 | ||
@@ -297,10 +297,8 @@ static int read_event_file(struct tep_handle *pevent, char *sys, | |||
297 | } | 297 | } |
298 | 298 | ||
299 | ret = do_read(buf, size); | 299 | ret = do_read(buf, size); |
300 | if (ret < 0) { | 300 | if (ret < 0) |
301 | free(buf); | ||
302 | goto out; | 301 | goto out; |
303 | } | ||
304 | 302 | ||
305 | ret = parse_event_file(pevent, buf, size, sys); | 303 | ret = parse_event_file(pevent, buf, size, sys); |
306 | if (ret < 0) | 304 | if (ret < 0) |
@@ -349,9 +347,12 @@ static int read_event_files(struct tep_handle *pevent) | |||
349 | for (x=0; x < count; x++) { | 347 | for (x=0; x < count; x++) { |
350 | size = read8(pevent); | 348 | size = read8(pevent); |
351 | ret = read_event_file(pevent, sys, size); | 349 | ret = read_event_file(pevent, sys, size); |
352 | if (ret) | 350 | if (ret) { |
351 | free(sys); | ||
353 | return ret; | 352 | return ret; |
353 | } | ||
354 | } | 354 | } |
355 | free(sys); | ||
355 | } | 356 | } |
356 | return 0; | 357 | return 0; |
357 | } | 358 | } |
diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c index 58bb72f266f3..95664b2f771e 100644 --- a/tools/perf/util/trace-event.c +++ b/tools/perf/util/trace-event.c | |||
@@ -72,12 +72,12 @@ void trace_event__cleanup(struct trace_event *t) | |||
72 | /* | 72 | /* |
73 | * Returns pointer with encoded error via <linux/err.h> interface. | 73 | * Returns pointer with encoded error via <linux/err.h> interface. |
74 | */ | 74 | */ |
75 | static struct event_format* | 75 | static struct tep_event_format* |
76 | tp_format(const char *sys, const char *name) | 76 | tp_format(const char *sys, const char *name) |
77 | { | 77 | { |
78 | char *tp_dir = get_events_file(sys); | 78 | char *tp_dir = get_events_file(sys); |
79 | struct tep_handle *pevent = tevent.pevent; | 79 | struct tep_handle *pevent = tevent.pevent; |
80 | struct event_format *event = NULL; | 80 | struct tep_event_format *event = NULL; |
81 | char path[PATH_MAX]; | 81 | char path[PATH_MAX]; |
82 | size_t size; | 82 | size_t size; |
83 | char *data; | 83 | char *data; |
@@ -102,7 +102,7 @@ tp_format(const char *sys, const char *name) | |||
102 | /* | 102 | /* |
103 | * Returns pointer with encoded error via <linux/err.h> interface. | 103 | * Returns pointer with encoded error via <linux/err.h> interface. |
104 | */ | 104 | */ |
105 | struct event_format* | 105 | struct tep_event_format* |
106 | trace_event__tp_format(const char *sys, const char *name) | 106 | trace_event__tp_format(const char *sys, const char *name) |
107 | { | 107 | { |
108 | if (!tevent_initialized && trace_event__init2()) | 108 | if (!tevent_initialized && trace_event__init2()) |
@@ -111,7 +111,7 @@ trace_event__tp_format(const char *sys, const char *name) | |||
111 | return tp_format(sys, name); | 111 | return tp_format(sys, name); |
112 | } | 112 | } |
113 | 113 | ||
114 | struct event_format *trace_event__tp_format_id(int id) | 114 | struct tep_event_format *trace_event__tp_format_id(int id) |
115 | { | 115 | { |
116 | if (!tevent_initialized && trace_event__init2()) | 116 | if (!tevent_initialized && trace_event__init2()) |
117 | return ERR_PTR(-ENOMEM); | 117 | return ERR_PTR(-ENOMEM); |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 40204ec3a7a2..f024d73bfc40 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #define _PERF_UTIL_TRACE_EVENT_H | 3 | #define _PERF_UTIL_TRACE_EVENT_H |
4 | 4 | ||
5 | #include <traceevent/event-parse.h> | 5 | #include <traceevent/event-parse.h> |
6 | #include <traceevent/trace-seq.h> | ||
6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
7 | 8 | ||
8 | struct machine; | 9 | struct machine; |
@@ -10,28 +11,28 @@ struct perf_sample; | |||
10 | union perf_event; | 11 | union perf_event; |
11 | struct perf_tool; | 12 | struct perf_tool; |
12 | struct thread; | 13 | struct thread; |
13 | struct plugin_list; | 14 | struct tep_plugin_list; |
14 | 15 | ||
15 | struct trace_event { | 16 | struct trace_event { |
16 | struct tep_handle *pevent; | 17 | struct tep_handle *pevent; |
17 | struct plugin_list *plugin_list; | 18 | struct tep_plugin_list *plugin_list; |
18 | }; | 19 | }; |
19 | 20 | ||
20 | int trace_event__init(struct trace_event *t); | 21 | int trace_event__init(struct trace_event *t); |
21 | void trace_event__cleanup(struct trace_event *t); | 22 | void trace_event__cleanup(struct trace_event *t); |
22 | int trace_event__register_resolver(struct machine *machine, | 23 | int trace_event__register_resolver(struct machine *machine, |
23 | tep_func_resolver_t *func); | 24 | tep_func_resolver_t *func); |
24 | struct event_format* | 25 | struct tep_event_format* |
25 | trace_event__tp_format(const char *sys, const char *name); | 26 | trace_event__tp_format(const char *sys, const char *name); |
26 | 27 | ||
27 | struct event_format *trace_event__tp_format_id(int id); | 28 | struct tep_event_format *trace_event__tp_format_id(int id); |
28 | 29 | ||
29 | int bigendian(void); | 30 | int bigendian(void); |
30 | 31 | ||
31 | void event_format__fprintf(struct event_format *event, | 32 | void event_format__fprintf(struct tep_event_format *event, |
32 | int cpu, void *data, int size, FILE *fp); | 33 | int cpu, void *data, int size, FILE *fp); |
33 | 34 | ||
34 | void event_format__print(struct event_format *event, | 35 | void event_format__print(struct tep_event_format *event, |
35 | int cpu, void *data, int size); | 36 | int cpu, void *data, int size); |
36 | 37 | ||
37 | int parse_ftrace_file(struct tep_handle *pevent, char *buf, unsigned long size); | 38 | int parse_ftrace_file(struct tep_handle *pevent, char *buf, unsigned long size); |
@@ -39,7 +40,7 @@ int parse_event_file(struct tep_handle *pevent, | |||
39 | char *buf, unsigned long size, char *sys); | 40 | char *buf, unsigned long size, char *sys); |
40 | 41 | ||
41 | unsigned long long | 42 | unsigned long long |
42 | raw_field_value(struct event_format *event, const char *name, void *data); | 43 | raw_field_value(struct tep_event_format *event, const char *name, void *data); |
43 | 44 | ||
44 | void parse_proc_kallsyms(struct tep_handle *pevent, char *file, unsigned int size); | 45 | void parse_proc_kallsyms(struct tep_handle *pevent, char *file, unsigned int size); |
45 | void parse_ftrace_printk(struct tep_handle *pevent, char *file, unsigned int size); | 46 | void parse_ftrace_printk(struct tep_handle *pevent, char *file, unsigned int size); |
@@ -47,9 +48,9 @@ void parse_saved_cmdline(struct tep_handle *pevent, char *file, unsigned int siz | |||
47 | 48 | ||
48 | ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); | 49 | ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); |
49 | 50 | ||
50 | struct event_format *trace_find_next_event(struct tep_handle *pevent, | 51 | struct tep_event_format *trace_find_next_event(struct tep_handle *pevent, |
51 | struct event_format *event); | 52 | struct tep_event_format *event); |
52 | unsigned long long read_size(struct event_format *event, void *ptr, int size); | 53 | unsigned long long read_size(struct tep_event_format *event, void *ptr, int size); |
53 | unsigned long long eval_flag(const char *flag); | 54 | unsigned long long eval_flag(const char *flag); |
54 | 55 | ||
55 | int read_tracing_data(int fd, struct list_head *pattrs); | 56 | int read_tracing_data(int fd, struct list_head *pattrs); |
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 6f318b15950e..5eff9bfc5758 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
@@ -45,13 +45,13 @@ static int __report_module(struct addr_location *al, u64 ip, | |||
45 | Dwarf_Addr s; | 45 | Dwarf_Addr s; |
46 | 46 | ||
47 | dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL); | 47 | dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL); |
48 | if (s != al->map->start) | 48 | if (s != al->map->start - al->map->pgoff) |
49 | mod = 0; | 49 | mod = 0; |
50 | } | 50 | } |
51 | 51 | ||
52 | if (!mod) | 52 | if (!mod) |
53 | mod = dwfl_report_elf(ui->dwfl, dso->short_name, | 53 | mod = dwfl_report_elf(ui->dwfl, dso->short_name, |
54 | (dso->symsrc_filename ? dso->symsrc_filename : dso->long_name), -1, al->map->start, | 54 | (dso->symsrc_filename ? dso->symsrc_filename : dso->long_name), -1, al->map->start - al->map->pgoff, |
55 | false); | 55 | false); |
56 | 56 | ||
57 | return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1; | 57 | return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index eac5b858a371..093352e93d50 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -221,7 +221,7 @@ out: | |||
221 | return err; | 221 | return err; |
222 | } | 222 | } |
223 | 223 | ||
224 | static int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size) | 224 | int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size) |
225 | { | 225 | { |
226 | void *ptr; | 226 | void *ptr; |
227 | loff_t pgoff; | 227 | loff_t pgoff; |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index dc58254a2b69..14508ee7707a 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -6,6 +6,7 @@ | |||
6 | /* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */ | 6 | /* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */ |
7 | #define _DEFAULT_SOURCE 1 | 7 | #define _DEFAULT_SOURCE 1 |
8 | 8 | ||
9 | #include <fcntl.h> | ||
9 | #include <stdbool.h> | 10 | #include <stdbool.h> |
10 | #include <stddef.h> | 11 | #include <stddef.h> |
11 | #include <stdlib.h> | 12 | #include <stdlib.h> |
@@ -35,6 +36,7 @@ bool lsdir_no_dot_filter(const char *name, struct dirent *d); | |||
35 | int copyfile(const char *from, const char *to); | 36 | int copyfile(const char *from, const char *to); |
36 | int copyfile_mode(const char *from, const char *to, mode_t mode); | 37 | int copyfile_mode(const char *from, const char *to, mode_t mode); |
37 | int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi); | 38 | int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi); |
39 | int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size); | ||
38 | 40 | ||
39 | ssize_t readn(int fd, void *buf, size_t n); | 41 | ssize_t readn(int fd, void *buf, size_t n); |
40 | ssize_t writen(int fd, const void *buf, size_t n); | 42 | ssize_t writen(int fd, const void *buf, size_t n); |