aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/include/tools/endian.h56
-rw-r--r--tools/lib/api/Makefile7
-rw-r--r--tools/lib/api/fd/array.c127
-rw-r--r--tools/lib/api/fd/array.h46
-rw-r--r--tools/lib/traceevent/plugin_scsi.c9
-rw-r--r--tools/perf/.gitignore3
-rw-r--r--tools/perf/Documentation/perf-diff.txt6
-rw-r--r--tools/perf/Documentation/perf-kvm.txt4
-rw-r--r--tools/perf/Documentation/perf-list.txt2
-rw-r--r--tools/perf/Documentation/perf-probe.txt3
-rw-r--r--tools/perf/Documentation/perf-record.txt8
-rw-r--r--tools/perf/Documentation/perf-report.txt17
-rw-r--r--tools/perf/Documentation/perf-script-perl.txt4
-rw-r--r--tools/perf/Documentation/perf-script-python.txt6
-rw-r--r--tools/perf/Documentation/perf-script.txt4
-rw-r--r--tools/perf/Documentation/perf-test.txt2
-rw-r--r--tools/perf/Documentation/perf-top.txt9
-rw-r--r--tools/perf/Documentation/perf-trace.txt2
-rw-r--r--tools/perf/Makefile.perf62
-rw-r--r--tools/perf/arch/arm/tests/dwarf-unwind.c1
-rw-r--r--tools/perf/arch/arm/util/unwind-libunwind.c1
-rw-r--r--tools/perf/arch/arm64/include/perf_regs.h2
-rw-r--r--tools/perf/arch/arm64/util/unwind-libunwind.c1
-rw-r--r--tools/perf/arch/common.c9
-rw-r--r--tools/perf/arch/powerpc/Makefile2
-rw-r--r--tools/perf/arch/powerpc/util/skip-callchain-idx.c39
-rw-r--r--tools/perf/bench/futex-hash.c7
-rw-r--r--tools/perf/bench/futex-requeue.c28
-rw-r--r--tools/perf/bench/futex-wake.c15
-rw-r--r--tools/perf/bench/sched-messaging.c2
-rw-r--r--tools/perf/builtin-annotate.c89
-rw-r--r--tools/perf/builtin-buildid-cache.c44
-rw-r--r--tools/perf/builtin-diff.c33
-rw-r--r--tools/perf/builtin-evlist.c2
-rw-r--r--tools/perf/builtin-help.c20
-rw-r--r--tools/perf/builtin-inject.c37
-rw-r--r--tools/perf/builtin-kmem.c56
-rw-r--r--tools/perf/builtin-kvm.c138
-rw-r--r--tools/perf/builtin-lock.c7
-rw-r--r--tools/perf/builtin-mem.c6
-rw-r--r--tools/perf/builtin-probe.c77
-rw-r--r--tools/perf/builtin-record.c168
-rw-r--r--tools/perf/builtin-report.c83
-rw-r--r--tools/perf/builtin-sched.c12
-rw-r--r--tools/perf/builtin-script.c104
-rw-r--r--tools/perf/builtin-stat.c117
-rw-r--r--tools/perf/builtin-timechart.c17
-rw-r--r--tools/perf/builtin-top.c135
-rw-r--r--tools/perf/builtin-trace.c79
-rw-r--r--tools/perf/config/Makefile101
-rw-r--r--tools/perf/config/Makefile.arch8
-rw-r--r--tools/perf/config/feature-checks/Makefile34
-rw-r--r--tools/perf/config/feature-checks/test-all.c5
-rw-r--r--tools/perf/config/feature-checks/test-compile.c4
-rw-r--r--tools/perf/config/feature-checks/test-zlib.c9
-rw-r--r--tools/perf/config/utilities.mak2
-rw-r--r--tools/perf/perf-read-vdso.c34
-rw-r--r--tools/perf/perf-sys.h30
-rw-r--r--tools/perf/perf-with-kcore.sh259
-rw-r--r--tools/perf/perf.c10
-rw-r--r--tools/perf/perf.h7
-rw-r--r--tools/perf/scripts/python/bin/export-to-postgresql-record8
-rw-r--r--tools/perf/scripts/python/bin/export-to-postgresql-report29
-rw-r--r--tools/perf/scripts/python/export-to-postgresql.py444
-rw-r--r--tools/perf/tests/builtin-test.c25
-rw-r--r--tools/perf/tests/code-reading.c13
-rw-r--r--tools/perf/tests/dwarf-unwind.c21
-rw-r--r--tools/perf/tests/fdarray.c174
-rw-r--r--tools/perf/tests/hists_cumulate.c8
-rw-r--r--tools/perf/tests/hists_filter.c25
-rw-r--r--tools/perf/tests/hists_link.c23
-rw-r--r--tools/perf/tests/hists_output.c20
-rw-r--r--tools/perf/tests/mmap-basic.c7
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c2
-rw-r--r--tools/perf/tests/open-syscall-all-cpus.c5
-rw-r--r--tools/perf/tests/open-syscall-tp-fields.c9
-rw-r--r--tools/perf/tests/open-syscall.c3
-rw-r--r--tools/perf/tests/parse-events.c36
-rw-r--r--tools/perf/tests/perf-record.c15
-rw-r--r--tools/perf/tests/pmu.c2
-rw-r--r--tools/perf/tests/rdpmc.c6
-rw-r--r--tools/perf/tests/sample-parsing.c55
-rw-r--r--tools/perf/tests/sw-clock.c6
-rw-r--r--tools/perf/tests/switch-tracking.c572
-rw-r--r--tools/perf/tests/task-exit.c8
-rw-r--r--tools/perf/tests/tests.h3
-rw-r--r--tools/perf/ui/browsers/annotate.c13
-rw-r--r--tools/perf/ui/browsers/header.c1
-rw-r--r--tools/perf/ui/browsers/hists.c460
-rw-r--r--tools/perf/ui/gtk/hists.c31
-rw-r--r--tools/perf/ui/hist.c284
-rw-r--r--tools/perf/ui/stdio/hist.c27
-rw-r--r--tools/perf/util/annotate.c63
-rw-r--r--tools/perf/util/annotate.h1
-rw-r--r--tools/perf/util/build-id.c348
-rw-r--r--tools/perf/util/build-id.h12
-rw-r--r--tools/perf/util/cache.h1
-rw-r--r--tools/perf/util/callchain.c277
-rw-r--r--tools/perf/util/callchain.h19
-rw-r--r--tools/perf/util/cloexec.c35
-rw-r--r--tools/perf/util/color.c16
-rw-r--r--tools/perf/util/color.h1
-rw-r--r--tools/perf/util/comm.c7
-rw-r--r--tools/perf/util/comm.h10
-rw-r--r--tools/perf/util/config.c40
-rw-r--r--tools/perf/util/data.c8
-rw-r--r--tools/perf/util/db-export.c428
-rw-r--r--tools/perf/util/db-export.h107
-rw-r--r--tools/perf/util/debug.c40
-rw-r--r--tools/perf/util/debug.h11
-rw-r--r--tools/perf/util/dso.c192
-rw-r--r--tools/perf/util/dso.h29
-rw-r--r--tools/perf/util/event.c34
-rw-r--r--tools/perf/util/event.h72
-rw-r--r--tools/perf/util/evlist.c317
-rw-r--r--tools/perf/util/evlist.h23
-rw-r--r--tools/perf/util/evsel.c292
-rw-r--r--tools/perf/util/evsel.h69
-rw-r--r--tools/perf/util/find-vdso-map.c30
-rw-r--r--tools/perf/util/header.c394
-rw-r--r--tools/perf/util/header.h8
-rw-r--r--tools/perf/util/hist.c95
-rw-r--r--tools/perf/util/hist.h67
-rw-r--r--tools/perf/util/include/linux/bitmap.h17
-rw-r--r--tools/perf/util/include/linux/bitops.h2
-rw-r--r--tools/perf/util/include/linux/string.h1
-rw-r--r--tools/perf/util/kvm-stat.h1
-rw-r--r--tools/perf/util/machine.c362
-rw-r--r--tools/perf/util/machine.h43
-rw-r--r--tools/perf/util/map.c19
-rw-r--r--tools/perf/util/map.h4
-rw-r--r--tools/perf/util/ordered-events.c286
-rw-r--r--tools/perf/util/ordered-events.h59
-rw-r--r--tools/perf/util/parse-events.c164
-rw-r--r--tools/perf/util/parse-events.h14
-rw-r--r--tools/perf/util/parse-events.l30
-rw-r--r--tools/perf/util/parse-events.y50
-rw-r--r--tools/perf/util/parse-options.c78
-rw-r--r--tools/perf/util/parse-options.h4
-rw-r--r--tools/perf/util/pmu.c236
-rw-r--r--tools/perf/util/pmu.h40
-rw-r--r--tools/perf/util/probe-event.c199
-rw-r--r--tools/perf/util/probe-event.h3
-rw-r--r--tools/perf/util/probe-finder.c23
-rw-r--r--tools/perf/util/python.c6
-rw-r--r--tools/perf/util/record.c40
-rw-r--r--tools/perf/util/run-command.c9
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c35
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c557
-rw-r--r--tools/perf/util/session.c508
-rw-r--r--tools/perf/util/session.h48
-rw-r--r--tools/perf/util/sort.c227
-rw-r--r--tools/perf/util/sort.h1
-rw-r--r--tools/perf/util/srcline.c17
-rw-r--r--tools/perf/util/string.c114
-rw-r--r--tools/perf/util/symbol-elf.c87
-rw-r--r--tools/perf/util/symbol-minimal.c1
-rw-r--r--tools/perf/util/symbol.c70
-rw-r--r--tools/perf/util/symbol.h41
-rw-r--r--tools/perf/util/thread-stack.c747
-rw-r--r--tools/perf/util/thread-stack.h111
-rw-r--r--tools/perf/util/thread.c45
-rw-r--r--tools/perf/util/thread.h19
-rw-r--r--tools/perf/util/thread_map.c21
-rw-r--r--tools/perf/util/thread_map.h1
-rw-r--r--tools/perf/util/tool.h5
-rw-r--r--tools/perf/util/trace-event-scripting.c7
-rw-r--r--tools/perf/util/trace-event.h1
-rw-r--r--tools/perf/util/unwind-libdw.c8
-rw-r--r--tools/perf/util/unwind-libunwind.c66
-rw-r--r--tools/perf/util/unwind.h22
-rw-r--r--tools/perf/util/util.c62
-rw-r--r--tools/perf/util/util.h33
-rw-r--r--tools/perf/util/vdso.c217
-rw-r--r--tools/perf/util/vdso.h4
-rw-r--r--tools/perf/util/zlib.c78
-rw-r--r--tools/power/acpi/os_specific/service_layers/osunixxf.c8
-rw-r--r--tools/power/acpi/tools/acpidump/apdump.c2
-rwxr-xr-xtools/testing/ktest/ktest.pl61
-rw-r--r--tools/testing/ktest/sample.conf10
-rw-r--r--tools/testing/selftests/memfd/Makefile21
-rw-r--r--tools/testing/selftests/memfd/memfd_test.c36
-rw-r--r--tools/testing/selftests/net/psock_fanout.c2
-rw-r--r--tools/testing/selftests/powerpc/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/primitives/Makefile17
l---------tools/testing/selftests/powerpc/primitives/asm/asm-compat.h1
-rw-r--r--tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h0
-rw-r--r--tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c147
l---------tools/testing/selftests/powerpc/primitives/word-at-a-time.h1
-rwxr-xr-x[-rw-r--r--]tools/testing/selftests/rcutorture/bin/config2frag.sh4
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/configcheck.sh4
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/configinit.sh4
-rw-r--r--tools/testing/selftests/rcutorture/bin/functions.sh20
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-build.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh11
-rwxr-xr-x[-rw-r--r--]tools/testing/selftests/rcutorture/bin/kvm.sh21
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-build.sh5
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-console.sh9
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-torture.sh5
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/CFLIST3
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK026
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK036
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK046
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh2
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/CFLIST3
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS019
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS025
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS0313
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TINY022
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot2
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE017
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot2
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE023
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE02-T3
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE035
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE041
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE051
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE061
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot3
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE074
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE085
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE08-T3
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot2
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE093
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/CFLIST14
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/N1-S-T-NH-SD-SMP-HP18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/N2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/N3-3-T-nh-SD-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/N4-A-t-NH-sd-SMP-HP18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/N5-U-T-NH-sd-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT3-NH20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/P1-S-T-NH-SD-SMP-HP19
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/P2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/P3-3-T-nh-SD-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/P4-A-t-NH-sd-SMP-HP22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/P5-U-T-NH-sd-SMP-hp27
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT2-NH22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v0.0/ver_functions.sh33
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/CFLIST17
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N1-S-T-NH-SD-SMP-HP19
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N3-3-T-nh-SD-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N4-A-t-NH-sd-SMP-HP18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N5-U-T-NH-sd-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N6---t-nh-SD-smp-hp19
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N7-4-T-NH-SD-SMP-HP26
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/N8-2-T-NH-SD-SMP-HP22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT3-NH20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P1-S-T-NH-SD-SMP-HP20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P3-3-T-nh-SD-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P4-A-t-NH-sd-SMP-HP22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P5-U-T-NH-sd-SMP-hp27
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P6---t-nh-SD-smp-hp18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP30
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-all30
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-none30
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-hp30
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT2-NH22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/CFLIST14
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/N1-S-T-NH-SD-SMP-HP19
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/N2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/N3-3-T-nh-SD-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/N4-A-t-NH-sd-SMP-HP18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/N5-U-T-NH-sd-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT3-NH20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/P1-S-T-NH-SD-SMP-HP20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/P2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/P3-3-T-nh-SD-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/P4-A-t-NH-sd-SMP-HP22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/P5-U-T-NH-sd-SMP-hp27
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT2-NH22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.3/ver_functions.sh44
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/CFLIST14
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/N1-S-T-NH-SD-SMP-HP19
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/N2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/N3-3-T-nh-SD-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/N4-A-t-NH-sd-SMP-HP18
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/N5-U-T-NH-sd-SMP-hp22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT3-NH20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/P1-S-T-NH-SD-SMP-HP20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/P2-2-t-nh-sd-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/P3-3-T-nh-SD-SMP-hp20
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/P4-A-t-NH-sd-SMP-HP22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/P5-U-T-NH-sd-SMP-hp27
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT1-nh23
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT2-NH22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/v3.5/ver_functions.sh57
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh2
-rw-r--r--tools/testing/selftests/rcutorture/doc/TINY_RCU.txt2
-rw-r--r--tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt15
-rw-r--r--tools/testing/selftests/rcutorture/doc/initrd.txt1
-rw-r--r--tools/testing/selftests/vm/Makefile1
-rw-r--r--tools/testing/selftests/vm/transhuge-stress.c144
-rw-r--r--tools/usb/ffs-test.c126
-rw-r--r--tools/vm/page-types.c1
314 files changed, 10294 insertions, 4528 deletions
diff --git a/tools/include/tools/endian.h b/tools/include/tools/endian.h
new file mode 100644
index 000000000000..8001194008da
--- /dev/null
+++ b/tools/include/tools/endian.h
@@ -0,0 +1,56 @@
+#ifndef _TOOLS_ENDIAN_H
+#define _TOOLS_ENDIAN_H
+
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#ifndef htole16
+#define htole16(x) (x)
+#endif
+#ifndef htole32
+#define htole32(x) (x)
+#endif
+#ifndef htole64
+#define htole64(x) (x)
+#endif
+
+#ifndef le16toh
+#define le16toh(x) (x)
+#endif
+
+#ifndef le32toh
+#define le32toh(x) (x)
+#endif
+
+#ifndef le64toh
+#define le64toh(x) (x)
+#endif
+
+#else /* __BYTE_ORDER */
+
+#ifndef htole16
+#define htole16(x) __bswap_16(x)
+#endif
+#ifndef htole32
+#define htole32(x) __bswap_32(x)
+#endif
+#ifndef htole64
+#define htole64(x) __bswap_64(x)
+#endif
+
+#ifndef le16toh
+#define le16toh(x) __bswap_16(x)
+#endif
+
+#ifndef le32toh
+#define le32toh(x) __bswap_32(x)
+#endif
+
+#ifndef le64toh
+#define le64toh(x) __bswap_64(x)
+#endif
+
+#endif
+
+#endif /* _TOOLS_ENDIAN_H */
diff --git a/tools/lib/api/Makefile b/tools/lib/api/Makefile
index ce00f7ee6455..36c08b1f4afb 100644
--- a/tools/lib/api/Makefile
+++ b/tools/lib/api/Makefile
@@ -10,9 +10,14 @@ LIB_OBJS=
LIB_H += fs/debugfs.h
LIB_H += fs/fs.h
+# See comment below about piggybacking...
+LIB_H += fd/array.h
LIB_OBJS += $(OUTPUT)fs/debugfs.o
LIB_OBJS += $(OUTPUT)fs/fs.o
+# XXX piggybacking here, need to introduce libapikfd, or rename this
+# to plain libapik.a and make it have it all api goodies
+LIB_OBJS += $(OUTPUT)fd/array.o
LIBFILE = libapikfs.a
@@ -29,7 +34,7 @@ $(LIBFILE): $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H)
libapi_dirs:
- $(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/
+ $(QUIET_MKDIR)mkdir -p $(OUTPUT)fd $(OUTPUT)fs
$(OUTPUT)%.o: %.c libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c
new file mode 100644
index 000000000000..0e636c4339b8
--- /dev/null
+++ b/tools/lib/api/fd/array.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Released under the GPL v2. (and only v2, not any later version)
+ */
+#include "array.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void fdarray__init(struct fdarray *fda, int nr_autogrow)
+{
+ fda->entries = NULL;
+ fda->priv = NULL;
+ fda->nr = fda->nr_alloc = 0;
+ fda->nr_autogrow = nr_autogrow;
+}
+
+int fdarray__grow(struct fdarray *fda, int nr)
+{
+ void *priv;
+ int nr_alloc = fda->nr_alloc + nr;
+ size_t psize = sizeof(fda->priv[0]) * nr_alloc;
+ size_t size = sizeof(struct pollfd) * nr_alloc;
+ struct pollfd *entries = realloc(fda->entries, size);
+
+ if (entries == NULL)
+ return -ENOMEM;
+
+ priv = realloc(fda->priv, psize);
+ if (priv == NULL) {
+ free(entries);
+ return -ENOMEM;
+ }
+
+ fda->nr_alloc = nr_alloc;
+ fda->entries = entries;
+ fda->priv = priv;
+ return 0;
+}
+
+struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow)
+{
+ struct fdarray *fda = calloc(1, sizeof(*fda));
+
+ if (fda != NULL) {
+ if (fdarray__grow(fda, nr_alloc)) {
+ free(fda);
+ fda = NULL;
+ } else {
+ fda->nr_autogrow = nr_autogrow;
+ }
+ }
+
+ return fda;
+}
+
+void fdarray__exit(struct fdarray *fda)
+{
+ free(fda->entries);
+ free(fda->priv);
+ fdarray__init(fda, 0);
+}
+
+void fdarray__delete(struct fdarray *fda)
+{
+ fdarray__exit(fda);
+ free(fda);
+}
+
+int fdarray__add(struct fdarray *fda, int fd, short revents)
+{
+ int pos = fda->nr;
+
+ if (fda->nr == fda->nr_alloc &&
+ fdarray__grow(fda, fda->nr_autogrow) < 0)
+ return -ENOMEM;
+
+ fda->entries[fda->nr].fd = fd;
+ fda->entries[fda->nr].events = revents;
+ fda->nr++;
+ return pos;
+}
+
+int fdarray__filter(struct fdarray *fda, short revents,
+ void (*entry_destructor)(struct fdarray *fda, int fd))
+{
+ int fd, nr = 0;
+
+ if (fda->nr == 0)
+ return 0;
+
+ for (fd = 0; fd < fda->nr; ++fd) {
+ if (fda->entries[fd].revents & revents) {
+ if (entry_destructor)
+ entry_destructor(fda, fd);
+
+ continue;
+ }
+
+ if (fd != nr) {
+ fda->entries[nr] = fda->entries[fd];
+ fda->priv[nr] = fda->priv[fd];
+ }
+
+ ++nr;
+ }
+
+ return fda->nr = nr;
+}
+
+int fdarray__poll(struct fdarray *fda, int timeout)
+{
+ return poll(fda->entries, fda->nr, timeout);
+}
+
+int fdarray__fprintf(struct fdarray *fda, FILE *fp)
+{
+ int fd, printed = fprintf(fp, "%d [ ", fda->nr);
+
+ for (fd = 0; fd < fda->nr; ++fd)
+ printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd);
+
+ return printed + fprintf(fp, " ]");
+}
diff --git a/tools/lib/api/fd/array.h b/tools/lib/api/fd/array.h
new file mode 100644
index 000000000000..45db01818f45
--- /dev/null
+++ b/tools/lib/api/fd/array.h
@@ -0,0 +1,46 @@
+#ifndef __API_FD_ARRAY__
+#define __API_FD_ARRAY__
+
+#include <stdio.h>
+
+struct pollfd;
+
+/**
+ * struct fdarray: Array of file descriptors
+ *
+ * @priv: Per array entry priv area, users should access just its contents,
+ * not set it to anything, as it is kept in synch with @entries, being
+ * realloc'ed, * for instance, in fdarray__{grow,filter}.
+ *
+ * I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
+ * but doing 'fda->priv = malloc(M)' is not allowed.
+ */
+struct fdarray {
+ int nr;
+ int nr_alloc;
+ int nr_autogrow;
+ struct pollfd *entries;
+ union {
+ int idx;
+ } *priv;
+};
+
+void fdarray__init(struct fdarray *fda, int nr_autogrow);
+void fdarray__exit(struct fdarray *fda);
+
+struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
+void fdarray__delete(struct fdarray *fda);
+
+int fdarray__add(struct fdarray *fda, int fd, short revents);
+int fdarray__poll(struct fdarray *fda, int timeout);
+int fdarray__filter(struct fdarray *fda, short revents,
+ void (*entry_destructor)(struct fdarray *fda, int fd));
+int fdarray__grow(struct fdarray *fda, int extra);
+int fdarray__fprintf(struct fdarray *fda, FILE *fp);
+
+static inline int fdarray__available_entries(struct fdarray *fda)
+{
+ return fda->nr_alloc - fda->nr;
+}
+
+#endif /* __API_FD_ARRAY__ */
diff --git a/tools/lib/traceevent/plugin_scsi.c b/tools/lib/traceevent/plugin_scsi.c
index eda326fc8620..3727de48c8d5 100644
--- a/tools/lib/traceevent/plugin_scsi.c
+++ b/tools/lib/traceevent/plugin_scsi.c
@@ -85,8 +85,9 @@ typedef unsigned int u32;
#define MOVE_MEDIUM 0xa5
#define EXCHANGE_MEDIUM 0xa6
#define READ_12 0xa8
+#define SERVICE_ACTION_OUT_12 0xa9
#define WRITE_12 0xaa
-#define READ_MEDIA_SERIAL_NUMBER 0xab
+#define SERVICE_ACTION_IN_12 0xab
#define WRITE_VERIFY_12 0xae
#define VERIFY_12 0xaf
#define SEARCH_HIGH_12 0xb0
@@ -107,7 +108,9 @@ typedef unsigned int u32;
#define VERIFY_16 0x8f
#define SYNCHRONIZE_CACHE_16 0x91
#define WRITE_SAME_16 0x93
-#define SERVICE_ACTION_IN 0x9e
+#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
+#define SERVICE_ACTION_IN_16 0x9e
+#define SERVICE_ACTION_OUT_16 0x9f
/* values for service action in */
#define SAI_READ_CAPACITY_16 0x10
#define SAI_GET_LBA_STATUS 0x12
@@ -393,7 +396,7 @@ scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len)
return scsi_trace_rw16(p, cdb, len);
case UNMAP:
return scsi_trace_unmap(p, cdb, len);
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
return scsi_trace_service_action_in(p, cdb, len);
case VARIABLE_LENGTH_CMD:
return scsi_trace_varlen(p, cdb, len);
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 782d86e961b9..40399c3d97d6 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -2,6 +2,8 @@ PERF-CFLAGS
PERF-GUI-VARS
PERF-VERSION-FILE
perf
+perf-read-vdso32
+perf-read-vdsox32
perf-help
perf-record
perf-report
@@ -15,6 +17,7 @@ perf.data
perf.data.old
output.svg
perf-archive
+perf-with-kcore
tags
TAGS
cscope*
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index b3b8abae62b8..e463caa3eb49 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -196,10 +196,10 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
- period being the hist entry period value
- - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option
+ - WEIGHT-A/WEIGHT-B being user supplied weights in the the '-c' option
behind ':' separator like '-c wdiff:1,2'.
- - WIEGHT-A being the weight of the data file
- - WIEGHT-B being the weight of the baseline data file
+ - WEIGHT-A being the weight of the data file
+ - WEIGHT-B being the weight of the baseline data file
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt
index 6e689dc89a2f..6252e776009c 100644
--- a/tools/perf/Documentation/perf-kvm.txt
+++ b/tools/perf/Documentation/perf-kvm.txt
@@ -100,7 +100,7 @@ OPTIONS
STAT REPORT OPTIONS
-------------------
--vcpu=<value>::
- analyze events which occures on this vcpu. (default: all vcpus)
+ analyze events which occur on this vcpu. (default: all vcpus)
--event=<value>::
event to be analyzed. Possible values: vmexit, mmio (x86 only),
@@ -134,7 +134,7 @@ STAT LIVE OPTIONS
Analyze events only for given process ID(s) (comma separated list).
--vcpu=<value>::
- analyze events which occures on this vcpu. (default: all vcpus)
+ analyze events which occur on this vcpu. (default: all vcpus)
--event=<value>::
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 6fce6a622206..cbb4f743d921 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -19,7 +19,7 @@ various perf commands with the -e option.
EVENT MODIFIERS
---------------
-Events can optionally have a modifer by appending a colon and one or
+Events can optionally have a modifier by appending a colon and one or
more modifiers. Modifiers allow the user to restrict the events to be
counted. The following modifiers exist:
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 1513935c399b..aaa869be3dc1 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -104,6 +104,9 @@ OPTIONS
Specify path to the executable or shared library file for user
space tracing. Can also be used with --funcs option.
+--demangle-kernel::
+ Demangle kernel symbols.
+
In absence of -m/-x options, perf probe checks if the first argument after
the options is an absolute path name. If its an absolute path, perf probe
uses it as a target module/target user space binary to probe.
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index d460049cae8e..af9a54ece024 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -146,7 +146,7 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
-N::
--no-buildid-cache::
-Do not update the builid cache. This saves some overhead in situations
+Do not update the buildid cache. This saves some overhead in situations
where the information in the perf.data file (which includes buildids)
is sufficient.
@@ -214,6 +214,12 @@ if combined with -a or -C options.
After starting the program, wait msecs before measuring. This is useful to
filter out the startup phase of the program, which is often very different.
+-I::
+--intr-regs::
+Capture machine state (registers) at interrupt, i.e., on counter overflows for
+each sample. List of captured registers depends on the architecture. This option
+is off by default.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index d2b59af62bc0..dd7cccdde498 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -147,7 +147,7 @@ OPTIONS
-w::
--column-widths=<width[,width...]>::
Force each column width to the provided list, for large terminal
- readability.
+ readability. 0 means no limit (default behavior).
-t::
--field-separator=::
@@ -159,7 +159,7 @@ OPTIONS
--dump-raw-trace::
Dump raw trace in ASCII.
--g [type,min[,limit],order[,key]]::
+-g [type,min[,limit],order[,key][,branch]]::
--call-graph::
Display call chains using type, min percent threshold, optional print
limit and order.
@@ -177,6 +177,11 @@ OPTIONS
- function: compare on functions
- address: compare on individual code addresses
+ branch can be:
+ - branch: include last branch information in callgraph
+ when available. Usually more convenient to use --branch-history
+ for this.
+
Default: fractal,0.5,callee,function.
--children::
@@ -266,6 +271,11 @@ OPTIONS
branch stacks and it will automatically switch to the branch view mode,
unless --no-branch-stack is used.
+--branch-history::
+ Add the addresses of sampled taken branches to the callstack.
+ This allows to examine the path the program took to each sample.
+ The data collection must have used -b (or -j) and -g.
+
--objdump=<path>::
Path to objdump binary.
@@ -276,6 +286,9 @@ OPTIONS
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
+--demangle-kernel::
+ Demangle kernel symbol names to human readable form (for C++ kernels).
+
--mem-mode::
Use the data addresses of samples in addition to instruction addresses
to build the histograms. To generate meaningful output, the perf.data
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
index d00bef231340..dfbb506d2c34 100644
--- a/tools/perf/Documentation/perf-script-perl.txt
+++ b/tools/perf/Documentation/perf-script-perl.txt
@@ -181,8 +181,8 @@ strings for flag and symbolic fields. These correspond to the strings
and values parsed from the 'print fmt' fields of the event format
files:
- flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name
- symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name
+ flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name
+ symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name
Perf::Trace::Context Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
index 9f1f054b8432..54acba221558 100644
--- a/tools/perf/Documentation/perf-script-python.txt
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -263,7 +263,7 @@ and having the counts we've tallied as values.
The print_syscall_totals() function iterates over the entries in the
dictionary and displays a line for each entry containing the syscall
-name (the dictonary keys contain the syscall ids, which are passed to
+name (the dictionary keys contain the syscall ids, which are passed to
the Util function syscall_name(), which translates the raw syscall
numbers to the corresponding syscall name strings). The output is
displayed after all the events in the trace have been processed, by
@@ -576,8 +576,8 @@ strings for flag and symbolic fields. These correspond to the strings
and values parsed from the 'print fmt' fields of the event format
files:
- flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
- symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
+ flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name
+ symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name
The *autodict* function returns a special kind of Python
dictionary that implements Perl's 'autovivifying' hashes in Python
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 05f9a0a6784c..21494806c0ab 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -115,7 +115,7 @@ OPTIONS
-f::
--fields::
Comma separated list of fields to print. Options are:
- comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline.
+ comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period.
Field list can be prepended with the type, trace, sw or hw,
to indicate to which event type the field list applies.
e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace
@@ -140,7 +140,7 @@ OPTIONS
"Overriding previous field request for all events."
- Alternativey, consider the order:
+ Alternatively, consider the order:
-f comm,tid,time,ip,sym -f trace:
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
index d1d3e5121f89..31a5c3ea7f74 100644
--- a/tools/perf/Documentation/perf-test.txt
+++ b/tools/perf/Documentation/perf-test.txt
@@ -25,7 +25,7 @@ OPTIONS
-------
-s::
--skip::
- Tests to skip (comma separater numeric list).
+ Tests to skip (comma separated numeric list).
-v::
--verbose::
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 180ae02137a5..3265b1070518 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -98,6 +98,9 @@ Default is to monitor all CPUS.
--hide_user_symbols::
Hide user symbols.
+--demangle-kernel::
+ Demangle kernel symbols.
+
-D::
--dump-symtab::
Dump the symbol table used for profiling.
@@ -193,6 +196,12 @@ Default is to monitor all CPUS.
sum of shown entries will be always 100%. "absolute" means it retains
the original value before and after the filter is applied.
+-w::
+--column-widths=<width[,width...]>::
+ Force each column width to the provided list, for large terminal
+ readability. 0 means no limit (default behavior).
+
+
INTERACTIVE PROMPTING KEYS
--------------------------
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 02aac831bdd9..7e1b1f2bb83c 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -20,7 +20,7 @@ scheduling events, etc.
This is a live mode tool in addition to working with perf.data files like
the other perf tools. Files can be generated using the 'perf record' command
but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*').
-Alernatively, the 'perf trace record' can be used as a shortcut to
+Alternatively, 'perf trace record' can be used as a shortcut to
automatically include the raw_syscalls events when writing events to a file.
The following options apply to perf trace; options to perf trace record are
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 2240974b7745..478efa9b2364 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -60,6 +60,15 @@ include config/utilities.mak
#
# Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
# for dwarf backtrace post unwind.
+#
+# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32
+# for reading the 32-bit compatibility VDSO in 64-bit mode
+#
+# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32
+# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
+#
+# Define NO_ZLIB if you do not want to support compressed kernel modules
+
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -126,6 +135,7 @@ PYRF_OBJS =
SCRIPT_SH =
SCRIPT_SH += perf-archive.sh
+SCRIPT_SH += perf-with-kcore.sh
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
@@ -170,11 +180,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
-#
-# Single 'perf' binary right now:
-#
PROGRAMS += $(OUTPUT)perf
+ifndef NO_PERF_READ_VDSO32
+PROGRAMS += $(OUTPUT)perf-read-vdso32
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+PROGRAMS += $(OUTPUT)perf-read-vdsox32
+endif
+
# what 'all' will build and 'install' will install, in perfexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -246,12 +261,14 @@ LIB_H += util/annotate.h
LIB_H += util/cache.h
LIB_H += util/callchain.h
LIB_H += util/build-id.h
+LIB_H += util/db-export.h
LIB_H += util/debug.h
LIB_H += util/pmu.h
LIB_H += util/event.h
LIB_H += util/evsel.h
LIB_H += util/evlist.h
LIB_H += util/exec_cmd.h
+LIB_H += util/find-vdso-map.c
LIB_H += util/levenshtein.h
LIB_H += util/machine.h
LIB_H += util/map.h
@@ -263,6 +280,7 @@ LIB_H += util/xyarray.h
LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
+LIB_H += util/ordered-events.h
LIB_H += util/strbuf.h
LIB_H += util/strlist.h
LIB_H += util/strfilter.h
@@ -302,6 +320,7 @@ LIB_H += ui/util.h
LIB_H += ui/ui.h
LIB_H += util/data.h
LIB_H += util/kvm-stat.h
+LIB_H += util/thread-stack.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -309,6 +328,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
+LIB_OBJS += $(OUTPUT)util/db-export.o
LIB_OBJS += $(OUTPUT)util/pmu.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
@@ -347,6 +367,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o
LIB_OBJS += $(OUTPUT)util/map.o
LIB_OBJS += $(OUTPUT)util/pstack.o
LIB_OBJS += $(OUTPUT)util/session.o
+LIB_OBJS += $(OUTPUT)util/ordered-events.o
LIB_OBJS += $(OUTPUT)util/comm.o
LIB_OBJS += $(OUTPUT)util/thread.o
LIB_OBJS += $(OUTPUT)util/thread_map.o
@@ -377,6 +398,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)util/tsc.o
LIB_OBJS += $(OUTPUT)util/cloexec.o
+LIB_OBJS += $(OUTPUT)util/thread-stack.o
LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o
@@ -399,6 +421,7 @@ LIB_OBJS += $(OUTPUT)tests/perf-record.o
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
+LIB_OBJS += $(OUTPUT)tests/fdarray.o
LIB_OBJS += $(OUTPUT)tests/pmu.o
LIB_OBJS += $(OUTPUT)tests/hists_common.o
LIB_OBJS += $(OUTPUT)tests/hists_link.o
@@ -423,6 +446,7 @@ endif
endif
LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
+LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
@@ -473,8 +497,6 @@ ifneq ($(OUTPUT),)
endif
ifdef NO_LIBELF
-EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
-
# Remove ELF/DWARF dependent codes
LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
@@ -563,6 +585,10 @@ ifndef NO_LIBNUMA
BUILTIN_OBJS += $(OUTPUT)bench/numa.o
endif
+ifndef NO_ZLIB
+ LIB_OBJS += $(OUTPUT)util/zlib.o
+endif
+
ifdef ASCIIDOC8
export ASCIIDOC8
endif
@@ -727,6 +753,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti
$(OUTPUT)perf-%: %.o $(PERFLIBS)
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
+ifndef NO_PERF_READ_VDSO32
+$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c
+ $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c
+ $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
@@ -765,7 +801,7 @@ $(LIBTRACEEVENT)-clean:
install-traceevent-plugins: $(LIBTRACEEVENT)
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins
-LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch])
+LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch])
# if subdir is set, we've been called from above so target has been built
# already
@@ -871,10 +907,20 @@ install-bin: all install-gtk
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
+ifndef NO_PERF_READ_VDSO32
+ $(call QUIET_INSTALL, perf-read-vdso32) \
+ $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
+ifndef NO_PERF_READ_VDSOX32
+ $(call QUIET_INSTALL, perf-read-vdsox32) \
+ $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
$(call QUIET_INSTALL, libexec) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(call QUIET_INSTALL, perf-archive) \
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ $(call QUIET_INSTALL, perf-with-kcore) \
+ $(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
ifndef NO_LIBPERL
$(call QUIET_INSTALL, perl-scripts) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
@@ -920,8 +966,8 @@ config-clean:
@$(MAKE) -C config/feature-checks clean >/dev/null
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
- $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
- $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
+ $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
+ $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean)
diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c
index 9f870d27cb39..62eff847f91c 100644
--- a/tools/perf/arch/arm/tests/dwarf-unwind.c
+++ b/tools/perf/arch/arm/tests/dwarf-unwind.c
@@ -3,6 +3,7 @@
#include "thread.h"
#include "map.h"
#include "event.h"
+#include "debug.h"
#include "tests/tests.h"
#define STACK_SIZE 8192
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c
index 729ed69a6664..62c397ed3d97 100644
--- a/tools/perf/arch/arm/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm/util/unwind-libunwind.c
@@ -3,6 +3,7 @@
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
+#include "../../util/debug.h"
int libunwind__arch_reg_id(int regnum)
{
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h
index e9441b9e2a30..1d3f39c3aa56 100644
--- a/tools/perf/arch/arm64/include/perf_regs.h
+++ b/tools/perf/arch/arm64/include/perf_regs.h
@@ -6,6 +6,8 @@
#include <asm/perf_regs.h>
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
+#define PERF_REGS_MAX PERF_REG_ARM64_MAX
+
#define PERF_REG_IP PERF_REG_ARM64_PC
#define PERF_REG_SP PERF_REG_ARM64_SP
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index 436ee43859dc..a87afa91a99e 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -3,6 +3,7 @@
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
+#include "../../util/debug.h"
int libunwind__arch_reg_id(int regnum)
{
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index 42faf369211c..49776f190abf 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -12,6 +12,11 @@ const char *const arm_triplets[] = {
NULL
};
+const char *const arm64_triplets[] = {
+ "aarch64-linux-android-",
+ NULL
+};
+
const char *const powerpc_triplets[] = {
"powerpc-unknown-linux-gnu-",
"powerpc64-unknown-linux-gnu-",
@@ -105,6 +110,8 @@ static const char *normalize_arch(char *arch)
return "x86";
if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5))
return "sparc";
+ if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64"))
+ return "arm64";
if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110"))
return "arm";
if (!strncmp(arch, "s390", 4))
@@ -159,6 +166,8 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
if (!strcmp(arch, "arm"))
path_list = arm_triplets;
+ else if (!strcmp(arch, "arm64"))
+ path_list = arm64_triplets;
else if (!strcmp(arch, "powerpc"))
path_list = powerpc_triplets;
else if (!strcmp(arch, "sh"))
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile
index b92219b1900d..6f7782bea5dd 100644
--- a/tools/perf/arch/powerpc/Makefile
+++ b/tools/perf/arch/powerpc/Makefile
@@ -1,6 +1,6 @@
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o
endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
-LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index a7c23a4b3778..3bb50eac5542 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -15,6 +15,7 @@
#include "util/thread.h"
#include "util/callchain.h"
+#include "util/debug.h"
/*
* When saving the callchain on Power, the kernel conservatively saves
@@ -144,7 +145,7 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
* yet used)
* -1 in case of errors
*/
-static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
+static int check_return_addr(struct dso *dso, Dwarf_Addr pc)
{
int rc = -1;
Dwfl *dwfl;
@@ -155,15 +156,27 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
Dwarf_Addr end = pc;
bool signalp;
- dwfl = dwfl_begin(&offline_callbacks);
- if (!dwfl) {
- pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
- return -1;
- }
+ dwfl = dso->dwfl;
- if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) {
- pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1));
- goto out;
+ if (!dwfl) {
+ dwfl = dwfl_begin(&offline_callbacks);
+ if (!dwfl) {
+ pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
+ return -1;
+ }
+
+ if (dwfl_report_offline(dwfl, "", dso->long_name, -1) == NULL) {
+ pr_debug("dwfl_report_offline() failed %s\n",
+ dwarf_errmsg(-1));
+ /*
+ * We normally cache the DWARF debug info and never
+ * call dwfl_end(). But to prevent fd leak, free in
+ * case of error.
+ */
+ dwfl_end(dwfl);
+ goto out;
+ }
+ dso->dwfl = dwfl;
}
mod = dwfl_addrmodule(dwfl, pc);
@@ -193,7 +206,6 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
rc = check_return_reg(ra_regno, frame);
out:
- dwfl_end(dwfl);
return rc;
}
@@ -220,8 +232,7 @@ out:
* index: of callchain entry that needs to be ignored (if any)
* -1 if no entry needs to be ignored or in case of errors
*/
-int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
- struct ip_callchain *chain)
+int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
{
struct addr_location al;
struct dso *dso = NULL;
@@ -234,7 +245,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
ip = chain->ips[2];
- thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
if (al.map)
@@ -245,7 +256,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
return skip_slot;
}
- rc = check_return_addr(dso->long_name, ip);
+ rc = check_return_addr(dso, ip);
pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n",
dso->long_name, chain->nr, ip, rc);
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index a84206e9c4aa..fc9bebd2cca0 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -26,6 +26,7 @@ static unsigned int nsecs = 10;
/* amount of futexes per thread */
static unsigned int nfutexes = 1024;
static bool fshared = false, done = false, silent = false;
+static int futex_flag = 0;
struct timeval start, end, runtime;
static pthread_mutex_t thread_lock;
@@ -75,8 +76,7 @@ static void *workerfn(void *arg)
* such as internal waitqueue handling, thus enlarging
* the critical region protected by hb->lock.
*/
- ret = futex_wait(&w->futex[i], 1234, NULL,
- fshared ? 0 : FUTEX_PRIVATE_FLAG);
+ ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
if (!silent &&
(!ret || errno != EAGAIN || errno != EWOULDBLOCK))
warn("Non-expected futex return call");
@@ -135,6 +135,9 @@ int bench_futex_hash(int argc, const char **argv,
if (!worker)
goto errmem;
+ if (!fshared)
+ futex_flag = FUTEX_PRIVATE_FLAG;
+
printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs);
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index 732403bfd31a..bedff6b5b3cf 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -30,16 +30,18 @@ static u_int32_t futex1 = 0, futex2 = 0;
static unsigned int nrequeue = 1;
static pthread_t *worker;
-static bool done = 0, silent = 0;
+static bool done = false, silent = false, fshared = false;
static pthread_mutex_t thread_lock;
static pthread_cond_t thread_parent, thread_worker;
static struct stats requeuetime_stats, requeued_stats;
static unsigned int ncpus, threads_starting, nthreads = 0;
+static int futex_flag = 0;
static const struct option options[] = {
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"),
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
+ OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
OPT_END()
};
@@ -70,7 +72,7 @@ static void *workerfn(void *arg __maybe_unused)
pthread_cond_wait(&thread_worker, &thread_lock);
pthread_mutex_unlock(&thread_lock);
- futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG);
+ futex_wait(&futex1, 0, NULL, futex_flag);
return NULL;
}
@@ -127,9 +129,12 @@ int bench_futex_requeue(int argc, const char **argv,
if (!worker)
err(EXIT_FAILURE, "calloc");
- printf("Run summary [PID %d]: Requeuing %d threads (from %p to %p), "
- "%d at a time.\n\n",
- getpid(), nthreads, &futex1, &futex2, nrequeue);
+ if (!fshared)
+ futex_flag = FUTEX_PRIVATE_FLAG;
+
+ printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), "
+ "%d at a time.\n\n", getpid(), nthreads,
+ fshared ? "shared":"private", &futex1, &futex2, nrequeue);
init_stats(&requeued_stats);
init_stats(&requeuetime_stats);
@@ -156,16 +161,20 @@ int bench_futex_requeue(int argc, const char **argv,
/* Ok, all threads are patiently blocked, start requeueing */
gettimeofday(&start, NULL);
- for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue)
+ for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) {
/*
* Do not wakeup any tasks blocked on futex1, allowing
* us to really measure futex_wait functionality.
*/
- futex_cmp_requeue(&futex1, 0, &futex2, 0, nrequeue,
- FUTEX_PRIVATE_FLAG);
+ futex_cmp_requeue(&futex1, 0, &futex2, 0,
+ nrequeue, futex_flag);
+ }
gettimeofday(&end, NULL);
timersub(&end, &start, &runtime);
+ if (nrequeued > nthreads)
+ nrequeued = nthreads;
+
update_stats(&requeued_stats, nrequeued);
update_stats(&requeuetime_stats, runtime.tv_usec);
@@ -175,7 +184,7 @@ int bench_futex_requeue(int argc, const char **argv,
}
/* everybody should be blocked on futex2, wake'em up */
- nrequeued = futex_wake(&futex2, nthreads, FUTEX_PRIVATE_FLAG);
+ nrequeued = futex_wake(&futex2, nthreads, futex_flag);
if (nthreads != nrequeued)
warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads);
@@ -184,7 +193,6 @@ int bench_futex_requeue(int argc, const char **argv,
if (ret)
err(EXIT_FAILURE, "pthread_join");
}
-
}
/* cleanup & report results */
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 50022cbce87e..929f762be47e 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -31,16 +31,18 @@ static u_int32_t futex1 = 0;
static unsigned int nwakes = 1;
pthread_t *worker;
-static bool done = false, silent = false;
+static bool done = false, silent = false, fshared = false;
static pthread_mutex_t thread_lock;
static pthread_cond_t thread_parent, thread_worker;
static struct stats waketime_stats, wakeup_stats;
static unsigned int ncpus, threads_starting, nthreads = 0;
+static int futex_flag = 0;
static const struct option options[] = {
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"),
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
+ OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
OPT_END()
};
@@ -58,7 +60,7 @@ static void *workerfn(void *arg __maybe_unused)
pthread_cond_wait(&thread_worker, &thread_lock);
pthread_mutex_unlock(&thread_lock);
- futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG);
+ futex_wait(&futex1, 0, NULL, futex_flag);
return NULL;
}
@@ -130,9 +132,12 @@ int bench_futex_wake(int argc, const char **argv,
if (!worker)
err(EXIT_FAILURE, "calloc");
- printf("Run summary [PID %d]: blocking on %d threads (at futex %p), "
+ if (!fshared)
+ futex_flag = FUTEX_PRIVATE_FLAG;
+
+ printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
"waking up %d at a time.\n\n",
- getpid(), nthreads, &futex1, nwakes);
+ getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes);
init_stats(&wakeup_stats);
init_stats(&waketime_stats);
@@ -160,7 +165,7 @@ int bench_futex_wake(int argc, const char **argv,
/* Ok, all threads are patiently blocked, start waking folks up */
gettimeofday(&start, NULL);
while (nwoken != nthreads)
- nwoken += futex_wake(&futex1, nwakes, FUTEX_PRIVATE_FLAG);
+ nwoken += futex_wake(&futex1, nwakes, futex_flag);
gettimeofday(&end, NULL);
timersub(&end, &start, &runtime);
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
index 52a56599a543..d7f281c2828d 100644
--- a/tools/perf/bench/sched-messaging.c
+++ b/tools/perf/bench/sched-messaging.c
@@ -26,7 +26,7 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <limits.h>
#include <err.h>
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 1ec429fef2be..e7417fe97a97 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -36,7 +36,8 @@
struct perf_annotate {
struct perf_tool tool;
- bool force, use_tui, use_stdio, use_gtk;
+ struct perf_session *session;
+ bool use_tui, use_stdio, use_gtk;
bool full_paths;
bool print_line;
bool skip_missing;
@@ -50,6 +51,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
struct addr_location *al,
struct perf_annotate *ann)
{
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
int ret;
@@ -65,13 +67,12 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
return 0;
}
- he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0,
- true);
+ he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true);
if (he == NULL)
return -ENOMEM;
ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
- hists__inc_nr_samples(&evsel->hists, true);
+ hists__inc_nr_samples(hists, true);
return ret;
}
@@ -188,18 +189,9 @@ find_next:
static int __cmd_annotate(struct perf_annotate *ann)
{
int ret;
- struct perf_session *session;
+ struct perf_session *session = ann->session;
struct perf_evsel *pos;
u64 total_nr_samples;
- struct perf_data_file file = {
- .path = input_name,
- .mode = PERF_DATA_MODE_READ,
- .force = ann->force,
- };
-
- session = perf_session__new(&file, false, &ann->tool);
- if (session == NULL)
- return -ENOMEM;
machines__set_symbol_filter(&session->machines, symbol__annotate_init);
@@ -207,22 +199,23 @@ static int __cmd_annotate(struct perf_annotate *ann)
ret = perf_session__cpu_bitmap(session, ann->cpu_list,
ann->cpu_bitmap);
if (ret)
- goto out_delete;
+ goto out;
}
if (!objdump_path) {
ret = perf_session_env__lookup_objdump(&session->header.env);
if (ret)
- goto out_delete;
+ goto out;
}
ret = perf_session__process_events(session, &ann->tool);
if (ret)
- goto out_delete;
+ goto out;
if (dump_trace) {
perf_session__fprintf_nr_events(session, stdout);
- goto out_delete;
+ perf_evlist__fprintf_nr_events(session->evlist, stdout);
+ goto out;
}
if (verbose > 3)
@@ -233,7 +226,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
total_nr_samples = 0;
evlist__for_each(session->evlist, pos) {
- struct hists *hists = &pos->hists;
+ struct hists *hists = evsel__hists(pos);
u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
if (nr_samples > 0) {
@@ -250,8 +243,8 @@ static int __cmd_annotate(struct perf_annotate *ann)
}
if (total_nr_samples == 0) {
- ui__error("The %s file has no samples!\n", file.path);
- goto out_delete;
+ ui__error("The %s file has no samples!\n", session->file->path);
+ goto out;
}
if (use_browser == 2) {
@@ -261,24 +254,12 @@ static int __cmd_annotate(struct perf_annotate *ann)
"perf_gtk__show_annotations");
if (show_annotations == NULL) {
ui__error("GTK browser not found!\n");
- goto out_delete;
+ goto out;
}
show_annotations();
}
-out_delete:
- /*
- * Speed up the exit process, for large files this can
- * take quite a while.
- *
- * XXX Enable this when using valgrind or if we ever
- * librarize this command.
- *
- * Also experiment with obstacks to see how much speed
- * up we'll get here.
- *
- * perf_session__delete(session);
- */
+out:
return ret;
}
@@ -297,10 +278,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
- .ordered_samples = true,
+ .ordered_events = true,
.ordering_requires_timestamps = true,
},
};
+ struct perf_data_file file = {
+ .path = input_name,
+ .mode = PERF_DATA_MODE_READ,
+ };
const struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
"input file name"),
@@ -308,7 +293,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
"only consider symbols in these dsos"),
OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
"symbol to annotate"),
- OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),
+ OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
@@ -341,6 +326,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
"Show event group information together"),
OPT_END()
};
+ int ret = hists__init();
+
+ if (ret < 0)
+ return ret;
argc = parse_options(argc, argv, options, annotate_usage, 0);
@@ -353,11 +342,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
setup_browser(true);
+ annotate.session = perf_session__new(&file, false, &annotate.tool);
+ if (annotate.session == NULL)
+ return -1;
+
symbol_conf.priv_size = sizeof(struct annotation);
symbol_conf.try_vmlinux_path = true;
- if (symbol__init() < 0)
- return -1;
+ ret = symbol__init(&annotate.session->header.env);
+ if (ret < 0)
+ goto out_delete;
if (setup_sorting() < 0)
usage_with_options(annotate_usage, options);
@@ -373,5 +367,20 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
annotate.sym_hist_filter = argv[0];
}
- return __cmd_annotate(&annotate);
+ ret = __cmd_annotate(&annotate);
+
+out_delete:
+ /*
+ * Speed up the exit process, for large files this can
+ * take quite a while.
+ *
+ * XXX Enable this when using valgrind or if we ever
+ * librarize this command.
+ *
+ * Also experiment with obstacks to see how much speed
+ * up we'll get here.
+ *
+ * perf_session__delete(session);
+ */
+ return ret;
}
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 2a2c78f80876..70385756da63 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -246,20 +246,9 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
return true;
}
-static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp)
+static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
{
- struct perf_data_file file = {
- .path = filename,
- .mode = PERF_DATA_MODE_READ,
- .force = force,
- };
- struct perf_session *session = perf_session__new(&file, false, NULL);
- if (session == NULL)
- return -1;
-
perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
- perf_session__delete(session);
-
return 0;
}
@@ -302,6 +291,12 @@ int cmd_buildid_cache(int argc, const char **argv,
*missing_filename = NULL,
*update_name_list_str = NULL,
*kcore_filename;
+ char sbuf[STRERR_BUFSIZE];
+
+ struct perf_data_file file = {
+ .mode = PERF_DATA_MODE_READ,
+ };
+ struct perf_session *session = NULL;
const struct option buildid_cache_options[] = {
OPT_STRING('a', "add", &add_name_list_str,
@@ -326,8 +321,17 @@ int cmd_buildid_cache(int argc, const char **argv,
argc = parse_options(argc, argv, buildid_cache_options,
buildid_cache_usage, 0);
- if (symbol__init() < 0)
- return -1;
+ if (missing_filename) {
+ file.path = missing_filename;
+ file.force = force;
+
+ session = perf_session__new(&file, false, NULL);
+ if (session == NULL)
+ return -1;
+ }
+
+ if (symbol__init(session ? &session->header.env : NULL) < 0)
+ goto out;
setup_pager();
@@ -344,7 +348,7 @@ int cmd_buildid_cache(int argc, const char **argv,
continue;
}
pr_warning("Couldn't add %s: %s\n",
- pos->s, strerror(errno));
+ pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
}
strlist__delete(list);
@@ -362,7 +366,7 @@ int cmd_buildid_cache(int argc, const char **argv,
continue;
}
pr_warning("Couldn't remove %s: %s\n",
- pos->s, strerror(errno));
+ pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
}
strlist__delete(list);
@@ -370,7 +374,7 @@ int cmd_buildid_cache(int argc, const char **argv,
}
if (missing_filename)
- ret = build_id_cache__fprintf_missing(missing_filename, force, stdout);
+ ret = build_id_cache__fprintf_missing(session, stdout);
if (update_name_list_str) {
list = strlist__new(true, update_name_list_str);
@@ -383,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv,
continue;
}
pr_warning("Couldn't update %s: %s\n",
- pos->s, strerror(errno));
+ pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
}
strlist__delete(list);
@@ -394,5 +398,9 @@ int cmd_buildid_cache(int argc, const char **argv,
build_id_cache__add_kcore(kcore_filename, debugdir, force))
pr_warning("Couldn't add %s\n", kcore_filename);
+out:
+ if (session)
+ perf_session__delete(session);
+
return ret;
}
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 9a5a035cb426..1ce425d101a9 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -327,6 +327,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
struct machine *machine)
{
struct addr_location al;
+ struct hists *hists = evsel__hists(evsel);
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
pr_warning("problem processing %d event, skipping it.\n",
@@ -334,7 +335,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
return -1;
}
- if (hists__add_entry(&evsel->hists, &al, sample->period,
+ if (hists__add_entry(hists, &al, sample->period,
sample->weight, sample->transaction)) {
pr_warning("problem incrementing symbol period, skipping event\n");
return -1;
@@ -346,9 +347,9 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
* hists__output_resort() and precompute needs the total
* period in order to sort entries by percentage delta.
*/
- evsel->hists.stats.total_period += sample->period;
+ hists->stats.total_period += sample->period;
if (!al.filtered)
- evsel->hists.stats.total_non_filtered_period += sample->period;
+ hists->stats.total_non_filtered_period += sample->period;
return 0;
}
@@ -356,11 +357,12 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
static struct perf_tool tool = {
.sample = diff__process_sample_event,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
.lost = perf_event__process_lost,
- .ordered_samples = true,
+ .ordered_events = true,
.ordering_requires_timestamps = true,
};
@@ -382,7 +384,7 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel) {
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
hists__collapse_resort(hists, NULL);
}
@@ -631,24 +633,26 @@ static void data_process(void)
bool first = true;
evlist__for_each(evlist_base, evsel_base) {
+ struct hists *hists_base = evsel__hists(evsel_base);
struct data__file *d;
int i;
data__for_each_file_new(i, d) {
struct perf_evlist *evlist = d->session->evlist;
struct perf_evsel *evsel;
+ struct hists *hists;
evsel = evsel_match(evsel_base, evlist);
if (!evsel)
continue;
- d->hists = &evsel->hists;
+ hists = evsel__hists(evsel);
+ d->hists = hists;
- hists__match(&evsel_base->hists, &evsel->hists);
+ hists__match(hists_base, hists);
if (!show_baseline_only)
- hists__link(&evsel_base->hists,
- &evsel->hists);
+ hists__link(hists_base, hists);
}
fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
@@ -659,7 +663,7 @@ static void data_process(void)
if (verbose || data__files_cnt > 2)
data__fprintf();
- hists__process(&evsel_base->hists);
+ hists__process(hists_base);
}
}
@@ -683,7 +687,7 @@ static int __cmd_diff(void)
d->session = perf_session__new(&d->file, false, &tool);
if (!d->session) {
pr_err("Failed to open %s\n", d->file.path);
- ret = -ENOMEM;
+ ret = -1;
goto out_delete;
}
@@ -1139,11 +1143,16 @@ static int data_init(int argc, const char **argv)
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
{
+ int ret = hists__init();
+
+ if (ret < 0)
+ return ret;
+
perf_config(perf_default_config, NULL);
argc = parse_options(argc, argv, options, diff_usage, 0);
- if (symbol__init() < 0)
+ if (symbol__init(NULL) < 0)
return -1;
if (data_init(argc, argv) < 0)
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 66e12f55c052..0f93f859b782 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
session = perf_session__new(&file, 0, NULL);
if (session == NULL)
- return -ENOMEM;
+ return -1;
evlist__for_each(session->evlist, pos)
perf_evsel__fprintf(pos, details, stdout);
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 0384d930480b..25d20628212e 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -103,6 +103,8 @@ static int check_emacsclient_version(void)
static void exec_woman_emacs(const char *path, const char *page)
{
+ char sbuf[STRERR_BUFSIZE];
+
if (!check_emacsclient_version()) {
/* This works only with emacsclient version >= 22. */
struct strbuf man_page = STRBUF_INIT;
@@ -111,16 +113,19 @@ static void exec_woman_emacs(const char *path, const char *page)
path = "emacsclient";
strbuf_addf(&man_page, "(woman \"%s\")", page);
execlp(path, "emacsclient", "-e", man_page.buf, NULL);
- warning("failed to exec '%s': %s", path, strerror(errno));
+ warning("failed to exec '%s': %s", path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
}
}
static void exec_man_konqueror(const char *path, const char *page)
{
const char *display = getenv("DISPLAY");
+
if (display && *display) {
struct strbuf man_page = STRBUF_INIT;
const char *filename = "kfmclient";
+ char sbuf[STRERR_BUFSIZE];
/* It's simpler to launch konqueror using kfmclient. */
if (path) {
@@ -139,24 +144,31 @@ static void exec_man_konqueror(const char *path, const char *page)
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
execlp(path, filename, "newTab", man_page.buf, NULL);
- warning("failed to exec '%s': %s", path, strerror(errno));
+ warning("failed to exec '%s': %s", path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
}
}
static void exec_man_man(const char *path, const char *page)
{
+ char sbuf[STRERR_BUFSIZE];
+
if (!path)
path = "man";
execlp(path, "man", page, NULL);
- warning("failed to exec '%s': %s", path, strerror(errno));
+ warning("failed to exec '%s': %s", path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
}
static void exec_man_cmd(const char *cmd, const char *page)
{
struct strbuf shell_cmd = STRBUF_INIT;
+ char sbuf[STRERR_BUFSIZE];
+
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
- warning("failed to exec '%s': %s", cmd, strerror(errno));
+ warning("failed to exec '%s': %s", cmd,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
}
static void add_man_viewer(const char *name)
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 9a02807387d6..84df2deed988 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -23,6 +23,7 @@
struct perf_inject {
struct perf_tool tool;
+ struct perf_session *session;
bool build_ids;
bool sched_stat;
const char *input_name;
@@ -216,8 +217,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
goto repipe;
}
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->ip, &al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
if (al.map != NULL) {
if (!al.map->dso->hit) {
@@ -340,12 +340,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
static int __cmd_inject(struct perf_inject *inject)
{
- struct perf_session *session;
int ret = -EINVAL;
- struct perf_data_file file = {
- .path = inject->input_name,
- .mode = PERF_DATA_MODE_READ,
- };
+ struct perf_session *session = inject->session;
struct perf_data_file *file_out = &inject->output;
signal(SIGINT, sig_handler);
@@ -357,16 +353,12 @@ static int __cmd_inject(struct perf_inject *inject)
inject->tool.tracing_data = perf_event__repipe_tracing_data;
}
- session = perf_session__new(&file, true, &inject->tool);
- if (session == NULL)
- return -ENOMEM;
-
if (inject->build_ids) {
inject->tool.sample = perf_event__inject_buildid;
} else if (inject->sched_stat) {
struct perf_evsel *evsel;
- inject->tool.ordered_samples = true;
+ inject->tool.ordered_events = true;
evlist__for_each(session->evlist, evsel) {
const char *name = perf_evsel__name(evsel);
@@ -396,8 +388,6 @@ static int __cmd_inject(struct perf_inject *inject)
perf_session__write_header(session, session->evlist, file_out->fd, true);
}
- perf_session__delete(session);
-
return ret;
}
@@ -419,6 +409,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.tracing_data = perf_event__repipe_op2_synth,
.finished_round = perf_event__repipe_op2_synth,
.build_id = perf_event__repipe_op2_synth,
+ .id_index = perf_event__repipe_op2_synth,
},
.input_name = "-",
.samples = LIST_HEAD_INIT(inject.samples),
@@ -427,6 +418,11 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.mode = PERF_DATA_MODE_WRITE,
},
};
+ struct perf_data_file file = {
+ .mode = PERF_DATA_MODE_READ,
+ };
+ int ret;
+
const struct option options[] = {
OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
"Inject build-ids into the output stream"),
@@ -461,8 +457,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
return -1;
}
- if (symbol__init() < 0)
+ file.path = inject.input_name;
+ inject.session = perf_session__new(&file, true, &inject.tool);
+ if (inject.session == NULL)
+ return -1;
+
+ if (symbol__init(&inject.session->header.env) < 0)
return -1;
- return __cmd_inject(&inject);
+ ret = __cmd_inject(&inject);
+
+ perf_session__delete(inject.session);
+
+ return ret;
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index bef3376bfaf3..f295141025bc 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -256,7 +256,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
static struct perf_tool perf_kmem = {
.sample = process_sample_event,
.comm = perf_event__process_comm,
- .ordered_samples = true,
+ .mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
+ .ordered_events = true,
};
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
@@ -403,10 +405,9 @@ static void sort_result(void)
__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
}
-static int __cmd_kmem(void)
+static int __cmd_kmem(struct perf_session *session)
{
int err = -EINVAL;
- struct perf_session *session;
const struct perf_evsel_str_handler kmem_tracepoints[] = {
{ "kmem:kmalloc", perf_evsel__process_alloc_event, },
{ "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, },
@@ -415,34 +416,22 @@ static int __cmd_kmem(void)
{ "kmem:kfree", perf_evsel__process_free_event, },
{ "kmem:kmem_cache_free", perf_evsel__process_free_event, },
};
- struct perf_data_file file = {
- .path = input_name,
- .mode = PERF_DATA_MODE_READ,
- };
-
- session = perf_session__new(&file, false, &perf_kmem);
- if (session == NULL)
- return -ENOMEM;
-
- if (perf_session__create_kernel_maps(session) < 0)
- goto out_delete;
if (!perf_session__has_traces(session, "kmem record"))
- goto out_delete;
+ goto out;
if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
pr_err("Initializing perf session tracepoint handlers failed\n");
- return -1;
+ goto out;
}
setup_pager();
err = perf_session__process_events(session, &perf_kmem);
if (err != 0)
- goto out_delete;
+ goto out;
sort_result();
print_result(session);
-out_delete:
- perf_session__delete(session);
+out:
return err;
}
@@ -689,29 +678,46 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
NULL,
NULL
};
+ struct perf_session *session;
+ struct perf_data_file file = {
+ .path = input_name,
+ .mode = PERF_DATA_MODE_READ,
+ };
+ int ret = -1;
+
argc = parse_options_subcommand(argc, argv, kmem_options,
kmem_subcommands, kmem_usage, 0);
if (!argc)
usage_with_options(kmem_usage, kmem_options);
- symbol__init();
-
if (!strncmp(argv[0], "rec", 3)) {
+ symbol__init(NULL);
return __cmd_record(argc, argv);
- } else if (!strcmp(argv[0], "stat")) {
+ }
+
+ session = perf_session__new(&file, false, &perf_kmem);
+ if (session == NULL)
+ return -1;
+
+ symbol__init(&session->header.env);
+
+ if (!strcmp(argv[0], "stat")) {
if (cpu__setup_cpunode_map())
- return -1;
+ goto out_delete;
if (list_empty(&caller_sort))
setup_sorting(&caller_sort, default_sort_order);
if (list_empty(&alloc_sort))
setup_sorting(&alloc_sort, default_sort_order);
- return __cmd_kmem();
+ ret = __cmd_kmem(session);
} else
usage_with_options(kmem_usage, kmem_options);
- return 0;
+out_delete:
+ perf_session__delete(session);
+
+ return ret;
}
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 43367eb00510..3c0f3d4fb021 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -376,7 +376,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
struct perf_sample *sample)
{
/* Only kvm_entry records vcpu id. */
- if (!thread->priv && kvm_entry_event(evsel)) {
+ if (!thread__priv(thread) && kvm_entry_event(evsel)) {
struct vcpu_event_record *vcpu_record;
vcpu_record = zalloc(sizeof(*vcpu_record));
@@ -386,10 +386,10 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
}
vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID);
- thread->priv = vcpu_record;
+ thread__set_priv(thread, vcpu_record);
}
- return thread->priv;
+ return thread__priv(thread);
}
static bool handle_kvm_event(struct perf_kvm_stat *kvm,
@@ -543,14 +543,12 @@ static void print_vcpu_info(struct perf_kvm_stat *kvm)
pr_info("Analyze events for ");
- if (kvm->live) {
- if (kvm->opts.target.system_wide)
- pr_info("all VMs, ");
- else if (kvm->opts.target.pid)
- pr_info("pid(s) %s, ", kvm->opts.target.pid);
- else
- pr_info("dazed and confused on what is monitored, ");
- }
+ if (kvm->opts.target.system_wide)
+ pr_info("all VMs, ");
+ else if (kvm->opts.target.pid)
+ pr_info("pid(s) %s, ", kvm->opts.target.pid);
+ else
+ pr_info("dazed and confused on what is monitored, ");
if (vcpu == -1)
pr_info("all VCPUs:\n\n");
@@ -592,8 +590,8 @@ static void print_result(struct perf_kvm_stat *kvm)
pr_info("%9s ", "Samples%");
pr_info("%9s ", "Time%");
- pr_info("%10s ", "Min Time");
- pr_info("%10s ", "Max Time");
+ pr_info("%11s ", "Min Time");
+ pr_info("%11s ", "Max Time");
pr_info("%16s ", "Avg time");
pr_info("\n\n");
@@ -610,8 +608,8 @@ static void print_result(struct perf_kvm_stat *kvm)
pr_info("%10llu ", (unsigned long long)ecount);
pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100);
pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100);
- pr_info("%8" PRIu64 "us ", min / 1000);
- pr_info("%8" PRIu64 "us ", max / 1000);
+ pr_info("%9.2fus ", (double)min / 1e3);
+ pr_info("%9.2fus ", (double)max / 1e3);
pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3,
kvm_event_rel_stddev(vcpu, event));
pr_info("\n");
@@ -732,7 +730,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
return -1;
}
- err = perf_session_queue_event(kvm->session, event, &sample, 0);
+ err = perf_session_queue_event(kvm->session, event, &kvm->tool, &sample, 0);
/*
* FIXME: Here we can't consume the event, as perf_session_queue_event will
* point to it, and it'll get possibly overwritten by the kernel.
@@ -785,7 +783,7 @@ static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm)
/* flush queue after each round in which we processed events */
if (ntotal) {
- kvm->session->ordered_samples.next_flush = flush_time;
+ kvm->session->ordered_events.next_flush = flush_time;
err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session);
if (err) {
if (kvm->lost_events)
@@ -885,15 +883,11 @@ static int fd_set_nonblock(int fd)
return 0;
}
-static
-int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save)
+static int perf_kvm__handle_stdin(void)
{
int c;
- tcsetattr(0, TCSANOW, tc_now);
c = getc(stdin);
- tcsetattr(0, TCSAFLUSH, tc_save);
-
if (c == 'q')
return 1;
@@ -902,9 +896,8 @@ int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save)
static int kvm_events_live_report(struct perf_kvm_stat *kvm)
{
- struct pollfd *pollfds = NULL;
- int nr_fds, nr_stdin, ret, err = -EINVAL;
- struct termios tc, save;
+ int nr_stdin, ret, err = -EINVAL;
+ struct termios save;
/* live flag must be set first */
kvm->live = true;
@@ -919,41 +912,25 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
goto out;
}
+ set_term_quiet_input(&save);
init_kvm_event_record(kvm);
- tcgetattr(0, &save);
- tc = save;
- tc.c_lflag &= ~(ICANON | ECHO);
- tc.c_cc[VMIN] = 0;
- tc.c_cc[VTIME] = 0;
-
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
- /* copy pollfds -- need to add timerfd and stdin */
- nr_fds = kvm->evlist->nr_fds;
- pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2));
- if (!pollfds) {
- err = -ENOMEM;
- goto out;
- }
- memcpy(pollfds, kvm->evlist->pollfd,
- sizeof(struct pollfd) * kvm->evlist->nr_fds);
-
/* add timer fd */
if (perf_kvm__timerfd_create(kvm) < 0) {
err = -1;
goto out;
}
- pollfds[nr_fds].fd = kvm->timerfd;
- pollfds[nr_fds].events = POLLIN;
- nr_fds++;
+ if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd) < 0)
+ goto out;
+
+ nr_stdin = perf_evlist__add_pollfd(kvm->evlist, fileno(stdin));
+ if (nr_stdin < 0)
+ goto out;
- pollfds[nr_fds].fd = fileno(stdin);
- pollfds[nr_fds].events = POLLIN;
- nr_stdin = nr_fds;
- nr_fds++;
if (fd_set_nonblock(fileno(stdin)) != 0)
goto out;
@@ -961,6 +938,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
perf_evlist__enable(kvm->evlist);
while (!done) {
+ struct fdarray *fda = &kvm->evlist->pollfd;
int rc;
rc = perf_kvm__mmap_read(kvm);
@@ -971,11 +949,11 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
if (err)
goto out;
- if (pollfds[nr_stdin].revents & POLLIN)
- done = perf_kvm__handle_stdin(&tc, &save);
+ if (fda->entries[nr_stdin].revents & POLLIN)
+ done = perf_kvm__handle_stdin();
if (!rc && !done)
- err = poll(pollfds, nr_fds, 100);
+ err = fdarray__poll(fda, 100);
}
perf_evlist__disable(kvm->evlist);
@@ -989,7 +967,7 @@ out:
if (kvm->timerfd >= 0)
close(kvm->timerfd);
- free(pollfds);
+ tcsetattr(0, TCSAFLUSH, &save);
return err;
}
@@ -998,6 +976,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
int err, rc = -1;
struct perf_evsel *pos;
struct perf_evlist *evlist = kvm->evlist;
+ char sbuf[STRERR_BUFSIZE];
perf_evlist__config(evlist, &kvm->opts);
@@ -1034,12 +1013,14 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
err = perf_evlist__open(evlist);
if (err < 0) {
- printf("Couldn't create the events: %s\n", strerror(errno));
+ printf("Couldn't create the events: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out;
}
if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) {
- ui__error("Failed to mmap the events: %s\n", strerror(errno));
+ ui__error("Failed to mmap the events: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
perf_evlist__close(evlist);
goto out;
}
@@ -1058,7 +1039,7 @@ static int read_events(struct perf_kvm_stat *kvm)
struct perf_tool eops = {
.sample = process_sample_event,
.comm = perf_event__process_comm,
- .ordered_samples = true,
+ .ordered_events = true,
};
struct perf_data_file file = {
.path = kvm->file_name,
@@ -1069,9 +1050,11 @@ static int read_events(struct perf_kvm_stat *kvm)
kvm->session = perf_session__new(&file, false, &kvm->tool);
if (!kvm->session) {
pr_err("Initializing perf session failed\n");
- return -EINVAL;
+ return -1;
}
+ symbol__init(&kvm->session->header.env);
+
if (!perf_session__has_traces(kvm->session, "kvm record"))
return -EINVAL;
@@ -1088,8 +1071,8 @@ static int read_events(struct perf_kvm_stat *kvm)
static int parse_target_str(struct perf_kvm_stat *kvm)
{
- if (kvm->pid_str) {
- kvm->pid_list = intlist__new(kvm->pid_str);
+ if (kvm->opts.target.pid) {
+ kvm->pid_list = intlist__new(kvm->opts.target.pid);
if (kvm->pid_list == NULL) {
pr_err("Error parsing process id string\n");
return -EINVAL;
@@ -1149,6 +1132,10 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
"-m", "1024",
"-c", "1",
};
+ const char * const kvm_stat_record_usage[] = {
+ "perf kvm stat record [<options>]",
+ NULL
+ };
const char * const *events_tp;
events_tp_size = 0;
@@ -1176,6 +1163,27 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
for (j = 1; j < (unsigned int)argc; j++, i++)
rec_argv[i] = argv[j];
+ set_option_flag(record_options, 'e', "event", PARSE_OPT_HIDDEN);
+ set_option_flag(record_options, 0, "filter", PARSE_OPT_HIDDEN);
+ set_option_flag(record_options, 'R', "raw-samples", PARSE_OPT_HIDDEN);
+
+ set_option_flag(record_options, 'F', "freq", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 0, "group", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'g', NULL, PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 0, "call-graph", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'd', "data", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'T', "timestamp", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'P', "period", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'n', "no-samples", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'N', "no-buildid-cache", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'B', "no-buildid", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'G', "cgroup", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'b', "branch-any", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'j', "branch-filter", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 'W', "weight", PARSE_OPT_DISABLED);
+ set_option_flag(record_options, 0, "transaction", PARSE_OPT_DISABLED);
+
+ record_usage = kvm_stat_record_usage;
return cmd_record(i, rec_argv, NULL);
}
@@ -1191,7 +1199,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
"key for sorting: sample(sort by samples number)"
" time (sort by avg time)"),
- OPT_STRING('p', "pid", &kvm->pid_str, "pid",
+ OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid",
"analyze events only for given process id(s)"),
OPT_END()
};
@@ -1201,8 +1209,6 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
NULL
};
- symbol__init();
-
if (argc) {
argc = parse_options(argc, argv,
kvm_events_report_options,
@@ -1212,6 +1218,9 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
kvm_events_report_options);
}
+ if (!kvm->opts.target.pid)
+ kvm->opts.target.system_wide = true;
+
return kvm_events_report_vcpu(kvm);
}
@@ -1311,7 +1320,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
kvm->tool.exit = perf_event__process_exit;
kvm->tool.fork = perf_event__process_fork;
kvm->tool.lost = process_lost_event;
- kvm->tool.ordered_samples = true;
+ kvm->tool.ordered_events = true;
perf_tool__fill_defaults(&kvm->tool);
/* set defaults */
@@ -1322,7 +1331,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
kvm->opts.target.uid_str = NULL;
kvm->opts.target.uid = UINT_MAX;
- symbol__init();
+ symbol__init(NULL);
disable_buildid_cache();
use_browser = 0;
@@ -1369,11 +1378,12 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
*/
kvm->session = perf_session__new(&file, false, &kvm->tool);
if (kvm->session == NULL) {
- err = -ENOMEM;
+ err = -1;
goto out;
}
kvm->session->evlist = kvm->evlist;
perf_session__set_id_hdr_size(kvm->session);
+ ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true);
machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target,
kvm->evlist->threads, false);
err = kvm_live_open_events(kvm);
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 6148afc995c6..e7ec71589da6 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -852,7 +852,7 @@ static int __cmd_report(bool display_info)
struct perf_tool eops = {
.sample = process_sample_event,
.comm = perf_event__process_comm,
- .ordered_samples = true,
+ .ordered_events = true,
};
struct perf_data_file file = {
.path = input_name,
@@ -862,9 +862,11 @@ static int __cmd_report(bool display_info)
session = perf_session__new(&file, false, &eops);
if (!session) {
pr_err("Initializing perf session failed\n");
- return -ENOMEM;
+ return -1;
}
+ symbol__init(&session->header.env);
+
if (!perf_session__has_traces(session, "lock record"))
goto out_delete;
@@ -974,7 +976,6 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
unsigned int i;
int rc = 0;
- symbol__init();
for (i = 0; i < LOCKHASH_SIZE; i++)
INIT_LIST_HEAD(lockhash_table + i);
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index 4a1a6c94a5eb..24db6ffe2957 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem)
&mem->tool);
if (session == NULL)
- return -ENOMEM;
+ return -1;
if (mem->cpu_list) {
ret = perf_session__cpu_bitmap(session, mem->cpu_list,
@@ -133,7 +133,7 @@ static int report_raw_events(struct perf_mem *mem)
goto out_delete;
}
- if (symbol__init() < 0)
+ if (symbol__init(&session->header.env) < 0)
return -1;
printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
@@ -194,7 +194,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
.lost = perf_event__process_lost,
.fork = perf_event__process_fork,
.build_id = perf_event__process_build_id,
- .ordered_samples = true,
+ .ordered_events = true,
},
.input_name = "perf.data",
};
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c63fa2925075..921bb6942503 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,6 +55,7 @@ static struct {
bool show_funcs;
bool mod_events;
bool uprobes;
+ bool quiet;
int nevents;
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
@@ -290,8 +291,11 @@ static void cleanup_params(void)
static void pr_err_with_code(const char *msg, int err)
{
+ char sbuf[STRERR_BUFSIZE];
+
pr_err("%s", msg);
- pr_debug(" Reason: %s (Code: %d)", strerror(-err), err);
+ pr_debug(" Reason: %s (Code: %d)",
+ strerror_r(-err, sbuf, sizeof(sbuf)), err);
pr_err("\n");
}
@@ -309,9 +313,11 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
#endif
NULL
};
- const struct option options[] = {
+ struct option options[] = {
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"),
+ OPT_BOOLEAN('q', "quiet", &params.quiet,
+ "be quiet (do not show any mesages)"),
OPT_BOOLEAN('l', "list", &params.list_events,
"list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
@@ -372,11 +378,21 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_CALLBACK('x', "exec", NULL, "executable|path",
"target executable name or path", opt_set_target),
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
- "Disable symbol demangling"),
+ "Enable symbol demangling"),
+ OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
+ "Enable kernel symbol demangling"),
OPT_END()
};
int ret;
+ set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE);
+ set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE);
+ set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE);
+#ifdef HAVE_DWARF_SUPPORT
+ set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE);
+ set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE);
+#endif
+
argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) {
@@ -391,6 +407,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}
}
+ if (params.quiet) {
+ if (verbose != 0) {
+ pr_err(" Error: -v and -q are exclusive.\n");
+ return -EINVAL;
+ }
+ verbose = -1;
+ }
+
if (params.max_probe_points == 0)
params.max_probe_points = MAX_PROBES;
@@ -404,22 +428,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (params.list_events) {
- if (params.mod_events) {
- pr_err(" Error: Don't use --list with --add/--del.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_lines) {
- pr_err(" Error: Don't use --list with --line.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_vars) {
- pr_err(" Error: Don't use --list with --vars.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_funcs) {
- pr_err(" Error: Don't use --list with --funcs.\n");
- usage_with_options(probe_usage, options);
- }
if (params.uprobes) {
pr_warning(" Error: Don't use --list with --exec.\n");
usage_with_options(probe_usage, options);
@@ -430,19 +438,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
return ret;
}
if (params.show_funcs) {
- if (params.nevents != 0 || params.dellist) {
- pr_err(" Error: Don't use --funcs with"
- " --add/--del.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_lines) {
- pr_err(" Error: Don't use --funcs with --line.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_vars) {
- pr_err(" Error: Don't use --funcs with --vars.\n");
- usage_with_options(probe_usage, options);
- }
if (!params.filter)
params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
NULL);
@@ -457,27 +452,13 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
#ifdef HAVE_DWARF_SUPPORT
if (params.show_lines) {
- if (params.mod_events) {
- pr_err(" Error: Don't use --line with"
- " --add/--del.\n");
- usage_with_options(probe_usage, options);
- }
- if (params.show_vars) {
- pr_err(" Error: Don't use --line with --vars.\n");
- usage_with_options(probe_usage, options);
- }
-
- ret = show_line_range(&params.line_range, params.target);
+ ret = show_line_range(&params.line_range, params.target,
+ params.uprobes);
if (ret < 0)
pr_err_with_code(" Error: Failed to show lines.", ret);
return ret;
}
if (params.show_vars) {
- if (params.mod_events) {
- pr_err(" Error: Don't use --vars with"
- " --add/--del.\n");
- usage_with_options(probe_usage, options);
- }
if (!params.filter)
params.filter = strfilter__new(DEFAULT_VAR_FILTER,
NULL);
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4869050e7194..8648c6d3003d 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -14,6 +14,8 @@
#include "util/parse-options.h"
#include "util/parse-events.h"
+#include "util/callchain.h"
+#include "util/cgroup.h"
#include "util/header.h"
#include "util/event.h"
#include "util/evlist.h"
@@ -65,8 +67,9 @@ static int process_synthesized_event(struct perf_tool *tool,
return record__write(rec, event, event->header.size);
}
-static int record__mmap_read(struct record *rec, struct perf_mmap *md)
+static int record__mmap_read(struct record *rec, int idx)
{
+ struct perf_mmap *md = &rec->evlist->mmap[idx];
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
@@ -102,8 +105,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md)
}
md->prev = old;
- perf_mmap__write_tail(md, old);
-
+ perf_evlist__mmap_consume(rec->evlist, idx);
out:
return rc;
}
@@ -161,7 +163,7 @@ try_again:
if (perf_evlist__apply_filters(evlist)) {
error("failed to set filter with %d (%s)\n", errno,
- strerror(errno));
+ strerror_r(errno, msg, sizeof(msg)));
rc = -1;
goto out;
}
@@ -175,7 +177,8 @@ try_again:
"(current value: %u)\n", opts->mmap_pages);
rc = -errno;
} else {
- pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ pr_err("failed to mmap with %d (%s)\n", errno,
+ strerror_r(errno, msg, sizeof(msg)));
rc = -errno;
}
goto out;
@@ -197,6 +200,17 @@ static int process_buildids(struct record *rec)
if (size == 0)
return 0;
+ /*
+ * During this process, it'll load kernel map and replace the
+ * dso->long_name to a real pathname it found. In this case
+ * we prefer the vmlinux path like
+ * /lib/modules/3.16.4/build/vmlinux
+ *
+ * rather than build-id path (in debug directory).
+ * $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551
+ */
+ symbol_conf.ignore_vmlinux_buildid = true;
+
return __perf_session__process_events(session, start,
size - start,
size, &build_id__mark_dso_hit_ops);
@@ -244,7 +258,7 @@ static int record__mmap_read_all(struct record *rec)
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
if (rec->evlist->mmap[i].base) {
- if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
+ if (record__mmap_read(rec, i) != 0) {
rc = -1;
goto out;
}
@@ -307,7 +321,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
struct record_opts *opts = &rec->opts;
struct perf_data_file *file = &rec->file;
struct perf_session *session;
- bool disabled = false;
+ bool disabled = false, draining = false;
rec->progname = argv[0];
@@ -456,9 +470,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
}
if (hits == rec->samples) {
- if (done)
+ if (done || draining)
break;
- err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
+ err = perf_evlist__poll(rec->evlist, -1);
/*
* Propagate error, only if there's any. Ignore positive
* number of returned events and interrupt error.
@@ -466,6 +480,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
if (err > 0 || (err < 0 && errno == EINTR))
err = 0;
waking++;
+
+ if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0)
+ draining = true;
}
/*
@@ -480,7 +497,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
}
if (forks && workload_exec_errno) {
- char msg[512];
+ char msg[STRERR_BUFSIZE];
const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
pr_err("Workload failed: %s\n", emsg);
err = -1;
@@ -620,154 +637,66 @@ error:
return ret;
}
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
-static int get_stack_size(char *str, unsigned long *_size)
-{
- char *endptr;
- unsigned long size;
- unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
-
- size = strtoul(str, &endptr, 0);
-
- do {
- if (*endptr)
- break;
-
- size = round_up(size, sizeof(u64));
- if (!size || size > max_size)
- break;
-
- *_size = size;
- return 0;
-
- } while (0);
-
- pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
- max_size, str);
- return -1;
-}
-#endif /* HAVE_DWARF_UNWIND_SUPPORT */
-
-int record_parse_callchain(const char *arg, struct record_opts *opts)
-{
- char *tok, *name, *saveptr = NULL;
- char *buf;
- int ret = -1;
-
- /* We need buffer that we know we can write to. */
- buf = malloc(strlen(arg) + 1);
- if (!buf)
- return -ENOMEM;
-
- strcpy(buf, arg);
-
- tok = strtok_r((char *)buf, ",", &saveptr);
- name = tok ? : (char *)buf;
-
- do {
- /* Framepointer style */
- if (!strncmp(name, "fp", sizeof("fp"))) {
- if (!strtok_r(NULL, ",", &saveptr)) {
- opts->call_graph = CALLCHAIN_FP;
- ret = 0;
- } else
- pr_err("callchain: No more arguments "
- "needed for -g fp\n");
- break;
-
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
- /* Dwarf style */
- } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
- const unsigned long default_stack_dump_size = 8192;
-
- ret = 0;
- opts->call_graph = CALLCHAIN_DWARF;
- opts->stack_dump_size = default_stack_dump_size;
-
- tok = strtok_r(NULL, ",", &saveptr);
- if (tok) {
- unsigned long size = 0;
-
- ret = get_stack_size(tok, &size);
- opts->stack_dump_size = size;
- }
-#endif /* HAVE_DWARF_UNWIND_SUPPORT */
- } else {
- pr_err("callchain: Unknown --call-graph option "
- "value: %s\n", arg);
- break;
- }
-
- } while (0);
-
- free(buf);
- return ret;
-}
-
-static void callchain_debug(struct record_opts *opts)
+static void callchain_debug(void)
{
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" };
- pr_debug("callchain: type %s\n", str[opts->call_graph]);
+ pr_debug("callchain: type %s\n", str[callchain_param.record_mode]);
- if (opts->call_graph == CALLCHAIN_DWARF)
+ if (callchain_param.record_mode == CALLCHAIN_DWARF)
pr_debug("callchain: stack dump size %d\n",
- opts->stack_dump_size);
+ callchain_param.dump_size);
}
-int record_parse_callchain_opt(const struct option *opt,
+int record_parse_callchain_opt(const struct option *opt __maybe_unused,
const char *arg,
int unset)
{
- struct record_opts *opts = opt->value;
int ret;
- opts->call_graph_enabled = !unset;
+ callchain_param.enabled = !unset;
/* --no-call-graph */
if (unset) {
- opts->call_graph = CALLCHAIN_NONE;
+ callchain_param.record_mode = CALLCHAIN_NONE;
pr_debug("callchain: disabled\n");
return 0;
}
- ret = record_parse_callchain(arg, opts);
+ ret = parse_callchain_record_opt(arg);
if (!ret)
- callchain_debug(opts);
+ callchain_debug();
return ret;
}
-int record_callchain_opt(const struct option *opt,
+int record_callchain_opt(const struct option *opt __maybe_unused,
const char *arg __maybe_unused,
int unset __maybe_unused)
{
- struct record_opts *opts = opt->value;
-
- opts->call_graph_enabled = !unset;
+ callchain_param.enabled = true;
- if (opts->call_graph == CALLCHAIN_NONE)
- opts->call_graph = CALLCHAIN_FP;
+ if (callchain_param.record_mode == CALLCHAIN_NONE)
+ callchain_param.record_mode = CALLCHAIN_FP;
- callchain_debug(opts);
+ callchain_debug();
return 0;
}
static int perf_record_config(const char *var, const char *value, void *cb)
{
- struct record *rec = cb;
-
if (!strcmp(var, "record.call-graph"))
- return record_parse_callchain(value, &rec->opts);
+ var = "call-graph.record-mode"; /* fall-through */
return perf_default_config(var, value, cb);
}
-static const char * const record_usage[] = {
+static const char * const __record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
NULL
};
+const char * const *record_usage = __record_usage;
/*
* XXX Ideally would be local to cmd_record() and passed to a record__new
@@ -781,6 +710,7 @@ static const char * const record_usage[] = {
*/
static struct record record = {
.opts = {
+ .sample_time = true,
.mmap_pages = UINT_MAX,
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
@@ -807,7 +737,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp";
* perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
* using pipes, etc.
*/
-const struct option record_options[] = {
+struct option __record_options[] = {
OPT_CALLBACK('e', "event", &record.evlist, "event",
"event selector. use 'perf list' to list available events",
parse_events_option),
@@ -881,9 +811,13 @@ const struct option record_options[] = {
"sample transaction flags (special events only)"),
OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread,
"use per-thread mmaps"),
+ OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs,
+ "Sample machine registers on interrupt"),
OPT_END()
};
+struct option *record_options = __record_options;
+
int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
{
int err = -ENOMEM;
@@ -907,7 +841,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
usage_with_options(record_usage, record_options);
}
- symbol__init();
+ symbol__init(NULL);
if (symbol_conf.kptr_restrict)
pr_warning(
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 21d830bafff3..39367609c707 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -58,17 +58,19 @@ struct report {
const char *symbol_filter_str;
float min_percent;
u64 nr_entries;
+ u64 queue_size;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
static int report__config(const char *var, const char *value, void *cb)
{
+ struct report *rep = cb;
+
if (!strcmp(var, "report.group")) {
symbol_conf.event_group = perf_config_bool(var, value);
return 0;
}
if (!strcmp(var, "report.percent-limit")) {
- struct report *rep = cb;
rep->min_percent = strtof(value, NULL);
return 0;
}
@@ -76,6 +78,10 @@ static int report__config(const char *var, const char *value, void *cb)
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "report.queue-size")) {
+ rep->queue_size = perf_config_u64(var, value);
+ return 0;
+ }
return perf_default_config(var, value, cb);
}
@@ -220,8 +226,9 @@ static int report__setup_sample_type(struct report *rep)
return -EINVAL;
}
if (symbol_conf.use_callchain) {
- ui__error("Selected -g but no callchain data. Did "
- "you call 'perf record' without -g?\n");
+ ui__error("Selected -g or --branch-history but no "
+ "callchain data. Did\n"
+ "you call 'perf record' without -g?\n");
return -1;
}
} else if (!rep->dont_use_callchains &&
@@ -251,6 +258,13 @@ static int report__setup_sample_type(struct report *rep)
}
}
+ if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
+ if ((sample_type & PERF_SAMPLE_REGS_USER) &&
+ (sample_type & PERF_SAMPLE_STACK_USER))
+ callchain_param.record_mode = CALLCHAIN_DWARF;
+ else
+ callchain_param.record_mode = CALLCHAIN_FP;
+ }
return 0;
}
@@ -282,12 +296,14 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
evname = buf;
for_each_group_member(pos, evsel) {
+ const struct hists *pos_hists = evsel__hists(pos);
+
if (symbol_conf.filter_relative) {
- nr_samples += pos->hists.stats.nr_non_filtered_samples;
- nr_events += pos->hists.stats.total_non_filtered_period;
+ nr_samples += pos_hists->stats.nr_non_filtered_samples;
+ nr_events += pos_hists->stats.total_non_filtered_period;
} else {
- nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
- nr_events += pos->hists.stats.total_period;
+ nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
+ nr_events += pos_hists->stats.total_period;
}
}
}
@@ -312,7 +328,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
struct perf_evsel *pos;
evlist__for_each(evlist, pos) {
- struct hists *hists = &pos->hists;
+ struct hists *hists = evsel__hists(pos);
const char *evname = perf_evsel__name(pos);
if (symbol_conf.event_group &&
@@ -421,7 +437,7 @@ static void report__collapse_hists(struct report *rep)
ui_progress__init(&prog, rep->nr_entries, "Merging related events...");
evlist__for_each(rep->session->evlist, pos) {
- struct hists *hists = &pos->hists;
+ struct hists *hists = evsel__hists(pos);
if (pos->idx == 0)
hists->symbol_filter_str = rep->symbol_filter_str;
@@ -431,7 +447,7 @@ static void report__collapse_hists(struct report *rep)
/* Non-group events are considered as leader */
if (symbol_conf.event_group &&
!perf_evsel__is_group_leader(pos)) {
- struct hists *leader_hists = &pos->leader->hists;
+ struct hists *leader_hists = evsel__hists(pos->leader);
hists__match(leader_hists, hists);
hists__link(leader_hists, hists);
@@ -479,6 +495,7 @@ static int __cmd_report(struct report *rep)
if (dump_trace) {
perf_session__fprintf_nr_events(session, stdout);
+ perf_evlist__fprintf_nr_events(session->evlist, stdout);
return 0;
}
}
@@ -494,7 +511,7 @@ static int __cmd_report(struct report *rep)
}
evlist__for_each(session->evlist, pos)
- hists__output_resort(&pos->hists);
+ hists__output_resort(evsel__hists(pos));
return report__browse_hists(rep);
}
@@ -559,7 +576,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
struct stat st;
bool has_br_stack = false;
int branch_mode = -1;
- int ret = -1;
+ bool branch_call_mode = false;
char callchain_default_opt[] = "fractal,0.5,callee";
const char * const report_usage[] = {
"perf report [<options>]",
@@ -578,7 +595,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.attr = perf_event__process_attr,
.tracing_data = perf_event__process_tracing_data,
.build_id = perf_event__process_build_id,
- .ordered_samples = true,
+ .ordered_events = true,
.ordering_requires_timestamps = true,
},
.max_stack = PERF_MAX_STACK_DEPTH,
@@ -622,8 +639,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
"Only display entries with parent-match"),
- OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
- "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
+ OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order[,branch]",
+ "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address), add branches. "
"Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
"Accumulate callchains of children and show total overhead as well"),
@@ -669,11 +686,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
"Show event group information together"),
OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
- "use branch records for histogram filling", parse_branch_mode),
+ "use branch records for per branch histogram filling",
+ parse_branch_mode),
+ OPT_BOOLEAN(0, "branch-history", &branch_call_mode,
+ "add last branch records to call history"),
OPT_STRING(0, "objdump", &objdump_path, "path",
"objdump binary to use for disassembly and annotations"),
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
"Disable symbol demangling"),
+ OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
+ "Enable kernel symbol demangling"),
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
OPT_CALLBACK(0, "percent-limit", &report, "percent",
"Don't show entries under that percent", parse_percent_limit),
@@ -684,6 +706,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
struct perf_data_file file = {
.mode = PERF_DATA_MODE_READ,
};
+ int ret = hists__init();
+
+ if (ret < 0)
+ return ret;
perf_config(report__config, &report);
@@ -712,17 +738,36 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
repeat:
session = perf_session__new(&file, false, &report.tool);
if (session == NULL)
- return -ENOMEM;
+ return -1;
+
+ if (report.queue_size) {
+ ordered_events__set_alloc_size(&session->ordered_events,
+ report.queue_size);
+ }
report.session = session;
has_br_stack = perf_header__has_feat(&session->header,
HEADER_BRANCH_STACK);
- if (branch_mode == -1 && has_br_stack) {
+ /*
+ * Branch mode is a tristate:
+ * -1 means default, so decide based on the file having branch data.
+ * 0/1 means the user chose a mode.
+ */
+ if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) &&
+ branch_call_mode == -1) {
sort__mode = SORT_MODE__BRANCH;
symbol_conf.cumulate_callchain = false;
}
+ if (branch_call_mode) {
+ callchain_param.key = CCKEY_ADDRESS;
+ callchain_param.branch_callstack = 1;
+ symbol_conf.use_callchain = true;
+ callchain_register_param(&callchain_param);
+ if (sort_order == NULL)
+ sort_order = "srcline,symbol,dso";
+ }
if (report.mem_mode) {
if (sort__mode == SORT_MODE__BRANCH) {
@@ -787,7 +832,7 @@ repeat:
}
}
- if (symbol__init() < 0)
+ if (symbol__init(&session->header.env) < 0)
goto error;
if (argc) {
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index f83c08c0dd87..891c3930080e 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -428,6 +428,7 @@ static u64 get_cpu_usage_nsec_parent(void)
static int self_open_counters(void)
{
struct perf_event_attr attr;
+ char sbuf[STRERR_BUFSIZE];
int fd;
memset(&attr, 0, sizeof(attr));
@@ -440,7 +441,8 @@ static int self_open_counters(void)
if (fd < 0)
pr_err("Error: sys_perf_event_open() syscall returned "
- "with %d (%s)\n", fd, strerror(errno));
+ "with %d (%s)\n", fd,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return fd;
}
@@ -1429,9 +1431,6 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
{
int err = 0;
- evsel->hists.stats.total_period += sample->period;
- hists__inc_nr_samples(&evsel->hists, true);
-
if (evsel->handler != NULL) {
tracepoint_handler f = evsel->handler;
err = f(tool, evsel, sample, machine);
@@ -1462,6 +1461,8 @@ static int perf_sched__read_events(struct perf_sched *sched,
return -1;
}
+ symbol__init(&session->header.env);
+
if (perf_session__set_tracepoints_handlers(session, handlers))
goto out_delete;
@@ -1662,7 +1663,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
.comm = perf_event__process_comm,
.lost = perf_event__process_lost,
.fork = perf_sched__process_fork_event,
- .ordered_samples = true,
+ .ordered_events = true,
},
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
.sort_list = LIST_HEAD_INIT(sched.sort_list),
@@ -1747,7 +1748,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
if (!strcmp(argv[0], "script"))
return cmd_script(argc, argv, prefix);
- symbol__init();
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strncmp(argv[0], "lat", 3)) {
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f57035b89c15..ce304dfd962a 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -23,7 +23,6 @@ static char const *generate_script_lang;
static bool debug_mode;
static u64 last_timestamp;
static u64 nr_unordered;
-extern const struct option record_options[];
static bool no_callchain;
static bool latency_format;
static bool system_wide;
@@ -44,6 +43,7 @@ enum perf_output_field {
PERF_OUTPUT_ADDR = 1U << 10,
PERF_OUTPUT_SYMOFFSET = 1U << 11,
PERF_OUTPUT_SRCLINE = 1U << 12,
+ PERF_OUTPUT_PERIOD = 1U << 13,
};
struct output_option {
@@ -63,6 +63,7 @@ struct output_option {
{.str = "addr", .field = PERF_OUTPUT_ADDR},
{.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET},
{.str = "srcline", .field = PERF_OUTPUT_SRCLINE},
+ {.str = "period", .field = PERF_OUTPUT_PERIOD},
};
/* default set to maintain compatibility with current format */
@@ -80,7 +81,8 @@ static struct {
.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
- PERF_OUTPUT_SYM | PERF_OUTPUT_DSO,
+ PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
+ PERF_OUTPUT_PERIOD,
.invalid_fields = PERF_OUTPUT_TRACE,
},
@@ -91,7 +93,8 @@ static struct {
.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
- PERF_OUTPUT_SYM | PERF_OUTPUT_DSO,
+ PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
+ PERF_OUTPUT_PERIOD,
.invalid_fields = PERF_OUTPUT_TRACE,
},
@@ -110,7 +113,8 @@ static struct {
.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
- PERF_OUTPUT_SYM | PERF_OUTPUT_DSO,
+ PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
+ PERF_OUTPUT_PERIOD,
.invalid_fields = PERF_OUTPUT_TRACE,
},
@@ -184,10 +188,6 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
PERF_OUTPUT_IP))
return -EINVAL;
-
- if (!no_callchain &&
- !(attr->sample_type & PERF_SAMPLE_CALLCHAIN))
- symbol_conf.use_callchain = false;
}
if (PRINT_FIELD(ADDR) &&
@@ -233,6 +233,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
PERF_OUTPUT_CPU))
return -EINVAL;
+ if (PRINT_FIELD(PERIOD) &&
+ perf_evsel__check_stype(evsel, PERF_SAMPLE_PERIOD, "PERIOD",
+ PERF_OUTPUT_PERIOD))
+ return -EINVAL;
+
return 0;
}
@@ -290,6 +295,19 @@ static int perf_session__check_output_opt(struct perf_session *session)
set_print_ip_opts(&evsel->attr);
}
+ if (!no_callchain) {
+ bool use_callchain = false;
+
+ evlist__for_each(session->evlist, evsel) {
+ if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
+ use_callchain = true;
+ break;
+ }
+ }
+ if (!use_callchain)
+ symbol_conf.use_callchain = false;
+ }
+
/*
* set default for tracepoints to print symbols only
* if callchains are present
@@ -360,7 +378,6 @@ static void print_sample_start(struct perf_sample *sample,
static void print_sample_addr(union perf_event *event,
struct perf_sample *sample,
- struct machine *machine,
struct thread *thread,
struct perf_event_attr *attr)
{
@@ -371,7 +388,7 @@ static void print_sample_addr(union perf_event *event,
if (!sample_addr_correlates_sym(attr))
return;
- perf_event__preprocess_sample_addr(event, sample, machine, thread, &al);
+ perf_event__preprocess_sample_addr(event, sample, thread, &al);
if (PRINT_FIELD(SYM)) {
printf(" ");
@@ -419,7 +436,7 @@ static void print_sample_bts(union perf_event *event,
((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[attr->type].user_set)) {
printf(" => ");
- print_sample_addr(event, sample, al->machine, thread, attr);
+ print_sample_addr(event, sample, thread, attr);
}
if (print_srcline_last)
@@ -439,6 +456,9 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
print_sample_start(sample, thread, evsel);
+ if (PRINT_FIELD(PERIOD))
+ printf("%10" PRIu64 " ", sample->period);
+
if (PRINT_FIELD(EVNAME)) {
const char *evname = perf_evsel__name(evsel);
printf("%s: ", evname ? evname : "[unknown]");
@@ -453,7 +473,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
event_format__print(evsel->tp_format, sample->cpu,
sample->raw_data, sample->raw_size);
if (PRINT_FIELD(ADDR))
- print_sample_addr(event, sample, al->machine, thread, attr);
+ print_sample_addr(event, sample, thread, attr);
if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain)
@@ -476,6 +496,11 @@ static int default_start_script(const char *script __maybe_unused,
return 0;
}
+static int default_flush_script(void)
+{
+ return 0;
+}
+
static int default_stop_script(void)
{
return 0;
@@ -489,6 +514,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused,
static struct scripting_ops default_scripting_ops = {
.start_script = default_start_script,
+ .flush_script = default_flush_script,
.stop_script = default_stop_script,
.process_event = process_event,
.generate_script = default_generate_script,
@@ -504,6 +530,11 @@ static void setup_scripting(void)
scripting_ops = &default_scripting_ops;
}
+static int flush_scripting(void)
+{
+ return scripting_ops->flush_script();
+}
+
static int cleanup_scripting(void)
{
pr_debug("\nperf script stopped\n");
@@ -552,7 +583,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
scripting_ops->process_event(event, sample, evsel, thread, &al);
- evsel->hists.stats.total_period += sample->period;
return 0;
}
@@ -1471,12 +1501,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
bool show_full_info = false;
bool header = false;
bool header_only = false;
+ bool script_started = false;
char *rec_script_path = NULL;
char *rep_script_path = NULL;
struct perf_session *session;
char *script_path = NULL;
const char **__argv;
- int i, j, err;
+ int i, j, err = 0;
struct perf_script script = {
.tool = {
.sample = process_sample_event,
@@ -1488,7 +1519,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
.attr = process_attr,
.tracing_data = perf_event__process_tracing_data,
.build_id = perf_event__process_build_id,
- .ordered_samples = true,
+ .ordered_events = true,
.ordering_requires_timestamps = true,
},
};
@@ -1523,7 +1554,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
"comma separated output fields prepend with 'type:'. "
"Valid types: hw,sw,trace,raw. "
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
- "addr,symoff", parse_output_fields),
+ "addr,symoff,period", parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
@@ -1718,26 +1749,28 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
exit(-1);
}
- if (symbol__init() < 0)
- return -1;
if (!script_name)
setup_pager();
session = perf_session__new(&file, false, &script.tool);
if (session == NULL)
- return -ENOMEM;
+ return -1;
if (header || header_only) {
perf_session__fprintf_info(session, stdout, show_full_info);
if (header_only)
- return 0;
+ goto out_delete;
}
+ if (symbol__init(&session->header.env) < 0)
+ goto out_delete;
+
script.session = session;
if (cpu_list) {
- if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))
- return -1;
+ err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
+ if (err < 0)
+ goto out_delete;
}
if (!no_callchain)
@@ -1752,53 +1785,62 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
if (output_set_by_user()) {
fprintf(stderr,
"custom fields not supported for generated scripts");
- return -1;
+ err = -EINVAL;
+ goto out_delete;
}
input = open(file.path, O_RDONLY); /* input_name */
if (input < 0) {
+ err = -errno;
perror("failed to open file");
- return -1;
+ goto out_delete;
}
err = fstat(input, &perf_stat);
if (err < 0) {
perror("failed to stat file");
- return -1;
+ goto out_delete;
}
if (!perf_stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
- return 0;
+ goto out_delete;
}
scripting_ops = script_spec__lookup(generate_script_lang);
if (!scripting_ops) {
fprintf(stderr, "invalid language specifier");
- return -1;
+ err = -ENOENT;
+ goto out_delete;
}
err = scripting_ops->generate_script(session->tevent.pevent,
"perf-script");
- goto out;
+ goto out_delete;
}
if (script_name) {
err = scripting_ops->start_script(script_name, argc, argv);
if (err)
- goto out;
+ goto out_delete;
pr_debug("perf script started with script %s\n\n", script_name);
+ script_started = true;
}
err = perf_session__check_output_opt(session);
if (err < 0)
- goto out;
+ goto out_delete;
err = __cmd_script(&script);
+ flush_scripting();
+
+out_delete:
perf_session__delete(session);
- cleanup_scripting();
+
+ if (script_started)
+ cleanup_scripting();
out:
return err;
}
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 3e80aa10cfd8..891086376381 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -43,6 +43,7 @@
#include "perf.h"
#include "builtin.h"
+#include "util/cgroup.h"
#include "util/util.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
@@ -387,20 +388,102 @@ static void update_shadow_stats(struct perf_evsel *counter, u64 *count)
update_stats(&runtime_itlb_cache_stats[0], count[0]);
}
+static void zero_per_pkg(struct perf_evsel *counter)
+{
+ if (counter->per_pkg_mask)
+ memset(counter->per_pkg_mask, 0, MAX_NR_CPUS);
+}
+
+static int check_per_pkg(struct perf_evsel *counter, int cpu, bool *skip)
+{
+ unsigned long *mask = counter->per_pkg_mask;
+ struct cpu_map *cpus = perf_evsel__cpus(counter);
+ int s;
+
+ *skip = false;
+
+ if (!counter->per_pkg)
+ return 0;
+
+ if (cpu_map__empty(cpus))
+ return 0;
+
+ if (!mask) {
+ mask = zalloc(MAX_NR_CPUS);
+ if (!mask)
+ return -ENOMEM;
+
+ counter->per_pkg_mask = mask;
+ }
+
+ s = cpu_map__get_socket(cpus, cpu);
+ if (s < 0)
+ return -1;
+
+ *skip = test_and_set_bit(s, mask) == 1;
+ return 0;
+}
+
+static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused,
+ struct perf_counts_values *count)
+{
+ struct perf_counts_values *aggr = &evsel->counts->aggr;
+ static struct perf_counts_values zero;
+ bool skip = false;
+
+ if (check_per_pkg(evsel, cpu, &skip)) {
+ pr_err("failed to read per-pkg counter\n");
+ return -1;
+ }
+
+ if (skip)
+ count = &zero;
+
+ switch (aggr_mode) {
+ case AGGR_CORE:
+ case AGGR_SOCKET:
+ case AGGR_NONE:
+ if (!evsel->snapshot)
+ perf_evsel__compute_deltas(evsel, cpu, count);
+ perf_counts_values__scale(count, scale, NULL);
+ evsel->counts->cpu[cpu] = *count;
+ update_shadow_stats(evsel, count->values);
+ break;
+ case AGGR_GLOBAL:
+ aggr->val += count->val;
+ if (scale) {
+ aggr->ena += count->ena;
+ aggr->run += count->run;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int read_counter(struct perf_evsel *counter);
+
/*
* Read out the results of a single counter:
* aggregate counts across CPUs in system-wide mode
*/
static int read_counter_aggr(struct perf_evsel *counter)
{
+ struct perf_counts_values *aggr = &counter->counts->aggr;
struct perf_stat *ps = counter->priv;
u64 *count = counter->counts->aggr.values;
int i;
- if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter),
- thread_map__nr(evsel_list->threads), scale) < 0)
+ aggr->val = aggr->ena = aggr->run = 0;
+
+ if (read_counter(counter))
return -1;
+ if (!counter->snapshot)
+ perf_evsel__compute_deltas(counter, -1, aggr);
+ perf_counts_values__scale(aggr, scale, &counter->counts->scaled);
+
for (i = 0; i < 3; i++)
update_stats(&ps->res_stats[i], count[i]);
@@ -423,16 +506,21 @@ static int read_counter_aggr(struct perf_evsel *counter)
*/
static int read_counter(struct perf_evsel *counter)
{
- u64 *count;
- int cpu;
+ int nthreads = thread_map__nr(evsel_list->threads);
+ int ncpus = perf_evsel__nr_cpus(counter);
+ int cpu, thread;
- for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
- if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0)
- return -1;
+ if (counter->system_wide)
+ nthreads = 1;
- count = counter->counts->cpu[cpu].values;
+ if (counter->per_pkg)
+ zero_per_pkg(counter);
- update_shadow_stats(counter, count);
+ for (thread = 0; thread < nthreads; thread++) {
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ if (perf_evsel__read_cb(counter, cpu, thread, read_cb))
+ return -1;
+ }
}
return 0;
@@ -593,7 +681,7 @@ static int __run_perf_stat(int argc, const char **argv)
if (perf_evlist__apply_filters(evsel_list)) {
error("failed to set filter with %d (%s)\n", errno,
- strerror(errno));
+ strerror_r(errno, msg, sizeof(msg)));
return -1;
}
@@ -732,7 +820,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
}
}
-static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
+static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
{
double msecs = avg / 1e6;
const char *fmt_v, *fmt_n;
@@ -741,7 +829,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
fmt_v = csv_output ? "%.6f%s" : "%18.6f%s";
fmt_n = csv_output ? "%s" : "%-25s";
- aggr_printout(evsel, cpu, nr);
+ aggr_printout(evsel, id, nr);
scnprintf(name, sizeof(name), "%s%s",
perf_evsel__name(evsel), csv_output ? "" : " (msec)");
@@ -947,11 +1035,12 @@ static void print_ll_cache_misses(int cpu,
fprintf(output, " of all LL-cache hits ");
}
-static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
+static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
{
double total, ratio = 0.0, total2;
double sc = evsel->scale;
const char *fmt;
+ int cpu = cpu_map__id_to_cpu(id);
if (csv_output) {
fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s";
@@ -962,7 +1051,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s";
}
- aggr_printout(evsel, cpu, nr);
+ aggr_printout(evsel, id, nr);
if (aggr_mode == AGGR_GLOBAL)
cpu = 0;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 2f1a5220c090..f3bb1a4bf060 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -528,7 +528,7 @@ static const char *cat_backtrace(union perf_event *event,
}
tal.filtered = 0;
- thread__find_addr_location(al.thread, machine, cpumode,
+ thread__find_addr_location(al.thread, cpumode,
MAP__FUNCTION, ip, &tal);
if (tal.sym)
@@ -1605,7 +1605,9 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
int ret = -EINVAL;
if (session == NULL)
- return -ENOMEM;
+ return -1;
+
+ symbol__init(&session->header.env);
(void)perf_header__process_sections(&session->header,
perf_data_file__fd(session->file),
@@ -1920,7 +1922,7 @@ int cmd_timechart(int argc, const char **argv,
.fork = process_fork_event,
.exit = process_exit_event,
.sample = process_sample_event,
- .ordered_samples = true,
+ .ordered_events = true,
},
.proc_num = 15,
.min_time = 1000000,
@@ -1961,7 +1963,7 @@ int cmd_timechart(int argc, const char **argv,
NULL
};
- const struct option record_options[] = {
+ const struct option timechart_record_options[] = {
OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
"output processes data only"),
@@ -1970,7 +1972,7 @@ int cmd_timechart(int argc, const char **argv,
OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
OPT_END()
};
- const char * const record_usage[] = {
+ const char * const timechart_record_usage[] = {
"perf timechart record [<options>]",
NULL
};
@@ -1982,10 +1984,9 @@ int cmd_timechart(int argc, const char **argv,
return -1;
}
- symbol__init();
-
if (argc && !strncmp(argv[0], "rec", 3)) {
- argc = parse_options(argc, argv, record_options, record_usage,
+ argc = parse_options(argc, argv, timechart_record_options,
+ timechart_record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (tchart.power_only && tchart.tasks_only) {
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 377971dc89a3..0aa7747ff139 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -59,7 +59,7 @@
#include <sys/syscall.h>
#include <sys/ioctl.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/uio.h>
@@ -251,6 +251,7 @@ static void perf_top__print_sym_table(struct perf_top *top)
char bf[160];
int printed = 0;
const int win_width = top->winsize.ws_col - 1;
+ struct hists *hists = evsel__hists(top->sym_evsel);
puts(CONSOLE_CLEAR);
@@ -261,13 +262,13 @@ static void perf_top__print_sym_table(struct perf_top *top)
printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
- if (top->sym_evsel->hists.stats.nr_lost_warned !=
- top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) {
- top->sym_evsel->hists.stats.nr_lost_warned =
- top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST];
+ if (hists->stats.nr_lost_warned !=
+ hists->stats.nr_events[PERF_RECORD_LOST]) {
+ hists->stats.nr_lost_warned =
+ hists->stats.nr_events[PERF_RECORD_LOST];
color_fprintf(stdout, PERF_COLOR_RED,
"WARNING: LOST %d chunks, Check IO/CPU overload",
- top->sym_evsel->hists.stats.nr_lost_warned);
+ hists->stats.nr_lost_warned);
++printed;
}
@@ -276,16 +277,19 @@ static void perf_top__print_sym_table(struct perf_top *top)
return;
}
- hists__collapse_resort(&top->sym_evsel->hists, NULL);
- hists__output_resort(&top->sym_evsel->hists);
- hists__decay_entries(&top->sym_evsel->hists,
- top->hide_user_symbols,
- top->hide_kernel_symbols);
- hists__output_recalc_col_len(&top->sym_evsel->hists,
- top->print_entries - printed);
+ if (top->zero) {
+ hists__delete_entries(hists);
+ } else {
+ hists__decay_entries(hists, top->hide_user_symbols,
+ top->hide_kernel_symbols);
+ }
+
+ hists__collapse_resort(hists, NULL);
+ hists__output_resort(hists);
+
+ hists__output_recalc_col_len(hists, top->print_entries - printed);
putchar('\n');
- hists__fprintf(&top->sym_evsel->hists, false,
- top->print_entries - printed, win_width,
+ hists__fprintf(hists, false, top->print_entries - printed, win_width,
top->min_percent, stdout);
}
@@ -328,6 +332,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
{
char *buf = malloc(0), *p;
struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL;
+ struct hists *hists = evsel__hists(top->sym_evsel);
struct rb_node *next;
size_t dummy = 0;
@@ -345,7 +350,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
if (p)
*p = 0;
- next = rb_first(&top->sym_evsel->hists.entries);
+ next = rb_first(&hists->entries);
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
@@ -427,18 +432,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
if (!perf_top__key_mapped(top, c)) {
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
- struct termios tc, save;
+ struct termios save;
perf_top__print_mapped_keys(top);
fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
fflush(stdout);
- tcgetattr(0, &save);
- tc = save;
- tc.c_lflag &= ~(ICANON | ECHO);
- tc.c_cc[VMIN] = 0;
- tc.c_cc[VTIME] = 0;
- tcsetattr(0, TCSANOW, &tc);
+ set_term_quiet_input(&save);
poll(&stdin_poll, 1, -1);
c = getc(stdin);
@@ -537,16 +537,24 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
static void perf_top__sort_new_samples(void *arg)
{
struct perf_top *t = arg;
+ struct hists *hists;
+
perf_top__reset_sample_counters(t);
if (t->evlist->selected != NULL)
t->sym_evsel = t->evlist->selected;
- hists__collapse_resort(&t->sym_evsel->hists, NULL);
- hists__output_resort(&t->sym_evsel->hists);
- hists__decay_entries(&t->sym_evsel->hists,
- t->hide_user_symbols,
- t->hide_kernel_symbols);
+ hists = evsel__hists(t->sym_evsel);
+
+ if (t->zero) {
+ hists__delete_entries(hists);
+ } else {
+ hists__decay_entries(hists, t->hide_user_symbols,
+ t->hide_kernel_symbols);
+ }
+
+ hists__collapse_resort(hists, NULL);
+ hists__output_resort(hists);
}
static void *display_thread_tui(void *arg)
@@ -567,8 +575,10 @@ static void *display_thread_tui(void *arg)
* Zooming in/out UIDs. For now juse use whatever the user passed
* via --uid.
*/
- evlist__for_each(top->evlist, pos)
- pos->hists.uid_filter_str = top->record_opts.target.uid_str;
+ evlist__for_each(top->evlist, pos) {
+ struct hists *hists = evsel__hists(pos);
+ hists->uid_filter_str = top->record_opts.target.uid_str;
+ }
perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent,
&top->session->header.env);
@@ -577,23 +587,32 @@ static void *display_thread_tui(void *arg)
return NULL;
}
+static void display_sig(int sig __maybe_unused)
+{
+ done = 1;
+}
+
+static void display_setup_sig(void)
+{
+ signal(SIGSEGV, display_sig);
+ signal(SIGFPE, display_sig);
+ signal(SIGINT, display_sig);
+ signal(SIGQUIT, display_sig);
+ signal(SIGTERM, display_sig);
+}
+
static void *display_thread(void *arg)
{
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
- struct termios tc, save;
+ struct termios save;
struct perf_top *top = arg;
int delay_msecs, c;
- tcgetattr(0, &save);
- tc = save;
- tc.c_lflag &= ~(ICANON | ECHO);
- tc.c_cc[VMIN] = 0;
- tc.c_cc[VTIME] = 0;
-
+ display_setup_sig();
pthread__unblock_sigwinch();
repeat:
delay_msecs = top->delay_secs * 1000;
- tcsetattr(0, TCSANOW, &tc);
+ set_term_quiet_input(&save);
/* trash return*/
getc(stdin);
@@ -620,13 +639,16 @@ repeat:
}
}
+ tcsetattr(0, TCSAFLUSH, &save);
return NULL;
}
-static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
+static int symbol_filter(struct map *map, struct symbol *sym)
{
const char *name = sym->name;
+ if (!map->dso->kernel)
+ return 0;
/*
* ppc64 uses function descriptors and appends a '.' to the
* start of every instruction address. Remove it.
@@ -750,6 +772,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
}
if (al.sym == NULL || !al.sym->ignore) {
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry_iter iter = {
.add_entry_cb = hist_iter__top_callback,
};
@@ -759,14 +782,14 @@ static void perf_event__process_sample(struct perf_tool *tool,
else
iter.ops = &hist_iter_normal;
- pthread_mutex_lock(&evsel->hists.lock);
+ pthread_mutex_lock(&hists->lock);
err = hist_entry_iter__add(&iter, &al, evsel, sample,
top->max_stack, top);
if (err < 0)
pr_err("Problem incrementing symbol period, skipping event\n");
- pthread_mutex_unlock(&evsel->hists.lock);
+ pthread_mutex_unlock(&hists->lock);
}
return;
@@ -831,7 +854,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
perf_event__process_sample(&top->tool, event, evsel,
&sample, machine);
} else if (event->header.type < PERF_RECORD_MAX) {
- hists__inc_nr_events(&evsel->hists, event->header.type);
+ hists__inc_nr_events(evsel__hists(evsel), event->header.type);
machine__process_event(machine, event, &sample);
} else
++session->stats.nr_unknown_events;
@@ -876,7 +899,7 @@ try_again:
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
ui__error("Failed to mmap with %d (%s)\n",
- errno, strerror(errno));
+ errno, strerror_r(errno, msg, sizeof(msg)));
goto out_err;
}
@@ -911,7 +934,7 @@ static int __cmd_top(struct perf_top *top)
top->session = perf_session__new(NULL, false, NULL);
if (top->session == NULL)
- return -ENOMEM;
+ return -1;
machines__set_symbol_filter(&top->session->machines, symbol_filter);
@@ -946,7 +969,7 @@ static int __cmd_top(struct perf_top *top)
perf_evlist__enable(top->evlist);
/* Wait for a minimal set of events before starting the snapshot */
- poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
+ perf_evlist__poll(top->evlist, 100);
perf_top__mmap_read(top);
@@ -963,7 +986,7 @@ static int __cmd_top(struct perf_top *top)
param.sched_priority = top->realtime_prio;
if (sched_setscheduler(0, SCHED_FIFO, &param)) {
ui__error("Could not set realtime priority.\n");
- goto out_delete;
+ goto out_join;
}
}
@@ -973,10 +996,12 @@ static int __cmd_top(struct perf_top *top)
perf_top__mmap_read(top);
if (hits == top->samples)
- ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
+ ret = perf_evlist__poll(top->evlist, 100);
}
ret = 0;
+out_join:
+ pthread_join(thread, NULL);
out_delete:
perf_session__delete(top->session);
top->session = NULL;
@@ -1000,10 +1025,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
static int perf_top_config(const char *var, const char *value, void *cb)
{
- struct perf_top *top = cb;
-
if (!strcmp(var, "top.call-graph"))
- return record_parse_callchain(value, &top->record_opts);
+ var = "call-graph.record-mode"; /* fall-through */
if (!strcmp(var, "top.children")) {
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
return 0;
@@ -1024,7 +1047,6 @@ parse_percent_limit(const struct option *opt, const char *arg,
int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
{
- int status = -1;
char errbuf[BUFSIZ];
struct perf_top top = {
.count_filter = 5,
@@ -1122,6 +1144,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
"Interleave source code with assembly code (default)"),
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
"Display raw encoding of assembly instructions (default)"),
+ OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
+ "Enable kernel symbol demangling"),
OPT_STRING(0, "objdump", &objdump_path, "path",
"objdump binary to use for disassembly and annotations"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
@@ -1131,12 +1155,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
"Don't show entries under that percent", parse_percent_limit),
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
"How to display percentage of filtered entries", parse_filter_percentage),
+ OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
+ "width[,width...]",
+ "don't try to adjust column width, use these fixed values"),
OPT_END()
};
const char * const top_usage[] = {
"perf top [<options>]",
NULL
};
+ int status = hists__init();
+
+ if (status < 0)
+ return status;
top.evlist = perf_evlist__new();
if (top.evlist == NULL)
@@ -1217,7 +1248,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
symbol_conf.priv_size = sizeof(struct annotation);
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
- if (symbol__init() < 0)
+ if (symbol__init(NULL) < 0)
return -1;
sort__setup_elide(stdout);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index a6c375224f46..83a4835c8118 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -402,6 +402,31 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,
#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags
+static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size,
+ struct syscall_arg *arg)
+{
+ int printed = 0, flags = arg->val;
+
+#define P_MREMAP_FLAG(n) \
+ if (flags & MREMAP_##n) { \
+ printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
+ flags &= ~MREMAP_##n; \
+ }
+
+ P_MREMAP_FLAG(MAYMOVE);
+#ifdef MREMAP_FIXED
+ P_MREMAP_FLAG(FIXED);
+#endif
+#undef P_MREMAP_FLAG
+
+ if (flags)
+ printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
+
+ return printed;
+}
+
+#define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags
+
static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size,
struct syscall_arg *arg)
{
@@ -1004,6 +1029,7 @@ static struct syscall_fmt {
[2] = SCA_MMAP_PROT, /* prot */ }, },
{ .name = "mremap", .hexret = true,
.arg_scnprintf = { [0] = SCA_HEX, /* addr */
+ [3] = SCA_MREMAP_FLAGS, /* flags */
[4] = SCA_HEX, /* new_addr */ }, },
{ .name = "munlock", .errmsg = true,
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
@@ -1163,13 +1189,13 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
if (thread == NULL)
goto fail;
- if (thread->priv == NULL)
- thread->priv = thread_trace__new();
+ if (thread__priv(thread) == NULL)
+ thread__set_priv(thread, thread_trace__new());
- if (thread->priv == NULL)
+ if (thread__priv(thread) == NULL)
goto fail;
- ttrace = thread->priv;
+ ttrace = thread__priv(thread);
++ttrace->nr_events;
return ttrace;
@@ -1222,7 +1248,7 @@ struct trace {
static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname)
{
- struct thread_trace *ttrace = thread->priv;
+ struct thread_trace *ttrace = thread__priv(thread);
if (fd > ttrace->paths.max) {
char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *));
@@ -1275,7 +1301,7 @@ static int thread__read_fd_path(struct thread *thread, int fd)
static const char *thread__fd_path(struct thread *thread, int fd,
struct trace *trace)
{
- struct thread_trace *ttrace = thread->priv;
+ struct thread_trace *ttrace = thread__priv(thread);
if (ttrace == NULL)
return NULL;
@@ -1312,7 +1338,7 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size,
{
int fd = arg->val;
size_t printed = syscall_arg__scnprintf_fd(bf, size, arg);
- struct thread_trace *ttrace = arg->thread->priv;
+ struct thread_trace *ttrace = thread__priv(arg->thread);
if (ttrace && fd >= 0 && fd <= ttrace->paths.max)
zfree(&ttrace->paths.table[fd]);
@@ -1385,7 +1411,7 @@ static int trace__tool_process(struct perf_tool *tool,
static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
{
- int err = symbol__init();
+ int err = symbol__init(NULL);
if (err)
return err;
@@ -1669,7 +1695,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
- int ret;
+ long ret;
u64 duration = 0;
struct thread *thread;
int id = perf_evsel__sc_tp_uint(evsel, id, sample);
@@ -1722,9 +1748,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
if (sc->fmt == NULL) {
signed_print:
- fprintf(trace->output, ") = %d", ret);
+ fprintf(trace->output, ") = %ld", ret);
} else if (ret < 0 && sc->fmt->errmsg) {
- char bf[256];
+ char bf[STRERR_BUFSIZE];
const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
*e = audit_errno_to_name(-ret);
@@ -1732,7 +1758,7 @@ signed_print:
} else if (ret == 0 && sc->fmt->timeout)
fprintf(trace->output, ") = 0 Timeout");
else if (sc->fmt->hexret)
- fprintf(trace->output, ") = %#x", ret);
+ fprintf(trace->output, ") = %#lx", ret);
else
goto signed_print;
@@ -1820,7 +1846,7 @@ static int trace__pgfault(struct trace *trace,
if (trace->summary_only)
return 0;
- thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION,
+ thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
sample->ip, &al);
trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output);
@@ -1833,11 +1859,11 @@ static int trace__pgfault(struct trace *trace,
fprintf(trace->output, "] => ");
- thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE,
+ thread__find_addr_location(thread, cpumode, MAP__VARIABLE,
sample->addr, &al);
if (!al.map) {
- thread__find_addr_location(thread, trace->host, cpumode,
+ thread__find_addr_location(thread, cpumode,
MAP__FUNCTION, sample->addr, &al);
if (al.map)
@@ -2018,6 +2044,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
int err = -1, i;
unsigned long before;
const bool forks = argc > 0;
+ bool draining = false;
+ char sbuf[STRERR_BUFSIZE];
trace->live = true;
@@ -2079,7 +2107,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
if (err < 0) {
- fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno));
+ fprintf(trace->output, "Couldn't mmap the events: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -2143,8 +2172,12 @@ next_event:
if (trace->nr_events == before) {
int timeout = done ? 100 : -1;
- if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0)
+ if (!draining && perf_evlist__poll(evlist, timeout) > 0) {
+ if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0)
+ draining = true;
+
goto again;
+ }
} else {
goto again;
}
@@ -2209,18 +2242,18 @@ static int trace__replay(struct trace *trace)
trace->tool.tracing_data = perf_event__process_tracing_data;
trace->tool.build_id = perf_event__process_build_id;
- trace->tool.ordered_samples = true;
+ trace->tool.ordered_events = true;
trace->tool.ordering_requires_timestamps = true;
/* add tid to output */
trace->multiple_threads = true;
- if (symbol__init() < 0)
- return -1;
-
session = perf_session__new(&file, false, &trace->tool);
if (session == NULL)
- return -ENOMEM;
+ return -1;
+
+ if (symbol__init(&session->header.env) < 0)
+ goto out;
trace->host = &session->machines.host;
@@ -2348,7 +2381,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
FILE *fp = data->fp;
size_t printed = data->printed;
struct trace *trace = data->trace;
- struct thread_trace *ttrace = thread->priv;
+ struct thread_trace *ttrace = thread__priv(thread);
double ratio;
if (ttrace == NULL)
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1f67aa02d240..5d4b039fe1ed 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -48,10 +48,6 @@ ifneq ($(ARCH),$(filter $(ARCH),x86 arm))
NO_LIBDW_DWARF_UNWIND := 1
endif
-ifeq ($(ARCH),powerpc)
- CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
-endif
-
ifeq ($(LIBUNWIND_LIBS),)
NO_LIBUNWIND := 1
else
@@ -120,6 +116,29 @@ ifdef PARSER_DEBUG
CFLAGS += -DPARSER_DEBUG
endif
+ifndef NO_LIBPYTHON
+ # Try different combinations to accommodate systems that only have
+ # python[2][-config] in weird combinations but always preferring
+ # python2 and python2-config as per pep-0394. If we catch a
+ # python[-config] in version 3, the version check will kill it.
+ PYTHON2 := $(if $(call get-executable,python2),python2,python)
+ override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2))
+ PYTHON2_CONFIG := \
+ $(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config)
+ override PYTHON_CONFIG := \
+ $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG))
+
+ PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG))
+
+ PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
+ PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
+
+ FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS)
+ FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS)
+ FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS)
+ FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS)
+endif
+
CFLAGS += -fno-omit-frame-pointer
CFLAGS += -ggdb3
CFLAGS += -funwind-tables
@@ -131,7 +150,7 @@ CFLAGS += -std=gnu99
# adding assembler files missing the .GNU-stack linker note.
LDFLAGS += -Wl,-z,noexecstack
-EXTLIBS = -lelf -lpthread -lrt -lm -ldl
+EXTLIBS = -lpthread -lrt -lm -ldl
ifneq ($(OUTPUT),)
OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/
@@ -181,7 +200,8 @@ CORE_FEATURE_TESTS = \
libunwind \
stackprotector-all \
timerfd \
- libdw-dwarf-unwind
+ libdw-dwarf-unwind \
+ zlib
LIB_FEATURE_TESTS = \
dwarf \
@@ -195,7 +215,8 @@ LIB_FEATURE_TESTS = \
libpython \
libslang \
libunwind \
- libdw-dwarf-unwind
+ libdw-dwarf-unwind \
+ zlib
VF_FEATURE_TESTS = \
backtrace \
@@ -211,7 +232,9 @@ VF_FEATURE_TESTS = \
bionic \
liberty \
liberty-z \
- cplus-demangle
+ cplus-demangle \
+ compile-32 \
+ compile-x32
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
@@ -331,6 +354,7 @@ endif # NO_LIBELF
ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBELF_SUPPORT
+ EXTLIBS += -lelf
ifeq ($(feature-libelf-mmap), 1)
CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT
@@ -350,11 +374,17 @@ ifndef NO_LIBELF
else
CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS)
LDFLAGS += $(LIBDW_LDFLAGS)
- EXTLIBS += -lelf -ldw
+ EXTLIBS += -ldw
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
endif # NO_LIBELF
+ifeq ($(ARCH),powerpc)
+ ifndef NO_DWARF
+ CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
+ endif
+endif
+
ifndef NO_LIBUNWIND
ifneq ($(feature-libunwind), 1)
msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR);
@@ -482,21 +512,14 @@ define disable-python_code
NO_LIBPYTHON := 1
endef
-override PYTHON := \
- $(call get-executable-or-default,PYTHON,python)
-
-ifndef PYTHON
- $(call disable-python,python interpreter)
+ifdef NO_LIBPYTHON
+ $(call disable-python)
else
- PYTHON_WORD := $(call shell-wordify,$(PYTHON))
-
- ifdef NO_LIBPYTHON
- $(call disable-python)
+ ifndef PYTHON
+ $(call disable-python,python interpreter)
else
-
- override PYTHON_CONFIG := \
- $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config)
+ PYTHON_WORD := $(call shell-wordify,$(PYTHON))
ifndef PYTHON_CONFIG
$(call disable-python,python-config tool)
@@ -584,6 +607,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),)
CFLAGS += -DHAVE_LIBBFD_SUPPORT
endif
+ifndef NO_ZLIB
+ ifeq ($(feature-zlib), 1)
+ CFLAGS += -DHAVE_ZLIB_SUPPORT
+ EXTLIBS += -lz
+ else
+ NO_ZLIB := 1
+ endif
+endif
+
ifndef NO_BACKTRACE
ifeq ($(feature-backtrace), 1)
CFLAGS += -DHAVE_BACKTRACE_SUPPORT
@@ -604,6 +636,31 @@ ifdef HAVE_KVM_STAT_SUPPORT
CFLAGS += -DHAVE_KVM_STAT_SUPPORT
endif
+ifeq (${IS_64_BIT}, 1)
+ ifndef NO_PERF_READ_VDSO32
+ $(call feature_check,compile-32)
+ ifeq ($(feature-compile-32), 1)
+ CFLAGS += -DHAVE_PERF_READ_VDSO32
+ else
+ NO_PERF_READ_VDSO32 := 1
+ endif
+ endif
+ ifneq (${IS_X86_64}, 1)
+ NO_PERF_READ_VDSOX32 := 1
+ endif
+ ifndef NO_PERF_READ_VDSOX32
+ $(call feature_check,compile-x32)
+ ifeq ($(feature-compile-x32), 1)
+ CFLAGS += -DHAVE_PERF_READ_VDSOX32
+ else
+ NO_PERF_READ_VDSOX32 := 1
+ endif
+ endif
+else
+ NO_PERF_READ_VDSO32 := 1
+ NO_PERF_READ_VDSOX32 := 1
+endif
+
# Among the variables below, these:
# perfexecdir
# template_dir
@@ -635,11 +692,13 @@ else
sysconfdir = $(prefix)/etc
ETC_PERFCONFIG = etc/perfconfig
endif
+ifndef lib
ifeq ($(IS_X86_64),1)
lib = lib64
else
lib = lib
endif
+endif # lib
libdir = $(prefix)/$(lib)
# Shell quote (do not use $(call) to accommodate ancient setups);
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch
index 4b06719ee984..851cd0172a76 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/perf/config/Makefile.arch
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64)
RAW_ARCH := x86_64
endif
endif
+
+ifeq (${IS_X86_64}, 1)
+ IS_64_BIT := 1
+else ifeq ($(ARCH),x86)
+ IS_64_BIT := 0
+else
+ IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+endif
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile
index 6088f8d8a434..53f19b5dbc37 100644
--- a/tools/perf/config/feature-checks/Makefile
+++ b/tools/perf/config/feature-checks/Makefile
@@ -27,7 +27,10 @@ FILES= \
test-libunwind-debug-frame.bin \
test-stackprotector-all.bin \
test-timerfd.bin \
- test-libdw-dwarf-unwind.bin
+ test-libdw-dwarf-unwind.bin \
+ test-compile-32.bin \
+ test-compile-x32.bin \
+ test-zlib.bin
CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -39,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS)
###############################
test-all.bin:
- $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl
+ $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz
test-hello.bin:
$(BUILD)
@@ -101,25 +104,11 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
test-libperl.bin:
$(BUILD) $(FLAGS_PERL_EMBED)
-override PYTHON := python
-override PYTHON_CONFIG := python-config
-
-escape-for-shell-sq = $(subst ','\'',$(1))
-shell-sq = '$(escape-for-shell-sq)'
-
-PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG))
-
-PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
-PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
-PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
-PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
-FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
-
test-libpython.bin:
- $(BUILD) $(FLAGS_PYTHON_EMBED)
+ $(BUILD)
test-libpython-version.bin:
- $(BUILD) $(FLAGS_PYTHON_EMBED)
+ $(BUILD)
test-libbfd.bin:
$(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl
@@ -145,6 +134,15 @@ test-libdw-dwarf-unwind.bin:
test-sync-compare-and-swap.bin:
$(BUILD) -Werror
+test-compile-32.bin:
+ $(CC) -m32 -o $(OUTPUT)$@ test-compile.c
+
+test-compile-x32.bin:
+ $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
+
+test-zlib.bin:
+ $(BUILD) -lz
+
-include *.d
###############################
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c
index a7d022e161c0..652e0098eba6 100644
--- a/tools/perf/config/feature-checks/test-all.c
+++ b/tools/perf/config/feature-checks/test-all.c
@@ -93,6 +93,10 @@
# include "test-sync-compare-and-swap.c"
#undef main
+#define main main_test_zlib
+# include "test-zlib.c"
+#undef main
+
int main(int argc, char *argv[])
{
main_test_libpython();
@@ -116,6 +120,7 @@ int main(int argc, char *argv[])
main_test_stackprotector_all();
main_test_libdw_dwarf_unwind();
main_test_sync_compare_and_swap(argc, argv);
+ main_test_zlib();
return 0;
}
diff --git a/tools/perf/config/feature-checks/test-compile.c b/tools/perf/config/feature-checks/test-compile.c
new file mode 100644
index 000000000000..31dbf45bf99c
--- /dev/null
+++ b/tools/perf/config/feature-checks/test-compile.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tools/perf/config/feature-checks/test-zlib.c b/tools/perf/config/feature-checks/test-zlib.c
new file mode 100644
index 000000000000..e111fff6240e
--- /dev/null
+++ b/tools/perf/config/feature-checks/test-zlib.c
@@ -0,0 +1,9 @@
+#include <zlib.h>
+
+int main(void)
+{
+ z_stream zs;
+
+ inflateInit(&zs);
+ return 0;
+}
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak
index 4d985e0f03f5..7076a62d0ff7 100644
--- a/tools/perf/config/utilities.mak
+++ b/tools/perf/config/utilities.mak
@@ -132,7 +132,7 @@ endef
#
# Usage: bool-value = $(call is-absolute,path)
#
-is-absolute = $(shell echo $(shell-sq) | grep ^/ -q && echo y)
+is-absolute = $(shell echo $(shell-sq) | grep -q ^/ && echo y)
# lookup
#
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c
new file mode 100644
index 000000000000..764e2547c25a
--- /dev/null
+++ b/tools/perf/perf-read-vdso.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <string.h>
+
+#define VDSO__MAP_NAME "[vdso]"
+
+/*
+ * Include definition of find_vdso_map() also used in util/vdso.c for
+ * building perf.
+ */
+#include "util/find-vdso-map.c"
+
+int main(void)
+{
+ void *start, *end;
+ size_t size, written;
+
+ if (find_vdso_map(&start, &end))
+ return 1;
+
+ size = end - start;
+
+ while (size) {
+ written = fwrite(start, 1, size, stdout);
+ if (!written)
+ return 1;
+ start += written;
+ size -= written;
+ }
+
+ if (fflush(stdout))
+ return 1;
+
+ return 0;
+}
diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h
index 937e4324ad94..a3b13d7dc1d4 100644
--- a/tools/perf/perf-sys.h
+++ b/tools/perf/perf-sys.h
@@ -13,7 +13,7 @@
#define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
-#define CPUINFO_PROC "model name"
+#define CPUINFO_PROC {"model name"}
#ifndef __NR_perf_event_open
# define __NR_perf_event_open 336
#endif
@@ -30,7 +30,7 @@
#define wmb() asm volatile("sfence" ::: "memory")
#define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
-#define CPUINFO_PROC "model name"
+#define CPUINFO_PROC {"model name"}
#ifndef __NR_perf_event_open
# define __NR_perf_event_open 298
#endif
@@ -47,14 +47,14 @@
#define mb() asm volatile ("sync" ::: "memory")
#define wmb() asm volatile ("sync" ::: "memory")
#define rmb() asm volatile ("sync" ::: "memory")
-#define CPUINFO_PROC "cpu"
+#define CPUINFO_PROC {"cpu"}
#endif
#ifdef __s390__
#define mb() asm volatile("bcr 15,0" ::: "memory")
#define wmb() asm volatile("bcr 15,0" ::: "memory")
#define rmb() asm volatile("bcr 15,0" ::: "memory")
-#define CPUINFO_PROC "vendor_id"
+#define CPUINFO_PROC {"vendor_id"}
#endif
#ifdef __sh__
@@ -67,14 +67,14 @@
# define wmb() asm volatile("" ::: "memory")
# define rmb() asm volatile("" ::: "memory")
#endif
-#define CPUINFO_PROC "cpu type"
+#define CPUINFO_PROC {"cpu type"}
#endif
#ifdef __hppa__
#define mb() asm volatile("" ::: "memory")
#define wmb() asm volatile("" ::: "memory")
#define rmb() asm volatile("" ::: "memory")
-#define CPUINFO_PROC "cpu"
+#define CPUINFO_PROC {"cpu"}
#endif
#ifdef __sparc__
@@ -87,14 +87,14 @@
#endif
#define wmb() asm volatile("":::"memory")
#define rmb() asm volatile("":::"memory")
-#define CPUINFO_PROC "cpu"
+#define CPUINFO_PROC {"cpu"}
#endif
#ifdef __alpha__
#define mb() asm volatile("mb" ::: "memory")
#define wmb() asm volatile("wmb" ::: "memory")
#define rmb() asm volatile("mb" ::: "memory")
-#define CPUINFO_PROC "cpu model"
+#define CPUINFO_PROC {"cpu model"}
#endif
#ifdef __ia64__
@@ -102,7 +102,7 @@
#define wmb() asm volatile ("mf" ::: "memory")
#define rmb() asm volatile ("mf" ::: "memory")
#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
-#define CPUINFO_PROC "model name"
+#define CPUINFO_PROC {"model name"}
#endif
#ifdef __arm__
@@ -113,7 +113,7 @@
#define mb() ((void(*)(void))0xffff0fa0)()
#define wmb() ((void(*)(void))0xffff0fa0)()
#define rmb() ((void(*)(void))0xffff0fa0)()
-#define CPUINFO_PROC "Processor"
+#define CPUINFO_PROC {"model name", "Processor"}
#endif
#ifdef __aarch64__
@@ -133,28 +133,28 @@
: "memory")
#define wmb() mb()
#define rmb() mb()
-#define CPUINFO_PROC "cpu model"
+#define CPUINFO_PROC {"cpu model"}
#endif
#ifdef __arc__
#define mb() asm volatile("" ::: "memory")
#define wmb() asm volatile("" ::: "memory")
#define rmb() asm volatile("" ::: "memory")
-#define CPUINFO_PROC "Processor"
+#define CPUINFO_PROC {"Processor"}
#endif
#ifdef __metag__
#define mb() asm volatile("" ::: "memory")
#define wmb() asm volatile("" ::: "memory")
#define rmb() asm volatile("" ::: "memory")
-#define CPUINFO_PROC "CPU"
+#define CPUINFO_PROC {"CPU"}
#endif
#ifdef __xtensa__
#define mb() asm volatile("memw" ::: "memory")
#define wmb() asm volatile("memw" ::: "memory")
#define rmb() asm volatile("" ::: "memory")
-#define CPUINFO_PROC "core ID"
+#define CPUINFO_PROC {"core ID"}
#endif
#ifdef __tile__
@@ -162,7 +162,7 @@
#define wmb() asm volatile ("mf" ::: "memory")
#define rmb() asm volatile ("mf" ::: "memory")
#define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory")
-#define CPUINFO_PROC "model name"
+#define CPUINFO_PROC {"model name"}
#endif
#define barrier() asm volatile ("" ::: "memory")
diff --git a/tools/perf/perf-with-kcore.sh b/tools/perf/perf-with-kcore.sh
new file mode 100644
index 000000000000..c7ff90a90e4e
--- /dev/null
+++ b/tools/perf/perf-with-kcore.sh
@@ -0,0 +1,259 @@
+#!/bin/bash
+# perf-with-kcore: use perf with a copy of kcore
+# Copyright (c) 2014, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+
+set -e
+
+usage()
+{
+ echo "Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]" >&2
+ echo " <perf sub-command> can be record, script, report or inject" >&2
+ echo " or: perf-with-kcore fix_buildid_cache_permissions" >&2
+ exit 1
+}
+
+find_perf()
+{
+ if [ -n "$PERF" ] ; then
+ return
+ fi
+ PERF=`which perf || true`
+ if [ -z "$PERF" ] ; then
+ echo "Failed to find perf" >&2
+ exit 1
+ fi
+ if [ ! -x "$PERF" ] ; then
+ echo "Failed to find perf" >&2
+ exit 1
+ fi
+ echo "Using $PERF"
+ "$PERF" version
+}
+
+copy_kcore()
+{
+ echo "Copying kcore"
+
+ if [ $EUID -eq 0 ] ; then
+ SUDO=""
+ else
+ SUDO="sudo"
+ fi
+
+ rm -f perf.data.junk
+ ("$PERF" record -o perf.data.junk $PERF_OPTIONS -- sleep 60) >/dev/null 2>/dev/null &
+ PERF_PID=$!
+
+ # Need to make sure that perf has started
+ sleep 1
+
+ KCORE=$(($SUDO "$PERF" buildid-cache -v -f -k /proc/kcore >/dev/null) 2>&1)
+ case "$KCORE" in
+ "kcore added to build-id cache directory "*)
+ KCORE_DIR=${KCORE#"kcore added to build-id cache directory "}
+ ;;
+ *)
+ kill $PERF_PID
+ wait >/dev/null 2>/dev/null || true
+ rm perf.data.junk
+ echo "$KCORE"
+ echo "Failed to find kcore" >&2
+ exit 1
+ ;;
+ esac
+
+ kill $PERF_PID
+ wait >/dev/null 2>/dev/null || true
+ rm perf.data.junk
+
+ $SUDO cp -a "$KCORE_DIR" "$(pwd)/$PERF_DATA_DIR"
+ $SUDO rm -f "$KCORE_DIR/kcore"
+ $SUDO rm -f "$KCORE_DIR/kallsyms"
+ $SUDO rm -f "$KCORE_DIR/modules"
+ $SUDO rmdir "$KCORE_DIR"
+
+ KCORE_DIR_BASENAME=$(basename "$KCORE_DIR")
+ KCORE_DIR="$(pwd)/$PERF_DATA_DIR/$KCORE_DIR_BASENAME"
+
+ $SUDO chown $UID "$KCORE_DIR"
+ $SUDO chown $UID "$KCORE_DIR/kcore"
+ $SUDO chown $UID "$KCORE_DIR/kallsyms"
+ $SUDO chown $UID "$KCORE_DIR/modules"
+
+ $SUDO chgrp $GROUPS "$KCORE_DIR"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/kcore"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/kallsyms"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/modules"
+
+ ln -s "$KCORE_DIR_BASENAME" "$PERF_DATA_DIR/kcore_dir"
+}
+
+fix_buildid_cache_permissions()
+{
+ if [ $EUID -ne 0 ] ; then
+ echo "This script must be run as root via sudo " >&2
+ exit 1
+ fi
+
+ if [ -z "$SUDO_USER" ] ; then
+ echo "This script must be run via sudo" >&2
+ exit 1
+ fi
+
+ USER_HOME=$(bash <<< "echo ~$SUDO_USER")
+
+ if [ "$HOME" != "$USER_HOME" ] ; then
+ echo "Fix unnecessary because root has a home: $HOME" >&2
+ exit 1
+ fi
+
+ echo "Fixing buildid cache permissions"
+
+ find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type f -links 1 ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type l ! -user "$SUDO_USER" -ls -exec chown -h "$SUDO_USER" \{\} \;
+
+ if [ -n "$SUDO_GID" ] ; then
+ find "$USER_HOME/.debug" -xdev -type d ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type f -links 1 ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type l ! -group "$SUDO_GID" -ls -exec chgrp -h "$SUDO_GID" \{\} \;
+ fi
+
+ echo "Done"
+}
+
+check_buildid_cache_permissions()
+{
+ if [ $EUID -eq 0 ] ; then
+ return
+ fi
+
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -user "$USER" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -user "$USER" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -user "$USER" -print -quit)
+
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -group "$GROUPS" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -group "$GROUPS" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -group "$GROUPS" -print -quit)
+
+ if [ -n "$PERMISSIONS_OK" ] ; then
+ echo "*** WARNING *** buildid cache permissions may need fixing" >&2
+ fi
+}
+
+record()
+{
+ echo "Recording"
+
+ if [ $EUID -ne 0 ] ; then
+
+ if [ "$(cat /proc/sys/kernel/kptr_restrict)" -ne 0 ] ; then
+ echo "*** WARNING *** /proc/sys/kernel/kptr_restrict prevents access to kernel addresses" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q ' -a \|^-a \| -a$\|^-a$\| --all-cpus \|^--all-cpus \| --all-cpus$\|^--all-cpus$' ; then
+ echo "*** WARNING *** system-wide tracing without root access will not be able to read all necessary information from /proc" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q 'intel_pt\|intel_bts\| -I\|^-I' ; then
+ if [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt -1 ] ; then
+ echo "*** WARNING *** /proc/sys/kernel/perf_event_paranoid restricts buffer size and tracepoint (sched_switch) use" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q ' --per-thread \|^--per-thread \| --per-thread$\|^--per-thread$' ; then
+ true
+ elif echo "$PERF_OPTIONS" | grep -q ' -t \|^-t \| -t$\|^-t$' ; then
+ true
+ elif [ ! -r /sys/kernel/debug -o ! -x /sys/kernel/debug ] ; then
+ echo "*** WARNING *** /sys/kernel/debug permissions prevent tracepoint (sched_switch) use" >&2
+ fi
+ fi
+ fi
+
+ if [ -z "$1" ] ; then
+ echo "Workload is required for recording" >&2
+ usage
+ fi
+
+ if [ -e "$PERF_DATA_DIR" ] ; then
+ echo "'$PERF_DATA_DIR' exists" >&2
+ exit 1
+ fi
+
+ find_perf
+
+ mkdir "$PERF_DATA_DIR"
+
+ echo "$PERF record -o $PERF_DATA_DIR/perf.data $PERF_OPTIONS -- $*"
+ "$PERF" record -o "$PERF_DATA_DIR/perf.data" $PERF_OPTIONS -- $* || true
+
+ if rmdir "$PERF_DATA_DIR" > /dev/null 2>/dev/null ; then
+ exit 1
+ fi
+
+ copy_kcore
+
+ echo "Done"
+}
+
+subcommand()
+{
+ find_perf
+ check_buildid_cache_permissions
+ echo "$PERF $PERF_SUB_COMMAND -i $PERF_DATA_DIR/perf.data --kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms $*"
+ "$PERF" $PERF_SUB_COMMAND -i "$PERF_DATA_DIR/perf.data" "--kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms" $*
+}
+
+if [ "$1" = "fix_buildid_cache_permissions" ] ; then
+ fix_buildid_cache_permissions
+ exit 0
+fi
+
+PERF_SUB_COMMAND=$1
+PERF_DATA_DIR=$2
+shift || true
+shift || true
+
+if [ -z "$PERF_SUB_COMMAND" ] ; then
+ usage
+fi
+
+if [ -z "$PERF_DATA_DIR" ] ; then
+ usage
+fi
+
+case "$PERF_SUB_COMMAND" in
+"record")
+ while [ "$1" != "--" ] ; do
+ PERF_OPTIONS+="$1 "
+ shift || break
+ done
+ if [ "$1" != "--" ] ; then
+ echo "Options and workload are required for recording" >&2
+ usage
+ fi
+ shift
+ record $*
+;;
+"script")
+ subcommand $*
+;;
+"report")
+ subcommand $*
+;;
+"inject")
+ subcommand $*
+;;
+*)
+ usage
+;;
+esac
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 2282d41879a2..452a8474d29d 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -313,6 +313,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
int status;
struct stat st;
const char *prefix;
+ char sbuf[STRERR_BUFSIZE];
prefix = NULL;
if (p->option & RUN_SETUP)
@@ -343,7 +344,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
status = 1;
/* Check for ENOSPC and EIO errors.. */
if (fflush(stdout)) {
- fprintf(stderr, "write failure on standard output: %s", strerror(errno));
+ fprintf(stderr, "write failure on standard output: %s",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out;
}
if (ferror(stdout)) {
@@ -351,7 +353,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
goto out;
}
if (fclose(stdout)) {
- fprintf(stderr, "close failed on standard output: %s", strerror(errno));
+ fprintf(stderr, "close failed on standard output: %s",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out;
}
status = 0;
@@ -466,6 +469,7 @@ void pthread__unblock_sigwinch(void)
int main(int argc, const char **argv)
{
const char *cmd;
+ char sbuf[STRERR_BUFSIZE];
/* The page_size is placed in util object. */
page_size = sysconf(_SC_PAGE_SIZE);
@@ -561,7 +565,7 @@ int main(int argc, const char **argv)
}
fprintf(stderr, "Failed to run command '%s': %s\n",
- cmd, strerror(errno));
+ cmd, strerror_r(errno, sbuf, sizeof(sbuf)));
out:
return 1;
}
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 510c65f72858..1dabb8553499 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void);
struct record_opts {
struct target target;
- int call_graph;
- bool call_graph_enabled;
bool group;
bool inherit_stat;
bool no_buffering;
@@ -54,15 +52,18 @@ struct record_opts {
bool sample_weight;
bool sample_time;
bool period;
+ bool sample_intr_regs;
unsigned int freq;
unsigned int mmap_pages;
unsigned int user_freq;
u64 branch_stack;
u64 default_interval;
u64 user_interval;
- u16 stack_dump_size;
bool sample_transaction;
unsigned initial_delay;
};
+struct option;
+extern const char * const *record_usage;
+extern struct option *record_options;
#endif
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record
new file mode 100644
index 000000000000..221d66e05713
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-record
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#
+# export perf data to a postgresql database. Can cover
+# perf ip samples (excluding the tracepoints). No special
+# record requirements, just record what you want to export.
+#
+perf record $@
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
new file mode 100644
index 000000000000..cd335b6e2a01
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -0,0 +1,29 @@
+#!/bin/bash
+# description: export perf data to a postgresql database
+# args: [database name] [columns] [calls]
+n_args=0
+for i in "$@"
+do
+ if expr match "$i" "-" > /dev/null ; then
+ break
+ fi
+ n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 3 ] ; then
+ echo "usage: export-to-postgresql-report [database name] [columns] [calls]"
+ exit
+fi
+if [ "$n_args" -gt 2 ] ; then
+ dbname=$1
+ columns=$2
+ calls=$3
+ shift 3
+elif [ "$n_args" -gt 1 ] ; then
+ dbname=$1
+ columns=$2
+ shift 2
+elif [ "$n_args" -gt 0 ] ; then
+ dbname=$1
+ shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
new file mode 100644
index 000000000000..4cdafd880074
--- /dev/null
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -0,0 +1,444 @@
+# export-to-postgresql.py: export perf data to a postgresql database
+# Copyright (c) 2014, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+
+import os
+import sys
+import struct
+import datetime
+
+from PySide.QtSql import *
+
+# Need to access PostgreSQL C library directly to use COPY FROM STDIN
+from ctypes import *
+libpq = CDLL("libpq.so.5")
+PQconnectdb = libpq.PQconnectdb
+PQconnectdb.restype = c_void_p
+PQfinish = libpq.PQfinish
+PQstatus = libpq.PQstatus
+PQexec = libpq.PQexec
+PQexec.restype = c_void_p
+PQresultStatus = libpq.PQresultStatus
+PQputCopyData = libpq.PQputCopyData
+PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ]
+PQputCopyEnd = libpq.PQputCopyEnd
+PQputCopyEnd.argtypes = [ c_void_p, c_void_p ]
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+# These perf imports are not used at present
+#from perf_trace_context import *
+#from Core import *
+
+perf_db_export_mode = True
+perf_db_export_calls = False
+
+def usage():
+ print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
+ print >> sys.stderr, "where: columns 'all' or 'branches'"
+ print >> sys.stderr, " calls 'calls' => create calls table"
+ raise Exception("Too few arguments")
+
+if (len(sys.argv) < 2):
+ usage()
+
+dbname = sys.argv[1]
+
+if (len(sys.argv) >= 3):
+ columns = sys.argv[2]
+else:
+ columns = "all"
+
+if columns not in ("all", "branches"):
+ usage()
+
+branches = (columns == "branches")
+
+if (len(sys.argv) >= 4):
+ if (sys.argv[3] == "calls"):
+ perf_db_export_calls = True
+ else:
+ usage()
+
+output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
+os.mkdir(output_dir_name)
+
+def do_query(q, s):
+ if (q.exec_(s)):
+ return
+ raise Exception("Query failed: " + q.lastError().text())
+
+print datetime.datetime.today(), "Creating database..."
+
+db = QSqlDatabase.addDatabase('QPSQL')
+query = QSqlQuery(db)
+db.setDatabaseName('postgres')
+db.open()
+try:
+ do_query(query, 'CREATE DATABASE ' + dbname)
+except:
+ os.rmdir(output_dir_name)
+ raise
+query.finish()
+query.clear()
+db.close()
+
+db.setDatabaseName(dbname)
+db.open()
+
+query = QSqlQuery(db)
+do_query(query, 'SET client_min_messages TO WARNING')
+
+do_query(query, 'CREATE TABLE selected_events ('
+ 'id bigint NOT NULL,'
+ 'name varchar(80))')
+do_query(query, 'CREATE TABLE machines ('
+ 'id bigint NOT NULL,'
+ 'pid integer,'
+ 'root_dir varchar(4096))')
+do_query(query, 'CREATE TABLE threads ('
+ 'id bigint NOT NULL,'
+ 'machine_id bigint,'
+ 'process_id bigint,'
+ 'pid integer,'
+ 'tid integer)')
+do_query(query, 'CREATE TABLE comms ('
+ 'id bigint NOT NULL,'
+ 'comm varchar(16))')
+do_query(query, 'CREATE TABLE comm_threads ('
+ 'id bigint NOT NULL,'
+ 'comm_id bigint,'
+ 'thread_id bigint)')
+do_query(query, 'CREATE TABLE dsos ('
+ 'id bigint NOT NULL,'
+ 'machine_id bigint,'
+ 'short_name varchar(256),'
+ 'long_name varchar(4096),'
+ 'build_id varchar(64))')
+do_query(query, 'CREATE TABLE symbols ('
+ 'id bigint NOT NULL,'
+ 'dso_id bigint,'
+ 'sym_start bigint,'
+ 'sym_end bigint,'
+ 'binding integer,'
+ 'name varchar(2048))')
+do_query(query, 'CREATE TABLE branch_types ('
+ 'id integer NOT NULL,'
+ 'name varchar(80))')
+
+if branches:
+ do_query(query, 'CREATE TABLE samples ('
+ 'id bigint NOT NULL,'
+ 'evsel_id bigint,'
+ 'machine_id bigint,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'dso_id bigint,'
+ 'symbol_id bigint,'
+ 'sym_offset bigint,'
+ 'ip bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'to_dso_id bigint,'
+ 'to_symbol_id bigint,'
+ 'to_sym_offset bigint,'
+ 'to_ip bigint,'
+ 'branch_type integer,'
+ 'in_tx boolean)')
+else:
+ do_query(query, 'CREATE TABLE samples ('
+ 'id bigint NOT NULL,'
+ 'evsel_id bigint,'
+ 'machine_id bigint,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'dso_id bigint,'
+ 'symbol_id bigint,'
+ 'sym_offset bigint,'
+ 'ip bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'to_dso_id bigint,'
+ 'to_symbol_id bigint,'
+ 'to_sym_offset bigint,'
+ 'to_ip bigint,'
+ 'period bigint,'
+ 'weight bigint,'
+ 'transaction bigint,'
+ 'data_src bigint,'
+ 'branch_type integer,'
+ 'in_tx boolean)')
+
+if perf_db_export_calls:
+ do_query(query, 'CREATE TABLE call_paths ('
+ 'id bigint NOT NULL,'
+ 'parent_id bigint,'
+ 'symbol_id bigint,'
+ 'ip bigint)')
+ do_query(query, 'CREATE TABLE calls ('
+ 'id bigint NOT NULL,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'call_path_id bigint,'
+ 'call_time bigint,'
+ 'return_time bigint,'
+ 'branch_count bigint,'
+ 'call_id bigint,'
+ 'return_id bigint,'
+ 'parent_call_path_id bigint,'
+ 'flags integer)')
+
+do_query(query, 'CREATE VIEW samples_view AS '
+ 'SELECT '
+ 'id,'
+ 'time,'
+ 'cpu,'
+ '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+ '(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
+ '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+ '(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
+ 'to_hex(ip) AS ip_hex,'
+ '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
+ 'sym_offset,'
+ '(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
+ 'to_hex(to_ip) AS to_ip_hex,'
+ '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
+ 'to_sym_offset,'
+ '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
+ '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
+ 'in_tx'
+ ' FROM samples')
+
+
+file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0)
+file_trailer = "\377\377"
+
+def open_output_file(file_name):
+ path_name = output_dir_name + "/" + file_name
+ file = open(path_name, "w+")
+ file.write(file_header)
+ return file
+
+def close_output_file(file):
+ file.write(file_trailer)
+ file.close()
+
+def copy_output_file_direct(file, table_name):
+ close_output_file(file)
+ sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')"
+ do_query(query, sql)
+
+# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
+def copy_output_file(file, table_name):
+ conn = PQconnectdb("dbname = " + dbname)
+ if (PQstatus(conn)):
+ raise Exception("COPY FROM STDIN PQconnectdb failed")
+ file.write(file_trailer)
+ file.seek(0)
+ sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
+ res = PQexec(conn, sql)
+ if (PQresultStatus(res) != 4):
+ raise Exception("COPY FROM STDIN PQexec failed")
+ data = file.read(65536)
+ while (len(data)):
+ ret = PQputCopyData(conn, data, len(data))
+ if (ret != 1):
+ raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret))
+ data = file.read(65536)
+ ret = PQputCopyEnd(conn, None)
+ if (ret != 1):
+ raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret))
+ PQfinish(conn)
+
+def remove_output_file(file):
+ name = file.name
+ file.close()
+ os.unlink(name)
+
+evsel_file = open_output_file("evsel_table.bin")
+machine_file = open_output_file("machine_table.bin")
+thread_file = open_output_file("thread_table.bin")
+comm_file = open_output_file("comm_table.bin")
+comm_thread_file = open_output_file("comm_thread_table.bin")
+dso_file = open_output_file("dso_table.bin")
+symbol_file = open_output_file("symbol_table.bin")
+branch_type_file = open_output_file("branch_type_table.bin")
+sample_file = open_output_file("sample_table.bin")
+if perf_db_export_calls:
+ call_path_file = open_output_file("call_path_table.bin")
+ call_file = open_output_file("call_table.bin")
+
+def trace_begin():
+ print datetime.datetime.today(), "Writing to intermediate files..."
+ # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
+ evsel_table(0, "unknown")
+ machine_table(0, 0, "unknown")
+ thread_table(0, 0, 0, -1, -1)
+ comm_table(0, "unknown")
+ dso_table(0, 0, "unknown", "unknown", "")
+ symbol_table(0, 0, 0, 0, 0, "unknown")
+ sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ if perf_db_export_calls:
+ call_path_table(0, 0, 0, 0)
+
+unhandled_count = 0
+
+def trace_end():
+ print datetime.datetime.today(), "Copying to database..."
+ copy_output_file(evsel_file, "selected_events")
+ copy_output_file(machine_file, "machines")
+ copy_output_file(thread_file, "threads")
+ copy_output_file(comm_file, "comms")
+ copy_output_file(comm_thread_file, "comm_threads")
+ copy_output_file(dso_file, "dsos")
+ copy_output_file(symbol_file, "symbols")
+ copy_output_file(branch_type_file, "branch_types")
+ copy_output_file(sample_file, "samples")
+ if perf_db_export_calls:
+ copy_output_file(call_path_file, "call_paths")
+ copy_output_file(call_file, "calls")
+
+ print datetime.datetime.today(), "Removing intermediate files..."
+ remove_output_file(evsel_file)
+ remove_output_file(machine_file)
+ remove_output_file(thread_file)
+ remove_output_file(comm_file)
+ remove_output_file(comm_thread_file)
+ remove_output_file(dso_file)
+ remove_output_file(symbol_file)
+ remove_output_file(branch_type_file)
+ remove_output_file(sample_file)
+ if perf_db_export_calls:
+ remove_output_file(call_path_file)
+ remove_output_file(call_file)
+ os.rmdir(output_dir_name)
+ print datetime.datetime.today(), "Adding primary keys"
+ do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
+ if perf_db_export_calls:
+ do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
+
+ print datetime.datetime.today(), "Adding foreign keys"
+ do_query(query, 'ALTER TABLE threads '
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
+ 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
+ do_query(query, 'ALTER TABLE comm_threads '
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)')
+ do_query(query, 'ALTER TABLE dsos '
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id)')
+ do_query(query, 'ALTER TABLE symbols '
+ 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id)')
+ do_query(query, 'ALTER TABLE samples '
+ 'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES selected_events (id),'
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id),'
+ 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
+ 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
+ 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
+ if perf_db_export_calls:
+ do_query(query, 'ALTER TABLE call_paths '
+ 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),'
+ 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)')
+ do_query(query, 'ALTER TABLE calls '
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),'
+ 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),'
+ 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),'
+ 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
+ do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
+
+ if (unhandled_count):
+ print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
+ print datetime.datetime.today(), "Done"
+
+def trace_unhandled(event_name, context, event_fields_dict):
+ global unhandled_count
+ unhandled_count += 1
+
+def sched__sched_switch(*x):
+ pass
+
+def evsel_table(evsel_id, evsel_name, *x):
+ n = len(evsel_name)
+ fmt = "!hiqi" + str(n) + "s"
+ value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
+ evsel_file.write(value)
+
+def machine_table(machine_id, pid, root_dir, *x):
+ n = len(root_dir)
+ fmt = "!hiqiii" + str(n) + "s"
+ value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
+ machine_file.write(value)
+
+def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
+ value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
+ thread_file.write(value)
+
+def comm_table(comm_id, comm_str, *x):
+ n = len(comm_str)
+ fmt = "!hiqi" + str(n) + "s"
+ value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
+ comm_file.write(value)
+
+def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
+ fmt = "!hiqiqiq"
+ value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id)
+ comm_thread_file.write(value)
+
+def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
+ n1 = len(short_name)
+ n2 = len(long_name)
+ n3 = len(build_id)
+ fmt = "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s"
+ value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id)
+ dso_file.write(value)
+
+def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
+ n = len(symbol_name)
+ fmt = "!hiqiqiqiqiii" + str(n) + "s"
+ value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
+ symbol_file.write(value)
+
+def branch_type_table(branch_type, name, *x):
+ n = len(name)
+ fmt = "!hiii" + str(n) + "s"
+ value = struct.pack(fmt, 2, 4, branch_type, n, name)
+ branch_type_file.write(value)
+
+def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x):
+ if branches:
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx)
+ else:
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
+ sample_file.write(value)
+
+def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
+ fmt = "!hiqiqiqiq"
+ value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
+ call_path_file.write(value)
+
+def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x):
+ fmt = "!hiqiqiqiqiqiqiqiqiqiqii"
+ value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags)
+ call_file.write(value)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 6f8b01bc6033..4b7d9ab0f049 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -6,6 +6,7 @@
#include <unistd.h>
#include <string.h>
#include "builtin.h"
+#include "hist.h"
#include "intlist.h"
#include "tests.h"
#include "debug.h"
@@ -84,7 +85,7 @@ static struct test {
.func = test__hists_link,
},
{
- .desc = "Try 'use perf' in python, checking link problems",
+ .desc = "Try 'import perf' in python, checking link problems",
.func = test__python_use,
},
{
@@ -154,6 +155,18 @@ static struct test {
.func = test__hists_cumulate,
},
{
+ .desc = "Test tracking with sched_switch",
+ .func = test__switch_tracking,
+ },
+ {
+ .desc = "Filter fds with revents mask in a fdarray",
+ .func = test__fdarray__filter,
+ },
+ {
+ .desc = "Add fd to a fdarray, making it autogrow",
+ .func = test__fdarray__add,
+ },
+ {
.func = NULL,
},
};
@@ -185,9 +198,11 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
static int run_test(struct test *test)
{
int status, err = -1, child = fork();
+ char sbuf[STRERR_BUFSIZE];
if (child < 0) {
- pr_err("failed to fork test: %s\n", strerror(errno));
+ pr_err("failed to fork test: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -1;
}
@@ -288,6 +303,10 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_END()
};
struct intlist *skiplist = NULL;
+ int ret = hists__init();
+
+ if (ret < 0)
+ return ret;
argc = parse_options(argc, argv, test_options, test_usage, 0);
if (argc >= 1 && !strcmp(argv[0], "list"))
@@ -297,7 +316,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
symbol_conf.sort_by_name = true;
symbol_conf.try_vmlinux_path = true;
- if (symbol__init() < 0)
+ if (symbol__init(NULL) < 0)
return -1;
if (skip != NULL)
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
index 67f2d6323558..f671ec37a7c4 100644
--- a/tools/perf/tests/code-reading.c
+++ b/tools/perf/tests/code-reading.c
@@ -133,8 +133,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf,
}
static int read_object_code(u64 addr, size_t len, u8 cpumode,
- struct thread *thread, struct machine *machine,
- struct state *state)
+ struct thread *thread, struct state *state)
{
struct addr_location al;
unsigned char buf1[BUFSZ];
@@ -145,8 +144,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr,
- &al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, addr, &al);
if (!al.map || !al.map->dso) {
pr_debug("thread__find_addr_map failed\n");
return -1;
@@ -170,8 +168,8 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
len = al.map->end - addr;
/* Read the object code using perf */
- ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1,
- len);
+ ret_len = dso__data_read_offset(al.map->dso, thread->mg->machine,
+ al.addr, buf1, len);
if (ret_len != len) {
pr_debug("dso__data_read_offset failed\n");
return -1;
@@ -264,8 +262,7 @@ static int process_sample_event(struct machine *machine,
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- return read_object_code(sample.ip, READLEN, cpumode, thread, machine,
- state);
+ return read_object_code(sample.ip, READLEN, cpumode, thread, state);
}
static int process_event(struct machine *machine, struct perf_evlist *evlist,
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 96adb730b744..ab28cca2cb97 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -9,6 +9,7 @@
#include "perf_regs.h"
#include "map.h"
#include "thread.h"
+#include "callchain.h"
static int mmap_handler(struct perf_tool *tool __maybe_unused,
union perf_event *event,
@@ -58,7 +59,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
}
__attribute__ ((noinline))
-static int unwind_thread(struct thread *thread, struct machine *machine)
+static int unwind_thread(struct thread *thread)
{
struct perf_sample sample;
unsigned long cnt = 0;
@@ -71,7 +72,7 @@ static int unwind_thread(struct thread *thread, struct machine *machine)
goto out;
}
- err = unwind__get_entries(unwind_entry, &cnt, machine, thread,
+ err = unwind__get_entries(unwind_entry, &cnt, thread,
&sample, MAX_STACK);
if (err)
pr_debug("unwind failed\n");
@@ -88,21 +89,21 @@ static int unwind_thread(struct thread *thread, struct machine *machine)
}
__attribute__ ((noinline))
-static int krava_3(struct thread *thread, struct machine *machine)
+static int krava_3(struct thread *thread)
{
- return unwind_thread(thread, machine);
+ return unwind_thread(thread);
}
__attribute__ ((noinline))
-static int krava_2(struct thread *thread, struct machine *machine)
+static int krava_2(struct thread *thread)
{
- return krava_3(thread, machine);
+ return krava_3(thread);
}
__attribute__ ((noinline))
-static int krava_1(struct thread *thread, struct machine *machine)
+static int krava_1(struct thread *thread)
{
- return krava_2(thread, machine);
+ return krava_2(thread);
}
int test__dwarf_unwind(void)
@@ -120,6 +121,8 @@ int test__dwarf_unwind(void)
return -1;
}
+ callchain_param.record_mode = CALLCHAIN_DWARF;
+
if (init_live_machine(machine)) {
pr_err("Could not init machine\n");
goto out;
@@ -134,7 +137,7 @@ int test__dwarf_unwind(void)
goto out;
}
- err = krava_1(thread, machine);
+ err = krava_1(thread);
out:
machine__delete_threads(machine);
diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c
new file mode 100644
index 000000000000..d24b837951d4
--- /dev/null
+++ b/tools/perf/tests/fdarray.c
@@ -0,0 +1,174 @@
+#include <api/fd/array.h>
+#include "util/debug.h"
+#include "tests/tests.h"
+
+static void fdarray__init_revents(struct fdarray *fda, short revents)
+{
+ int fd;
+
+ fda->nr = fda->nr_alloc;
+
+ for (fd = 0; fd < fda->nr; ++fd) {
+ fda->entries[fd].fd = fda->nr - fd;
+ fda->entries[fd].revents = revents;
+ }
+}
+
+static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE *fp)
+{
+ int printed = 0;
+
+ if (!verbose)
+ return 0;
+
+ printed += fprintf(fp, "\n%s: ", prefix);
+ return printed + fdarray__fprintf(fda, fp);
+}
+
+int test__fdarray__filter(void)
+{
+ int nr_fds, expected_fd[2], fd, err = TEST_FAIL;
+ struct fdarray *fda = fdarray__new(5, 5);
+
+ if (fda == NULL) {
+ pr_debug("\nfdarray__new() failed!");
+ goto out;
+ }
+
+ fdarray__init_revents(fda, POLLIN);
+ nr_fds = fdarray__filter(fda, POLLHUP, NULL);
+ if (nr_fds != fda->nr_alloc) {
+ pr_debug("\nfdarray__filter()=%d != %d shouldn't have filtered anything",
+ nr_fds, fda->nr_alloc);
+ goto out_delete;
+ }
+
+ fdarray__init_revents(fda, POLLHUP);
+ nr_fds = fdarray__filter(fda, POLLHUP, NULL);
+ if (nr_fds != 0) {
+ pr_debug("\nfdarray__filter()=%d != %d, should have filtered all fds",
+ nr_fds, fda->nr_alloc);
+ goto out_delete;
+ }
+
+ fdarray__init_revents(fda, POLLHUP);
+ fda->entries[2].revents = POLLIN;
+ expected_fd[0] = fda->entries[2].fd;
+
+ pr_debug("\nfiltering all but fda->entries[2]:");
+ fdarray__fprintf_prefix(fda, "before", stderr);
+ nr_fds = fdarray__filter(fda, POLLHUP, NULL);
+ fdarray__fprintf_prefix(fda, " after", stderr);
+ if (nr_fds != 1) {
+ pr_debug("\nfdarray__filter()=%d != 1, should have left just one event", nr_fds);
+ goto out_delete;
+ }
+
+ if (fda->entries[0].fd != expected_fd[0]) {
+ pr_debug("\nfda->entries[0].fd=%d != %d\n",
+ fda->entries[0].fd, expected_fd[0]);
+ goto out_delete;
+ }
+
+ fdarray__init_revents(fda, POLLHUP);
+ fda->entries[0].revents = POLLIN;
+ expected_fd[0] = fda->entries[0].fd;
+ fda->entries[3].revents = POLLIN;
+ expected_fd[1] = fda->entries[3].fd;
+
+ pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):");
+ fdarray__fprintf_prefix(fda, "before", stderr);
+ nr_fds = fdarray__filter(fda, POLLHUP, NULL);
+ fdarray__fprintf_prefix(fda, " after", stderr);
+ if (nr_fds != 2) {
+ pr_debug("\nfdarray__filter()=%d != 2, should have left just two events",
+ nr_fds);
+ goto out_delete;
+ }
+
+ for (fd = 0; fd < 2; ++fd) {
+ if (fda->entries[fd].fd != expected_fd[fd]) {
+ pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd,
+ fda->entries[fd].fd, expected_fd[fd]);
+ goto out_delete;
+ }
+ }
+
+ pr_debug("\n");
+
+ err = 0;
+out_delete:
+ fdarray__delete(fda);
+out:
+ return err;
+}
+
+int test__fdarray__add(void)
+{
+ int err = TEST_FAIL;
+ struct fdarray *fda = fdarray__new(2, 2);
+
+ if (fda == NULL) {
+ pr_debug("\nfdarray__new() failed!");
+ goto out;
+ }
+
+#define FDA_CHECK(_idx, _fd, _revents) \
+ if (fda->entries[_idx].fd != _fd) { \
+ pr_debug("\n%d: fda->entries[%d](%d) != %d!", \
+ __LINE__, _idx, fda->entries[1].fd, _fd); \
+ goto out_delete; \
+ } \
+ if (fda->entries[_idx].events != (_revents)) { \
+ pr_debug("\n%d: fda->entries[%d].revents(%d) != %d!", \
+ __LINE__, _idx, fda->entries[_idx].fd, _revents); \
+ goto out_delete; \
+ }
+
+#define FDA_ADD(_idx, _fd, _revents, _nr) \
+ if (fdarray__add(fda, _fd, _revents) < 0) { \
+ pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \
+ __LINE__,_fd, _revents); \
+ goto out_delete; \
+ } \
+ if (fda->nr != _nr) { \
+ pr_debug("\n%d: fdarray__add(fda, %d, %d)=%d != %d", \
+ __LINE__,_fd, _revents, fda->nr, _nr); \
+ goto out_delete; \
+ } \
+ FDA_CHECK(_idx, _fd, _revents)
+
+ FDA_ADD(0, 1, POLLIN, 1);
+ FDA_ADD(1, 2, POLLERR, 2);
+
+ fdarray__fprintf_prefix(fda, "before growing array", stderr);
+
+ FDA_ADD(2, 35, POLLHUP, 3);
+
+ if (fda->entries == NULL) {
+ pr_debug("\nfdarray__add(fda, 35, POLLHUP) should have allocated fda->pollfd!");
+ goto out_delete;
+ }
+
+ fdarray__fprintf_prefix(fda, "after 3rd add", stderr);
+
+ FDA_ADD(3, 88, POLLIN | POLLOUT, 4);
+
+ fdarray__fprintf_prefix(fda, "after 4th add", stderr);
+
+ FDA_CHECK(0, 1, POLLIN);
+ FDA_CHECK(1, 2, POLLERR);
+ FDA_CHECK(2, 35, POLLHUP);
+ FDA_CHECK(3, 88, POLLIN | POLLOUT);
+
+#undef FDA_ADD
+#undef FDA_CHECK
+
+ pr_debug("\n");
+
+ err = 0;
+out_delete:
+ fdarray__delete(fda);
+out:
+ return err;
+}
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 0ac240db2e24..614d5c4978ab 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -245,7 +245,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
static int test1(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
/*
* expected output:
*
@@ -295,7 +295,7 @@ out:
static int test2(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
/*
* expected output:
*
@@ -442,7 +442,7 @@ out:
static int test3(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
/*
* expected output:
*
@@ -498,7 +498,7 @@ out:
static int test4(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
/*
* expected output:
*
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 821f581fd930..74f257a81265 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -43,7 +43,7 @@ static struct sample fake_samples[] = {
};
static int add_hist_entries(struct perf_evlist *evlist,
- struct machine *machine __maybe_unused)
+ struct machine *machine)
{
struct perf_evsel *evsel;
struct addr_location al;
@@ -66,11 +66,12 @@ static int add_hist_entries(struct perf_evlist *evlist,
.ops = &hist_iter_normal,
.hide_unresolved = false,
};
+ struct hists *hists = evsel__hists(evsel);
/* make sure it has no filter at first */
- evsel->hists.thread_filter = NULL;
- evsel->hists.dso_filter = NULL;
- evsel->hists.symbol_filter_str = NULL;
+ hists->thread_filter = NULL;
+ hists->dso_filter = NULL;
+ hists->symbol_filter_str = NULL;
sample.pid = fake_samples[i].pid;
sample.tid = fake_samples[i].pid;
@@ -134,7 +135,7 @@ int test__hists_filter(void)
goto out;
evlist__for_each(evlist, evsel) {
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
hists__collapse_resort(hists, NULL);
hists__output_resort(hists);
@@ -160,7 +161,7 @@ int test__hists_filter(void)
hists->stats.total_non_filtered_period);
/* now applying thread filter for 'bash' */
- evsel->hists.thread_filter = fake_samples[9].thread;
+ hists->thread_filter = fake_samples[9].thread;
hists__filter_by_thread(hists);
if (verbose > 2) {
@@ -185,11 +186,11 @@ int test__hists_filter(void)
hists->stats.total_non_filtered_period == 400);
/* remove thread filter first */
- evsel->hists.thread_filter = NULL;
+ hists->thread_filter = NULL;
hists__filter_by_thread(hists);
/* now applying dso filter for 'kernel' */
- evsel->hists.dso_filter = fake_samples[0].map->dso;
+ hists->dso_filter = fake_samples[0].map->dso;
hists__filter_by_dso(hists);
if (verbose > 2) {
@@ -214,7 +215,7 @@ int test__hists_filter(void)
hists->stats.total_non_filtered_period == 300);
/* remove dso filter first */
- evsel->hists.dso_filter = NULL;
+ hists->dso_filter = NULL;
hists__filter_by_dso(hists);
/*
@@ -224,7 +225,7 @@ int test__hists_filter(void)
* be counted as a separate entry but the sample count and
* total period will be remained.
*/
- evsel->hists.symbol_filter_str = "main";
+ hists->symbol_filter_str = "main";
hists__filter_by_symbol(hists);
if (verbose > 2) {
@@ -249,8 +250,8 @@ int test__hists_filter(void)
hists->stats.total_non_filtered_period == 300);
/* now applying all filters at once. */
- evsel->hists.thread_filter = fake_samples[1].thread;
- evsel->hists.dso_filter = fake_samples[1].map->dso;
+ hists->thread_filter = fake_samples[1].thread;
+ hists->dso_filter = fake_samples[1].map->dso;
hists__filter_by_thread(hists);
hists__filter_by_dso(hists);
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index d4b34b0f50a2..278ba8344c23 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -73,6 +73,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
* "bash [libc] malloc" so total 9 entries will be in the tree.
*/
evlist__for_each(evlist, evsel) {
+ struct hists *hists = evsel__hists(evsel);
+
for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
const union perf_event event = {
.header = {
@@ -87,7 +89,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
&sample) < 0)
goto out;
- he = __hists__add_entry(&evsel->hists, &al, NULL,
+ he = __hists__add_entry(hists, &al, NULL,
NULL, NULL, 1, 1, 0, true);
if (he == NULL)
goto out;
@@ -111,7 +113,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
&sample) < 0)
goto out;
- he = __hists__add_entry(&evsel->hists, &al, NULL,
+ he = __hists__add_entry(hists, &al, NULL,
NULL, NULL, 1, 1, 0, true);
if (he == NULL)
goto out;
@@ -271,6 +273,7 @@ static int validate_link(struct hists *leader, struct hists *other)
int test__hists_link(void)
{
int err = -1;
+ struct hists *hists, *first_hists;
struct machines machines;
struct machine *machine = NULL;
struct perf_evsel *evsel, *first;
@@ -306,24 +309,28 @@ int test__hists_link(void)
goto out;
evlist__for_each(evlist, evsel) {
- hists__collapse_resort(&evsel->hists, NULL);
+ hists = evsel__hists(evsel);
+ hists__collapse_resort(hists, NULL);
if (verbose > 2)
- print_hists_in(&evsel->hists);
+ print_hists_in(hists);
}
first = perf_evlist__first(evlist);
evsel = perf_evlist__last(evlist);
+ first_hists = evsel__hists(first);
+ hists = evsel__hists(evsel);
+
/* match common entries */
- hists__match(&first->hists, &evsel->hists);
- err = validate_match(&first->hists, &evsel->hists);
+ hists__match(first_hists, hists);
+ err = validate_match(first_hists, hists);
if (err)
goto out;
/* link common and/or dummy entries */
- hists__link(&first->hists, &evsel->hists);
- err = validate_link(&first->hists, &evsel->hists);
+ hists__link(first_hists, hists);
+ err = validate_link(first_hists, hists);
if (err)
goto out;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index e3bbd6c54c1b..a748f2be1222 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -122,7 +122,7 @@ typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
static int test1(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
struct rb_root *root;
struct rb_node *node;
@@ -159,7 +159,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine)
print_hists_out(hists);
}
- root = &evsel->hists.entries;
+ root = &hists->entries;
node = rb_first(root);
he = rb_entry(node, struct hist_entry, rb_node);
TEST_ASSERT_VAL("Invalid hist entry",
@@ -224,7 +224,7 @@ out:
static int test2(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
struct rb_root *root;
struct rb_node *node;
@@ -259,7 +259,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine)
print_hists_out(hists);
}
- root = &evsel->hists.entries;
+ root = &hists->entries;
node = rb_first(root);
he = rb_entry(node, struct hist_entry, rb_node);
TEST_ASSERT_VAL("Invalid hist entry",
@@ -280,7 +280,7 @@ out:
static int test3(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
struct rb_root *root;
struct rb_node *node;
@@ -313,7 +313,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine)
print_hists_out(hists);
}
- root = &evsel->hists.entries;
+ root = &hists->entries;
node = rb_first(root);
he = rb_entry(node, struct hist_entry, rb_node);
TEST_ASSERT_VAL("Invalid hist entry",
@@ -354,7 +354,7 @@ out:
static int test4(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
struct rb_root *root;
struct rb_node *node;
@@ -391,7 +391,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
print_hists_out(hists);
}
- root = &evsel->hists.entries;
+ root = &hists->entries;
node = rb_first(root);
he = rb_entry(node, struct hist_entry, rb_node);
TEST_ASSERT_VAL("Invalid hist entry",
@@ -456,7 +456,7 @@ out:
static int test5(struct perf_evsel *evsel, struct machine *machine)
{
int err;
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
struct rb_root *root;
struct rb_node *node;
@@ -494,7 +494,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine)
print_hists_out(hists);
}
- root = &evsel->hists.entries;
+ root = &hists->entries;
node = rb_first(root);
he = rb_entry(node, struct hist_entry, rb_node);
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 142263492f6f..9b9622a33932 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -31,6 +31,7 @@ int test__basic_mmap(void)
unsigned int nr_events[nsyscalls],
expected_nr_events[nsyscalls], i, j;
struct perf_evsel *evsels[nsyscalls], *evsel;
+ char sbuf[STRERR_BUFSIZE];
threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
@@ -49,7 +50,7 @@ int test__basic_mmap(void)
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
- cpus->map[0], strerror(errno));
+ cpus->map[0], strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_free_cpus;
}
@@ -79,7 +80,7 @@ int test__basic_mmap(void)
if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -89,7 +90,7 @@ int test__basic_mmap(void)
if (perf_evlist__mmap(evlist, 128, true) < 0) {
pr_debug("failed to mmap events: %d (%s)\n", errno,
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
index 4a456fef66ca..2113f1c8611f 100644
--- a/tools/perf/tests/mmap-thread-lookup.c
+++ b/tools/perf/tests/mmap-thread-lookup.c
@@ -187,7 +187,7 @@ static int mmap_events(synth_cb synth)
pr_debug("looking for map %p\n", td->map);
- thread__find_addr_map(thread, machine,
+ thread__find_addr_map(thread,
PERF_RECORD_MISC_USER, MAP__FUNCTION,
(unsigned long) (td->map + 1), &al);
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c
index 5fecdbd2f5f7..8fa82d1700c7 100644
--- a/tools/perf/tests/open-syscall-all-cpus.c
+++ b/tools/perf/tests/open-syscall-all-cpus.c
@@ -12,6 +12,7 @@ int test__open_syscall_event_on_all_cpus(void)
unsigned int nr_open_calls = 111, i;
cpu_set_t cpu_set;
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
+ char sbuf[STRERR_BUFSIZE];
if (threads == NULL) {
pr_debug("thread_map__new\n");
@@ -35,7 +36,7 @@ int test__open_syscall_event_on_all_cpus(void)
if (perf_evsel__open(evsel, cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_evsel_delete;
}
@@ -56,7 +57,7 @@ int test__open_syscall_event_on_all_cpus(void)
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
cpus->map[cpu],
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_close_fd;
}
for (i = 0; i < ncalls; ++i) {
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c
index 0785b64ffd6c..127dcae0b760 100644
--- a/tools/perf/tests/open-syscall-tp-fields.c
+++ b/tools/perf/tests/open-syscall-tp-fields.c
@@ -22,6 +22,7 @@ int test__syscall_open_tp_fields(void)
struct perf_evlist *evlist = perf_evlist__new();
struct perf_evsel *evsel;
int err = -1, i, nr_events = 0, nr_polls = 0;
+ char sbuf[STRERR_BUFSIZE];
if (evlist == NULL) {
pr_debug("%s: perf_evlist__new\n", __func__);
@@ -48,13 +49,15 @@ int test__syscall_open_tp_fields(void)
err = perf_evlist__open(evlist);
if (err < 0) {
- pr_debug("perf_evlist__open: %s\n", strerror(errno));
+ pr_debug("perf_evlist__open: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
err = perf_evlist__mmap(evlist, UINT_MAX, false);
if (err < 0) {
- pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
+ pr_debug("perf_evlist__mmap: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -102,7 +105,7 @@ int test__syscall_open_tp_fields(void)
}
if (nr_events == before)
- poll(evlist->pollfd, evlist->nr_fds, 10);
+ perf_evlist__poll(evlist, 10);
if (++nr_polls > 5) {
pr_debug("%s: no events!\n", __func__);
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c
index c1dc7d25f38c..a33b2daae40f 100644
--- a/tools/perf/tests/open-syscall.c
+++ b/tools/perf/tests/open-syscall.c
@@ -9,6 +9,7 @@ int test__open_syscall_event(void)
struct perf_evsel *evsel;
unsigned int nr_open_calls = 111, i;
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
+ char sbuf[STRERR_BUFSIZE];
if (threads == NULL) {
pr_debug("thread_map__new\n");
@@ -24,7 +25,7 @@ int test__open_syscall_event(void)
if (perf_evsel__open_per_thread(evsel, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_evsel_delete;
}
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 5941927a4b7f..7f2f51f93619 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -457,6 +457,36 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
return 0;
}
+
+static int test__checkevent_pmu_events_mix(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
+
+ /* pmu-event:u */
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong exclude_user",
+ !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel",
+ evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+
+ /* cpu/pmu-event/u*/
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong exclude_user",
+ !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel",
+ evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+
+ return 0;
+}
+
static int test__checkterms_simple(struct list_head *terms)
{
struct parse_events_term *term;
@@ -1554,6 +1584,12 @@ static int test_pmu_events(void)
e.check = test__checkevent_pmu_events;
ret = test_event(&e);
+ if (ret)
+ break;
+ snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
+ e.name = name;
+ e.check = test__checkevent_pmu_events_mix;
+ ret = test_event(&e);
#undef MAX_NAME
}
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c
index aca1a83dd13a..7a228a2a070b 100644
--- a/tools/perf/tests/perf-record.c
+++ b/tools/perf/tests/perf-record.c
@@ -59,6 +59,7 @@ int test__PERF_RECORD(void)
int err = -1, errs = 0, i, wakeups = 0;
u32 cpu;
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
+ char sbuf[STRERR_BUFSIZE];
if (evlist == NULL || argv == NULL) {
pr_debug("Not enough memory to create evlist\n");
@@ -100,7 +101,8 @@ int test__PERF_RECORD(void)
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
if (err < 0) {
- pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
+ pr_debug("sched__get_first_possible_cpu: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -110,7 +112,8 @@ int test__PERF_RECORD(void)
* So that we can check perf_sample.cpu on all the samples.
*/
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
- pr_debug("sched_setaffinity: %s\n", strerror(errno));
+ pr_debug("sched_setaffinity: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -120,7 +123,8 @@ int test__PERF_RECORD(void)
*/
err = perf_evlist__open(evlist);
if (err < 0) {
- pr_debug("perf_evlist__open: %s\n", strerror(errno));
+ pr_debug("perf_evlist__open: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -131,7 +135,8 @@ int test__PERF_RECORD(void)
*/
err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
if (err < 0) {
- pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
+ pr_debug("perf_evlist__mmap: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -263,7 +268,7 @@ int test__PERF_RECORD(void)
* perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
*/
if (total_events == before && false)
- poll(evlist->pollfd, evlist->nr_fds, -1);
+ perf_evlist__poll(evlist, -1);
sleep(1);
if (++wakeups > 5) {
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index 12b322fa3475..eeb68bb1972d 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -152,7 +152,7 @@ int test__pmu(void)
if (ret)
break;
- ret = perf_pmu__config_terms(&formats, &attr, terms);
+ ret = perf_pmu__config_terms(&formats, &attr, terms, false);
if (ret)
break;
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c
index c04d1f268576..d31f2c4d9f64 100644
--- a/tools/perf/tests/rdpmc.c
+++ b/tools/perf/tests/rdpmc.c
@@ -100,6 +100,7 @@ static int __test__rdpmc(void)
};
u64 delta_sum = 0;
struct sigaction sa;
+ char sbuf[STRERR_BUFSIZE];
sigfillset(&sa.sa_mask);
sa.sa_sigaction = segfault_handler;
@@ -109,14 +110,15 @@ static int __test__rdpmc(void)
perf_event_open_cloexec_flag());
if (fd < 0) {
pr_err("Error: sys_perf_event_open() syscall returned "
- "with %d (%s)\n", fd, strerror(errno));
+ "with %d (%s)\n", fd,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -1;
}
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == (void *)(-1)) {
pr_err("Error: mmap() syscall returned with (%s)\n",
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_close;
}
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index ca292f9a4ae2..4908c648a597 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -126,16 +126,28 @@ static bool samples_same(const struct perf_sample *s1,
if (type & PERF_SAMPLE_TRANSACTION)
COMP(transaction);
+ if (type & PERF_SAMPLE_REGS_INTR) {
+ size_t sz = hweight_long(s1->intr_regs.mask) * sizeof(u64);
+
+ COMP(intr_regs.mask);
+ COMP(intr_regs.abi);
+ if (s1->intr_regs.abi &&
+ (!s1->intr_regs.regs || !s2->intr_regs.regs ||
+ memcmp(s1->intr_regs.regs, s2->intr_regs.regs, sz))) {
+ pr_debug("Samples differ at 'intr_regs'\n");
+ return false;
+ }
+ }
+
return true;
}
-static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
+static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
{
struct perf_evsel evsel = {
.needs_swap = false,
.attr = {
.sample_type = sample_type,
- .sample_regs_user = sample_regs_user,
.read_format = read_format,
},
};
@@ -154,7 +166,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
/* 1 branch_entry */
.data = {1, 211, 212, 213},
};
- u64 user_regs[64];
+ u64 regs[64];
const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL};
const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL};
struct perf_sample sample = {
@@ -176,8 +188,8 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
.branch_stack = &branch_stack.branch_stack,
.user_regs = {
.abi = PERF_SAMPLE_REGS_ABI_64,
- .mask = sample_regs_user,
- .regs = user_regs,
+ .mask = sample_regs,
+ .regs = regs,
},
.user_stack = {
.size = sizeof(data),
@@ -187,14 +199,25 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
.time_enabled = 0x030a59d664fca7deULL,
.time_running = 0x011b6ae553eb98edULL,
},
+ .intr_regs = {
+ .abi = PERF_SAMPLE_REGS_ABI_64,
+ .mask = sample_regs,
+ .regs = regs,
+ },
};
struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},};
struct perf_sample sample_out;
size_t i, sz, bufsz;
int err, ret = -1;
- for (i = 0; i < sizeof(user_regs); i++)
- *(i + (u8 *)user_regs) = i & 0xfe;
+ if (sample_type & PERF_SAMPLE_REGS_USER)
+ evsel.attr.sample_regs_user = sample_regs;
+
+ if (sample_type & PERF_SAMPLE_REGS_INTR)
+ evsel.attr.sample_regs_intr = sample_regs;
+
+ for (i = 0; i < sizeof(regs); i++)
+ *(i + (u8 *)regs) = i & 0xfe;
if (read_format & PERF_FORMAT_GROUP) {
sample.read.group.nr = 4;
@@ -271,7 +294,7 @@ int test__sample_parsing(void)
{
const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15};
u64 sample_type;
- u64 sample_regs_user;
+ u64 sample_regs;
size_t i;
int err;
@@ -280,7 +303,7 @@ int test__sample_parsing(void)
* were added. Please actually update the test rather than just change
* the condition below.
*/
- if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) {
+ if (PERF_SAMPLE_MAX > PERF_SAMPLE_REGS_INTR << 1) {
pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n");
return -1;
}
@@ -297,22 +320,24 @@ int test__sample_parsing(void)
}
continue;
}
+ sample_regs = 0;
if (sample_type == PERF_SAMPLE_REGS_USER)
- sample_regs_user = 0x3fff;
- else
- sample_regs_user = 0;
+ sample_regs = 0x3fff;
+
+ if (sample_type == PERF_SAMPLE_REGS_INTR)
+ sample_regs = 0xff0fff;
- err = do_test(sample_type, sample_regs_user, 0);
+ err = do_test(sample_type, sample_regs, 0);
if (err)
return err;
}
/* Test all sample format bits together */
sample_type = PERF_SAMPLE_MAX - 1;
- sample_regs_user = 0x3fff;
+ sample_regs = 0x3fff; /* shared yb intr and user regs */
for (i = 0; i < ARRAY_SIZE(rf); i++) {
- err = do_test(sample_type, sample_regs_user, rf[i]);
+ err = do_test(sample_type, sample_regs, rf[i]);
if (err)
return err;
}
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c
index 983d6b8562a8..1aa21c90731b 100644
--- a/tools/perf/tests/sw-clock.c
+++ b/tools/perf/tests/sw-clock.c
@@ -22,6 +22,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
volatile int tmp = 0;
u64 total_periods = 0;
int nr_samples = 0;
+ char sbuf[STRERR_BUFSIZE];
union perf_event *event;
struct perf_evsel *evsel;
struct perf_evlist *evlist;
@@ -62,14 +63,15 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
err = -errno;
pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n",
- strerror(errno), knob, (u64)attr.sample_freq);
+ strerror_r(errno, sbuf, sizeof(sbuf)),
+ knob, (u64)attr.sample_freq);
goto out_delete_evlist;
}
err = perf_evlist__mmap(evlist, 128, true);
if (err < 0) {
pr_debug("failed to mmap event: %d (%s)\n", errno,
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
new file mode 100644
index 000000000000..cc68648c7c55
--- /dev/null
+++ b/tools/perf/tests/switch-tracking.c
@@ -0,0 +1,572 @@
+#include <sys/time.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "tests.h"
+
+static int spin_sleep(void)
+{
+ struct timeval start, now, diff, maxtime;
+ struct timespec ts;
+ int err, i;
+
+ maxtime.tv_sec = 0;
+ maxtime.tv_usec = 50000;
+
+ err = gettimeofday(&start, NULL);
+ if (err)
+ return err;
+
+ /* Spin for 50ms */
+ while (1) {
+ for (i = 0; i < 1000; i++)
+ barrier();
+
+ err = gettimeofday(&now, NULL);
+ if (err)
+ return err;
+
+ timersub(&now, &start, &diff);
+ if (timercmp(&diff, &maxtime, > /* For checkpatch */))
+ break;
+ }
+
+ ts.tv_nsec = 50 * 1000 * 1000;
+ ts.tv_sec = 0;
+
+ /* Sleep for 50ms */
+ err = nanosleep(&ts, NULL);
+ if (err == EINTR)
+ err = 0;
+
+ return err;
+}
+
+struct switch_tracking {
+ struct perf_evsel *switch_evsel;
+ struct perf_evsel *cycles_evsel;
+ pid_t *tids;
+ int nr_tids;
+ int comm_seen[4];
+ int cycles_before_comm_1;
+ int cycles_between_comm_2_and_comm_3;
+ int cycles_after_comm_4;
+};
+
+static int check_comm(struct switch_tracking *switch_tracking,
+ union perf_event *event, const char *comm, int nr)
+{
+ if (event->header.type == PERF_RECORD_COMM &&
+ (pid_t)event->comm.pid == getpid() &&
+ (pid_t)event->comm.tid == getpid() &&
+ strcmp(event->comm.comm, comm) == 0) {
+ if (switch_tracking->comm_seen[nr]) {
+ pr_debug("Duplicate comm event\n");
+ return -1;
+ }
+ switch_tracking->comm_seen[nr] = 1;
+ pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
+ return 1;
+ }
+ return 0;
+}
+
+static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
+{
+ int i, nr = cpu + 1;
+
+ if (cpu < 0)
+ return -1;
+
+ if (!switch_tracking->tids) {
+ switch_tracking->tids = calloc(nr, sizeof(pid_t));
+ if (!switch_tracking->tids)
+ return -1;
+ for (i = 0; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ if (cpu >= switch_tracking->nr_tids) {
+ void *addr;
+
+ addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
+ if (!addr)
+ return -1;
+ switch_tracking->tids = addr;
+ for (i = switch_tracking->nr_tids; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int process_sample_event(struct perf_evlist *evlist,
+ union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ struct perf_sample sample;
+ struct perf_evsel *evsel;
+ pid_t next_tid, prev_tid;
+ int cpu, err;
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ evsel = perf_evlist__id2evsel(evlist, sample.id);
+ if (evsel == switch_tracking->switch_evsel) {
+ next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
+ prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
+ cpu = sample.cpu;
+ pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
+ cpu, prev_tid, next_tid);
+ err = check_cpu(switch_tracking, cpu);
+ if (err)
+ return err;
+ /*
+ * Check for no missing sched_switch events i.e. that the
+ * evsel->system_wide flag has worked.
+ */
+ if (switch_tracking->tids[cpu] != -1 &&
+ switch_tracking->tids[cpu] != prev_tid) {
+ pr_debug("Missing sched_switch events\n");
+ return -1;
+ }
+ switch_tracking->tids[cpu] = next_tid;
+ }
+
+ if (evsel == switch_tracking->cycles_evsel) {
+ pr_debug3("cycles event\n");
+ if (!switch_tracking->comm_seen[0])
+ switch_tracking->cycles_before_comm_1 = 1;
+ if (switch_tracking->comm_seen[1] &&
+ !switch_tracking->comm_seen[2])
+ switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
+ if (switch_tracking->comm_seen[3])
+ switch_tracking->cycles_after_comm_4 = 1;
+ }
+
+ return 0;
+}
+
+static int process_event(struct perf_evlist *evlist, union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ if (event->header.type == PERF_RECORD_SAMPLE)
+ return process_sample_event(evlist, event, switch_tracking);
+
+ if (event->header.type == PERF_RECORD_COMM) {
+ int err, done = 0;
+
+ err = check_comm(switch_tracking, event, "Test COMM 1", 0);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 2", 1);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 3", 2);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 4", 3);
+ if (err < 0)
+ return -1;
+ done += err;
+ if (done != 1) {
+ pr_debug("Unexpected comm event\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct event_node {
+ struct list_head list;
+ union perf_event *event;
+ u64 event_time;
+};
+
+static int add_event(struct perf_evlist *evlist, struct list_head *events,
+ union perf_event *event)
+{
+ struct perf_sample sample;
+ struct event_node *node;
+
+ node = malloc(sizeof(struct event_node));
+ if (!node) {
+ pr_debug("malloc failed\n");
+ return -1;
+ }
+ node->event = event;
+ list_add(&node->list, events);
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ if (!sample.time) {
+ pr_debug("event with no time\n");
+ return -1;
+ }
+
+ node->event_time = sample.time;
+
+ return 0;
+}
+
+static void free_event_nodes(struct list_head *events)
+{
+ struct event_node *node;
+
+ while (!list_empty(events)) {
+ node = list_entry(events->next, struct event_node, list);
+ list_del(&node->list);
+ free(node);
+ }
+}
+
+static int compar(const void *a, const void *b)
+{
+ const struct event_node *nodea = a;
+ const struct event_node *nodeb = b;
+ s64 cmp = nodea->event_time - nodeb->event_time;
+
+ return cmp;
+}
+
+static int process_events(struct perf_evlist *evlist,
+ struct switch_tracking *switch_tracking)
+{
+ union perf_event *event;
+ unsigned pos, cnt = 0;
+ LIST_HEAD(events);
+ struct event_node *events_array, *node;
+ int i, ret;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ cnt += 1;
+ ret = add_event(evlist, &events, event);
+ perf_evlist__mmap_consume(evlist, i);
+ if (ret < 0)
+ goto out_free_nodes;
+ }
+ }
+
+ events_array = calloc(cnt, sizeof(struct event_node));
+ if (!events_array) {
+ pr_debug("calloc failed\n");
+ ret = -1;
+ goto out_free_nodes;
+ }
+
+ pos = 0;
+ list_for_each_entry(node, &events, list)
+ events_array[pos++] = *node;
+
+ qsort(events_array, cnt, sizeof(struct event_node), compar);
+
+ for (pos = 0; pos < cnt; pos++) {
+ ret = process_event(evlist, events_array[pos].event,
+ switch_tracking);
+ if (ret < 0)
+ goto out_free;
+ }
+
+ ret = 0;
+out_free:
+ pr_debug("%u events recorded\n", cnt);
+ free(events_array);
+out_free_nodes:
+ free_event_nodes(&events);
+ return ret;
+}
+
+/**
+ * test__switch_tracking - test using sched_switch and tracking events.
+ *
+ * This function implements a test that checks that sched_switch events and
+ * tracking events can be recorded for a workload (current process) using the
+ * evsel->system_wide and evsel->tracking flags (respectively) with other events
+ * sometimes enabled or disabled.
+ */
+int test__switch_tracking(void)
+{
+ const char *sched_switch = "sched:sched_switch";
+ struct switch_tracking switch_tracking = { .tids = NULL, };
+ struct record_opts opts = {
+ .mmap_pages = UINT_MAX,
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .freq = 4000,
+ .target = {
+ .uses_mmap = true,
+ },
+ };
+ struct thread_map *threads = NULL;
+ struct cpu_map *cpus = NULL;
+ struct perf_evlist *evlist = NULL;
+ struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
+ struct perf_evsel *switch_evsel, *tracking_evsel;
+ const char *comm;
+ int err = -1;
+
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
+ if (!threads) {
+ pr_debug("thread_map__new failed!\n");
+ goto out_err;
+ }
+
+ cpus = cpu_map__new(NULL);
+ if (!cpus) {
+ pr_debug("cpu_map__new failed!\n");
+ goto out_err;
+ }
+
+ evlist = perf_evlist__new();
+ if (!evlist) {
+ pr_debug("perf_evlist__new failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__set_maps(evlist, cpus, threads);
+
+ /* First event */
+ err = parse_events(evlist, "cpu-clock:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ cpu_clocks_evsel = perf_evlist__last(evlist);
+
+ /* Second event */
+ err = parse_events(evlist, "cycles:u");
+ if (err) {
+ pr_debug("Failed to parse event cycles:u\n");
+ goto out_err;
+ }
+
+ cycles_evsel = perf_evlist__last(evlist);
+
+ /* Third event */
+ if (!perf_evlist__can_select_event(evlist, sched_switch)) {
+ fprintf(stderr, " (no sched_switch)");
+ err = 0;
+ goto out;
+ }
+
+ err = parse_events(evlist, sched_switch);
+ if (err) {
+ pr_debug("Failed to parse event %s\n", sched_switch);
+ goto out_err;
+ }
+
+ switch_evsel = perf_evlist__last(evlist);
+
+ perf_evsel__set_sample_bit(switch_evsel, CPU);
+ perf_evsel__set_sample_bit(switch_evsel, TIME);
+
+ switch_evsel->system_wide = true;
+ switch_evsel->no_aux_samples = true;
+ switch_evsel->immediate = true;
+
+ /* Test moving an event to the front */
+ if (cycles_evsel == perf_evlist__first(evlist)) {
+ pr_debug("cycles event already at front");
+ goto out_err;
+ }
+ perf_evlist__to_front(evlist, cycles_evsel);
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Failed to move cycles event to front");
+ goto out_err;
+ }
+
+ perf_evsel__set_sample_bit(cycles_evsel, CPU);
+ perf_evsel__set_sample_bit(cycles_evsel, TIME);
+
+ /* Fourth event */
+ err = parse_events(evlist, "dummy:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ tracking_evsel = perf_evlist__last(evlist);
+
+ perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+ tracking_evsel->attr.freq = 0;
+ tracking_evsel->attr.sample_period = 1;
+
+ perf_evsel__set_sample_bit(tracking_evsel, TIME);
+
+ /* Config events */
+ perf_evlist__config(evlist, &opts);
+
+ /* Check moved event is still at the front */
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Front event no longer at front");
+ goto out_err;
+ }
+
+ /* Check tracking event is tracking */
+ if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
+ pr_debug("Tracking event not tracking\n");
+ goto out_err;
+ }
+
+ /* Check non-tracking events are not tracking */
+ evlist__for_each(evlist, evsel) {
+ if (evsel != tracking_evsel) {
+ if (evsel->attr.mmap || evsel->attr.comm) {
+ pr_debug("Non-tracking event is tracking\n");
+ goto out_err;
+ }
+ }
+ }
+
+ if (perf_evlist__open(evlist) < 0) {
+ fprintf(stderr, " (not supported)");
+ err = 0;
+ goto out;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err) {
+ pr_debug("perf_evlist__mmap failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__enable(evlist);
+
+ err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 1";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__disable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 2";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 3";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__enable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 4";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__disable(evlist);
+
+ switch_tracking.switch_evsel = switch_evsel;
+ switch_tracking.cycles_evsel = cycles_evsel;
+
+ err = process_events(evlist, &switch_tracking);
+
+ zfree(&switch_tracking.tids);
+
+ if (err)
+ goto out_err;
+
+ /* Check all 4 comm events were seen i.e. that evsel->tracking works */
+ if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
+ !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
+ pr_debug("Missing comm events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled */
+ if (!switch_tracking.cycles_before_comm_1) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got disabled */
+ if (switch_tracking.cycles_between_comm_2_and_comm_3) {
+ pr_debug("cycles events even though event was disabled\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled again */
+ if (!switch_tracking.cycles_after_comm_4) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+out:
+ if (evlist) {
+ perf_evlist__disable(evlist);
+ perf_evlist__delete(evlist);
+ } else {
+ cpu_map__delete(cpus);
+ thread_map__delete(threads);
+ }
+
+ return err;
+
+out_err:
+ err = -1;
+ goto out;
+}
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c
index 5ff3db318f12..3a8fedef83bc 100644
--- a/tools/perf/tests/task-exit.c
+++ b/tools/perf/tests/task-exit.c
@@ -42,6 +42,7 @@ int test__task_exit(void)
.uses_mmap = true,
};
const char *argv[] = { "true", NULL };
+ char sbuf[STRERR_BUFSIZE];
signal(SIGCHLD, sig_handler);
@@ -82,13 +83,14 @@ int test__task_exit(void)
err = perf_evlist__open(evlist);
if (err < 0) {
- pr_debug("Couldn't open the evlist: %s\n", strerror(-err));
+ pr_debug("Couldn't open the evlist: %s\n",
+ strerror_r(-err, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
if (perf_evlist__mmap(evlist, 128, true) < 0) {
pr_debug("failed to mmap events: %d (%s)\n", errno,
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
goto out_delete_evlist;
}
@@ -103,7 +105,7 @@ retry:
}
if (!exited || !nr_exit) {
- poll(evlist->pollfd, evlist->nr_fds, -1);
+ perf_evlist__poll(evlist, -1);
goto retry;
}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ed64790a395f..00e776a87a9c 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -48,6 +48,9 @@ int test__mmap_thread_lookup(void);
int test__thread_mg_share(void);
int test__hists_output(void);
int test__hists_cumulate(void);
+int test__switch_tracking(void);
+int test__fdarray__filter(void);
+int test__fdarray__add(void);
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index f0697a3aede0..1e0a2fd80115 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -27,6 +27,7 @@ static struct annotate_browser_opt {
bool hide_src_code,
use_offset,
jump_arrows,
+ show_linenr,
show_nr_jumps;
} annotate_browser__opts = {
.use_offset = true,
@@ -128,7 +129,11 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
if (!*dl->line)
slsmg_write_nstring(" ", width - pcnt_width);
else if (dl->offset == -1) {
- printed = scnprintf(bf, sizeof(bf), "%*s ",
+ if (dl->line_nr && annotate_browser__opts.show_linenr)
+ printed = scnprintf(bf, sizeof(bf), "%-*d ",
+ ab->addr_width + 1, dl->line_nr);
+ else
+ printed = scnprintf(bf, sizeof(bf), "%*s ",
ab->addr_width, " ");
slsmg_write_nstring(bf, printed);
slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
@@ -733,6 +738,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
"o Toggle disassembler output/simplified view\n"
"s Toggle source code view\n"
"/ Search string\n"
+ "k Toggle line numbers\n"
"r Run available scripts\n"
"? Search string backwards\n");
continue;
@@ -741,6 +747,10 @@ static int annotate_browser__run(struct annotate_browser *browser,
script_browse(NULL);
continue;
}
+ case 'k':
+ annotate_browser__opts.show_linenr =
+ !annotate_browser__opts.show_linenr;
+ break;
case 'H':
nd = browser->curr_hot;
break;
@@ -984,6 +994,7 @@ static struct annotate_config {
} annotate__configs[] = {
ANNOTATE_CFG(hide_src_code),
ANNOTATE_CFG(jump_arrows),
+ ANNOTATE_CFG(show_linenr),
ANNOTATE_CFG(show_nr_jumps),
ANNOTATE_CFG(use_offset),
};
diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c
index 89c16b988618..e8278c558d4a 100644
--- a/tools/perf/ui/browsers/header.c
+++ b/tools/perf/ui/browsers/header.c
@@ -1,6 +1,7 @@
#include "util/cache.h"
#include "util/debug.h"
#include "ui/browser.h"
+#include "ui/keysyms.h"
#include "ui/ui.h"
#include "ui/util.h"
#include "ui/libslang.h"
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index a94b11fc5e00..502daff76ceb 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -10,6 +10,7 @@
#include "../../util/pstack.h"
#include "../../util/sort.h"
#include "../../util/util.h"
+#include "../../util/top.h"
#include "../../arch/common.h"
#include "../browser.h"
@@ -34,7 +35,9 @@ struct hist_browser {
extern void hist_browser__init_hpp(void);
-static int hists__browser_title(struct hists *hists, char *bf, size_t size);
+static int hists__browser_title(struct hists *hists,
+ struct hist_browser_timer *hbt,
+ char *bf, size_t size);
static void hist_browser__update_nr_entries(struct hist_browser *hb);
static struct rb_node *hists__filter_entries(struct rb_node *nd,
@@ -224,23 +227,30 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no
}
}
-static void callchain_node__init_have_children(struct callchain_node *node)
+static void callchain_node__init_have_children(struct callchain_node *node,
+ bool has_sibling)
{
struct callchain_list *chain;
- list_for_each_entry(chain, &node->val, list)
+ chain = list_entry(node->val.next, struct callchain_list, list);
+ chain->ms.has_children = has_sibling;
+
+ if (!list_empty(&node->val)) {
+ chain = list_entry(node->val.prev, struct callchain_list, list);
chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
+ }
callchain_node__init_have_children_rb_tree(node);
}
static void callchain__init_have_children(struct rb_root *root)
{
- struct rb_node *nd;
+ struct rb_node *nd = rb_first(root);
+ bool has_sibling = nd && rb_next(nd);
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
- callchain_node__init_have_children(node);
+ callchain_node__init_have_children(node, has_sibling);
}
}
@@ -387,7 +397,7 @@ static int hist_browser__run(struct hist_browser *browser,
browser->b.entries = &browser->hists->entries;
browser->b.nr_entries = hist_browser__nr_entries(browser);
- hists__browser_title(browser->hists, title, sizeof(title));
+ hists__browser_title(browser->hists, hbt, title, sizeof(title));
if (ui_browser__show(&browser->b, title,
"Press '?' for help on key bindings") < 0)
@@ -414,7 +424,8 @@ static int hist_browser__run(struct hist_browser *browser,
ui_browser__warn_lost_events(&browser->b);
}
- hists__browser_title(browser->hists, title, sizeof(title));
+ hists__browser_title(browser->hists,
+ hbt, title, sizeof(title));
ui_browser__show_title(&browser->b, title);
continue;
}
@@ -457,43 +468,90 @@ out:
return key;
}
-static char *callchain_list__sym_name(struct callchain_list *cl,
- char *bf, size_t bfsize, bool show_dso)
+struct callchain_print_arg {
+ /* for hists browser */
+ off_t row_offset;
+ bool is_current_entry;
+
+ /* for file dump */
+ FILE *fp;
+ int printed;
+};
+
+typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
+ struct callchain_list *chain,
+ const char *str, int offset,
+ unsigned short row,
+ struct callchain_print_arg *arg);
+
+static void hist_browser__show_callchain_entry(struct hist_browser *browser,
+ struct callchain_list *chain,
+ const char *str, int offset,
+ unsigned short row,
+ struct callchain_print_arg *arg)
+{
+ int color, width;
+ char folded_sign = callchain_list__folded(chain);
+
+ color = HE_COLORSET_NORMAL;
+ width = browser->b.width - (offset + 2);
+ if (ui_browser__is_current_entry(&browser->b, row)) {
+ browser->selection = &chain->ms;
+ color = HE_COLORSET_SELECTED;
+ arg->is_current_entry = true;
+ }
+
+ ui_browser__set_color(&browser->b, color);
+ hist_browser__gotorc(browser, row, 0);
+ slsmg_write_nstring(" ", offset);
+ slsmg_printf("%c ", folded_sign);
+ slsmg_write_nstring(str, width);
+}
+
+static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
+ struct callchain_list *chain,
+ const char *str, int offset,
+ unsigned short row __maybe_unused,
+ struct callchain_print_arg *arg)
{
- int printed;
+ char folded_sign = callchain_list__folded(chain);
- if (cl->ms.sym)
- printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
- else
- printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
+ arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
+ folded_sign, str);
+}
+
+typedef bool (*check_output_full_fn)(struct hist_browser *browser,
+ unsigned short row);
- if (show_dso)
- scnprintf(bf + printed, bfsize - printed, " %s",
- cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
+static bool hist_browser__check_output_full(struct hist_browser *browser,
+ unsigned short row)
+{
+ return browser->b.rows == row;
+}
- return bf;
+static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
+ unsigned short row __maybe_unused)
+{
+ return false;
}
#define LEVEL_OFFSET_STEP 3
-static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
- struct callchain_node *chain_node,
- u64 total, int level,
- unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
+static int hist_browser__show_callchain(struct hist_browser *browser,
+ struct rb_root *root, int level,
+ unsigned short row, u64 total,
+ print_callchain_entry_fn print,
+ struct callchain_print_arg *arg,
+ check_output_full_fn is_output_full)
{
struct rb_node *node;
- int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
- u64 new_total, remaining;
+ int first_row = row, offset = level * LEVEL_OFFSET_STEP;
+ u64 new_total;
+ bool need_percent;
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = chain_node->children_hit;
- else
- new_total = total;
+ node = rb_first(root);
+ need_percent = !!rb_next(node);
- remaining = new_total;
- node = rb_first(&chain_node->rb_root);
while (node) {
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
struct rb_node *next = rb_next(node);
@@ -503,30 +561,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
int first = true;
int extra_offset = 0;
- remaining -= cumul;
-
list_for_each_entry(chain, &child->val, list) {
char bf[1024], *alloc_str;
const char *str;
- int color;
bool was_first = first;
if (first)
first = false;
- else
+ else if (need_percent)
extra_offset = LEVEL_OFFSET_STEP;
folded_sign = callchain_list__folded(chain);
- if (*row_offset != 0) {
- --*row_offset;
+ if (arg->row_offset != 0) {
+ arg->row_offset--;
goto do_next;
}
alloc_str = NULL;
str = callchain_list__sym_name(chain, bf, sizeof(bf),
browser->show_dso);
- if (was_first) {
- double percent = cumul * 100.0 / new_total;
+
+ if (was_first && need_percent) {
+ double percent = cumul * 100.0 / total;
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
str = "Not enough memory!";
@@ -534,22 +590,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
str = alloc_str;
}
- color = HE_COLORSET_NORMAL;
- width = browser->b.width - (offset + extra_offset + 2);
- if (ui_browser__is_current_entry(&browser->b, row)) {
- browser->selection = &chain->ms;
- color = HE_COLORSET_SELECTED;
- *is_current_entry = true;
- }
+ print(browser, chain, str, offset + extra_offset, row, arg);
- ui_browser__set_color(&browser->b, color);
- hist_browser__gotorc(browser, row, 0);
- slsmg_write_nstring(" ", offset + extra_offset);
- slsmg_printf("%c ", folded_sign);
- slsmg_write_nstring(str, width);
free(alloc_str);
- if (++row == browser->b.rows)
+ if (is_output_full(browser, ++row))
goto out;
do_next:
if (folded_sign == '+')
@@ -558,89 +603,21 @@ do_next:
if (folded_sign == '-') {
const int new_level = level + (extra_offset ? 2 : 1);
- row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
- new_level, row, row_offset,
- is_current_entry);
- }
- if (row == browser->b.rows)
- goto out;
- node = next;
- }
-out:
- return row - first_row;
-}
-static int hist_browser__show_callchain_node(struct hist_browser *browser,
- struct callchain_node *node,
- int level, unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
-{
- struct callchain_list *chain;
- int first_row = row,
- offset = level * LEVEL_OFFSET_STEP,
- width = browser->b.width - offset;
- char folded_sign = ' ';
-
- list_for_each_entry(chain, &node->val, list) {
- char bf[1024], *s;
- int color;
-
- folded_sign = callchain_list__folded(chain);
-
- if (*row_offset != 0) {
- --*row_offset;
- continue;
- }
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = child->children_hit;
+ else
+ new_total = total;
- color = HE_COLORSET_NORMAL;
- if (ui_browser__is_current_entry(&browser->b, row)) {
- browser->selection = &chain->ms;
- color = HE_COLORSET_SELECTED;
- *is_current_entry = true;
+ row += hist_browser__show_callchain(browser, &child->rb_root,
+ new_level, row, new_total,
+ print, arg, is_output_full);
}
-
- s = callchain_list__sym_name(chain, bf, sizeof(bf),
- browser->show_dso);
- hist_browser__gotorc(browser, row, 0);
- ui_browser__set_color(&browser->b, color);
- slsmg_write_nstring(" ", offset);
- slsmg_printf("%c ", folded_sign);
- slsmg_write_nstring(s, width - 2);
-
- if (++row == browser->b.rows)
- goto out;
- }
-
- if (folded_sign == '-')
- row += hist_browser__show_callchain_node_rb_tree(browser, node,
- browser->hists->stats.total_period,
- level + 1, row,
- row_offset,
- is_current_entry);
-out:
- return row - first_row;
-}
-
-static int hist_browser__show_callchain(struct hist_browser *browser,
- struct rb_root *chain,
- int level, unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
-{
- struct rb_node *nd;
- int first_row = row;
-
- for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
- struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
-
- row += hist_browser__show_callchain_node(browser, node, level,
- row, row_offset,
- is_current_entry);
- if (row == browser->b.rows)
+ if (is_output_full(browser, row))
break;
+ node = next;
}
-
+out:
return row - first_row;
}
@@ -653,17 +630,18 @@ struct hpp_arg {
static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
{
struct hpp_arg *arg = hpp->ptr;
- int ret;
+ int ret, len;
va_list args;
double percent;
va_start(args, fmt);
+ len = va_arg(args, int);
percent = va_arg(args, double);
va_end(args);
ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
- ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
+ ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
slsmg_printf("%s", hpp->buf);
advance_hpp(hpp, ret);
@@ -677,12 +655,12 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \
} \
\
static int \
-hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
+hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
- return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
- __hpp__slsmg_color_printf, true); \
+ return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
+ __hpp__slsmg_color_printf, true); \
}
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
@@ -692,18 +670,20 @@ static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
} \
\
static int \
-hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
+hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
if (!symbol_conf.cumulate_callchain) { \
- int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
+ int len = fmt->user_len ?: fmt->len; \
+ int ret = scnprintf(hpp->buf, hpp->size, \
+ "%*s", len, "N/A"); \
slsmg_printf("%s", hpp->buf); \
\
return ret; \
} \
- return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
- __hpp__slsmg_color_printf, true); \
+ return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
+ " %*.2f%%", __hpp__slsmg_color_printf, true); \
}
__HPP_COLOR_PERCENT_FN(overhead, period)
@@ -812,10 +792,25 @@ static int hist_browser__show_entry(struct hist_browser *browser,
--row_offset;
if (folded_sign == '-' && row != browser->b.rows) {
- printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
- 1, row, &row_offset,
- &current_entry);
- if (current_entry)
+ u64 total = hists__total_period(entry->hists);
+ struct callchain_print_arg arg = {
+ .row_offset = row_offset,
+ .is_current_entry = current_entry,
+ };
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL) {
+ if (symbol_conf.cumulate_callchain)
+ total = entry->stat_acc->period;
+ else
+ total = entry->stat.period;
+ }
+
+ printed += hist_browser__show_callchain(browser,
+ &entry->sorted_chain, 1, row, total,
+ hist_browser__show_callchain_entry, &arg,
+ hist_browser__check_output_full);
+
+ if (arg.is_current_entry)
browser->he_selection = entry;
}
@@ -847,9 +842,6 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
if (perf_hpp__should_skip(fmt))
continue;
- /* We need to add the length of the columns header. */
- perf_hpp__reset_width(fmt, hists);
-
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
if (advance_hpp_check(&dummy_hpp, ret))
break;
@@ -1074,113 +1066,21 @@ do_offset:
}
}
-static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
- struct callchain_node *chain_node,
- u64 total, int level,
- FILE *fp)
-{
- struct rb_node *node;
- int offset = level * LEVEL_OFFSET_STEP;
- u64 new_total, remaining;
- int printed = 0;
-
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = chain_node->children_hit;
- else
- new_total = total;
-
- remaining = new_total;
- node = rb_first(&chain_node->rb_root);
- while (node) {
- struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
- struct rb_node *next = rb_next(node);
- u64 cumul = callchain_cumul_hits(child);
- struct callchain_list *chain;
- char folded_sign = ' ';
- int first = true;
- int extra_offset = 0;
-
- remaining -= cumul;
-
- list_for_each_entry(chain, &child->val, list) {
- char bf[1024], *alloc_str;
- const char *str;
- bool was_first = first;
-
- if (first)
- first = false;
- else
- extra_offset = LEVEL_OFFSET_STEP;
-
- folded_sign = callchain_list__folded(chain);
-
- alloc_str = NULL;
- str = callchain_list__sym_name(chain, bf, sizeof(bf),
- browser->show_dso);
- if (was_first) {
- double percent = cumul * 100.0 / new_total;
-
- if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
- str = "Not enough memory!";
- else
- str = alloc_str;
- }
-
- printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
- free(alloc_str);
- if (folded_sign == '+')
- break;
- }
-
- if (folded_sign == '-') {
- const int new_level = level + (extra_offset ? 2 : 1);
- printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
- new_level, fp);
- }
-
- node = next;
- }
-
- return printed;
-}
-
-static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
- struct callchain_node *node,
- int level, FILE *fp)
-{
- struct callchain_list *chain;
- int offset = level * LEVEL_OFFSET_STEP;
- char folded_sign = ' ';
- int printed = 0;
-
- list_for_each_entry(chain, &node->val, list) {
- char bf[1024], *s;
-
- folded_sign = callchain_list__folded(chain);
- s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
- printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
- }
-
- if (folded_sign == '-')
- printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
- browser->hists->stats.total_period,
- level + 1, fp);
- return printed;
-}
-
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
- struct rb_root *chain, int level, FILE *fp)
+ struct hist_entry *he, FILE *fp)
{
- struct rb_node *nd;
- int printed = 0;
-
- for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
- struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ u64 total = hists__total_period(he->hists);
+ struct callchain_print_arg arg = {
+ .fp = fp,
+ };
- printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
- }
+ if (symbol_conf.cumulate_callchain)
+ total = he->stat_acc->period;
- return printed;
+ hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
+ hist_browser__fprintf_callchain_entry, &arg,
+ hist_browser__check_dump_full);
+ return arg.printed;
}
static int hist_browser__fprintf_entry(struct hist_browser *browser,
@@ -1219,7 +1119,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
printed += fprintf(fp, "%s\n", rtrim(s));
if (folded_sign == '-')
- printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
+ printed += hist_browser__fprintf_callchain(browser, he, fp);
return printed;
}
@@ -1305,7 +1205,15 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser
return browser->he_selection->thread;
}
-static int hists__browser_title(struct hists *hists, char *bf, size_t size)
+/* Check whether the browser is for 'top' or 'report' */
+static inline bool is_report_browser(void *timer)
+{
+ return timer == NULL;
+}
+
+static int hists__browser_title(struct hists *hists,
+ struct hist_browser_timer *hbt,
+ char *bf, size_t size)
{
char unit;
int printed;
@@ -1330,12 +1238,14 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size)
ev_name = buf;
for_each_group_member(pos, evsel) {
+ struct hists *pos_hists = evsel__hists(pos);
+
if (symbol_conf.filter_relative) {
- nr_samples += pos->hists.stats.nr_non_filtered_samples;
- nr_events += pos->hists.stats.total_non_filtered_period;
+ nr_samples += pos_hists->stats.nr_non_filtered_samples;
+ nr_events += pos_hists->stats.total_non_filtered_period;
} else {
- nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
- nr_events += pos->hists.stats.total_period;
+ nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
+ nr_events += pos_hists->stats.total_period;
}
}
}
@@ -1357,6 +1267,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size)
if (dso)
printed += scnprintf(bf + printed, size - printed,
", DSO: %s", dso->short_name);
+ if (!is_report_browser(hbt)) {
+ struct perf_top *top = hbt->arg;
+
+ if (top->zero)
+ printed += scnprintf(bf + printed, size - printed, " [z]");
+ }
+
return printed;
}
@@ -1368,12 +1285,6 @@ static inline void free_popup_options(char **options, int n)
zfree(&options[i]);
}
-/* Check whether the browser is for 'top' or 'report' */
-static inline bool is_report_browser(void *timer)
-{
- return timer == NULL;
-}
-
/*
* Only runtime switching of perf data file will make "input_name" point
* to a malloced buffer. So add "is_input_name_malloced" flag to decide
@@ -1488,7 +1399,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
float min_pcnt,
struct perf_session_env *env)
{
- struct hists *hists = &evsel->hists;
+ struct hists *hists = evsel__hists(evsel);
struct hist_browser *browser = hist_browser__new(hists);
struct branch_info *bi;
struct pstack *fstack;
@@ -1498,6 +1409,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
char buf[64];
char script_opt[64];
int delay_secs = hbt ? hbt->refresh : 0;
+ struct perf_hpp_fmt *fmt;
#define HIST_BROWSER_HELP_COMMON \
"h/?/F1 Show this window\n" \
@@ -1529,6 +1441,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"P Print histograms to perf.hist.N\n"
"t Zoom into current Thread\n"
"V Verbose (DSO names in callchains, etc)\n"
+ "z Toggle zeroing of samples\n"
"/ Filter symbol by name";
if (browser == NULL)
@@ -1547,6 +1460,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
memset(options, 0, sizeof(options));
+ perf_hpp__for_each_format(fmt)
+ perf_hpp__reset_width(fmt, hists);
+
+ if (symbol_conf.col_width_list_str)
+ perf_hpp__set_user_width(symbol_conf.col_width_list_str);
+
while (1) {
const struct thread *thread = NULL;
const struct dso *dso = NULL;
@@ -1623,6 +1542,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
case 'F':
symbol_conf.filter_relative ^= 1;
continue;
+ case 'z':
+ if (!is_report_browser(hbt)) {
+ struct perf_top *top = hbt->arg;
+
+ top->zero = !top->zero;
+ }
+ continue;
case K_F1:
case 'h':
case '?':
@@ -1888,8 +1814,9 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
struct perf_evsel_menu *menu = container_of(browser,
struct perf_evsel_menu, b);
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ struct hists *hists = evsel__hists(evsel);
bool current_entry = ui_browser__is_current_entry(browser, row);
- unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+ unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
const char *ev_name = perf_evsel__name(evsel);
char bf[256], unit;
const char *warn = " ";
@@ -1904,7 +1831,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
ev_name = perf_evsel__group_name(evsel);
for_each_group_member(pos, evsel) {
- nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+ struct hists *pos_hists = evsel__hists(pos);
+ nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
}
}
@@ -1913,7 +1841,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
unit, unit == ' ' ? "" : " ", ev_name);
slsmg_printf("%s", bf);
- nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
+ nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
if (nr_events != 0) {
menu->lost_events = true;
if (!current_entry)
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 6ca60e482cdc..4b3585eed1e8 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -11,6 +11,7 @@
static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
{
int ret = 0;
+ int len;
va_list args;
double percent;
const char *markup;
@@ -18,6 +19,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
size_t size = hpp->size;
va_start(args, fmt);
+ len = va_arg(args, int);
percent = va_arg(args, double);
va_end(args);
@@ -25,7 +27,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
if (markup)
ret += scnprintf(buf, size, markup);
- ret += scnprintf(buf + ret, size - ret, fmt, percent);
+ ret += scnprintf(buf + ret, size - ret, fmt, len, percent);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
@@ -39,12 +41,12 @@ static u64 he_get_##_field(struct hist_entry *he) \
return he->stat._field; \
} \
\
-static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
+static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
- return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
- __percent_color_snprintf, true); \
+ return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
+ __percent_color_snprintf, true); \
}
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
@@ -57,8 +59,8 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
- return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
- __percent_color_snprintf, true); \
+ return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
+ __percent_color_snprintf, true); \
}
__HPP_COLOR_PERCENT_FN(overhead, period)
@@ -87,15 +89,6 @@ void perf_gtk__init_hpp(void)
perf_gtk__hpp_color_overhead_acc;
}
-static void callchain_list__sym_name(struct callchain_list *cl,
- char *bf, size_t bfsize)
-{
- if (cl->ms.sym)
- scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
- else
- scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
-}
-
static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
GtkTreeIter *parent, int col, u64 total)
{
@@ -126,7 +119,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
gtk_tree_store_set(store, &iter, 0, buf, -1);
- callchain_list__sym_name(chain, buf, sizeof(buf));
+ callchain_list__sym_name(chain, buf, sizeof(buf), false);
gtk_tree_store_set(store, &iter, col, buf, -1);
if (need_new_parent) {
@@ -205,10 +198,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
if (perf_hpp__is_sort_entry(fmt))
sym_col = col_idx;
- fmt->header(fmt, &hpp, hists_to_evsel(hists));
-
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
- -1, ltrim(s),
+ -1, fmt->name,
renderer, "markup",
col_idx++, NULL);
}
@@ -319,7 +310,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_container_add(GTK_CONTAINER(window), vbox);
evlist__for_each(evlist, pos) {
- struct hists *hists = &pos->hists;
+ struct hists *hists = evsel__hists(pos);
const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 498adb23c02e..2af18376b077 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -15,9 +15,9 @@
__ret; \
})
-int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
- hpp_field_fn get_field, const char *fmt,
- hpp_snprint_fn print_fn, bool fmt_percent)
+static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
+ hpp_field_fn get_field, const char *fmt, int len,
+ hpp_snprint_fn print_fn, bool fmt_percent)
{
int ret;
struct hists *hists = he->hists;
@@ -32,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
if (total)
percent = 100.0 * get_field(he) / total;
- ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);
+ ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
} else
- ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
+ ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
if (perf_evsel__is_group_event(evsel)) {
int prev_idx, idx_delta;
@@ -60,19 +60,19 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
*/
if (fmt_percent) {
ret += hpp__call_print_fn(hpp, print_fn,
- fmt, 0.0);
+ fmt, len, 0.0);
} else {
ret += hpp__call_print_fn(hpp, print_fn,
- fmt, 0ULL);
+ fmt, len, 0ULL);
}
}
if (fmt_percent) {
- ret += hpp__call_print_fn(hpp, print_fn, fmt,
+ ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
100.0 * period / total);
} else {
ret += hpp__call_print_fn(hpp, print_fn, fmt,
- period);
+ len, period);
}
prev_idx = perf_evsel__group_idx(evsel);
@@ -86,10 +86,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
*/
if (fmt_percent) {
ret += hpp__call_print_fn(hpp, print_fn,
- fmt, 0.0);
+ fmt, len, 0.0);
} else {
ret += hpp__call_print_fn(hpp, print_fn,
- fmt, 0ULL);
+ fmt, len, 0ULL);
}
}
}
@@ -104,16 +104,35 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
return ret;
}
-int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
- hpp_field_fn get_field, const char *fmt,
- hpp_snprint_fn print_fn, bool fmt_percent)
+int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he, hpp_field_fn get_field,
+ const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
+{
+ int len = fmt->user_len ?: fmt->len;
+
+ if (symbol_conf.field_sep) {
+ return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
+ print_fn, fmt_percent);
+ }
+
+ if (fmt_percent)
+ len -= 2; /* 2 for a space and a % sign */
+ else
+ len -= 1;
+
+ return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
+}
+
+int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he, hpp_field_fn get_field,
+ const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
{
if (!symbol_conf.cumulate_callchain) {
- return snprintf(hpp->buf, hpp->size, "%*s",
- fmt_percent ? 8 : 12, "N/A");
+ int len = fmt->user_len ?: fmt->len;
+ return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
}
- return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent);
+ return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
}
static int field_cmp(u64 field_a, u64 field_b)
@@ -190,30 +209,26 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
return ret;
}
-#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
-static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
- struct perf_hpp *hpp, \
- struct perf_evsel *evsel) \
-{ \
- int len = _min_width; \
- \
- if (symbol_conf.event_group) \
- len = max(len, evsel->nr_members * _unit_width); \
- \
- return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
-}
-
-#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
-static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
- struct perf_hpp *hpp __maybe_unused, \
- struct perf_evsel *evsel) \
-{ \
- int len = _min_width; \
- \
- if (symbol_conf.event_group) \
- len = max(len, evsel->nr_members * _unit_width); \
- \
- return len; \
+static int hpp__width_fn(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp __maybe_unused,
+ struct perf_evsel *evsel)
+{
+ int len = fmt->user_len ?: fmt->len;
+
+ if (symbol_conf.event_group)
+ len = max(len, evsel->nr_members * fmt->len);
+
+ if (len < (int)strlen(fmt->name))
+ len = strlen(fmt->name);
+
+ return len;
+}
+
+static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct perf_evsel *evsel)
+{
+ int len = hpp__width_fn(fmt, hpp, evsel);
+ return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
}
static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
@@ -221,11 +236,12 @@ static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
va_list args;
ssize_t ssize = hpp->size;
double percent;
- int ret;
+ int ret, len;
va_start(args, fmt);
+ len = va_arg(args, int);
percent = va_arg(args, double);
- ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent);
+ ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent);
va_end(args);
return (ret >= ssize) ? (ssize - 1) : ret;
@@ -250,20 +266,19 @@ static u64 he_get_##_field(struct hist_entry *he) \
return he->stat._field; \
} \
\
-static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
+static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
- return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
- hpp_color_scnprintf, true); \
+ return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
+ hpp_color_scnprintf, true); \
}
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
-static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
+static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
- const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
- return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
- hpp_entry_scnprintf, true); \
+ return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
+ hpp_entry_scnprintf, true); \
}
#define __HPP_SORT_FN(_type, _field) \
@@ -278,20 +293,19 @@ static u64 he_get_acc_##_field(struct hist_entry *he) \
return he->stat_acc->_field; \
} \
\
-static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
+static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
- return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
- hpp_color_scnprintf, true); \
+ return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
+ hpp_color_scnprintf, true); \
}
#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
-static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
+static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
- const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
- return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \
- hpp_entry_scnprintf, true); \
+ return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
+ hpp_entry_scnprintf, true); \
}
#define __HPP_SORT_ACC_FN(_type, _field) \
@@ -306,12 +320,11 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \
return he->stat._field; \
} \
\
-static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
+static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
- const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
- return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \
- hpp_entry_scnprintf, false); \
+ return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \
+ hpp_entry_scnprintf, false); \
}
#define __HPP_SORT_RAW_FN(_type, _field) \
@@ -321,37 +334,29 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
}
-#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
-__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
-__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
+#define HPP_PERCENT_FNS(_type, _field) \
__HPP_COLOR_PERCENT_FN(_type, _field) \
__HPP_ENTRY_PERCENT_FN(_type, _field) \
__HPP_SORT_FN(_type, _field)
-#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\
-__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
-__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
+#define HPP_PERCENT_ACC_FNS(_type, _field) \
__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
__HPP_SORT_ACC_FN(_type, _field)
-#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
-__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
-__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
+#define HPP_RAW_FNS(_type, _field) \
__HPP_ENTRY_RAW_FN(_type, _field) \
__HPP_SORT_RAW_FN(_type, _field)
-__HPP_HEADER_FN(overhead_self, "Self", 8, 8)
-
-HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
-HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
-HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
-HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
-HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
-HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)
+HPP_PERCENT_FNS(overhead, period)
+HPP_PERCENT_FNS(overhead_sys, period_sys)
+HPP_PERCENT_FNS(overhead_us, period_us)
+HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys)
+HPP_PERCENT_FNS(overhead_guest_us, period_guest_us)
+HPP_PERCENT_ACC_FNS(overhead_acc, period)
-HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
-HPP_RAW_FNS(period, "Period", period, 12, 12)
+HPP_RAW_FNS(samples, nr_events)
+HPP_RAW_FNS(period, period)
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
struct hist_entry *b __maybe_unused)
@@ -359,47 +364,50 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
return 0;
}
-#define HPP__COLOR_PRINT_FNS(_name) \
+#define HPP__COLOR_PRINT_FNS(_name, _fn) \
{ \
- .header = hpp__header_ ## _name, \
- .width = hpp__width_ ## _name, \
- .color = hpp__color_ ## _name, \
- .entry = hpp__entry_ ## _name, \
+ .name = _name, \
+ .header = hpp__header_fn, \
+ .width = hpp__width_fn, \
+ .color = hpp__color_ ## _fn, \
+ .entry = hpp__entry_ ## _fn, \
.cmp = hpp__nop_cmp, \
.collapse = hpp__nop_cmp, \
- .sort = hpp__sort_ ## _name, \
+ .sort = hpp__sort_ ## _fn, \
}
-#define HPP__COLOR_ACC_PRINT_FNS(_name) \
+#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \
{ \
- .header = hpp__header_ ## _name, \
- .width = hpp__width_ ## _name, \
- .color = hpp__color_ ## _name, \
- .entry = hpp__entry_ ## _name, \
+ .name = _name, \
+ .header = hpp__header_fn, \
+ .width = hpp__width_fn, \
+ .color = hpp__color_ ## _fn, \
+ .entry = hpp__entry_ ## _fn, \
.cmp = hpp__nop_cmp, \
.collapse = hpp__nop_cmp, \
- .sort = hpp__sort_ ## _name, \
+ .sort = hpp__sort_ ## _fn, \
}
-#define HPP__PRINT_FNS(_name) \
+#define HPP__PRINT_FNS(_name, _fn) \
{ \
- .header = hpp__header_ ## _name, \
- .width = hpp__width_ ## _name, \
- .entry = hpp__entry_ ## _name, \
+ .name = _name, \
+ .header = hpp__header_fn, \
+ .width = hpp__width_fn, \
+ .entry = hpp__entry_ ## _fn, \
.cmp = hpp__nop_cmp, \
.collapse = hpp__nop_cmp, \
- .sort = hpp__sort_ ## _name, \
+ .sort = hpp__sort_ ## _fn, \
}
struct perf_hpp_fmt perf_hpp__format[] = {
- HPP__COLOR_PRINT_FNS(overhead),
- HPP__COLOR_PRINT_FNS(overhead_sys),
- HPP__COLOR_PRINT_FNS(overhead_us),
- HPP__COLOR_PRINT_FNS(overhead_guest_sys),
- HPP__COLOR_PRINT_FNS(overhead_guest_us),
- HPP__COLOR_ACC_PRINT_FNS(overhead_acc),
- HPP__PRINT_FNS(samples),
- HPP__PRINT_FNS(period)
+ HPP__COLOR_PRINT_FNS("Overhead", overhead),
+ HPP__COLOR_PRINT_FNS("sys", overhead_sys),
+ HPP__COLOR_PRINT_FNS("usr", overhead_us),
+ HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys),
+ HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us),
+ HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc),
+ HPP__PRINT_FNS("Samples", samples),
+ HPP__PRINT_FNS("Period", period)
};
LIST_HEAD(perf_hpp__list);
@@ -444,14 +452,12 @@ void perf_hpp__init(void)
/*
* If user specified field order, no need to setup default fields.
*/
- if (field_order)
+ if (is_strict_order(field_order))
return;
if (symbol_conf.cumulate_callchain) {
perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
-
- perf_hpp__format[PERF_HPP__OVERHEAD].header =
- hpp__header_overhead_self;
+ perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
}
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
@@ -513,11 +519,11 @@ void perf_hpp__column_disable(unsigned col)
void perf_hpp__cancel_cumulate(void)
{
- if (field_order)
+ if (is_strict_order(field_order))
return;
perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
- perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead;
+ perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead";
}
void perf_hpp__setup_output_field(void)
@@ -622,3 +628,59 @@ unsigned int hists__sort_list_width(struct hists *hists)
return ret;
}
+
+void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
+{
+ int idx;
+
+ if (perf_hpp__is_sort_entry(fmt))
+ return perf_hpp__reset_sort_width(fmt, hists);
+
+ for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
+ if (fmt == &perf_hpp__format[idx])
+ break;
+ }
+
+ if (idx == PERF_HPP__MAX_INDEX)
+ return;
+
+ switch (idx) {
+ case PERF_HPP__OVERHEAD:
+ case PERF_HPP__OVERHEAD_SYS:
+ case PERF_HPP__OVERHEAD_US:
+ case PERF_HPP__OVERHEAD_ACC:
+ fmt->len = 8;
+ break;
+
+ case PERF_HPP__OVERHEAD_GUEST_SYS:
+ case PERF_HPP__OVERHEAD_GUEST_US:
+ fmt->len = 9;
+ break;
+
+ case PERF_HPP__SAMPLES:
+ case PERF_HPP__PERIOD:
+ fmt->len = 12;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void perf_hpp__set_user_width(const char *width_list_str)
+{
+ struct perf_hpp_fmt *fmt;
+ const char *ptr = width_list_str;
+
+ perf_hpp__for_each_format(fmt) {
+ char *p;
+
+ int len = strtol(ptr, &p, 10);
+ fmt->user_len = len;
+
+ if (*p == ',')
+ ptr = p + 1;
+ else
+ break;
+ }
+}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 40af0acb4fe9..dfcbc90146ef 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -41,6 +41,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
{
int i;
size_t ret = 0;
+ char bf[1024];
ret += callchain__fprintf_left_margin(fp, left_margin);
for (i = 0; i < depth; i++) {
@@ -56,11 +57,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
} else
ret += fprintf(fp, "%s", " ");
}
- if (chain->ms.sym)
- ret += fprintf(fp, "%s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
-
+ fputs(callchain_list__sym_name(chain, bf, sizeof(bf), false), fp);
+ fputc('\n', fp);
return ret;
}
@@ -168,6 +166,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
struct rb_node *node;
int i = 0;
int ret = 0;
+ char bf[1024];
/*
* If have one single callchain root, don't bother printing
@@ -196,10 +195,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
} else
ret += callchain__fprintf_left_margin(fp, left_margin);
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+ ret += fprintf(fp, "%s\n", callchain_list__sym_name(chain, bf, sizeof(bf),
+ false));
if (++entries_printed == callchain_param.print_limit)
break;
@@ -219,6 +216,7 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
{
struct callchain_list *chain;
size_t ret = 0;
+ char bf[1024];
if (!node)
return 0;
@@ -229,11 +227,8 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
list_for_each_entry(chain, &node->val, list) {
if (chain->ip >= PERF_CONTEXT_MAX)
continue;
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n",
- (void *)(long)chain->ip);
+ ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain,
+ bf, sizeof(bf), false));
}
return ret;
@@ -395,10 +390,12 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
init_rem_hits();
-
perf_hpp__for_each_format(fmt)
perf_hpp__reset_width(fmt, hists);
+ if (symbol_conf.col_width_list_str)
+ perf_hpp__set_user_width(symbol_conf.col_width_list_str);
+
if (!show_header)
goto print_entries;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 809b4c50beae..79999ceaf2be 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -17,11 +17,13 @@
#include "debug.h"
#include "annotate.h"
#include "evsel.h"
+#include <regex.h>
#include <pthread.h>
#include <linux/bitops.h>
const char *disassembler_style;
const char *objdump_path;
+static regex_t file_lineno;
static struct ins *ins__find(const char *name);
static int disasm_line__parse(char *line, char **namep, char **rawp);
@@ -232,9 +234,16 @@ static int mov__parse(struct ins_operands *ops)
return -1;
target = ++s;
+ comment = strchr(s, '#');
- while (s[0] != '\0' && !isspace(s[0]))
- ++s;
+ if (comment != NULL)
+ s = comment - 1;
+ else
+ s = strchr(s, '\0') - 1;
+
+ while (s > target && isspace(s[0]))
+ --s;
+ s++;
prev = *s;
*s = '\0';
@@ -244,7 +253,6 @@ static int mov__parse(struct ins_operands *ops)
if (ops->target.raw == NULL)
goto out_free_source;
- comment = strchr(s, '#');
if (comment == NULL)
return 0;
@@ -472,7 +480,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
- if (addr < sym->start || addr > sym->end)
+ if (addr < sym->start || addr >= sym->end)
return -ERANGE;
offset = addr - sym->start;
@@ -564,13 +572,15 @@ out_free_name:
return -1;
}
-static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
+static struct disasm_line *disasm_line__new(s64 offset, char *line,
+ size_t privsize, int line_nr)
{
struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
if (dl != NULL) {
dl->offset = offset;
dl->line = strdup(line);
+ dl->line_nr = line_nr;
if (dl->line == NULL)
goto out_delete;
@@ -782,13 +792,15 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
* The ops.raw part will be parsed further according to type of the instruction.
*/
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
- FILE *file, size_t privsize)
+ FILE *file, size_t privsize,
+ int *line_nr)
{
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *dl;
char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
size_t line_len;
s64 line_ip, offset = -1;
+ regmatch_t match[2];
if (getline(&line, &line_len, file) < 0)
return -1;
@@ -806,6 +818,12 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
line_ip = -1;
parsed_line = line;
+ /* /filename:linenr ? Save line number and ignore. */
+ if (regexec(&file_lineno, line, 2, match, 0) == 0) {
+ *line_nr = atoi(line + match[1].rm_so);
+ return 0;
+ }
+
/*
* Strip leading spaces:
*/
@@ -830,14 +848,15 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
end = map__rip_2objdump(map, sym->end);
offset = line_ip - start;
- if ((u64)line_ip < start || (u64)line_ip > end)
+ if ((u64)line_ip < start || (u64)line_ip >= end)
offset = -1;
else
parsed_line = tmp2 + 1;
}
- dl = disasm_line__new(offset, parsed_line, privsize);
+ dl = disasm_line__new(offset, parsed_line, privsize, *line_nr);
free(line);
+ (*line_nr)++;
if (dl == NULL)
return -1;
@@ -863,6 +882,11 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
return 0;
}
+static __attribute__((constructor)) void symbol__init_regexpr(void)
+{
+ regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED);
+}
+
static void delete_last_nop(struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
@@ -898,11 +922,10 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
char symfs_filename[PATH_MAX];
struct kcore_extract kce;
bool delete_extract = false;
+ int lineno = 0;
- if (filename) {
- snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
- symbol_conf.symfs, filename);
- }
+ if (filename)
+ symbol__join_symfs(symfs_filename, filename);
if (filename == NULL) {
if (dso->has_build_id) {
@@ -911,6 +934,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
return -ENOMEM;
}
goto fallback;
+ } else if (dso__is_kcore(dso)) {
+ goto fallback;
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
strstr(command, "[kernel.kallsyms]") ||
access(symfs_filename, R_OK)) {
@@ -922,8 +947,7 @@ fallback:
* DSO is the same as when 'perf record' ran.
*/
filename = (char *)dso->long_name;
- snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
- symbol_conf.symfs, filename);
+ symbol__join_symfs(symfs_filename, filename);
free_filename = false;
}
@@ -963,7 +987,7 @@ fallback:
kce.kcore_filename = symfs_filename;
kce.addr = map__rip_2objdump(map, sym->start);
kce.offs = sym->start;
- kce.len = sym->end + 1 - sym->start;
+ kce.len = sym->end - sym->start;
if (!kcore_extract__create(&kce)) {
delete_extract = true;
strlcpy(symfs_filename, kce.extract_filename,
@@ -979,12 +1003,12 @@ fallback:
snprintf(command, sizeof(command),
"%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64
- " -d %s %s -C %s 2>/dev/null|grep -v %s|expand",
+ " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand",
objdump_path ? objdump_path : "objdump",
disassembler_style ? "-M " : "",
disassembler_style ? disassembler_style : "",
map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end+1),
+ map__rip_2objdump(map, sym->end),
symbol_conf.annotate_asm_raw ? "" : "--no-show-raw",
symbol_conf.annotate_src ? "-S" : "",
symfs_filename, filename);
@@ -996,7 +1020,8 @@ fallback:
goto out_free_filename;
while (!feof(file))
- if (symbol__parse_objdump_line(sym, map, file, privsize) < 0)
+ if (symbol__parse_objdump_line(sym, map, file, privsize,
+ &lineno) < 0)
break;
/*
@@ -1167,7 +1192,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
goto next;
offset = start + i;
- src_line->path = get_srcline(map->dso, offset);
+ src_line->path = get_srcline(map->dso, offset, NULL, false);
insert_source_line(&tmp_root, src_line);
next:
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 112d6e268150..0784a9420528 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -58,6 +58,7 @@ struct disasm_line {
char *line;
char *name;
struct ins *ins;
+ int line_nr;
struct ins_operands ops;
};
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index a904a4cfe7d3..e8d79e5bfaf7 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -15,6 +15,11 @@
#include "debug.h"
#include "session.h"
#include "tool.h"
+#include "header.h"
+#include "vdso.h"
+
+
+static bool no_buildid_cache;
int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
union perf_event *event,
@@ -33,8 +38,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
return -1;
}
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->ip, &al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
if (al.map != NULL)
al.map->dso->hit = 1;
@@ -106,3 +110,343 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
build_id_hex, build_id_hex + 2);
return bf;
}
+
+#define dsos__for_each_with_build_id(pos, head) \
+ list_for_each_entry(pos, head, node) \
+ if (!pos->has_build_id) \
+ continue; \
+ else
+
+static int write_buildid(const char *name, size_t name_len, u8 *build_id,
+ pid_t pid, u16 misc, int fd)
+{
+ int err;
+ struct build_id_event b;
+ size_t len;
+
+ len = name_len + 1;
+ len = PERF_ALIGN(len, NAME_ALIGN);
+
+ memset(&b, 0, sizeof(b));
+ memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
+ b.pid = pid;
+ b.header.misc = misc;
+ b.header.size = sizeof(b) + len;
+
+ err = writen(fd, &b, sizeof(b));
+ if (err < 0)
+ return err;
+
+ return write_padded(fd, name, name_len + 1, len);
+}
+
+static int __dsos__write_buildid_table(struct list_head *head,
+ struct machine *machine,
+ pid_t pid, u16 misc, int fd)
+{
+ char nm[PATH_MAX];
+ struct dso *pos;
+
+ dsos__for_each_with_build_id(pos, head) {
+ int err;
+ const char *name;
+ size_t name_len;
+
+ if (!pos->hit)
+ continue;
+
+ if (dso__is_vdso(pos)) {
+ name = pos->short_name;
+ name_len = pos->short_name_len + 1;
+ } else if (dso__is_kcore(pos)) {
+ machine__mmap_name(machine, nm, sizeof(nm));
+ name = nm;
+ name_len = strlen(nm) + 1;
+ } else {
+ name = pos->long_name;
+ name_len = pos->long_name_len + 1;
+ }
+
+ err = write_buildid(name, name_len, pos->build_id,
+ pid, misc, fd);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int machine__write_buildid_table(struct machine *machine, int fd)
+{
+ int err;
+ u16 kmisc = PERF_RECORD_MISC_KERNEL,
+ umisc = PERF_RECORD_MISC_USER;
+
+ if (!machine__is_host(machine)) {
+ kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+ umisc = PERF_RECORD_MISC_GUEST_USER;
+ }
+
+ err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
+ machine->pid, kmisc, fd);
+ if (err == 0)
+ err = __dsos__write_buildid_table(&machine->user_dsos.head,
+ machine, machine->pid, umisc,
+ fd);
+ return err;
+}
+
+int perf_session__write_buildid_table(struct perf_session *session, int fd)
+{
+ struct rb_node *nd;
+ int err = machine__write_buildid_table(&session->machines.host, fd);
+
+ if (err)
+ return err;
+
+ for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ err = machine__write_buildid_table(pos, fd);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static int __dsos__hit_all(struct list_head *head)
+{
+ struct dso *pos;
+
+ list_for_each_entry(pos, head, node)
+ pos->hit = true;
+
+ return 0;
+}
+
+static int machine__hit_all_dsos(struct machine *machine)
+{
+ int err;
+
+ err = __dsos__hit_all(&machine->kernel_dsos.head);
+ if (err)
+ return err;
+
+ return __dsos__hit_all(&machine->user_dsos.head);
+}
+
+int dsos__hit_all(struct perf_session *session)
+{
+ struct rb_node *nd;
+ int err;
+
+ err = machine__hit_all_dsos(&session->machines.host);
+ if (err)
+ return err;
+
+ for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+
+ err = machine__hit_all_dsos(pos);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void disable_buildid_cache(void)
+{
+ no_buildid_cache = true;
+}
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms, bool is_vdso)
+{
+ const size_t size = PATH_MAX;
+ char *realname, *filename = zalloc(size),
+ *linkname = zalloc(size), *targetname;
+ int len, err = -1;
+ bool slash = is_kallsyms || is_vdso;
+
+ if (is_kallsyms) {
+ if (symbol_conf.kptr_restrict) {
+ pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
+ err = 0;
+ goto out_free;
+ }
+ realname = (char *) name;
+ } else
+ realname = realpath(name, NULL);
+
+ if (realname == NULL || filename == NULL || linkname == NULL)
+ goto out_free;
+
+ len = scnprintf(filename, size, "%s%s%s",
+ debugdir, slash ? "/" : "",
+ is_vdso ? DSO__NAME_VDSO : realname);
+ if (mkdir_p(filename, 0755))
+ goto out_free;
+
+ snprintf(filename + len, size - len, "/%s", sbuild_id);
+
+ if (access(filename, F_OK)) {
+ if (is_kallsyms) {
+ if (copyfile("/proc/kallsyms", filename))
+ goto out_free;
+ } else if (link(realname, filename) && copyfile(name, filename))
+ goto out_free;
+ }
+
+ len = scnprintf(linkname, size, "%s/.build-id/%.2s",
+ debugdir, sbuild_id);
+
+ if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
+ goto out_free;
+
+ snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
+ targetname = filename + strlen(debugdir) - 5;
+ memcpy(targetname, "../..", 5);
+
+ if (symlink(targetname, linkname) == 0)
+ err = 0;
+out_free:
+ if (!is_kallsyms)
+ free(realname);
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
+ const char *name, const char *debugdir,
+ bool is_kallsyms, bool is_vdso)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(build_id, build_id_size, sbuild_id);
+
+ return build_id_cache__add_s(sbuild_id, debugdir, name,
+ is_kallsyms, is_vdso);
+}
+
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
+{
+ const size_t size = PATH_MAX;
+ char *filename = zalloc(size),
+ *linkname = zalloc(size);
+ int err = -1;
+
+ if (filename == NULL || linkname == NULL)
+ goto out_free;
+
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, sbuild_id + 2);
+
+ if (access(linkname, F_OK))
+ goto out_free;
+
+ if (readlink(linkname, filename, size - 1) < 0)
+ goto out_free;
+
+ if (unlink(linkname))
+ goto out_free;
+
+ /*
+ * Since the link is relative, we must make it absolute:
+ */
+ snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+ debugdir, sbuild_id, filename);
+
+ if (unlink(linkname))
+ goto out_free;
+
+ err = 0;
+out_free:
+ free(filename);
+ free(linkname);
+ return err;
+}
+
+static int dso__cache_build_id(struct dso *dso, struct machine *machine,
+ const char *debugdir)
+{
+ bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
+ bool is_vdso = dso__is_vdso(dso);
+ const char *name = dso->long_name;
+ char nm[PATH_MAX];
+
+ if (dso__is_kcore(dso)) {
+ is_kallsyms = true;
+ machine__mmap_name(machine, nm, sizeof(nm));
+ name = nm;
+ }
+ return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
+ debugdir, is_kallsyms, is_vdso);
+}
+
+static int __dsos__cache_build_ids(struct list_head *head,
+ struct machine *machine, const char *debugdir)
+{
+ struct dso *pos;
+ int err = 0;
+
+ dsos__for_each_with_build_id(pos, head)
+ if (dso__cache_build_id(pos, machine, debugdir))
+ err = -1;
+
+ return err;
+}
+
+static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
+{
+ int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine,
+ debugdir);
+ ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine,
+ debugdir);
+ return ret;
+}
+
+int perf_session__cache_build_ids(struct perf_session *session)
+{
+ struct rb_node *nd;
+ int ret;
+ char debugdir[PATH_MAX];
+
+ if (no_buildid_cache)
+ return 0;
+
+ snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+
+ if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
+ return -1;
+
+ ret = machine__cache_build_ids(&session->machines.host, debugdir);
+
+ for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__cache_build_ids(pos, debugdir);
+ }
+ return ret ? -1 : 0;
+}
+
+static bool machine__read_build_ids(struct machine *machine, bool with_hits)
+{
+ bool ret;
+
+ ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
+ ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
+ return ret;
+}
+
+bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
+{
+ struct rb_node *nd;
+ bool ret = machine__read_build_ids(&session->machines.host, with_hits);
+
+ for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= machine__read_build_ids(pos, with_hits);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index ae392561470b..8236319514d5 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -15,4 +15,16 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);
int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel,
struct machine *machine);
+
+int dsos__hit_all(struct perf_session *session);
+
+bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
+int perf_session__write_buildid_table(struct perf_session *session, int fd);
+int perf_session__cache_build_ids(struct perf_session *session);
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms, bool is_vdso);
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+void disable_buildid_cache(void);
+
#endif
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 7b176dd02e1a..5cf9e1b5989d 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -22,6 +22,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *);
extern int perf_config(config_fn_t fn, void *);
extern int perf_config_int(const char *, const char *);
+extern u64 perf_config_u64(const char *, const char *);
extern int perf_config_bool(const char *, const char *);
extern int config_error_nonbool(const char *);
extern const char *perf_config_dirname(const char *, const char *);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 437ee09727e6..cf524a35cc84 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -25,77 +25,176 @@
__thread struct callchain_cursor callchain_cursor;
-int
-parse_callchain_report_opt(const char *arg)
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+static int get_stack_size(const char *str, unsigned long *_size)
{
- char *tok, *tok2;
char *endptr;
+ unsigned long size;
+ unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
- symbol_conf.use_callchain = true;
+ size = strtoul(str, &endptr, 0);
- if (!arg)
+ do {
+ if (*endptr)
+ break;
+
+ size = round_up(size, sizeof(u64));
+ if (!size || size > max_size)
+ break;
+
+ *_size = size;
return 0;
- tok = strtok((char *)arg, ",");
- if (!tok)
- return -1;
+ } while (0);
- /* get the output mode */
- if (!strncmp(tok, "graph", strlen(arg))) {
- callchain_param.mode = CHAIN_GRAPH_ABS;
+ pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
+ max_size, str);
+ return -1;
+}
+#endif /* HAVE_DWARF_UNWIND_SUPPORT */
- } else if (!strncmp(tok, "flat", strlen(arg))) {
- callchain_param.mode = CHAIN_FLAT;
- } else if (!strncmp(tok, "fractal", strlen(arg))) {
- callchain_param.mode = CHAIN_GRAPH_REL;
- } else if (!strncmp(tok, "none", strlen(arg))) {
- callchain_param.mode = CHAIN_NONE;
- symbol_conf.use_callchain = false;
- return 0;
- } else {
- return -1;
- }
+int parse_callchain_record_opt(const char *arg)
+{
+ char *tok, *name, *saveptr = NULL;
+ char *buf;
+ int ret = -1;
+
+ /* We need buffer that we know we can write to. */
+ buf = malloc(strlen(arg) + 1);
+ if (!buf)
+ return -ENOMEM;
+
+ strcpy(buf, arg);
+
+ tok = strtok_r((char *)buf, ",", &saveptr);
+ name = tok ? : (char *)buf;
+
+ do {
+ /* Framepointer style */
+ if (!strncmp(name, "fp", sizeof("fp"))) {
+ if (!strtok_r(NULL, ",", &saveptr)) {
+ callchain_param.record_mode = CALLCHAIN_FP;
+ ret = 0;
+ } else
+ pr_err("callchain: No more arguments "
+ "needed for -g fp\n");
+ break;
- /* get the min percentage */
- tok = strtok(NULL, ",");
- if (!tok)
- goto setup;
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+ /* Dwarf style */
+ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
+ const unsigned long default_stack_dump_size = 8192;
- callchain_param.min_percent = strtod(tok, &endptr);
- if (tok == endptr)
- return -1;
+ ret = 0;
+ callchain_param.record_mode = CALLCHAIN_DWARF;
+ callchain_param.dump_size = default_stack_dump_size;
+
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (tok) {
+ unsigned long size = 0;
- /* get the print limit */
- tok2 = strtok(NULL, ",");
- if (!tok2)
- goto setup;
+ ret = get_stack_size(tok, &size);
+ callchain_param.dump_size = size;
+ }
+#endif /* HAVE_DWARF_UNWIND_SUPPORT */
+ } else {
+ pr_err("callchain: Unknown --call-graph option "
+ "value: %s\n", arg);
+ break;
+ }
- if (tok2[0] != 'c') {
- callchain_param.print_limit = strtoul(tok2, &endptr, 0);
- tok2 = strtok(NULL, ",");
- if (!tok2)
- goto setup;
+ } while (0);
+
+ free(buf);
+ return ret;
+}
+
+static int parse_callchain_mode(const char *value)
+{
+ if (!strncmp(value, "graph", strlen(value))) {
+ callchain_param.mode = CHAIN_GRAPH_ABS;
+ return 0;
+ }
+ if (!strncmp(value, "flat", strlen(value))) {
+ callchain_param.mode = CHAIN_FLAT;
+ return 0;
+ }
+ if (!strncmp(value, "fractal", strlen(value))) {
+ callchain_param.mode = CHAIN_GRAPH_REL;
+ return 0;
}
+ return -1;
+}
- /* get the call chain order */
- if (!strncmp(tok2, "caller", strlen("caller")))
+static int parse_callchain_order(const char *value)
+{
+ if (!strncmp(value, "caller", strlen(value))) {
callchain_param.order = ORDER_CALLER;
- else if (!strncmp(tok2, "callee", strlen("callee")))
+ return 0;
+ }
+ if (!strncmp(value, "callee", strlen(value))) {
callchain_param.order = ORDER_CALLEE;
- else
- return -1;
+ return 0;
+ }
+ return -1;
+}
- /* Get the sort key */
- tok2 = strtok(NULL, ",");
- if (!tok2)
- goto setup;
- if (!strncmp(tok2, "function", strlen("function")))
+static int parse_callchain_sort_key(const char *value)
+{
+ if (!strncmp(value, "function", strlen(value))) {
callchain_param.key = CCKEY_FUNCTION;
- else if (!strncmp(tok2, "address", strlen("address")))
+ return 0;
+ }
+ if (!strncmp(value, "address", strlen(value))) {
callchain_param.key = CCKEY_ADDRESS;
- else
- return -1;
-setup:
+ return 0;
+ }
+ if (!strncmp(value, "branch", strlen(value))) {
+ callchain_param.branch_callstack = 1;
+ return 0;
+ }
+ return -1;
+}
+
+int
+parse_callchain_report_opt(const char *arg)
+{
+ char *tok;
+ char *endptr;
+ bool minpcnt_set = false;
+
+ symbol_conf.use_callchain = true;
+
+ if (!arg)
+ return 0;
+
+ while ((tok = strtok((char *)arg, ",")) != NULL) {
+ if (!strncmp(tok, "none", strlen(tok))) {
+ callchain_param.mode = CHAIN_NONE;
+ symbol_conf.use_callchain = false;
+ return 0;
+ }
+
+ if (!parse_callchain_mode(tok) ||
+ !parse_callchain_order(tok) ||
+ !parse_callchain_sort_key(tok)) {
+ /* parsing ok - move on to the next */
+ } else if (!minpcnt_set) {
+ /* try to get the min percent */
+ callchain_param.min_percent = strtod(tok, &endptr);
+ if (tok == endptr)
+ return -1;
+ minpcnt_set = true;
+ } else {
+ /* try print limit at last */
+ callchain_param.print_limit = strtoul(tok, &endptr, 0);
+ if (tok == endptr)
+ return -1;
+ }
+
+ arg = NULL;
+ }
+
if (callchain_register_param(&callchain_param) < 0) {
pr_err("Can't register callchain params\n");
return -1;
@@ -103,6 +202,47 @@ setup:
return 0;
}
+int perf_callchain_config(const char *var, const char *value)
+{
+ char *endptr;
+
+ if (prefixcmp(var, "call-graph."))
+ return 0;
+ var += sizeof("call-graph.") - 1;
+
+ if (!strcmp(var, "record-mode"))
+ return parse_callchain_record_opt(value);
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+ if (!strcmp(var, "dump-size")) {
+ unsigned long size = 0;
+ int ret;
+
+ ret = get_stack_size(value, &size);
+ callchain_param.dump_size = size;
+
+ return ret;
+ }
+#endif
+ if (!strcmp(var, "print-type"))
+ return parse_callchain_mode(value);
+ if (!strcmp(var, "order"))
+ return parse_callchain_order(value);
+ if (!strcmp(var, "sort-key"))
+ return parse_callchain_sort_key(value);
+ if (!strcmp(var, "threshold")) {
+ callchain_param.min_percent = strtod(value, &endptr);
+ if (value == endptr)
+ return -1;
+ }
+ if (!strcmp(var, "print-limit")) {
+ callchain_param.print_limit = strtod(value, &endptr);
+ if (value == endptr)
+ return -1;
+ }
+
+ return 0;
+}
+
static void
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
enum chain_mode mode)
@@ -618,8 +758,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
sort__has_parent) {
- return machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, parent, al, max_stack);
+ return thread__resolve_callchain(al->thread, evsel, sample,
+ parent, al, max_stack);
}
return 0;
}
@@ -672,3 +812,32 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
out:
return 1;
}
+
+char *callchain_list__sym_name(struct callchain_list *cl,
+ char *bf, size_t bfsize, bool show_dso)
+{
+ int printed;
+
+ if (cl->ms.sym) {
+ if (callchain_param.key == CCKEY_ADDRESS &&
+ cl->ms.map && !cl->srcline)
+ cl->srcline = get_srcline(cl->ms.map->dso,
+ map__rip_2objdump(cl->ms.map,
+ cl->ip),
+ cl->ms.sym, false);
+ if (cl->srcline)
+ printed = scnprintf(bf, bfsize, "%s %s",
+ cl->ms.sym->name, cl->srcline);
+ else
+ printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
+ } else
+ printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
+
+ if (show_dso)
+ scnprintf(bf + printed, bfsize - printed, " %s",
+ cl->ms.map ?
+ cl->ms.map->dso->short_name :
+ "unknown");
+
+ return bf;
+}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index da43619d6173..dbc08cf5f970 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -54,17 +54,24 @@ enum chain_key {
};
struct callchain_param {
+ bool enabled;
+ enum perf_call_graph_mode record_mode;
+ u32 dump_size;
enum chain_mode mode;
u32 print_limit;
double min_percent;
sort_chain_func_t sort;
enum chain_order order;
enum chain_key key;
+ bool branch_callstack;
};
+extern struct callchain_param callchain_param;
+
struct callchain_list {
u64 ip;
struct map_symbol ms;
+ char *srcline;
struct list_head list;
};
@@ -154,7 +161,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
struct option;
struct hist_entry;
-int record_parse_callchain(const char *arg, struct record_opts *opts);
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
int record_callchain_opt(const struct option *opt, const char *arg, int unset);
@@ -166,7 +172,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
bool hide_unresolved);
extern const char record_callchain_help[];
+int parse_callchain_record_opt(const char *arg);
int parse_callchain_report_opt(const char *arg);
+int perf_callchain_config(const char *var, const char *value);
static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
struct callchain_cursor *src)
@@ -178,15 +186,16 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
}
#ifdef HAVE_SKIP_CALLCHAIN_IDX
-extern int arch_skip_callchain_idx(struct machine *machine,
- struct thread *thread, struct ip_callchain *chain);
+extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain);
#else
-static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused,
- struct thread *thread __maybe_unused,
+static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused,
struct ip_callchain *chain __maybe_unused)
{
return -1;
}
#endif
+char *callchain_list__sym_name(struct callchain_list *cl,
+ char *bf, size_t bfsize, bool show_dso);
+
#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c
index c5d05ec17220..47b78b3f0325 100644
--- a/tools/perf/util/cloexec.c
+++ b/tools/perf/util/cloexec.c
@@ -1,7 +1,9 @@
+#include <sched.h>
#include "util.h"
#include "../perf.h"
#include "cloexec.h"
#include "asm/bug.h"
+#include "debug.h"
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
@@ -9,15 +11,30 @@ static int perf_flag_probe(void)
{
/* use 'safest' configuration as used in perf_evsel__fallback() */
struct perf_event_attr attr = {
- .type = PERF_COUNT_SW_CPU_CLOCK,
+ .type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_CPU_CLOCK,
+ .exclude_kernel = 1,
};
int fd;
int err;
+ int cpu;
+ pid_t pid = -1;
+ char sbuf[STRERR_BUFSIZE];
- /* check cloexec flag */
- fd = sys_perf_event_open(&attr, 0, -1, -1,
- PERF_FLAG_FD_CLOEXEC);
+ cpu = sched_getcpu();
+ if (cpu < 0)
+ cpu = 0;
+
+ while (1) {
+ /* check cloexec flag */
+ fd = sys_perf_event_open(&attr, pid, cpu, -1,
+ PERF_FLAG_FD_CLOEXEC);
+ if (fd < 0 && pid == -1 && errno == EACCES) {
+ pid = 0;
+ continue;
+ }
+ break;
+ }
err = errno;
if (fd >= 0) {
@@ -25,17 +42,17 @@ static int perf_flag_probe(void)
return 1;
}
- WARN_ONCE(err != EINVAL,
+ WARN_ONCE(err != EINVAL && err != EBUSY,
"perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
- err, strerror(err));
+ err, strerror_r(err, sbuf, sizeof(sbuf)));
/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
- fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
err = errno;
- if (WARN_ONCE(fd < 0,
+ if (WARN_ONCE(fd < 0 && err != EBUSY,
"perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
- err, strerror(err)))
+ err, strerror_r(err, sbuf, sizeof(sbuf))))
return -1;
close(fd);
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 87b8672eb413..f4654183d391 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -335,3 +335,19 @@ int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...)
va_end(args);
return value_color_snprintf(bf, size, fmt, percent);
}
+
+int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ double percent;
+ const char *color;
+
+ va_start(args, fmt);
+ len = va_arg(args, int);
+ percent = va_arg(args, double);
+ va_end(args);
+
+ color = get_percent_color(percent);
+ return color_snprintf(bf, size, color, fmt, len, percent);
+}
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h
index 7ff30a62a132..0a594b8a0c26 100644
--- a/tools/perf/util/color.h
+++ b/tools/perf/util/color.h
@@ -41,6 +41,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
int value_color_snprintf(char *bf, size_t size, const char *fmt, double value);
int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...);
+int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...);
int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
const char *get_percent_color(double percent);
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index f9e777629e21..b2bb59df65e1 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -74,7 +74,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
return new;
}
-struct comm *comm__new(const char *str, u64 timestamp)
+struct comm *comm__new(const char *str, u64 timestamp, bool exec)
{
struct comm *comm = zalloc(sizeof(*comm));
@@ -82,6 +82,7 @@ struct comm *comm__new(const char *str, u64 timestamp)
return NULL;
comm->start = timestamp;
+ comm->exec = exec;
comm->comm_str = comm_str__findnew(str, &comm_str_root);
if (!comm->comm_str) {
@@ -94,7 +95,7 @@ struct comm *comm__new(const char *str, u64 timestamp)
return comm;
}
-int comm__override(struct comm *comm, const char *str, u64 timestamp)
+int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
{
struct comm_str *new, *old = comm->comm_str;
@@ -106,6 +107,8 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp)
comm_str__put(old);
comm->comm_str = new;
comm->start = timestamp;
+ if (exec)
+ comm->exec = true;
return 0;
}
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index fac5bd51befc..71c9c39340d4 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -11,11 +11,17 @@ struct comm {
struct comm_str *comm_str;
u64 start;
struct list_head list;
+ bool exec;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
};
void comm__free(struct comm *comm);
-struct comm *comm__new(const char *str, u64 timestamp);
+struct comm *comm__new(const char *str, u64 timestamp, bool exec);
const char *comm__str(const struct comm *comm);
-int comm__override(struct comm *comm, const char *str, u64 timestamp);
+int comm__override(struct comm *comm, const char *str, u64 timestamp,
+ bool exec);
#endif /* __PERF_COMM_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 1e5e2e5af6b1..57ff826f150b 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data)
const unsigned char *bomptr = utf8_bom;
for (;;) {
- int c = get_next_char();
+ int line, c = get_next_char();
+
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data)
if (!isalpha(c))
break;
var[baselen] = tolower(c);
- if (get_value(fn, data, var, baselen+1) < 0)
+
+ /*
+ * The get_value function might or might not reach the '\n',
+ * so saving the current line number for error reporting.
+ */
+ line = config_linenr;
+ if (get_value(fn, data, var, baselen+1) < 0) {
+ config_linenr = line;
break;
+ }
}
die("bad config file line %d in %s", config_linenr, config_file_name);
}
@@ -286,6 +295,21 @@ static int parse_unit_factor(const char *end, unsigned long *val)
return 0;
}
+static int perf_parse_llong(const char *value, long long *ret)
+{
+ if (value && *value) {
+ char *end;
+ long long val = strtoll(value, &end, 0);
+ unsigned long factor = 1;
+
+ if (!parse_unit_factor(end, &factor))
+ return 0;
+ *ret = val * factor;
+ return 1;
+ }
+ return 0;
+}
+
static int perf_parse_long(const char *value, long *ret)
{
if (value && *value) {
@@ -307,6 +331,15 @@ static void die_bad_config(const char *name)
die("bad config value for '%s'", name);
}
+u64 perf_config_u64(const char *name, const char *value)
+{
+ long long ret = 0;
+
+ if (!perf_parse_llong(value, &ret))
+ die_bad_config(name);
+ return (u64) ret;
+}
+
int perf_config_int(const char *name, const char *value)
{
long ret = 0;
@@ -372,6 +405,9 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "ui."))
return perf_ui_config(var, value);
+ if (!prefixcmp(var, "call-graph."))
+ return perf_callchain_config(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
index 29d720cf5844..1921942fc2e0 100644
--- a/tools/perf/util/data.c
+++ b/tools/perf/util/data.c
@@ -50,12 +50,14 @@ static int open_file_read(struct perf_data_file *file)
{
struct stat st;
int fd;
+ char sbuf[STRERR_BUFSIZE];
fd = open(file->path, O_RDONLY);
if (fd < 0) {
int err = errno;
- pr_err("failed to open %s: %s", file->path, strerror(err));
+ pr_err("failed to open %s: %s", file->path,
+ strerror_r(err, sbuf, sizeof(sbuf)));
if (err == ENOENT && !strcmp(file->path, "perf.data"))
pr_err(" (try 'perf record' first)");
pr_err("\n");
@@ -88,6 +90,7 @@ static int open_file_read(struct perf_data_file *file)
static int open_file_write(struct perf_data_file *file)
{
int fd;
+ char sbuf[STRERR_BUFSIZE];
if (check_backup(file))
return -1;
@@ -95,7 +98,8 @@ static int open_file_write(struct perf_data_file *file)
fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if (fd < 0)
- pr_err("failed to open %s : %s\n", file->path, strerror(errno));
+ pr_err("failed to open %s : %s\n", file->path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return fd;
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
new file mode 100644
index 000000000000..c81dae399763
--- /dev/null
+++ b/tools/perf/util/db-export.c
@@ -0,0 +1,428 @@
+/*
+ * db-export.c: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <errno.h>
+
+#include "evsel.h"
+#include "machine.h"
+#include "thread.h"
+#include "comm.h"
+#include "symbol.h"
+#include "event.h"
+#include "util.h"
+#include "thread-stack.h"
+#include "db-export.h"
+
+struct deferred_export {
+ struct list_head node;
+ struct comm *comm;
+};
+
+static int db_export__deferred(struct db_export *dbe)
+{
+ struct deferred_export *de;
+ int err;
+
+ while (!list_empty(&dbe->deferred)) {
+ de = list_entry(dbe->deferred.next, struct deferred_export,
+ node);
+ err = dbe->export_comm(dbe, de->comm);
+ list_del(&de->node);
+ free(de);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void db_export__free_deferred(struct db_export *dbe)
+{
+ struct deferred_export *de;
+
+ while (!list_empty(&dbe->deferred)) {
+ de = list_entry(dbe->deferred.next, struct deferred_export,
+ node);
+ list_del(&de->node);
+ free(de);
+ }
+}
+
+static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
+{
+ struct deferred_export *de;
+
+ de = zalloc(sizeof(struct deferred_export));
+ if (!de)
+ return -ENOMEM;
+
+ de->comm = comm;
+ list_add_tail(&de->node, &dbe->deferred);
+
+ return 0;
+}
+
+int db_export__init(struct db_export *dbe)
+{
+ memset(dbe, 0, sizeof(struct db_export));
+ INIT_LIST_HEAD(&dbe->deferred);
+ return 0;
+}
+
+int db_export__flush(struct db_export *dbe)
+{
+ return db_export__deferred(dbe);
+}
+
+void db_export__exit(struct db_export *dbe)
+{
+ db_export__free_deferred(dbe);
+ call_return_processor__free(dbe->crp);
+ dbe->crp = NULL;
+}
+
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ if (evsel->db_id)
+ return 0;
+
+ evsel->db_id = ++dbe->evsel_last_db_id;
+
+ if (dbe->export_evsel)
+ return dbe->export_evsel(dbe, evsel);
+
+ return 0;
+}
+
+int db_export__machine(struct db_export *dbe, struct machine *machine)
+{
+ if (machine->db_id)
+ return 0;
+
+ machine->db_id = ++dbe->machine_last_db_id;
+
+ if (dbe->export_machine)
+ return dbe->export_machine(dbe, machine);
+
+ return 0;
+}
+
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm)
+{
+ u64 main_thread_db_id = 0;
+ int err;
+
+ if (thread->db_id)
+ return 0;
+
+ thread->db_id = ++dbe->thread_last_db_id;
+
+ if (thread->pid_ != -1) {
+ struct thread *main_thread;
+
+ if (thread->pid_ == thread->tid) {
+ main_thread = thread;
+ } else {
+ main_thread = machine__findnew_thread(machine,
+ thread->pid_,
+ thread->pid_);
+ if (!main_thread)
+ return -ENOMEM;
+ err = db_export__thread(dbe, main_thread, machine,
+ comm);
+ if (err)
+ return err;
+ if (comm) {
+ err = db_export__comm_thread(dbe, comm, thread);
+ if (err)
+ return err;
+ }
+ }
+ main_thread_db_id = main_thread->db_id;
+ }
+
+ if (dbe->export_thread)
+ return dbe->export_thread(dbe, thread, main_thread_db_id,
+ machine);
+
+ return 0;
+}
+
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread)
+{
+ int err;
+
+ if (comm->db_id)
+ return 0;
+
+ comm->db_id = ++dbe->comm_last_db_id;
+
+ if (dbe->export_comm) {
+ if (main_thread->comm_set)
+ err = dbe->export_comm(dbe, comm);
+ else
+ err = db_export__defer_comm(dbe, comm);
+ if (err)
+ return err;
+ }
+
+ return db_export__comm_thread(dbe, comm, main_thread);
+}
+
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread)
+{
+ u64 db_id;
+
+ db_id = ++dbe->comm_thread_last_db_id;
+
+ if (dbe->export_comm_thread)
+ return dbe->export_comm_thread(dbe, db_id, comm, thread);
+
+ return 0;
+}
+
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ if (dso->db_id)
+ return 0;
+
+ dso->db_id = ++dbe->dso_last_db_id;
+
+ if (dbe->export_dso)
+ return dbe->export_dso(dbe, dso, machine);
+
+ return 0;
+}
+
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ u64 *sym_db_id = symbol__priv(sym);
+
+ if (*sym_db_id)
+ return 0;
+
+ *sym_db_id = ++dbe->symbol_last_db_id;
+
+ if (dbe->export_symbol)
+ return dbe->export_symbol(dbe, sym, dso);
+
+ return 0;
+}
+
+static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
+{
+ if (thread->pid_ == thread->tid)
+ return thread;
+
+ if (thread->pid_ == -1)
+ return NULL;
+
+ return machine__find_thread(machine, thread->pid_, thread->pid_);
+}
+
+static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
+ u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
+{
+ int err;
+
+ if (al->map) {
+ struct dso *dso = al->map->dso;
+
+ err = db_export__dso(dbe, dso, al->machine);
+ if (err)
+ return err;
+ *dso_db_id = dso->db_id;
+
+ if (!al->sym) {
+ al->sym = symbol__new(al->addr, 0, 0, "unknown");
+ if (al->sym)
+ symbols__insert(&dso->symbols[al->map->type],
+ al->sym);
+ }
+
+ if (al->sym) {
+ u64 *db_id = symbol__priv(al->sym);
+
+ err = db_export__symbol(dbe, al->sym, dso);
+ if (err)
+ return err;
+ *sym_db_id = *db_id;
+ *offset = al->addr - al->sym->start;
+ }
+ }
+
+ return 0;
+}
+
+int db_export__branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name)
+{
+ if (dbe->export_branch_type)
+ return dbe->export_branch_type(dbe, branch_type, name);
+
+ return 0;
+}
+
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al)
+{
+ struct export_sample es = {
+ .event = event,
+ .sample = sample,
+ .evsel = evsel,
+ .thread = thread,
+ .al = al,
+ };
+ struct thread *main_thread;
+ struct comm *comm = NULL;
+ int err;
+
+ err = db_export__evsel(dbe, evsel);
+ if (err)
+ return err;
+
+ err = db_export__machine(dbe, al->machine);
+ if (err)
+ return err;
+
+ main_thread = get_main_thread(al->machine, thread);
+ if (main_thread)
+ comm = machine__thread_exec_comm(al->machine, main_thread);
+
+ err = db_export__thread(dbe, thread, al->machine, comm);
+ if (err)
+ return err;
+
+ if (comm) {
+ err = db_export__comm(dbe, comm, main_thread);
+ if (err)
+ return err;
+ es.comm_db_id = comm->db_id;
+ }
+
+ es.db_id = ++dbe->sample_last_db_id;
+
+ err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
+ if (err)
+ return err;
+
+ if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
+ sample_addr_correlates_sym(&evsel->attr)) {
+ struct addr_location addr_al;
+
+ perf_event__preprocess_sample_addr(event, sample, thread, &addr_al);
+ err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
+ &es.addr_sym_db_id, &es.addr_offset);
+ if (err)
+ return err;
+ if (dbe->crp) {
+ err = thread_stack__process(thread, comm, sample, al,
+ &addr_al, es.db_id,
+ dbe->crp);
+ if (err)
+ return err;
+ }
+ }
+
+ if (dbe->export_sample)
+ return dbe->export_sample(dbe, &es);
+
+ return 0;
+}
+
+static struct {
+ u32 branch_type;
+ const char *name;
+} branch_types[] = {
+ {0, "no branch"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"},
+ {PERF_IP_FLAG_BRANCH, "unconditional jump"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT,
+ "software interrupt"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT,
+ "return from interrupt"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET,
+ "system call"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET,
+ "return from system call"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
+ PERF_IP_FLAG_INTERRUPT, "hardware interrupt"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"},
+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"},
+ {0, NULL}
+};
+
+int db_export__branch_types(struct db_export *dbe)
+{
+ int i, err = 0;
+
+ for (i = 0; branch_types[i].name ; i++) {
+ err = db_export__branch_type(dbe, branch_types[i].branch_type,
+ branch_types[i].name);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+int db_export__call_path(struct db_export *dbe, struct call_path *cp)
+{
+ int err;
+
+ if (cp->db_id)
+ return 0;
+
+ if (cp->parent) {
+ err = db_export__call_path(dbe, cp->parent);
+ if (err)
+ return err;
+ }
+
+ cp->db_id = ++dbe->call_path_last_db_id;
+
+ if (dbe->export_call_path)
+ return dbe->export_call_path(dbe, cp);
+
+ return 0;
+}
+
+int db_export__call_return(struct db_export *dbe, struct call_return *cr)
+{
+ int err;
+
+ if (cr->db_id)
+ return 0;
+
+ err = db_export__call_path(dbe, cr->cp);
+ if (err)
+ return err;
+
+ cr->db_id = ++dbe->call_return_last_db_id;
+
+ if (dbe->export_call_return)
+ return dbe->export_call_return(dbe, cr);
+
+ return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
new file mode 100644
index 000000000000..adbd22d66798
--- /dev/null
+++ b/tools/perf/util/db-export.h
@@ -0,0 +1,107 @@
+/*
+ * db-export.h: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_DB_EXPORT_H
+#define __PERF_DB_EXPORT_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct perf_evsel;
+struct machine;
+struct thread;
+struct comm;
+struct dso;
+struct perf_sample;
+struct addr_location;
+struct call_return_processor;
+struct call_path;
+struct call_return;
+
+struct export_sample {
+ union perf_event *event;
+ struct perf_sample *sample;
+ struct perf_evsel *evsel;
+ struct thread *thread;
+ struct addr_location *al;
+ u64 db_id;
+ u64 comm_db_id;
+ u64 dso_db_id;
+ u64 sym_db_id;
+ u64 offset; /* ip offset from symbol start */
+ u64 addr_dso_db_id;
+ u64 addr_sym_db_id;
+ u64 addr_offset; /* addr offset from symbol start */
+};
+
+struct db_export {
+ int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+ int (*export_machine)(struct db_export *dbe, struct machine *machine);
+ int (*export_thread)(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine);
+ int (*export_comm)(struct db_export *dbe, struct comm *comm);
+ int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread);
+ int (*export_dso)(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+ int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+ int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
+ const char *name);
+ int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+ int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
+ int (*export_call_return)(struct db_export *dbe,
+ struct call_return *cr);
+ struct call_return_processor *crp;
+ u64 evsel_last_db_id;
+ u64 machine_last_db_id;
+ u64 thread_last_db_id;
+ u64 comm_last_db_id;
+ u64 comm_thread_last_db_id;
+ u64 dso_last_db_id;
+ u64 symbol_last_db_id;
+ u64 sample_last_db_id;
+ u64 call_path_last_db_id;
+ u64 call_return_last_db_id;
+ struct list_head deferred;
+};
+
+int db_export__init(struct db_export *dbe);
+int db_export__flush(struct db_export *dbe);
+void db_export__exit(struct db_export *dbe);
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__machine(struct db_export *dbe, struct machine *machine);
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm);
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread);
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread);
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+int db_export__branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name);
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al);
+
+int db_export__branch_types(struct db_export *dbe);
+
+int db_export__call_path(struct db_export *dbe, struct call_path *cp);
+int db_export__call_return(struct db_export *dbe, struct call_return *cr);
+
+#endif
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 71d419362634..ad60b2f20258 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -13,15 +13,20 @@
#include "util.h"
#include "target.h"
+#define NSECS_PER_SEC 1000000000ULL
+#define NSECS_PER_USEC 1000ULL
+
int verbose;
bool dump_trace = false, quiet = false;
+int debug_ordered_events;
+static int redirect_to_stderr;
static int _eprintf(int level, int var, const char *fmt, va_list args)
{
int ret = 0;
if (var >= level) {
- if (use_browser >= 1)
+ if (use_browser >= 1 && !redirect_to_stderr)
ui_helpline__vshow(fmt, args);
else
ret = vfprintf(stderr, fmt, args);
@@ -42,6 +47,35 @@ int eprintf(int level, int var, const char *fmt, ...)
return ret;
}
+static int __eprintf_time(u64 t, const char *fmt, va_list args)
+{
+ int ret = 0;
+ u64 secs, usecs, nsecs = t;
+
+ secs = nsecs / NSECS_PER_SEC;
+ nsecs -= secs * NSECS_PER_SEC;
+ usecs = nsecs / NSECS_PER_USEC;
+
+ ret = fprintf(stderr, "[%13" PRIu64 ".%06" PRIu64 "] ",
+ secs, usecs);
+ ret += vfprintf(stderr, fmt, args);
+ return ret;
+}
+
+int eprintf_time(int level, int var, u64 t, const char *fmt, ...)
+{
+ int ret = 0;
+ va_list args;
+
+ if (var >= level) {
+ va_start(args, fmt);
+ ret = __eprintf_time(t, fmt, args);
+ va_end(args);
+ }
+
+ return ret;
+}
+
/*
* Overloading libtraceevent standard info print
* function, display with -v in perf.
@@ -110,7 +144,9 @@ static struct debug_variable {
const char *name;
int *ptr;
} debug_variables[] = {
- { .name = "verbose", .ptr = &verbose },
+ { .name = "verbose", .ptr = &verbose },
+ { .name = "ordered-events", .ptr = &debug_ordered_events},
+ { .name = "stderr", .ptr = &redirect_to_stderr},
{ .name = NULL, }
};
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 89fb6b0f7ab2..be264d6f3b30 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -3,6 +3,7 @@
#define __PERF_DEBUG_H
#include <stdbool.h>
+#include <string.h>
#include "event.h"
#include "../ui/helpline.h"
#include "../ui/progress.h"
@@ -10,6 +11,7 @@
extern int verbose;
extern bool quiet, dump_trace;
+extern int debug_ordered_events;
#ifndef pr_fmt
#define pr_fmt(fmt) fmt
@@ -29,6 +31,14 @@ extern bool quiet, dump_trace;
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_time_N(n, var, t, fmt, ...) \
+ eprintf_time(n, var, t, fmt, ##__VA_ARGS__)
+
+#define pr_oe_time(t, fmt, ...) pr_time_N(1, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_oe_time2(t, fmt, ...) pr_time_N(2, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__)
+
+#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */
+
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(union perf_event *event);
@@ -38,6 +48,7 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
void pr_stat(const char *fmt, ...);
int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
+int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
int perf_debug_option(const char *str);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 90d02c661dd4..45be944d450a 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso)
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
+ [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm',
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
+ [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M',
[DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
};
@@ -37,6 +39,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
{
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
int ret = 0;
+ size_t len;
switch (type) {
case DSO_BINARY_TYPE__DEBUGLINK: {
@@ -60,26 +63,25 @@ int dso__read_binary_type_filename(const struct dso *dso,
break;
case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
- snprintf(filename, size, "%s/usr/lib/debug%s.debug",
- symbol_conf.symfs, dso->long_name);
+ len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
+ snprintf(filename + len, size - len, "%s.debug", dso->long_name);
break;
case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
- snprintf(filename, size, "%s/usr/lib/debug%s",
- symbol_conf.symfs, dso->long_name);
+ len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
+ snprintf(filename + len, size - len, "%s", dso->long_name);
break;
case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
{
const char *last_slash;
- size_t len;
size_t dir_size;
last_slash = dso->long_name + dso->long_name_len;
while (last_slash != dso->long_name && *last_slash != '/')
last_slash--;
- len = scnprintf(filename, size, "%s", symbol_conf.symfs);
+ len = __symbol__join_symfs(filename, size, "");
dir_size = last_slash - dso->long_name + 2;
if (dir_size > (size - len)) {
ret = -1;
@@ -100,26 +102,26 @@ int dso__read_binary_type_filename(const struct dso *dso,
build_id__sprintf(dso->build_id,
sizeof(dso->build_id),
build_id_hex);
- snprintf(filename, size,
- "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
- symbol_conf.symfs, build_id_hex, build_id_hex + 2);
+ len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/");
+ snprintf(filename + len, size - len, "%.2s/%s.debug",
+ build_id_hex, build_id_hex + 2);
break;
case DSO_BINARY_TYPE__VMLINUX:
case DSO_BINARY_TYPE__GUEST_VMLINUX:
case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
- snprintf(filename, size, "%s%s",
- symbol_conf.symfs, dso->long_name);
+ __symbol__join_symfs(filename, size, dso->long_name);
break;
case DSO_BINARY_TYPE__GUEST_KMODULE:
- snprintf(filename, size, "%s%s%s", symbol_conf.symfs,
- root_dir, dso->long_name);
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ path__join3(filename, size, symbol_conf.symfs,
+ root_dir, dso->long_name);
break;
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
- snprintf(filename, size, "%s%s", symbol_conf.symfs,
- dso->long_name);
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ __symbol__join_symfs(filename, size, dso->long_name);
break;
case DSO_BINARY_TYPE__KCORE:
@@ -139,6 +141,73 @@ int dso__read_binary_type_filename(const struct dso *dso,
return ret;
}
+static const struct {
+ const char *fmt;
+ int (*decompress)(const char *input, int output);
+} compressions[] = {
+#ifdef HAVE_ZLIB_SUPPORT
+ { "gz", gzip_decompress_to_file },
+#endif
+ { NULL, NULL },
+};
+
+bool is_supported_compression(const char *ext)
+{
+ unsigned i;
+
+ for (i = 0; compressions[i].fmt; i++) {
+ if (!strcmp(ext, compressions[i].fmt))
+ return true;
+ }
+ return false;
+}
+
+bool is_kmodule_extension(const char *ext)
+{
+ if (strncmp(ext, "ko", 2))
+ return false;
+
+ if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3)))
+ return true;
+
+ return false;
+}
+
+bool is_kernel_module(const char *pathname, bool *compressed)
+{
+ const char *ext = strrchr(pathname, '.');
+
+ if (ext == NULL)
+ return false;
+
+ if (is_supported_compression(ext + 1)) {
+ if (compressed)
+ *compressed = true;
+ ext -= 3;
+ } else if (compressed)
+ *compressed = false;
+
+ return is_kmodule_extension(ext + 1);
+}
+
+bool decompress_to_file(const char *ext, const char *filename, int output_fd)
+{
+ unsigned i;
+
+ for (i = 0; compressions[i].fmt; i++) {
+ if (!strcmp(ext, compressions[i].fmt))
+ return !compressions[i].decompress(filename,
+ output_fd);
+ }
+ return false;
+}
+
+bool dso__needs_decompress(struct dso *dso)
+{
+ return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
+ dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
+}
+
/*
* Global list of open DSOs and the counter.
*/
@@ -164,13 +233,15 @@ static void close_first_dso(void);
static int do_open(char *name)
{
int fd;
+ char sbuf[STRERR_BUFSIZE];
do {
fd = open(name, O_RDONLY);
if (fd >= 0)
return fd;
- pr_debug("dso open failed, mmap: %s\n", strerror(errno));
+ pr_debug("dso open failed, mmap: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
if (!dso__data_open_cnt || errno != EMFILE)
break;
@@ -532,10 +603,12 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
static int data_file_size(struct dso *dso)
{
struct stat st;
+ char sbuf[STRERR_BUFSIZE];
if (!dso->data.file_size) {
if (fstat(dso->data.fd, &st)) {
- pr_err("dso mmap failed, fstat: %s\n", strerror(errno));
+ pr_err("dso mmap failed, fstat: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -1;
}
dso->data.file_size = st.st_size;
@@ -651,6 +724,65 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
return dso;
}
+/*
+ * Find a matching entry and/or link current entry to RB tree.
+ * Either one of the dso or name parameter must be non-NULL or the
+ * function will not work.
+ */
+static struct dso *dso__findlink_by_longname(struct rb_root *root,
+ struct dso *dso, const char *name)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+
+ if (!name)
+ name = dso->long_name;
+ /*
+ * Find node with the matching name
+ */
+ while (*p) {
+ struct dso *this = rb_entry(*p, struct dso, rb_node);
+ int rc = strcmp(name, this->long_name);
+
+ parent = *p;
+ if (rc == 0) {
+ /*
+ * In case the new DSO is a duplicate of an existing
+ * one, print an one-time warning & put the new entry
+ * at the end of the list of duplicates.
+ */
+ if (!dso || (dso == this))
+ return this; /* Find matching dso */
+ /*
+ * The core kernel DSOs may have duplicated long name.
+ * In this case, the short name should be different.
+ * Comparing the short names to differentiate the DSOs.
+ */
+ rc = strcmp(dso->short_name, this->short_name);
+ if (rc == 0) {
+ pr_err("Duplicated dso name: %s\n", name);
+ return NULL;
+ }
+ }
+ if (rc < 0)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+ if (dso) {
+ /* Add new node and rebalance tree */
+ rb_link_node(&dso->rb_node, parent, p);
+ rb_insert_color(&dso->rb_node, root);
+ }
+ return NULL;
+}
+
+static inline struct dso *
+dso__find_by_longname(const struct rb_root *root, const char *name)
+{
+ return dso__findlink_by_longname((struct rb_root *)root, NULL, name);
+}
+
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
{
if (name == NULL)
@@ -753,6 +885,7 @@ struct dso *dso__new(const char *name)
dso->a2l_fails = 1;
dso->kernel = DSO_TYPE_USER;
dso->needs_swap = DSO_SWAP__UNSET;
+ RB_CLEAR_NODE(&dso->rb_node);
INIT_LIST_HEAD(&dso->node);
INIT_LIST_HEAD(&dso->data.open_entry);
}
@@ -763,6 +896,10 @@ struct dso *dso__new(const char *name)
void dso__delete(struct dso *dso)
{
int i;
+
+ if (!RB_EMPTY_NODE(&dso->rb_node))
+ pr_err("DSO %s is still in rbtree when being deleted!\n",
+ dso->long_name);
for (i = 0; i < MAP__NR_TYPES; ++i)
symbols__delete(&dso->symbols[i]);
@@ -849,35 +986,34 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
return have_build_id;
}
-void dsos__add(struct list_head *head, struct dso *dso)
+void dsos__add(struct dsos *dsos, struct dso *dso)
{
- list_add_tail(&dso->node, head);
+ list_add_tail(&dso->node, &dsos->head);
+ dso__findlink_by_longname(&dsos->root, dso, NULL);
}
-struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short)
+struct dso *dsos__find(const struct dsos *dsos, const char *name,
+ bool cmp_short)
{
struct dso *pos;
if (cmp_short) {
- list_for_each_entry(pos, head, node)
+ list_for_each_entry(pos, &dsos->head, node)
if (strcmp(pos->short_name, name) == 0)
return pos;
return NULL;
}
- list_for_each_entry(pos, head, node)
- if (strcmp(pos->long_name, name) == 0)
- return pos;
- return NULL;
+ return dso__find_by_longname(&dsos->root, name);
}
-struct dso *__dsos__findnew(struct list_head *head, const char *name)
+struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
{
- struct dso *dso = dsos__find(head, name, false);
+ struct dso *dso = dsos__find(dsos, name, false);
if (!dso) {
dso = dso__new(name);
if (dso != NULL) {
- dsos__add(head, dso);
+ dsos__add(dsos, dso);
dso__set_basename(dso);
}
}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 5e463c0964d4..3782c82c6e44 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -22,7 +22,9 @@ enum dso_binary_type {
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
DSO_BINARY_TYPE__GUEST_KMODULE,
+ DSO_BINARY_TYPE__GUEST_KMODULE_COMP,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+ DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP,
DSO_BINARY_TYPE__KCORE,
DSO_BINARY_TYPE__GUEST_KCORE,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
@@ -90,8 +92,18 @@ struct dso_cache {
char data[0];
};
+/*
+ * DSOs are put into both a list for fast iteration and rbtree for fast
+ * long name lookup.
+ */
+struct dsos {
+ struct list_head head;
+ struct rb_root root; /* rbtree root sorted by long name */
+};
+
struct dso {
struct list_head node;
+ struct rb_node rb_node; /* rbtree node sorted by long name */
struct rb_root symbols[MAP__NR_TYPES];
struct rb_root symbol_names[MAP__NR_TYPES];
void *a2l;
@@ -117,6 +129,7 @@ struct dso {
const char *long_name;
u16 long_name_len;
u16 short_name_len;
+ void *dwfl; /* DWARF debug info */
/* dso data file */
struct {
@@ -128,6 +141,11 @@ struct dso {
struct list_head open_entry;
} data;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
+
char name[0];
};
@@ -169,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);
char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size);
+bool is_supported_compression(const char *ext);
+bool is_kmodule_extension(const char *ext);
+bool is_kernel_module(const char *pathname, bool *compressed);
+bool decompress_to_file(const char *ext, const char *filename, int output_fd);
+bool dso__needs_decompress(struct dso *dso);
/*
* The dso__data_* external interface provides following functions:
@@ -224,10 +247,10 @@ struct map *dso__new_map(const char *name);
struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
const char *short_name, int dso_type);
-void dsos__add(struct list_head *head, struct dso *dso);
-struct dso *dsos__find(const struct list_head *head, const char *name,
+void dsos__add(struct dsos *dsos, struct dso *dso);
+struct dso *dsos__find(const struct dsos *dsos, const char *name,
bool cmp_short);
-struct dso *__dsos__findnew(struct list_head *head, const char *name);
+struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 1398c83d896d..6c6d044e959a 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
[PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
[PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
+ [PERF_RECORD_ID_INDEX] = "ID_INDEX",
};
const char *perf_event__name(unsigned int id)
@@ -558,13 +559,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
struct map *map;
struct kmap *kmap;
int err;
+ union perf_event *event;
+
+ if (machine->vmlinux_maps[0] == NULL)
+ return -1;
+
/*
* We should get this from /sys/kernel/sections/.text, but till that is
* available use this, and after it is use this as a fallback for older
* kernels.
*/
- union perf_event *event = zalloc((sizeof(event->mmap) +
- machine->id_hdr_size));
+ event = zalloc((sizeof(event->mmap) + machine->id_hdr_size));
if (event == NULL) {
pr_debug("Not enough memory synthesizing mmap event "
"for kernel modules\n");
@@ -726,12 +731,12 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,
return machine__process_event(machine, event, sample);
}
-void thread__find_addr_map(struct thread *thread,
- struct machine *machine, u8 cpumode,
+void thread__find_addr_map(struct thread *thread, u8 cpumode,
enum map_type type, u64 addr,
struct addr_location *al)
{
struct map_groups *mg = thread->mg;
+ struct machine *machine = mg->machine;
bool load_map = false;
al->machine = machine;
@@ -784,9 +789,9 @@ try_again:
* "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list.
*/
- if ((long long)al->addr < 0 &&
- cpumode == PERF_RECORD_MISC_USER &&
- machine && mg != &machine->kmaps) {
+ if (cpumode == PERF_RECORD_MISC_USER && machine &&
+ mg != &machine->kmaps &&
+ machine__kernel_ip(machine, al->addr)) {
mg = &machine->kmaps;
load_map = true;
goto try_again;
@@ -802,14 +807,14 @@ try_again:
}
}
-void thread__find_addr_location(struct thread *thread, struct machine *machine,
+void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al)
{
- thread__find_addr_map(thread, machine, cpumode, type, addr, al);
+ thread__find_addr_map(thread, cpumode, type, addr, al);
if (al->map != NULL)
al->sym = map__find_symbol(al->map, al->addr,
- machine->symbol_filter);
+ thread->mg->machine->symbol_filter);
else
al->sym = NULL;
}
@@ -838,8 +843,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
machine->vmlinux_maps[MAP__FUNCTION] == NULL)
machine__create_kernel_maps(machine);
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->ip, al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al);
dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>");
@@ -898,16 +902,14 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr)
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
- struct machine *machine,
struct thread *thread,
struct addr_location *al)
{
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->addr, al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al);
if (!al->map)
- thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
+ thread__find_addr_map(thread, cpumode, MAP__VARIABLE,
sample->addr, al);
al->cpu = sample->cpu;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 94d6976180da..c4ffe2bd0738 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -143,6 +143,32 @@ struct branch_stack {
struct branch_entry entries[0];
};
+enum {
+ PERF_IP_FLAG_BRANCH = 1ULL << 0,
+ PERF_IP_FLAG_CALL = 1ULL << 1,
+ PERF_IP_FLAG_RETURN = 1ULL << 2,
+ PERF_IP_FLAG_CONDITIONAL = 1ULL << 3,
+ PERF_IP_FLAG_SYSCALLRET = 1ULL << 4,
+ PERF_IP_FLAG_ASYNC = 1ULL << 5,
+ PERF_IP_FLAG_INTERRUPT = 1ULL << 6,
+ PERF_IP_FLAG_TX_ABORT = 1ULL << 7,
+ PERF_IP_FLAG_TRACE_BEGIN = 1ULL << 8,
+ PERF_IP_FLAG_TRACE_END = 1ULL << 9,
+ PERF_IP_FLAG_IN_TX = 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK (\
+ PERF_IP_FLAG_BRANCH |\
+ PERF_IP_FLAG_CALL |\
+ PERF_IP_FLAG_RETURN |\
+ PERF_IP_FLAG_CONDITIONAL |\
+ PERF_IP_FLAG_SYSCALLRET |\
+ PERF_IP_FLAG_ASYNC |\
+ PERF_IP_FLAG_INTERRUPT |\
+ PERF_IP_FLAG_TX_ABORT |\
+ PERF_IP_FLAG_TRACE_BEGIN |\
+ PERF_IP_FLAG_TRACE_END)
+
struct perf_sample {
u64 ip;
u32 pid, tid;
@@ -156,10 +182,13 @@ struct perf_sample {
u32 cpu;
u32 raw_size;
u64 data_src;
+ u32 flags;
+ u16 insn_len;
void *raw_data;
struct ip_callchain *callchain;
struct branch_stack *branch_stack;
struct regs_dump user_regs;
+ struct regs_dump intr_regs;
struct stack_dump user_stack;
struct sample_read read;
};
@@ -185,9 +214,37 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_BUILD_ID = 67,
PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_ID_INDEX = 69,
PERF_RECORD_HEADER_MAX
};
+/*
+ * The kernel collects the number of events it couldn't send in a stretch and
+ * when possible sends this number in a PERF_RECORD_LOST event. The number of
+ * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
+ * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
+ * the sum of all struct lost_event.lost fields reported.
+ *
+ * The total_period is needed because by default auto-freq is used, so
+ * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
+ * the total number of low level events, it is necessary to to sum all struct
+ * sample_event.period and stash the result in total_period.
+ */
+struct events_stats {
+ u64 total_period;
+ u64 total_non_filtered_period;
+ u64 total_lost;
+ u64 total_invalid_chains;
+ u32 nr_events[PERF_RECORD_HEADER_MAX];
+ u32 nr_non_filtered_samples;
+ u32 nr_lost_warned;
+ u32 nr_unknown_events;
+ u32 nr_invalid_chains;
+ u32 nr_unknown_id;
+ u32 nr_unprocessable_samples;
+ u32 nr_unordered_events;
+};
+
struct attr_event {
struct perf_event_header header;
struct perf_event_attr attr;
@@ -211,6 +268,19 @@ struct tracing_data_event {
u32 size;
};
+struct id_index_entry {
+ u64 id;
+ u64 idx;
+ u64 cpu;
+ u64 tid;
+};
+
+struct id_index_event {
+ struct perf_event_header header;
+ u64 nr;
+ struct id_index_entry entries[0];
+};
+
union perf_event {
struct perf_event_header header;
struct mmap_event mmap;
@@ -225,6 +295,7 @@ union perf_event {
struct event_type_event event_type;
struct tracing_data_event tracing_data;
struct build_id_event build_id;
+ struct id_index_event id_index;
};
void perf_event__print_totals(void);
@@ -294,7 +365,6 @@ bool is_bts_event(struct perf_event_attr *attr);
bool sample_addr_correlates_sym(struct perf_event_attr *attr);
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
- struct machine *machine,
struct thread *thread,
struct addr_location *al);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 814e954c1318..cfbe2b99b9aa 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -25,6 +25,9 @@
#include <linux/bitops.h>
#include <linux/hash.h>
+static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
+static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
+
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
@@ -37,6 +40,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
INIT_HLIST_HEAD(&evlist->heads[i]);
INIT_LIST_HEAD(&evlist->entries);
perf_evlist__set_maps(evlist, cpus, threads);
+ fdarray__init(&evlist->pollfd, 64);
evlist->workload.pid = -1;
}
@@ -102,7 +106,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)
void perf_evlist__exit(struct perf_evlist *evlist)
{
zfree(&evlist->mmap);
- zfree(&evlist->pollfd);
+ fdarray__exit(&evlist->pollfd);
}
void perf_evlist__delete(struct perf_evlist *evlist)
@@ -122,6 +126,7 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
{
list_add_tail(&entry->node, &evlist->entries);
entry->idx = evlist->nr_entries;
+ entry->tracking = !entry->idx;
if (!evlist->nr_entries++)
perf_evlist__set_id_pos(evlist);
@@ -265,17 +270,27 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
return 0;
}
+static int perf_evlist__nr_threads(struct perf_evlist *evlist,
+ struct perf_evsel *evsel)
+{
+ if (evsel->system_wide)
+ return 1;
+ else
+ return thread_map__nr(evlist->threads);
+}
+
void perf_evlist__disable(struct perf_evlist *evlist)
{
int cpu, thread;
struct perf_evsel *pos;
int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = thread_map__nr(evlist->threads);
+ int nr_threads;
for (cpu = 0; cpu < nr_cpus; cpu++) {
evlist__for_each(evlist, pos) {
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
continue;
+ nr_threads = perf_evlist__nr_threads(evlist, pos);
for (thread = 0; thread < nr_threads; thread++)
ioctl(FD(pos, cpu, thread),
PERF_EVENT_IOC_DISABLE, 0);
@@ -288,12 +303,13 @@ void perf_evlist__enable(struct perf_evlist *evlist)
int cpu, thread;
struct perf_evsel *pos;
int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = thread_map__nr(evlist->threads);
+ int nr_threads;
for (cpu = 0; cpu < nr_cpus; cpu++) {
evlist__for_each(evlist, pos) {
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
continue;
+ nr_threads = perf_evlist__nr_threads(evlist, pos);
for (thread = 0; thread < nr_threads; thread++)
ioctl(FD(pos, cpu, thread),
PERF_EVENT_IOC_ENABLE, 0);
@@ -305,12 +321,14 @@ int perf_evlist__disable_event(struct perf_evlist *evlist,
struct perf_evsel *evsel)
{
int cpu, thread, err;
+ int nr_cpus = cpu_map__nr(evlist->cpus);
+ int nr_threads = perf_evlist__nr_threads(evlist, evsel);
if (!evsel->fd)
return 0;
- for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
- for (thread = 0; thread < evlist->threads->nr; thread++) {
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ for (thread = 0; thread < nr_threads; thread++) {
err = ioctl(FD(evsel, cpu, thread),
PERF_EVENT_IOC_DISABLE, 0);
if (err)
@@ -324,12 +342,14 @@ int perf_evlist__enable_event(struct perf_evlist *evlist,
struct perf_evsel *evsel)
{
int cpu, thread, err;
+ int nr_cpus = cpu_map__nr(evlist->cpus);
+ int nr_threads = perf_evlist__nr_threads(evlist, evsel);
if (!evsel->fd)
return -EINVAL;
- for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
- for (thread = 0; thread < evlist->threads->nr; thread++) {
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ for (thread = 0; thread < nr_threads; thread++) {
err = ioctl(FD(evsel, cpu, thread),
PERF_EVENT_IOC_ENABLE, 0);
if (err)
@@ -339,21 +359,111 @@ int perf_evlist__enable_event(struct perf_evlist *evlist,
return 0;
}
-static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
+static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
+ struct perf_evsel *evsel, int cpu)
+{
+ int thread, err;
+ int nr_threads = perf_evlist__nr_threads(evlist, evsel);
+
+ if (!evsel->fd)
+ return -EINVAL;
+
+ for (thread = 0; thread < nr_threads; thread++) {
+ err = ioctl(FD(evsel, cpu, thread),
+ PERF_EVENT_IOC_ENABLE, 0);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
+ struct perf_evsel *evsel,
+ int thread)
+{
+ int cpu, err;
+ int nr_cpus = cpu_map__nr(evlist->cpus);
+
+ if (!evsel->fd)
+ return -EINVAL;
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
+ struct perf_evsel *evsel, int idx)
+{
+ bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus);
+
+ if (per_cpu_mmaps)
+ return perf_evlist__enable_event_cpu(evlist, evsel, idx);
+ else
+ return perf_evlist__enable_event_thread(evlist, evsel, idx);
+}
+
+int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
{
int nr_cpus = cpu_map__nr(evlist->cpus);
int nr_threads = thread_map__nr(evlist->threads);
- int nfds = nr_cpus * nr_threads * evlist->nr_entries;
- evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
- return evlist->pollfd != NULL ? 0 : -ENOMEM;
+ int nfds = 0;
+ struct perf_evsel *evsel;
+
+ evlist__for_each(evlist, evsel) {
+ if (evsel->system_wide)
+ nfds += nr_cpus;
+ else
+ nfds += nr_cpus * nr_threads;
+ }
+
+ if (fdarray__available_entries(&evlist->pollfd) < nfds &&
+ fdarray__grow(&evlist->pollfd, nfds) < 0)
+ return -ENOMEM;
+
+ return 0;
}
-void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
+static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx)
{
- fcntl(fd, F_SETFL, O_NONBLOCK);
- evlist->pollfd[evlist->nr_fds].fd = fd;
- evlist->pollfd[evlist->nr_fds].events = POLLIN;
- evlist->nr_fds++;
+ int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP);
+ /*
+ * Save the idx so that when we filter out fds POLLHUP'ed we can
+ * close the associated evlist->mmap[] entry.
+ */
+ if (pos >= 0) {
+ evlist->pollfd.priv[pos].idx = idx;
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ }
+
+ return pos;
+}
+
+int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
+{
+ return __perf_evlist__add_pollfd(evlist, fd, -1);
+}
+
+static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd)
+{
+ struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd);
+
+ perf_evlist__mmap_put(evlist, fda->priv[fd].idx);
+}
+
+int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
+{
+ return fdarray__filter(&evlist->pollfd, revents_and_mask,
+ perf_evlist__munmap_filtered);
+}
+
+int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
+{
+ return fdarray__poll(&evlist->pollfd, timeout);
}
static void perf_evlist__id_hash(struct perf_evlist *evlist,
@@ -417,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
return 0;
}
+static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
+ struct perf_evsel *evsel, int idx, int cpu,
+ int thread)
+{
+ struct perf_sample_id *sid = SID(evsel, cpu, thread);
+ sid->idx = idx;
+ if (evlist->cpus && cpu >= 0)
+ sid->cpu = evlist->cpus->map[cpu];
+ else
+ sid->cpu = -1;
+ if (!evsel->system_wide && evlist->threads && thread >= 0)
+ sid->tid = evlist->threads->map[thread];
+ else
+ sid->tid = -1;
+}
+
struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
{
struct hlist_head *head;
@@ -566,14 +692,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
return event;
}
+static bool perf_mmap__empty(struct perf_mmap *md)
+{
+ return perf_mmap__read_head(md) != md->prev;
+}
+
+static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx)
+{
+ ++evlist->mmap[idx].refcnt;
+}
+
+static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx)
+{
+ BUG_ON(evlist->mmap[idx].refcnt == 0);
+
+ if (--evlist->mmap[idx].refcnt == 0)
+ __perf_evlist__munmap(evlist, idx);
+}
+
void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
{
+ struct perf_mmap *md = &evlist->mmap[idx];
+
if (!evlist->overwrite) {
- struct perf_mmap *md = &evlist->mmap[idx];
unsigned int old = md->prev;
perf_mmap__write_tail(md, old);
}
+
+ if (md->refcnt == 1 && perf_mmap__empty(md))
+ perf_evlist__mmap_put(evlist, idx);
}
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
@@ -581,6 +729,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
if (evlist->mmap[idx].base != NULL) {
munmap(evlist->mmap[idx].base, evlist->mmap_len);
evlist->mmap[idx].base = NULL;
+ evlist->mmap[idx].refcnt = 0;
}
}
@@ -614,6 +763,20 @@ struct mmap_params {
static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
struct mmap_params *mp, int fd)
{
+ /*
+ * The last one will be done at perf_evlist__mmap_consume(), so that we
+ * make sure we don't prevent tools from consuming every last event in
+ * the ring buffer.
+ *
+ * I.e. we can get the POLLHUP meaning that the fd doesn't exist
+ * anymore, but the last events for it are still in the ring buffer,
+ * waiting to be consumed.
+ *
+ * Tools can chose to ignore this at their own discretion, but the
+ * evlist layer can't just drop it when filtering events in
+ * perf_evlist__filter_pollfd().
+ */
+ evlist->mmap[idx].refcnt = 2;
evlist->mmap[idx].prev = 0;
evlist->mmap[idx].mask = mp->mask;
evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot,
@@ -625,7 +788,6 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
return -1;
}
- perf_evlist__add_pollfd(evlist, fd);
return 0;
}
@@ -636,7 +798,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel) {
- int fd = FD(evsel, cpu, thread);
+ int fd;
+
+ if (evsel->system_wide && thread)
+ continue;
+
+ fd = FD(evsel, cpu, thread);
if (*output == -1) {
*output = fd;
@@ -645,11 +812,30 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
} else {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
return -1;
+
+ perf_evlist__mmap_get(evlist, idx);
}
- if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
- perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
+ /*
+ * The system_wide flag causes a selected event to be opened
+ * always without a pid. Consequently it will never get a
+ * POLLHUP, but it is used for tracking in combination with
+ * other events, so it should not need to be polled anyway.
+ * Therefore don't add it for polling.
+ */
+ if (!evsel->system_wide &&
+ __perf_evlist__add_pollfd(evlist, fd, idx) < 0) {
+ perf_evlist__mmap_put(evlist, idx);
return -1;
+ }
+
+ if (evsel->attr.read_format & PERF_FORMAT_ID) {
+ if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
+ fd) < 0)
+ return -1;
+ perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
+ thread);
+ }
}
return 0;
@@ -804,7 +990,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)
return -ENOMEM;
- if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
+ if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
return -ENOMEM;
evlist->overwrite = overwrite;
@@ -845,6 +1031,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
out_delete_threads:
thread_map__delete(evlist->threads);
+ evlist->threads = NULL;
return -1;
}
@@ -1017,11 +1204,51 @@ void perf_evlist__close(struct perf_evlist *evlist)
}
}
+static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
+{
+ int err = -ENOMEM;
+
+ /*
+ * Try reading /sys/devices/system/cpu/online to get
+ * an all cpus map.
+ *
+ * FIXME: -ENOMEM is the best we can do here, the cpu_map
+ * code needs an overhaul to properly forward the
+ * error, and we may not want to do that fallback to a
+ * default cpu identity map :-\
+ */
+ evlist->cpus = cpu_map__new(NULL);
+ if (evlist->cpus == NULL)
+ goto out;
+
+ evlist->threads = thread_map__new_dummy();
+ if (evlist->threads == NULL)
+ goto out_free_cpus;
+
+ err = 0;
+out:
+ return err;
+out_free_cpus:
+ cpu_map__delete(evlist->cpus);
+ evlist->cpus = NULL;
+ goto out;
+}
+
int perf_evlist__open(struct perf_evlist *evlist)
{
struct perf_evsel *evsel;
int err;
+ /*
+ * Default: one fd per CPU, all threads, aka systemwide
+ * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL
+ */
+ if (evlist->threads == NULL && evlist->cpus == NULL) {
+ err = perf_evlist__create_syswide_maps(evlist);
+ if (err < 0)
+ goto out_err;
+ }
+
perf_evlist__update_id_pos(evlist);
evlist__for_each(evlist, evsel) {
@@ -1061,6 +1288,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
}
if (!evlist->workload.pid) {
+ int ret;
+
if (pipe_output)
dup2(2, 1);
@@ -1078,8 +1307,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
/*
* Wait until the parent tells us to go.
*/
- if (read(go_pipe[0], &bf, 1) == -1)
- perror("unable to read pipe");
+ ret = read(go_pipe[0], &bf, 1);
+ /*
+ * The parent will ask for the execvp() to be performed by
+ * writing exactly one byte, in workload.cork_fd, usually via
+ * perf_evlist__start_workload().
+ *
+ * For cancelling the workload without actuallin running it,
+ * the parent will just close workload.cork_fd, without writing
+ * anything, i.e. read will return zero and we just exit()
+ * here.
+ */
+ if (ret != 1) {
+ if (ret == -1)
+ perror("unable to read pipe");
+ exit(ret);
+ }
execvp(argv[0], (char **)argv);
@@ -1102,8 +1345,14 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
sigaction(SIGUSR1, &act, NULL);
}
- if (target__none(target))
+ if (target__none(target)) {
+ if (evlist->threads == NULL) {
+ fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n",
+ __func__, __LINE__);
+ goto out_close_pipes;
+ }
evlist->threads->map[0] = evlist->workload.pid;
+ }
close(child_ready_pipe[1]);
close(go_pipe[0]);
@@ -1202,7 +1451,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
int printed, value;
- char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf));
+ char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf));
switch (err) {
case EACCES:
@@ -1250,3 +1499,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
list_splice(&move, &evlist->entries);
}
+
+void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
+ struct perf_evsel *tracking_evsel)
+{
+ struct perf_evsel *evsel;
+
+ if (tracking_evsel->tracking)
+ return;
+
+ evlist__for_each(evlist, evsel) {
+ if (evsel != tracking_evsel)
+ evsel->tracking = false;
+ }
+
+ tracking_evsel->tracking = true;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index f5173cd63693..649b0c597283 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -2,6 +2,7 @@
#define __PERF_EVLIST_H 1
#include <linux/list.h>
+#include <api/fd/array.h>
#include <stdio.h>
#include "../perf.h"
#include "event.h"
@@ -17,9 +18,15 @@ struct record_opts;
#define PERF_EVLIST__HLIST_BITS 8
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
+/**
+ * struct perf_mmap - perf's ring buffer mmap details
+ *
+ * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this
+ */
struct perf_mmap {
void *base;
int mask;
+ int refcnt;
unsigned int prev;
char event_copy[PERF_SAMPLE_MAX_SIZE];
};
@@ -29,7 +36,6 @@ struct perf_evlist {
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
int nr_entries;
int nr_groups;
- int nr_fds;
int nr_mmaps;
size_t mmap_len;
int id_pos;
@@ -40,8 +46,8 @@ struct perf_evlist {
pid_t pid;
} workload;
bool overwrite;
+ struct fdarray pollfd;
struct perf_mmap *mmap;
- struct pollfd *pollfd;
struct thread_map *threads;
struct cpu_map *cpus;
struct perf_evsel *selected;
@@ -82,7 +88,11 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, u64 id);
-void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
+int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
+int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
+int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask);
+
+int perf_evlist__poll(struct perf_evlist *evlist, int timeout);
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
@@ -107,6 +117,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
void *ucontext));
int perf_evlist__start_workload(struct perf_evlist *evlist);
+struct option;
+
int perf_evlist__parse_mmap_pages(const struct option *opt,
const char *str,
int unset);
@@ -122,6 +134,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist,
struct perf_evsel *evsel);
int perf_evlist__enable_event(struct perf_evlist *evlist,
struct perf_evsel *evsel);
+int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
+ struct perf_evsel *evsel, int idx);
void perf_evlist__set_selected(struct perf_evlist *evlist,
struct perf_evsel *evsel);
@@ -262,4 +276,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
#define evlist__for_each_safe(evlist, tmp, evsel) \
__evlist__for_each_safe(&(evlist)->entries, tmp, evsel)
+void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
+ struct perf_evsel *tracking_evsel);
+
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 21a373ebea22..1e90c8557ede 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -15,6 +15,8 @@
#include <linux/perf_event.h>
#include <sys/resource.h>
#include "asm/bug.h"
+#include "callchain.h"
+#include "cgroup.h"
#include "evsel.h"
#include "evlist.h"
#include "util.h"
@@ -32,6 +34,48 @@ static struct {
bool cloexec;
} perf_missing_features;
+static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused)
+{
+ return 0;
+}
+
+static void perf_evsel__no_extra_fini(struct perf_evsel *evsel __maybe_unused)
+{
+}
+
+static struct {
+ size_t size;
+ int (*init)(struct perf_evsel *evsel);
+ void (*fini)(struct perf_evsel *evsel);
+} perf_evsel__object = {
+ .size = sizeof(struct perf_evsel),
+ .init = perf_evsel__no_extra_init,
+ .fini = perf_evsel__no_extra_fini,
+};
+
+int perf_evsel__object_config(size_t object_size,
+ int (*init)(struct perf_evsel *evsel),
+ void (*fini)(struct perf_evsel *evsel))
+{
+
+ if (object_size == 0)
+ goto set_methods;
+
+ if (perf_evsel__object.size > object_size)
+ return -EINVAL;
+
+ perf_evsel__object.size = object_size;
+
+set_methods:
+ if (init != NULL)
+ perf_evsel__object.init = init;
+
+ if (fini != NULL)
+ perf_evsel__object.fini = fini;
+
+ return 0;
+}
+
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
int __perf_evsel__sample_size(u64 sample_type)
@@ -116,16 +160,6 @@ void perf_evsel__calc_id_pos(struct perf_evsel *evsel)
evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type);
}
-void hists__init(struct hists *hists)
-{
- memset(hists, 0, sizeof(*hists));
- hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
- hists->entries_in = &hists->entries_in_array[0];
- hists->entries_collapsed = RB_ROOT;
- hists->entries = RB_ROOT;
- pthread_mutex_init(&hists->lock, NULL);
-}
-
void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
enum perf_event_sample_format bit)
{
@@ -162,19 +196,20 @@ void perf_evsel__init(struct perf_evsel *evsel,
struct perf_event_attr *attr, int idx)
{
evsel->idx = idx;
+ evsel->tracking = !idx;
evsel->attr = *attr;
evsel->leader = evsel;
evsel->unit = "";
evsel->scale = 1.0;
INIT_LIST_HEAD(&evsel->node);
- hists__init(&evsel->hists);
+ perf_evsel__object.init(evsel);
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
perf_evsel__calc_id_pos(evsel);
}
struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
{
- struct perf_evsel *evsel = zalloc(sizeof(*evsel));
+ struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
if (evsel != NULL)
perf_evsel__init(evsel, attr, idx);
@@ -184,7 +219,7 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
{
- struct perf_evsel *evsel = zalloc(sizeof(*evsel));
+ struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
if (evsel != NULL) {
struct perf_event_attr attr = {
@@ -502,20 +537,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
}
static void
-perf_evsel__config_callgraph(struct perf_evsel *evsel,
- struct record_opts *opts)
+perf_evsel__config_callgraph(struct perf_evsel *evsel)
{
bool function = perf_evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->attr;
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
- if (opts->call_graph == CALLCHAIN_DWARF) {
+ if (callchain_param.record_mode == CALLCHAIN_DWARF) {
if (!function) {
perf_evsel__set_sample_bit(evsel, REGS_USER);
perf_evsel__set_sample_bit(evsel, STACK_USER);
attr->sample_regs_user = PERF_REGS_MASK;
- attr->sample_stack_user = opts->stack_dump_size;
+ attr->sample_stack_user = callchain_param.dump_size;
attr->exclude_callchain_user = 1;
} else {
pr_info("Cannot use DWARF unwind for function trace event,"
@@ -561,7 +595,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
{
struct perf_evsel *leader = evsel->leader;
struct perf_event_attr *attr = &evsel->attr;
- int track = !evsel->idx; /* only the first counter needs these */
+ int track = evsel->tracking;
bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
@@ -624,8 +658,21 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
attr->mmap_data = track;
}
- if (opts->call_graph_enabled && !evsel->no_aux_samples)
- perf_evsel__config_callgraph(evsel, opts);
+ /*
+ * We don't allow user space callchains for function trace
+ * event, due to issues with page faults while tracing page
+ * fault handler and its overall trickiness nature.
+ */
+ if (perf_evsel__is_function_event(evsel))
+ evsel->attr.exclude_callchain_user = 1;
+
+ if (callchain_param.enabled && !evsel->no_aux_samples)
+ perf_evsel__config_callgraph(evsel);
+
+ if (opts->sample_intr_regs) {
+ attr->sample_regs_intr = PERF_REGS_MASK;
+ perf_evsel__set_sample_bit(evsel, REGS_INTR);
+ }
if (target__has_cpu(&opts->target))
perf_evsel__set_sample_bit(evsel, CPU);
@@ -633,9 +680,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
if (opts->period)
perf_evsel__set_sample_bit(evsel, PERIOD);
- if (!perf_missing_features.sample_id_all &&
- (opts->sample_time || !opts->no_inherit ||
- target__has_cpu(&opts->target) || per_cpu))
+ /*
+ * When the user explicitely disabled time don't force it here.
+ */
+ if (opts->sample_time &&
+ (!perf_missing_features.sample_id_all &&
+ (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu)))
perf_evsel__set_sample_bit(evsel, TIME);
if (opts->raw_samples && !evsel->no_aux_samples) {
@@ -689,9 +739,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
}
}
-int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
+static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
{
int cpu, thread;
+
+ if (evsel->system_wide)
+ nthreads = 1;
+
evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
if (evsel->fd) {
@@ -710,6 +764,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea
{
int cpu, thread;
+ if (evsel->system_wide)
+ nthreads = 1;
+
for (cpu = 0; cpu < ncpus; cpu++) {
for (thread = 0; thread < nthreads; thread++) {
int fd = FD(evsel, cpu, thread),
@@ -740,6 +797,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
+ if (evsel->system_wide)
+ nthreads = 1;
+
evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
if (evsel->sample_id == NULL)
return -ENOMEM;
@@ -767,13 +827,13 @@ int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
return evsel->counts != NULL ? 0 : -ENOMEM;
}
-void perf_evsel__free_fd(struct perf_evsel *evsel)
+static void perf_evsel__free_fd(struct perf_evsel *evsel)
{
xyarray__delete(evsel->fd);
evsel->fd = NULL;
}
-void perf_evsel__free_id(struct perf_evsel *evsel)
+static void perf_evsel__free_id(struct perf_evsel *evsel)
{
xyarray__delete(evsel->sample_id);
evsel->sample_id = NULL;
@@ -784,6 +844,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
{
int cpu, thread;
+ if (evsel->system_wide)
+ nthreads = 1;
+
for (cpu = 0; cpu < ncpus; cpu++)
for (thread = 0; thread < nthreads; ++thread) {
close(FD(evsel, cpu, thread));
@@ -801,22 +864,20 @@ void perf_evsel__exit(struct perf_evsel *evsel)
assert(list_empty(&evsel->node));
perf_evsel__free_fd(evsel);
perf_evsel__free_id(evsel);
+ close_cgroup(evsel->cgrp);
+ zfree(&evsel->group_name);
+ zfree(&evsel->name);
+ perf_evsel__object.fini(evsel);
}
void perf_evsel__delete(struct perf_evsel *evsel)
{
perf_evsel__exit(evsel);
- close_cgroup(evsel->cgrp);
- zfree(&evsel->group_name);
- if (evsel->tp_format)
- pevent_free_format(evsel->tp_format);
- zfree(&evsel->name);
free(evsel);
}
-static inline void compute_deltas(struct perf_evsel *evsel,
- int cpu,
- struct perf_counts_values *count)
+void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu,
+ struct perf_counts_values *count)
{
struct perf_counts_values tmp;
@@ -836,78 +897,60 @@ static inline void compute_deltas(struct perf_evsel *evsel,
count->run = count->run - tmp.run;
}
-int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
- int cpu, int thread, bool scale)
+void perf_counts_values__scale(struct perf_counts_values *count,
+ bool scale, s8 *pscaled)
{
- struct perf_counts_values count;
- size_t nv = scale ? 3 : 1;
-
- if (FD(evsel, cpu, thread) < 0)
- return -EINVAL;
-
- if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
- return -ENOMEM;
-
- if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
- return -errno;
-
- compute_deltas(evsel, cpu, &count);
+ s8 scaled = 0;
if (scale) {
- if (count.run == 0)
- count.val = 0;
- else if (count.run < count.ena)
- count.val = (u64)((double)count.val * count.ena / count.run + 0.5);
+ if (count->run == 0) {
+ scaled = -1;
+ count->val = 0;
+ } else if (count->run < count->ena) {
+ scaled = 1;
+ count->val = (u64)((double) count->val * count->ena / count->run + 0.5);
+ }
} else
- count.ena = count.run = 0;
+ count->ena = count->run = 0;
- evsel->counts->cpu[cpu] = count;
- return 0;
+ if (pscaled)
+ *pscaled = scaled;
}
-int __perf_evsel__read(struct perf_evsel *evsel,
- int ncpus, int nthreads, bool scale)
+int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread,
+ perf_evsel__read_cb_t cb)
{
- size_t nv = scale ? 3 : 1;
- int cpu, thread;
- struct perf_counts_values *aggr = &evsel->counts->aggr, count;
+ struct perf_counts_values count;
- aggr->val = aggr->ena = aggr->run = 0;
+ memset(&count, 0, sizeof(count));
- for (cpu = 0; cpu < ncpus; cpu++) {
- for (thread = 0; thread < nthreads; thread++) {
- if (FD(evsel, cpu, thread) < 0)
- continue;
+ if (FD(evsel, cpu, thread) < 0)
+ return -EINVAL;
- if (readn(FD(evsel, cpu, thread),
- &count, nv * sizeof(u64)) < 0)
- return -errno;
+ if (readn(FD(evsel, cpu, thread), &count, sizeof(count)) < 0)
+ return -errno;
- aggr->val += count.val;
- if (scale) {
- aggr->ena += count.ena;
- aggr->run += count.run;
- }
- }
- }
+ return cb(evsel, cpu, thread, &count);
+}
+
+int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+ int cpu, int thread, bool scale)
+{
+ struct perf_counts_values count;
+ size_t nv = scale ? 3 : 1;
- compute_deltas(evsel, -1, aggr);
+ if (FD(evsel, cpu, thread) < 0)
+ return -EINVAL;
- evsel->counts->scaled = 0;
- if (scale) {
- if (aggr->run == 0) {
- evsel->counts->scaled = -1;
- aggr->val = 0;
- return 0;
- }
+ if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
+ return -ENOMEM;
- if (aggr->run < aggr->ena) {
- evsel->counts->scaled = 1;
- aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5);
- }
- } else
- aggr->ena = aggr->run = 0;
+ if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
+ return -errno;
+ perf_evsel__compute_deltas(evsel, cpu, &count);
+ perf_counts_values__scale(&count, scale, NULL);
+ evsel->counts->cpu[cpu] = count;
return 0;
}
@@ -985,6 +1028,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)
ret += PRINT_ATTR_X64(branch_sample_type);
ret += PRINT_ATTR_X64(sample_regs_user);
ret += PRINT_ATTR_U32(sample_stack_user);
+ ret += PRINT_ATTR_X64(sample_regs_intr);
ret += fprintf(fp, "%.60s\n", graph_dotted_line);
@@ -994,13 +1038,18 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
struct thread_map *threads)
{
- int cpu, thread;
+ int cpu, thread, nthreads;
unsigned long flags = PERF_FLAG_FD_CLOEXEC;
int pid = -1, err;
enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
+ if (evsel->system_wide)
+ nthreads = 1;
+ else
+ nthreads = threads->nr;
+
if (evsel->fd == NULL &&
- perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
+ perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0)
return -ENOMEM;
if (evsel->cgrp) {
@@ -1024,10 +1073,10 @@ retry_sample_id:
for (cpu = 0; cpu < cpus->nr; cpu++) {
- for (thread = 0; thread < threads->nr; thread++) {
+ for (thread = 0; thread < nthreads; thread++) {
int group_fd;
- if (!evsel->cgrp)
+ if (!evsel->cgrp && !evsel->system_wide)
pid = threads->map[thread];
group_fd = get_group_fd(evsel, cpu, thread);
@@ -1100,7 +1149,7 @@ out_close:
close(FD(evsel, cpu, thread));
FD(evsel, cpu, thread) = -1;
}
- thread = threads->nr;
+ thread = nthreads;
} while (--cpu >= 0);
return err;
}
@@ -1479,6 +1528,23 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
array++;
}
+ data->intr_regs.abi = PERF_SAMPLE_REGS_ABI_NONE;
+ if (type & PERF_SAMPLE_REGS_INTR) {
+ OVERFLOW_CHECK_u64(array);
+ data->intr_regs.abi = *array;
+ array++;
+
+ if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) {
+ u64 mask = evsel->attr.sample_regs_intr;
+
+ sz = hweight_long(mask) * sizeof(u64);
+ OVERFLOW_CHECK(array, sz, max_size);
+ data->intr_regs.mask = mask;
+ data->intr_regs.regs = (u64 *)array;
+ array = (void *)array + sz;
+ }
+ }
+
return 0;
}
@@ -1574,6 +1640,16 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
if (type & PERF_SAMPLE_TRANSACTION)
result += sizeof(u64);
+ if (type & PERF_SAMPLE_REGS_INTR) {
+ if (sample->intr_regs.abi) {
+ result += sizeof(u64);
+ sz = hweight_long(sample->intr_regs.mask) * sizeof(u64);
+ result += sz;
+ } else {
+ result += sizeof(u64);
+ }
+ }
+
return result;
}
@@ -1752,6 +1828,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
array++;
}
+ if (type & PERF_SAMPLE_REGS_INTR) {
+ if (sample->intr_regs.abi) {
+ *array++ = sample->intr_regs.abi;
+ sz = hweight_long(sample->intr_regs.mask) * sizeof(u64);
+ memcpy(array, sample->intr_regs.regs, sz);
+ array = (void *)array + sz;
+ } else {
+ *array++ = 0;
+ }
+ }
+
return 0;
}
@@ -1881,7 +1968,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value)
bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU),
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
- bit_name(IDENTIFIER),
+ bit_name(IDENTIFIER), bit_name(REGS_INTR),
{ .name = NULL, }
};
#undef bit_name
@@ -2002,6 +2089,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
int err, char *msg, size_t size)
{
+ char sbuf[STRERR_BUFSIZE];
+
switch (err) {
case EPERM:
case EACCES:
@@ -2036,13 +2125,20 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
"No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.");
#endif
break;
+ case EBUSY:
+ if (find_process("oprofiled"))
+ return scnprintf(msg, size,
+ "The PMU counters are busy/taken by another profiler.\n"
+ "We found oprofile daemon running, please stop it and try again.");
+ break;
default:
break;
}
return scnprintf(msg, size,
- "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n"
+ "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"
"/bin/dmesg may provide additional information.\n"
"No CONFIG_PERF_EVENTS=y kernel support configured?\n",
- err, strerror(err), perf_evsel__name(evsel));
+ err, strerror_r(err, sbuf, sizeof(sbuf)),
+ perf_evsel__name(evsel));
}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index d7f93ce0ebc1..38622747d130 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -7,8 +7,6 @@
#include <linux/perf_event.h>
#include <linux/types.h>
#include "xyarray.h"
-#include "cgroup.h"
-#include "hist.h"
#include "symbol.h"
struct perf_counts_values {
@@ -38,11 +36,16 @@ struct perf_sample_id {
struct hlist_node node;
u64 id;
struct perf_evsel *evsel;
+ int idx;
+ int cpu;
+ pid_t tid;
/* Holds total ID period value for PERF_SAMPLE_READ processing. */
u64 period;
};
+struct cgroup_sel;
+
/** struct perf_evsel - event selector
*
* @name - Can be set to retain the original event name passed by the user,
@@ -54,6 +57,7 @@ struct perf_sample_id {
* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
* is used there is an id sample appended to non-sample events
+ * @priv: And what is in its containing unnamed union are tool specific
*/
struct perf_evsel {
struct list_head node;
@@ -66,14 +70,15 @@ struct perf_evsel {
struct perf_counts *prev_raw_counts;
int idx;
u32 ids;
- struct hists hists;
char *name;
double scale;
const char *unit;
+ bool snapshot;
struct event_format *tp_format;
union {
void *priv;
off_t id_offset;
+ u64 db_id;
};
struct cgroup_sel *cgrp;
void *handler;
@@ -85,6 +90,10 @@ struct perf_evsel {
bool needs_swap;
bool no_aux_samples;
bool immediate;
+ bool system_wide;
+ bool tracking;
+ bool per_pkg;
+ unsigned long *per_pkg_mask;
/* parse modifier helper */
int exclude_GH;
int nr_members;
@@ -98,13 +107,22 @@ union u64_swap {
u32 val32[2];
};
-#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
-
struct cpu_map;
+struct target;
struct thread_map;
struct perf_evlist;
struct record_opts;
+void perf_counts_values__scale(struct perf_counts_values *count,
+ bool scale, s8 *pscaled);
+
+void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu,
+ struct perf_counts_values *count);
+
+int perf_evsel__object_config(size_t object_size,
+ int (*init)(struct perf_evsel *evsel),
+ void (*fini)(struct perf_evsel *evsel));
+
struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
@@ -151,12 +169,9 @@ const char *perf_evsel__name(struct perf_evsel *evsel);
const char *perf_evsel__group_name(struct perf_evsel *evsel);
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
-int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
-void perf_evsel__free_fd(struct perf_evsel *evsel);
-void perf_evsel__free_id(struct perf_evsel *evsel);
void perf_evsel__free_counts(struct perf_evsel *evsel);
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
@@ -221,6 +236,13 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1,
(a)->attr.type == (b)->attr.type && \
(a)->attr.config == (b)->attr.config)
+typedef int (perf_evsel__read_cb_t)(struct perf_evsel *evsel,
+ int cpu, int thread,
+ struct perf_counts_values *count);
+
+int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread,
+ perf_evsel__read_cb_t cb);
+
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
int cpu, int thread, bool scale);
@@ -250,37 +272,6 @@ static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel,
return __perf_evsel__read_on_cpu(evsel, cpu, thread, true);
}
-int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads,
- bool scale);
-
-/**
- * perf_evsel__read - Read the aggregate results on all CPUs
- *
- * @evsel - event selector to read value
- * @ncpus - Number of cpus affected, from zero
- * @nthreads - Number of threads affected, from zero
- */
-static inline int perf_evsel__read(struct perf_evsel *evsel,
- int ncpus, int nthreads)
-{
- return __perf_evsel__read(evsel, ncpus, nthreads, false);
-}
-
-/**
- * perf_evsel__read_scaled - Read the aggregate results on all CPUs, scaled
- *
- * @evsel - event selector to read value
- * @ncpus - Number of cpus affected, from zero
- * @nthreads - Number of threads affected, from zero
- */
-static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
- int ncpus, int nthreads)
-{
- return __perf_evsel__read(evsel, ncpus, nthreads, true);
-}
-
-void hists__init(struct hists *hists);
-
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
struct perf_sample *sample);
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c
new file mode 100644
index 000000000000..95ef1cffc056
--- /dev/null
+++ b/tools/perf/util/find-vdso-map.c
@@ -0,0 +1,30 @@
+static int find_vdso_map(void **start, void **end)
+{
+ FILE *maps;
+ char line[128];
+ int found = 0;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ fprintf(stderr, "vdso: cannot open maps\n");
+ return -1;
+ }
+
+ while (!found && fgets(line, sizeof(line), maps)) {
+ int m = -1;
+
+ /* We care only about private r-x mappings. */
+ if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+ start, end, &m))
+ continue;
+ if (m < 0)
+ continue;
+
+ if (!strncmp(&line[m], VDSO__MAP_NAME,
+ sizeof(VDSO__MAP_NAME) - 1))
+ found = 1;
+ }
+
+ fclose(maps);
+ return !found;
+}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 158c787ce0c4..b20e40c74468 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -24,8 +24,6 @@
#include "build-id.h"
#include "data.h"
-static bool no_buildid_cache = false;
-
static u32 header_argc;
static const char **header_argv;
@@ -79,10 +77,7 @@ static int do_write(int fd, const void *buf, size_t size)
return 0;
}
-#define NAME_ALIGN 64
-
-static int write_padded(int fd, const void *bf, size_t count,
- size_t count_aligned)
+int write_padded(int fd, const void *bf, size_t count, size_t count_aligned)
{
static const char zero_buf[NAME_ALIGN];
int err = do_write(fd, bf, count);
@@ -171,336 +166,6 @@ perf_header__set_cmdline(int argc, const char **argv)
return 0;
}
-#define dsos__for_each_with_build_id(pos, head) \
- list_for_each_entry(pos, head, node) \
- if (!pos->has_build_id) \
- continue; \
- else
-
-static int write_buildid(const char *name, size_t name_len, u8 *build_id,
- pid_t pid, u16 misc, int fd)
-{
- int err;
- struct build_id_event b;
- size_t len;
-
- len = name_len + 1;
- len = PERF_ALIGN(len, NAME_ALIGN);
-
- memset(&b, 0, sizeof(b));
- memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
- b.pid = pid;
- b.header.misc = misc;
- b.header.size = sizeof(b) + len;
-
- err = do_write(fd, &b, sizeof(b));
- if (err < 0)
- return err;
-
- return write_padded(fd, name, name_len + 1, len);
-}
-
-static int __dsos__hit_all(struct list_head *head)
-{
- struct dso *pos;
-
- list_for_each_entry(pos, head, node)
- pos->hit = true;
-
- return 0;
-}
-
-static int machine__hit_all_dsos(struct machine *machine)
-{
- int err;
-
- err = __dsos__hit_all(&machine->kernel_dsos);
- if (err)
- return err;
-
- return __dsos__hit_all(&machine->user_dsos);
-}
-
-int dsos__hit_all(struct perf_session *session)
-{
- struct rb_node *nd;
- int err;
-
- err = machine__hit_all_dsos(&session->machines.host);
- if (err)
- return err;
-
- for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
-
- err = machine__hit_all_dsos(pos);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int __dsos__write_buildid_table(struct list_head *head,
- struct machine *machine,
- pid_t pid, u16 misc, int fd)
-{
- char nm[PATH_MAX];
- struct dso *pos;
-
- dsos__for_each_with_build_id(pos, head) {
- int err;
- const char *name;
- size_t name_len;
-
- if (!pos->hit)
- continue;
-
- if (dso__is_vdso(pos)) {
- name = pos->short_name;
- name_len = pos->short_name_len + 1;
- } else if (dso__is_kcore(pos)) {
- machine__mmap_name(machine, nm, sizeof(nm));
- name = nm;
- name_len = strlen(nm) + 1;
- } else {
- name = pos->long_name;
- name_len = pos->long_name_len + 1;
- }
-
- err = write_buildid(name, name_len, pos->build_id,
- pid, misc, fd);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int machine__write_buildid_table(struct machine *machine, int fd)
-{
- int err;
- u16 kmisc = PERF_RECORD_MISC_KERNEL,
- umisc = PERF_RECORD_MISC_USER;
-
- if (!machine__is_host(machine)) {
- kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
- umisc = PERF_RECORD_MISC_GUEST_USER;
- }
-
- err = __dsos__write_buildid_table(&machine->kernel_dsos, machine,
- machine->pid, kmisc, fd);
- if (err == 0)
- err = __dsos__write_buildid_table(&machine->user_dsos, machine,
- machine->pid, umisc, fd);
- return err;
-}
-
-static int dsos__write_buildid_table(struct perf_header *header, int fd)
-{
- struct perf_session *session = container_of(header,
- struct perf_session, header);
- struct rb_node *nd;
- int err = machine__write_buildid_table(&session->machines.host, fd);
-
- if (err)
- return err;
-
- for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- err = machine__write_buildid_table(pos, fd);
- if (err)
- break;
- }
- return err;
-}
-
-int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms, bool is_vdso)
-{
- const size_t size = PATH_MAX;
- char *realname, *filename = zalloc(size),
- *linkname = zalloc(size), *targetname;
- int len, err = -1;
- bool slash = is_kallsyms || is_vdso;
-
- if (is_kallsyms) {
- if (symbol_conf.kptr_restrict) {
- pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
- err = 0;
- goto out_free;
- }
- realname = (char *) name;
- } else
- realname = realpath(name, NULL);
-
- if (realname == NULL || filename == NULL || linkname == NULL)
- goto out_free;
-
- len = scnprintf(filename, size, "%s%s%s",
- debugdir, slash ? "/" : "",
- is_vdso ? DSO__NAME_VDSO : realname);
- if (mkdir_p(filename, 0755))
- goto out_free;
-
- snprintf(filename + len, size - len, "/%s", sbuild_id);
-
- if (access(filename, F_OK)) {
- if (is_kallsyms) {
- if (copyfile("/proc/kallsyms", filename))
- goto out_free;
- } else if (link(realname, filename) && copyfile(name, filename))
- goto out_free;
- }
-
- len = scnprintf(linkname, size, "%s/.build-id/%.2s",
- debugdir, sbuild_id);
-
- if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
- goto out_free;
-
- snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
- targetname = filename + strlen(debugdir) - 5;
- memcpy(targetname, "../..", 5);
-
- if (symlink(targetname, linkname) == 0)
- err = 0;
-out_free:
- if (!is_kallsyms)
- free(realname);
- free(filename);
- free(linkname);
- return err;
-}
-
-static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
- const char *name, const char *debugdir,
- bool is_kallsyms, bool is_vdso)
-{
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(build_id, build_id_size, sbuild_id);
-
- return build_id_cache__add_s(sbuild_id, debugdir, name,
- is_kallsyms, is_vdso);
-}
-
-int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
-{
- const size_t size = PATH_MAX;
- char *filename = zalloc(size),
- *linkname = zalloc(size);
- int err = -1;
-
- if (filename == NULL || linkname == NULL)
- goto out_free;
-
- snprintf(linkname, size, "%s/.build-id/%.2s/%s",
- debugdir, sbuild_id, sbuild_id + 2);
-
- if (access(linkname, F_OK))
- goto out_free;
-
- if (readlink(linkname, filename, size - 1) < 0)
- goto out_free;
-
- if (unlink(linkname))
- goto out_free;
-
- /*
- * Since the link is relative, we must make it absolute:
- */
- snprintf(linkname, size, "%s/.build-id/%.2s/%s",
- debugdir, sbuild_id, filename);
-
- if (unlink(linkname))
- goto out_free;
-
- err = 0;
-out_free:
- free(filename);
- free(linkname);
- return err;
-}
-
-static int dso__cache_build_id(struct dso *dso, struct machine *machine,
- const char *debugdir)
-{
- bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
- bool is_vdso = dso__is_vdso(dso);
- const char *name = dso->long_name;
- char nm[PATH_MAX];
-
- if (dso__is_kcore(dso)) {
- is_kallsyms = true;
- machine__mmap_name(machine, nm, sizeof(nm));
- name = nm;
- }
- return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
- debugdir, is_kallsyms, is_vdso);
-}
-
-static int __dsos__cache_build_ids(struct list_head *head,
- struct machine *machine, const char *debugdir)
-{
- struct dso *pos;
- int err = 0;
-
- dsos__for_each_with_build_id(pos, head)
- if (dso__cache_build_id(pos, machine, debugdir))
- err = -1;
-
- return err;
-}
-
-static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
-{
- int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine,
- debugdir);
- ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);
- return ret;
-}
-
-static int perf_session__cache_build_ids(struct perf_session *session)
-{
- struct rb_node *nd;
- int ret;
- char debugdir[PATH_MAX];
-
- snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
-
- if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
- return -1;
-
- ret = machine__cache_build_ids(&session->machines.host, debugdir);
-
- for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret |= machine__cache_build_ids(pos, debugdir);
- }
- return ret ? -1 : 0;
-}
-
-static bool machine__read_build_ids(struct machine *machine, bool with_hits)
-{
- bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits);
- ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);
- return ret;
-}
-
-static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
-{
- struct rb_node *nd;
- bool ret = machine__read_build_ids(&session->machines.host, with_hits);
-
- for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret |= machine__read_build_ids(pos, with_hits);
- }
-
- return ret;
-}
-
static int write_tracing_data(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist)
{
@@ -519,13 +184,12 @@ static int write_build_id(int fd, struct perf_header *h,
if (!perf_session__read_build_ids(session, true))
return -1;
- err = dsos__write_buildid_table(h, fd);
+ err = perf_session__write_buildid_table(session, fd);
if (err < 0) {
pr_debug("failed to write buildid table\n");
return err;
}
- if (!no_buildid_cache)
- perf_session__cache_build_ids(session);
+ perf_session__cache_build_ids(session);
return 0;
}
@@ -575,16 +239,12 @@ static int write_version(int fd, struct perf_header *h __maybe_unused,
return do_write_string(fd, perf_version_string);
}
-static int write_cpudesc(int fd, struct perf_header *h __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+static int __write_cpudesc(int fd, const char *cpuinfo_proc)
{
-#ifndef CPUINFO_PROC
-#define CPUINFO_PROC NULL
-#endif
FILE *file;
char *buf = NULL;
char *s, *p;
- const char *search = CPUINFO_PROC;
+ const char *search = cpuinfo_proc;
size_t len = 0;
int ret = -1;
@@ -601,8 +261,10 @@ static int write_cpudesc(int fd, struct perf_header *h __maybe_unused,
break;
}
- if (ret)
+ if (ret) {
+ ret = -1;
goto done;
+ }
s = buf;
@@ -634,6 +296,25 @@ done:
return ret;
}
+static int write_cpudesc(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
+{
+#ifndef CPUINFO_PROC
+#define CPUINFO_PROC {"model name", }
+#endif
+ const char *cpuinfo_procs[] = CPUINFO_PROC;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cpuinfo_procs); i++) {
+ int ret;
+ ret = __write_cpudesc(fd, cpuinfo_procs[i]);
+ if (ret >= 0)
+ return ret;
+ }
+ return -1;
+}
+
+
static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist __maybe_unused)
{
@@ -946,7 +627,8 @@ static int write_total_mem(int fd, struct perf_header *h __maybe_unused,
n = sscanf(buf, "%*s %"PRIu64, &mem);
if (n == 1)
ret = do_write(fd, &mem, sizeof(mem));
- }
+ } else
+ ret = -1;
free(buf);
fclose(fp);
return ret;
@@ -1548,7 +1230,7 @@ static int __event_process_build_id(struct build_id_event *bev,
struct perf_session *session)
{
int err = -1;
- struct list_head *head;
+ struct dsos *dsos;
struct machine *machine;
u16 misc;
struct dso *dso;
@@ -1563,28 +1245,28 @@ static int __event_process_build_id(struct build_id_event *bev,
switch (misc) {
case PERF_RECORD_MISC_KERNEL:
dso_type = DSO_TYPE_KERNEL;
- head = &machine->kernel_dsos;
+ dsos = &machine->kernel_dsos;
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
dso_type = DSO_TYPE_GUEST_KERNEL;
- head = &machine->kernel_dsos;
+ dsos = &machine->kernel_dsos;
break;
case PERF_RECORD_MISC_USER:
case PERF_RECORD_MISC_GUEST_USER:
dso_type = DSO_TYPE_USER;
- head = &machine->user_dsos;
+ dsos = &machine->user_dsos;
break;
default:
goto out;
}
- dso = __dsos__findnew(head, filename);
+ dso = __dsos__findnew(dsos, filename);
if (dso != NULL) {
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
dso__set_build_id(dso, &bev->build_id);
- if (filename[0] == '[')
+ if (!is_kernel_module(filename, NULL))
dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id),
@@ -2458,6 +2140,7 @@ static const int attr_file_abi_sizes[] = {
[1] = PERF_ATTR_SIZE_VER1,
[2] = PERF_ATTR_SIZE_VER2,
[3] = PERF_ATTR_SIZE_VER3,
+ [4] = PERF_ATTR_SIZE_VER4,
0,
};
@@ -3105,8 +2788,3 @@ int perf_event__process_build_id(struct perf_tool *tool __maybe_unused,
session);
return 0;
}
-
-void disable_buildid_cache(void)
-{
- no_buildid_cache = true;
-}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 8f5cbaea64a5..3bb90ac172a1 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd,
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
-int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms, bool is_vdso);
-int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
-
int perf_event__synthesize_attr(struct perf_tool *tool,
struct perf_event_attr *attr, u32 ids, u64 *id,
perf_event__handler_t process);
@@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool,
struct perf_session *session);
bool is_perf_magic(u64 magic);
-int dsos__hit_all(struct perf_session *session);
+#define NAME_ALIGN 64
+
+int write_padded(int fd, const void *bf, size_t count, size_t count_aligned);
/*
* arch specific callback
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 30df6187ee02..6e88b9e395df 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -3,6 +3,7 @@
#include "hist.h"
#include "session.h"
#include "sort.h"
+#include "evlist.h"
#include "evsel.h"
#include "annotate.h"
#include <math.h>
@@ -14,13 +15,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists,
static bool hists__filter_entry_by_symbol(struct hists *hists,
struct hist_entry *he);
-struct callchain_param callchain_param = {
- .mode = CHAIN_GRAPH_REL,
- .min_percent = 0.5,
- .order = ORDER_CALLEE,
- .key = CCKEY_FUNCTION
-};
-
u16 hists__col_len(struct hists *hists, enum hist_column col)
{
return hists->col_len[col];
@@ -277,6 +271,28 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
}
}
+void hists__delete_entries(struct hists *hists)
+{
+ struct rb_node *next = rb_first(&hists->entries);
+ struct hist_entry *n;
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &hists->entries);
+
+ if (sort__need_collapse)
+ rb_erase(&n->rb_node_in, &hists->entries_collapsed);
+
+ --hists->nr_entries;
+ if (!n->filtered)
+ --hists->nr_non_filtered_entries;
+
+ hist_entry__free(n);
+ }
+}
+
/*
* histogram, sorted on item, collects periods
*/
@@ -494,6 +510,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
{
u64 cost;
struct mem_info *mi = iter->priv;
+ struct hists *hists = evsel__hists(iter->evsel);
struct hist_entry *he;
if (mi == NULL)
@@ -510,7 +527,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
* and this is indirectly achieved by passing period=weight here
* and the he_stat__add_period() function.
*/
- he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi,
+ he = __hists__add_entry(hists, al, iter->parent, NULL, mi,
cost, cost, 0, true);
if (!he)
return -ENOMEM;
@@ -524,13 +541,14 @@ iter_finish_mem_entry(struct hist_entry_iter *iter,
struct addr_location *al __maybe_unused)
{
struct perf_evsel *evsel = iter->evsel;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he = iter->he;
int err = -EINVAL;
if (he == NULL)
goto out;
- hists__inc_nr_samples(&evsel->hists, he->filtered);
+ hists__inc_nr_samples(hists, he->filtered);
err = hist_entry__append_callchain(he, iter->sample);
@@ -596,6 +614,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
{
struct branch_info *bi;
struct perf_evsel *evsel = iter->evsel;
+ struct hists *hists = evsel__hists(evsel);
struct hist_entry *he = NULL;
int i = iter->curr;
int err = 0;
@@ -609,12 +628,12 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
* The report shows the percentage of total branches captured
* and not events sampled. Thus we use a pseudo period of 1.
*/
- he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL,
+ he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
1, 1, 0, true);
if (he == NULL)
return -ENOMEM;
- hists__inc_nr_samples(&evsel->hists, he->filtered);
+ hists__inc_nr_samples(hists, he->filtered);
out:
iter->he = he;
@@ -646,7 +665,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location
struct perf_sample *sample = iter->sample;
struct hist_entry *he;
- he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+ he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
sample->period, sample->weight,
sample->transaction, true);
if (he == NULL)
@@ -669,7 +688,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter,
iter->he = NULL;
- hists__inc_nr_samples(&evsel->hists, he->filtered);
+ hists__inc_nr_samples(evsel__hists(evsel), he->filtered);
return hist_entry__append_callchain(he, sample);
}
@@ -702,12 +721,13 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
struct addr_location *al)
{
struct perf_evsel *evsel = iter->evsel;
+ struct hists *hists = evsel__hists(evsel);
struct perf_sample *sample = iter->sample;
struct hist_entry **he_cache = iter->priv;
struct hist_entry *he;
int err = 0;
- he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+ he = __hists__add_entry(hists, al, iter->parent, NULL, NULL,
sample->period, sample->weight,
sample->transaction, true);
if (he == NULL)
@@ -724,7 +744,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
*/
callchain_cursor_commit(&callchain_cursor);
- hists__inc_nr_samples(&evsel->hists, he->filtered);
+ hists__inc_nr_samples(hists, he->filtered);
return err;
}
@@ -780,7 +800,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
}
}
- he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+ he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
sample->period, sample->weight,
sample->transaction, false);
if (he == NULL)
@@ -1386,6 +1406,21 @@ int hists__link(struct hists *leader, struct hists *other)
return 0;
}
+
+size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp)
+{
+ struct perf_evsel *pos;
+ size_t ret = 0;
+
+ evlist__for_each(evlist, pos) {
+ ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
+ ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp);
+ }
+
+ return ret;
+}
+
+
u64 hists__total_period(struct hists *hists)
{
return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
@@ -1412,3 +1447,31 @@ int perf_hist_config(const char *var, const char *value)
return 0;
}
+
+static int hists_evsel__init(struct perf_evsel *evsel)
+{
+ struct hists *hists = evsel__hists(evsel);
+
+ memset(hists, 0, sizeof(*hists));
+ hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
+ hists->entries_in = &hists->entries_in_array[0];
+ hists->entries_collapsed = RB_ROOT;
+ hists->entries = RB_ROOT;
+ pthread_mutex_init(&hists->lock, NULL);
+ return 0;
+}
+
+/*
+ * XXX We probably need a hists_evsel__exit() to free the hist_entries
+ * stored in the rbtree...
+ */
+
+int hists__init(void)
+{
+ int err = perf_evsel__object_config(sizeof(struct hists_evsel),
+ hists_evsel__init, NULL);
+ if (err)
+ fputs("FATAL ERROR: Couldn't setup hists class\n", stderr);
+
+ return err;
+}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 742f49a85725..d0ef9a19a744 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -4,12 +4,11 @@
#include <linux/types.h>
#include <pthread.h>
#include "callchain.h"
+#include "evsel.h"
#include "header.h"
#include "color.h"
#include "ui/progress.h"
-extern struct callchain_param callchain_param;
-
struct hist_entry;
struct addr_location;
struct symbol;
@@ -23,32 +22,6 @@ enum hist_filter {
HIST_FILTER__HOST,
};
-/*
- * The kernel collects the number of events it couldn't send in a stretch and
- * when possible sends this number in a PERF_RECORD_LOST event. The number of
- * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
- * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
- * the sum of all struct lost_event.lost fields reported.
- *
- * The total_period is needed because by default auto-freq is used, so
- * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
- * the total number of low level events, it is necessary to to sum all struct
- * sample_event.period and stash the result in total_period.
- */
-struct events_stats {
- u64 total_period;
- u64 total_non_filtered_period;
- u64 total_lost;
- u64 total_invalid_chains;
- u32 nr_events[PERF_RECORD_HEADER_MAX];
- u32 nr_non_filtered_samples;
- u32 nr_lost_warned;
- u32 nr_unknown_events;
- u32 nr_invalid_chains;
- u32 nr_unknown_id;
- u32 nr_unprocessable_samples;
-};
-
enum hist_column {
HISTC_SYMBOL,
HISTC_DSO,
@@ -152,6 +125,7 @@ void hists__output_resort(struct hists *hists);
void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
+void hists__delete_entries(struct hists *hists);
void hists__output_recalc_col_len(struct hists *hists, int max_rows);
u64 hists__total_period(struct hists *hists);
@@ -164,6 +138,7 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
int max_cols, float min_pcnt, FILE *fp);
+size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp);
void hists__filter_by_dso(struct hists *hists);
void hists__filter_by_thread(struct hists *hists);
@@ -184,6 +159,25 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he);
void hists__match(struct hists *leader, struct hists *other);
int hists__link(struct hists *leader, struct hists *other);
+struct hists_evsel {
+ struct perf_evsel evsel;
+ struct hists hists;
+};
+
+static inline struct perf_evsel *hists_to_evsel(struct hists *hists)
+{
+ struct hists_evsel *hevsel = container_of(hists, struct hists_evsel, hists);
+ return &hevsel->evsel;
+}
+
+static inline struct hists *evsel__hists(struct perf_evsel *evsel)
+{
+ struct hists_evsel *hevsel = (struct hists_evsel *)evsel;
+ return &hevsel->hists;
+}
+
+int hists__init(void);
+
struct perf_hpp {
char *buf;
size_t size;
@@ -192,6 +186,7 @@ struct perf_hpp {
};
struct perf_hpp_fmt {
+ const char *name;
int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct perf_evsel *evsel);
int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
@@ -207,6 +202,8 @@ struct perf_hpp_fmt {
struct list_head list;
struct list_head sort_list;
bool elide;
+ int len;
+ int user_len;
};
extern struct list_head perf_hpp__list;
@@ -261,17 +258,19 @@ static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
}
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
+void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists);
+void perf_hpp__set_user_width(const char *width_list_str);
typedef u64 (*hpp_field_fn)(struct hist_entry *he);
typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
-int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
- hpp_field_fn get_field, const char *fmt,
- hpp_snprint_fn print_fn, bool fmt_percent);
-int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
- hpp_field_fn get_field, const char *fmt,
- hpp_snprint_fn print_fn, bool fmt_percent);
+int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he, hpp_field_fn get_field,
+ const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent);
+int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he, hpp_field_fn get_field,
+ const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent);
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
{
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index 01ffd12dc791..40bd21488032 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
__bitmap_or(dst, src1, src2, nbits);
}
+/**
+ * test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ */
+static inline int test_and_set_bit(int nr, unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+ unsigned long old;
+
+ old = *p;
+ *p = old | mask;
+
+ return (old & mask) != 0;
+}
+
#endif /* _PERF_BITOPS_H */
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index dadfa7e54287..c3294163de17 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -15,6 +15,8 @@
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define for_each_set_bit(bit, addr, size) \
for ((bit) = find_first_bit((addr), (size)); \
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
index 97a800738226..6f19c548ecc0 100644
--- a/tools/perf/util/include/linux/string.h
+++ b/tools/perf/util/include/linux/string.h
@@ -1,4 +1,3 @@
#include <string.h>
void *memdup(const void *src, size_t len);
-int str_append(char **s, int *len, const char *a);
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h
index 0b5a8cd2ee79..cf1d7913783b 100644
--- a/tools/perf/util/kvm-stat.h
+++ b/tools/perf/util/kvm-stat.h
@@ -92,7 +92,6 @@ struct perf_kvm_stat {
u64 lost_events;
u64 duration;
- const char *pid_str;
struct intlist *pid_list;
struct rb_root result;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 16bba9fff2c8..15dd0a9691ce 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -12,13 +12,20 @@
#include <stdbool.h>
#include <symbol/kallsyms.h>
#include "unwind.h"
+#include "linux/hash.h"
+
+static void dsos__init(struct dsos *dsos)
+{
+ INIT_LIST_HEAD(&dsos->head);
+ dsos->root = RB_ROOT;
+}
int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
{
- map_groups__init(&machine->kmaps);
+ map_groups__init(&machine->kmaps, machine);
RB_CLEAR_NODE(&machine->rb_node);
- INIT_LIST_HEAD(&machine->user_dsos);
- INIT_LIST_HEAD(&machine->kernel_dsos);
+ dsos__init(&machine->user_dsos);
+ dsos__init(&machine->kernel_dsos);
machine->threads = RB_ROOT;
INIT_LIST_HEAD(&machine->dead_threads);
@@ -26,11 +33,12 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->vdso_info = NULL;
- machine->kmaps.machine = machine;
machine->pid = pid;
machine->symbol_filter = NULL;
machine->id_hdr_size = 0;
+ machine->comm_exec = false;
+ machine->kernel_start = 0;
machine->root_dir = strdup(root_dir);
if (machine->root_dir == NULL)
@@ -70,11 +78,12 @@ out_delete:
return NULL;
}
-static void dsos__delete(struct list_head *dsos)
+static void dsos__delete(struct dsos *dsos)
{
struct dso *pos, *n;
- list_for_each_entry_safe(pos, n, dsos, node) {
+ list_for_each_entry_safe(pos, n, &dsos->head, node) {
+ RB_CLEAR_NODE(&pos->rb_node);
list_del(&pos->node);
dso__delete(pos);
}
@@ -179,6 +188,19 @@ void machines__set_symbol_filter(struct machines *machines,
}
}
+void machines__set_comm_exec(struct machines *machines, bool comm_exec)
+{
+ struct rb_node *nd;
+
+ machines->host.comm_exec = comm_exec;
+
+ for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
+ struct machine *machine = rb_entry(nd, struct machine, rb_node);
+
+ machine->comm_exec = comm_exec;
+ }
+}
+
struct machine *machines__find(struct machines *machines, pid_t pid)
{
struct rb_node **p = &machines->guests.rb_node;
@@ -297,7 +319,7 @@ static void machine__update_thread_pid(struct machine *machine,
goto out_err;
if (!leader->mg)
- leader->mg = map_groups__new();
+ leader->mg = map_groups__new(machine);
if (!leader->mg)
goto out_err;
@@ -398,17 +420,31 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid,
return __machine__findnew_thread(machine, pid, tid, false);
}
+struct comm *machine__thread_exec_comm(struct machine *machine,
+ struct thread *thread)
+{
+ if (machine->comm_exec)
+ return thread__exec_comm(thread);
+ else
+ return thread__comm(thread);
+}
+
int machine__process_comm_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample)
{
struct thread *thread = machine__findnew_thread(machine,
event->comm.pid,
event->comm.tid);
+ bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC;
+
+ if (exec)
+ machine->comm_exec = true;
if (dump_trace)
perf_event__fprintf_comm(event, stdout);
- if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) {
+ if (thread == NULL ||
+ __thread__set_comm(thread, event->comm.comm, sample->time, exec)) {
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
return -1;
}
@@ -429,6 +465,7 @@ struct map *machine__new_module(struct machine *machine, u64 start,
{
struct map *map;
struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename);
+ bool compressed;
if (dso == NULL)
return NULL;
@@ -441,6 +478,11 @@ struct map *machine__new_module(struct machine *machine, u64 start,
dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
else
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
+
+ /* _KMODULE_COMP should be next to _KMODULE */
+ if (is_kernel_module(filename, &compressed) && compressed)
+ dso->symtab_type++;
+
map_groups__insert(&machine->kmaps, map);
return map;
}
@@ -448,23 +490,23 @@ struct map *machine__new_module(struct machine *machine, u64 start,
size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
{
struct rb_node *nd;
- size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) +
- __dsos__fprintf(&machines->host.user_dsos, fp);
+ size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) +
+ __dsos__fprintf(&machines->host.user_dsos.head, fp);
for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
- ret += __dsos__fprintf(&pos->kernel_dsos, fp);
- ret += __dsos__fprintf(&pos->user_dsos, fp);
+ ret += __dsos__fprintf(&pos->kernel_dsos.head, fp);
+ ret += __dsos__fprintf(&pos->user_dsos.head, fp);
}
return ret;
}
-size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
+size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm)
{
- return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) +
- __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm);
+ return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) +
+ __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm);
}
size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
@@ -565,8 +607,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
* Returns the name of the start symbol in *symbol_name. Pass in NULL as
* symbol_name if it's not that important.
*/
-static u64 machine__get_kernel_start_addr(struct machine *machine,
- const char **symbol_name)
+static u64 machine__get_running_kernel_start(struct machine *machine,
+ const char **symbol_name)
{
char filename[PATH_MAX];
int i;
@@ -593,7 +635,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine,
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
{
enum map_type type;
- u64 start = machine__get_kernel_start_addr(machine, NULL);
+ u64 start = machine__get_running_kernel_start(machine, NULL);
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
@@ -826,8 +868,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
struct map *map;
char *long_name;
- if (dot == NULL || strcmp(dot, ".ko"))
+ if (dot == NULL)
continue;
+
+ /* On some system, modules are compressed like .ko.gz */
+ if (is_supported_compression(dot + 1) &&
+ is_kmodule_extension(dot - 2))
+ dot -= 3;
+
snprintf(dso_name, sizeof(dso_name), "[%.*s]",
(int)(dot - dent->d_name), dent->d_name);
@@ -912,7 +960,7 @@ int machine__create_kernel_maps(struct machine *machine)
{
struct dso *kernel = machine__get_kernel(machine);
const char *name;
- u64 addr = machine__get_kernel_start_addr(machine, &name);
+ u64 addr = machine__get_running_kernel_start(machine, &name);
if (!addr)
return -1;
@@ -965,7 +1013,7 @@ static bool machine__uses_kcore(struct machine *machine)
{
struct dso *dso;
- list_for_each_entry(dso, &machine->kernel_dsos, node) {
+ list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
if (dso__is_kcore(dso))
return true;
}
@@ -1009,6 +1057,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
dot = strrchr(name, '.');
if (dot == NULL)
goto out_problem;
+ /* On some system, modules are compressed like .ko.gz */
+ if (is_supported_compression(dot + 1))
+ dot -= 3;
+ if (!is_kmodule_extension(dot + 1))
+ goto out_problem;
snprintf(short_module_name, sizeof(short_module_name),
"[%.*s]", (int)(dot - name), name);
strxfrchar(short_module_name, '-', '_');
@@ -1033,8 +1086,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
* Should be there already, from the build-id table in
* the header.
*/
- struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
- kmmap_prefix);
+ struct dso *kernel = NULL;
+ struct dso *dso;
+
+ list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
+ if (is_kernel_module(dso->long_name, NULL))
+ continue;
+
+ kernel = dso;
+ break;
+ }
+
+ if (kernel == NULL)
+ kernel = __dsos__findnew(&machine->kernel_dsos,
+ kmmap_prefix);
if (kernel == NULL)
goto out_problem;
@@ -1042,6 +1107,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
if (__machine__create_kernel_maps(machine, kernel) < 0)
goto out_problem;
+ if (strstr(kernel->long_name, "vmlinux"))
+ dso__set_short_name(kernel, "[kernel.vmlinux]", false);
+
machine__set_kernel_mmap_len(machine, event);
/*
@@ -1254,7 +1322,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
return 0;
}
-static void ip__resolve_ams(struct machine *machine, struct thread *thread,
+static void ip__resolve_ams(struct thread *thread,
struct addr_map_symbol *ams,
u64 ip)
{
@@ -1268,7 +1336,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
* Thus, we have to try consecutively until we find a match
* or else, the symbol is unknown
*/
- thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al);
+ thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al);
ams->addr = ip;
ams->al_addr = al.addr;
@@ -1276,15 +1344,23 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
ams->map = al.map;
}
-static void ip__resolve_data(struct machine *machine, struct thread *thread,
+static void ip__resolve_data(struct thread *thread,
u8 m, struct addr_map_symbol *ams, u64 addr)
{
struct addr_location al;
memset(&al, 0, sizeof(al));
- thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
- &al);
+ thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al);
+ if (al.map == NULL) {
+ /*
+ * some shared data regions have execute bit set which puts
+ * their mapping in the MAP__FUNCTION type array.
+ * Check there as a fallback option before dropping the sample.
+ */
+ thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al);
+ }
+
ams->addr = addr;
ams->al_addr = al.addr;
ams->sym = al.sym;
@@ -1299,14 +1375,45 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
if (!mi)
return NULL;
- ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip);
- ip__resolve_data(al->machine, al->thread, al->cpumode,
- &mi->daddr, sample->addr);
+ ip__resolve_ams(al->thread, &mi->iaddr, sample->ip);
+ ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr);
mi->data_src.val = sample->data_src;
return mi;
}
+static int add_callchain_ip(struct thread *thread,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ int cpumode,
+ u64 ip)
+{
+ struct addr_location al;
+
+ al.filtered = 0;
+ al.sym = NULL;
+ if (cpumode == -1)
+ thread__find_cpumode_addr_location(thread, MAP__FUNCTION,
+ ip, &al);
+ else
+ thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
+ ip, &al);
+ if (al.sym != NULL) {
+ if (sort__has_parent && !*parent &&
+ symbol__match_regex(al.sym, &parent_regex))
+ *parent = al.sym;
+ else if (have_ignore_callees && root_al &&
+ symbol__match_regex(al.sym, &ignore_callees_regex)) {
+ /* Treat this symbol as the root,
+ forgetting its callees. */
+ *root_al = al;
+ callchain_cursor_reset(&callchain_cursor);
+ }
+ }
+
+ return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym);
+}
+
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al)
{
@@ -1318,16 +1425,57 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
return NULL;
for (i = 0; i < bs->nr; i++) {
- ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to);
- ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from);
+ ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to);
+ ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags;
}
return bi;
}
-static int machine__resolve_callchain_sample(struct machine *machine,
- struct thread *thread,
+#define CHASHSZ 127
+#define CHASHBITS 7
+#define NO_ENTRY 0xff
+
+#define PERF_MAX_BRANCH_DEPTH 127
+
+/* Remove loops. */
+static int remove_loops(struct branch_entry *l, int nr)
+{
+ int i, j, off;
+ unsigned char chash[CHASHSZ];
+
+ memset(chash, NO_ENTRY, sizeof(chash));
+
+ BUG_ON(PERF_MAX_BRANCH_DEPTH > 255);
+
+ for (i = 0; i < nr; i++) {
+ int h = hash_64(l[i].from, CHASHBITS) % CHASHSZ;
+
+ /* no collision handling for now */
+ if (chash[h] == NO_ENTRY) {
+ chash[h] = i;
+ } else if (l[chash[h]].from == l[i].from) {
+ bool is_loop = true;
+ /* check if it is a real loop */
+ off = 0;
+ for (j = chash[h]; j < i && i + off < nr; j++, off++)
+ if (l[j].from != l[i + off].from) {
+ is_loop = false;
+ break;
+ }
+ if (is_loop) {
+ memmove(l + i, l + i + off,
+ (nr - (i + off)) * sizeof(*l));
+ nr -= off;
+ }
+ }
+ }
+ return nr;
+}
+
+static int thread__resolve_callchain_sample(struct thread *thread,
struct ip_callchain *chain,
+ struct branch_stack *branch,
struct symbol **parent,
struct addr_location *root_al,
int max_stack)
@@ -1337,24 +1485,83 @@ static int machine__resolve_callchain_sample(struct machine *machine,
int i;
int j;
int err;
- int skip_idx __maybe_unused;
+ int skip_idx = -1;
+ int first_call = 0;
+
+ /*
+ * Based on DWARF debug information, some architectures skip
+ * a callchain entry saved by the kernel.
+ */
+ if (chain->nr < PERF_MAX_STACK_DEPTH)
+ skip_idx = arch_skip_callchain_idx(thread, chain);
callchain_cursor_reset(&callchain_cursor);
+ /*
+ * Add branches to call stack for easier browsing. This gives
+ * more context for a sample than just the callers.
+ *
+ * This uses individual histograms of paths compared to the
+ * aggregated histograms the normal LBR mode uses.
+ *
+ * Limitations for now:
+ * - No extra filters
+ * - No annotations (should annotate somehow)
+ */
+
+ if (branch && callchain_param.branch_callstack) {
+ int nr = min(max_stack, (int)branch->nr);
+ struct branch_entry be[nr];
+
+ if (branch->nr > PERF_MAX_BRANCH_DEPTH) {
+ pr_warning("corrupted branch chain. skipping...\n");
+ goto check_calls;
+ }
+
+ for (i = 0; i < nr; i++) {
+ if (callchain_param.order == ORDER_CALLEE) {
+ be[i] = branch->entries[i];
+ /*
+ * Check for overlap into the callchain.
+ * The return address is one off compared to
+ * the branch entry. To adjust for this
+ * assume the calling instruction is not longer
+ * than 8 bytes.
+ */
+ if (i == skip_idx ||
+ chain->ips[first_call] >= PERF_CONTEXT_MAX)
+ first_call++;
+ else if (be[i].from < chain->ips[first_call] &&
+ be[i].from >= chain->ips[first_call] - 8)
+ first_call++;
+ } else
+ be[i] = branch->entries[branch->nr - i - 1];
+ }
+
+ nr = remove_loops(be, nr);
+
+ for (i = 0; i < nr; i++) {
+ err = add_callchain_ip(thread, parent, root_al,
+ -1, be[i].to);
+ if (!err)
+ err = add_callchain_ip(thread, parent, root_al,
+ -1, be[i].from);
+ if (err == -EINVAL)
+ break;
+ if (err)
+ return err;
+ }
+ chain_nr -= nr;
+ }
+
+check_calls:
if (chain->nr > PERF_MAX_STACK_DEPTH) {
pr_warning("corrupted callchain. skipping...\n");
return 0;
}
- /*
- * Based on DWARF debug information, some architectures skip
- * a callchain entry saved by the kernel.
- */
- skip_idx = arch_skip_callchain_idx(machine, thread, chain);
-
- for (i = 0; i < chain_nr; i++) {
+ for (i = first_call; i < chain_nr; i++) {
u64 ip;
- struct addr_location al;
if (callchain_param.order == ORDER_CALLEE)
j = i;
@@ -1391,24 +1598,10 @@ static int machine__resolve_callchain_sample(struct machine *machine,
continue;
}
- al.filtered = 0;
- thread__find_addr_location(thread, machine, cpumode,
- MAP__FUNCTION, ip, &al);
- if (al.sym != NULL) {
- if (sort__has_parent && !*parent &&
- symbol__match_regex(al.sym, &parent_regex))
- *parent = al.sym;
- else if (have_ignore_callees && root_al &&
- symbol__match_regex(al.sym, &ignore_callees_regex)) {
- /* Treat this symbol as the root,
- forgetting its callees. */
- *root_al = al;
- callchain_cursor_reset(&callchain_cursor);
- }
- }
-
- err = callchain_cursor_append(&callchain_cursor,
- ip, al.map, al.sym);
+ err = add_callchain_ip(thread, parent, root_al,
+ cpumode, ip);
+ if (err == -EINVAL)
+ break;
if (err)
return err;
}
@@ -1423,19 +1616,16 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
entry->map, entry->sym);
}
-int machine__resolve_callchain(struct machine *machine,
- struct perf_evsel *evsel,
- struct thread *thread,
- struct perf_sample *sample,
- struct symbol **parent,
- struct addr_location *root_al,
- int max_stack)
+int thread__resolve_callchain(struct thread *thread,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ int max_stack)
{
- int ret;
-
- ret = machine__resolve_callchain_sample(machine, thread,
- sample->callchain, parent,
- root_al, max_stack);
+ int ret = thread__resolve_callchain_sample(thread, sample->callchain,
+ sample->branch_stack,
+ parent, root_al, max_stack);
if (ret)
return ret;
@@ -1449,7 +1639,7 @@ int machine__resolve_callchain(struct machine *machine,
(!sample->user_stack.size))
return 0;
- return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
+ return unwind__get_entries(unwind_entry, &callchain_cursor,
thread, sample, max_stack);
}
@@ -1531,3 +1721,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
return 0;
}
+
+int machine__get_kernel_start(struct machine *machine)
+{
+ struct map *map = machine__kernel_map(machine, MAP__FUNCTION);
+ int err = 0;
+
+ /*
+ * The only addresses above 2^63 are kernel addresses of a 64-bit
+ * kernel. Note that addresses are unsigned so that on a 32-bit system
+ * all addresses including kernel addresses are less than 2^32. In
+ * that case (32-bit system), if the kernel mapping is unknown, all
+ * addresses will be assumed to be in user space - see
+ * machine__kernel_ip().
+ */
+ machine->kernel_start = 1ULL << 63;
+ if (map) {
+ err = map__load(map, machine->symbol_filter);
+ if (map->start)
+ machine->kernel_start = map->start;
+ }
+ return err;
+}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index b972824e6294..e8b7779a0a3f 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -4,6 +4,7 @@
#include <sys/types.h>
#include <linux/rbtree.h>
#include "map.h"
+#include "dso.h"
#include "event.h"
struct addr_location;
@@ -26,17 +27,23 @@ struct machine {
struct rb_node rb_node;
pid_t pid;
u16 id_hdr_size;
+ bool comm_exec;
char *root_dir;
struct rb_root threads;
struct list_head dead_threads;
struct thread *last_match;
struct vdso_info *vdso_info;
- struct list_head user_dsos;
- struct list_head kernel_dsos;
+ struct dsos user_dsos;
+ struct dsos kernel_dsos;
struct map_groups kmaps;
struct map *vmlinux_maps[MAP__NR_TYPES];
+ u64 kernel_start;
symbol_filter_t symbol_filter;
pid_t *current_tid;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
};
static inline
@@ -45,8 +52,26 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
return machine->vmlinux_maps[type];
}
+int machine__get_kernel_start(struct machine *machine);
+
+static inline u64 machine__kernel_start(struct machine *machine)
+{
+ if (!machine->kernel_start)
+ machine__get_kernel_start(machine);
+ return machine->kernel_start;
+}
+
+static inline bool machine__kernel_ip(struct machine *machine, u64 ip)
+{
+ u64 kernel_start = machine__kernel_start(machine);
+
+ return ip >= kernel_start;
+}
+
struct thread *machine__find_thread(struct machine *machine, pid_t pid,
pid_t tid);
+struct comm *machine__thread_exec_comm(struct machine *machine,
+ struct thread *thread);
int machine__process_comm_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample);
@@ -88,6 +113,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size);
void machines__set_symbol_filter(struct machines *machines,
symbol_filter_t symbol_filter);
+void machines__set_comm_exec(struct machines *machines, bool comm_exec);
struct machine *machine__new_host(void);
int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
@@ -100,13 +126,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al);
struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al);
-int machine__resolve_callchain(struct machine *machine,
- struct perf_evsel *evsel,
- struct thread *thread,
- struct perf_sample *sample,
- struct symbol **parent,
- struct addr_location *root_al,
- int max_stack);
+int thread__resolve_callchain(struct thread *thread,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ int max_stack);
/*
* Default guest kernel is defined by parameter --guestkallsyms
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 31b8905dd863..62ca9f2607d5 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename)
static inline int is_no_dso_memory(const char *filename)
{
return !strncmp(filename, "[stack", 6) ||
+ !strncmp(filename, "/SYSV",5) ||
!strcmp(filename, "[heap]");
}
@@ -359,7 +360,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
if (map && map->dso) {
srcline = get_srcline(map->dso,
- map__rip_2objdump(map, addr));
+ map__rip_2objdump(map, addr), NULL, true);
if (srcline != SRCLINE_UNKNOWN)
ret = fprintf(fp, "%s%s", prefix, srcline);
free_srcline(srcline);
@@ -412,14 +413,14 @@ u64 map__objdump_2mem(struct map *map, u64 ip)
return ip + map->reloc;
}
-void map_groups__init(struct map_groups *mg)
+void map_groups__init(struct map_groups *mg, struct machine *machine)
{
int i;
for (i = 0; i < MAP__NR_TYPES; ++i) {
mg->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&mg->removed_maps[i]);
}
- mg->machine = NULL;
+ mg->machine = machine;
mg->refcnt = 1;
}
@@ -470,12 +471,12 @@ bool map_groups__empty(struct map_groups *mg)
return true;
}
-struct map_groups *map_groups__new(void)
+struct map_groups *map_groups__new(struct machine *machine)
{
struct map_groups *mg = malloc(sizeof(*mg));
if (mg != NULL)
- map_groups__init(mg);
+ map_groups__init(mg, machine);
return mg;
}
@@ -555,7 +556,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter)
{
- if (ams->addr < ams->map->start || ams->addr > ams->map->end) {
+ if (ams->addr < ams->map->start || ams->addr >= ams->map->end) {
if (ams->map->groups == NULL)
return -1;
ams->map = map_groups__find(ams->map->groups, ams->map->type,
@@ -663,7 +664,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
goto move_map;
}
- before->end = map->start - 1;
+ before->end = map->start;
map_groups__insert(mg, before);
if (verbose >= 2)
map__fprintf(before, fp);
@@ -677,7 +678,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
goto move_map;
}
- after->start = map->end + 1;
+ after->start = map->end;
map_groups__insert(mg, after);
if (verbose >= 2)
map__fprintf(after, fp);
@@ -751,7 +752,7 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
m = rb_entry(parent, struct map, rb_node);
if (ip < m->start)
p = &(*p)->rb_left;
- else if (ip > m->end)
+ else if (ip >= m->end)
p = &(*p)->rb_right;
else
return m;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 2f83954af050..6951a9d42339 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -64,7 +64,7 @@ struct map_groups {
int refcnt;
};
-struct map_groups *map_groups__new(void);
+struct map_groups *map_groups__new(struct machine *machine);
void map_groups__delete(struct map_groups *mg);
bool map_groups__empty(struct map_groups *mg);
@@ -150,7 +150,7 @@ void maps__remove(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 addr);
struct map *maps__first(struct rb_root *maps);
struct map *maps__next(struct map *map);
-void map_groups__init(struct map_groups *mg);
+void map_groups__init(struct map_groups *mg, struct machine *machine);
void map_groups__exit(struct map_groups *mg);
int map_groups__clone(struct map_groups *mg,
struct map_groups *parent, enum map_type type);
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
new file mode 100644
index 000000000000..fd4be94125fb
--- /dev/null
+++ b/tools/perf/util/ordered-events.c
@@ -0,0 +1,286 @@
+#include <linux/list.h>
+#include <linux/compiler.h>
+#include <linux/string.h>
+#include "ordered-events.h"
+#include "evlist.h"
+#include "session.h"
+#include "asm/bug.h"
+#include "debug.h"
+
+#define pr_N(n, fmt, ...) \
+ eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__)
+
+#define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__)
+
+static void queue_event(struct ordered_events *oe, struct ordered_event *new)
+{
+ struct ordered_event *last = oe->last;
+ u64 timestamp = new->timestamp;
+ struct list_head *p;
+
+ ++oe->nr_events;
+ oe->last = new;
+
+ pr_oe_time2(timestamp, "queue_event nr_events %u\n", oe->nr_events);
+
+ if (!last) {
+ list_add(&new->list, &oe->events);
+ oe->max_timestamp = timestamp;
+ return;
+ }
+
+ /*
+ * last event might point to some random place in the list as it's
+ * the last queued event. We expect that the new event is close to
+ * this.
+ */
+ if (last->timestamp <= timestamp) {
+ while (last->timestamp <= timestamp) {
+ p = last->list.next;
+ if (p == &oe->events) {
+ list_add_tail(&new->list, &oe->events);
+ oe->max_timestamp = timestamp;
+ return;
+ }
+ last = list_entry(p, struct ordered_event, list);
+ }
+ list_add_tail(&new->list, &last->list);
+ } else {
+ while (last->timestamp > timestamp) {
+ p = last->list.prev;
+ if (p == &oe->events) {
+ list_add(&new->list, &oe->events);
+ return;
+ }
+ last = list_entry(p, struct ordered_event, list);
+ }
+ list_add(&new->list, &last->list);
+ }
+}
+
+static union perf_event *__dup_event(struct ordered_events *oe,
+ union perf_event *event)
+{
+ union perf_event *new_event = NULL;
+
+ if (oe->cur_alloc_size < oe->max_alloc_size) {
+ new_event = memdup(event, event->header.size);
+ if (new_event)
+ oe->cur_alloc_size += event->header.size;
+ }
+
+ return new_event;
+}
+
+static union perf_event *dup_event(struct ordered_events *oe,
+ union perf_event *event)
+{
+ return oe->copy_on_queue ? __dup_event(oe, event) : event;
+}
+
+static void free_dup_event(struct ordered_events *oe, union perf_event *event)
+{
+ if (oe->copy_on_queue) {
+ oe->cur_alloc_size -= event->header.size;
+ free(event);
+ }
+}
+
+#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event))
+static struct ordered_event *alloc_event(struct ordered_events *oe,
+ union perf_event *event)
+{
+ struct list_head *cache = &oe->cache;
+ struct ordered_event *new = NULL;
+ union perf_event *new_event;
+
+ new_event = dup_event(oe, event);
+ if (!new_event)
+ return NULL;
+
+ if (!list_empty(cache)) {
+ new = list_entry(cache->next, struct ordered_event, list);
+ list_del(&new->list);
+ } else if (oe->buffer) {
+ new = oe->buffer + oe->buffer_idx;
+ if (++oe->buffer_idx == MAX_SAMPLE_BUFFER)
+ oe->buffer = NULL;
+ } else if (oe->cur_alloc_size < oe->max_alloc_size) {
+ size_t size = MAX_SAMPLE_BUFFER * sizeof(*new);
+
+ oe->buffer = malloc(size);
+ if (!oe->buffer) {
+ free_dup_event(oe, new_event);
+ return NULL;
+ }
+
+ pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n",
+ oe->cur_alloc_size, size, oe->max_alloc_size);
+
+ oe->cur_alloc_size += size;
+ list_add(&oe->buffer->list, &oe->to_free);
+
+ /* First entry is abused to maintain the to_free list. */
+ oe->buffer_idx = 2;
+ new = oe->buffer + 1;
+ } else {
+ pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size);
+ }
+
+ new->event = new_event;
+ return new;
+}
+
+struct ordered_event *
+ordered_events__new(struct ordered_events *oe, u64 timestamp,
+ union perf_event *event)
+{
+ struct ordered_event *new;
+
+ new = alloc_event(oe, event);
+ if (new) {
+ new->timestamp = timestamp;
+ queue_event(oe, new);
+ }
+
+ return new;
+}
+
+void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event)
+{
+ list_move(&event->list, &oe->cache);
+ oe->nr_events--;
+ free_dup_event(oe, event->event);
+}
+
+static int __ordered_events__flush(struct perf_session *s,
+ struct perf_tool *tool)
+{
+ struct ordered_events *oe = &s->ordered_events;
+ struct list_head *head = &oe->events;
+ struct ordered_event *tmp, *iter;
+ struct perf_sample sample;
+ u64 limit = oe->next_flush;
+ u64 last_ts = oe->last ? oe->last->timestamp : 0ULL;
+ bool show_progress = limit == ULLONG_MAX;
+ struct ui_progress prog;
+ int ret;
+
+ if (!tool->ordered_events || !limit)
+ return 0;
+
+ if (show_progress)
+ ui_progress__init(&prog, oe->nr_events, "Processing time ordered events...");
+
+ list_for_each_entry_safe(iter, tmp, head, list) {
+ if (session_done())
+ return 0;
+
+ if (iter->timestamp > limit)
+ break;
+
+ ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample);
+ if (ret)
+ pr_err("Can't parse sample, err = %d\n", ret);
+ else {
+ ret = perf_session__deliver_event(s, iter->event, &sample, tool,
+ iter->file_offset);
+ if (ret)
+ return ret;
+ }
+
+ ordered_events__delete(oe, iter);
+ oe->last_flush = iter->timestamp;
+
+ if (show_progress)
+ ui_progress__update(&prog, 1);
+ }
+
+ if (list_empty(head))
+ oe->last = NULL;
+ else if (last_ts <= limit)
+ oe->last = list_entry(head->prev, struct ordered_event, list);
+
+ return 0;
+}
+
+int ordered_events__flush(struct perf_session *s, struct perf_tool *tool,
+ enum oe_flush how)
+{
+ struct ordered_events *oe = &s->ordered_events;
+ static const char * const str[] = {
+ "NONE",
+ "FINAL",
+ "ROUND",
+ "HALF ",
+ };
+ int err;
+
+ switch (how) {
+ case OE_FLUSH__FINAL:
+ oe->next_flush = ULLONG_MAX;
+ break;
+
+ case OE_FLUSH__HALF:
+ {
+ struct ordered_event *first, *last;
+ struct list_head *head = &oe->events;
+
+ first = list_entry(head->next, struct ordered_event, list);
+ last = oe->last;
+
+ /* Warn if we are called before any event got allocated. */
+ if (WARN_ONCE(!last || list_empty(head), "empty queue"))
+ return 0;
+
+ oe->next_flush = first->timestamp;
+ oe->next_flush += (last->timestamp - first->timestamp) / 2;
+ break;
+ }
+
+ case OE_FLUSH__ROUND:
+ case OE_FLUSH__NONE:
+ default:
+ break;
+ };
+
+ pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE %s, nr_events %u\n",
+ str[how], oe->nr_events);
+ pr_oe_time(oe->max_timestamp, "max_timestamp\n");
+
+ err = __ordered_events__flush(s, tool);
+
+ if (!err) {
+ if (how == OE_FLUSH__ROUND)
+ oe->next_flush = oe->max_timestamp;
+
+ oe->last_flush_type = how;
+ }
+
+ pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush POST %s, nr_events %u\n",
+ str[how], oe->nr_events);
+ pr_oe_time(oe->last_flush, "last_flush\n");
+
+ return err;
+}
+
+void ordered_events__init(struct ordered_events *oe)
+{
+ INIT_LIST_HEAD(&oe->events);
+ INIT_LIST_HEAD(&oe->cache);
+ INIT_LIST_HEAD(&oe->to_free);
+ oe->max_alloc_size = (u64) -1;
+ oe->cur_alloc_size = 0;
+}
+
+void ordered_events__free(struct ordered_events *oe)
+{
+ while (!list_empty(&oe->to_free)) {
+ struct ordered_event *event;
+
+ event = list_entry(oe->to_free.next, struct ordered_event, list);
+ list_del(&event->list);
+ free_dup_event(oe, event->event);
+ free(event);
+ }
+}
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h
new file mode 100644
index 000000000000..7b8f9b011f38
--- /dev/null
+++ b/tools/perf/util/ordered-events.h
@@ -0,0 +1,59 @@
+#ifndef __ORDERED_EVENTS_H
+#define __ORDERED_EVENTS_H
+
+#include <linux/types.h>
+#include "tool.h"
+
+struct perf_session;
+
+struct ordered_event {
+ u64 timestamp;
+ u64 file_offset;
+ union perf_event *event;
+ struct list_head list;
+};
+
+enum oe_flush {
+ OE_FLUSH__NONE,
+ OE_FLUSH__FINAL,
+ OE_FLUSH__ROUND,
+ OE_FLUSH__HALF,
+};
+
+struct ordered_events {
+ u64 last_flush;
+ u64 next_flush;
+ u64 max_timestamp;
+ u64 max_alloc_size;
+ u64 cur_alloc_size;
+ struct list_head events;
+ struct list_head cache;
+ struct list_head to_free;
+ struct ordered_event *buffer;
+ struct ordered_event *last;
+ int buffer_idx;
+ unsigned int nr_events;
+ enum oe_flush last_flush_type;
+ bool copy_on_queue;
+};
+
+struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp,
+ union perf_event *event);
+void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event);
+int ordered_events__flush(struct perf_session *s, struct perf_tool *tool,
+ enum oe_flush how);
+void ordered_events__init(struct ordered_events *oe);
+void ordered_events__free(struct ordered_events *oe);
+
+static inline
+void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size)
+{
+ oe->max_alloc_size = size;
+}
+
+static inline
+void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy)
+{
+ oe->copy_on_queue = copy;
+}
+#endif /* __ORDERED_EVENTS_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 1e15df10a88c..77b43fe43d55 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -6,10 +6,11 @@
#include "parse-options.h"
#include "parse-events.h"
#include "exec_cmd.h"
-#include "linux/string.h"
+#include "string.h"
#include "symbol.h"
#include "cache.h"
#include "header.h"
+#include "debug.h"
#include <api/fs/debugfs.h>
#include "parse-events-bison.h"
#define YY_EXTRA_TYPE int
@@ -29,6 +30,15 @@ extern int parse_events_debug;
#endif
int parse_events_parse(void *data, void *scanner);
+static struct perf_pmu_event_symbol *perf_pmu_events_list;
+/*
+ * The variable indicates the number of supported pmu event symbols.
+ * 0 means not initialized and ready to init
+ * -1 means failed to init, don't try anymore
+ * >0 is the number of supported pmu event symbols
+ */
+static int perf_pmu_events_list_num;
+
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles",
@@ -633,18 +643,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
char *name, struct list_head *head_config)
{
struct perf_event_attr attr;
+ struct perf_pmu_info info;
struct perf_pmu *pmu;
struct perf_evsel *evsel;
- const char *unit;
- double scale;
pmu = perf_pmu__find(name);
if (!pmu)
return -EINVAL;
- memset(&attr, 0, sizeof(attr));
+ if (pmu->default_config) {
+ memcpy(&attr, pmu->default_config,
+ sizeof(struct perf_event_attr));
+ } else {
+ memset(&attr, 0, sizeof(attr));
+ }
- if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
+ if (!head_config) {
+ attr.type = pmu->type;
+ evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+ return evsel ? 0 : -ENOMEM;
+ }
+
+ if (perf_pmu__check_alias(pmu, head_config, &info))
return -EINVAL;
/*
@@ -659,8 +679,10 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
pmu->cpus);
if (evsel) {
- evsel->unit = unit;
- evsel->scale = scale;
+ evsel->unit = info.unit;
+ evsel->scale = info.scale;
+ evsel->per_pkg = info.per_pkg;
+ evsel->snapshot = info.snapshot;
}
return evsel ? 0 : -ENOMEM;
@@ -852,30 +874,111 @@ int parse_events_name(struct list_head *list, char *name)
return 0;
}
-static int parse_events__scanner(const char *str, void *data, int start_token);
+static int
+comp_pmu(const void *p1, const void *p2)
+{
+ struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
+ struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
-static int parse_events_fixup(int ret, const char *str, void *data,
- int start_token)
+ return strcmp(pmu1->symbol, pmu2->symbol);
+}
+
+static void perf_pmu__parse_cleanup(void)
{
- char *o = strdup(str);
- char *s = NULL;
- char *t = o;
- char *p;
+ if (perf_pmu_events_list_num > 0) {
+ struct perf_pmu_event_symbol *p;
+ int i;
+
+ for (i = 0; i < perf_pmu_events_list_num; i++) {
+ p = perf_pmu_events_list + i;
+ free(p->symbol);
+ }
+ free(perf_pmu_events_list);
+ perf_pmu_events_list = NULL;
+ perf_pmu_events_list_num = 0;
+ }
+}
+
+#define SET_SYMBOL(str, stype) \
+do { \
+ p->symbol = str; \
+ if (!p->symbol) \
+ goto err; \
+ p->type = stype; \
+} while (0)
+
+/*
+ * Read the pmu events list from sysfs
+ * Save it into perf_pmu_events_list
+ */
+static void perf_pmu__parse_init(void)
+{
+
+ struct perf_pmu *pmu = NULL;
+ struct perf_pmu_alias *alias;
int len = 0;
- if (!o)
- return ret;
- while ((p = strsep(&t, ",")) != NULL) {
- if (s)
- str_append(&s, &len, ",");
- str_append(&s, &len, "cpu/");
- str_append(&s, &len, p);
- str_append(&s, &len, "/");
+ pmu = perf_pmu__find("cpu");
+ if ((pmu == NULL) || list_empty(&pmu->aliases)) {
+ perf_pmu_events_list_num = -1;
+ return;
}
- free(o);
- if (!s)
- return -ENOMEM;
- return parse_events__scanner(s, data, start_token);
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ if (strchr(alias->name, '-'))
+ len++;
+ len++;
+ }
+ perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
+ if (!perf_pmu_events_list)
+ return;
+ perf_pmu_events_list_num = len;
+
+ len = 0;
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
+ char *tmp = strchr(alias->name, '-');
+
+ if (tmp != NULL) {
+ SET_SYMBOL(strndup(alias->name, tmp - alias->name),
+ PMU_EVENT_SYMBOL_PREFIX);
+ p++;
+ SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
+ len += 2;
+ } else {
+ SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
+ len++;
+ }
+ }
+ qsort(perf_pmu_events_list, len,
+ sizeof(struct perf_pmu_event_symbol), comp_pmu);
+
+ return;
+err:
+ perf_pmu__parse_cleanup();
+}
+
+enum perf_pmu_event_symbol_type
+perf_pmu__parse_check(const char *name)
+{
+ struct perf_pmu_event_symbol p, *r;
+
+ /* scan kernel pmu events from sysfs if needed */
+ if (perf_pmu_events_list_num == 0)
+ perf_pmu__parse_init();
+ /*
+ * name "cpu" could be prefix of cpu-cycles or cpu// events.
+ * cpu-cycles has been handled by hardcode.
+ * So it must be cpu// events, not kernel pmu event.
+ */
+ if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
+ return PMU_EVENT_SYMBOL_ERR;
+
+ p.symbol = strdup(name);
+ r = bsearch(&p, perf_pmu_events_list,
+ (size_t) perf_pmu_events_list_num,
+ sizeof(struct perf_pmu_event_symbol), comp_pmu);
+ free(p.symbol);
+ return r ? r->type : PMU_EVENT_SYMBOL_ERR;
}
static int parse_events__scanner(const char *str, void *data, int start_token)
@@ -898,8 +1001,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
parse_events__flush_buffer(buffer, scanner);
parse_events__delete_buffer(buffer, scanner);
parse_events_lex_destroy(scanner);
- if (ret && !strchr(str, '/'))
- ret = parse_events_fixup(ret, str, data, start_token);
return ret;
}
@@ -934,6 +1035,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
int ret;
ret = parse_events__scanner(str, &data, PE_START_EVENTS);
+ perf_pmu__parse_cleanup();
if (!ret) {
int entries = data.idx - evlist->nr_entries;
perf_evlist__splice_list_tail(evlist, &data.list, entries);
@@ -973,7 +1075,7 @@ int parse_filter(const struct option *opt, const char *str,
if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
fprintf(stderr,
- "-F option should follow a -e tracepoint option\n");
+ "--filter option should follow a -e tracepoint option\n");
return -1;
}
@@ -1006,9 +1108,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
+ char sbuf[STRERR_BUFSIZE];
if (debugfs_valid_mountpoint(tracing_events_path)) {
- printf(" [ Tracepoints not available: %s ]\n", strerror(errno));
+ printf(" [ Tracepoints not available: %s ]\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return;
}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index df094b4ed5ed..db2cf78ff0f3 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024)
+enum perf_pmu_event_symbol_type {
+ PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */
+ PMU_EVENT_SYMBOL, /* normal style PMU event */
+ PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */
+ PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */
+};
+
+struct perf_pmu_event_symbol {
+ char *symbol;
+ enum perf_pmu_event_symbol_type type;
+};
+
enum {
PARSE_EVENTS__TERM_TYPE_NUM,
PARSE_EVENTS__TERM_TYPE_STR,
@@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type);
int parse_events_add_pmu(struct list_head *list, int *idx,
char *pmu , struct list_head *head_config);
+enum perf_pmu_event_symbol_type
+perf_pmu__parse_check(const char *name);
void parse_events__set_leader(char *name, struct list_head *list);
void parse_events_update_lists(struct list_head *list_event,
struct list_head *list_all);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 343299575b30..906630bbf8eb 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token)
return token;
}
+static int pmu_str_check(yyscan_t scanner)
+{
+ YYSTYPE *yylval = parse_events_get_lval(scanner);
+ char *text = parse_events_get_text(scanner);
+
+ yylval->str = strdup(text);
+ switch (perf_pmu__parse_check(text)) {
+ case PMU_EVENT_SYMBOL_PREFIX:
+ return PE_PMU_EVENT_PRE;
+ case PMU_EVENT_SYMBOL_SUFFIX:
+ return PE_PMU_EVENT_SUF;
+ case PMU_EVENT_SYMBOL:
+ return PE_KERNEL_PMU_EVENT;
+ default:
+ return PE_NAME;
+ }
+}
+
static int sym(yyscan_t scanner, int type, int config)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
@@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
+ /*
+ * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
+ * Because the prefix cycles is mixed up with cpu-cycles.
+ * loads and stores are mixed up with cache event
+ */
+cycles-ct { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
+cycles-t { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
+mem-loads { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
+mem-stores { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
+
L1-dcache|l1-d|l1d|L1-data |
L1-icache|l1-i|l1i|L1-instruction |
LLC|L2 |
@@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); }
{num_hex} { return value(yyscanner, 16); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
-{name} { return str(yyscanner, PE_NAME); }
+{name} { return pmu_str_check(yyscanner); }
"/" { BEGIN(config); return '/'; }
- { return '-'; }
, { BEGIN(event); return ','; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 0bc87ba46bf3..93c4c9fbc922 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list,
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR
+%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
@@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list,
%type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP
%type <str> PE_EVENT_NAME
+%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> value_sym
%type <head> event_config
%type <term> event_term
@@ -210,6 +212,54 @@ PE_NAME '/' event_config '/'
parse_events__free_terms($3);
$$ = list;
}
+|
+PE_NAME '/' '/'
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL));
+ $$ = list;
+}
+|
+PE_KERNEL_PMU_EVENT sep_dc
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *head;
+ struct parse_events_term *term;
+ struct list_head *list;
+
+ ALLOC_LIST(head);
+ ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, 1));
+ list_add_tail(&term->list, head);
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head));
+ parse_events__free_terms(head);
+ $$ = list;
+}
+|
+PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *head;
+ struct parse_events_term *term;
+ struct list_head *list;
+ char pmu_name[128];
+ snprintf(&pmu_name, 128, "%s-%s", $1, $3);
+
+ ALLOC_LIST(head);
+ ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ &pmu_name, 1));
+ list_add_tail(&term->list, head);
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head));
+ parse_events__free_terms(head);
+ $$ = list;
+}
value_sym:
PE_VALUE_SYM_HW
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index bf48092983c6..f62dee7bd924 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -42,7 +42,26 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "takes no value", flags);
if (unset && (opt->flags & PARSE_OPT_NONEG))
return opterror(opt, "isn't available", flags);
-
+ if (opt->flags & PARSE_OPT_DISABLED)
+ return opterror(opt, "is not usable", flags);
+
+ if (opt->flags & PARSE_OPT_EXCLUSIVE) {
+ if (p->excl_opt) {
+ char msg[128];
+
+ if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
+ p->excl_opt->long_name == NULL) {
+ scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
+ p->excl_opt->short_name);
+ } else {
+ scnprintf(msg, sizeof(msg), "cannot be used with %s",
+ p->excl_opt->long_name);
+ }
+ opterror(opt, msg, flags);
+ return -3;
+ }
+ p->excl_opt = opt;
+ }
if (!(flags & OPT_SHORT) && p->opt) {
switch (opt->type) {
case OPTION_CALLBACK:
@@ -343,13 +362,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+ int excl_short_opt = 1;
+ const char *arg;
/* we must reset ->opt, unknown short option leave it dangling */
ctx->opt = NULL;
for (; ctx->argc; ctx->argc--, ctx->argv++) {
- const char *arg = ctx->argv[0];
-
+ arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) {
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
@@ -358,19 +378,21 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
}
if (arg[1] != '-') {
- ctx->opt = arg + 1;
+ ctx->opt = ++arg;
if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options, arg + 1, 1);
+ return parse_options_usage(usagestr, options, arg, 1);
case -2:
goto unknown;
+ case -3:
+ goto exclusive;
default:
break;
}
if (ctx->opt)
- check_typos(arg + 1, options);
+ check_typos(arg, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0);
@@ -387,6 +409,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
ctx->argv[0] = strdup(ctx->opt - 1);
*(char *)ctx->argv[0] = '-';
goto unknown;
+ case -3:
+ goto exclusive;
default:
break;
}
@@ -402,19 +426,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
break;
}
- if (internal_help && !strcmp(arg + 2, "help-all"))
+ arg += 2;
+ if (internal_help && !strcmp(arg, "help-all"))
return usage_with_options_internal(usagestr, options, 1);
- if (internal_help && !strcmp(arg + 2, "help"))
+ if (internal_help && !strcmp(arg, "help"))
return usage_with_options_internal(usagestr, options, 0);
- if (!strcmp(arg + 2, "list-opts"))
+ if (!strcmp(arg, "list-opts"))
return PARSE_OPT_LIST_OPTS;
- if (!strcmp(arg + 2, "list-cmds"))
+ if (!strcmp(arg, "list-cmds"))
return PARSE_OPT_LIST_SUBCMDS;
- switch (parse_long_opt(ctx, arg + 2, options)) {
+ switch (parse_long_opt(ctx, arg, options)) {
case -1:
- return parse_options_usage(usagestr, options, arg + 2, 0);
+ return parse_options_usage(usagestr, options, arg, 0);
case -2:
goto unknown;
+ case -3:
+ excl_short_opt = 0;
+ goto exclusive;
default:
break;
}
@@ -426,6 +454,17 @@ unknown:
ctx->opt = NULL;
}
return PARSE_OPT_DONE;
+
+exclusive:
+ parse_options_usage(usagestr, options, arg, excl_short_opt);
+ if ((excl_short_opt && ctx->excl_opt->short_name) ||
+ ctx->excl_opt->long_name == NULL) {
+ char opt = ctx->excl_opt->short_name;
+ parse_options_usage(NULL, options, &opt, 1);
+ } else {
+ parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
+ }
+ return PARSE_OPT_HELP;
}
int parse_options_end(struct parse_opt_ctx_t *ctx)
@@ -509,6 +548,8 @@ static void print_option_help(const struct option *opts, int full)
}
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
return;
+ if (opts->flags & PARSE_OPT_DISABLED)
+ return;
pos = fprintf(stderr, " ");
if (opts->short_name)
@@ -679,3 +720,16 @@ int parse_opt_verbosity_cb(const struct option *opt,
}
return 0;
}
+
+void set_option_flag(struct option *opts, int shortopt, const char *longopt,
+ int flag)
+{
+ for (; opts->type != OPTION_END; opts++) {
+ if ((shortopt && opts->short_name == shortopt) ||
+ (opts->long_name && longopt &&
+ !strcmp(opts->long_name, longopt))) {
+ opts->flags |= flag;
+ break;
+ }
+ }
+}
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index b59ba858e73d..97b153fb4999 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -38,6 +38,8 @@ enum parse_opt_option_flags {
PARSE_OPT_NONEG = 4,
PARSE_OPT_HIDDEN = 8,
PARSE_OPT_LASTARG_DEFAULT = 16,
+ PARSE_OPT_DISABLED = 32,
+ PARSE_OPT_EXCLUSIVE = 64,
};
struct option;
@@ -173,6 +175,7 @@ struct parse_opt_ctx_t {
const char **out;
int argc, cpidx;
const char *opt;
+ const struct option *excl_opt;
int flags;
};
@@ -211,4 +214,5 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
+void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag);
#endif /* __PERF_PARSE_OPTIONS_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 7a811eb61f75..5c9c4947cfb4 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -2,6 +2,8 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
#include <dirent.h>
#include <api/fs/fs.h>
#include <locale.h>
@@ -10,16 +12,6 @@
#include "parse-events.h"
#include "cpumap.h"
-#define UNIT_MAX_LEN 31 /* max length for event unit name */
-
-struct perf_pmu_alias {
- char *name;
- struct list_head terms;
- struct list_head list;
- char unit[UNIT_MAX_LEN+1];
- double scale;
-};
-
struct perf_pmu_format {
char *name;
int value;
@@ -171,6 +163,41 @@ error:
return -1;
}
+static int
+perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name)
+{
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name);
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ close(fd);
+
+ alias->per_pkg = true;
+ return 0;
+}
+
+static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
+ char *dir, char *name)
+{
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name);
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ alias->snapshot = true;
+ close(fd);
+ return 0;
+}
+
static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
{
struct perf_pmu_alias *alias;
@@ -189,6 +216,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
INIT_LIST_HEAD(&alias->terms);
alias->scale = 1.0;
alias->unit[0] = '\0';
+ alias->per_pkg = false;
ret = parse_events_terms(&alias->terms, buf);
if (ret) {
@@ -202,12 +230,31 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
*/
perf_pmu__parse_unit(alias, dir, name);
perf_pmu__parse_scale(alias, dir, name);
+ perf_pmu__parse_per_pkg(alias, dir, name);
+ perf_pmu__parse_snapshot(alias, dir, name);
list_add_tail(&alias->list, list);
return 0;
}
+static inline bool pmu_alias_info_file(char *name)
+{
+ size_t len;
+
+ len = strlen(name);
+ if (len > 5 && !strcmp(name + len - 5, ".unit"))
+ return true;
+ if (len > 6 && !strcmp(name + len - 6, ".scale"))
+ return true;
+ if (len > 8 && !strcmp(name + len - 8, ".per-pkg"))
+ return true;
+ if (len > 9 && !strcmp(name + len - 9, ".snapshot"))
+ return true;
+
+ return false;
+}
+
/*
* Process all the sysfs attributes located under the directory
* specified in 'dir' parameter.
@@ -216,7 +263,6 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
- size_t len;
int ret = 0;
event_dir = opendir(dir);
@@ -232,13 +278,9 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
continue;
/*
- * skip .unit and .scale info files
- * parsed in perf_pmu__new_alias()
+ * skip info files parsed in perf_pmu__new_alias()
*/
- len = strlen(name);
- if (len > 5 && !strcmp(name + len - 5, ".unit"))
- continue;
- if (len > 6 && !strcmp(name + len - 6, ".scale"))
+ if (pmu_alias_info_file(name))
continue;
snprintf(path, PATH_MAX, "%s/%s", dir, name);
@@ -387,6 +429,12 @@ static struct cpu_map *pmu_cpumask(const char *name)
return cpus;
}
+struct perf_event_attr *__attribute__((weak))
+perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
+{
+ return NULL;
+}
+
static struct perf_pmu *pmu_lookup(const char *name)
{
struct perf_pmu *pmu;
@@ -421,6 +469,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
pmu->name = strdup(name);
pmu->type = type;
list_add_tail(&pmu->list, &pmus);
+
+ pmu->default_config = perf_pmu__get_default_config(pmu);
+
return pmu;
}
@@ -479,28 +530,24 @@ pmu_find_format(struct list_head *formats, char *name)
}
/*
- * Returns value based on the format definition (format parameter)
+ * Sets value based on the format definition (format parameter)
* and unformated value (value parameter).
- *
- * TODO maybe optimize a little ;)
*/
-static __u64 pmu_format_value(unsigned long *format, __u64 value)
+static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
+ bool zero)
{
unsigned long fbit, vbit;
- __u64 v = 0;
for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
if (!test_bit(fbit, format))
continue;
- if (!(value & (1llu << vbit++)))
- continue;
-
- v |= (1llu << fbit);
+ if (value & (1llu << vbit++))
+ *v |= (1llu << fbit);
+ else if (zero)
+ *v &= ~(1llu << fbit);
}
-
- return v;
}
/*
@@ -509,7 +556,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)
*/
static int pmu_config_term(struct list_head *formats,
struct perf_event_attr *attr,
- struct parse_events_term *term)
+ struct parse_events_term *term,
+ bool zero)
{
struct perf_pmu_format *format;
__u64 *vp;
@@ -548,18 +596,19 @@ static int pmu_config_term(struct list_head *formats,
* non-hardcoded terms, here's the place to translate
* them into value.
*/
- *vp |= pmu_format_value(format->bits, term->val.num);
+ pmu_format_value(format->bits, term->val.num, vp, zero);
return 0;
}
int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr,
- struct list_head *head_terms)
+ struct list_head *head_terms,
+ bool zero)
{
struct parse_events_term *term;
list_for_each_entry(term, head_terms, list)
- if (pmu_config_term(formats, attr, term))
+ if (pmu_config_term(formats, attr, term, zero))
return -EINVAL;
return 0;
@@ -573,8 +622,10 @@ int perf_pmu__config_terms(struct list_head *formats,
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms)
{
+ bool zero = !!pmu->default_config;
+
attr->type = pmu->type;
- return perf_pmu__config_terms(&pmu->format, attr, head_terms);
+ return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero);
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
@@ -608,23 +659,27 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
}
-static int check_unit_scale(struct perf_pmu_alias *alias,
- const char **unit, double *scale)
+static int check_info_data(struct perf_pmu_alias *alias,
+ struct perf_pmu_info *info)
{
/*
* Only one term in event definition can
- * define unit and scale, fail if there's
- * more than one.
+ * define unit, scale and snapshot, fail
+ * if there's more than one.
*/
- if ((*unit && alias->unit) ||
- (*scale && alias->scale))
+ if ((info->unit && alias->unit) ||
+ (info->scale && alias->scale) ||
+ (info->snapshot && alias->snapshot))
return -EINVAL;
if (alias->unit)
- *unit = alias->unit;
+ info->unit = alias->unit;
if (alias->scale)
- *scale = alias->scale;
+ info->scale = alias->scale;
+
+ if (alias->snapshot)
+ info->snapshot = alias->snapshot;
return 0;
}
@@ -634,18 +689,21 @@ static int check_unit_scale(struct perf_pmu_alias *alias,
* defined for the alias
*/
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- const char **unit, double *scale)
+ struct perf_pmu_info *info)
{
struct parse_events_term *term, *h;
struct perf_pmu_alias *alias;
int ret;
+ info->per_pkg = false;
+
/*
* Mark unit and scale as not set
* (different from default values, see below)
*/
- *unit = NULL;
- *scale = 0.0;
+ info->unit = NULL;
+ info->scale = 0.0;
+ info->snapshot = false;
list_for_each_entry_safe(term, h, head_terms, list) {
alias = pmu_find_alias(pmu, term);
@@ -655,10 +713,13 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
if (ret)
return ret;
- ret = check_unit_scale(alias, unit, scale);
+ ret = check_info_data(alias, info);
if (ret)
return ret;
+ if (alias->per_pkg)
+ info->per_pkg = true;
+
list_del(&term->list);
free(term);
}
@@ -668,11 +729,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
* set defaults as for evsel
* unit cannot left to NULL
*/
- if (*unit == NULL)
- *unit = "";
+ if (info->unit == NULL)
+ info->unit = "";
- if (*scale == 0.0)
- *scale = 1.0;
+ if (info->scale == 0.0)
+ info->scale = 1.0;
return 0;
}
@@ -738,15 +799,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
pmu = NULL;
len = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list)
len++;
- aliases = malloc(sizeof(char *) * len);
+ if (pmu->selectable)
+ len++;
+ }
+ aliases = zalloc(sizeof(char *) * len);
if (!aliases)
- return;
+ goto out_enomem;
pmu = NULL;
j = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) {
char *name = format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu");
@@ -756,13 +820,23 @@ void print_pmu_events(const char *event_glob, bool name_only)
(!is_cpu && strglobmatch(alias->name,
event_glob))))
continue;
- aliases[j] = name;
+
if (is_cpu && !name_only)
- aliases[j] = format_alias_or(buf, sizeof(buf),
- pmu, alias);
- aliases[j] = strdup(aliases[j]);
+ name = format_alias_or(buf, sizeof(buf), pmu, alias);
+
+ aliases[j] = strdup(name);
+ if (aliases[j] == NULL)
+ goto out_enomem;
j++;
}
+ if (pmu->selectable) {
+ char *s;
+ if (asprintf(&s, "%s//", pmu->name) < 0)
+ goto out_enomem;
+ aliases[j] = s;
+ j++;
+ }
+ }
len = j;
qsort(aliases, len, sizeof(char *), cmp_string);
for (j = 0; j < len; j++) {
@@ -771,12 +845,20 @@ void print_pmu_events(const char *event_glob, bool name_only)
continue;
}
printf(" %-50s [Kernel PMU event]\n", aliases[j]);
- zfree(&aliases[j]);
printed++;
}
if (printed)
printf("\n");
- free(aliases);
+out_free:
+ for (j = 0; j < len; j++)
+ zfree(&aliases[j]);
+ zfree(&aliases);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print PMU events\n");
+ if (aliases)
+ goto out_free;
}
bool pmu_have_event(const char *pname, const char *name)
@@ -794,3 +876,39 @@ bool pmu_have_event(const char *pname, const char *name)
}
return false;
}
+
+static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs;
+
+ sysfs = sysfs__mountpoint();
+ if (!sysfs)
+ return NULL;
+
+ snprintf(path, PATH_MAX,
+ "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name);
+
+ if (stat(path, &st) < 0)
+ return NULL;
+
+ return fopen(path, "r");
+}
+
+int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
+ ...)
+{
+ va_list args;
+ FILE *file;
+ int ret = EOF;
+
+ va_start(args, fmt);
+ file = perf_pmu__open_file(pmu, name);
+ if (file) {
+ ret = vfscanf(file, fmt, args);
+ fclose(file);
+ }
+ va_end(args);
+ return ret;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index c14a543ce1f3..6b1249fbdb5f 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -13,13 +13,36 @@ enum {
#define PERF_PMU_FORMAT_BITS 64
+struct perf_event_attr;
+
struct perf_pmu {
char *name;
__u32 type;
+ bool selectable;
+ struct perf_event_attr *default_config;
struct cpu_map *cpus;
- struct list_head format;
- struct list_head aliases;
- struct list_head list;
+ struct list_head format; /* HEAD struct perf_pmu_format -> list */
+ struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
+ struct list_head list; /* ELEM */
+};
+
+struct perf_pmu_info {
+ const char *unit;
+ double scale;
+ bool per_pkg;
+ bool snapshot;
+};
+
+#define UNIT_MAX_LEN 31 /* max length for event unit name */
+
+struct perf_pmu_alias {
+ char *name;
+ struct list_head terms; /* HEAD struct parse_events_term -> list */
+ struct list_head list; /* ELEM */
+ char unit[UNIT_MAX_LEN+1];
+ double scale;
+ bool per_pkg;
+ bool snapshot;
};
struct perf_pmu *perf_pmu__find(const char *name);
@@ -27,9 +50,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms);
int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr,
- struct list_head *head_terms);
+ struct list_head *head_terms,
+ bool zero);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- const char **unit, double *scale);
+ struct perf_pmu_info *info);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
struct list_head *head_terms);
int perf_pmu_wrap(void);
@@ -45,5 +69,11 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
void print_pmu_events(const char *event_glob, bool name_only);
bool pmu_have_event(const char *pname, const char *name);
+int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
+ ...) __attribute__((format(scanf, 3, 4)));
+
int perf_pmu__test(void);
+
+struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
+
#endif /* __PMU_H */
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 9a0a1839a377..28eb1417cb2a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -79,7 +79,7 @@ static int init_symbol_maps(bool user_only)
int ret;
symbol_conf.sort_by_name = true;
- ret = symbol__init();
+ ret = symbol__init(NULL);
if (ret < 0) {
pr_debug("Failed to init symbol map.\n");
goto out;
@@ -184,7 +184,8 @@ static struct dso *kernel_get_module_dso(const char *module)
const char *vmlinux_name;
if (module) {
- list_for_each_entry(dso, &host_machine->kernel_dsos, node) {
+ list_for_each_entry(dso, &host_machine->kernel_dsos.head,
+ node) {
if (strncmp(dso->short_name + 1, module,
dso->short_name_len - 2) == 0)
goto found;
@@ -258,21 +259,33 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
#ifdef HAVE_DWARF_SUPPORT
/* Open new debuginfo of given module */
-static struct debuginfo *open_debuginfo(const char *module)
+static struct debuginfo *open_debuginfo(const char *module, bool silent)
{
const char *path = module;
+ struct debuginfo *ret;
if (!module || !strchr(module, '/')) {
path = kernel_get_module_path(module);
if (!path) {
- pr_err("Failed to find path of %s module.\n",
- module ?: "kernel");
+ if (!silent)
+ pr_err("Failed to find path of %s module.\n",
+ module ?: "kernel");
return NULL;
}
}
- return debuginfo__new(path);
+ ret = debuginfo__new(path);
+ if (!ret && !silent) {
+ pr_warning("The %s file has no debug information.\n", path);
+ if (!module || !strtailcmp(path, ".ko"))
+ pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
+ else
+ pr_warning("Rebuild with -g, ");
+ pr_warning("or install an appropriate debuginfo package.\n");
+ }
+ return ret;
}
+
static int get_text_start_address(const char *exec, unsigned long *address)
{
Elf *elf;
@@ -333,15 +346,13 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
pr_debug("try to find information at %" PRIx64 " in %s\n", addr,
tp->module ? : "kernel");
- dinfo = open_debuginfo(tp->module);
+ dinfo = open_debuginfo(tp->module, verbose == 0);
if (dinfo) {
ret = debuginfo__find_probe_point(dinfo,
(unsigned long)addr, pp);
debuginfo__delete(dinfo);
- } else {
- pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr);
+ } else
ret = -ENOENT;
- }
if (ret > 0) {
pp->retprobe = tp->retprobe;
@@ -457,13 +468,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct debuginfo *dinfo;
int ntevs, ret = 0;
- dinfo = open_debuginfo(target);
+ dinfo = open_debuginfo(target, !need_dwarf);
if (!dinfo) {
- if (need_dwarf) {
- pr_warning("Failed to open debuginfo file.\n");
+ if (need_dwarf)
return -ENOENT;
- }
pr_debug("Could not open debuginfo. Try to use symbols.\n");
return 0;
}
@@ -565,7 +574,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)
{
- char buf[LINEBUF_SIZE];
+ char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE];
const char *color = show_num ? "" : PERF_COLOR_BLUE;
const char *prefix = NULL;
@@ -585,7 +594,8 @@ static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)
return 1;
error:
if (ferror(fp)) {
- pr_warning("File read error: %s\n", strerror(errno));
+ pr_warning("File read error: %s\n",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -1;
}
return 0;
@@ -618,13 +628,12 @@ static int __show_line_range(struct line_range *lr, const char *module)
FILE *fp;
int ret;
char *tmp;
+ char sbuf[STRERR_BUFSIZE];
/* Search a line range */
- dinfo = open_debuginfo(module);
- if (!dinfo) {
- pr_warning("Failed to open debuginfo file.\n");
+ dinfo = open_debuginfo(module, false);
+ if (!dinfo)
return -ENOENT;
- }
ret = debuginfo__find_line_range(dinfo, lr);
debuginfo__delete(dinfo);
@@ -656,7 +665,7 @@ static int __show_line_range(struct line_range *lr, const char *module)
fp = fopen(lr->path, "r");
if (fp == NULL) {
pr_warning("Failed to open %s: %s\n", lr->path,
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -errno;
}
/* Skip to starting line number */
@@ -689,11 +698,11 @@ end:
return ret;
}
-int show_line_range(struct line_range *lr, const char *module)
+int show_line_range(struct line_range *lr, const char *module, bool user)
{
int ret;
- ret = init_symbol_maps(false);
+ ret = init_symbol_maps(user);
if (ret < 0)
return ret;
ret = __show_line_range(lr, module);
@@ -768,13 +777,12 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
int i, ret = 0;
struct debuginfo *dinfo;
- ret = init_symbol_maps(false);
+ ret = init_symbol_maps(pevs->uprobes);
if (ret < 0)
return ret;
- dinfo = open_debuginfo(module);
+ dinfo = open_debuginfo(module, false);
if (!dinfo) {
- pr_warning("Failed to open debuginfo file.\n");
ret = -ENOENT;
goto out;
}
@@ -815,7 +823,8 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
}
int show_line_range(struct line_range *lr __maybe_unused,
- const char *module __maybe_unused)
+ const char *module __maybe_unused,
+ bool user __maybe_unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
@@ -1405,8 +1414,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
return tmp - buf;
error:
- pr_debug("Failed to synthesize perf probe argument: %s\n",
- strerror(-ret));
+ pr_debug("Failed to synthesize perf probe argument: %d\n", ret);
return ret;
}
@@ -1455,8 +1463,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
return buf;
error:
- pr_debug("Failed to synthesize perf probe point: %s\n",
- strerror(-ret));
+ pr_debug("Failed to synthesize perf probe point: %d\n", ret);
free(buf);
return NULL;
}
@@ -1780,10 +1787,11 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
memset(tev, 0, sizeof(*tev));
}
-static void print_warn_msg(const char *file, bool is_kprobe)
+static void print_open_warning(int err, bool is_kprobe)
{
+ char sbuf[STRERR_BUFSIZE];
- if (errno == ENOENT) {
+ if (err == -ENOENT) {
const char *config;
if (!is_kprobe)
@@ -1791,25 +1799,43 @@ static void print_warn_msg(const char *file, bool is_kprobe)
else
config = "CONFIG_KPROBE_EVENTS";
- pr_warning("%s file does not exist - please rebuild kernel"
- " with %s.\n", file, config);
- } else
- pr_warning("Failed to open %s file: %s\n", file,
- strerror(errno));
+ pr_warning("%cprobe_events file does not exist"
+ " - please rebuild kernel with %s.\n",
+ is_kprobe ? 'k' : 'u', config);
+ } else if (err == -ENOTSUP)
+ pr_warning("Debugfs is not mounted.\n");
+ else
+ pr_warning("Failed to open %cprobe_events: %s\n",
+ is_kprobe ? 'k' : 'u',
+ strerror_r(-err, sbuf, sizeof(sbuf)));
+}
+
+static void print_both_open_warning(int kerr, int uerr)
+{
+ /* Both kprobes and uprobes are disabled, warn it. */
+ if (kerr == -ENOTSUP && uerr == -ENOTSUP)
+ pr_warning("Debugfs is not mounted.\n");
+ else if (kerr == -ENOENT && uerr == -ENOENT)
+ pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
+ "or/and CONFIG_UPROBE_EVENTS.\n");
+ else {
+ char sbuf[STRERR_BUFSIZE];
+ pr_warning("Failed to open kprobe events: %s.\n",
+ strerror_r(-kerr, sbuf, sizeof(sbuf)));
+ pr_warning("Failed to open uprobe events: %s.\n",
+ strerror_r(-uerr, sbuf, sizeof(sbuf)));
+ }
}
-static int open_probe_events(const char *trace_file, bool readwrite,
- bool is_kprobe)
+static int open_probe_events(const char *trace_file, bool readwrite)
{
char buf[PATH_MAX];
const char *__debugfs;
int ret;
__debugfs = debugfs_find_mountpoint();
- if (__debugfs == NULL) {
- pr_warning("Debugfs is not mounted.\n");
- return -ENOENT;
- }
+ if (__debugfs == NULL)
+ return -ENOTSUP;
ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file);
if (ret >= 0) {
@@ -1820,19 +1846,19 @@ static int open_probe_events(const char *trace_file, bool readwrite,
ret = open(buf, O_RDONLY, 0);
if (ret < 0)
- print_warn_msg(buf, is_kprobe);
+ ret = -errno;
}
return ret;
}
static int open_kprobe_events(bool readwrite)
{
- return open_probe_events("tracing/kprobe_events", readwrite, true);
+ return open_probe_events("tracing/kprobe_events", readwrite);
}
static int open_uprobe_events(bool readwrite)
{
- return open_probe_events("tracing/uprobe_events", readwrite, false);
+ return open_probe_events("tracing/uprobe_events", readwrite);
}
/* Get raw string list of current kprobe_events or uprobe_events */
@@ -1857,7 +1883,7 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)
p[idx] = '\0';
ret = strlist__add(sl, buf);
if (ret < 0) {
- pr_debug("strlist__add failed: %s\n", strerror(-ret));
+ pr_debug("strlist__add failed (%d)\n", ret);
strlist__delete(sl);
return NULL;
}
@@ -1884,21 +1910,21 @@ static int show_perf_probe_event(struct perf_probe_event *pev,
if (ret < 0)
return ret;
- printf(" %-20s (on %s", buf, place);
+ pr_info(" %-20s (on %s", buf, place);
if (module)
- printf(" in %s", module);
+ pr_info(" in %s", module);
if (pev->nargs > 0) {
- printf(" with");
+ pr_info(" with");
for (i = 0; i < pev->nargs; i++) {
ret = synthesize_perf_probe_arg(&pev->args[i],
buf, 128);
if (ret < 0)
break;
- printf(" %s", buf);
+ pr_info(" %s", buf);
}
}
- printf(")\n");
+ pr_info(")\n");
free(place);
return ret;
}
@@ -1916,7 +1942,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)
rawlist = get_probe_trace_command_rawlist(fd);
if (!rawlist)
- return -ENOENT;
+ return -ENOMEM;
strlist__for_each(ent, rawlist) {
ret = parse_probe_trace_command(ent->s, &tev);
@@ -1940,27 +1966,34 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)
/* List up current perf-probe events */
int show_perf_probe_events(void)
{
- int fd, ret;
+ int kp_fd, up_fd, ret;
setup_pager();
- fd = open_kprobe_events(false);
-
- if (fd < 0)
- return fd;
ret = init_symbol_maps(false);
if (ret < 0)
return ret;
- ret = __show_perf_probe_events(fd, true);
- close(fd);
+ kp_fd = open_kprobe_events(false);
+ if (kp_fd >= 0) {
+ ret = __show_perf_probe_events(kp_fd, true);
+ close(kp_fd);
+ if (ret < 0)
+ goto out;
+ }
- fd = open_uprobe_events(false);
- if (fd >= 0) {
- ret = __show_perf_probe_events(fd, false);
- close(fd);
+ up_fd = open_uprobe_events(false);
+ if (kp_fd < 0 && up_fd < 0) {
+ print_both_open_warning(kp_fd, up_fd);
+ ret = kp_fd;
+ goto out;
}
+ if (up_fd >= 0) {
+ ret = __show_perf_probe_events(up_fd, false);
+ close(up_fd);
+ }
+out:
exit_symbol_maps();
return ret;
}
@@ -1976,6 +2009,8 @@ static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
memset(&tev, 0, sizeof(tev));
rawlist = get_probe_trace_command_rawlist(fd);
+ if (!rawlist)
+ return NULL;
sl = strlist__new(true, NULL);
strlist__for_each(ent, rawlist) {
ret = parse_probe_trace_command(ent->s, &tev);
@@ -2005,6 +2040,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
{
int ret = 0;
char *buf = synthesize_probe_trace_command(tev);
+ char sbuf[STRERR_BUFSIZE];
if (!buf) {
pr_debug("Failed to synthesize probe trace event.\n");
@@ -2016,7 +2052,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
ret = write(fd, buf, strlen(buf));
if (ret <= 0)
pr_warning("Failed to write event: %s\n",
- strerror(errno));
+ strerror_r(errno, sbuf, sizeof(sbuf)));
}
free(buf);
return ret;
@@ -2030,7 +2066,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
/* Try no suffix */
ret = e_snprintf(buf, len, "%s", base);
if (ret < 0) {
- pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ pr_debug("snprintf() failed: %d\n", ret);
return ret;
}
if (!strlist__has_entry(namelist, buf))
@@ -2046,7 +2082,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
for (i = 1; i < MAX_EVENT_INDEX; i++) {
ret = e_snprintf(buf, len, "%s_%d", base, i);
if (ret < 0) {
- pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ pr_debug("snprintf() failed: %d\n", ret);
return ret;
}
if (!strlist__has_entry(namelist, buf))
@@ -2075,8 +2111,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
else
fd = open_kprobe_events(true);
- if (fd < 0)
+ if (fd < 0) {
+ print_open_warning(fd, !pev->uprobes);
return fd;
+ }
+
/* Get current event names */
namelist = get_probe_trace_event_names(fd, false);
if (!namelist) {
@@ -2085,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
}
ret = 0;
- printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
+ pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
if (pev->event)
@@ -2140,8 +2179,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (ret >= 0) {
/* Show how to use the event. */
- printf("\nYou can now use it in all perf tools, such as:\n\n");
- printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
+ pr_info("\nYou can now use it in all perf tools, such as:\n\n");
+ pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event);
}
@@ -2405,10 +2444,11 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
goto error;
}
- printf("Removed event: %s\n", ent->s);
+ pr_info("Removed event: %s\n", ent->s);
return 0;
error:
- pr_warning("Failed to delete event: %s\n", strerror(-ret));
+ pr_warning("Failed to delete event: %s\n",
+ strerror_r(-ret, buf, sizeof(buf)));
return ret;
}
@@ -2449,15 +2489,18 @@ int del_perf_probe_events(struct strlist *dellist)
/* Get current event names */
kfd = open_kprobe_events(true);
- if (kfd < 0)
- return kfd;
+ if (kfd >= 0)
+ namelist = get_probe_trace_event_names(kfd, true);
- namelist = get_probe_trace_event_names(kfd, true);
ufd = open_uprobe_events(true);
-
if (ufd >= 0)
unamelist = get_probe_trace_event_names(ufd, true);
+ if (kfd < 0 && ufd < 0) {
+ print_both_open_warning(kfd, ufd);
+ goto error;
+ }
+
if (namelist == NULL && unamelist == NULL)
goto error;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 776c9347a3b6..e01e9943139f 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -128,7 +128,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
bool force_add);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
-extern int show_line_range(struct line_range *lr, const char *module);
+extern int show_line_range(struct line_range *lr, const char *module,
+ bool user);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
int max_probe_points, const char *module,
struct strfilter *filter, bool externs);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index dca9145d704c..c7918f83b300 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -281,6 +281,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
Dwarf_Die type;
char buf[16];
+ char sbuf[STRERR_BUFSIZE];
int bsize, boffs, total;
int ret;
@@ -367,7 +368,7 @@ formatted:
if (ret >= 16)
ret = -E2BIG;
pr_warning("Failed to convert variable type: %s\n",
- strerror(-ret));
+ strerror_r(-ret, sbuf, sizeof(sbuf)));
return ret;
}
tvar->type = strdup(buf);
@@ -608,14 +609,18 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
return -EINVAL;
}
- /* Get an appropriate symbol from symtab */
- symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
+ symbol = dwarf_diename(sp_die);
if (!symbol) {
- pr_warning("Failed to find symbol at 0x%lx\n",
- (unsigned long)paddr);
- return -ENOENT;
+ /* Try to get the symbol name from symtab */
+ symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
+ if (!symbol) {
+ pr_warning("Failed to find symbol at 0x%lx\n",
+ (unsigned long)paddr);
+ return -ENOENT;
+ }
+ eaddr = sym.st_value;
}
- tp->offset = (unsigned long)(paddr - sym.st_value);
+ tp->offset = (unsigned long)(paddr - eaddr);
tp->address = (unsigned long)paddr;
tp->symbol = strdup(symbol);
if (!tp->symbol)
@@ -779,10 +784,12 @@ static int find_lazy_match_lines(struct intlist *list,
size_t line_len;
ssize_t len;
int count = 0, linenum = 1;
+ char sbuf[STRERR_BUFSIZE];
fp = fopen(fname, "r");
if (!fp) {
- pr_warning("Failed to open %s: %s\n", fname, strerror(errno));
+ pr_warning("Failed to open %s: %s\n", fname,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -errno;
}
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 12aa9b0d0ba1..3dda85ca50c1 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -736,7 +736,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist,
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout))
return NULL;
- n = poll(evlist->pollfd, evlist->nr_fds, timeout);
+ n = perf_evlist__poll(evlist, timeout);
if (n < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
@@ -753,9 +753,9 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist,
PyObject *list = PyList_New(0);
int i;
- for (i = 0; i < evlist->nr_fds; ++i) {
+ for (i = 0; i < evlist->pollfd.nr; ++i) {
PyObject *file;
- FILE *fp = fdopen(evlist->pollfd[i].fd, "r");
+ FILE *fp = fdopen(evlist->pollfd.entries[i].fd, "r");
if (fp == NULL)
goto free_list;
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index fe8079edbdc1..cf69325b985f 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -14,6 +14,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
struct perf_evsel *evsel;
unsigned long flags = perf_event_open_cloexec_flag();
int err = -EAGAIN, fd;
+ static pid_t pid = -1;
evlist = perf_evlist__new();
if (!evlist)
@@ -24,14 +25,22 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
evsel = perf_evlist__first(evlist);
- fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags);
- if (fd < 0)
- goto out_delete;
+ while (1) {
+ fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
+ if (fd < 0) {
+ if (pid == -1 && errno == EACCES) {
+ pid = 0;
+ continue;
+ }
+ goto out_delete;
+ }
+ break;
+ }
close(fd);
fn(evsel);
- fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags);
+ fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
if (fd < 0) {
if (errno == EINVAL)
err = -EINVAL;
@@ -47,7 +56,7 @@ out_delete:
static bool perf_probe_api(setup_probe_fn_t fn)
{
- const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL};
+ const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
struct cpu_map *cpus;
int cpu, ret, i = 0;
@@ -106,7 +115,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)
evlist__for_each(evlist, evsel) {
perf_evsel__config(evsel, opts);
- if (!evsel->idx && use_comm_exec)
+ if (evsel->tracking && use_comm_exec)
evsel->attr.comm_exec = 1;
}
@@ -201,6 +210,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
struct perf_evsel *evsel;
int err, fd, cpu;
bool ret = false;
+ pid_t pid = -1;
temp_evlist = perf_evlist__new();
if (!temp_evlist)
@@ -221,12 +231,20 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
cpu = evlist->cpus->map[0];
}
- fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1,
- perf_event_open_cloexec_flag());
- if (fd >= 0) {
- close(fd);
- ret = true;
+ while (1) {
+ fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1,
+ perf_event_open_cloexec_flag());
+ if (fd < 0) {
+ if (pid == -1 && errno == EACCES) {
+ pid = 0;
+ continue;
+ }
+ goto out_delete;
+ }
+ break;
}
+ close(fd);
+ ret = true;
out_delete:
perf_evlist__delete(temp_evlist);
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c
index da8e9b285f51..34622b53e733 100644
--- a/tools/perf/util/run-command.c
+++ b/tools/perf/util/run-command.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "run-command.h"
#include "exec_cmd.h"
+#include "debug.h"
static inline void close_pair(int fd[2])
{
@@ -19,6 +20,7 @@ int start_command(struct child_process *cmd)
{
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
+ char sbuf[STRERR_BUFSIZE];
/*
* In case of errors we must keep the promise to close FDs
@@ -99,7 +101,7 @@ int start_command(struct child_process *cmd)
if (cmd->dir && chdir(cmd->dir))
die("exec %s: cd to %s failed (%s)", cmd->argv[0],
- cmd->dir, strerror(errno));
+ cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf)));
if (cmd->env) {
for (; *cmd->env; cmd->env++) {
if (strchr(*cmd->env, '='))
@@ -153,6 +155,8 @@ int start_command(struct child_process *cmd)
static int wait_or_whine(pid_t pid)
{
+ char sbuf[STRERR_BUFSIZE];
+
for (;;) {
int status, code;
pid_t waiting = waitpid(pid, &status, 0);
@@ -160,7 +164,8 @@ static int wait_or_whine(pid_t pid)
if (waiting < 0) {
if (errno == EINTR)
continue;
- error("waitpid failed (%s)", strerror(errno));
+ error("waitpid failed (%s)",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -ERR_RUN_COMMAND_WAITPID;
}
if (waiting != pid)
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index b2dba9c0a3a1..22ebc46226e7 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <ctype.h>
#include <errno.h>
+#include <linux/bitmap.h>
#include "../util.h"
#include <EXTERN.h>
@@ -57,7 +58,7 @@ INTERP my_perl;
#define FTRACE_MAX_EVENT \
((1 << (sizeof(unsigned short) * 8)) - 1)
-struct event_format *events[FTRACE_MAX_EVENT];
+static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);
extern struct scripting_context *scripting_context;
@@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next);
}
-static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
-{
- static char ev_name[256];
- struct event_format *event;
- int type = evsel->attr.config;
-
- if (events[type])
- return events[type];
-
- events[type] = event = evsel->tp_format;
- if (!event)
- return NULL;
-
- sprintf(ev_name, "%s::%s", event->system, event->name);
-
- define_event_symbols(event, ev_name, event->print_fmt.args);
-
- return event;
-}
-
static void perl_process_tracepoint(struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread)
{
+ struct event_format *event = evsel->tp_format;
struct format_field *field;
static char handler[256];
unsigned long long val;
unsigned long s, ns;
- struct event_format *event;
int pid;
int cpu = sample->cpu;
void *data = sample->raw_data;
@@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample,
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return;
- event = find_cache_event(evsel);
if (!event)
die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
@@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample,
sprintf(handler, "%s::%s", event->system, event->name);
+ if (!test_and_set_bit(event->id, events_defined))
+ define_event_symbols(event, handler, event->print_fmt.args);
+
s = nsecs / NSECS_PER_SEC;
ns = nsecs - s * NSECS_PER_SEC;
@@ -432,6 +415,11 @@ error:
return err;
}
+static int perl_flush_script(void)
+{
+ return 0;
+}
+
/*
* Stop trace script
*/
@@ -633,6 +621,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops perl_scripting_ops = {
.name = "Perl",
.start_script = perl_start_script,
+ .flush_script = perl_flush_script,
.stop_script = perl_stop_script,
.process_event = perl_process_event,
.generate_script = perl_generate_script,
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index cbce2545da45..d808a328f4dc 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,14 +24,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include <errno.h>
+#include <linux/bitmap.h>
#include "../../perf.h"
#include "../debug.h"
+#include "../callchain.h"
#include "../evsel.h"
#include "../util.h"
#include "../event.h"
#include "../thread.h"
+#include "../comm.h"
+#include "../machine.h"
+#include "../db-export.h"
+#include "../thread-stack.h"
#include "../trace-event.h"
#include "../machine.h"
@@ -40,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void);
#define FTRACE_MAX_EVENT \
((1 << (sizeof(unsigned short) * 8)) - 1)
-struct event_format *events[FTRACE_MAX_EVENT];
+static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);
#define MAX_FIELDS 64
#define N_COMMON_FIELDS 7
@@ -52,6 +59,24 @@ static int zero_flag_atom;
static PyObject *main_module, *main_dict;
+struct tables {
+ struct db_export dbe;
+ PyObject *evsel_handler;
+ PyObject *machine_handler;
+ PyObject *thread_handler;
+ PyObject *comm_handler;
+ PyObject *comm_thread_handler;
+ PyObject *dso_handler;
+ PyObject *symbol_handler;
+ PyObject *branch_type_handler;
+ PyObject *sample_handler;
+ PyObject *call_path_handler;
+ PyObject *call_return_handler;
+ bool db_export_mode;
+};
+
+static struct tables tables_global;
+
static void handler_call_die(const char *handler_name) NORETURN;
static void handler_call_die(const char *handler_name)
{
@@ -73,6 +98,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj
Py_DECREF(val);
}
+static PyObject *get_handler(const char *handler_name)
+{
+ PyObject *handler;
+
+ handler = PyDict_GetItemString(main_dict, handler_name);
+ if (handler && !PyCallable_Check(handler))
+ return NULL;
+ return handler;
+}
+
+static void call_object(PyObject *handler, PyObject *args, const char *die_msg)
+{
+ PyObject *retval;
+
+ retval = PyObject_CallObject(handler, args);
+ if (retval == NULL)
+ handler_call_die(die_msg);
+ Py_DECREF(retval);
+}
+
+static void try_call_object(const char *handler_name, PyObject *args)
+{
+ PyObject *handler;
+
+ handler = get_handler(handler_name);
+ if (handler)
+ call_object(handler, args, handler_name);
+}
+
static void define_value(enum print_arg_type field_type,
const char *ev_name,
const char *field_name,
@@ -80,7 +134,7 @@ static void define_value(enum print_arg_type field_type,
const char *field_str)
{
const char *handler_name = "define_flag_value";
- PyObject *handler, *t, *retval;
+ PyObject *t;
unsigned long long value;
unsigned n = 0;
@@ -98,13 +152,7 @@ static void define_value(enum print_arg_type field_type,
PyTuple_SetItem(t, n++, PyInt_FromLong(value));
PyTuple_SetItem(t, n++, PyString_FromString(field_str));
- handler = PyDict_GetItemString(main_dict, handler_name);
- if (handler && PyCallable_Check(handler)) {
- retval = PyObject_CallObject(handler, t);
- if (retval == NULL)
- handler_call_die(handler_name);
- Py_DECREF(retval);
- }
+ try_call_object(handler_name, t);
Py_DECREF(t);
}
@@ -127,7 +175,7 @@ static void define_field(enum print_arg_type field_type,
const char *delim)
{
const char *handler_name = "define_flag_field";
- PyObject *handler, *t, *retval;
+ PyObject *t;
unsigned n = 0;
if (field_type == PRINT_SYMBOL)
@@ -145,13 +193,7 @@ static void define_field(enum print_arg_type field_type,
if (field_type == PRINT_FLAGS)
PyTuple_SetItem(t, n++, PyString_FromString(delim));
- handler = PyDict_GetItemString(main_dict, handler_name);
- if (handler && PyCallable_Check(handler)) {
- retval = PyObject_CallObject(handler, t);
- if (retval == NULL)
- handler_call_die(handler_name);
- Py_DECREF(retval);
- }
+ try_call_object(handler_name, t);
Py_DECREF(t);
}
@@ -214,31 +256,6 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next);
}
-static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
-{
- static char ev_name[256];
- struct event_format *event;
- int type = evsel->attr.config;
-
- /*
- * XXX: Do we really need to cache this since now we have evsel->tp_format
- * cached already? Need to re-read this "cache" routine that as well calls
- * define_event_symbols() :-\
- */
- if (events[type])
- return events[type];
-
- events[type] = event = evsel->tp_format;
- if (!event)
- return NULL;
-
- sprintf(ev_name, "%s__%s", event->system, event->name);
-
- define_event_symbols(event, ev_name, event->print_fmt.args);
-
- return event;
-}
-
static PyObject *get_field_numeric_entry(struct event_format *event,
struct format_field *field, void *data)
{
@@ -294,9 +311,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
if (!symbol_conf.use_callchain || !sample->callchain)
goto exit;
- if (machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, NULL, NULL,
- PERF_MAX_STACK_DEPTH) != 0) {
+ if (thread__resolve_callchain(al->thread, evsel,
+ sample, NULL, NULL,
+ PERF_MAX_STACK_DEPTH) != 0) {
pr_err("Failed to resolve callchain. Skipping\n");
goto exit;
}
@@ -362,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample,
struct thread *thread,
struct addr_location *al)
{
- PyObject *handler, *retval, *context, *t, *obj, *callchain;
+ struct event_format *event = evsel->tp_format;
+ PyObject *handler, *context, *t, *obj, *callchain;
PyObject *dict = NULL;
static char handler_name[256];
struct format_field *field;
unsigned long s, ns;
- struct event_format *event;
unsigned n = 0;
int pid;
int cpu = sample->cpu;
@@ -379,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample,
if (!t)
Py_FatalError("couldn't create Python tuple");
- event = find_cache_event(evsel);
if (!event)
die("ug! no event found for type %d", (int)evsel->attr.config);
@@ -387,9 +403,10 @@ static void python_process_tracepoint(struct perf_sample *sample,
sprintf(handler_name, "%s__%s", event->system, event->name);
- handler = PyDict_GetItemString(main_dict, handler_name);
- if (handler && !PyCallable_Check(handler))
- handler = NULL;
+ if (!test_and_set_bit(event->id, events_defined))
+ define_event_symbols(event, handler_name, event->print_fmt.args);
+
+ handler = get_handler(handler_name);
if (!handler) {
dict = PyDict_New();
if (!dict)
@@ -450,31 +467,304 @@ static void python_process_tracepoint(struct perf_sample *sample,
Py_FatalError("error resizing Python tuple");
if (handler) {
- retval = PyObject_CallObject(handler, t);
- if (retval == NULL)
- handler_call_die(handler_name);
- Py_DECREF(retval);
+ call_object(handler, t, handler_name);
} else {
- handler = PyDict_GetItemString(main_dict, "trace_unhandled");
- if (handler && PyCallable_Check(handler)) {
-
- retval = PyObject_CallObject(handler, t);
- if (retval == NULL)
- handler_call_die("trace_unhandled");
- Py_DECREF(retval);
- }
+ try_call_object("trace_unhandled", t);
Py_DECREF(dict);
}
Py_DECREF(t);
}
+static PyObject *tuple_new(unsigned int sz)
+{
+ PyObject *t;
+
+ t = PyTuple_New(sz);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+ return t;
+}
+
+static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val)
+{
+#if BITS_PER_LONG == 64
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+#endif
+#if BITS_PER_LONG == 32
+ return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val));
+#endif
+}
+
+static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val)
+{
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+}
+
+static int tuple_set_string(PyObject *t, unsigned int pos, const char *s)
+{
+ return PyTuple_SetItem(t, pos, PyString_FromString(s));
+}
+
+static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, evsel->db_id);
+ tuple_set_string(t, 1, perf_evsel__name(evsel));
+
+ call_object(tables->evsel_handler, t, "evsel_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_machine(struct db_export *dbe,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, machine->db_id);
+ tuple_set_s32(t, 1, machine->pid);
+ tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : "");
+
+ call_object(tables->machine_handler, t, "machine_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_thread(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, thread->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_u64(t, 2, main_thread_db_id);
+ tuple_set_s32(t, 3, thread->pid_);
+ tuple_set_s32(t, 4, thread->tid);
+
+ call_object(tables->thread_handler, t, "thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm(struct db_export *dbe, struct comm *comm)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, comm->db_id);
+ tuple_set_string(t, 1, comm__str(comm));
+
+ call_object(tables->comm_handler, t, "comm_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm_thread(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, db_id);
+ tuple_set_u64(t, 1, comm->db_id);
+ tuple_set_u64(t, 2, thread->db_id);
+
+ call_object(tables->comm_thread_handler, t, "comm_thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+ PyObject *t;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, dso->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_string(t, 2, dso->short_name);
+ tuple_set_string(t, 3, dso->long_name);
+ tuple_set_string(t, 4, sbuild_id);
+
+ call_object(tables->dso_handler, t, "dso_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ u64 *sym_db_id = symbol__priv(sym);
+ PyObject *t;
+
+ t = tuple_new(6);
+
+ tuple_set_u64(t, 0, *sym_db_id);
+ tuple_set_u64(t, 1, dso->db_id);
+ tuple_set_u64(t, 2, sym->start);
+ tuple_set_u64(t, 3, sym->end);
+ tuple_set_s32(t, 4, sym->binding);
+ tuple_set_string(t, 5, sym->name);
+
+ call_object(tables->symbol_handler, t, "symbol_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_s32(t, 0, branch_type);
+ tuple_set_string(t, 1, name);
+
+ call_object(tables->branch_type_handler, t, "branch_type_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_sample(struct db_export *dbe,
+ struct export_sample *es)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(21);
+
+ tuple_set_u64(t, 0, es->db_id);
+ tuple_set_u64(t, 1, es->evsel->db_id);
+ tuple_set_u64(t, 2, es->al->machine->db_id);
+ tuple_set_u64(t, 3, es->thread->db_id);
+ tuple_set_u64(t, 4, es->comm_db_id);
+ tuple_set_u64(t, 5, es->dso_db_id);
+ tuple_set_u64(t, 6, es->sym_db_id);
+ tuple_set_u64(t, 7, es->offset);
+ tuple_set_u64(t, 8, es->sample->ip);
+ tuple_set_u64(t, 9, es->sample->time);
+ tuple_set_s32(t, 10, es->sample->cpu);
+ tuple_set_u64(t, 11, es->addr_dso_db_id);
+ tuple_set_u64(t, 12, es->addr_sym_db_id);
+ tuple_set_u64(t, 13, es->addr_offset);
+ tuple_set_u64(t, 14, es->sample->addr);
+ tuple_set_u64(t, 15, es->sample->period);
+ tuple_set_u64(t, 16, es->sample->weight);
+ tuple_set_u64(t, 17, es->sample->transaction);
+ tuple_set_u64(t, 18, es->sample->data_src);
+ tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);
+ tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX));
+
+ call_object(tables->sample_handler, t, "sample_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_call_path(struct db_export *dbe, struct call_path *cp)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+ u64 parent_db_id, sym_db_id;
+
+ parent_db_id = cp->parent ? cp->parent->db_id : 0;
+ sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0;
+
+ t = tuple_new(4);
+
+ tuple_set_u64(t, 0, cp->db_id);
+ tuple_set_u64(t, 1, parent_db_id);
+ tuple_set_u64(t, 2, sym_db_id);
+ tuple_set_u64(t, 3, cp->ip);
+
+ call_object(tables->call_path_handler, t, "call_path_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_call_return(struct db_export *dbe,
+ struct call_return *cr)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
+ PyObject *t;
+
+ t = tuple_new(11);
+
+ tuple_set_u64(t, 0, cr->db_id);
+ tuple_set_u64(t, 1, cr->thread->db_id);
+ tuple_set_u64(t, 2, comm_db_id);
+ tuple_set_u64(t, 3, cr->cp->db_id);
+ tuple_set_u64(t, 4, cr->call_time);
+ tuple_set_u64(t, 5, cr->return_time);
+ tuple_set_u64(t, 6, cr->branch_count);
+ tuple_set_u64(t, 7, cr->call_ref);
+ tuple_set_u64(t, 8, cr->return_ref);
+ tuple_set_u64(t, 9, cr->cp->parent->db_id);
+ tuple_set_s32(t, 10, cr->flags);
+
+ call_object(tables->call_return_handler, t, "call_return_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_process_call_return(struct call_return *cr, void *data)
+{
+ struct db_export *dbe = data;
+
+ return db_export__call_return(dbe, cr);
+}
+
static void python_process_general_event(struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
struct addr_location *al)
{
- PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample;
+ PyObject *handler, *t, *dict, *callchain, *dict_sample;
static char handler_name[64];
unsigned n = 0;
@@ -496,8 +786,8 @@ static void python_process_general_event(struct perf_sample *sample,
snprintf(handler_name, sizeof(handler_name), "%s", "process_event");
- handler = PyDict_GetItemString(main_dict, handler_name);
- if (!handler || !PyCallable_Check(handler))
+ handler = get_handler(handler_name);
+ if (!handler)
goto exit;
pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
@@ -539,63 +829,121 @@ static void python_process_general_event(struct perf_sample *sample,
if (_PyTuple_Resize(&t, n) == -1)
Py_FatalError("error resizing Python tuple");
- retval = PyObject_CallObject(handler, t);
- if (retval == NULL)
- handler_call_die(handler_name);
- Py_DECREF(retval);
+ call_object(handler, t, handler_name);
exit:
Py_DECREF(dict);
Py_DECREF(t);
}
-static void python_process_event(union perf_event *event __maybe_unused,
+static void python_process_event(union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
struct addr_location *al)
{
+ struct tables *tables = &tables_global;
+
switch (evsel->attr.type) {
case PERF_TYPE_TRACEPOINT:
python_process_tracepoint(sample, evsel, thread, al);
break;
/* Reserve for future process_hw/sw/raw APIs */
default:
- python_process_general_event(sample, evsel, thread, al);
+ if (tables->db_export_mode)
+ db_export__sample(&tables->dbe, event, sample, evsel,
+ thread, al);
+ else
+ python_process_general_event(sample, evsel, thread, al);
}
}
static int run_start_sub(void)
{
- PyObject *handler, *retval;
- int err = 0;
-
main_module = PyImport_AddModule("__main__");
if (main_module == NULL)
return -1;
Py_INCREF(main_module);
main_dict = PyModule_GetDict(main_module);
- if (main_dict == NULL) {
- err = -1;
+ if (main_dict == NULL)
goto error;
- }
Py_INCREF(main_dict);
- handler = PyDict_GetItemString(main_dict, "trace_begin");
- if (handler == NULL || !PyCallable_Check(handler))
- goto out;
+ try_call_object("trace_begin", NULL);
- retval = PyObject_CallObject(handler, NULL);
- if (retval == NULL)
- handler_call_die("trace_begin");
+ return 0;
- Py_DECREF(retval);
- return err;
error:
Py_XDECREF(main_dict);
Py_XDECREF(main_module);
-out:
- return err;
+ return -1;
+}
+
+#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \
+ tables->handler_name = get_handler(#table_name); \
+ if (tables->handler_name) \
+ tables->dbe.export_ ## name = python_export_ ## name; \
+} while (0)
+
+#define SET_TABLE_HANDLER(name) \
+ SET_TABLE_HANDLER_(name, name ## _handler, name ## _table)
+
+static void set_table_handlers(struct tables *tables)
+{
+ const char *perf_db_export_mode = "perf_db_export_mode";
+ const char *perf_db_export_calls = "perf_db_export_calls";
+ PyObject *db_export_mode, *db_export_calls;
+ bool export_calls = false;
+ int ret;
+
+ memset(tables, 0, sizeof(struct tables));
+ if (db_export__init(&tables->dbe))
+ Py_FatalError("failed to initialize export");
+
+ db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode);
+ if (!db_export_mode)
+ return;
+
+ ret = PyObject_IsTrue(db_export_mode);
+ if (ret == -1)
+ handler_call_die(perf_db_export_mode);
+ if (!ret)
+ return;
+
+ tables->dbe.crp = NULL;
+ db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls);
+ if (db_export_calls) {
+ ret = PyObject_IsTrue(db_export_calls);
+ if (ret == -1)
+ handler_call_die(perf_db_export_calls);
+ export_calls = !!ret;
+ }
+
+ if (export_calls) {
+ tables->dbe.crp =
+ call_return_processor__new(python_process_call_return,
+ &tables->dbe);
+ if (!tables->dbe.crp)
+ Py_FatalError("failed to create calls processor");
+ }
+
+ tables->db_export_mode = true;
+ /*
+ * Reserve per symbol space for symbol->db_id via symbol__priv()
+ */
+ symbol_conf.priv_size = sizeof(u64);
+
+ SET_TABLE_HANDLER(evsel);
+ SET_TABLE_HANDLER(machine);
+ SET_TABLE_HANDLER(thread);
+ SET_TABLE_HANDLER(comm);
+ SET_TABLE_HANDLER(comm_thread);
+ SET_TABLE_HANDLER(dso);
+ SET_TABLE_HANDLER(symbol);
+ SET_TABLE_HANDLER(branch_type);
+ SET_TABLE_HANDLER(sample);
+ SET_TABLE_HANDLER(call_path);
+ SET_TABLE_HANDLER(call_return);
}
/*
@@ -603,6 +951,7 @@ out:
*/
static int python_start_script(const char *script, int argc, const char **argv)
{
+ struct tables *tables = &tables_global;
const char **command_line;
char buf[PATH_MAX];
int i, err = 0;
@@ -641,6 +990,14 @@ static int python_start_script(const char *script, int argc, const char **argv)
free(command_line);
+ set_table_handlers(tables);
+
+ if (tables->db_export_mode) {
+ err = db_export__branch_types(&tables->dbe);
+ if (err)
+ goto error;
+ }
+
return err;
error:
Py_Finalize();
@@ -649,28 +1006,29 @@ error:
return err;
}
+static int python_flush_script(void)
+{
+ struct tables *tables = &tables_global;
+
+ return db_export__flush(&tables->dbe);
+}
+
/*
* Stop trace script
*/
static int python_stop_script(void)
{
- PyObject *handler, *retval;
- int err = 0;
+ struct tables *tables = &tables_global;
- handler = PyDict_GetItemString(main_dict, "trace_end");
- if (handler == NULL || !PyCallable_Check(handler))
- goto out;
+ try_call_object("trace_end", NULL);
+
+ db_export__exit(&tables->dbe);
- retval = PyObject_CallObject(handler, NULL);
- if (retval == NULL)
- handler_call_die("trace_end");
- Py_DECREF(retval);
-out:
Py_XDECREF(main_dict);
Py_XDECREF(main_module);
Py_Finalize();
- return err;
+ return 0;
}
static int python_generate_script(struct pevent *pevent, const char *outfile)
@@ -843,6 +1201,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops python_scripting_ops = {
.name = "Python",
.start_script = python_start_script,
+ .flush_script = python_flush_script,
.stop_script = python_stop_script,
.process_event = python_process_event,
.generate_script = python_generate_script,
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 88dfef70c13d..5f0e05a76c05 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -14,6 +14,7 @@
#include "util.h"
#include "cpumap.h"
#include "perf_regs.h"
+#include "asm/bug.h"
static int perf_session__open(struct perf_session *session)
{
@@ -66,6 +67,25 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session)
machines__destroy_kernel_maps(&session->machines);
}
+static bool perf_session__has_comm_exec(struct perf_session *session)
+{
+ struct perf_evsel *evsel;
+
+ evlist__for_each(session->evlist, evsel) {
+ if (evsel->attr.comm_exec)
+ return true;
+ }
+
+ return false;
+}
+
+static void perf_session__set_comm_exec(struct perf_session *session)
+{
+ bool comm_exec = perf_session__has_comm_exec(session);
+
+ machines__set_comm_exec(&session->machines, comm_exec);
+}
+
struct perf_session *perf_session__new(struct perf_data_file *file,
bool repipe, struct perf_tool *tool)
{
@@ -75,9 +95,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
goto out;
session->repipe = repipe;
- INIT_LIST_HEAD(&session->ordered_samples.samples);
- INIT_LIST_HEAD(&session->ordered_samples.sample_cache);
- INIT_LIST_HEAD(&session->ordered_samples.to_free);
+ ordered_events__init(&session->ordered_events);
machines__init(&session->machines);
if (file) {
@@ -91,6 +109,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
goto out_close;
perf_session__set_id_hdr_size(session);
+ perf_session__set_comm_exec(session);
}
}
@@ -100,13 +119,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
* kernel MMAP event, in perf_event__process_mmap().
*/
if (perf_session__create_kernel_maps(session) < 0)
- goto out_delete;
+ pr_warning("Cannot read kernel map\n");
}
if (tool && tool->ordering_requires_timestamps &&
- tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) {
+ tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
- tool->ordered_samples = false;
+ tool->ordered_events = false;
}
return session;
@@ -209,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
+static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
void perf_tool__fill_defaults(struct perf_tool *tool)
{
if (tool->sample == NULL)
@@ -238,11 +266,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
if (tool->build_id == NULL)
tool->build_id = process_finished_round_stub;
if (tool->finished_round == NULL) {
- if (tool->ordered_samples)
+ if (tool->ordered_events)
tool->finished_round = process_finished_round;
else
tool->finished_round = process_finished_round_stub;
}
+ if (tool->id_index == NULL)
+ tool->id_index = process_id_index_stub;
}
static void swap_sample_id_all(union perf_event *event, void *data)
@@ -441,90 +471,10 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
[PERF_RECORD_HEADER_BUILD_ID] = NULL,
+ [PERF_RECORD_ID_INDEX] = perf_event__all64_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
-struct sample_queue {
- u64 timestamp;
- u64 file_offset;
- union perf_event *event;
- struct list_head list;
-};
-
-static void perf_session_free_sample_buffers(struct perf_session *session)
-{
- struct ordered_samples *os = &session->ordered_samples;
-
- while (!list_empty(&os->to_free)) {
- struct sample_queue *sq;
-
- sq = list_entry(os->to_free.next, struct sample_queue, list);
- list_del(&sq->list);
- free(sq);
- }
-}
-
-static int perf_session_deliver_event(struct perf_session *session,
- union perf_event *event,
- struct perf_sample *sample,
- struct perf_tool *tool,
- u64 file_offset);
-
-static int flush_sample_queue(struct perf_session *s,
- struct perf_tool *tool)
-{
- struct ordered_samples *os = &s->ordered_samples;
- struct list_head *head = &os->samples;
- struct sample_queue *tmp, *iter;
- struct perf_sample sample;
- u64 limit = os->next_flush;
- u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
- bool show_progress = limit == ULLONG_MAX;
- struct ui_progress prog;
- int ret;
-
- if (!tool->ordered_samples || !limit)
- return 0;
-
- if (show_progress)
- ui_progress__init(&prog, os->nr_samples, "Processing time ordered events...");
-
- list_for_each_entry_safe(iter, tmp, head, list) {
- if (session_done())
- return 0;
-
- if (iter->timestamp > limit)
- break;
-
- ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample);
- if (ret)
- pr_err("Can't parse sample, err = %d\n", ret);
- else {
- ret = perf_session_deliver_event(s, iter->event, &sample, tool,
- iter->file_offset);
- if (ret)
- return ret;
- }
-
- os->last_flush = iter->timestamp;
- list_del(&iter->list);
- list_add(&iter->list, &os->sample_cache);
- os->nr_samples--;
-
- if (show_progress)
- ui_progress__update(&prog, 1);
- }
-
- if (list_empty(head)) {
- os->last_sample = NULL;
- } else if (last_ts <= limit) {
- os->last_sample =
- list_entry(head->prev, struct sample_queue, list);
- }
-
- return 0;
-}
-
/*
* When perf record finishes a pass on every buffers, it records this pseudo
* event.
@@ -568,99 +518,38 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event __maybe_unused,
struct perf_session *session)
{
- int ret = flush_sample_queue(session, tool);
- if (!ret)
- session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
-
- return ret;
-}
-
-/* The queue is ordered by time */
-static void __queue_event(struct sample_queue *new, struct perf_session *s)
-{
- struct ordered_samples *os = &s->ordered_samples;
- struct sample_queue *sample = os->last_sample;
- u64 timestamp = new->timestamp;
- struct list_head *p;
-
- ++os->nr_samples;
- os->last_sample = new;
-
- if (!sample) {
- list_add(&new->list, &os->samples);
- os->max_timestamp = timestamp;
- return;
- }
-
- /*
- * last_sample might point to some random place in the list as it's
- * the last queued event. We expect that the new event is close to
- * this.
- */
- if (sample->timestamp <= timestamp) {
- while (sample->timestamp <= timestamp) {
- p = sample->list.next;
- if (p == &os->samples) {
- list_add_tail(&new->list, &os->samples);
- os->max_timestamp = timestamp;
- return;
- }
- sample = list_entry(p, struct sample_queue, list);
- }
- list_add_tail(&new->list, &sample->list);
- } else {
- while (sample->timestamp > timestamp) {
- p = sample->list.prev;
- if (p == &os->samples) {
- list_add(&new->list, &os->samples);
- return;
- }
- sample = list_entry(p, struct sample_queue, list);
- }
- list_add(&new->list, &sample->list);
- }
+ return ordered_events__flush(session, tool, OE_FLUSH__ROUND);
}
-#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue))
-
int perf_session_queue_event(struct perf_session *s, union perf_event *event,
- struct perf_sample *sample, u64 file_offset)
+ struct perf_tool *tool, struct perf_sample *sample,
+ u64 file_offset)
{
- struct ordered_samples *os = &s->ordered_samples;
- struct list_head *sc = &os->sample_cache;
+ struct ordered_events *oe = &s->ordered_events;
u64 timestamp = sample->time;
- struct sample_queue *new;
+ struct ordered_event *new;
if (!timestamp || timestamp == ~0ULL)
return -ETIME;
- if (timestamp < s->ordered_samples.last_flush) {
- printf("Warning: Timestamp below last timeslice flush\n");
- return -EINVAL;
- }
+ if (timestamp < oe->last_flush) {
+ pr_oe_time(timestamp, "out of order event\n");
+ pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n",
+ oe->last_flush_type);
- if (!list_empty(sc)) {
- new = list_entry(sc->next, struct sample_queue, list);
- list_del(&new->list);
- } else if (os->sample_buffer) {
- new = os->sample_buffer + os->sample_buffer_idx;
- if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER)
- os->sample_buffer = NULL;
- } else {
- os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new));
- if (!os->sample_buffer)
- return -ENOMEM;
- list_add(&os->sample_buffer->list, &os->to_free);
- os->sample_buffer_idx = 2;
- new = os->sample_buffer + 1;
+ s->stats.nr_unordered_events++;
}
- new->timestamp = timestamp;
- new->file_offset = file_offset;
- new->event = event;
+ new = ordered_events__new(oe, timestamp, event);
+ if (!new) {
+ ordered_events__flush(s, tool, OE_FLUSH__HALF);
+ new = ordered_events__new(oe, timestamp, event);
+ }
- __queue_event(new, s);
+ if (!new)
+ return -ENOMEM;
+ new->file_offset = file_offset;
return 0;
}
@@ -699,15 +588,46 @@ static void regs_dump__printf(u64 mask, u64 *regs)
}
}
+static const char *regs_abi[] = {
+ [PERF_SAMPLE_REGS_ABI_NONE] = "none",
+ [PERF_SAMPLE_REGS_ABI_32] = "32-bit",
+ [PERF_SAMPLE_REGS_ABI_64] = "64-bit",
+};
+
+static inline const char *regs_dump_abi(struct regs_dump *d)
+{
+ if (d->abi > PERF_SAMPLE_REGS_ABI_64)
+ return "unknown";
+
+ return regs_abi[d->abi];
+}
+
+static void regs__printf(const char *type, struct regs_dump *regs)
+{
+ u64 mask = regs->mask;
+
+ printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n",
+ type,
+ mask,
+ regs_dump_abi(regs));
+
+ regs_dump__printf(mask, regs->regs);
+}
+
static void regs_user__printf(struct perf_sample *sample)
{
struct regs_dump *user_regs = &sample->user_regs;
- if (user_regs->regs) {
- u64 mask = user_regs->mask;
- printf("... user regs: mask 0x%" PRIx64 "\n", mask);
- regs_dump__printf(mask, user_regs->regs);
- }
+ if (user_regs->regs)
+ regs__printf("user", user_regs);
+}
+
+static void regs_intr__printf(struct perf_sample *sample)
+{
+ struct regs_dump *intr_regs = &sample->intr_regs;
+
+ if (intr_regs->regs)
+ regs__printf("intr", intr_regs);
}
static void stack_user__printf(struct stack_dump *dump)
@@ -806,6 +726,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
if (sample_type & PERF_SAMPLE_REGS_USER)
regs_user__printf(sample);
+ if (sample_type & PERF_SAMPLE_REGS_INTR)
+ regs_intr__printf(sample);
+
if (sample_type & PERF_SAMPLE_STACK_USER)
stack_user__printf(&sample->user_stack);
@@ -920,11 +843,10 @@ perf_session__deliver_sample(struct perf_session *session,
&sample->read.one, machine);
}
-static int perf_session_deliver_event(struct perf_session *session,
- union perf_event *event,
- struct perf_sample *sample,
- struct perf_tool *tool,
- u64 file_offset)
+int perf_session__deliver_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool, u64 file_offset)
{
struct perf_evsel *evsel;
struct machine *machine;
@@ -932,22 +854,6 @@ static int perf_session_deliver_event(struct perf_session *session,
dump_event(session, event, file_offset, sample);
evsel = perf_evlist__id2evsel(session->evlist, sample->id);
- if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) {
- /*
- * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here
- * because the tools right now may apply filters, discarding
- * some of the samples. For consistency, in the future we
- * should have something like nr_filtered_samples and remove
- * the sample->period from total_sample_period, etc, KISS for
- * now tho.
- *
- * Also testing against NULL allows us to handle files without
- * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the
- * future probably it'll be a good idea to restrict event
- * processing via perf_session to files with both set.
- */
- hists__inc_nr_events(&evsel->hists, event->header.type);
- }
machine = perf_session__find_machine_for_cpumode(session, event,
sample);
@@ -1005,8 +911,10 @@ static s64 perf_session__process_user_event(struct perf_session *session,
switch (event->header.type) {
case PERF_RECORD_HEADER_ATTR:
err = tool->attr(tool, event, &session->evlist);
- if (err == 0)
+ if (err == 0) {
perf_session__set_id_hdr_size(session);
+ perf_session__set_comm_exec(session);
+ }
return err;
case PERF_RECORD_HEADER_EVENT_TYPE:
/*
@@ -1022,11 +930,26 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->build_id(tool, event, session);
case PERF_RECORD_FINISHED_ROUND:
return tool->finished_round(tool, event, session);
+ case PERF_RECORD_ID_INDEX:
+ return tool->id_index(tool, event, session);
default:
return -EINVAL;
}
}
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool)
+{
+ events_stats__inc(&session->stats, event->header.type);
+
+ if (event->header.type >= PERF_RECORD_USER_TYPE_START)
+ return perf_session__process_user_event(session, event, tool, 0);
+
+ return perf_session__deliver_event(session, event, sample, tool, 0);
+}
+
static void event_swap(union perf_event *event, bool sample_id_all)
{
perf_event__swap_op swap;
@@ -1036,6 +959,61 @@ static void event_swap(union perf_event *event, bool sample_id_all)
swap(event, sample_id_all);
}
+int perf_session__peek_event(struct perf_session *session, off_t file_offset,
+ void *buf, size_t buf_sz,
+ union perf_event **event_ptr,
+ struct perf_sample *sample)
+{
+ union perf_event *event;
+ size_t hdr_sz, rest;
+ int fd;
+
+ if (session->one_mmap && !session->header.needs_swap) {
+ event = file_offset - session->one_mmap_offset +
+ session->one_mmap_addr;
+ goto out_parse_sample;
+ }
+
+ if (perf_data_file__is_pipe(session->file))
+ return -1;
+
+ fd = perf_data_file__fd(session->file);
+ hdr_sz = sizeof(struct perf_event_header);
+
+ if (buf_sz < hdr_sz)
+ return -1;
+
+ if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 ||
+ readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz)
+ return -1;
+
+ event = (union perf_event *)buf;
+
+ if (session->header.needs_swap)
+ perf_event_header__bswap(&event->header);
+
+ if (event->header.size < hdr_sz)
+ return -1;
+
+ rest = event->header.size - hdr_sz;
+
+ if (readn(fd, &buf, rest) != (ssize_t)rest)
+ return -1;
+
+ if (session->header.needs_swap)
+ event_swap(event, perf_evlist__sample_id_all(session->evlist));
+
+out_parse_sample:
+
+ if (sample && event->header.type < PERF_RECORD_USER_TYPE_START &&
+ perf_evlist__parse_sample(session->evlist, event, sample))
+ return -1;
+
+ *event_ptr = event;
+
+ return 0;
+}
+
static s64 perf_session__process_event(struct perf_session *session,
union perf_event *event,
struct perf_tool *tool,
@@ -1062,15 +1040,15 @@ static s64 perf_session__process_event(struct perf_session *session,
if (ret)
return ret;
- if (tool->ordered_samples) {
- ret = perf_session_queue_event(session, event, &sample,
+ if (tool->ordered_events) {
+ ret = perf_session_queue_event(session, event, tool, &sample,
file_offset);
if (ret != -ETIME)
return ret;
}
- return perf_session_deliver_event(session, event, &sample, tool,
- file_offset);
+ return perf_session__deliver_event(session, event, &sample, tool,
+ file_offset);
}
void perf_event_header__bswap(struct perf_event_header *hdr)
@@ -1136,6 +1114,9 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
"Do you have a KVM guest running and not using 'perf kvm'?\n",
session->stats.nr_unprocessable_samples);
}
+
+ if (session->stats.nr_unordered_events != 0)
+ ui__warning("%u out of order events recorded.\n", session->stats.nr_unordered_events);
}
volatile int session_done;
@@ -1222,12 +1203,11 @@ more:
goto more;
done:
/* do the final flush for ordered samples */
- session->ordered_samples.next_flush = ULLONG_MAX;
- err = flush_sample_queue(session, tool);
+ err = ordered_events__flush(session, tool, OE_FLUSH__FINAL);
out_err:
free(buf);
perf_session__warn_about_errors(session, tool);
- perf_session_free_sample_buffers(session);
+ ordered_events__free(&session->ordered_events);
return err;
}
@@ -1368,12 +1348,11 @@ more:
out:
/* do the final flush for ordered samples */
- session->ordered_samples.next_flush = ULLONG_MAX;
- err = flush_sample_queue(session, tool);
+ err = ordered_events__flush(session, tool, OE_FLUSH__FINAL);
out_err:
ui_progress__finish();
perf_session__warn_about_errors(session, tool);
- perf_session_free_sample_buffers(session);
+ ordered_events__free(&session->ordered_events);
session->one_mmap = false;
return err;
}
@@ -1455,16 +1434,9 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
{
- struct perf_evsel *pos;
size_t ret = fprintf(fp, "Aggregated stats:\n");
ret += events_stats__fprintf(&session->stats, fp);
-
- evlist__for_each(session->evlist, pos) {
- ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
- ret += events_stats__fprintf(&pos->hists.stats, fp);
- }
-
return ret;
}
@@ -1505,9 +1477,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) {
struct addr_location node_al;
- if (machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, NULL, NULL,
- PERF_MAX_STACK_DEPTH) != 0) {
+ if (thread__resolve_callchain(al->thread, evsel,
+ sample, NULL, NULL,
+ PERF_MAX_STACK_DEPTH) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");
return;
@@ -1682,3 +1654,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
out:
return err;
}
+
+int perf_event__process_id_index(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct perf_evlist *evlist = session->evlist;
+ struct id_index_event *ie = &event->id_index;
+ size_t i, nr, max_nr;
+
+ max_nr = (ie->header.size - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+ nr = ie->nr;
+ if (nr > max_nr)
+ return -EINVAL;
+
+ if (dump_trace)
+ fprintf(stdout, " nr: %zu\n", nr);
+
+ for (i = 0; i < nr; i++) {
+ struct id_index_entry *e = &ie->entries[i];
+ struct perf_sample_id *sid;
+
+ if (dump_trace) {
+ fprintf(stdout, " ... id: %"PRIu64, e->id);
+ fprintf(stdout, " idx: %"PRIu64, e->idx);
+ fprintf(stdout, " cpu: %"PRId64, e->cpu);
+ fprintf(stdout, " tid: %"PRId64"\n", e->tid);
+ }
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid)
+ return -ENOENT;
+ sid->idx = e->idx;
+ sid->cpu = e->cpu;
+ sid->tid = e->tid;
+ }
+ return 0;
+}
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine)
+{
+ union perf_event *ev;
+ struct perf_evsel *evsel;
+ size_t nr = 0, i = 0, sz, max_nr, n;
+ int err;
+
+ pr_debug2("Synthesizing id index\n");
+
+ max_nr = (UINT16_MAX - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+
+ evlist__for_each(evlist, evsel)
+ nr += evsel->ids;
+
+ n = nr > max_nr ? max_nr : nr;
+ sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry);
+ ev = zalloc(sz);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->id_index.header.type = PERF_RECORD_ID_INDEX;
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = n;
+
+ evlist__for_each(evlist, evsel) {
+ u32 j;
+
+ for (j = 0; j < evsel->ids; j++) {
+ struct id_index_entry *e;
+ struct perf_sample_id *sid;
+
+ if (i >= n) {
+ err = process(tool, ev, NULL, machine);
+ if (err)
+ goto out_err;
+ nr -= n;
+ i = 0;
+ }
+
+ e = &ev->id_index.entries[i++];
+
+ e->id = evsel->id[j];
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid) {
+ free(ev);
+ return -ENOENT;
+ }
+
+ e->idx = sid->idx;
+ e->cpu = sid->cpu;
+ e->tid = sid->tid;
+ }
+ }
+
+ sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry);
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = nr;
+
+ err = process(tool, ev, NULL, machine);
+out_err:
+ free(ev);
+
+ return err;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 0321013bd9fd..dc26ebf60fe4 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -2,33 +2,19 @@
#define __PERF_SESSION_H
#include "trace-event.h"
-#include "hist.h"
#include "event.h"
#include "header.h"
#include "machine.h"
#include "symbol.h"
#include "thread.h"
#include "data.h"
+#include "ordered-events.h"
#include <linux/rbtree.h>
#include <linux/perf_event.h>
-struct sample_queue;
struct ip_callchain;
struct thread;
-struct ordered_samples {
- u64 last_flush;
- u64 next_flush;
- u64 max_timestamp;
- struct list_head samples;
- struct list_head sample_cache;
- struct list_head to_free;
- struct sample_queue *sample_buffer;
- struct sample_queue *last_sample;
- int sample_buffer_idx;
- unsigned int nr_samples;
-};
-
struct perf_session {
struct perf_header header;
struct machines machines;
@@ -39,7 +25,7 @@ struct perf_session {
bool one_mmap;
void *one_mmap_addr;
u64 one_mmap_offset;
- struct ordered_samples ordered_samples;
+ struct ordered_events ordered_events;
struct perf_data_file *file;
};
@@ -58,6 +44,11 @@ void perf_session__delete(struct perf_session *session);
void perf_event_header__bswap(struct perf_event_header *hdr);
+int perf_session__peek_event(struct perf_session *session, off_t file_offset,
+ void *buf, size_t buf_sz,
+ union perf_event **event_ptr,
+ struct perf_sample *sample);
+
int __perf_session__process_events(struct perf_session *session,
u64 data_offset, u64 data_size, u64 size,
struct perf_tool *tool);
@@ -65,10 +56,16 @@ int perf_session__process_events(struct perf_session *session,
struct perf_tool *tool);
int perf_session_queue_event(struct perf_session *s, union perf_event *event,
- struct perf_sample *sample, u64 file_offset);
+ struct perf_tool *tool, struct perf_sample *sample,
+ u64 file_offset);
void perf_tool__fill_defaults(struct perf_tool *tool);
+int perf_session__deliver_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool, u64 file_offset);
+
int perf_session__resolve_callchain(struct perf_session *session,
struct perf_evsel *evsel,
struct thread *thread,
@@ -128,5 +125,20 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done;
-#define session_done() (*(volatile int *)(&session_done))
+#define session_done() ACCESS_ONCE(session_done)
+
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool);
+
+int perf_event__process_id_index(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 14e5a039bc45..9139dda9f9a3 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -70,12 +70,14 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
const char *comm = thread__comm_str(he->thread);
- return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
- comm ?: "", he->thread->tid);
+
+ width = max(7U, width) - 6;
+ return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid,
+ width, width, comm ?: "");
}
struct sort_entry sort_thread = {
- .se_header = "Command: Pid",
+ .se_header = " Pid:Command",
.se_cmp = sort__thread_cmp,
.se_snprintf = hist_entry__thread_snprintf,
.se_width_idx = HISTC_THREAD,
@@ -106,7 +108,7 @@ sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
}
struct sort_entry sort_comm = {
@@ -152,10 +154,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf,
if (map && map->dso) {
const char *dso_name = !verbose ? map->dso->short_name :
map->dso->long_name;
- return repsep_snprintf(bf, size, "%-*s", width, dso_name);
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
}
- return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
}
static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
@@ -257,7 +259,10 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
width - ret, "");
}
- return ret;
+ if (ret > width)
+ bf[width] = '\0';
+
+ return width;
}
static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
@@ -286,7 +291,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
else {
struct map *map = left->ms.map;
left->srcline = get_srcline(map->dso,
- map__rip_2objdump(map, left->ip));
+ map__rip_2objdump(map, left->ip),
+ left->ms.sym, true);
}
}
if (!right->srcline) {
@@ -295,17 +301,17 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
else {
struct map *map = right->ms.map;
right->srcline = get_srcline(map->dso,
- map__rip_2objdump(map, right->ip));
+ map__rip_2objdump(map, right->ip),
+ right->ms.sym, true);
}
}
return strcmp(right->srcline, left->srcline);
}
static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
- size_t size,
- unsigned int width __maybe_unused)
+ size_t size, unsigned int width)
{
- return repsep_snprintf(bf, size, "%s", he->srcline);
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline);
}
struct sort_entry sort_srcline = {
@@ -332,7 +338,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return repsep_snprintf(bf, size, "%-*s", width,
+ return repsep_snprintf(bf, size, "%-*.*s", width, width,
he->parent ? he->parent->name : "[other]");
}
@@ -354,7 +360,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return repsep_snprintf(bf, size, "%*d", width, he->cpu);
+ return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
}
struct sort_entry sort_cpu = {
@@ -369,6 +375,9 @@ struct sort_entry sort_cpu = {
static int64_t
sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return _sort__dso_cmp(left->branch_info->from.map,
right->branch_info->from.map);
}
@@ -376,13 +385,19 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return _hist_entry__dso_snprintf(he->branch_info->from.map,
- bf, size, width);
+ if (he->branch_info)
+ return _hist_entry__dso_snprintf(he->branch_info->from.map,
+ bf, size, width);
+ else
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}
static int64_t
sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return _sort__dso_cmp(left->branch_info->to.map,
right->branch_info->to.map);
}
@@ -390,8 +405,11 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return _hist_entry__dso_snprintf(he->branch_info->to.map,
- bf, size, width);
+ if (he->branch_info)
+ return _hist_entry__dso_snprintf(he->branch_info->to.map,
+ bf, size, width);
+ else
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}
static int64_t
@@ -400,6 +418,12 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
struct addr_map_symbol *from_l = &left->branch_info->from;
struct addr_map_symbol *from_r = &right->branch_info->from;
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
+ from_l = &left->branch_info->from;
+ from_r = &right->branch_info->from;
+
if (!from_l->sym && !from_r->sym)
return _sort__addr_cmp(from_l->addr, from_r->addr);
@@ -409,8 +433,13 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
static int64_t
sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
- struct addr_map_symbol *to_l = &left->branch_info->to;
- struct addr_map_symbol *to_r = &right->branch_info->to;
+ struct addr_map_symbol *to_l, *to_r;
+
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
+ to_l = &left->branch_info->to;
+ to_r = &right->branch_info->to;
if (!to_l->sym && !to_r->sym)
return _sort__addr_cmp(to_l->addr, to_r->addr);
@@ -421,19 +450,27 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- struct addr_map_symbol *from = &he->branch_info->from;
- return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
- he->level, bf, size, width);
+ if (he->branch_info) {
+ struct addr_map_symbol *from = &he->branch_info->from;
+ return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
+ he->level, bf, size, width);
+ }
+
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}
static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- struct addr_map_symbol *to = &he->branch_info->to;
- return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
- he->level, bf, size, width);
+ if (he->branch_info) {
+ struct addr_map_symbol *to = &he->branch_info->to;
+ return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
+ he->level, bf, size, width);
+ }
+
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}
struct sort_entry sort_dso_from = {
@@ -467,11 +504,13 @@ struct sort_entry sort_sym_to = {
static int64_t
sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
{
- const unsigned char mp = left->branch_info->flags.mispred !=
- right->branch_info->flags.mispred;
- const unsigned char p = left->branch_info->flags.predicted !=
- right->branch_info->flags.predicted;
+ unsigned char mp, p;
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
+ mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
+ p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
return mp || p;
}
@@ -479,12 +518,14 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width){
static const char *out = "N/A";
- if (he->branch_info->flags.predicted)
- out = "N";
- else if (he->branch_info->flags.mispred)
- out = "Y";
+ if (he->branch_info) {
+ if (he->branch_info->flags.predicted)
+ out = "N";
+ else if (he->branch_info->flags.mispred)
+ out = "Y";
+ }
- return repsep_snprintf(bf, size, "%-*s", width, out);
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
}
/* --sort daddr_sym */
@@ -985,6 +1026,9 @@ struct sort_entry sort_mem_dcacheline = {
static int64_t
sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return left->branch_info->flags.abort !=
right->branch_info->flags.abort;
}
@@ -992,10 +1036,15 @@ sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- static const char *out = ".";
+ static const char *out = "N/A";
+
+ if (he->branch_info) {
+ if (he->branch_info->flags.abort)
+ out = "A";
+ else
+ out = ".";
+ }
- if (he->branch_info->flags.abort)
- out = "A";
return repsep_snprintf(bf, size, "%-*s", width, out);
}
@@ -1009,6 +1058,9 @@ struct sort_entry sort_abort = {
static int64_t
sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return left->branch_info->flags.in_tx !=
right->branch_info->flags.in_tx;
}
@@ -1016,10 +1068,14 @@ sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- static const char *out = ".";
+ static const char *out = "N/A";
- if (he->branch_info->flags.in_tx)
- out = "T";
+ if (he->branch_info) {
+ if (he->branch_info->flags.in_tx)
+ out = "T";
+ else
+ out = ".";
+ }
return repsep_snprintf(bf, size, "%-*s", width, out);
}
@@ -1194,7 +1250,7 @@ bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
return hse_a->se == hse_b->se;
}
-void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
+void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
{
struct hpp_sort_entry *hse;
@@ -1202,20 +1258,21 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
return;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
- hists__new_col_len(hists, hse->se->se_width_idx,
- strlen(hse->se->se_header));
+ hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
}
static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct perf_evsel *evsel)
{
struct hpp_sort_entry *hse;
- size_t len;
+ size_t len = fmt->user_len;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
- len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
- return scnprintf(hpp->buf, hpp->size, "%-*s", len, hse->se->se_header);
+ if (!len)
+ len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
+
+ return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
}
static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
@@ -1223,20 +1280,26 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
struct perf_evsel *evsel)
{
struct hpp_sort_entry *hse;
+ size_t len = fmt->user_len;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
- return hists__col_len(&evsel->hists, hse->se->se_width_idx);
+ if (!len)
+ len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
+
+ return len;
}
static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he)
{
struct hpp_sort_entry *hse;
- size_t len;
+ size_t len = fmt->user_len;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
- len = hists__col_len(he->hists, hse->se->se_width_idx);
+
+ if (!len)
+ len = hists__col_len(he->hists, hse->se->se_width_idx);
return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
}
@@ -1253,6 +1316,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
}
hse->se = sd->entry;
+ hse->hpp.name = sd->entry->se_header;
hse->hpp.header = __sort__hpp_header;
hse->hpp.width = __sort__hpp_width;
hse->hpp.entry = __sort__hpp_entry;
@@ -1265,6 +1329,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
INIT_LIST_HEAD(&hse->hpp.list);
INIT_LIST_HEAD(&hse->hpp.sort_list);
hse->hpp.elide = false;
+ hse->hpp.len = 0;
+ hse->hpp.user_len = 0;
return hse;
}
@@ -1432,14 +1498,49 @@ static const char *get_default_sort_order(void)
return default_sort_orders[sort__mode];
}
+static int setup_sort_order(void)
+{
+ char *new_sort_order;
+
+ /*
+ * Append '+'-prefixed sort order to the default sort
+ * order string.
+ */
+ if (!sort_order || is_strict_order(sort_order))
+ return 0;
+
+ if (sort_order[1] == '\0') {
+ error("Invalid --sort key: `+'");
+ return -EINVAL;
+ }
+
+ /*
+ * We allocate new sort_order string, but we never free it,
+ * because it's checked over the rest of the code.
+ */
+ if (asprintf(&new_sort_order, "%s,%s",
+ get_default_sort_order(), sort_order + 1) < 0) {
+ error("Not enough memory to set up --sort");
+ return -ENOMEM;
+ }
+
+ sort_order = new_sort_order;
+ return 0;
+}
+
static int __setup_sorting(void)
{
char *tmp, *tok, *str;
- const char *sort_keys = sort_order;
+ const char *sort_keys;
int ret = 0;
+ ret = setup_sort_order();
+ if (ret)
+ return ret;
+
+ sort_keys = sort_order;
if (sort_keys == NULL) {
- if (field_order) {
+ if (is_strict_order(field_order)) {
/*
* If user specified field order but no sort order,
* we'll honor it and not add default sort orders.
@@ -1625,23 +1726,36 @@ static void reset_dimensions(void)
memory_sort_dimensions[i].taken = 0;
}
+bool is_strict_order(const char *order)
+{
+ return order && (*order != '+');
+}
+
static int __setup_output_field(void)
{
- char *tmp, *tok, *str;
- int ret = 0;
+ char *tmp, *tok, *str, *strp;
+ int ret = -EINVAL;
if (field_order == NULL)
return 0;
reset_dimensions();
- str = strdup(field_order);
+ strp = str = strdup(field_order);
if (str == NULL) {
error("Not enough memory to setup output fields");
return -ENOMEM;
}
- for (tok = strtok_r(str, ", ", &tmp);
+ if (!is_strict_order(field_order))
+ strp++;
+
+ if (!strlen(strp)) {
+ error("Invalid --fields key: `+'");
+ goto out;
+ }
+
+ for (tok = strtok_r(strp, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) {
ret = output_field_add(tok);
if (ret == -EINVAL) {
@@ -1653,6 +1767,7 @@ static int __setup_output_field(void)
}
}
+out:
free(str);
return ret;
}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 041f0c9cea2b..c03e4ff8beff 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide);
int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
+bool is_strict_order(const char *order);
#endif /* __PERF_SORT_H */
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index f3e4bc5fe5d2..e73b6a5c9e0f 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -8,6 +8,8 @@
#include "util/util.h"
#include "util/debug.h"
+#include "symbol.h"
+
#ifdef HAVE_LIBBFD_SUPPORT
/*
@@ -250,7 +252,8 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
*/
#define A2L_FAIL_LIMIT 123
-char *get_srcline(struct dso *dso, unsigned long addr)
+char *get_srcline(struct dso *dso, unsigned long addr, struct symbol *sym,
+ bool show_sym)
{
char *file = NULL;
unsigned line = 0;
@@ -258,7 +261,7 @@ char *get_srcline(struct dso *dso, unsigned long addr)
const char *dso_name;
if (!dso->has_srcline)
- return SRCLINE_UNKNOWN;
+ goto out;
if (dso->symsrc_filename)
dso_name = dso->symsrc_filename;
@@ -274,7 +277,7 @@ char *get_srcline(struct dso *dso, unsigned long addr)
if (!addr2line(dso_name, addr, &file, &line, dso))
goto out;
- if (asprintf(&srcline, "%s:%u", file, line) < 0) {
+ if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) {
free(file);
goto out;
}
@@ -289,7 +292,13 @@ out:
dso->has_srcline = 0;
dso__free_a2l(dso);
}
- return SRCLINE_UNKNOWN;
+ if (sym) {
+ if (asprintf(&srcline, "%s+%ld", show_sym ? sym->name : "",
+ addr - sym->start) < 0)
+ return SRCLINE_UNKNOWN;
+ } else if (asprintf(&srcline, "%s[%lx]", dso->short_name, addr) < 0)
+ return SRCLINE_UNKNOWN;
+ return srcline;
}
void free_srcline(char *srcline)
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 2553e5b55b89..6afd6106ceb5 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -9,78 +9,48 @@
*/
s64 perf_atoll(const char *str)
{
- unsigned int i;
- s64 length = -1, unit = 1;
+ s64 length;
+ char *p;
+ char c;
if (!isdigit(str[0]))
goto out_err;
- for (i = 1; i < strlen(str); i++) {
- switch (str[i]) {
- case 'B':
- case 'b':
- break;
- case 'K':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto kilo;
- case 'k':
- if (str[i + 1] != 'b')
- goto out_err;
-kilo:
- unit = K;
- break;
- case 'M':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto mega;
- case 'm':
- if (str[i + 1] != 'b')
- goto out_err;
-mega:
- unit = K * K;
- break;
- case 'G':
- if (str[i + 1] != 'B')
+ length = strtoll(str, &p, 10);
+ switch (c = *p++) {
+ case 'b': case 'B':
+ if (*p)
goto out_err;
- else
- goto giga;
- case 'g':
- if (str[i + 1] != 'b')
- goto out_err;
-giga:
- unit = K * K * K;
+ case '\0':
+ return length;
+ default:
+ goto out_err;
+ /* two-letter suffices */
+ case 'k': case 'K':
+ length <<= 10;
break;
- case 'T':
- if (str[i + 1] != 'B')
- goto out_err;
- else
- goto tera;
- case 't':
- if (str[i + 1] != 'b')
- goto out_err;
-tera:
- unit = K * K * K * K;
+ case 'm': case 'M':
+ length <<= 20;
break;
- case '\0': /* only specified figures */
- unit = 1;
+ case 'g': case 'G':
+ length <<= 30;
break;
- default:
- if (!isdigit(str[i]))
- goto out_err;
+ case 't': case 'T':
+ length <<= 40;
break;
- }
}
-
- length = atoll(str) * unit;
- goto out;
+ /* we want the cases to match */
+ if (islower(c)) {
+ if (strcmp(p, "b") != 0)
+ goto out_err;
+ } else {
+ if (strcmp(p, "B") != 0)
+ goto out_err;
+ }
+ return length;
out_err:
- length = -1;
-out:
- return length;
+ return -1;
}
/*
@@ -387,27 +357,3 @@ void *memdup(const void *src, size_t len)
return p;
}
-
-/**
- * str_append - reallocate string and append another
- * @s: pointer to string pointer
- * @len: pointer to len (initialized)
- * @a: string to append.
- */
-int str_append(char **s, int *len, const char *a)
-{
- int olen = *s ? strlen(*s) : 0;
- int nlen = olen + strlen(a) + 1;
- if (*len < nlen) {
- *len = *len * 2;
- if (*len < nlen)
- *len = nlen;
- *s = realloc(*s, *len);
- if (!*s)
- return -ENOMEM;
- if (olen == 0)
- **s = 0;
- }
- strcat(*s, a);
- return 0;
-}
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index d75349979e65..06fcd1bf98b6 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -6,10 +6,32 @@
#include <inttypes.h>
#include "symbol.h"
+#include "machine.h"
#include "vdso.h"
#include <symbol/kallsyms.h>
#include "debug.h"
+#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
+extern char *cplus_demangle(const char *, int);
+
+static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i)
+{
+ return cplus_demangle(c, i);
+}
+#else
+#ifdef NO_DEMANGLE
+static inline char *bfd_demangle(void __maybe_unused *v,
+ const char __maybe_unused *c,
+ int __maybe_unused i)
+{
+ return NULL;
+}
+#else
+#define PACKAGE 'perf'
+#include <bfd.h>
+#endif
+#endif
+
#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT
static int elf_getphdrnum(Elf *elf, size_t *dst)
{
@@ -545,6 +567,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata)
return 0;
}
+static int decompress_kmodule(struct dso *dso, const char *name,
+ enum dso_binary_type type)
+{
+ int fd;
+ const char *ext = strrchr(name, '.');
+ char tmpbuf[] = "/tmp/perf-kmod-XXXXXX";
+
+ if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP &&
+ type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) ||
+ type != dso->symtab_type)
+ return -1;
+
+ if (!ext || !is_supported_compression(ext + 1))
+ return -1;
+
+ fd = mkstemp(tmpbuf);
+ if (fd < 0)
+ return -1;
+
+ if (!decompress_to_file(ext + 1, name, fd)) {
+ close(fd);
+ fd = -1;
+ }
+
+ unlink(tmpbuf);
+
+ return fd;
+}
+
bool symsrc__possibly_runtime(struct symsrc *ss)
{
return ss->dynsym || ss->opdsec;
@@ -570,7 +621,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf;
int fd;
- fd = open(name, O_RDONLY);
+ if (dso__needs_decompress(dso))
+ fd = decompress_kmodule(dso, name, type);
+ else
+ fd = open(name, O_RDONLY);
+
if (fd < 0)
return -1;
@@ -680,6 +735,11 @@ static u64 ref_reloc(struct kmap *kmap)
return 0;
}
+static bool want_demangle(bool is_kernel_sym)
+{
+ return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle;
+}
+
int dso__load_sym(struct dso *dso, struct map *map,
struct symsrc *syms_ss, struct symsrc *runtime_ss,
symbol_filter_t filter, int kmodule)
@@ -712,6 +772,14 @@ int dso__load_sym(struct dso *dso, struct map *map,
symbols__delete(&dso->symbols[map->type]);
if (!syms_ss->symtab) {
+ /*
+ * If the vmlinux is stripped, fail so we will fall back
+ * to using kallsyms. The vmlinux runtime symbols aren't
+ * of much use.
+ */
+ if (dso->kernel)
+ goto out_elf_end;
+
syms_ss->symtab = syms_ss->dynsym;
syms_ss->symshdr = syms_ss->dynshdr;
}
@@ -736,7 +804,7 @@ int dso__load_sym(struct dso *dso, struct map *map,
if (symstrs == NULL)
goto out_elf_end;
- sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ sec_strndx = elf_getscn(runtime_ss->elf, runtime_ss->ehdr.e_shstrndx);
if (sec_strndx == NULL)
goto out_elf_end;
@@ -916,7 +984,11 @@ int dso__load_sym(struct dso *dso, struct map *map,
}
curr_dso->symtab_type = dso->symtab_type;
map_groups__insert(kmap->kmaps, curr_map);
- dsos__add(&dso->node, curr_dso);
+ /*
+ * The new DSO should go to the kernel DSOS
+ */
+ dsos__add(&map->groups->machine->kernel_dsos,
+ curr_dso);
dso__set_loaded(curr_dso, map->type);
} else
curr_dso = curr_map->dso;
@@ -938,9 +1010,12 @@ new_symbol:
* DWARF DW_compile_unit has this, but we don't always have access
* to it...
*/
- if (symbol_conf.demangle) {
- demangled = bfd_demangle(NULL, elf_name,
- DMGL_PARAMS | DMGL_ANSI);
+ if (want_demangle(dso->kernel || kmodule)) {
+ int demangle_flags = DMGL_NO_OPTS;
+ if (verbose)
+ demangle_flags = DMGL_PARAMS | DMGL_ANSI;
+
+ demangled = bfd_demangle(NULL, elf_name, demangle_flags);
if (demangled != NULL)
elf_name = demangled;
}
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index c9541fea9514..fa585c63f56a 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -341,7 +341,6 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) {
dso__set_build_id(dso, build_id);
- return 1;
}
return 0;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index eb06746b06b2..c24c5b83156c 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -15,6 +15,7 @@
#include "machine.h"
#include "symbol.h"
#include "strlist.h"
+#include "header.h"
#include <elf.h>
#include <limits.h>
@@ -33,6 +34,7 @@ struct symbol_conf symbol_conf = {
.try_vmlinux_path = true,
.annotate_src = true,
.demangle = true,
+ .demangle_kernel = false,
.cumulate_callchain = true,
.show_hist_headers = true,
.symfs = "",
@@ -49,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = {
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
DSO_BINARY_TYPE__GUEST_KMODULE,
+ DSO_BINARY_TYPE__GUEST_KMODULE_COMP,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+ DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
DSO_BINARY_TYPE__NOT_FOUND,
};
@@ -184,7 +188,7 @@ void symbols__fixup_end(struct rb_root *symbols)
curr = rb_entry(nd, struct symbol, rb_node);
if (prev->end == prev->start && prev->end != curr->start)
- prev->end = curr->start - 1;
+ prev->end = curr->start;
}
/* Last entry */
@@ -205,7 +209,7 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
prev = curr;
curr = rb_entry(nd, struct map, rb_node);
- prev->end = curr->start - 1;
+ prev->end = curr->start;
}
/*
@@ -227,7 +231,7 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name)
sym = ((void *)sym) + symbol_conf.priv_size;
sym->start = start;
- sym->end = len ? start + len - 1 : start;
+ sym->end = len ? start + len : start;
sym->binding = binding;
sym->namelen = namelen - 1;
@@ -323,7 +327,7 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip)
if (ip < s->start)
n = n->rb_left;
- else if (ip > s->end)
+ else if (ip >= s->end)
n = n->rb_right;
else
return s;
@@ -523,10 +527,15 @@ struct process_kallsyms_args {
struct dso *dso;
};
+/*
+ * These are symbols in the kernel image, so make sure that
+ * sym is from a kernel DSO.
+ */
bool symbol__is_idle(struct symbol *sym)
{
const char * const idle_symbols[] = {
"cpu_idle",
+ "cpu_startup_entry",
"intel_idle",
"default_idle",
"native_safe_halt",
@@ -1293,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
return dso->kernel == DSO_TYPE_GUEST_KERNEL;
case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
/*
* kernel modules know their symtab type - it's set when
* creating a module dso in machine__new_module().
@@ -1361,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
return -1;
kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
- dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE;
+ dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
+ dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE ||
+ dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
/*
* Iterate over candidate debug images.
@@ -1468,8 +1481,7 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,
if (vmlinux[0] == '/')
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux);
else
- snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
- symbol_conf.symfs, vmlinux);
+ symbol__join_symfs(symfs_vmlinux, vmlinux);
if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
@@ -1499,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
symbol_filter_t filter)
{
int i, err = 0;
- char *filename;
-
- pr_debug("Looking at the vmlinux_path (%d entries long)\n",
- vmlinux_path__nr_entries + 1);
+ char *filename = NULL;
- filename = dso__build_id_filename(dso, NULL, 0);
+ if (!symbol_conf.ignore_vmlinux_buildid)
+ filename = dso__build_id_filename(dso, NULL, 0);
if (filename != NULL) {
err = dso__load_vmlinux(dso, map, filename, true, filter);
if (err > 0)
@@ -1512,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
free(filename);
}
+ pr_debug("Looking at the vmlinux_path (%d entries long)\n",
+ vmlinux_path__nr_entries + 1);
+
for (i = 0; i < vmlinux_path__nr_entries; ++i) {
err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter);
if (err > 0)
@@ -1745,12 +1758,13 @@ static void vmlinux_path__exit(void)
zfree(&vmlinux_path);
}
-static int vmlinux_path__init(void)
+static int vmlinux_path__init(struct perf_session_env *env)
{
struct utsname uts;
char bf[PATH_MAX];
+ char *kernel_version;
- vmlinux_path = malloc(sizeof(char *) * 5);
+ vmlinux_path = malloc(sizeof(char *) * 6);
if (vmlinux_path == NULL)
return -1;
@@ -1763,25 +1777,37 @@ static int vmlinux_path__init(void)
goto out_fail;
++vmlinux_path__nr_entries;
- /* only try running kernel version if no symfs was given */
+ /* only try kernel version if no symfs was given */
if (symbol_conf.symfs[0] != 0)
return 0;
- if (uname(&uts) < 0)
- return -1;
+ if (env) {
+ kernel_version = env->os_release;
+ } else {
+ if (uname(&uts) < 0)
+ goto out_fail;
+
+ kernel_version = uts.release;
+ }
- snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+ snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version);
vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
goto out_fail;
++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
+ snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s",
+ kernel_version);
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ goto out_fail;
+ ++vmlinux_path__nr_entries;
+ snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version);
vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
goto out_fail;
++vmlinux_path__nr_entries;
snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
- uts.release);
+ kernel_version);
vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
goto out_fail;
@@ -1827,7 +1853,7 @@ static bool symbol__read_kptr_restrict(void)
return value;
}
-int symbol__init(void)
+int symbol__init(struct perf_session_env *env)
{
const char *symfs;
@@ -1842,7 +1868,7 @@ int symbol__init(void)
symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
sizeof(struct symbol));
- if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
+ if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0)
return -1;
if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index e7295e93cff9..9d602e9c6f59 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -13,6 +13,7 @@
#include <libgen.h>
#include "build-id.h"
#include "event.h"
+#include "util.h"
#ifdef HAVE_LIBELF_SUPPORT
#include <libelf.h>
@@ -22,27 +23,6 @@
#include "dso.h"
-#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
-extern char *cplus_demangle(const char *, int);
-
-static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i)
-{
- return cplus_demangle(c, i);
-}
-#else
-#ifdef NO_DEMANGLE
-static inline char *bfd_demangle(void __maybe_unused *v,
- const char __maybe_unused *c,
- int __maybe_unused i)
-{
- return NULL;
-}
-#else
-#define PACKAGE 'perf'
-#include <bfd.h>
-#endif
-#endif
-
/*
* libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
* for newer versions we can use mmap to reduce memory usage:
@@ -59,6 +39,7 @@ extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
#endif
#ifndef DMGL_PARAMS
+#define DMGL_NO_OPTS 0 /* For readability... */
#define DMGL_PARAMS (1 << 0) /* Include function args */
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
#endif
@@ -93,7 +74,7 @@ void symbols__delete(struct rb_root *symbols);
static inline size_t symbol__size(const struct symbol *sym)
{
- return sym->end - sym->start + 1;
+ return sym->end - sym->start;
}
struct strlist;
@@ -103,6 +84,7 @@ struct symbol_conf {
unsigned short nr_events;
bool try_vmlinux_path,
ignore_vmlinux,
+ ignore_vmlinux_buildid,
show_kernel_path,
use_modules,
sort_by_name,
@@ -118,8 +100,10 @@ struct symbol_conf {
annotate_src,
event_group,
demangle,
+ demangle_kernel,
filter_relative,
- show_hist_headers;
+ show_hist_headers,
+ branch_callstack;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
@@ -143,6 +127,14 @@ struct symbol_conf {
};
extern struct symbol_conf symbol_conf;
+
+static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
+{
+ return path__join(bf, size, symbol_conf.symfs, path);
+}
+
+#define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path)
+
extern int vmlinux_path__nr_entries;
extern char **vmlinux_path;
@@ -253,7 +245,8 @@ int modules__parse(const char *filename, void *arg,
int filename__read_debuglink(const char *filename, char *debuglink,
size_t size);
-int symbol__init(void);
+struct perf_session_env;
+int symbol__init(struct perf_session_env *env);
void symbol__exit(void);
void symbol__elf_init(void);
struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name);
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
new file mode 100644
index 000000000000..9ed59a452d1f
--- /dev/null
+++ b/tools/perf/util/thread-stack.c
@@ -0,0 +1,747 @@
+/*
+ * thread-stack.c: Synthesize a thread's stack using call / return events
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include "thread.h"
+#include "event.h"
+#include "machine.h"
+#include "util.h"
+#include "debug.h"
+#include "symbol.h"
+#include "comm.h"
+#include "thread-stack.h"
+
+#define CALL_PATH_BLOCK_SHIFT 8
+#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
+#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
+
+struct call_path_block {
+ struct call_path cp[CALL_PATH_BLOCK_SIZE];
+ struct list_head node;
+};
+
+/**
+ * struct call_path_root - root of all call paths.
+ * @call_path: root call path
+ * @blocks: list of blocks to store call paths
+ * @next: next free space
+ * @sz: number of spaces
+ */
+struct call_path_root {
+ struct call_path call_path;
+ struct list_head blocks;
+ size_t next;
+ size_t sz;
+};
+
+/**
+ * struct call_return_processor - provides a call-back to consume call-return
+ * information.
+ * @cpr: call path root
+ * @process: call-back that accepts call/return information
+ * @data: anonymous data for call-back
+ */
+struct call_return_processor {
+ struct call_path_root *cpr;
+ int (*process)(struct call_return *cr, void *data);
+ void *data;
+};
+
+#define STACK_GROWTH 2048
+
+/**
+ * struct thread_stack_entry - thread stack entry.
+ * @ret_addr: return address
+ * @timestamp: timestamp (if known)
+ * @ref: external reference (e.g. db_id of sample)
+ * @branch_count: the branch count when the entry was created
+ * @cp: call path
+ * @no_call: a 'call' was not seen
+ */
+struct thread_stack_entry {
+ u64 ret_addr;
+ u64 timestamp;
+ u64 ref;
+ u64 branch_count;
+ struct call_path *cp;
+ bool no_call;
+};
+
+/**
+ * struct thread_stack - thread stack constructed from 'call' and 'return'
+ * branch samples.
+ * @stack: array that holds the stack
+ * @cnt: number of entries in the stack
+ * @sz: current maximum stack size
+ * @trace_nr: current trace number
+ * @branch_count: running branch count
+ * @kernel_start: kernel start address
+ * @last_time: last timestamp
+ * @crp: call/return processor
+ * @comm: current comm
+ */
+struct thread_stack {
+ struct thread_stack_entry *stack;
+ size_t cnt;
+ size_t sz;
+ u64 trace_nr;
+ u64 branch_count;
+ u64 kernel_start;
+ u64 last_time;
+ struct call_return_processor *crp;
+ struct comm *comm;
+};
+
+static int thread_stack__grow(struct thread_stack *ts)
+{
+ struct thread_stack_entry *new_stack;
+ size_t sz, new_sz;
+
+ new_sz = ts->sz + STACK_GROWTH;
+ sz = new_sz * sizeof(struct thread_stack_entry);
+
+ new_stack = realloc(ts->stack, sz);
+ if (!new_stack)
+ return -ENOMEM;
+
+ ts->stack = new_stack;
+ ts->sz = new_sz;
+
+ return 0;
+}
+
+static struct thread_stack *thread_stack__new(struct thread *thread,
+ struct call_return_processor *crp)
+{
+ struct thread_stack *ts;
+
+ ts = zalloc(sizeof(struct thread_stack));
+ if (!ts)
+ return NULL;
+
+ if (thread_stack__grow(ts)) {
+ free(ts);
+ return NULL;
+ }
+
+ if (thread->mg && thread->mg->machine)
+ ts->kernel_start = machine__kernel_start(thread->mg->machine);
+ else
+ ts->kernel_start = 1ULL << 63;
+ ts->crp = crp;
+
+ return ts;
+}
+
+static int thread_stack__push(struct thread_stack *ts, u64 ret_addr)
+{
+ int err = 0;
+
+ if (ts->cnt == ts->sz) {
+ err = thread_stack__grow(ts);
+ if (err) {
+ pr_warning("Out of memory: discarding thread stack\n");
+ ts->cnt = 0;
+ }
+ }
+
+ ts->stack[ts->cnt++].ret_addr = ret_addr;
+
+ return err;
+}
+
+static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
+{
+ size_t i;
+
+ /*
+ * In some cases there may be functions which are not seen to return.
+ * For example when setjmp / longjmp has been used. Or the perf context
+ * switch in the kernel which doesn't stop and start tracing in exactly
+ * the same code path. When that happens the return address will be
+ * further down the stack. If the return address is not found at all,
+ * we assume the opposite (i.e. this is a return for a call that wasn't
+ * seen for some reason) and leave the stack alone.
+ */
+ for (i = ts->cnt; i; ) {
+ if (ts->stack[--i].ret_addr == ret_addr) {
+ ts->cnt = i;
+ return;
+ }
+ }
+}
+
+static bool thread_stack__in_kernel(struct thread_stack *ts)
+{
+ if (!ts->cnt)
+ return false;
+
+ return ts->stack[ts->cnt - 1].cp->in_kernel;
+}
+
+static int thread_stack__call_return(struct thread *thread,
+ struct thread_stack *ts, size_t idx,
+ u64 timestamp, u64 ref, bool no_return)
+{
+ struct call_return_processor *crp = ts->crp;
+ struct thread_stack_entry *tse;
+ struct call_return cr = {
+ .thread = thread,
+ .comm = ts->comm,
+ .db_id = 0,
+ };
+
+ tse = &ts->stack[idx];
+ cr.cp = tse->cp;
+ cr.call_time = tse->timestamp;
+ cr.return_time = timestamp;
+ cr.branch_count = ts->branch_count - tse->branch_count;
+ cr.call_ref = tse->ref;
+ cr.return_ref = ref;
+ if (tse->no_call)
+ cr.flags |= CALL_RETURN_NO_CALL;
+ if (no_return)
+ cr.flags |= CALL_RETURN_NO_RETURN;
+
+ return crp->process(&cr, crp->data);
+}
+
+static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+{
+ struct call_return_processor *crp = ts->crp;
+ int err;
+
+ if (!crp) {
+ ts->cnt = 0;
+ return 0;
+ }
+
+ while (ts->cnt) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ ts->last_time, 0, true);
+ if (err) {
+ pr_err("Error flushing thread stack!\n");
+ ts->cnt = 0;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+ u64 to_ip, u16 insn_len, u64 trace_nr)
+{
+ if (!thread)
+ return -EINVAL;
+
+ if (!thread->ts) {
+ thread->ts = thread_stack__new(thread, NULL);
+ if (!thread->ts) {
+ pr_warning("Out of memory: no thread stack\n");
+ return -ENOMEM;
+ }
+ thread->ts->trace_nr = trace_nr;
+ }
+
+ /*
+ * When the trace is discontinuous, the trace_nr changes. In that case
+ * the stack might be completely invalid. Better to report nothing than
+ * to report something misleading, so flush the stack.
+ */
+ if (trace_nr != thread->ts->trace_nr) {
+ if (thread->ts->trace_nr)
+ thread_stack__flush(thread, thread->ts);
+ thread->ts->trace_nr = trace_nr;
+ }
+
+ /* Stop here if thread_stack__process() is in use */
+ if (thread->ts->crp)
+ return 0;
+
+ if (flags & PERF_IP_FLAG_CALL) {
+ u64 ret_addr;
+
+ if (!to_ip)
+ return 0;
+ ret_addr = from_ip + insn_len;
+ if (ret_addr == to_ip)
+ return 0; /* Zero-length calls are excluded */
+ return thread_stack__push(thread->ts, ret_addr);
+ } else if (flags & PERF_IP_FLAG_RETURN) {
+ if (!from_ip)
+ return 0;
+ thread_stack__pop(thread->ts, to_ip);
+ }
+
+ return 0;
+}
+
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
+{
+ if (!thread || !thread->ts)
+ return;
+
+ if (trace_nr != thread->ts->trace_nr) {
+ if (thread->ts->trace_nr)
+ thread_stack__flush(thread, thread->ts);
+ thread->ts->trace_nr = trace_nr;
+ }
+}
+
+void thread_stack__free(struct thread *thread)
+{
+ if (thread->ts) {
+ thread_stack__flush(thread, thread->ts);
+ zfree(&thread->ts->stack);
+ zfree(&thread->ts);
+ }
+}
+
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+ size_t sz, u64 ip)
+{
+ size_t i;
+
+ if (!thread || !thread->ts)
+ chain->nr = 1;
+ else
+ chain->nr = min(sz, thread->ts->cnt + 1);
+
+ chain->ips[0] = ip;
+
+ for (i = 1; i < chain->nr; i++)
+ chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
+}
+
+static void call_path__init(struct call_path *cp, struct call_path *parent,
+ struct symbol *sym, u64 ip, bool in_kernel)
+{
+ cp->parent = parent;
+ cp->sym = sym;
+ cp->ip = sym ? 0 : ip;
+ cp->db_id = 0;
+ cp->in_kernel = in_kernel;
+ RB_CLEAR_NODE(&cp->rb_node);
+ cp->children = RB_ROOT;
+}
+
+static struct call_path_root *call_path_root__new(void)
+{
+ struct call_path_root *cpr;
+
+ cpr = zalloc(sizeof(struct call_path_root));
+ if (!cpr)
+ return NULL;
+ call_path__init(&cpr->call_path, NULL, NULL, 0, false);
+ INIT_LIST_HEAD(&cpr->blocks);
+ return cpr;
+}
+
+static void call_path_root__free(struct call_path_root *cpr)
+{
+ struct call_path_block *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+ list_del(&pos->node);
+ free(pos);
+ }
+ free(cpr);
+}
+
+static struct call_path *call_path__new(struct call_path_root *cpr,
+ struct call_path *parent,
+ struct symbol *sym, u64 ip,
+ bool in_kernel)
+{
+ struct call_path_block *cpb;
+ struct call_path *cp;
+ size_t n;
+
+ if (cpr->next < cpr->sz) {
+ cpb = list_last_entry(&cpr->blocks, struct call_path_block,
+ node);
+ } else {
+ cpb = zalloc(sizeof(struct call_path_block));
+ if (!cpb)
+ return NULL;
+ list_add_tail(&cpb->node, &cpr->blocks);
+ cpr->sz += CALL_PATH_BLOCK_SIZE;
+ }
+
+ n = cpr->next++ & CALL_PATH_BLOCK_MASK;
+ cp = &cpb->cp[n];
+
+ call_path__init(cp, parent, sym, ip, in_kernel);
+
+ return cp;
+}
+
+static struct call_path *call_path__findnew(struct call_path_root *cpr,
+ struct call_path *parent,
+ struct symbol *sym, u64 ip, u64 ks)
+{
+ struct rb_node **p;
+ struct rb_node *node_parent = NULL;
+ struct call_path *cp;
+ bool in_kernel = ip >= ks;
+
+ if (sym)
+ ip = 0;
+
+ if (!parent)
+ return call_path__new(cpr, parent, sym, ip, in_kernel);
+
+ p = &parent->children.rb_node;
+ while (*p != NULL) {
+ node_parent = *p;
+ cp = rb_entry(node_parent, struct call_path, rb_node);
+
+ if (cp->sym == sym && cp->ip == ip)
+ return cp;
+
+ if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ cp = call_path__new(cpr, parent, sym, ip, in_kernel);
+ if (!cp)
+ return NULL;
+
+ rb_link_node(&cp->rb_node, node_parent, p);
+ rb_insert_color(&cp->rb_node, &parent->children);
+
+ return cp;
+}
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+ void *data)
+{
+ struct call_return_processor *crp;
+
+ crp = zalloc(sizeof(struct call_return_processor));
+ if (!crp)
+ return NULL;
+ crp->cpr = call_path_root__new();
+ if (!crp->cpr)
+ goto out_free;
+ crp->process = process;
+ crp->data = data;
+ return crp;
+
+out_free:
+ free(crp);
+ return NULL;
+}
+
+void call_return_processor__free(struct call_return_processor *crp)
+{
+ if (crp) {
+ call_path_root__free(crp->cpr);
+ free(crp);
+ }
+}
+
+static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
+ u64 timestamp, u64 ref, struct call_path *cp,
+ bool no_call)
+{
+ struct thread_stack_entry *tse;
+ int err;
+
+ if (ts->cnt == ts->sz) {
+ err = thread_stack__grow(ts);
+ if (err)
+ return err;
+ }
+
+ tse = &ts->stack[ts->cnt++];
+ tse->ret_addr = ret_addr;
+ tse->timestamp = timestamp;
+ tse->ref = ref;
+ tse->branch_count = ts->branch_count;
+ tse->cp = cp;
+ tse->no_call = no_call;
+
+ return 0;
+}
+
+static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
+ u64 ret_addr, u64 timestamp, u64 ref,
+ struct symbol *sym)
+{
+ int err;
+
+ if (!ts->cnt)
+ return 1;
+
+ if (ts->cnt == 1) {
+ struct thread_stack_entry *tse = &ts->stack[0];
+
+ if (tse->cp->sym == sym)
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ }
+
+ if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ } else {
+ size_t i = ts->cnt - 1;
+
+ while (i--) {
+ if (ts->stack[i].ret_addr != ret_addr)
+ continue;
+ i += 1;
+ while (ts->cnt > i) {
+ err = thread_stack__call_return(thread, ts,
+ --ts->cnt,
+ timestamp, ref,
+ true);
+ if (err)
+ return err;
+ }
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ }
+ }
+
+ return 1;
+}
+
+static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ struct symbol *sym;
+ u64 ip;
+
+ if (sample->ip) {
+ ip = sample->ip;
+ sym = from_al->sym;
+ } else if (sample->addr) {
+ ip = sample->addr;
+ sym = to_al->sym;
+ } else {
+ return 0;
+ }
+
+ cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
+ true);
+}
+
+static int thread_stack__no_call_return(struct thread *thread,
+ struct thread_stack *ts,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp, *parent;
+ u64 ks = ts->kernel_start;
+ int err;
+
+ if (sample->ip >= ks && sample->addr < ks) {
+ /* Return to userspace, so pop all kernel addresses */
+ while (thread_stack__in_kernel(ts)) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ sample->time, ref,
+ true);
+ if (err)
+ return err;
+ }
+
+ /* If the stack is empty, push the userspace address */
+ if (!ts->cnt) {
+ cp = call_path__findnew(cpr, &cpr->call_path,
+ to_al->sym, sample->addr,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+ return thread_stack__push_cp(ts, 0, sample->time, ref,
+ cp, true);
+ }
+ } else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
+ /* Return to userspace, so pop all kernel addresses */
+ while (thread_stack__in_kernel(ts)) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ sample->time, ref,
+ true);
+ if (err)
+ return err;
+ }
+ }
+
+ if (ts->cnt)
+ parent = ts->stack[ts->cnt - 1].cp;
+ else
+ parent = &cpr->call_path;
+
+ /* This 'return' had no 'call', so push and pop top of stack */
+ cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
+ true);
+ if (err)
+ return err;
+
+ return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
+ to_al->sym);
+}
+
+static int thread_stack__trace_begin(struct thread *thread,
+ struct thread_stack *ts, u64 timestamp,
+ u64 ref)
+{
+ struct thread_stack_entry *tse;
+ int err;
+
+ if (!ts->cnt)
+ return 0;
+
+ /* Pop trace end */
+ tse = &ts->stack[ts->cnt - 1];
+ if (tse->cp->sym == NULL && tse->cp->ip == 0) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int thread_stack__trace_end(struct thread_stack *ts,
+ struct perf_sample *sample, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ u64 ret_addr;
+
+ /* No point having 'trace end' on the bottom of the stack */
+ if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
+ return 0;
+
+ cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ ret_addr = sample->ip + sample->insn_len;
+
+ return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
+ false);
+}
+
+int thread_stack__process(struct thread *thread, struct comm *comm,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref,
+ struct call_return_processor *crp)
+{
+ struct thread_stack *ts = thread->ts;
+ int err = 0;
+
+ if (ts) {
+ if (!ts->crp) {
+ /* Supersede thread_stack__event() */
+ thread_stack__free(thread);
+ thread->ts = thread_stack__new(thread, crp);
+ if (!thread->ts)
+ return -ENOMEM;
+ ts = thread->ts;
+ ts->comm = comm;
+ }
+ } else {
+ thread->ts = thread_stack__new(thread, crp);
+ if (!thread->ts)
+ return -ENOMEM;
+ ts = thread->ts;
+ ts->comm = comm;
+ }
+
+ /* Flush stack on exec */
+ if (ts->comm != comm && thread->pid_ == thread->tid) {
+ err = thread_stack__flush(thread, ts);
+ if (err)
+ return err;
+ ts->comm = comm;
+ }
+
+ /* If the stack is empty, put the current symbol on the stack */
+ if (!ts->cnt) {
+ err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
+ ref);
+ if (err)
+ return err;
+ }
+
+ ts->branch_count += 1;
+ ts->last_time = sample->time;
+
+ if (sample->flags & PERF_IP_FLAG_CALL) {
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ u64 ret_addr;
+
+ if (!sample->ip || !sample->addr)
+ return 0;
+
+ ret_addr = sample->ip + sample->insn_len;
+ if (ret_addr == sample->addr)
+ return 0; /* Zero-length calls are excluded */
+
+ cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
+ to_al->sym, sample->addr,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+ err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
+ cp, false);
+ } else if (sample->flags & PERF_IP_FLAG_RETURN) {
+ if (!sample->ip || !sample->addr)
+ return 0;
+
+ err = thread_stack__pop_cp(thread, ts, sample->addr,
+ sample->time, ref, from_al->sym);
+ if (err) {
+ if (err < 0)
+ return err;
+ err = thread_stack__no_call_return(thread, ts, sample,
+ from_al, to_al, ref);
+ }
+ } else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) {
+ err = thread_stack__trace_begin(thread, ts, sample->time, ref);
+ } else if (sample->flags & PERF_IP_FLAG_TRACE_END) {
+ err = thread_stack__trace_end(ts, sample, ref);
+ }
+
+ return err;
+}
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
new file mode 100644
index 000000000000..b843bbef8ba2
--- /dev/null
+++ b/tools/perf/util/thread-stack.h
@@ -0,0 +1,111 @@
+/*
+ * thread-stack.h: Synthesize a thread's stack using call / return events
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_THREAD_STACK_H
+#define __PERF_THREAD_STACK_H
+
+#include <sys/types.h>
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+
+struct thread;
+struct comm;
+struct ip_callchain;
+struct symbol;
+struct dso;
+struct call_return_processor;
+struct comm;
+struct perf_sample;
+struct addr_location;
+
+/*
+ * Call/Return flags.
+ *
+ * CALL_RETURN_NO_CALL: 'return' but no matching 'call'
+ * CALL_RETURN_NO_RETURN: 'call' but no matching 'return'
+ */
+enum {
+ CALL_RETURN_NO_CALL = 1 << 0,
+ CALL_RETURN_NO_RETURN = 1 << 1,
+};
+
+/**
+ * struct call_return - paired call/return information.
+ * @thread: thread in which call/return occurred
+ * @comm: comm in which call/return occurred
+ * @cp: call path
+ * @call_time: timestamp of call (if known)
+ * @return_time: timestamp of return (if known)
+ * @branch_count: number of branches seen between call and return
+ * @call_ref: external reference to 'call' sample (e.g. db_id)
+ * @return_ref: external reference to 'return' sample (e.g. db_id)
+ * @db_id: id used for db-export
+ * @flags: Call/Return flags
+ */
+struct call_return {
+ struct thread *thread;
+ struct comm *comm;
+ struct call_path *cp;
+ u64 call_time;
+ u64 return_time;
+ u64 branch_count;
+ u64 call_ref;
+ u64 return_ref;
+ u64 db_id;
+ u32 flags;
+};
+
+/**
+ * struct call_path - node in list of calls leading to a function call.
+ * @parent: call path to the parent function call
+ * @sym: symbol of function called
+ * @ip: only if sym is null, the ip of the function
+ * @db_id: id used for db-export
+ * @in_kernel: whether function is a in the kernel
+ * @rb_node: node in parent's tree of called functions
+ * @children: tree of call paths of functions called
+ *
+ * In combination with the call_return structure, the call_path structure
+ * defines a context-sensitve call-graph.
+ */
+struct call_path {
+ struct call_path *parent;
+ struct symbol *sym;
+ u64 ip;
+ u64 db_id;
+ bool in_kernel;
+ struct rb_node rb_node;
+ struct rb_root children;
+};
+
+int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+ u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+ size_t sz, u64 ip);
+void thread_stack__free(struct thread *thread);
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+ void *data);
+void call_return_processor__free(struct call_return_processor *crp);
+int thread_stack__process(struct thread *thread, struct comm *comm,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref,
+ struct call_return_processor *crp);
+
+#endif
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 12c7a253a63c..9ebc8b1f9be5 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -4,9 +4,11 @@
#include <string.h>
#include "session.h"
#include "thread.h"
+#include "thread-stack.h"
#include "util.h"
#include "debug.h"
#include "comm.h"
+#include "unwind.h"
int thread__init_map_groups(struct thread *thread, struct machine *machine)
{
@@ -14,7 +16,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine)
pid_t pid = thread->pid_;
if (pid == thread->tid || pid == -1) {
- thread->mg = map_groups__new();
+ thread->mg = map_groups__new(machine);
} else {
leader = machine__findnew_thread(machine, pid, pid);
if (leader)
@@ -37,17 +39,21 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);
+ if (unwind__prepare_access(thread) < 0)
+ goto err_thread;
+
comm_str = malloc(32);
if (!comm_str)
goto err_thread;
snprintf(comm_str, 32, ":%d", tid);
- comm = comm__new(comm_str, 0);
+ comm = comm__new(comm_str, 0, false);
free(comm_str);
if (!comm)
goto err_thread;
list_add(&comm->list, &thread->comm_list);
+
}
return thread;
@@ -61,6 +67,8 @@ void thread__delete(struct thread *thread)
{
struct comm *comm, *tmp;
+ thread_stack__free(thread);
+
if (thread->mg) {
map_groups__put(thread->mg);
thread->mg = NULL;
@@ -69,6 +77,7 @@ void thread__delete(struct thread *thread)
list_del(&comm->list);
comm__free(comm);
}
+ unwind__finish_access(thread);
free(thread);
}
@@ -81,22 +90,38 @@ struct comm *thread__comm(const struct thread *thread)
return list_first_entry(&thread->comm_list, struct comm, list);
}
-/* CHECKME: time should always be 0 if event aren't ordered */
-int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)
+struct comm *thread__exec_comm(const struct thread *thread)
+{
+ struct comm *comm, *last = NULL;
+
+ list_for_each_entry(comm, &thread->comm_list, list) {
+ if (comm->exec)
+ return comm;
+ last = comm;
+ }
+
+ return last;
+}
+
+int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
+ bool exec)
{
struct comm *new, *curr = thread__comm(thread);
int err;
- /* Override latest entry if it had no specific time coverage */
- if (!curr->start) {
- err = comm__override(curr, str, timestamp);
+ /* Override the default :tid entry */
+ if (!thread->comm_set) {
+ err = comm__override(curr, str, timestamp, exec);
if (err)
return err;
} else {
- new = comm__new(str, timestamp);
+ new = comm__new(str, timestamp, exec);
if (!new)
return -ENOMEM;
list_add(&new->list, &thread->comm_list);
+
+ if (exec)
+ unwind__flush_access(thread);
}
thread->comm_set = true;
@@ -175,7 +200,6 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
}
void thread__find_cpumode_addr_location(struct thread *thread,
- struct machine *machine,
enum map_type type, u64 addr,
struct addr_location *al)
{
@@ -188,8 +212,7 @@ void thread__find_cpumode_addr_location(struct thread *thread,
};
for (i = 0; i < ARRAY_SIZE(cpumodes); i++) {
- thread__find_addr_location(thread, machine, cpumodes[i], type,
- addr, al);
+ thread__find_addr_location(thread, cpumodes[i], type, addr, al);
if (al->map)
break;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 716b7723cce2..160fd066a7d1 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -8,6 +8,8 @@
#include "symbol.h"
#include <strlist.h>
+struct thread_stack;
+
struct thread {
union {
struct rb_node rb_node;
@@ -23,8 +25,10 @@ struct thread {
bool dead; /* if set thread has exited */
struct list_head comm_list;
int comm_len;
+ u64 db_id;
void *priv;
+ struct thread_stack *ts;
};
struct machine;
@@ -38,24 +42,31 @@ static inline void thread__exited(struct thread *thread)
thread->dead = true;
}
-int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp);
+int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
+ bool exec);
+static inline int thread__set_comm(struct thread *thread, const char *comm,
+ u64 timestamp)
+{
+ return __thread__set_comm(thread, comm, timestamp, false);
+}
+
int thread__comm_len(struct thread *thread);
struct comm *thread__comm(const struct thread *thread);
+struct comm *thread__exec_comm(const struct thread *thread);
const char *thread__comm_str(const struct thread *thread);
void thread__insert_map(struct thread *thread, struct map *map);
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);
size_t thread__fprintf(struct thread *thread, FILE *fp);
-void thread__find_addr_map(struct thread *thread, struct machine *machine,
+void thread__find_addr_map(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al);
-void thread__find_addr_location(struct thread *thread, struct machine *machine,
+void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al);
void thread__find_cpumode_addr_location(struct thread *thread,
- struct machine *machine,
enum map_type type, u64 addr,
struct addr_location *al);
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 5d3215912105..f93b9734735b 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -214,6 +214,17 @@ out_free_threads:
goto out;
}
+struct thread_map *thread_map__new_dummy(void)
+{
+ struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
+
+ if (threads != NULL) {
+ threads->map[0] = -1;
+ threads->nr = 1;
+ }
+ return threads;
+}
+
static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
{
struct thread_map *threads = NULL, *nt;
@@ -224,14 +235,8 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
struct strlist *slist;
/* perf-stat expects threads to be generated even if tid not given */
- if (!tid_str) {
- threads = malloc(sizeof(*threads) + sizeof(pid_t));
- if (threads != NULL) {
- threads->map[0] = -1;
- threads->nr = 1;
- }
- return threads;
- }
+ if (!tid_str)
+ return thread_map__new_dummy();
slist = strlist__new(false, tid_str);
if (!slist)
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index 0cd8b3108084..95313f43cc0f 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -9,6 +9,7 @@ struct thread_map {
pid_t map[];
};
+struct thread_map *thread_map__new_dummy(void);
struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new_by_uid(uid_t uid);
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index 4385816d3d49..bb2708bbfaca 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -39,8 +39,9 @@ struct perf_tool {
event_attr_op attr;
event_op2 tracing_data;
event_op2 finished_round,
- build_id;
- bool ordered_samples;
+ build_id,
+ id_index;
+ bool ordered_events;
bool ordering_requires_timestamps;
};
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 57aaccc1692e..5c9bdd1591a9 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -30,6 +30,11 @@
struct scripting_context *scripting_context;
+static int flush_script_unsupported(void)
+{
+ return 0;
+}
+
static int stop_script_unsupported(void)
{
return 0;
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent
struct scripting_ops python_scripting_unsupported_ops = {
.name = "Python",
.start_script = python_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = python_generate_script_unsupported,
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent
struct scripting_ops perl_scripting_unsupported_ops = {
.name = "Perl",
.start_script = perl_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = perl_generate_script_unsupported,
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 7b6d68688327..52aaa19e1eb1 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -64,6 +64,7 @@ struct perf_session;
struct scripting_ops {
const char *name;
int (*start_script) (const char *script, int argc, const char **argv);
+ int (*flush_script) (void);
int (*stop_script) (void);
void (*process_event) (union perf_event *event,
struct perf_sample *sample,
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 7419768c38b1..2dcfe9a7c8d0 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -26,7 +26,7 @@ static int __report_module(struct addr_location *al, u64 ip,
Dwfl_Module *mod;
struct dso *dso = NULL;
- thread__find_addr_location(ui->thread, ui->machine,
+ thread__find_addr_location(ui->thread,
PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, al);
@@ -89,7 +89,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
struct addr_location al;
ssize_t size;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al);
if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@@ -164,14 +164,14 @@ frame_callback(Dwfl_Frame *state, void *arg)
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine, struct thread *thread,
+ struct thread *thread,
struct perf_sample *data,
int max_stack)
{
struct unwind_info ui = {
.sample = data,
.thread = thread,
- .machine = machine,
+ .machine = thread->mg->machine,
.cb = cb,
.arg = arg,
.max_stack = max_stack,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 92b56db52471..371219a6daf1 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -24,6 +24,7 @@
#include <linux/list.h>
#include <libunwind.h>
#include <libunwind-ptrace.h>
+#include "callchain.h"
#include "thread.h"
#include "session.h"
#include "perf_regs.h"
@@ -283,7 +284,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
{
struct addr_location al;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
return al.map;
}
@@ -373,7 +374,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
struct addr_location al;
ssize_t size;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al);
if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@@ -475,14 +476,13 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as,
pr_debug("unwind: put_unwind_info called\n");
}
-static int entry(u64 ip, struct thread *thread, struct machine *machine,
+static int entry(u64 ip, struct thread *thread,
unwind_entry_cb_t cb, void *arg)
{
struct unwind_entry e;
struct addr_location al;
- thread__find_addr_location(thread, machine,
- PERF_RECORD_MISC_USER,
+ thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
e.ip = ip;
@@ -525,12 +525,12 @@ static unw_accessors_t accessors = {
.get_proc_name = get_proc_name,
};
-static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
- void *arg, int max_stack)
+int unwind__prepare_access(struct thread *thread)
{
unw_addr_space_t addr_space;
- unw_cursor_t c;
- int ret;
+
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return 0;
addr_space = unw_create_addr_space(&accessors, 0);
if (!addr_space) {
@@ -538,6 +538,45 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
return -ENOMEM;
}
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ thread__set_priv(thread, addr_space);
+
+ return 0;
+}
+
+void unwind__flush_access(struct thread *thread)
+{
+ unw_addr_space_t addr_space;
+
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return;
+
+ addr_space = thread__priv(thread);
+ unw_flush_cache(addr_space, 0, 0);
+}
+
+void unwind__finish_access(struct thread *thread)
+{
+ unw_addr_space_t addr_space;
+
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return;
+
+ addr_space = thread__priv(thread);
+ unw_destroy_addr_space(addr_space);
+}
+
+static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
+ void *arg, int max_stack)
+{
+ unw_addr_space_t addr_space;
+ unw_cursor_t c;
+ int ret;
+
+ addr_space = thread__priv(ui->thread);
+ if (addr_space == NULL)
+ return -1;
+
ret = unw_init_remote(&c, addr_space, ui);
if (ret)
display_error(ret);
@@ -546,22 +585,21 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
unw_word_t ip;
unw_get_reg(&c, UNW_REG_IP, &ip);
- ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;
+ ret = ip ? entry(ip, ui->thread, cb, arg) : 0;
}
- unw_destroy_addr_space(addr_space);
return ret;
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine, struct thread *thread,
+ struct thread *thread,
struct perf_sample *data, int max_stack)
{
u64 ip;
struct unwind_info ui = {
.sample = data,
.thread = thread,
- .machine = machine,
+ .machine = thread->mg->machine,
};
int ret;
@@ -572,7 +610,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (ret)
return ret;
- ret = entry(ip, thread, machine, cb, arg);
+ ret = entry(ip, thread, cb, arg);
if (ret)
return -ENOMEM;
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index f03061260b4e..12790cf94618 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include "event.h"
#include "symbol.h"
+#include "thread.h"
struct unwind_entry {
struct map *map;
@@ -15,23 +16,40 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
#ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine,
struct thread *thread,
struct perf_sample *data, int max_stack);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
int libunwind__arch_reg_id(int regnum);
+int unwind__prepare_access(struct thread *thread);
+void unwind__flush_access(struct thread *thread);
+void unwind__finish_access(struct thread *thread);
+#else
+static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
+{
+ return 0;
+}
+
+static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
#endif
#else
static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
void *arg __maybe_unused,
- struct machine *machine __maybe_unused,
struct thread *thread __maybe_unused,
struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused)
{
return 0;
}
+
+static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
+{
+ return 0;
+}
+
+static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index e52e7461911b..d5eab3f3323f 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -13,6 +13,15 @@
#include <limits.h>
#include <byteswap.h>
#include <linux/kernel.h>
+#include <unistd.h>
+#include "callchain.h"
+
+struct callchain_param callchain_param = {
+ .mode = CHAIN_GRAPH_REL,
+ .min_percent = 0.5,
+ .order = ORDER_CALLEE,
+ .key = CCKEY_FUNCTION
+};
/*
* XXX We need to find a better place for these things...
@@ -282,6 +291,18 @@ void get_term_dimensions(struct winsize *ws)
ws->ws_col = 80;
}
+void set_term_quiet_input(struct termios *old)
+{
+ struct termios tc;
+
+ tcgetattr(0, old);
+ tc = *old;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &tc);
+}
+
static void set_tracing_events_path(const char *mountpoint)
{
snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s",
@@ -443,6 +464,7 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep)
size_t size = 0, alloc_size = 0;
void *bf = NULL, *nbf;
int fd, n, err = 0;
+ char sbuf[STRERR_BUFSIZE];
fd = open(filename, O_RDONLY);
if (fd < 0)
@@ -463,8 +485,8 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep)
n = read(fd, bf + size, alloc_size - size);
if (n < 0) {
if (size) {
- pr_warning("read failed %d: %s\n",
- errno, strerror(errno));
+ pr_warning("read failed %d: %s\n", errno,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
err = 0;
} else
err = -errno;
@@ -536,3 +558,39 @@ void mem_bswap_64(void *src, int byte_size)
++m;
}
}
+
+bool find_process(const char *name)
+{
+ size_t len = strlen(name);
+ DIR *dir;
+ struct dirent *d;
+ int ret = -1;
+
+ dir = opendir(procfs__mountpoint());
+ if (!dir)
+ return -1;
+
+ /* Walk through the directory. */
+ while (ret && (d = readdir(dir)) != NULL) {
+ char path[PATH_MAX];
+ char *data;
+ size_t size;
+
+ if ((d->d_type != DT_DIR) ||
+ !strcmp(".", d->d_name) ||
+ !strcmp("..", d->d_name))
+ continue;
+
+ scnprintf(path, sizeof(path), "%s/%s/comm",
+ procfs__mountpoint(), d->d_name);
+
+ if (filename__read_str(path, &data, &size))
+ continue;
+
+ ret = strncmp(name, data, len);
+ free(data);
+ }
+
+ closedir(dir);
+ return ret ? false : true;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 66864364ccb4..419bee030f83 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -39,6 +39,8 @@
#define _ALL_SOURCE 1
#define _BSD_SOURCE 1
+/* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */
+#define _DEFAULT_SOURCE 1
#define HAS_BOOL
#include <unistd.h>
@@ -64,16 +66,18 @@
#include <regex.h>
#include <utime.h>
#include <sys/wait.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <inttypes.h>
+#include <linux/kernel.h>
#include <linux/magic.h>
#include <linux/types.h>
#include <sys/ttydefaults.h>
#include <api/fs/debugfs.h>
#include <termios.h>
#include <linux/bitops.h>
+#include <termios.h>
extern const char *graph_line;
extern const char *graph_dotted_line;
@@ -150,7 +154,6 @@ extern void set_die_routine(void (*routine)(const char *err, va_list params) NOR
extern int prefixcmp(const char *str, const char *prefix);
extern void set_buildid_dir(void);
-extern void disable_buildid_cache(void);
static inline const char *skip_prefix(const char *str, const char *prefix)
{
@@ -307,6 +310,7 @@ extern unsigned int page_size;
extern int cacheline_size;
void get_term_dimensions(struct winsize *ws);
+void set_term_quiet_input(struct termios *old);
struct parse_tag {
char tag;
@@ -317,9 +321,26 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags);
#define SRCLINE_UNKNOWN ((char *) "??:0")
+static inline int path__join(char *bf, size_t size,
+ const char *path1, const char *path2)
+{
+ return scnprintf(bf, size, "%s%s%s", path1, path1[0] ? "/" : "", path2);
+}
+
+static inline int path__join3(char *bf, size_t size,
+ const char *path1, const char *path2,
+ const char *path3)
+{
+ return scnprintf(bf, size, "%s%s%s%s%s",
+ path1, path1[0] ? "/" : "",
+ path2, path2[0] ? "/" : "", path3);
+}
+
struct dso;
+struct symbol;
-char *get_srcline(struct dso *dso, unsigned long addr);
+char *get_srcline(struct dso *dso, unsigned long addr, struct symbol *sym,
+ bool show_sym);
void free_srcline(char *srcline);
int filename__read_int(const char *filename, int *value);
@@ -330,4 +351,10 @@ void mem_bswap_64(void *src, int byte_size);
void mem_bswap_32(void *src, int byte_size);
const char *get_filename_for_perf_kvm(void);
+bool find_process(const char *name);
+
+#ifdef HAVE_ZLIB_SUPPORT
+int gzip_decompress_to_file(const char *input, int output_fd);
+#endif
+
#endif /* GIT_COMPAT_UTIL_H */
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index adca69384fcc..5c7dd796979d 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -12,9 +12,16 @@
#include "util.h"
#include "symbol.h"
#include "machine.h"
+#include "thread.h"
#include "linux/string.h"
#include "debug.h"
+/*
+ * Include definition of find_vdso_map() also used in perf-read-vdso.c for
+ * building perf-read-vdso32 and perf-read-vdsox32.
+ */
+#include "find-vdso-map.c"
+
#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
struct vdso_file {
@@ -22,10 +29,15 @@ struct vdso_file {
bool error;
char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
const char *dso_name;
+ const char *read_prog;
};
struct vdso_info {
struct vdso_file vdso;
+#if BITS_PER_LONG == 64
+ struct vdso_file vdso32;
+ struct vdso_file vdsox32;
+#endif
};
static struct vdso_info *vdso_info__new(void)
@@ -35,42 +47,23 @@ static struct vdso_info *vdso_info__new(void)
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO,
},
+#if BITS_PER_LONG == 64
+ .vdso32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSO32,
+ .read_prog = "perf-read-vdso32",
+ },
+ .vdsox32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSOX32,
+ .read_prog = "perf-read-vdsox32",
+ },
+#endif
};
return memdup(&vdso_info_init, sizeof(vdso_info_init));
}
-static int find_vdso_map(void **start, void **end)
-{
- FILE *maps;
- char line[128];
- int found = 0;
-
- maps = fopen("/proc/self/maps", "r");
- if (!maps) {
- pr_err("vdso: cannot open maps\n");
- return -1;
- }
-
- while (!found && fgets(line, sizeof(line), maps)) {
- int m = -1;
-
- /* We care only about private r-x mappings. */
- if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
- start, end, &m))
- continue;
- if (m < 0)
- continue;
-
- if (!strncmp(&line[m], VDSO__MAP_NAME,
- sizeof(VDSO__MAP_NAME) - 1))
- found = 1;
- }
-
- fclose(maps);
- return !found;
-}
-
static char *get_file(struct vdso_file *vdso_file)
{
char *vdso = NULL;
@@ -117,6 +110,12 @@ void vdso__exit(struct machine *machine)
if (vdso_info->vdso.found)
unlink(vdso_info->vdso.temp_file_name);
+#if BITS_PER_LONG == 64
+ if (vdso_info->vdso32.found)
+ unlink(vdso_info->vdso32.temp_file_name);
+ if (vdso_info->vdsox32.found)
+ unlink(vdso_info->vdsox32.temp_file_name);
+#endif
zfree(&machine->vdso_info);
}
@@ -135,6 +134,153 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,
return dso;
}
+#if BITS_PER_LONG == 64
+
+static enum dso_type machine__thread_dso_type(struct machine *machine,
+ struct thread *thread)
+{
+ enum dso_type dso_type = DSO__TYPE_UNKNOWN;
+ struct map *map;
+ struct dso *dso;
+
+ map = map_groups__first(thread->mg, MAP__FUNCTION);
+ for (; map ; map = map_groups__next(map)) {
+ dso = map->dso;
+ if (!dso || dso->long_name[0] != '/')
+ continue;
+ dso_type = dso__type(dso, machine);
+ if (dso_type != DSO__TYPE_UNKNOWN)
+ break;
+ }
+
+ return dso_type;
+}
+
+static int vdso__do_copy_compat(FILE *f, int fd)
+{
+ char buf[4096];
+ size_t count;
+
+ while (1) {
+ count = fread(buf, 1, sizeof(buf), f);
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
+ break;
+ if (count && writen(fd, buf, count) != (ssize_t)count)
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int vdso__copy_compat(const char *prog, int fd)
+{
+ FILE *f;
+ int err;
+
+ f = popen(prog, "r");
+ if (!f)
+ return -errno;
+
+ err = vdso__do_copy_compat(f, fd);
+
+ if (pclose(f) == -1)
+ return -errno;
+
+ return err;
+}
+
+static int vdso__create_compat_file(const char *prog, char *temp_name)
+{
+ int fd, err;
+
+ fd = mkstemp(temp_name);
+ if (fd < 0)
+ return -errno;
+
+ err = vdso__copy_compat(prog, fd);
+
+ if (close(fd) == -1)
+ return -errno;
+
+ return err;
+}
+
+static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
+{
+ int err;
+
+ if (vdso_file->found)
+ return vdso_file->temp_file_name;
+
+ if (vdso_file->error)
+ return NULL;
+
+ err = vdso__create_compat_file(vdso_file->read_prog,
+ vdso_file->temp_file_name);
+ if (err) {
+ pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
+ vdso_file->error = true;
+ return NULL;
+ }
+
+ vdso_file->found = true;
+
+ return vdso_file->temp_file_name;
+}
+
+static struct dso *vdso__findnew_compat(struct machine *machine,
+ struct vdso_file *vdso_file)
+{
+ const char *file_name;
+ struct dso *dso;
+
+ dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true);
+ if (dso)
+ return dso;
+
+ file_name = vdso__get_compat_file(vdso_file);
+ if (!file_name)
+ return NULL;
+
+ return vdso__new(machine, vdso_file->dso_name, file_name);
+}
+
+static int vdso__dso_findnew_compat(struct machine *machine,
+ struct thread *thread,
+ struct vdso_info *vdso_info,
+ struct dso **dso)
+{
+ enum dso_type dso_type;
+
+ dso_type = machine__thread_dso_type(machine, thread);
+
+#ifndef HAVE_PERF_READ_VDSO32
+ if (dso_type == DSO__TYPE_32BIT)
+ return 0;
+#endif
+#ifndef HAVE_PERF_READ_VDSOX32
+ if (dso_type == DSO__TYPE_X32BIT)
+ return 0;
+#endif
+
+ switch (dso_type) {
+ case DSO__TYPE_32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdso32);
+ return 1;
+ case DSO__TYPE_X32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32);
+ return 1;
+ case DSO__TYPE_UNKNOWN:
+ case DSO__TYPE_64BIT:
+ default:
+ return 0;
+ }
+}
+
+#endif
+
struct dso *vdso__dso_findnew(struct machine *machine,
struct thread *thread __maybe_unused)
{
@@ -148,6 +294,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,
if (!vdso_info)
return NULL;
+#if BITS_PER_LONG == 64
+ if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso))
+ return dso;
+#endif
+
dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
if (!dso) {
char *file;
@@ -164,5 +315,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,
bool dso__is_vdso(struct dso *dso)
{
- return !strcmp(dso->short_name, DSO__NAME_VDSO);
+ return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSOX32);
}
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index af9d6929a215..d97da1616f0c 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -7,7 +7,9 @@
#define VDSO__MAP_NAME "[vdso]"
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO32 "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
static inline bool is_vdso_map(const char *filename)
{
diff --git a/tools/perf/util/zlib.c b/tools/perf/util/zlib.c
new file mode 100644
index 000000000000..495a449fc25c
--- /dev/null
+++ b/tools/perf/util/zlib.c
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <zlib.h>
+
+#include "util/util.h"
+#include "util/debug.h"
+
+
+#define CHUNK_SIZE 16384
+
+int gzip_decompress_to_file(const char *input, int output_fd)
+{
+ int ret = Z_STREAM_ERROR;
+ int input_fd;
+ void *ptr;
+ int len;
+ struct stat stbuf;
+ unsigned char buf[CHUNK_SIZE];
+ z_stream zs = {
+ .zalloc = Z_NULL,
+ .zfree = Z_NULL,
+ .opaque = Z_NULL,
+ .avail_in = 0,
+ .next_in = Z_NULL,
+ };
+
+ input_fd = open(input, O_RDONLY);
+ if (input_fd < 0)
+ return -1;
+
+ if (fstat(input_fd, &stbuf) < 0)
+ goto out_close;
+
+ ptr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
+ if (ptr == MAP_FAILED)
+ goto out_close;
+
+ if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK)
+ goto out_unmap;
+
+ zs.next_in = ptr;
+ zs.avail_in = stbuf.st_size;
+
+ do {
+ zs.next_out = buf;
+ zs.avail_out = CHUNK_SIZE;
+
+ ret = inflate(&zs, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ /* fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ goto out;
+ default:
+ break;
+ }
+
+ len = CHUNK_SIZE - zs.avail_out;
+ if (writen(output_fd, buf, len) != len) {
+ ret = Z_DATA_ERROR;
+ goto out;
+ }
+
+ } while (ret != Z_STREAM_END);
+
+out:
+ inflateEnd(&zs);
+out_unmap:
+ munmap(ptr, stbuf.st_size);
+out_close:
+ close(input_fd);
+
+ return ret == Z_STREAM_END ? 0 : -1;
+}
diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c
index 60b58cd18410..7ccb073f8316 100644
--- a/tools/power/acpi/os_specific/service_layers/osunixxf.c
+++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c
@@ -122,6 +122,14 @@ static void os_enter_line_edit_mode(void)
{
struct termios local_term_attributes;
+ term_attributes_were_set = 0;
+
+ /* STDIN must be a terminal */
+
+ if (!isatty(STDIN_FILENO)) {
+ return;
+ }
+
/* Get and keep the original attributes */
if (tcgetattr(STDIN_FILENO, &original_term_attributes)) {
diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c
index 53cee781e24e..24d32968802d 100644
--- a/tools/power/acpi/tools/acpidump/apdump.c
+++ b/tools/power/acpi/tools/acpidump/apdump.c
@@ -146,7 +146,7 @@ u32 ap_get_table_length(struct acpi_table_header *table)
if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
- return (rsdp->length);
+ return (acpi_tb_get_rsdp_length(rsdp));
}
/* Normal ACPI table */
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index 55ab700f6ba5..bf1398180785 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -194,6 +194,7 @@ my $config_bisect_check;
my $patchcheck_type;
my $patchcheck_start;
+my $patchcheck_cherry;
my $patchcheck_end;
# set when a test is something other that just building or install
@@ -320,6 +321,7 @@ my %option_map = (
"PATCHCHECK_TYPE" => \$patchcheck_type,
"PATCHCHECK_START" => \$patchcheck_start,
+ "PATCHCHECK_CHERRY" => \$patchcheck_cherry,
"PATCHCHECK_END" => \$patchcheck_end,
);
@@ -1448,6 +1450,12 @@ sub wait_for_monitor {
}
}
print "** Monitor flushed **\n";
+
+ # if stop is defined but wasn't hit, return error
+ # used by reboot (which wants to see a reboot)
+ if (defined($stop) && !$booted) {
+ $bug = 1;
+ }
return $bug;
}
@@ -2336,15 +2344,17 @@ sub success {
sub answer_bisect {
for (;;) {
- doprint "Pass or fail? [p/f]";
+ doprint "Pass, fail, or skip? [p/f/s]";
my $ans = <STDIN>;
chomp $ans;
if ($ans eq "p" || $ans eq "P") {
return 1;
} elsif ($ans eq "f" || $ans eq "F") {
return 0;
+ } elsif ($ans eq "s" || $ans eq "S") {
+ return -1;
} else {
- print "Please answer 'P' or 'F'\n";
+ print "Please answer 'p', 'f', or 's'\n";
}
}
}
@@ -2726,15 +2736,17 @@ sub bisect {
run_command "git bisect start$start_files" or
dodie "could not start bisect";
- run_command "git bisect good $good" or
- dodie "could not set bisect good to $good";
-
- run_git_bisect "git bisect bad $bad" or
- dodie "could not set bisect bad to $bad";
-
if (defined($replay)) {
run_command "git bisect replay $replay" or
dodie "failed to run replay";
+ } else {
+
+ run_command "git bisect good $good" or
+ dodie "could not set bisect good to $good";
+
+ run_git_bisect "git bisect bad $bad" or
+ dodie "could not set bisect bad to $bad";
+
}
if (defined($start)) {
@@ -3181,9 +3193,16 @@ sub patchcheck {
my $start = $patchcheck_start;
+ my $cherry = $patchcheck_cherry;
+ if (!defined($cherry)) {
+ $cherry = 0;
+ }
+
my $end = "HEAD";
if (defined($patchcheck_end)) {
$end = $patchcheck_end;
+ } elsif ($cherry) {
+ die "PATCHCHECK_END must be defined with PATCHCHECK_CHERRY\n";
}
# Get the true sha1's since we can use things like HEAD~3
@@ -3197,24 +3216,38 @@ sub patchcheck {
$type = "boot";
}
- open (IN, "git log --pretty=oneline $end|") or
- dodie "could not get git list";
+ if ($cherry) {
+ open (IN, "git cherry -v $start $end|") or
+ dodie "could not get git list";
+ } else {
+ open (IN, "git log --pretty=oneline $end|") or
+ dodie "could not get git list";
+ }
my @list;
while (<IN>) {
chomp;
+ # git cherry adds a '+' we want to remove
+ s/^\+ //;
$list[$#list+1] = $_;
last if (/^$start/);
}
close(IN);
- if ($list[$#list] !~ /^$start/) {
- fail "SHA1 $start not found";
+ if (!$cherry) {
+ if ($list[$#list] !~ /^$start/) {
+ fail "SHA1 $start not found";
+ }
+
+ # go backwards in the list
+ @list = reverse @list;
}
- # go backwards in the list
- @list = reverse @list;
+ doprint("Going to test the following commits:\n");
+ foreach my $l (@list) {
+ doprint "$l\n";
+ }
my $save_clean = $noclean;
my %ignored_warnings;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 911e45ad657a..6c58cd8bbbae 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -906,6 +906,16 @@
#
# PATCHCHECK_END is the last patch to check (default HEAD)
#
+# PATCHCHECK_CHERRY if set to non zero, then git cherry will be
+# performed against PATCHCHECK_START and PATCHCHECK_END. That is
+#
+# git cherry ${PATCHCHECK_START} ${PATCHCHECK_END}
+#
+# Then the changes found will be tested.
+#
+# Note, PATCHCHECK_CHERRY requires PATCHCHECK_END to be defined.
+# (default 0)
+#
# PATCHCHECK_TYPE is required and is the type of test to run:
# build, boot, test.
#
diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile
index ad4ab01cd28f..b80cd10d53ba 100644
--- a/tools/testing/selftests/memfd/Makefile
+++ b/tools/testing/selftests/memfd/Makefile
@@ -1,38 +1,17 @@
-uname_M := $(shell uname -m 2>/dev/null || echo not)
-ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
-ifeq ($(ARCH),i386)
- ARCH := x86
-endif
-ifeq ($(ARCH),x86_64)
- ARCH := x86
-endif
-
CFLAGS += -D_FILE_OFFSET_BITS=64
-CFLAGS += -I../../../../arch/x86/include/generated/uapi/
-CFLAGS += -I../../../../arch/x86/include/uapi/
CFLAGS += -I../../../../include/uapi/
CFLAGS += -I../../../../include/
all:
-ifeq ($(ARCH),x86)
gcc $(CFLAGS) memfd_test.c -o memfd_test
-else
- echo "Not an x86 target, can't build memfd selftest"
-endif
run_tests: all
-ifeq ($(ARCH),x86)
gcc $(CFLAGS) memfd_test.c -o memfd_test
-endif
@./memfd_test || echo "memfd_test: [FAIL]"
build_fuse:
-ifeq ($(ARCH),x86)
gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt
gcc $(CFLAGS) fuse_test.c -o fuse_test
-else
- echo "Not an x86 target, can't build memfd selftest"
-endif
run_fuse: build_fuse
@./run_fuse_test.sh || echo "fuse_test: [FAIL]"
diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c
index 3634c909b1b0..0b9eafb7ab7b 100644
--- a/tools/testing/selftests/memfd/memfd_test.c
+++ b/tools/testing/selftests/memfd/memfd_test.c
@@ -59,9 +59,9 @@ static void mfd_fail_new(const char *name, unsigned int flags)
}
}
-static __u64 mfd_assert_get_seals(int fd)
+static unsigned int mfd_assert_get_seals(int fd)
{
- long r;
+ int r;
r = fcntl(fd, F_GET_SEALS);
if (r < 0) {
@@ -69,50 +69,48 @@ static __u64 mfd_assert_get_seals(int fd)
abort();
}
- return r;
+ return (unsigned int)r;
}
-static void mfd_assert_has_seals(int fd, __u64 seals)
+static void mfd_assert_has_seals(int fd, unsigned int seals)
{
- __u64 s;
+ unsigned int s;
s = mfd_assert_get_seals(fd);
if (s != seals) {
- printf("%llu != %llu = GET_SEALS(%d)\n",
- (unsigned long long)seals, (unsigned long long)s, fd);
+ printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd);
abort();
}
}
-static void mfd_assert_add_seals(int fd, __u64 seals)
+static void mfd_assert_add_seals(int fd, unsigned int seals)
{
- long r;
- __u64 s;
+ int r;
+ unsigned int s;
s = mfd_assert_get_seals(fd);
r = fcntl(fd, F_ADD_SEALS, seals);
if (r < 0) {
- printf("ADD_SEALS(%d, %llu -> %llu) failed: %m\n",
- fd, (unsigned long long)s, (unsigned long long)seals);
+ printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals);
abort();
}
}
-static void mfd_fail_add_seals(int fd, __u64 seals)
+static void mfd_fail_add_seals(int fd, unsigned int seals)
{
- long r;
- __u64 s;
+ int r;
+ unsigned int s;
r = fcntl(fd, F_GET_SEALS);
if (r < 0)
s = 0;
else
- s = r;
+ s = (unsigned int)r;
r = fcntl(fd, F_ADD_SEALS, seals);
if (r >= 0) {
- printf("ADD_SEALS(%d, %llu -> %llu) didn't fail as expected\n",
- fd, (unsigned long long)s, (unsigned long long)seals);
+ printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n",
+ fd, s, seals);
abort();
}
}
@@ -205,7 +203,7 @@ static void mfd_fail_open(int fd, int flags, mode_t mode)
sprintf(buf, "/proc/self/fd/%d", fd);
r = open(buf, flags, mode);
if (r >= 0) {
- printf("open(%s) didn't fail as expected\n");
+ printf("open(%s) didn't fail as expected\n", buf);
abort();
}
}
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index 57b9c2b7c4ff..6f6733331d95 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -128,7 +128,7 @@ static int sock_fanout_read_ring(int fd, void *ring)
struct tpacket2_hdr *header = ring;
int count = 0;
- while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) {
+ while (count < RING_NUM_FRAMES && header->tp_status & TP_STATUS_USER) {
count++;
header = ring + (count * getpagesize());
}
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 74a78cedce37..f6ff90a76bd7 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
export CC CFLAGS
-TARGETS = pmu copyloops mm tm
+TARGETS = pmu copyloops mm tm primitives
endif
diff --git a/tools/testing/selftests/powerpc/primitives/Makefile b/tools/testing/selftests/powerpc/primitives/Makefile
new file mode 100644
index 000000000000..ea737ca01732
--- /dev/null
+++ b/tools/testing/selftests/powerpc/primitives/Makefile
@@ -0,0 +1,17 @@
+CFLAGS += -I$(CURDIR)
+
+PROGS := load_unaligned_zeropad
+
+all: $(PROGS)
+
+$(PROGS): ../harness.c
+
+run_tests: all
+ @-for PROG in $(PROGS); do \
+ ./$$PROG; \
+ done;
+
+clean:
+ rm -f $(PROGS) *.o
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h b/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h
new file mode 120000
index 000000000000..b14255e15a25
--- /dev/null
+++ b/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h
@@ -0,0 +1 @@
+../.././../../../../arch/powerpc/include/asm/asm-compat.h \ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h b/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
new file mode 100644
index 000000000000..d1b647509596
--- /dev/null
+++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
@@ -0,0 +1,147 @@
+/*
+ * Userspace test harness for load_unaligned_zeropad. Creates two
+ * pages and uses mprotect to prevent access to the second page and
+ * a SEGV handler that walks the exception tables and runs the fixup
+ * routine.
+ *
+ * The results are compared against a normal load that is that is
+ * performed while access to the second page is enabled via mprotect.
+ *
+ * Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#define FIXUP_SECTION ".ex_fixup"
+
+#include "word-at-a-time.h"
+
+#include "utils.h"
+
+
+static int page_size;
+static char *mem_region;
+
+static int protect_region(void)
+{
+ if (mprotect(mem_region + page_size, page_size, PROT_NONE)) {
+ perror("mprotect");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int unprotect_region(void)
+{
+ if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) {
+ perror("mprotect");
+ return 1;
+ }
+
+ return 0;
+}
+
+extern char __start___ex_table[];
+extern char __stop___ex_table[];
+
+#if defined(__powerpc64__)
+#define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP]
+#elif defined(__powerpc__)
+#define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP]
+#else
+#error implement UCONTEXT_NIA
+#endif
+
+static int segv_error;
+
+static void segv_handler(int signr, siginfo_t *info, void *ptr)
+{
+ ucontext_t *uc = (ucontext_t *)ptr;
+ unsigned long addr = (unsigned long)info->si_addr;
+ unsigned long *ip = &UCONTEXT_NIA(uc);
+ unsigned long *ex_p = (unsigned long *)__start___ex_table;
+
+ while (ex_p < (unsigned long *)__stop___ex_table) {
+ unsigned long insn, fixup;
+
+ insn = *ex_p++;
+ fixup = *ex_p++;
+
+ if (insn == *ip) {
+ *ip = fixup;
+ return;
+ }
+ }
+
+ printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr);
+ segv_error++;
+}
+
+static void setup_segv_handler(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_sigaction = segv_handler;
+ action.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &action, NULL);
+}
+
+static int do_one_test(char *p, int page_offset)
+{
+ unsigned long should;
+ unsigned long got;
+
+ FAIL_IF(unprotect_region());
+ should = *(unsigned long *)p;
+ FAIL_IF(protect_region());
+
+ got = load_unaligned_zeropad(p);
+
+ if (should != got)
+ printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should);
+
+ return 0;
+}
+
+static int test_body(void)
+{
+ unsigned long i;
+
+ page_size = getpagesize();
+ mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+
+ FAIL_IF(mem_region == MAP_FAILED);
+
+ for (i = 0; i < page_size; i++)
+ mem_region[i] = i;
+
+ memset(mem_region+page_size, 0, page_size);
+
+ setup_segv_handler();
+
+ for (i = 0; i < page_size; i++)
+ FAIL_IF(do_one_test(mem_region+i, i));
+
+ FAIL_IF(segv_error);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(test_body, "load_unaligned_zeropad");
+}
diff --git a/tools/testing/selftests/powerpc/primitives/word-at-a-time.h b/tools/testing/selftests/powerpc/primitives/word-at-a-time.h
new file mode 120000
index 000000000000..eb74401b591f
--- /dev/null
+++ b/tools/testing/selftests/powerpc/primitives/word-at-a-time.h
@@ -0,0 +1 @@
+../../../../../arch/powerpc/include/asm/word-at-a-time.h \ No newline at end of file
diff --git a/tools/testing/selftests/rcutorture/bin/config2frag.sh b/tools/testing/selftests/rcutorture/bin/config2frag.sh
index 9f9ffcd427d3..56f51ae13d73 100644..100755
--- a/tools/testing/selftests/rcutorture/bin/config2frag.sh
+++ b/tools/testing/selftests/rcutorture/bin/config2frag.sh
@@ -1,5 +1,5 @@
-#!/bin/sh
-# Usage: sh config2frag.sh < .config > configfrag
+#!/bin/bash
+# Usage: config2frag.sh < .config > configfrag
#
# Converts the "# CONFIG_XXX is not set" to "CONFIG_XXX=n" so that the
# resulting file becomes a legitimate Kconfig fragment.
diff --git a/tools/testing/selftests/rcutorture/bin/configcheck.sh b/tools/testing/selftests/rcutorture/bin/configcheck.sh
index d686537dd55c..eee31e261bf7 100755
--- a/tools/testing/selftests/rcutorture/bin/configcheck.sh
+++ b/tools/testing/selftests/rcutorture/bin/configcheck.sh
@@ -1,5 +1,5 @@
-#!/bin/sh
-# Usage: sh configcheck.sh .config .config-template
+#!/bin/bash
+# Usage: configcheck.sh .config .config-template
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh
index 9c3f3d39b934..15f1a17ca96e 100755
--- a/tools/testing/selftests/rcutorture/bin/configinit.sh
+++ b/tools/testing/selftests/rcutorture/bin/configinit.sh
@@ -1,6 +1,6 @@
-#!/bin/sh
+#!/bin/bash
#
-# sh configinit.sh config-spec-file [ build output dir ]
+# Usage: configinit.sh config-spec-file [ build output dir ]
#
# Create a .config file from the spec file. Run from the kernel source tree.
# Exits with 0 if all went well, with 1 if all went well but the config
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh
index d01b865bb100..b325470c01b3 100644
--- a/tools/testing/selftests/rcutorture/bin/functions.sh
+++ b/tools/testing/selftests/rcutorture/bin/functions.sh
@@ -64,6 +64,26 @@ configfrag_boot_params () {
fi
}
+# configfrag_boot_cpus bootparam-string config-fragment-file config-cpus
+#
+# Decreases number of CPUs based on any maxcpus= boot parameters specified.
+configfrag_boot_cpus () {
+ local bootargs="`configfrag_boot_params "$1" "$2"`"
+ local maxcpus
+ if echo "${bootargs}" | grep -q 'maxcpus=[0-9]'
+ then
+ maxcpus="`echo "${bootargs}" | sed -e 's/^.*maxcpus=\([0-9]*\).*$/\1/'`"
+ if test "$3" -gt "$maxcpus"
+ then
+ echo $maxcpus
+ else
+ echo $3
+ fi
+ else
+ echo $3
+ fi
+}
+
# configfrag_hotplug_cpu config-fragment-file
#
# Returns 1 if the config fragment specifies hotplug CPU.
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
index 7c1e56b46de4..00cb0db2643d 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
@@ -2,7 +2,7 @@
#
# Build a kvm-ready Linux kernel from the tree in the current directory.
#
-# Usage: sh kvm-build.sh config-template build-dir more-configs
+# Usage: kvm-build.sh config-template build-dir more-configs
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
index 7f1ff1a8fc4b..43f764098e50 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
@@ -2,7 +2,7 @@
#
# Analyze a given results directory for locktorture progress.
#
-# Usage: sh kvm-recheck-lock.sh resdir
+# Usage: kvm-recheck-lock.sh resdir
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
index 307c4b95f325..d6cc07fc137f 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
@@ -2,7 +2,7 @@
#
# Analyze a given results directory for rcutorture progress.
#
-# Usage: sh kvm-recheck-rcu.sh resdir
+# Usage: kvm-recheck-rcu.sh resdir
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
index 3f6c9b78d177..4f5b20f367a9 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
@@ -4,7 +4,7 @@
# check the build and console output for errors. Given a directory
# containing results directories, this recursively checks them all.
#
-# Usage: sh kvm-recheck.sh resdir ...
+# Usage: kvm-recheck.sh resdir ...
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
index 0f69dcbf9def..8ca9f21f2efc 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
@@ -6,7 +6,7 @@
# Execute this in the source tree. Do not run it as a background task
# because qemu does not seem to like that much.
#
-# Usage: sh kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args
+# Usage: kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args
#
# qemu-args defaults to "-nographic", along with arguments specifying the
# number of CPUs and other options generated from
@@ -45,7 +45,7 @@ trap 'rm -rf $T' 0
touch $T
. $KVM/bin/functions.sh
-. $KVPATH/ver_functions.sh
+. $CONFIGFRAG/ver_functions.sh
config_template=${1}
config_dir=`echo $config_template | sed -e 's,/[^/]*$,,'`
@@ -140,6 +140,7 @@ fi
# Generate -smp qemu argument.
qemu_args="-nographic $qemu_args"
cpu_count=`configNR_CPUS.sh $config_template`
+cpu_count=`configfrag_boot_cpus "$boot_args" "$config_template" "$cpu_count"`
vcpus=`identify_qemu_vcpus`
if test $cpu_count -gt $vcpus
then
@@ -167,8 +168,8 @@ then
touch $resdir/buildonly
exit 0
fi
-echo $QEMU $qemu_args -m 512 -kernel $builddir/$BOOT_IMAGE -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
-( $QEMU $qemu_args -m 512 -kernel $builddir/$BOOT_IMAGE -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) &
+echo $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
+( $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) &
qemu_pid=$!
commandcompleted=0
echo Monitoring qemu job at pid $qemu_pid
@@ -214,7 +215,7 @@ then
fi
if test $kruntime -ge $((seconds + grace))
then
- echo "!!! Hang at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1
+ echo "!!! PID $qemu_pid hung at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1
kill -KILL $qemu_pid
break
fi
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh
index 589e9c38413b..368d64ac779e 100644..100755
--- a/tools/testing/selftests/rcutorture/bin/kvm.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm.sh
@@ -7,7 +7,7 @@
# Edit the definitions below to set the locations of the various directories,
# as well as the test duration.
#
-# Usage: sh kvm.sh [ options ]
+# Usage: kvm.sh [ options ]
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -47,7 +47,6 @@ resdir=""
configs=""
cpus=0
ds=`date +%Y.%m.%d-%H:%M:%S`
-kversion=""
. functions.sh
@@ -64,7 +63,6 @@ usage () {
echo " --duration minutes"
echo " --interactive"
echo " --kmake-arg kernel-make-arguments"
- echo " --kversion vN.NN"
echo " --mac nn:nn:nn:nn:nn:nn"
echo " --no-initrd"
echo " --qemu-args qemu-system-..."
@@ -128,11 +126,6 @@ do
TORTURE_KMAKE_ARG="$2"
shift
;;
- --kversion)
- checkarg --kversion "(kernel version)" $# "$2" '^v[0-9.]*$' '^error'
- kversion=$2
- shift
- ;;
--mac)
checkarg --mac "(MAC address)" $# "$2" '^\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}$' error
TORTURE_QEMU_MAC=$2
@@ -170,11 +163,10 @@ do
done
CONFIGFRAG=${KVM}/configs/${TORTURE_SUITE}; export CONFIGFRAG
-KVPATH=${CONFIGFRAG}/$kversion; export KVPATH
if test -z "$configs"
then
- configs="`cat $CONFIGFRAG/$kversion/CFLIST`"
+ configs="`cat $CONFIGFRAG/CFLIST`"
fi
if test -z "$resdir"
@@ -186,9 +178,11 @@ fi
touch $T/cfgcpu
for CF in $configs
do
- if test -f "$CONFIGFRAG/$kversion/$CF"
+ if test -f "$CONFIGFRAG/$CF"
then
- echo $CF `configNR_CPUS.sh $CONFIGFRAG/$kversion/$CF` >> $T/cfgcpu
+ cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF`
+ cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF" "$cpu_count"`
+ echo $CF $cpu_count >> $T/cfgcpu
else
echo "The --configs file $CF does not exist, terminating."
exit 1
@@ -250,7 +244,6 @@ END {
cat << ___EOF___ > $T/script
CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG
KVM="$KVM"; export KVM
-KVPATH="$KVPATH"; export KVPATH
PATH="$PATH"; export PATH
TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE
TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
@@ -283,7 +276,7 @@ then
fi
___EOF___
awk < $T/cfgcpu.pack \
- -v CONFIGDIR="$CONFIGFRAG/$kversion/" \
+ -v CONFIGDIR="$CONFIGFRAG/" \
-v KVM="$KVM" \
-v ncpus=$cpus \
-v rd=$resdir/$ds/ \
diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh
index 543230951c38..499d1e598e42 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-build.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Check the build output from an rcutorture run for goodness.
# The "file" is a pathname on the local system, and "title" is
@@ -6,8 +6,7 @@
#
# The file must contain kernel build output.
#
-# Usage:
-# sh parse-build.sh file title
+# Usage: parse-build.sh file title
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh
index 4185d4cab32e..f962ba4cf68b 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-console.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh
@@ -1,11 +1,10 @@
-#!/bin/sh
+#!/bin/bash
#
# Check the console output from an rcutorture run for oopses.
# The "file" is a pathname on the local system, and "title" is
# a text string for error-message purposes.
#
-# Usage:
-# sh parse-console.sh file title
+# Usage: parse-console.sh file title
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -33,6 +32,10 @@ title="$2"
. functions.sh
+if grep -Pq '\x00' < $file
+then
+ print_warning Console output contains nul bytes, old qemu still running?
+fi
egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T
if test -s $T
then
diff --git a/tools/testing/selftests/rcutorture/bin/parse-torture.sh b/tools/testing/selftests/rcutorture/bin/parse-torture.sh
index 3455560ab4e4..e3c5f0705696 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-torture.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-torture.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Check the console output from a torture run for goodness.
# The "file" is a pathname on the local system, and "title" is
@@ -7,8 +7,7 @@
# The file must contain torture output, but can be interspersed
# with other dmesg text, as in console-log output.
#
-# Usage:
-# sh parse-torture.sh file title
+# Usage: parse-torture.sh file title
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tools/testing/selftests/rcutorture/configs/lock/CFLIST b/tools/testing/selftests/rcutorture/configs/lock/CFLIST
index a061b22d1892..6910b7370761 100644
--- a/tools/testing/selftests/rcutorture/configs/lock/CFLIST
+++ b/tools/testing/selftests/rcutorture/configs/lock/CFLIST
@@ -1 +1,4 @@
LOCK01
+LOCK02
+LOCK03
+LOCK04 \ No newline at end of file
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK02 b/tools/testing/selftests/rcutorture/configs/lock/LOCK02
new file mode 100644
index 000000000000..1d1da1477fc3
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK02
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot
new file mode 100644
index 000000000000..5aa44b4f1b51
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot
@@ -0,0 +1 @@
+locktorture.torture_type=mutex_lock
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK03 b/tools/testing/selftests/rcutorture/configs/lock/LOCK03
new file mode 100644
index 000000000000..1d1da1477fc3
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK03
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot
new file mode 100644
index 000000000000..a67bbe0245c9
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot
@@ -0,0 +1 @@
+locktorture.torture_type=rwsem_lock
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK04 b/tools/testing/selftests/rcutorture/configs/lock/LOCK04
new file mode 100644
index 000000000000..1d1da1477fc3
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK04
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot
new file mode 100644
index 000000000000..48c04fe47fb4
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot
@@ -0,0 +1 @@
+locktorture.torture_type=rw_lock
diff --git a/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh
index 9746ea1cd6c7..252aae618984 100644
--- a/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh
+++ b/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh
@@ -38,6 +38,6 @@ per_version_boot_params () {
echo $1 `locktorture_param_onoff "$1" "$2"` \
locktorture.stat_interval=15 \
locktorture.shutdown_secs=$3 \
- locktorture.locktorture_runnable=1 \
+ locktorture.torture_runnable=1 \
locktorture.verbose=1
}
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST
index cd3d29cb0a47..a3a1a05a2b5c 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST
+++ b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST
@@ -11,3 +11,6 @@ SRCU-N
SRCU-P
TINY01
TINY02
+TASKS01
+TASKS02
+TASKS03
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS01 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01
new file mode 100644
index 000000000000..97f0a0b27ef7
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01
@@ -0,0 +1,9 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_RCU=y
+CONFIG_TASKS_RCU=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot
new file mode 100644
index 000000000000..cd2a188eeb6d
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS02 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02
new file mode 100644
index 000000000000..696d2ea74d13
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02
@@ -0,0 +1,5 @@
+CONFIG_SMP=n
+CONFIG_PREEMPT_NONE=y
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=n
+CONFIG_TASKS_RCU=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot
new file mode 100644
index 000000000000..cd2a188eeb6d
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS03 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03
new file mode 100644
index 000000000000..9c60da5b5d1d
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03
@@ -0,0 +1,13 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_HOTPLUG_CPU=n
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+CONFIG_TASKS_RCU=y
+CONFIG_HZ_PERIODIC=n
+CONFIG_NO_HZ_IDLE=n
+CONFIG_NO_HZ_FULL=y
+CONFIG_NO_HZ_FULL_ALL=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot
new file mode 100644
index 000000000000..cd2a188eeb6d
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 b/tools/testing/selftests/rcutorture/configs/rcu/TINY02
index f4feaee40776..36e41df3d27a 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TINY02
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY02
@@ -7,6 +7,8 @@ CONFIG_HZ_PERIODIC=y
CONFIG_NO_HZ_IDLE=n
CONFIG_NO_HZ_FULL=n
CONFIG_RCU_TRACE=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_PROVE_RCU=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_PREEMPT_COUNT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot b/tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot
new file mode 100644
index 000000000000..0f0802730014
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot
@@ -0,0 +1,2 @@
+rcupdate.rcu_self_test=1
+rcupdate.rcu_self_test_bh=1
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 b/tools/testing/selftests/rcutorture/configs/rcu/TREE01
index 063b7079c621..f8a10a7500c6 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01
@@ -1,21 +1,18 @@
CONFIG_SMP=y
-CONFIG_NR_CPUS=8
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_TRACE=y
CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_FANOUT=8
-CONFIG_RCU_FANOUT_EXACT=n
+CONFIG_MAXSMP=y
CONFIG_RCU_NOCB_CPU=y
CONFIG_RCU_NOCB_CPU_ZERO=y
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
index 0fc8a3428938..adc3abc82fb8 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
@@ -1 +1 @@
-rcutorture.torture_type=rcu_bh
+rcutorture.torture_type=rcu_bh maxcpus=8
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 b/tools/testing/selftests/rcutorture/configs/rcu/TREE02
index ea119ba2f7d4..629122fb8b4a 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=8
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
@@ -19,6 +19,5 @@ CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=y
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T b/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
index 19cf9485f48a..a25de47888a4 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=8
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
@@ -19,6 +19,5 @@ CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=y
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
index f4567fb3e332..53f24e0a0ab6 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=8
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=y
CONFIG_NO_HZ_IDLE=n
CONFIG_NO_HZ_FULL=n
@@ -15,7 +15,6 @@ CONFIG_RCU_FANOUT_EXACT=n
CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
+CONFIG_RCU_KTHREAD_PRIO=2
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 b/tools/testing/selftests/rcutorture/configs/rcu/TREE04
index 0a262fbb0c12..0f84db35b36d 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04
@@ -19,5 +19,4 @@ CONFIG_RCU_FANOUT_EXACT=n
CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_RCU_CPU_STALL_VERBOSE=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 b/tools/testing/selftests/rcutorture/configs/rcu/TREE05
index 3a06b97e9a73..212e3bfd2b2a 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05
@@ -19,5 +19,4 @@ CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RCU=y
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot
index 3b42b8b033cd..15b3e1a86f74 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot
@@ -1 +1,2 @@
rcutorture.torture_type=sched
+rcupdate.rcu_self_test_sched=1
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 b/tools/testing/selftests/rcutorture/configs/rcu/TREE06
index 8f084cca91bf..7eee63b44218 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE06
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06
@@ -20,5 +20,4 @@ CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RCU=y
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot
new file mode 100644
index 000000000000..da9a03a398db
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot
@@ -0,0 +1,3 @@
+rcupdate.rcu_self_test=1
+rcupdate.rcu_self_test_bh=1
+rcupdate.rcu_self_test_sched=1
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 b/tools/testing/selftests/rcutorture/configs/rcu/TREE07
index ab6225506909..92a97fa97dec 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE07
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07
@@ -1,5 +1,6 @@
CONFIG_SMP=y
CONFIG_NR_CPUS=16
+CONFIG_CPUMASK_OFFSTACK=y
CONFIG_PREEMPT_NONE=y
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=n
@@ -7,7 +8,7 @@ CONFIG_PREEMPT=n
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=n
CONFIG_NO_HZ_FULL=y
-CONFIG_NO_HZ_FULL_ALL=y
+CONFIG_NO_HZ_FULL_ALL=n
CONFIG_NO_HZ_FULL_SYSIDLE=y
CONFIG_RCU_FAST_NO_HZ=n
CONFIG_RCU_TRACE=y
@@ -18,5 +19,4 @@ CONFIG_RCU_FANOUT_EXACT=n
CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot
new file mode 100644
index 000000000000..d44609937503
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot
@@ -0,0 +1 @@
+nohz_full=2-9
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 b/tools/testing/selftests/rcutorture/configs/rcu/TREE08
index 69a2e255bf98..5812027d6f9f 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=16
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
@@ -18,7 +18,8 @@ CONFIG_RCU_FANOUT_LEAF=2
CONFIG_RCU_NOCB_CPU=y
CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_DEBUG_LOCK_ALLOC=n
+CONFIG_PROVE_LOCKING=y
+CONFIG_PROVE_RCU=y
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T b/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T
index a0f32fb8f17e..3eaeccacb083 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=16
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
@@ -19,6 +19,5 @@ CONFIG_RCU_NOCB_CPU=y
CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
index 3b42b8b033cd..2561daf605ad 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
@@ -1 +1,3 @@
rcutorture.torture_type=sched
+rcupdate.rcu_self_test=1
+rcupdate.rcu_self_test_sched=1
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE09 b/tools/testing/selftests/rcutorture/configs/rcu/TREE09
index b7a62a540ad1..6076b36f6c0b 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE09
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE09
@@ -3,7 +3,7 @@ CONFIG_NR_CPUS=1
CONFIG_PREEMPT_NONE=n
CONFIG_PREEMPT_VOLUNTARY=n
CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+#CHECK#CONFIG_PREEMPT_RCU=y
CONFIG_HZ_PERIODIC=n
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
@@ -14,6 +14,5 @@ CONFIG_HIBERNATION=n
CONFIG_RCU_NOCB_CPU=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_RCU_CPU_STALL_INFO=n
-CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/CFLIST
deleted file mode 100644
index 18223947bbcb..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/CFLIST
+++ /dev/null
@@ -1,14 +0,0 @@
-P1-S-T-NH-SD-SMP-HP
-P2-2-t-nh-sd-SMP-hp
-P3-3-T-nh-SD-SMP-hp
-P4-A-t-NH-sd-SMP-HP
-P5-U-T-NH-sd-SMP-hp
-N1-S-T-NH-SD-SMP-HP
-N2-2-t-nh-sd-SMP-hp
-N3-3-T-nh-SD-SMP-hp
-N4-A-t-NH-sd-SMP-HP
-N5-U-T-NH-sd-SMP-hp
-PT1-nh
-PT2-NH
-NT1-nh
-NT3-NH
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index d3ef873eb6e7..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 02e418572b1b..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b3100f69c8cf..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index c56b44530725..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index 90d924fea9e9..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/N5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT1-nh
deleted file mode 100644
index 023f312a931c..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT3-NH b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT3-NH
deleted file mode 100644
index 6fd0235dae73..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/NT3-NH
+++ /dev/null
@@ -1,20 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index f72402d7c13d..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,19 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 0f3b667d2a9f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b035e141bf2a..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index 3ccf6a9447f5..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index a55c00877fe4..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/P5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,27 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT1-nh
deleted file mode 100644
index e3361c3894a1..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT2-NH b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT2-NH
deleted file mode 100644
index 64abfc3b4d94..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/PT2-NH
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcu/v0.0/ver_functions.sh
deleted file mode 100644
index 5ace37a89780..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v0.0/ver_functions.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Kernel-version-dependent shell functions for the rest of the scripts.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, you can access it online at
-# http://www.gnu.org/licenses/gpl-2.0.html.
-#
-# Copyright (C) IBM Corporation, 2013
-#
-# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-
-# per_version_boot_params bootparam-string config-file seconds
-#
-# Adds per-version torture-module parameters to kernels supporting them.
-# Which old kernels do not.
-per_version_boot_params () {
- echo rcutorture.stat_interval=15 \
- rcutorture.shutdown_secs=$3 \
- rcutorture.rcutorture_runnable=1 \
- rcutorture.test_no_idle_hz=1 \
- rcutorture.verbose=1
-}
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/CFLIST
deleted file mode 100644
index da4cbc668f2a..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/CFLIST
+++ /dev/null
@@ -1,17 +0,0 @@
-sysidleY.2013.06.19a
-sysidleN.2013.06.19a
-P1-S-T-NH-SD-SMP-HP
-P2-2-t-nh-sd-SMP-hp
-P3-3-T-nh-SD-SMP-hp
-P4-A-t-NH-sd-SMP-HP
-P5-U-T-NH-sd-SMP-hp
-P6---t-nh-SD-smp-hp
-N1-S-T-NH-SD-SMP-HP
-N2-2-t-nh-sd-SMP-hp
-N3-3-T-nh-SD-SMP-hp
-N4-A-t-NH-sd-SMP-HP
-N5-U-T-NH-sd-SMP-hp
-PT1-nh
-PT2-NH
-NT1-nh
-NT3-NH
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index d81e11d280aa..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,19 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 02e418572b1b..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b3100f69c8cf..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index c56b44530725..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index 90d924fea9e9..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N6---t-nh-SD-smp-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N6---t-nh-SD-smp-hp
deleted file mode 100644
index 0ccc36d72738..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N6---t-nh-SD-smp-hp
+++ /dev/null
@@ -1,19 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_NR_CPUS=1
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N7-4-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N7-4-T-NH-SD-SMP-HP
deleted file mode 100644
index 3f640cf84973..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N7-4-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,26 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_RCU_NOCB_CPU_NONE=y
-CONFIG_RCU_NOCB_CPU_ZERO=n
-CONFIG_RCU_NOCB_CPU_ALL=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N8-2-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N8-2-T-NH-SD-SMP-HP
deleted file mode 100644
index 285da2dd8ac3..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/N8-2-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=14
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT1-nh
deleted file mode 100644
index 023f312a931c..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT3-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT3-NH
deleted file mode 100644
index 6fd0235dae73..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/NT3-NH
+++ /dev/null
@@ -1,20 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index 9647c44cf4b7..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 0f3b667d2a9f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b035e141bf2a..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index 3ccf6a9447f5..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index a55c00877fe4..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,27 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P6---t-nh-SD-smp-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P6---t-nh-SD-smp-hp
deleted file mode 100644
index f4c9175828bf..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P6---t-nh-SD-smp-hp
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=n
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP
deleted file mode 100644
index 77a8c5b75763..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,30 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_RCU_NOCB_CPU_NONE=n
-CONFIG_RCU_NOCB_CPU_ZERO=n
-CONFIG_RCU_NOCB_CPU_ALL=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_SLUB=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-all b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-all
deleted file mode 100644
index 0eecebc6e95f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-all
+++ /dev/null
@@ -1,30 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_RCU_NOCB_CPU_NONE=y
-CONFIG_RCU_NOCB_CPU_ZERO=n
-CONFIG_RCU_NOCB_CPU_ALL=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_SLUB=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-none b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-none
deleted file mode 100644
index 0eecebc6e95f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-HP-none
+++ /dev/null
@@ -1,30 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_RCU_NOCB_CPU_NONE=y
-CONFIG_RCU_NOCB_CPU_ZERO=n
-CONFIG_RCU_NOCB_CPU_ALL=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_SLUB=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-hp
deleted file mode 100644
index 588bc70420cd..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/P7-4-T-NH-SD-SMP-hp
+++ /dev/null
@@ -1,30 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=16
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_RCU_NOCB_CPU_NONE=n
-CONFIG_RCU_NOCB_CPU_ZERO=y
-CONFIG_RCU_NOCB_CPU_ALL=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_SLUB=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT1-nh
deleted file mode 100644
index e3361c3894a1..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT2-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT2-NH
deleted file mode 100644
index 64abfc3b4d94..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.12/PT2-NH
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/CFLIST
deleted file mode 100644
index 18223947bbcb..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/CFLIST
+++ /dev/null
@@ -1,14 +0,0 @@
-P1-S-T-NH-SD-SMP-HP
-P2-2-t-nh-sd-SMP-hp
-P3-3-T-nh-SD-SMP-hp
-P4-A-t-NH-sd-SMP-HP
-P5-U-T-NH-sd-SMP-hp
-N1-S-T-NH-SD-SMP-HP
-N2-2-t-nh-sd-SMP-hp
-N3-3-T-nh-SD-SMP-hp
-N4-A-t-NH-sd-SMP-HP
-N5-U-T-NH-sd-SMP-hp
-PT1-nh
-PT2-NH
-NT1-nh
-NT3-NH
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index d81e11d280aa..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,19 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 02e418572b1b..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b3100f69c8cf..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index c56b44530725..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index 90d924fea9e9..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/N5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT1-nh
deleted file mode 100644
index 023f312a931c..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT3-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT3-NH
deleted file mode 100644
index 6fd0235dae73..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/NT3-NH
+++ /dev/null
@@ -1,20 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index 9647c44cf4b7..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 0f3b667d2a9f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b035e141bf2a..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index 3ccf6a9447f5..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index a55c00877fe4..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/P5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,27 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT1-nh
deleted file mode 100644
index e3361c3894a1..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT2-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT2-NH
deleted file mode 100644
index 64abfc3b4d94..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/PT2-NH
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcu/v3.3/ver_functions.sh
deleted file mode 100644
index bae55692ce6e..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.3/ver_functions.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-#
-# Kernel-version-dependent shell functions for the rest of the scripts.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, you can access it online at
-# http://www.gnu.org/licenses/gpl-2.0.html.
-#
-# Copyright (C) IBM Corporation, 2013
-#
-# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-
-# rcutorture_param_onoff bootparam-string config-file
-#
-# Adds onoff rcutorture module parameters to kernels having it.
-rcutorture_param_onoff () {
- if ! bootparam_hotplug_cpu "$1" && configfrag_hotplug_cpu "$2"
- then
- echo CPU-hotplug kernel, adding rcutorture onoff. 1>&2
- echo rcutorture.onoff_interval=3 rcutorture.onoff_holdoff=30
- fi
-}
-
-# per_version_boot_params bootparam-string config-file seconds
-#
-# Adds per-version torture-module parameters to kernels supporting them.
-per_version_boot_params () {
- echo $1 `rcutorture_param_onoff "$1" "$2"` \
- rcutorture.stat_interval=15 \
- rcutorture.shutdown_secs=$3 \
- rcutorture.rcutorture_runnable=1 \
- rcutorture.test_no_idle_hz=1 \
- rcutorture.verbose=1
-}
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/CFLIST
deleted file mode 100644
index 18223947bbcb..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/CFLIST
+++ /dev/null
@@ -1,14 +0,0 @@
-P1-S-T-NH-SD-SMP-HP
-P2-2-t-nh-sd-SMP-hp
-P3-3-T-nh-SD-SMP-hp
-P4-A-t-NH-sd-SMP-HP
-P5-U-T-NH-sd-SMP-hp
-N1-S-T-NH-SD-SMP-HP
-N2-2-t-nh-sd-SMP-hp
-N3-3-T-nh-SD-SMP-hp
-N4-A-t-NH-sd-SMP-HP
-N5-U-T-NH-sd-SMP-hp
-PT1-nh
-PT2-NH
-NT1-nh
-NT3-NH
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index d81e11d280aa..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,19 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 02e418572b1b..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b3100f69c8cf..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index c56b44530725..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,18 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index 90d924fea9e9..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/N5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-#CHECK#CONFIG_TREE_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT1-nh
deleted file mode 100644
index 023f312a931c..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT3-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT3-NH
deleted file mode 100644
index 6fd0235dae73..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/NT3-NH
+++ /dev/null
@@ -1,20 +0,0 @@
-#CHECK#CONFIG_TINY_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=y
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=n
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P1-S-T-NH-SD-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P1-S-T-NH-SD-SMP-HP
deleted file mode 100644
index 9647c44cf4b7..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P1-S-T-NH-SD-SMP-HP
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_RCU_FAST_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=8
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P2-2-t-nh-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P2-2-t-nh-sd-SMP-hp
deleted file mode 100644
index 0f3b667d2a9f..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P2-2-t-nh-sd-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=4
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P3-3-T-nh-SD-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P3-3-T-nh-SD-SMP-hp
deleted file mode 100644
index b035e141bf2a..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P3-3-T-nh-SD-SMP-hp
+++ /dev/null
@@ -1,20 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_NO_HZ=n
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=2
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P4-A-t-NH-sd-SMP-HP b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P4-A-t-NH-sd-SMP-HP
deleted file mode 100644
index 3ccf6a9447f5..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P4-A-t-NH-sd-SMP-HP
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_RCU_TRACE=n
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=n
-CONFIG_HOTPLUG_CPU=y
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P5-U-T-NH-sd-SMP-hp b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P5-U-T-NH-sd-SMP-hp
deleted file mode 100644
index a55c00877fe4..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/P5-U-T-NH-sd-SMP-hp
+++ /dev/null
@@ -1,27 +0,0 @@
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_CPU_STALL_INFO=y
-CONFIG_NO_HZ=y
-CONFIG_SMP=y
-CONFIG_RCU_FANOUT=6
-CONFIG_NR_CPUS=8
-CONFIG_RCU_FANOUT_EXACT=y
-CONFIG_HOTPLUG_CPU=n
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-#CHECK#CONFIG_TREE_PREEMPT_RCU=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_RT_MUTEXES=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT1-nh b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT1-nh
deleted file mode 100644
index e3361c3894a1..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT1-nh
+++ /dev/null
@@ -1,23 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
-CONFIG_RCU_TRACE=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=n
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT2-NH b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT2-NH
deleted file mode 100644
index 64abfc3b4d94..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/PT2-NH
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_TINY_PREEMPT_RCU=y
-CONFIG_RCU_TORTURE_TEST=m
-CONFIG_MODULE_UNLOAD=y
-CONFIG_SUSPEND=n
-CONFIG_HIBERNATION=n
-#
-CONFIG_SMP=n
-#
-CONFIG_HOTPLUG_CPU=n
-#
-CONFIG_NO_HZ=y
-#
-CONFIG_PREEMPT_NONE=n
-CONFIG_PREEMPT_VOLUNTARY=n
-CONFIG_PREEMPT=y
-CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_PRINTK_TIME=y
-
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcu/v3.5/ver_functions.sh
deleted file mode 100644
index 8977d8d31b19..000000000000
--- a/tools/testing/selftests/rcutorture/configs/rcu/v3.5/ver_functions.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-#
-# Kernel-version-dependent shell functions for the rest of the scripts.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, you can access it online at
-# http://www.gnu.org/licenses/gpl-2.0.html.
-#
-# Copyright (C) IBM Corporation, 2013
-#
-# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-
-# rcutorture_param_n_barrier_cbs bootparam-string
-#
-# Adds n_barrier_cbs rcutorture module parameter to kernels having it.
-rcutorture_param_n_barrier_cbs () {
- if echo $1 | grep -q "rcutorture\.n_barrier_cbs"
- then
- :
- else
- echo rcutorture.n_barrier_cbs=4
- fi
-}
-
-# rcutorture_param_onoff bootparam-string config-file
-#
-# Adds onoff rcutorture module parameters to kernels having it.
-rcutorture_param_onoff () {
- if ! bootparam_hotplug_cpu "$1" && configfrag_hotplug_cpu "$2"
- then
- echo CPU-hotplug kernel, adding rcutorture onoff. 1>&2
- echo rcutorture.onoff_interval=3 rcutorture.onoff_holdoff=30
- fi
-}
-
-# per_version_boot_params bootparam-string config-file seconds
-#
-# Adds per-version torture-module parameters to kernels supporting them.
-per_version_boot_params () {
- echo $1 `rcutorture_param_onoff "$1" "$2"` \
- `rcutorture_param_n_barrier_cbs "$1"` \
- rcutorture.stat_interval=15 \
- rcutorture.shutdown_secs=$3 \
- rcutorture.rcutorture_runnable=1 \
- rcutorture.test_no_idle_hz=1 \
- rcutorture.verbose=1
-}
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh
index 8977d8d31b19..ffb85ed786fa 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh
+++ b/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh
@@ -51,7 +51,7 @@ per_version_boot_params () {
`rcutorture_param_n_barrier_cbs "$1"` \
rcutorture.stat_interval=15 \
rcutorture.shutdown_secs=$3 \
- rcutorture.rcutorture_runnable=1 \
+ rcutorture.torture_runnable=1 \
rcutorture.test_no_idle_hz=1 \
rcutorture.verbose=1
}
diff --git a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
index 28db67b54e55..9ef33a743b73 100644
--- a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
+++ b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
@@ -34,7 +34,7 @@ CONFIG_PREEMPT
CONFIG_PREEMPT_RCU
CONFIG_SMP
CONFIG_TINY_RCU
-CONFIG_TREE_PREEMPT_RCU
+CONFIG_PREEMPT_RCU
CONFIG_TREE_RCU
All forced by CONFIG_TINY_RCU.
diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
index 3e588db86a17..ec03c883db00 100644
--- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
+++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
@@ -1,5 +1,5 @@
This document gives a brief rationale for the TREE_RCU-related test
-cases, a group that includes TREE_PREEMPT_RCU.
+cases, a group that includes PREEMPT_RCU.
Kconfig Parameters:
@@ -14,10 +14,9 @@ CONFIG_NO_HZ_FULL_SYSIDLE -- Do one.
CONFIG_PREEMPT -- Do half. (First three and #8.)
CONFIG_PROVE_LOCKING -- Do all but two, covering CONFIG_PROVE_RCU and not.
CONFIG_PROVE_RCU -- Do all but one under CONFIG_PROVE_LOCKING.
-CONFIG_RCU_BOOST -- one of TREE_PREEMPT_RCU.
-CONFIG_RCU_BOOST_PRIO -- set to 2 for _BOOST testing.
-CONFIG_RCU_CPU_STALL_INFO -- do one with and without _VERBOSE.
-CONFIG_RCU_CPU_STALL_VERBOSE -- do one with and without _INFO.
+CONFIG_RCU_BOOST -- one of PREEMPT_RCU.
+CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing.
+CONFIG_RCU_CPU_STALL_INFO -- Do one.
CONFIG_RCU_FANOUT -- Cover hierarchy as currently, but overlap with others.
CONFIG_RCU_FANOUT_EXACT -- Do one.
CONFIG_RCU_FANOUT_LEAF -- Do one non-default.
@@ -27,7 +26,7 @@ CONFIG_RCU_NOCB_CPU_ALL -- Do one.
CONFIG_RCU_NOCB_CPU_NONE -- Do one.
CONFIG_RCU_NOCB_CPU_ZERO -- Do one.
CONFIG_RCU_TRACE -- Do half.
-CONFIG_SMP -- Need one !SMP for TREE_PREEMPT_RCU.
+CONFIG_SMP -- Need one !SMP for PREEMPT_RCU.
RCU-bh: Do one with PREEMPT and one with !PREEMPT.
RCU-sched: Do one with PREEMPT but not BOOST.
@@ -77,7 +76,7 @@ CONFIG_RCU_CPU_STALL_TIMEOUT
CONFIG_RCU_STALL_COMMON
- Implied by TREE_RCU and TREE_PREEMPT_RCU.
+ Implied by TREE_RCU and PREEMPT_RCU.
CONFIG_RCU_TORTURE_TEST
CONFIG_RCU_TORTURE_TEST_RUNNABLE
@@ -88,7 +87,7 @@ CONFIG_RCU_USER_QS
Redundant with CONFIG_NO_HZ_FULL.
-CONFIG_TREE_PREEMPT_RCU
+CONFIG_PREEMPT_RCU
CONFIG_TREE_RCU
These are controlled by CONFIG_PREEMPT.
diff --git a/tools/testing/selftests/rcutorture/doc/initrd.txt b/tools/testing/selftests/rcutorture/doc/initrd.txt
index 49d134c25c04..4170e714f044 100644
--- a/tools/testing/selftests/rcutorture/doc/initrd.txt
+++ b/tools/testing/selftests/rcutorture/doc/initrd.txt
@@ -6,6 +6,7 @@ this case. There are probably much better ways of doing this.
That said, here are the commands:
------------------------------------------------------------------------
+cd tools/testing/selftests/rcutorture
zcat /initrd.img > /tmp/initrd.img.zcat
mkdir initrd
cd initrd
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index 3f94e1afd6cf..4c4b1f631ecf 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -3,6 +3,7 @@
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall
BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen hugetlbfstest
+BINARIES += transhuge-stress
all: $(BINARIES)
%: %.c
diff --git a/tools/testing/selftests/vm/transhuge-stress.c b/tools/testing/selftests/vm/transhuge-stress.c
new file mode 100644
index 000000000000..fd7f1b4a96f9
--- /dev/null
+++ b/tools/testing/selftests/vm/transhuge-stress.c
@@ -0,0 +1,144 @@
+/*
+ * Stress test for transparent huge pages, memory compaction and migration.
+ *
+ * Authors: Konstantin Khlebnikov <koct9i@gmail.com>
+ *
+ * This is free and unencumbered software released into the public domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <err.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#define PAGE_SHIFT 12
+#define HPAGE_SHIFT 21
+
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+#define HPAGE_SIZE (1 << HPAGE_SHIFT)
+
+#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
+#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
+
+int pagemap_fd;
+
+int64_t allocate_transhuge(void *ptr)
+{
+ uint64_t ent[2];
+
+ /* drop pmd */
+ if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_ANONYMOUS |
+ MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
+ errx(2, "mmap transhuge");
+
+ if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
+ err(2, "MADV_HUGEPAGE");
+
+ /* allocate transparent huge page */
+ *(volatile void **)ptr = ptr;
+
+ if (pread(pagemap_fd, ent, sizeof(ent),
+ (uintptr_t)ptr >> (PAGE_SHIFT - 3)) != sizeof(ent))
+ err(2, "read pagemap");
+
+ if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
+ PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
+ !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - PAGE_SHIFT)) - 1)))
+ return PAGEMAP_PFN(ent[0]);
+
+ return -1;
+}
+
+int main(int argc, char **argv)
+{
+ size_t ram, len;
+ void *ptr, *p;
+ struct timespec a, b;
+ double s;
+ uint8_t *map;
+ size_t map_len;
+
+ ram = sysconf(_SC_PHYS_PAGES);
+ if (ram > SIZE_MAX / sysconf(_SC_PAGESIZE) / 4)
+ ram = SIZE_MAX / 4;
+ else
+ ram *= sysconf(_SC_PAGESIZE);
+
+ if (argc == 1)
+ len = ram;
+ else if (!strcmp(argv[1], "-h"))
+ errx(1, "usage: %s [size in MiB]", argv[0]);
+ else
+ len = atoll(argv[1]) << 20;
+
+ warnx("allocate %zd transhuge pages, using %zd MiB virtual memory"
+ " and %zd MiB of ram", len >> HPAGE_SHIFT, len >> 20,
+ len >> (20 + HPAGE_SHIFT - PAGE_SHIFT - 1));
+
+ pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+ if (pagemap_fd < 0)
+ err(2, "open pagemap");
+
+ len -= len % HPAGE_SIZE;
+ ptr = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
+ if (ptr == MAP_FAILED)
+ err(2, "initial mmap");
+ ptr += HPAGE_SIZE - (uintptr_t)ptr % HPAGE_SIZE;
+
+ if (madvise(ptr, len, MADV_HUGEPAGE))
+ err(2, "MADV_HUGEPAGE");
+
+ map_len = ram >> (HPAGE_SHIFT - 1);
+ map = malloc(map_len);
+ if (!map)
+ errx(2, "map malloc");
+
+ while (1) {
+ int nr_succeed = 0, nr_failed = 0, nr_pages = 0;
+
+ memset(map, 0, map_len);
+
+ clock_gettime(CLOCK_MONOTONIC, &a);
+ for (p = ptr; p < ptr + len; p += HPAGE_SIZE) {
+ int64_t pfn;
+
+ pfn = allocate_transhuge(p);
+
+ if (pfn < 0) {
+ nr_failed++;
+ } else {
+ size_t idx = pfn >> (HPAGE_SHIFT - PAGE_SHIFT);
+
+ nr_succeed++;
+ if (idx >= map_len) {
+ map = realloc(map, idx + 1);
+ if (!map)
+ errx(2, "map realloc");
+ memset(map + map_len, 0, idx + 1 - map_len);
+ map_len = idx + 1;
+ }
+ if (!map[idx])
+ nr_pages++;
+ map[idx] = 1;
+ }
+
+ /* split transhuge page, keep last page */
+ if (madvise(p, HPAGE_SIZE - PAGE_SIZE, MADV_DONTNEED))
+ err(2, "MADV_DONTNEED");
+ }
+ clock_gettime(CLOCK_MONOTONIC, &b);
+ s = b.tv_sec - a.tv_sec + (b.tv_nsec - a.tv_nsec) / 1000000000.;
+
+ warnx("%.3f s/loop, %.3f ms/page, %10.3f MiB/s\t"
+ "%4d succeed, %4d failed, %4d different pages",
+ s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20),
+ nr_succeed, nr_failed, nr_pages);
+ }
+}
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
index a87e99f37c52..88d5e71be044 100644
--- a/tools/usb/ffs-test.c
+++ b/tools/usb/ffs-test.c
@@ -1,5 +1,5 @@
/*
- * ffs-test.c.c -- user mode filesystem api for usb composite function
+ * ffs-test.c -- user mode filesystem api for usb composite function
*
* Copyright (C) 2010 Samsung Electronics
* Author: Michal Nazarewicz <mina86@mina86.com>
@@ -29,6 +29,7 @@
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -106,7 +107,9 @@ static void _msg(unsigned level, const char *fmt, ...)
/******************** Descriptors and Strings *******************************/
static const struct {
- struct usb_functionfs_descs_head header;
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio sink;
@@ -114,11 +117,12 @@ static const struct {
} __attribute__((packed)) fs_descs, hs_descs;
} __attribute__((packed)) descriptors = {
.header = {
- .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
.length = cpu_to_le32(sizeof descriptors),
- .fs_count = cpu_to_le32(3),
- .hs_count = cpu_to_le32(3),
},
+ .fs_count = cpu_to_le32(3),
.fs_descs = {
.intf = {
.bLength = sizeof descriptors.fs_descs.intf,
@@ -142,6 +146,7 @@ static const struct {
/* .wMaxPacketSize = autoconfiguration (kernel) */
},
},
+ .hs_count = cpu_to_le32(3),
.hs_descs = {
.intf = {
.bLength = sizeof descriptors.fs_descs.intf,
@@ -168,6 +173,89 @@ static const struct {
},
};
+static size_t descs_to_legacy(void **legacy, const void *descriptors_v2)
+{
+ const unsigned char *descs_end, *descs_start;
+ __u32 length, fs_count = 0, hs_count = 0, count;
+
+ /* Read v2 header */
+ {
+ const struct {
+ const struct usb_functionfs_descs_head_v2 header;
+ const __le32 counts[];
+ } __attribute__((packed)) *const in = descriptors_v2;
+ const __le32 *counts = in->counts;
+ __u32 flags;
+
+ if (le32_to_cpu(in->header.magic) !=
+ FUNCTIONFS_DESCRIPTORS_MAGIC_V2)
+ return 0;
+ length = le32_to_cpu(in->header.length);
+ if (length <= sizeof in->header)
+ return 0;
+ length -= sizeof in->header;
+ flags = le32_to_cpu(in->header.flags);
+ if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC))
+ return 0;
+
+#define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \
+ if (!(flags & (flg))) \
+ break; \
+ if (length < 4) \
+ return 0; \
+ ret = le32_to_cpu(*counts); \
+ length -= 4; \
+ ++counts; \
+ } while (0)
+
+ GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC);
+ GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC);
+ GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC);
+
+ count = fs_count + hs_count;
+ if (!count)
+ return 0;
+ descs_start = (const void *)counts;
+
+#undef GET_NEXT_COUNT_IF_FLAG
+ }
+
+ /*
+ * Find the end of FS and HS USB descriptors. SS descriptors
+ * are ignored since legacy format does not support them.
+ */
+ descs_end = descs_start;
+ do {
+ if (length < *descs_end)
+ return 0;
+ length -= *descs_end;
+ descs_end += *descs_end;
+ } while (--count);
+
+ /* Allocate legacy descriptors and copy the data. */
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ struct {
+ struct usb_functionfs_descs_head header;
+ __u8 descriptors[];
+ } __attribute__((packed)) *out;
+#pragma GCC diagnostic pop
+
+ length = sizeof out->header + (descs_end - descs_start);
+ out = malloc(length);
+ out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ out->header.length = cpu_to_le32(length);
+ out->header.fs_count = cpu_to_le32(fs_count);
+ out->header.hs_count = cpu_to_le32(hs_count);
+ memcpy(out->descriptors, descs_start, descs_end - descs_start);
+ *legacy = out;
+ }
+
+ return length;
+}
+
#define STR_INTERFACE_ "Source/Sink"
@@ -487,12 +575,29 @@ ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
return nbytes;
}
-static void ep0_init(struct thread *t)
+static void ep0_init(struct thread *t, bool legacy_descriptors)
{
+ void *legacy;
ssize_t ret;
+ size_t len;
+
+ if (legacy_descriptors) {
+ info("%s: writing descriptors\n", t->filename);
+ goto legacy;
+ }
- info("%s: writing descriptors\n", t->filename);
+ info("%s: writing descriptors (in v2 format)\n", t->filename);
ret = write(t->fd, &descriptors, sizeof descriptors);
+
+ if (ret < 0 && errno == EINVAL) {
+ warn("%s: new format rejected, trying legacy\n", t->filename);
+legacy:
+ len = descs_to_legacy(&legacy, &descriptors);
+ if (len) {
+ ret = write(t->fd, legacy, len);
+ free(legacy);
+ }
+ }
die_on(ret < 0, "%s: write: descriptors", t->filename);
info("%s: writing strings\n", t->filename);
@@ -503,14 +608,15 @@ static void ep0_init(struct thread *t)
/******************** Main **************************************************/
-int main(void)
+int main(int argc, char **argv)
{
+ bool legacy_descriptors;
unsigned i;
- /* XXX TODO: Argument parsing missing */
+ legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l");
init_thread(threads);
- ep0_init(threads);
+ ep0_init(threads, legacy_descriptors);
for (i = 1; i < sizeof threads / sizeof *threads; ++i)
init_thread(threads + i);
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c
index c4d6d2e20e0d..264fbc297e0b 100644
--- a/tools/vm/page-types.c
+++ b/tools/vm/page-types.c
@@ -132,6 +132,7 @@ static const char * const page_flag_names[] = {
[KPF_NOPAGE] = "n:nopage",
[KPF_KSM] = "x:ksm",
[KPF_THP] = "t:thp",
+ [KPF_BALLOON] = "o:balloon",
[KPF_RESERVED] = "r:reserved",
[KPF_MLOCKED] = "m:mlocked",