mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 00:35:01 +00:00
perf tools changes for v6.13
perf record ----------- * Enable leader sampling for inherited task events. It was supported only for system-wide events but the kernel started to support such a setup since v6.12. This is to reduce the number of PMU interrupts. The samples of the leader event will contain counts of other events and no samples will be generated for the other member events. $ perf record -e '{cycles,instructions}:S' ${MYPROG} perf report ----------- * Fix --branch-history option to display more branch-related information like prediction, abort and cycles which is available on Intel machines. $ perf record -bg -- perf test -w brstack $ perf report --branch-history ... # # Overhead Source:Line Symbol Shared Object Predicted Abort Cycles IPC [IPC Coverage] # ........ ........................ .............. .................... ......... ..... ...... .................... # 8.17% copy_page_64.S:19 [k] copy_page [kernel.kallsyms] 50.0% 0 5 - - | ---xas_load xarray.h:171 | |--5.68%--xas_load xarray.c:245 (cycles:1) | xas_load xarray.c:242 | xas_load xarray.h:1260 (cycles:1) | xas_descend xarray.c:146 | xas_load xarray.c:244 (cycles:2) | xas_load xarray.c:245 | xas_descend xarray.c:218 (cycles:10) ... perf stat --------- * Add HWMON PMU support. The HWMON provides various system information like CPU/GPU temperature, fan speed and so on. Expose them as PMU events so that users can see the values using perf stat commands. $ perf stat -e temp_cpu,fan1 true Performance counter stats for 'true': 60.00 'C temp_cpu 0 rpm fan1 0.000745382 seconds time elapsed 0.000883000 seconds user 0.000000000 seconds sys * Display metric threshold in JSON output. Some metrics define thresholds to classify value ranges. It used to be in a different color but it won't work for JSON. Add "metric-threshold" field to the JSON that can be one of "good", "less good", "nearly bad" and "bad". # perf stat -a -M TopdownL1 -j true {"counter-value" : "18693525.000000", "unit" : "", "event" : "TOPDOWN.SLOTS", "event-runtime" : 5552708, "pcnt-running" : 100.00, "metric-value" : "43.226002", "metric-unit" : "% tma_backend_bound", "metric-threshold" : "bad"} {"metric-value" : "29.212267", "metric-unit" : "% tma_frontend_bound", "metric-threshold" : "bad"} {"metric-value" : "7.138972", "metric-unit" : "% tma_bad_speculation", "metric-threshold" : "good"} {"metric-value" : "20.422759", "metric-unit" : "% tma_retiring", "metric-threshold" : "good"} {"counter-value" : "3817732.000000", "unit" : "", "event" : "topdown-retiring", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "5472824.000000", "unit" : "", "event" : "topdown-fe-bound", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "7984780.000000", "unit" : "", "event" : "topdown-be-bound", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "1418181.000000", "unit" : "", "event" : "topdown-bad-spec", "event-runtime" : 5552708, "pcnt-running" : 100.00, } ... perf sched ---------- * Add -P/--pre-migrations option for 'timehist' sub-command to track time a task waited on a run-queue before migrating to a different CPU. $ perf sched timehist -P time cpu task name wait time sch delay run time pre-mig time [tid/pid] (msec) (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- --------- 585940.535527 [0000] perf[584885] 0.000 0.000 0.000 0.000 585940.535535 [0000] migration/0[20] 0.000 0.002 0.008 0.000 585940.535559 [0001] perf[584885] 0.000 0.000 0.000 0.000 585940.535563 [0001] migration/1[25] 0.000 0.001 0.004 0.000 585940.535678 [0002] perf[584885] 0.000 0.000 0.000 0.000 585940.535686 [0002] migration/2[31] 0.000 0.002 0.008 0.000 585940.535905 [0001] <idle> 0.000 0.000 0.342 0.000 585940.535938 [0003] perf[584885] 0.000 0.000 0.000 0.000 585940.537048 [0001] sleep[584886] 0.000 0.019 1.142 0.001 585940.537749 [0002] <idle> 0.000 0.000 2.062 0.000 ... Build ----- * Make libunwind opt-in (LIBUNWIND=1) rather than opt-out. The perf tools are generally built with libelf and libdw which has unwinder functionality. The libunwind support predates it and no need to have duplicate unwinders by default. * Rename NO_DWARF=1 build option to NO_LIBDW=1 in order to clarify it's using libdw for handling DWARF information. Internals --------- * Do not set exclude_guest bit in the perf_event_attr by default. This was causing a trouble in AMD IBS PMU as it doesn't support the bit. The bit will be set when it's needed later by the fallback logic. Also update the missing feature detection logic to make sure not clear supported bits unnecessarily. * Run perf test in parallel by default and mark flaky tests "exclusive" to run them serially at the end. Some test numbers are changed but the test can complete in less than half the time. JSON vendor events ------------------ * Add AMD Zen 5 events and metrics. * Add i.MX91 and i.MX95 DDR metrics * Fix HiSilicon HIP08 Topdown metric name. * Support compat events on PowerPC. Signed-off-by: Namhyung Kim <namhyung@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSo2x5BnqMqsoHtzsmMstVUGiXMgwUCZ0Qi3gAKCRCMstVUGiXM g6NIAP49eoSmQF40u55sJN0J7RpYd+bTgXZkahv0IUCBX98TLwEA2NrK0oUcB84C xeanq28/3JxNM/oBpsEvvB8mb/0lGwI= =FAVF -----END PGP SIGNATURE----- Merge tag 'perf-tools-for-v6.13-2024-11-24' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools Pull perf tools updates from Namhyung Kim: "perf record: - Enable leader sampling for inherited task events. It was supported only for system-wide events but the kernel started to support such a setup since v6.12. This is to reduce the number of PMU interrupts. The samples of the leader event will contain counts of other events and no samples will be generated for the other member events. $ perf record -e '{cycles,instructions}:S' ${MYPROG} perf report: - Fix --branch-history option to display more branch-related information like prediction, abort and cycles which is available on Intel machines. $ perf record -bg -- perf test -w brstack $ perf report --branch-history ... # # Overhead Source:Line Symbol Shared Object Predicted Abort Cycles IPC [IPC Coverage] # ........ ........................ .............. .................... ......... ..... ...... .................... # 8.17% copy_page_64.S:19 [k] copy_page [kernel.kallsyms] 50.0% 0 5 - - | ---xas_load xarray.h:171 | |--5.68%--xas_load xarray.c:245 (cycles:1) | xas_load xarray.c:242 | xas_load xarray.h:1260 (cycles:1) | xas_descend xarray.c:146 | xas_load xarray.c:244 (cycles:2) | xas_load xarray.c:245 | xas_descend xarray.c:218 (cycles:10) ... perf stat: - Add HWMON PMU support. The HWMON provides various system information like CPU/GPU temperature, fan speed and so on. Expose them as PMU events so that users can see the values using perf stat commands. $ perf stat -e temp_cpu,fan1 true Performance counter stats for 'true': 60.00 'C temp_cpu 0 rpm fan1 0.000745382 seconds time elapsed 0.000883000 seconds user 0.000000000 seconds sys - Display metric threshold in JSON output. Some metrics define thresholds to classify value ranges. It used to be in a different color but it won't work for JSON. Add "metric-threshold" field to the JSON that can be one of "good", "less good", "nearly bad" and "bad". # perf stat -a -M TopdownL1 -j true {"counter-value" : "18693525.000000", "unit" : "", "event" : "TOPDOWN.SLOTS", "event-runtime" : 5552708, "pcnt-running" : 100.00, "metric-value" : "43.226002", "metric-unit" : "% tma_backend_bound", "metric-threshold" : "bad"} {"metric-value" : "29.212267", "metric-unit" : "% tma_frontend_bound", "metric-threshold" : "bad"} {"metric-value" : "7.138972", "metric-unit" : "% tma_bad_speculation", "metric-threshold" : "good"} {"metric-value" : "20.422759", "metric-unit" : "% tma_retiring", "metric-threshold" : "good"} {"counter-value" : "3817732.000000", "unit" : "", "event" : "topdown-retiring", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "5472824.000000", "unit" : "", "event" : "topdown-fe-bound", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "7984780.000000", "unit" : "", "event" : "topdown-be-bound", "event-runtime" : 5552708, "pcnt-running" : 100.00, } {"counter-value" : "1418181.000000", "unit" : "", "event" : "topdown-bad-spec", "event-runtime" : 5552708, "pcnt-running" : 100.00, } ... perf sched: - Add -P/--pre-migrations option for 'timehist' sub-command to track time a task waited on a run-queue before migrating to a different CPU. $ perf sched timehist -P time cpu task name wait time sch delay run time pre-mig time [tid/pid] (msec) (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- --------- 585940.535527 [0000] perf[584885] 0.000 0.000 0.000 0.000 585940.535535 [0000] migration/0[20] 0.000 0.002 0.008 0.000 585940.535559 [0001] perf[584885] 0.000 0.000 0.000 0.000 585940.535563 [0001] migration/1[25] 0.000 0.001 0.004 0.000 585940.535678 [0002] perf[584885] 0.000 0.000 0.000 0.000 585940.535686 [0002] migration/2[31] 0.000 0.002 0.008 0.000 585940.535905 [0001] <idle> 0.000 0.000 0.342 0.000 585940.535938 [0003] perf[584885] 0.000 0.000 0.000 0.000 585940.537048 [0001] sleep[584886] 0.000 0.019 1.142 0.001 585940.537749 [0002] <idle> 0.000 0.000 2.062 0.000 ... Build: - Make libunwind opt-in (LIBUNWIND=1) rather than opt-out. The perf tools are generally built with libelf and libdw which has unwinder functionality. The libunwind support predates it and no need to have duplicate unwinders by default. - Rename NO_DWARF=1 build option to NO_LIBDW=1 in order to clarify it's using libdw for handling DWARF information. Internals: - Do not set exclude_guest bit in the perf_event_attr by default. This was causing a trouble in AMD IBS PMU as it doesn't support the bit. The bit will be set when it's needed later by the fallback logic. Also update the missing feature detection logic to make sure not clear supported bits unnecessarily. - Run perf test in parallel by default and mark flaky tests "exclusive" to run them serially at the end. Some test numbers are changed but the test can complete in less than half the time. JSON vendor events: - Add AMD Zen 5 events and metrics. - Add i.MX91 and i.MX95 DDR metrics - Fix HiSilicon HIP08 Topdown metric name. - Support compat events on PowerPC" * tag 'perf-tools-for-v6.13-2024-11-24' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools: (232 commits) perf tests: Fix hwmon parsing with PMU name test perf hwmon_pmu: Ensure hwmon key union is zeroed before use perf tests hwmon_pmu: Remove double evlist__delete() perf/test: fix perf ftrace test on s390 perf bpf-filter: Return -ENOMEM directly when pfi allocation fails perf test: Correct hwmon test PMU detection perf: Remove unused del_perf_probe_events() perf pmu: Move pmu_metrics_table__find and remove ARM override perf jevents: Add map_for_cpu() perf header: Pass a perf_cpu rather than a PMU to get_cpuid_str perf header: Avoid transitive PMU includes perf arm64 header: Use cpu argument in get_cpuid perf header: Refactor get_cpuid to take a CPU for ARM perf header: Move is_cpu_online to numa bench perf jevents: fix breakage when do perf stat on system metric perf test: Add missing __exit calls in tool/hwmon tests perf tests: Make leader sampling test work without branch event perf util: Remove kernel version deadcode perf test shell trace_exit_race: Use --no-comm to avoid cases where COMM isn't resolved perf test shell trace_exit_race: Show what went wrong in verbose mode ...
This commit is contained in:
commit
b50ecc5aca
@ -30,9 +30,7 @@ endef
|
||||
#
|
||||
FEATURE_TESTS_BASIC := \
|
||||
backtrace \
|
||||
dwarf \
|
||||
dwarf_getlocations \
|
||||
dwarf_getcfi \
|
||||
libdw \
|
||||
eventfd \
|
||||
fortify-source \
|
||||
get_current_dir_name \
|
||||
@ -61,7 +59,6 @@ FEATURE_TESTS_BASIC := \
|
||||
reallocarray \
|
||||
stackprotector-all \
|
||||
timerfd \
|
||||
libdw-dwarf-unwind \
|
||||
zlib \
|
||||
lzma \
|
||||
get_cpuid \
|
||||
@ -121,8 +118,7 @@ ifeq ($(FEATURE_TESTS),all)
|
||||
endif
|
||||
|
||||
FEATURE_DISPLAY ?= \
|
||||
dwarf \
|
||||
dwarf_getlocations \
|
||||
libdw \
|
||||
glibc \
|
||||
libbfd \
|
||||
libbfd-buildid \
|
||||
@ -134,7 +130,6 @@ FEATURE_DISPLAY ?= \
|
||||
libpython \
|
||||
libcrypto \
|
||||
libunwind \
|
||||
libdw-dwarf-unwind \
|
||||
libcapstone \
|
||||
llvm-perf \
|
||||
zlib \
|
||||
@ -234,7 +229,7 @@ endef
|
||||
|
||||
#
|
||||
# generates feature value assignment for name, like:
|
||||
# $(call feature_assign,dwarf) == feature-dwarf=1
|
||||
# $(call feature_assign,libdw) == feature-libdw=1
|
||||
#
|
||||
feature_assign = feature-$(1)=$(feature-$(1))
|
||||
|
||||
|
@ -5,9 +5,7 @@ FILES= \
|
||||
test-all.bin \
|
||||
test-backtrace.bin \
|
||||
test-bionic.bin \
|
||||
test-dwarf.bin \
|
||||
test-dwarf_getlocations.bin \
|
||||
test-dwarf_getcfi.bin \
|
||||
test-libdw.bin \
|
||||
test-eventfd.bin \
|
||||
test-fortify-source.bin \
|
||||
test-get_current_dir_name.bin \
|
||||
@ -53,7 +51,6 @@ FILES= \
|
||||
test-pthread-barrier.bin \
|
||||
test-stackprotector-all.bin \
|
||||
test-timerfd.bin \
|
||||
test-libdw-dwarf-unwind.bin \
|
||||
test-libbabeltrace.bin \
|
||||
test-libcapstone.bin \
|
||||
test-compile-32.bin \
|
||||
@ -169,9 +166,9 @@ $(OUTPUT)test-libopencsd.bin:
|
||||
$(BUILD) # -lopencsd_c_api -lopencsd provided by
|
||||
# $(FEATURE_CHECK_LDFLAGS-libopencsd)
|
||||
|
||||
DWARFLIBS := -ldw
|
||||
DWLIBS := -ldw
|
||||
ifeq ($(findstring -static,${LDFLAGS}),-static)
|
||||
DWARFLIBS += -lelf -lz -llzma -lbz2 -lzstd
|
||||
DWLIBS += -lelf -lz -llzma -lbz2 -lzstd
|
||||
|
||||
LIBDW_VERSION := $(shell $(PKG_CONFIG) --modversion libdw).0.0
|
||||
LIBDW_VERSION_1 := $(word 1, $(subst ., ,$(LIBDW_VERSION)))
|
||||
@ -180,21 +177,15 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
|
||||
# Elfutils merged libebl.a into libdw.a starting from version 0.177,
|
||||
# Link libebl.a only if libdw is older than this version.
|
||||
ifeq ($(shell test $(LIBDW_VERSION_2) -lt 177; echo $$?),0)
|
||||
DWARFLIBS += -lebl
|
||||
DWLIBS += -lebl
|
||||
endif
|
||||
|
||||
# Must put -ldl after -lebl for dependency
|
||||
DWARFLIBS += -ldl
|
||||
endif
|
||||
|
||||
$(OUTPUT)test-dwarf.bin:
|
||||
$(BUILD) $(DWARFLIBS)
|
||||
|
||||
$(OUTPUT)test-dwarf_getlocations.bin:
|
||||
$(BUILD) $(DWARFLIBS)
|
||||
|
||||
$(OUTPUT)test-dwarf_getcfi.bin:
|
||||
$(BUILD) $(DWARFLIBS)
|
||||
$(OUTPUT)test-libdw.bin:
|
||||
$(BUILD) $(DWLIBS)
|
||||
|
||||
$(OUTPUT)test-libelf-getphdrnum.bin:
|
||||
$(BUILD) -lelf
|
||||
@ -321,9 +312,6 @@ $(OUTPUT)test-backtrace.bin:
|
||||
$(OUTPUT)test-timerfd.bin:
|
||||
$(BUILD)
|
||||
|
||||
$(OUTPUT)test-libdw-dwarf-unwind.bin:
|
||||
$(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind)
|
||||
|
||||
$(OUTPUT)test-libbabeltrace.bin:
|
||||
$(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace)
|
||||
|
||||
|
@ -38,12 +38,8 @@
|
||||
# include "test-glibc.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_dwarf
|
||||
# include "test-dwarf.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_dwarf_getlocations
|
||||
# include "test-dwarf_getlocations.c"
|
||||
#define main main_test_libdw
|
||||
# include "test-libdw.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_eventfd
|
||||
@ -98,10 +94,6 @@
|
||||
# include "test-stackprotector-all.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_libdw_dwarf_unwind
|
||||
# include "test-libdw-dwarf-unwind.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_zlib
|
||||
# include "test-zlib.c"
|
||||
#undef main
|
||||
@ -187,8 +179,7 @@ int main(int argc, char *argv[])
|
||||
main_test_get_current_dir_name();
|
||||
main_test_gettid();
|
||||
main_test_glibc();
|
||||
main_test_dwarf();
|
||||
main_test_dwarf_getlocations();
|
||||
main_test_libdw();
|
||||
main_test_eventfd();
|
||||
main_test_libelf_getphdrnum();
|
||||
main_test_libelf_gelf_getnote();
|
||||
@ -202,7 +193,6 @@ int main(int argc, char *argv[])
|
||||
main_test_numa_num_possible_cpus();
|
||||
main_test_timerfd();
|
||||
main_test_stackprotector_all();
|
||||
main_test_libdw_dwarf_unwind();
|
||||
main_test_zlib();
|
||||
main_test_pthread_attr_setaffinity_np();
|
||||
main_test_pthread_barrier();
|
||||
|
@ -1,11 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
#include <elfutils/version.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Dwarf *dbg = dwarf_begin(0, DWARF_C_READ);
|
||||
|
||||
return (long)dbg;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdio.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Dwarf *dwarf = NULL;
|
||||
return dwarf_getcfi(dwarf) == NULL;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdlib.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Dwarf_Addr base, start, end;
|
||||
Dwarf_Attribute attr;
|
||||
Dwarf_Op *op;
|
||||
size_t nops;
|
||||
ptrdiff_t offset = 0;
|
||||
return (int)dwarf_getlocations(&attr, offset, &base, &start, &end, &op, &nops);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/*
|
||||
* This function is guarded via: __nonnull_attribute__ (1, 2).
|
||||
* Passing '1' as arguments value. This code is never executed,
|
||||
* only compiled.
|
||||
*/
|
||||
dwfl_thread_getframes((void *) 1, (void *) 1, NULL);
|
||||
return 0;
|
||||
}
|
56
tools/build/feature/test-libdw.c
Normal file
56
tools/build/feature/test-libdw.c
Normal file
@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdlib.h>
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <elfutils/version.h>
|
||||
|
||||
int test_libdw(void)
|
||||
{
|
||||
Dwarf *dbg = dwarf_begin(0, DWARF_C_READ);
|
||||
|
||||
return (long)dbg;
|
||||
}
|
||||
|
||||
int test_libdw_unwind(void)
|
||||
{
|
||||
/*
|
||||
* This function is guarded via: __nonnull_attribute__ (1, 2).
|
||||
* Passing '1' as arguments value. This code is never executed,
|
||||
* only compiled.
|
||||
*/
|
||||
dwfl_thread_getframes((void *) 1, (void *) 1, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_libdw_getlocations(void)
|
||||
{
|
||||
Dwarf_Addr base, start, end;
|
||||
Dwarf_Attribute attr;
|
||||
Dwarf_Op *op;
|
||||
size_t nops;
|
||||
ptrdiff_t offset = 0;
|
||||
|
||||
return (int)dwarf_getlocations(&attr, offset, &base, &start, &end, &op, &nops);
|
||||
}
|
||||
|
||||
int test_libdw_getcfi(void)
|
||||
{
|
||||
Dwarf *dwarf = NULL;
|
||||
|
||||
return dwarf_getcfi(dwarf) == NULL;
|
||||
}
|
||||
|
||||
int test_elfutils(void)
|
||||
{
|
||||
Dwarf_CFI *cfi = NULL;
|
||||
|
||||
dwarf_cfi_end(cfi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_libdw() + test_libdw_unwind() + test_libdw_getlocations() +
|
||||
test_libdw_getcfi() + test_elfutils();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <traceevent/trace-seq.h>
|
||||
#include <trace-seq.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
@ -189,6 +189,7 @@ static inline ssize_t io__getdelim(struct io *io, char **line_out, size_t *line_
|
||||
err_out:
|
||||
free(line);
|
||||
*line_out = NULL;
|
||||
*line_len_out = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ install-man: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \
|
||||
$(INSTALL) -m 644 $(MAN_7) $(DESTDIR)$(man7dir);
|
||||
|
||||
install-html:
|
||||
install-html: $(MAN_HTML)
|
||||
$(call QUIET_INSTALL, html) \
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(htmldir); \
|
||||
$(INSTALL) -m 644 $(MAN_HTML) $(DESTDIR)$(htmldir); \
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <perf/evsel.h>
|
||||
#include <perf/cpumap.h>
|
||||
#include <perf/threadmap.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/list.h>
|
||||
#include <internal/evsel.h>
|
||||
#include <linux/zalloc.h>
|
||||
@ -23,6 +24,7 @@ void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr,
|
||||
int idx)
|
||||
{
|
||||
INIT_LIST_HEAD(&evsel->node);
|
||||
INIT_LIST_HEAD(&evsel->per_stream_periods);
|
||||
evsel->attr = *attr;
|
||||
evsel->idx = idx;
|
||||
evsel->leader = evsel;
|
||||
@ -531,10 +533,56 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
|
||||
void perf_evsel__free_id(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_sample_id_period *pos, *n;
|
||||
|
||||
xyarray__delete(evsel->sample_id);
|
||||
evsel->sample_id = NULL;
|
||||
zfree(&evsel->id);
|
||||
evsel->ids = 0;
|
||||
|
||||
perf_evsel_for_each_per_thread_period_safe(evsel, n, pos) {
|
||||
list_del_init(&pos->node);
|
||||
free(pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool perf_evsel__attr_has_per_thread_sample_period(struct perf_evsel *evsel)
|
||||
{
|
||||
return (evsel->attr.sample_type & PERF_SAMPLE_READ) &&
|
||||
(evsel->attr.sample_type & PERF_SAMPLE_TID) &&
|
||||
evsel->attr.inherit;
|
||||
}
|
||||
|
||||
u64 *perf_sample_id__get_period_storage(struct perf_sample_id *sid, u32 tid, bool per_thread)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct perf_sample_id_period *res;
|
||||
int hash;
|
||||
|
||||
if (!per_thread)
|
||||
return &sid->period;
|
||||
|
||||
hash = hash_32(tid, PERF_SAMPLE_ID__HLIST_BITS);
|
||||
head = &sid->periods[hash];
|
||||
|
||||
hlist_for_each_entry(res, head, hnode)
|
||||
if (res->tid == tid)
|
||||
return &res->period;
|
||||
|
||||
if (sid->evsel == NULL)
|
||||
return NULL;
|
||||
|
||||
res = zalloc(sizeof(struct perf_sample_id_period));
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&res->node);
|
||||
res->tid = tid;
|
||||
|
||||
list_add_tail(&res->node, &sid->evsel->per_stream_periods);
|
||||
hlist_add_head(&res->hnode, &sid->periods[hash]);
|
||||
|
||||
return &res->period;
|
||||
}
|
||||
|
||||
void perf_counts_values__scale(struct perf_counts_values *count,
|
||||
|
@ -11,6 +11,32 @@
|
||||
struct perf_thread_map;
|
||||
struct xyarray;
|
||||
|
||||
/**
|
||||
* The per-thread accumulated period storage node.
|
||||
*/
|
||||
struct perf_sample_id_period {
|
||||
struct list_head node;
|
||||
struct hlist_node hnode;
|
||||
/* Holds total ID period value for PERF_SAMPLE_READ processing. */
|
||||
u64 period;
|
||||
/* The TID that the values belongs to */
|
||||
u32 tid;
|
||||
};
|
||||
|
||||
/**
|
||||
* perf_evsel_for_each_per_thread_period_safe - safely iterate thru all the
|
||||
* per_stream_periods
|
||||
* @evlist:perf_evsel instance to iterate
|
||||
* @item: struct perf_sample_id_period iterator
|
||||
* @tmp: struct perf_sample_id_period temp iterator
|
||||
*/
|
||||
#define perf_evsel_for_each_per_thread_period_safe(evsel, tmp, item) \
|
||||
list_for_each_entry_safe(item, tmp, &(evsel)->per_stream_periods, node)
|
||||
|
||||
|
||||
#define PERF_SAMPLE_ID__HLIST_BITS 4
|
||||
#define PERF_SAMPLE_ID__HLIST_SIZE (1 << PERF_SAMPLE_ID__HLIST_BITS)
|
||||
|
||||
/*
|
||||
* Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are
|
||||
* more than one entry in the evlist.
|
||||
@ -34,8 +60,32 @@ struct perf_sample_id {
|
||||
pid_t machine_pid;
|
||||
struct perf_cpu vcpu;
|
||||
|
||||
/* Holds total ID period value for PERF_SAMPLE_READ processing. */
|
||||
u64 period;
|
||||
/*
|
||||
* Per-thread, and global event counts are mutually exclusive:
|
||||
* Whilst it is possible to combine events into a group with differing
|
||||
* values of PERF_SAMPLE_READ, it is not valid to have inconsistent
|
||||
* values for `inherit`. Therefore it is not possible to have a
|
||||
* situation where a per-thread event is sampled as a global event;
|
||||
* all !inherit groups are global, and all groups where the sampling
|
||||
* event is inherit + PERF_SAMPLE_READ will be per-thread. Any event
|
||||
* that is part of such a group that is inherit but not PERF_SAMPLE_READ
|
||||
* will be read as per-thread. If such an event can also trigger a
|
||||
* sample (such as with sample_period > 0) then it will not cause
|
||||
* `read_format` to be included in its PERF_RECORD_SAMPLE, and
|
||||
* therefore will not expose the per-thread group members as global.
|
||||
*/
|
||||
union {
|
||||
/*
|
||||
* Holds total ID period value for PERF_SAMPLE_READ processing
|
||||
* (when period is not per-thread).
|
||||
*/
|
||||
u64 period;
|
||||
/*
|
||||
* Holds total ID period value for PERF_SAMPLE_READ processing
|
||||
* (when period is per-thread).
|
||||
*/
|
||||
struct hlist_head periods[PERF_SAMPLE_ID__HLIST_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
struct perf_evsel {
|
||||
@ -58,6 +108,10 @@ struct perf_evsel {
|
||||
u32 ids;
|
||||
struct perf_evsel *leader;
|
||||
|
||||
/* For events where the read_format value is per-thread rather than
|
||||
* global, stores the per-thread cumulative period */
|
||||
struct list_head per_stream_periods;
|
||||
|
||||
/* parse modifier helper */
|
||||
int nr_members;
|
||||
/*
|
||||
@ -88,4 +142,9 @@ int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter);
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
void perf_evsel__free_id(struct perf_evsel *evsel);
|
||||
|
||||
bool perf_evsel__attr_has_per_thread_sample_period(struct perf_evsel *evsel);
|
||||
|
||||
u64 *perf_sample_id__get_period_storage(struct perf_sample_id *sid, u32 tid,
|
||||
bool per_thread);
|
||||
|
||||
#endif /* __LIBPERF_INTERNAL_EVSEL_H */
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <linux/string.h>
|
||||
@ -217,8 +218,40 @@ static int wait_or_whine(struct child_process *cmd, bool block)
|
||||
|
||||
int check_if_command_finished(struct child_process *cmd)
|
||||
{
|
||||
#ifdef __linux__
|
||||
char filename[FILENAME_MAX + 12];
|
||||
char status_line[256];
|
||||
FILE *status_file;
|
||||
|
||||
/*
|
||||
* Check by reading /proc/<pid>/status as calling waitpid causes
|
||||
* stdout/stderr to be closed and data lost.
|
||||
*/
|
||||
sprintf(filename, "/proc/%d/status", cmd->pid);
|
||||
status_file = fopen(filename, "r");
|
||||
if (status_file == NULL) {
|
||||
/* Open failed assume finish_command was called. */
|
||||
return true;
|
||||
}
|
||||
while (fgets(status_line, sizeof(status_line), status_file) != NULL) {
|
||||
char *p;
|
||||
|
||||
if (strncmp(status_line, "State:", 6))
|
||||
continue;
|
||||
|
||||
fclose(status_file);
|
||||
p = status_line + 6;
|
||||
while (isspace(*p))
|
||||
p++;
|
||||
return *p == 'Z' ? 1 : 0;
|
||||
}
|
||||
/* Read failed assume finish_command was called. */
|
||||
fclose(status_file);
|
||||
return 1;
|
||||
#else
|
||||
wait_or_whine(cmd, /*block=*/false);
|
||||
return cmd->finished;
|
||||
#endif
|
||||
}
|
||||
|
||||
int finish_command(struct child_process *cmd)
|
||||
|
@ -20,8 +20,8 @@ static __noreturn inline void die(const char *err, ...)
|
||||
|
||||
va_start(params, err);
|
||||
report(" Fatal: ", err, params);
|
||||
exit(128);
|
||||
va_end(params);
|
||||
exit(128);
|
||||
}
|
||||
|
||||
#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
|
||||
|
6
tools/perf/.gitignore
vendored
6
tools/perf/.gitignore
vendored
@ -39,9 +39,9 @@ trace/beauty/generated/
|
||||
pmu-events/pmu-events.c
|
||||
pmu-events/jevents
|
||||
pmu-events/metric_test.log
|
||||
tests/shell/*.shellcheck_log
|
||||
tests/shell/coresight/*.shellcheck_log
|
||||
tests/shell/lib/*.shellcheck_log
|
||||
pmu-events/empty-pmu-events.log
|
||||
pmu-events/test-empty-pmu-events.c
|
||||
*.shellcheck_log
|
||||
feature/
|
||||
libapi/
|
||||
libbpf/
|
||||
|
@ -1,6 +1,6 @@
|
||||
i synthesize instructions events
|
||||
y synthesize cycles events
|
||||
b synthesize branches events (branch misses for Arm SPE)
|
||||
b synthesize branches events
|
||||
c synthesize branches events (calls only)
|
||||
r synthesize branches events (returns only)
|
||||
x synthesize transactions events
|
||||
|
@ -187,7 +187,7 @@ groups:
|
||||
7 llc-access
|
||||
2 tlb-miss
|
||||
1K tlb-access
|
||||
36 branch-miss
|
||||
36 branch
|
||||
0 remote-access
|
||||
900 memory
|
||||
|
||||
|
@ -47,15 +47,15 @@ feature::
|
||||
bpf / HAVE_LIBBPF_SUPPORT
|
||||
bpf_skeletons / HAVE_BPF_SKEL
|
||||
debuginfod / HAVE_DEBUGINFOD_SUPPORT
|
||||
dwarf / HAVE_DWARF_SUPPORT
|
||||
dwarf_getlocations / HAVE_DWARF_GETLOCATIONS_SUPPORT
|
||||
dwarf / HAVE_LIBDW_SUPPORT
|
||||
dwarf_getlocations / HAVE_LIBDW_SUPPORT
|
||||
dwarf-unwind / HAVE_DWARF_UNWIND_SUPPORT
|
||||
auxtrace / HAVE_AUXTRACE_SUPPORT
|
||||
libaudit / HAVE_LIBAUDIT_SUPPORT
|
||||
libbfd / HAVE_LIBBFD_SUPPORT
|
||||
libcapstone / HAVE_LIBCAPSTONE_SUPPORT
|
||||
libcrypto / HAVE_LIBCRYPTO_SUPPORT
|
||||
libdw-dwarf-unwind / HAVE_DWARF_SUPPORT
|
||||
libdw-dwarf-unwind / HAVE_LIBDW_SUPPORT
|
||||
libelf / HAVE_LIBELF_SUPPORT
|
||||
libnuma / HAVE_LIBNUMA_SUPPORT
|
||||
libopencsd / HAVE_CSTRACE_SUPPORT
|
||||
|
@ -247,6 +247,19 @@ annotate.*::
|
||||
These are in control of addresses, jump function, source code
|
||||
in lines of assembly code from a specific program.
|
||||
|
||||
annotate.disassemblers::
|
||||
Choose the disassembler to use: "objdump", "llvm", "capstone",
|
||||
if not specified it will first try, if available, the "llvm" one,
|
||||
then, if it fails, "capstone", and finally the original "objdump"
|
||||
based one.
|
||||
|
||||
Choosing a different one is useful when handling some feature that
|
||||
is known to be best support at some point by one of the options,
|
||||
to compare the output when in doubt about some bug, etc.
|
||||
|
||||
This can be a list, in order of preference, the first one that works
|
||||
finishes the process.
|
||||
|
||||
annotate.addr2line::
|
||||
addr2line binary to use for file names and line numbers.
|
||||
|
||||
|
@ -8,7 +8,7 @@ perf-list - List all symbolic event types
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf list' [--no-desc] [--long-desc]
|
||||
'perf list' [<options>]
|
||||
[hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]
|
||||
|
||||
DESCRIPTION
|
||||
@ -243,6 +243,21 @@ For accessing trace point events perf needs to have read access to
|
||||
/sys/kernel/tracing, even when perf_event_paranoid is in a relaxed
|
||||
setting.
|
||||
|
||||
TOOL/HWMON EVENTS
|
||||
-----------------
|
||||
|
||||
Some events don't have an associated PMU instead reading values
|
||||
available to software without perf_event_open. As these events don't
|
||||
support sampling they can only really be read by tools like perf stat.
|
||||
|
||||
Tool events provide times and certain system parameters. Examples
|
||||
include duration_time, user_time, system_time and num_cpus_online.
|
||||
|
||||
Hwmon events provide easy access to hwmon sysfs data typically in
|
||||
/sys/class/hwmon. This information includes temperatures, fan speeds
|
||||
and energy usage.
|
||||
|
||||
|
||||
TRACING
|
||||
-------
|
||||
|
||||
|
@ -391,6 +391,14 @@ OPTIONS
|
||||
This allows to examine the path the program took to each sample.
|
||||
The data collection must have used -b (or -j) and -g.
|
||||
|
||||
Also show with some branch flags that can be:
|
||||
- Predicted: display the average percentage of predicated branches.
|
||||
(predicated number / total number)
|
||||
- Abort: display the number of tsx aborted branches.
|
||||
- Cycles: cycles in basic block.
|
||||
|
||||
- iterations: display the average number of iterations in callchain list.
|
||||
|
||||
--addr2line=<path>::
|
||||
Path to addr2line binary.
|
||||
|
||||
|
@ -221,6 +221,14 @@ OPTIONS for 'perf sched timehist'
|
||||
priorities are specified with -: 120-129. A combination of both can also be
|
||||
provided: 0,120-129.
|
||||
|
||||
-P::
|
||||
--pre-migrations::
|
||||
Show pre-migration wait time. pre-migration wait time is the time spent
|
||||
by a task waiting on a runqueue but not getting the chance to run there
|
||||
and is migrated to a different runqueue where it is finally run. This
|
||||
time between sched_wakeup and migrate_task is the pre-migration wait
|
||||
time.
|
||||
|
||||
OPTIONS for 'perf sched replay'
|
||||
------------------------------
|
||||
|
||||
|
@ -624,7 +624,7 @@ as perf_trace_context.perf_script_context .
|
||||
perf_set_itrace_options(context, itrace_options) - set --itrace options if they have not been set already
|
||||
perf_sample_srcline(context) - returns source_file_name, line_number
|
||||
perf_sample_srccode(context) - returns source_file_name, line_number, source_line
|
||||
|
||||
perf_config_get(config_name) - returns the value of the named config item, or None if unset
|
||||
|
||||
Util.py Module
|
||||
~~~~~~~~~~~~~~
|
||||
|
@ -48,3 +48,20 @@ OPTIONS
|
||||
|
||||
--dso::
|
||||
Specify a DSO for the "Symbols" test.
|
||||
|
||||
-w::
|
||||
--workload=::
|
||||
Run a built-in workload, to list them use '--list-workloads', current ones include:
|
||||
noploop, thloop, leafloop, sqrtloop, brstack, datasym and landlock.
|
||||
|
||||
Used with the shell script regression tests.
|
||||
|
||||
Some accept an extra parameter:
|
||||
|
||||
seconds: leafloop, noploop, sqrtloop, thloop
|
||||
nrloops: brstack
|
||||
|
||||
The datasym and landlock workloads don't accept any.
|
||||
|
||||
--list-workloads::
|
||||
List the available workloads to use with -w/--workload.
|
||||
|
@ -31,7 +31,7 @@ $(call detected_var,SRCARCH)
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
NO_SYSCALL_TABLE := 1
|
||||
|
||||
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 powerpc arm64 s390 mips loongarch))
|
||||
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 powerpc arm64 s390 mips loongarch riscv))
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
|
||||
@ -83,6 +83,10 @@ ifeq ($(ARCH),mips)
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-mips
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),riscv)
|
||||
CFLAGS += -I$(OUTPUT)arch/riscv/include/generated
|
||||
endif
|
||||
|
||||
# So far there's only x86 and arm libdw unwind support merged in perf.
|
||||
# Disable it on all other architectures in case libdw unwind
|
||||
# support is detected in system. Add supported architectures
|
||||
@ -91,6 +95,10 @@ ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv loon
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
ifneq ($(LIBUNWIND),1)
|
||||
NO_LIBUNWIND := 1
|
||||
endif
|
||||
|
||||
ifeq ($(LIBUNWIND_LIBS),)
|
||||
NO_LIBUNWIND := 1
|
||||
endif
|
||||
@ -162,8 +170,8 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
|
||||
# Must put -ldl after -lebl for dependency
|
||||
DWARFLIBS += -ldl
|
||||
endif
|
||||
FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS)
|
||||
FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) $(DWARFLIBS)
|
||||
FEATURE_CHECK_CFLAGS-libdw := $(LIBDW_CFLAGS)
|
||||
FEATURE_CHECK_LDFLAGS-libdw := $(LIBDW_LDFLAGS) $(DWARFLIBS)
|
||||
|
||||
# for linking with debug library, run like:
|
||||
# make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/
|
||||
@ -203,10 +211,6 @@ FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arc
|
||||
# include ARCH specific config
|
||||
-include $(src-perf)/arch/$(SRCARCH)/Makefile
|
||||
|
||||
ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
|
||||
CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
|
||||
endif
|
||||
|
||||
include $(srctree)/tools/scripts/utilities.mak
|
||||
|
||||
ifeq ($(call get-executable,$(FLEX)),)
|
||||
@ -426,7 +430,7 @@ ifeq ($(feature-file-handle), 1)
|
||||
endif
|
||||
|
||||
ifdef NO_LIBELF
|
||||
NO_DWARF := 1
|
||||
NO_LIBDW := 1
|
||||
NO_LIBUNWIND := 1
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
NO_LIBBPF := 1
|
||||
@ -461,28 +465,11 @@ else
|
||||
endif
|
||||
endif
|
||||
else
|
||||
ifndef NO_LIBDW_DWARF_UNWIND
|
||||
ifneq ($(feature-libdw-dwarf-unwind),1)
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
$(warning No libdw DWARF unwind found, Please install elfutils-devel/libdw-dev >= 0.158 and/or set LIBDW_DIR)
|
||||
ifneq ($(feature-libdw), 1)
|
||||
ifndef NO_LIBDW
|
||||
$(warning No libdw.h found or old libdw.h found or elfutils is older than 0.157, disables dwarf support. Please install new elfutils-devel/libdw-dev)
|
||||
NO_LIBDW := 1
|
||||
endif
|
||||
endif
|
||||
ifneq ($(feature-dwarf), 1)
|
||||
ifndef NO_DWARF
|
||||
$(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev)
|
||||
NO_DWARF := 1
|
||||
endif
|
||||
else
|
||||
ifneq ($(feature-dwarf_getlocations), 1)
|
||||
$(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.157)
|
||||
else
|
||||
CFLAGS += -DHAVE_DWARF_GETLOCATIONS_SUPPORT
|
||||
endif # dwarf_getlocations
|
||||
ifneq ($(feature-dwarf_getcfi), 1)
|
||||
$(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.142)
|
||||
else
|
||||
CFLAGS += -DHAVE_DWARF_CFI_SUPPORT
|
||||
endif # dwarf_getcfi
|
||||
endif # Dwarf support
|
||||
endif # libelf support
|
||||
endif # NO_LIBELF
|
||||
@ -493,7 +480,7 @@ ifeq ($(feature-libaio), 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_DWARF
|
||||
ifdef NO_LIBDW
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
@ -571,17 +558,12 @@ ifndef NO_LIBELF
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_DWARF
|
||||
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
|
||||
$(warning DWARF register mappings have not been defined for architecture $(SRCARCH), DWARF support disabled)
|
||||
NO_DWARF := 1
|
||||
else
|
||||
CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS)
|
||||
LDFLAGS += $(LIBDW_LDFLAGS)
|
||||
EXTLIBS += ${DWARFLIBS}
|
||||
$(call detected,CONFIG_DWARF)
|
||||
endif # PERF_HAVE_DWARF_REGS
|
||||
endif # NO_DWARF
|
||||
ifndef NO_LIBDW
|
||||
CFLAGS += -DHAVE_LIBDW_SUPPORT $(LIBDW_CFLAGS)
|
||||
LDFLAGS += $(LIBDW_LDFLAGS)
|
||||
EXTLIBS += ${DWARFLIBS}
|
||||
$(call detected,CONFIG_LIBDW)
|
||||
endif # NO_LIBDW
|
||||
|
||||
ifndef NO_LIBBPF
|
||||
ifeq ($(feature-bpf), 1)
|
||||
@ -630,7 +612,7 @@ ifdef PERF_HAVE_JITDUMP
|
||||
endif
|
||||
|
||||
ifeq ($(SRCARCH),powerpc)
|
||||
ifndef NO_DWARF
|
||||
ifndef NO_LIBDW
|
||||
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
|
||||
endif
|
||||
endif
|
||||
@ -750,8 +732,6 @@ endif
|
||||
ifeq ($(dwarf-post-unwind),1)
|
||||
CFLAGS += -DHAVE_DWARF_UNWIND_SUPPORT
|
||||
$(call detected,CONFIG_DWARF_UNWIND)
|
||||
else
|
||||
NO_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
ifndef NO_LOCAL_LIBUNWIND
|
||||
@ -1194,7 +1174,7 @@ endif
|
||||
ifneq ($(NO_LIBTRACEEVENT),1)
|
||||
$(call feature_check,libtraceevent)
|
||||
ifeq ($(feature-libtraceevent), 1)
|
||||
CFLAGS += -DHAVE_LIBTRACEEVENT
|
||||
CFLAGS += -DHAVE_LIBTRACEEVENT $(shell $(PKG_CONFIG) --cflags libtraceevent)
|
||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L libtraceevent)
|
||||
EXTLIBS += $(shell $(PKG_CONFIG) --libs-only-l libtraceevent)
|
||||
LIBTRACEEVENT_VERSION := $(shell $(PKG_CONFIG) --modversion libtraceevent).0.0
|
||||
|
@ -40,7 +40,7 @@ include ../scripts/utilities.mak
|
||||
#
|
||||
# Define EXTRA_PERFLIBS to pass extra libraries to PERFLIBS.
|
||||
#
|
||||
# Define NO_DWARF if you do not want debug-info analysis feature at all.
|
||||
# Define NO_LIBDW if you do not want debug-info analysis feature at all.
|
||||
#
|
||||
# Define WERROR=0 to disable treating any warnings as errors.
|
||||
#
|
||||
@ -52,7 +52,7 @@ include ../scripts/utilities.mak
|
||||
#
|
||||
# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
|
||||
#
|
||||
# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf
|
||||
# Define LIBUNWIND if you do not want libunwind dependency for dwarf
|
||||
# backtrace post unwind.
|
||||
#
|
||||
# Define NO_BACKTRACE if you do not want stack backtrace debug feature
|
||||
@ -1128,12 +1128,11 @@ endif
|
||||
install-tests: all install-gtk
|
||||
$(call QUIET_INSTALL, tests) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||
$(INSTALL) tests/attr.py -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||
$(INSTALL) tests/pe-file.exe* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
|
||||
$(INSTALL) tests/attr/* -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \
|
||||
$(INSTALL) tests/shell/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/attr'; \
|
||||
$(INSTALL) tests/shell/attr/* -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/attr'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \
|
||||
$(INSTALL) tests/shell/lib/*.sh -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \
|
||||
$(INSTALL) tests/shell/lib/*.py -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \
|
||||
|
@ -5,5 +5,7 @@ static int arc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
{
|
||||
arch->initialized = true;
|
||||
arch->objdump.comment_char = ';';
|
||||
arch->e_machine = EM_ARC;
|
||||
arch->e_flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@ -53,6 +53,8 @@ static int arm__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = arm__associate_instruction_ops;
|
||||
arch->objdump.comment_char = ';';
|
||||
arch->objdump.skip_functions_char = '+';
|
||||
arch->e_machine = EM_ARM;
|
||||
arch->e_flags = 0;
|
||||
return 0;
|
||||
|
||||
out_free_call:
|
||||
|
@ -1,7 +1,5 @@
|
||||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Will Deacon, ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = __stringify(%r##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
REG_DWARFNUM_NAME("%fp", 11),
|
||||
REG_DWARFNUM_NAME("%ip", 12),
|
||||
REG_DWARFNUM_NAME("%sp", 13),
|
||||
REG_DWARFNUM_NAME("%lr", 14),
|
||||
REG_DWARFNUM_NAME("%pc", 15),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
||||
#
|
||||
|
@ -113,6 +113,8 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = arm64__associate_instruction_ops;
|
||||
arch->objdump.comment_char = '/';
|
||||
arch->objdump.skip_functions_char = '+';
|
||||
arch->e_machine = EM_AARCH64;
|
||||
arch->e_flags = 0;
|
||||
return 0;
|
||||
|
||||
out_free_call:
|
||||
|
@ -4,7 +4,6 @@ perf-util-y += perf_regs.o
|
||||
perf-util-y += tsc.o
|
||||
perf-util-y += pmu.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
|
@ -23,9 +23,12 @@
|
||||
#include "../../../util/debug.h"
|
||||
#include "../../../util/auxtrace.h"
|
||||
#include "../../../util/record.h"
|
||||
#include "../../../util/header.h"
|
||||
#include "../../../util/arm-spe.h"
|
||||
#include <tools/libc_compat.h> // reallocarray
|
||||
|
||||
#define ARM_SPE_CPU_MAGIC 0x1010101010101010ULL
|
||||
|
||||
#define KiB(x) ((x) * 1024)
|
||||
#define MiB(x) ((x) * 1024 * 1024)
|
||||
|
||||
@ -37,11 +40,84 @@ struct arm_spe_recording {
|
||||
bool *wrapped;
|
||||
};
|
||||
|
||||
/*
|
||||
* arm_spe_find_cpus() returns a new cpu map, and the caller should invoke
|
||||
* perf_cpu_map__put() to release the map after use.
|
||||
*/
|
||||
static struct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist)
|
||||
{
|
||||
struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
|
||||
struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
|
||||
struct perf_cpu_map *intersect_cpus;
|
||||
|
||||
/* cpu map is not "any" CPU , we have specific CPUs to work with */
|
||||
if (!perf_cpu_map__has_any_cpu(event_cpus)) {
|
||||
intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
|
||||
perf_cpu_map__put(online_cpus);
|
||||
/* Event can be "any" CPU so count all CPUs. */
|
||||
} else {
|
||||
intersect_cpus = online_cpus;
|
||||
}
|
||||
|
||||
return intersect_cpus;
|
||||
}
|
||||
|
||||
static size_t
|
||||
arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused,
|
||||
struct evlist *evlist __maybe_unused)
|
||||
struct evlist *evlist)
|
||||
{
|
||||
return ARM_SPE_AUXTRACE_PRIV_SIZE;
|
||||
struct perf_cpu_map *cpu_map = arm_spe_find_cpus(evlist);
|
||||
size_t size;
|
||||
|
||||
if (!cpu_map)
|
||||
return 0;
|
||||
|
||||
size = ARM_SPE_AUXTRACE_PRIV_MAX +
|
||||
ARM_SPE_CPU_PRIV_MAX * perf_cpu_map__nr(cpu_map);
|
||||
size *= sizeof(u64);
|
||||
|
||||
perf_cpu_map__put(cpu_map);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int arm_spe_save_cpu_header(struct auxtrace_record *itr,
|
||||
struct perf_cpu cpu, __u64 data[])
|
||||
{
|
||||
struct arm_spe_recording *sper =
|
||||
container_of(itr, struct arm_spe_recording, itr);
|
||||
struct perf_pmu *pmu = NULL;
|
||||
char *cpuid = NULL;
|
||||
u64 val;
|
||||
|
||||
/* Read CPU MIDR */
|
||||
cpuid = get_cpuid_allow_env_override(cpu);
|
||||
if (!cpuid)
|
||||
return -ENOMEM;
|
||||
val = strtol(cpuid, NULL, 16);
|
||||
|
||||
data[ARM_SPE_MAGIC] = ARM_SPE_CPU_MAGIC;
|
||||
data[ARM_SPE_CPU] = cpu.cpu;
|
||||
data[ARM_SPE_CPU_NR_PARAMS] = ARM_SPE_CPU_PRIV_MAX - ARM_SPE_CPU_MIDR;
|
||||
data[ARM_SPE_CPU_MIDR] = val;
|
||||
|
||||
/* Find the associate Arm SPE PMU for the CPU */
|
||||
if (perf_cpu_map__has(sper->arm_spe_pmu->cpus, cpu))
|
||||
pmu = sper->arm_spe_pmu;
|
||||
|
||||
if (!pmu) {
|
||||
/* No Arm SPE PMU is found */
|
||||
data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX;
|
||||
data[ARM_SPE_CAP_MIN_IVAL] = 0;
|
||||
} else {
|
||||
data[ARM_SPE_CPU_PMU_TYPE] = pmu->type;
|
||||
|
||||
if (perf_pmu__scan_file(pmu, "caps/min_interval", "%lu", &val) != 1)
|
||||
val = 0;
|
||||
data[ARM_SPE_CAP_MIN_IVAL] = val;
|
||||
}
|
||||
|
||||
free(cpuid);
|
||||
return ARM_SPE_CPU_PRIV_MAX;
|
||||
}
|
||||
|
||||
static int arm_spe_info_fill(struct auxtrace_record *itr,
|
||||
@ -49,20 +125,46 @@ static int arm_spe_info_fill(struct auxtrace_record *itr,
|
||||
struct perf_record_auxtrace_info *auxtrace_info,
|
||||
size_t priv_size)
|
||||
{
|
||||
int i, ret;
|
||||
size_t offset;
|
||||
struct arm_spe_recording *sper =
|
||||
container_of(itr, struct arm_spe_recording, itr);
|
||||
struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
|
||||
struct perf_cpu_map *cpu_map;
|
||||
struct perf_cpu cpu;
|
||||
__u64 *data;
|
||||
|
||||
if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE)
|
||||
if (priv_size != arm_spe_info_priv_size(itr, session->evlist))
|
||||
return -EINVAL;
|
||||
|
||||
if (!session->evlist->core.nr_mmaps)
|
||||
return -EINVAL;
|
||||
|
||||
auxtrace_info->type = PERF_AUXTRACE_ARM_SPE;
|
||||
auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type;
|
||||
cpu_map = arm_spe_find_cpus(session->evlist);
|
||||
if (!cpu_map)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
auxtrace_info->type = PERF_AUXTRACE_ARM_SPE;
|
||||
auxtrace_info->priv[ARM_SPE_HEADER_VERSION] = ARM_SPE_HEADER_CURRENT_VERSION;
|
||||
auxtrace_info->priv[ARM_SPE_HEADER_SIZE] =
|
||||
ARM_SPE_AUXTRACE_PRIV_MAX - ARM_SPE_HEADER_VERSION;
|
||||
auxtrace_info->priv[ARM_SPE_PMU_TYPE_V2] = arm_spe_pmu->type;
|
||||
auxtrace_info->priv[ARM_SPE_CPUS_NUM] = perf_cpu_map__nr(cpu_map);
|
||||
|
||||
offset = ARM_SPE_AUXTRACE_PRIV_MAX;
|
||||
perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
|
||||
assert(offset < priv_size);
|
||||
data = &auxtrace_info->priv[offset];
|
||||
ret = arm_spe_save_cpu_header(itr, cpu, data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
perf_cpu_map__put(cpu_map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -188,9 +290,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel__is_aux_event(evsel)) {
|
||||
if (!strstarts(evsel->pmu_name, ARM_SPE_PMU_NAME)) {
|
||||
if (!strstarts(evsel->pmu->name, ARM_SPE_PMU_NAME)) {
|
||||
pr_err("Found unexpected auxtrace event: %s\n",
|
||||
evsel->pmu_name);
|
||||
evsel->pmu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
opts->full_auxtrace = true;
|
||||
|
@ -1,92 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Will Deacon, ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <dwarf-regs.h>
|
||||
#include <linux/ptrace.h> /* for struct user_pt_regs */
|
||||
#include <linux/stringify.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = __stringify(%x##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
#define DWARFNUM2OFFSET(index) \
|
||||
(index * sizeof((struct user_pt_regs *)0)->regs[0])
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
GPR_DWARFNUM_NAME(11),
|
||||
GPR_DWARFNUM_NAME(12),
|
||||
GPR_DWARFNUM_NAME(13),
|
||||
GPR_DWARFNUM_NAME(14),
|
||||
GPR_DWARFNUM_NAME(15),
|
||||
GPR_DWARFNUM_NAME(16),
|
||||
GPR_DWARFNUM_NAME(17),
|
||||
GPR_DWARFNUM_NAME(18),
|
||||
GPR_DWARFNUM_NAME(19),
|
||||
GPR_DWARFNUM_NAME(20),
|
||||
GPR_DWARFNUM_NAME(21),
|
||||
GPR_DWARFNUM_NAME(22),
|
||||
GPR_DWARFNUM_NAME(23),
|
||||
GPR_DWARFNUM_NAME(24),
|
||||
GPR_DWARFNUM_NAME(25),
|
||||
GPR_DWARFNUM_NAME(26),
|
||||
GPR_DWARFNUM_NAME(27),
|
||||
GPR_DWARFNUM_NAME(28),
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
REG_DWARFNUM_NAME("%lr", 30),
|
||||
REG_DWARFNUM_NAME("%sp", 31),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return DWARFNUM2OFFSET(roff->dwarfnum);
|
||||
return -EINVAL;
|
||||
}
|
@ -14,73 +14,66 @@
|
||||
#define MIDR_REVISION_MASK GENMASK(3, 0)
|
||||
#define MIDR_VARIANT_MASK GENMASK(23, 20)
|
||||
|
||||
static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
|
||||
static int _get_cpuid(char *buf, size_t sz, struct perf_cpu cpu)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
FILE *file;
|
||||
const char *sysfs = sysfs__mountpoint();
|
||||
struct perf_cpu cpu;
|
||||
int idx, ret = EINVAL;
|
||||
|
||||
assert(cpu.cpu != -1);
|
||||
if (!sysfs || sz < MIDR_SIZE)
|
||||
return EINVAL;
|
||||
|
||||
perf_cpu_map__for_each_cpu(cpu, idx, cpus) {
|
||||
char path[PATH_MAX];
|
||||
FILE *file;
|
||||
scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, sysfs, cpu.cpu);
|
||||
|
||||
scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR,
|
||||
sysfs, cpu.cpu);
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file) {
|
||||
pr_debug("fopen failed for file %s\n", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fgets(buf, MIDR_SIZE, file)) {
|
||||
fclose(file);
|
||||
continue;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
/* got midr break loop */
|
||||
ret = 0;
|
||||
break;
|
||||
file = fopen(path, "r");
|
||||
if (!file) {
|
||||
pr_debug("fopen failed for file %s\n", path);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (!fgets(buf, MIDR_SIZE, file)) {
|
||||
pr_debug("Failed to read file %s\n", path);
|
||||
fclose(file);
|
||||
return EINVAL;
|
||||
}
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_cpuid(char *buf, size_t sz)
|
||||
int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu)
|
||||
{
|
||||
struct perf_cpu_map *cpus = perf_cpu_map__new_online_cpus();
|
||||
int ret;
|
||||
struct perf_cpu_map *cpus;
|
||||
int idx;
|
||||
|
||||
if (cpu.cpu != -1)
|
||||
return _get_cpuid(buf, sz, cpu);
|
||||
|
||||
cpus = perf_cpu_map__new_online_cpus();
|
||||
if (!cpus)
|
||||
return EINVAL;
|
||||
|
||||
ret = _get_cpuid(buf, sz, cpus);
|
||||
perf_cpu_map__for_each_cpu(cpu, idx, cpus) {
|
||||
int ret = _get_cpuid(buf, sz, cpu);
|
||||
|
||||
perf_cpu_map__put(cpus);
|
||||
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
char *get_cpuid_str(struct perf_pmu *pmu)
|
||||
char *get_cpuid_str(struct perf_cpu cpu)
|
||||
{
|
||||
char *buf = NULL;
|
||||
char *buf = malloc(MIDR_SIZE);
|
||||
int res;
|
||||
|
||||
if (!pmu || !pmu->cpus)
|
||||
return NULL;
|
||||
|
||||
buf = malloc(MIDR_SIZE);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
/* read midr from list of cpus mapped to this pmu */
|
||||
res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus);
|
||||
res = get_cpuid(buf, MIDR_SIZE, cpu);
|
||||
if (res) {
|
||||
pr_err("failed to get cpuid string for PMU %s\n", pmu->name);
|
||||
pr_err("failed to get cpuid string for CPU %d\n", cpu.cpu);
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
@ -1,30 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <internal/cpumap.h>
|
||||
#include "../../../util/cpumap.h"
|
||||
#include "../../../util/header.h"
|
||||
#include "../../../util/pmu.h"
|
||||
#include "../../../util/pmus.h"
|
||||
#include "../../../util/tool_pmu.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include <math.h>
|
||||
|
||||
const struct pmu_metrics_table *pmu_metrics_table__find(void)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
/* Metrics aren't currently supported on heterogeneous Arm systems */
|
||||
if (perf_pmus__num_core_pmus() > 1)
|
||||
return NULL;
|
||||
|
||||
/* Doesn't matter which one here because they'll all be the same */
|
||||
pmu = perf_pmus__find_core_pmu();
|
||||
if (pmu)
|
||||
return perf_pmu__find_metrics_table(pmu);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double perf_pmu__cpu_slots_per_cycle(void)
|
||||
u64 tool_pmu__cpu_slots_per_cycle(void)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
unsigned long long slots = 0;
|
||||
@ -41,5 +22,5 @@ double perf_pmu__cpu_slots_per_cycle(void)
|
||||
filename__read_ull(path, &slots);
|
||||
}
|
||||
|
||||
return slots ? (double)slots : NAN;
|
||||
return slots;
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
@ -43,6 +43,11 @@ static int csky__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->initialized = true;
|
||||
arch->objdump.comment_char = '/';
|
||||
arch->associate_instruction_ops = csky__associate_ins_ops;
|
||||
|
||||
arch->e_machine = EM_CSKY;
|
||||
#if defined(__CSKYABIV2__)
|
||||
arch->e_flags = EF_CSKY_ABIV2;
|
||||
#else
|
||||
arch->e_flags = EF_CSKY_ABIV1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
@ -1,8 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
||||
|
@ -131,6 +131,8 @@ int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = loongarch__associate_ins_ops;
|
||||
arch->initialized = true;
|
||||
arch->objdump.comment_char = '#';
|
||||
arch->e_machine = EM_LOONGARCH;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,7 +1,6 @@
|
||||
perf-util-y += header.o
|
||||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
|
@ -1,44 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h> /* for EINVAL */
|
||||
#include <string.h> /* for strcmp */
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
static struct pt_regs_dwarfnum loongarch_gpr_table[] = {
|
||||
{"%r0", 0}, {"%r1", 1}, {"%r2", 2}, {"%r3", 3},
|
||||
{"%r4", 4}, {"%r5", 5}, {"%r6", 6}, {"%r7", 7},
|
||||
{"%r8", 8}, {"%r9", 9}, {"%r10", 10}, {"%r11", 11},
|
||||
{"%r12", 12}, {"%r13", 13}, {"%r14", 14}, {"%r15", 15},
|
||||
{"%r16", 16}, {"%r17", 17}, {"%r18", 18}, {"%r19", 19},
|
||||
{"%r20", 20}, {"%r21", 21}, {"%r22", 22}, {"%r23", 23},
|
||||
{"%r24", 24}, {"%r25", 25}, {"%r26", 26}, {"%r27", 27},
|
||||
{"%r28", 28}, {"%r29", 29}, {"%r30", 30}, {"%r31", 31},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
n %= 32;
|
||||
return loongarch_gpr_table[n].name;
|
||||
}
|
||||
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
|
||||
for (roff = loongarch_gpr_table; roff->name != NULL; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return roff->dwarfnum;
|
||||
return -EINVAL;
|
||||
}
|
@ -70,7 +70,7 @@ static char *_get_cpuid(void)
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
int get_cpuid(char *buffer, size_t sz)
|
||||
int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
char *cpuid = _get_cpuid();
|
||||
@ -90,7 +90,7 @@ int get_cpuid(char *buffer, size_t sz)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||
char *get_cpuid_str(struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
return _get_cpuid();
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
|
||||
# Syscall table generation for perf
|
||||
out := $(OUTPUT)arch/mips/include/generated/asm
|
||||
header := $(out)/syscalls_n64.c
|
||||
|
@ -40,6 +40,8 @@ int mips__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = mips__associate_ins_ops;
|
||||
arch->initialized = true;
|
||||
arch->objdump.comment_char = '#';
|
||||
arch->e_machine = EM_MIPS;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,3 +1,2 @@
|
||||
perf-util-y += perf_regs.o
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
|
@ -1,38 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2013 Cavium, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
static const char *mips_gpr_names[32] = {
|
||||
"$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",
|
||||
"$10", "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19",
|
||||
"$20", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29",
|
||||
"$30", "$31"
|
||||
};
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
if (n < 32)
|
||||
return mips_gpr_names[n];
|
||||
if (n == 64)
|
||||
return "hi";
|
||||
if (n == 65)
|
||||
return "lo";
|
||||
return NULL;
|
||||
}
|
@ -1,10 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
||||
#
|
||||
|
@ -255,7 +255,7 @@ static struct ins_ops *check_ppc_insn(struct disasm_line *dl)
|
||||
* is moved to r31. update_insn_state_powerpc tracks these state
|
||||
* changes
|
||||
*/
|
||||
#ifdef HAVE_DWARF_SUPPORT
|
||||
#ifdef HAVE_LIBDW_SUPPORT
|
||||
static void update_insn_state_powerpc(struct type_state *state,
|
||||
struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,
|
||||
struct disasm_line *dl)
|
||||
@ -300,7 +300,7 @@ static void update_insn_state_powerpc(struct type_state *state,
|
||||
insn_offset, src->reg1, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
#endif /* HAVE_DWARF_SUPPORT */
|
||||
#endif /* HAVE_LIBDW_SUPPORT */
|
||||
|
||||
static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
{
|
||||
@ -309,6 +309,8 @@ static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = powerpc__associate_instruction_ops;
|
||||
arch->objdump.comment_char = '#';
|
||||
annotate_opts.show_asm_raw = true;
|
||||
arch->e_machine = EM_PPC;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -7,8 +7,7 @@ perf-util-y += sym-handling.o
|
||||
perf-util-y += evsel.o
|
||||
perf-util-y += event.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_DWARF) += skip-callchain-idx.o
|
||||
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
|
||||
|
||||
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
@ -1,153 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Ian Munsie, IBM Corporation.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <dwarf-regs.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
unsigned int ptregs_offset;
|
||||
};
|
||||
|
||||
#define REG_DWARFNUM_NAME(r, num) \
|
||||
{.name = __stringify(%)__stringify(r), .dwarfnum = num, \
|
||||
.ptregs_offset = offsetof(struct pt_regs, r)}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = __stringify(%gpr##num), .dwarfnum = num, \
|
||||
.ptregs_offset = offsetof(struct pt_regs, gpr[num])}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0, .ptregs_offset = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
GPR_DWARFNUM_NAME(11),
|
||||
GPR_DWARFNUM_NAME(12),
|
||||
GPR_DWARFNUM_NAME(13),
|
||||
GPR_DWARFNUM_NAME(14),
|
||||
GPR_DWARFNUM_NAME(15),
|
||||
GPR_DWARFNUM_NAME(16),
|
||||
GPR_DWARFNUM_NAME(17),
|
||||
GPR_DWARFNUM_NAME(18),
|
||||
GPR_DWARFNUM_NAME(19),
|
||||
GPR_DWARFNUM_NAME(20),
|
||||
GPR_DWARFNUM_NAME(21),
|
||||
GPR_DWARFNUM_NAME(22),
|
||||
GPR_DWARFNUM_NAME(23),
|
||||
GPR_DWARFNUM_NAME(24),
|
||||
GPR_DWARFNUM_NAME(25),
|
||||
GPR_DWARFNUM_NAME(26),
|
||||
GPR_DWARFNUM_NAME(27),
|
||||
GPR_DWARFNUM_NAME(28),
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
GPR_DWARFNUM_NAME(30),
|
||||
GPR_DWARFNUM_NAME(31),
|
||||
REG_DWARFNUM_NAME(msr, 66),
|
||||
REG_DWARFNUM_NAME(ctr, 109),
|
||||
REG_DWARFNUM_NAME(link, 108),
|
||||
REG_DWARFNUM_NAME(xer, 101),
|
||||
REG_DWARFNUM_NAME(dar, 119),
|
||||
REG_DWARFNUM_NAME(dsisr, 118),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return roff->ptregs_offset;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define PPC_OP(op) (((op) >> 26) & 0x3F)
|
||||
#define PPC_RA(a) (((a) >> 16) & 0x1f)
|
||||
#define PPC_RT(t) (((t) >> 21) & 0x1f)
|
||||
#define PPC_RB(b) (((b) >> 11) & 0x1f)
|
||||
#define PPC_D(D) ((D) & 0xfffe)
|
||||
#define PPC_DS(DS) ((DS) & 0xfffc)
|
||||
#define OP_LD 58
|
||||
#define OP_STD 62
|
||||
|
||||
static int get_source_reg(u32 raw_insn)
|
||||
{
|
||||
return PPC_RA(raw_insn);
|
||||
}
|
||||
|
||||
static int get_target_reg(u32 raw_insn)
|
||||
{
|
||||
return PPC_RT(raw_insn);
|
||||
}
|
||||
|
||||
static int get_offset_opcode(u32 raw_insn)
|
||||
{
|
||||
int opcode = PPC_OP(raw_insn);
|
||||
|
||||
/* DS- form */
|
||||
if ((opcode == OP_LD) || (opcode == OP_STD))
|
||||
return PPC_DS(raw_insn);
|
||||
else
|
||||
return PPC_D(raw_insn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the required fields for op_loc depending on if it
|
||||
* is a source or target.
|
||||
* D form: ins RT,D(RA) -> src_reg1 = RA, offset = D, dst_reg1 = RT
|
||||
* DS form: ins RT,DS(RA) -> src_reg1 = RA, offset = DS, dst_reg1 = RT
|
||||
* X form: ins RT,RA,RB -> src_reg1 = RA, src_reg2 = RB, dst_reg1 = RT
|
||||
*/
|
||||
void get_powerpc_regs(u32 raw_insn, int is_source,
|
||||
struct annotated_op_loc *op_loc)
|
||||
{
|
||||
if (is_source)
|
||||
op_loc->reg1 = get_source_reg(raw_insn);
|
||||
else
|
||||
op_loc->reg1 = get_target_reg(raw_insn);
|
||||
|
||||
if (op_loc->multi_regs)
|
||||
op_loc->reg2 = PPC_RB(raw_insn);
|
||||
|
||||
/* TODO: Implement offset handling for X Form */
|
||||
if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31))
|
||||
op_loc->offset = get_offset_opcode(raw_insn);
|
||||
}
|
@ -10,9 +10,21 @@
|
||||
#include "utils_header.h"
|
||||
#include "metricgroup.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
static bool is_compat_mode(void)
|
||||
{
|
||||
u64 base_platform = getauxval(AT_BASE_PLATFORM);
|
||||
u64 platform = getauxval(AT_PLATFORM);
|
||||
|
||||
if (!strcmp((char *)platform, (char *)base_platform))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
unsigned long pvr;
|
||||
int nb;
|
||||
@ -30,11 +42,29 @@ get_cpuid(char *buffer, size_t sz)
|
||||
}
|
||||
|
||||
char *
|
||||
get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||
get_cpuid_str(struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
char *bufp;
|
||||
unsigned long pvr;
|
||||
|
||||
if (asprintf(&bufp, "0x%.8lx", mfspr(SPRN_PVR)) < 0)
|
||||
/*
|
||||
* IBM Power System supports compatible mode. That is
|
||||
* Nth generation platform can support previous generation
|
||||
* OS in a mode called compatibile mode. For ex. LPAR can be
|
||||
* booted in a Power9 mode when the system is a Power10.
|
||||
*
|
||||
* In the compatible mode, care must be taken when generating
|
||||
* PVR value. When read, PVR will be of the AT_BASE_PLATFORM
|
||||
* To support generic events, return 0x00ffffff as pvr when
|
||||
* booted in compat mode. Based on this pvr value, json will
|
||||
* pick events from pmu-events/arch/powerpc/compat
|
||||
*/
|
||||
if (!is_compat_mode())
|
||||
pvr = mfspr(SPRN_PVR);
|
||||
else
|
||||
pvr = 0x00ffffff;
|
||||
|
||||
if (asprintf(&bufp, "0x%.8lx", pvr) < 0)
|
||||
bufp = NULL;
|
||||
|
||||
return bufp;
|
||||
|
@ -1,6 +1,25 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
||||
#
|
||||
# Syscall table generation for perf
|
||||
#
|
||||
|
||||
out := $(OUTPUT)arch/riscv/include/generated/asm
|
||||
header := $(out)/syscalls.c
|
||||
incpath := $(srctree)/tools
|
||||
sysdef := $(srctree)/tools/arch/riscv/include/uapi/asm/unistd.h
|
||||
sysprf := $(srctree)/tools/perf/arch/riscv/entry/syscalls/
|
||||
systbl := $(sysprf)/mksyscalltbl
|
||||
|
||||
# Create output directory if not already present
|
||||
$(shell [ -d '$(out)' ] || mkdir -p '$(out)')
|
||||
|
||||
$(header): $(sysdef) $(systbl)
|
||||
$(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@
|
||||
|
||||
clean::
|
||||
$(call QUIET_CLEAN, riscv) $(RM) $(header)
|
||||
|
||||
archheaders: $(header)
|
||||
|
47
tools/perf/arch/riscv/entry/syscalls/mksyscalltbl
Executable file
47
tools/perf/arch/riscv/entry/syscalls/mksyscalltbl
Executable file
@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Generate system call table for perf. Derived from
|
||||
# powerpc script.
|
||||
#
|
||||
# Copyright IBM Corp. 2017
|
||||
# Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
||||
# Changed by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
|
||||
# Changed by: Kim Phillips <kim.phillips@arm.com>
|
||||
# Changed by: Björn Töpel <bjorn@rivosinc.com>
|
||||
|
||||
gcc=$1
|
||||
hostcc=$2
|
||||
incpath=$3
|
||||
input=$4
|
||||
|
||||
if ! test -r $input; then
|
||||
echo "Could not read input file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_sc_table()
|
||||
{
|
||||
local sc nr max_nr
|
||||
|
||||
while read sc nr; do
|
||||
printf "%s\n" " [$nr] = \"$sc\","
|
||||
max_nr=$nr
|
||||
done
|
||||
|
||||
echo "#define SYSCALLTBL_RISCV_MAX_ID $max_nr"
|
||||
}
|
||||
|
||||
create_table()
|
||||
{
|
||||
echo "#include \"$input\""
|
||||
echo "static const char *const syscalltbl_riscv[] = {"
|
||||
create_sc_table
|
||||
echo "};"
|
||||
}
|
||||
|
||||
$gcc -E -dM -x c -I $incpath/include/uapi $input \
|
||||
|awk '$2 ~ "__NR" && $3 !~ "__NR3264_" {
|
||||
sub("^#define __NR(3264)?_", "");
|
||||
print | "sort -k2 -n"}' \
|
||||
|create_table
|
42
tools/perf/arch/riscv/include/dwarf-regs-table.h
Normal file
42
tools/perf/arch/riscv/include/dwarf-regs-table.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifdef DEFINE_DWARF_REGSTR_TABLE
|
||||
/* This is included in perf/util/dwarf-regs.c */
|
||||
|
||||
#define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg
|
||||
|
||||
static const char * const riscv_regstr_tbl[] = {
|
||||
REG_DWARFNUM_NAME("%zero", 0),
|
||||
REG_DWARFNUM_NAME("%ra", 1),
|
||||
REG_DWARFNUM_NAME("%sp", 2),
|
||||
REG_DWARFNUM_NAME("%gp", 3),
|
||||
REG_DWARFNUM_NAME("%tp", 4),
|
||||
REG_DWARFNUM_NAME("%t0", 5),
|
||||
REG_DWARFNUM_NAME("%t1", 6),
|
||||
REG_DWARFNUM_NAME("%t2", 7),
|
||||
REG_DWARFNUM_NAME("%s0", 8),
|
||||
REG_DWARFNUM_NAME("%s1", 9),
|
||||
REG_DWARFNUM_NAME("%a0", 10),
|
||||
REG_DWARFNUM_NAME("%a1", 11),
|
||||
REG_DWARFNUM_NAME("%a2", 12),
|
||||
REG_DWARFNUM_NAME("%a3", 13),
|
||||
REG_DWARFNUM_NAME("%a4", 14),
|
||||
REG_DWARFNUM_NAME("%a5", 15),
|
||||
REG_DWARFNUM_NAME("%a6", 16),
|
||||
REG_DWARFNUM_NAME("%a7", 17),
|
||||
REG_DWARFNUM_NAME("%s2", 18),
|
||||
REG_DWARFNUM_NAME("%s3", 19),
|
||||
REG_DWARFNUM_NAME("%s4", 20),
|
||||
REG_DWARFNUM_NAME("%s5", 21),
|
||||
REG_DWARFNUM_NAME("%s6", 22),
|
||||
REG_DWARFNUM_NAME("%s7", 23),
|
||||
REG_DWARFNUM_NAME("%s8", 24),
|
||||
REG_DWARFNUM_NAME("%s9", 25),
|
||||
REG_DWARFNUM_NAME("%s10", 26),
|
||||
REG_DWARFNUM_NAME("%s11", 27),
|
||||
REG_DWARFNUM_NAME("%t3", 28),
|
||||
REG_DWARFNUM_NAME("%t4", 29),
|
||||
REG_DWARFNUM_NAME("%t5", 30),
|
||||
REG_DWARFNUM_NAME("%t6", 31),
|
||||
};
|
||||
|
||||
#endif
|
@ -2,5 +2,4 @@ perf-util-y += perf_regs.o
|
||||
perf-util-y += header.o
|
||||
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
@ -1,72 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h> /* for EINVAL */
|
||||
#include <string.h> /* for strcmp */
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
struct pt_regs_dwarfnum riscv_dwarf_regs_table[] = {
|
||||
REG_DWARFNUM_NAME("%zero", 0),
|
||||
REG_DWARFNUM_NAME("%ra", 1),
|
||||
REG_DWARFNUM_NAME("%sp", 2),
|
||||
REG_DWARFNUM_NAME("%gp", 3),
|
||||
REG_DWARFNUM_NAME("%tp", 4),
|
||||
REG_DWARFNUM_NAME("%t0", 5),
|
||||
REG_DWARFNUM_NAME("%t1", 6),
|
||||
REG_DWARFNUM_NAME("%t2", 7),
|
||||
REG_DWARFNUM_NAME("%s0", 8),
|
||||
REG_DWARFNUM_NAME("%s1", 9),
|
||||
REG_DWARFNUM_NAME("%a0", 10),
|
||||
REG_DWARFNUM_NAME("%a1", 11),
|
||||
REG_DWARFNUM_NAME("%a2", 12),
|
||||
REG_DWARFNUM_NAME("%a3", 13),
|
||||
REG_DWARFNUM_NAME("%a4", 14),
|
||||
REG_DWARFNUM_NAME("%a5", 15),
|
||||
REG_DWARFNUM_NAME("%a6", 16),
|
||||
REG_DWARFNUM_NAME("%a7", 17),
|
||||
REG_DWARFNUM_NAME("%s2", 18),
|
||||
REG_DWARFNUM_NAME("%s3", 19),
|
||||
REG_DWARFNUM_NAME("%s4", 20),
|
||||
REG_DWARFNUM_NAME("%s5", 21),
|
||||
REG_DWARFNUM_NAME("%s6", 22),
|
||||
REG_DWARFNUM_NAME("%s7", 23),
|
||||
REG_DWARFNUM_NAME("%s8", 24),
|
||||
REG_DWARFNUM_NAME("%s9", 25),
|
||||
REG_DWARFNUM_NAME("%s10", 26),
|
||||
REG_DWARFNUM_NAME("%s11", 27),
|
||||
REG_DWARFNUM_NAME("%t3", 28),
|
||||
REG_DWARFNUM_NAME("%t4", 29),
|
||||
REG_DWARFNUM_NAME("%t5", 30),
|
||||
REG_DWARFNUM_NAME("%t6", 31),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
#define RISCV_MAX_REGS ((sizeof(riscv_dwarf_regs_table) / \
|
||||
sizeof(riscv_dwarf_regs_table[0])) - 1)
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n < RISCV_MAX_REGS) ? riscv_dwarf_regs_table[n].name : NULL;
|
||||
}
|
||||
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
|
||||
for (roff = riscv_dwarf_regs_table; roff->name; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return roff->dwarfnum;
|
||||
return -EINVAL;
|
||||
}
|
@ -81,7 +81,7 @@ static char *_get_cpuid(void)
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
int get_cpuid(char *buffer, size_t sz)
|
||||
int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
char *cpuid = _get_cpuid();
|
||||
int ret = 0;
|
||||
@ -98,7 +98,7 @@ int get_cpuid(char *buffer, size_t sz)
|
||||
}
|
||||
|
||||
char *
|
||||
get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||
get_cpuid_str(struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
return _get_cpuid();
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ int riscv64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->associate_instruction_ops = riscv64__associate_ins_ops;
|
||||
arch->initialized = true;
|
||||
arch->objdump.comment_char = '#';
|
||||
arch->e_machine = EM_RISCV;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,9 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
||||
#
|
||||
|
@ -166,6 +166,8 @@ static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
if (s390__cpuid_parse(arch, cpuid))
|
||||
err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING;
|
||||
}
|
||||
arch->e_machine = EM_S390;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -2,7 +2,6 @@ perf-util-y += header.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
perf-util-y += machine.o
|
||||
|
@ -1,43 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright IBM Corp. 2010, 2017
|
||||
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <string.h>
|
||||
#include <dwarf-regs.h>
|
||||
#include "dwarf-regs-table.h"
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n >= ARRAY_SIZE(s390_dwarf_regs)) ? NULL : s390_dwarf_regs[n];
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the register name into an offset to struct pt_regs (kernel).
|
||||
* This is required by the BPF prologue generator. The BPF
|
||||
* program is called in the BPF overflow handler in the perf
|
||||
* core.
|
||||
*/
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
unsigned long gpr;
|
||||
|
||||
if (!name || strncmp(name, "%r", 2))
|
||||
return -EINVAL;
|
||||
|
||||
errno = 0;
|
||||
gpr = strtoul(name + 2, NULL, 10);
|
||||
if (errno || gpr >= 16)
|
||||
return -EINVAL;
|
||||
|
||||
return offsetof(user_pt_regs, gprs) + 8 * gpr;
|
||||
}
|
@ -27,7 +27,7 @@
|
||||
#define SYSINFO "/proc/sysinfo"
|
||||
#define SRVLVL "/proc/service_levels"
|
||||
|
||||
int get_cpuid(char *buffer, size_t sz)
|
||||
int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
char *cp, *line = NULL, *line2;
|
||||
char type[8], model[33], version[8], manufacturer[32], authorization[8];
|
||||
@ -137,11 +137,11 @@ int get_cpuid(char *buffer, size_t sz)
|
||||
return (nbytes >= sz) ? ENOBUFS : 0;
|
||||
}
|
||||
|
||||
char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||
char *get_cpuid_str(struct perf_cpu cpu)
|
||||
{
|
||||
char *buf = malloc(128);
|
||||
|
||||
if (buf && get_cpuid(buf, 128))
|
||||
if (buf && get_cpuid(buf, 128, cpu))
|
||||
zfree(&buf);
|
||||
return buf;
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
perf-util-y += util/
|
@ -1,4 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
@ -1 +0,0 @@
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
@ -1,41 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* Generic dwarf analysis helpers
|
||||
*/
|
||||
|
||||
#define SH_MAX_REGS 18
|
||||
const char *sh_regs_table[SH_MAX_REGS] = {
|
||||
"r0",
|
||||
"r1",
|
||||
"r2",
|
||||
"r3",
|
||||
"r4",
|
||||
"r5",
|
||||
"r6",
|
||||
"r7",
|
||||
"r8",
|
||||
"r9",
|
||||
"r10",
|
||||
"r11",
|
||||
"r12",
|
||||
"r13",
|
||||
"r14",
|
||||
"r15",
|
||||
"pc",
|
||||
"pr",
|
||||
};
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n < SH_MAX_REGS) ? sh_regs_table[n] : NULL;
|
||||
}
|
@ -1 +0,0 @@
|
||||
perf-util-y += util/
|
@ -1,6 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@ -163,6 +163,8 @@ static int sparc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
arch->initialized = true;
|
||||
arch->associate_instruction_ops = sparc__associate_instruction_ops;
|
||||
arch->objdump.comment_char = '#';
|
||||
arch->e_machine = EM_SPARC;
|
||||
arch->e_flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1 +0,0 @@
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
@ -1,39 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 David S. Miller <davem@davemloft.net>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define SPARC_MAX_REGS 96
|
||||
|
||||
const char *sparc_regs_table[SPARC_MAX_REGS] = {
|
||||
"%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
|
||||
"%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7",
|
||||
"%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
|
||||
"%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7",
|
||||
"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
|
||||
"%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
|
||||
"%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
|
||||
"%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
|
||||
"%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39",
|
||||
"%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47",
|
||||
"%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55",
|
||||
"%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63",
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n < SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL;
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
||||
###
|
||||
|
@ -202,12 +202,13 @@ static int x86__annotate_init(struct arch *arch, char *cpuid)
|
||||
if (x86__cpuid_parse(arch, cpuid))
|
||||
err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING;
|
||||
}
|
||||
|
||||
arch->e_machine = EM_X86_64;
|
||||
arch->e_flags = 0;
|
||||
arch->initialized = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DWARF_SUPPORT
|
||||
#ifdef HAVE_LIBDW_SUPPORT
|
||||
static void update_insn_state_x86(struct type_state *state,
|
||||
struct data_loc_info *dloc, Dwarf_Die *cu_die,
|
||||
struct disasm_line *dl)
|
||||
|
@ -1,128 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "tests/tests.h"
|
||||
#include "cloexec.h"
|
||||
#include "debug.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "arch-tests.h"
|
||||
#include <internal/lib.h> // page_size
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static pid_t spawn(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid)
|
||||
return pid;
|
||||
|
||||
while(1)
|
||||
sleep(5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an event group that contains both a sampled hardware
|
||||
* (cpu-cycles) and software (intel_cqm/llc_occupancy/) event. We then
|
||||
* wait for the hardware perf counter to overflow and generate a PMI,
|
||||
* which triggers an event read for both of the events in the group.
|
||||
*
|
||||
* Since reading Intel CQM event counters requires sending SMP IPIs, the
|
||||
* CQM pmu needs to handle the above situation gracefully, and return
|
||||
* the last read counter value to avoid triggering a WARN_ON_ONCE() in
|
||||
* smp_call_function_many() caused by sending IPIs from NMI context.
|
||||
*/
|
||||
int test__intel_cqm_count_nmi_context(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
struct evlist *evlist = NULL;
|
||||
struct evsel *evsel = NULL;
|
||||
struct perf_event_attr pe;
|
||||
int i, fd[2], flag, ret;
|
||||
size_t mmap_len;
|
||||
void *event;
|
||||
pid_t pid;
|
||||
int err = TEST_FAIL;
|
||||
|
||||
flag = perf_event_open_cloexec_flag();
|
||||
|
||||
evlist = evlist__new();
|
||||
if (!evlist) {
|
||||
pr_debug("evlist__new failed\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
ret = parse_event(evlist, "intel_cqm/llc_occupancy/");
|
||||
if (ret) {
|
||||
pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n");
|
||||
err = TEST_SKIP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
evsel = evlist__first(evlist);
|
||||
if (!evsel) {
|
||||
pr_debug("evlist__first failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&pe, 0, sizeof(pe));
|
||||
pe.size = sizeof(pe);
|
||||
|
||||
pe.type = PERF_TYPE_HARDWARE;
|
||||
pe.config = PERF_COUNT_HW_CPU_CYCLES;
|
||||
pe.read_format = PERF_FORMAT_GROUP;
|
||||
|
||||
pe.sample_period = 128;
|
||||
pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ;
|
||||
|
||||
pid = spawn();
|
||||
|
||||
fd[0] = sys_perf_event_open(&pe, pid, -1, -1, flag);
|
||||
if (fd[0] < 0) {
|
||||
pr_debug("failed to open event\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&pe, 0, sizeof(pe));
|
||||
pe.size = sizeof(pe);
|
||||
|
||||
pe.type = evsel->attr.type;
|
||||
pe.config = evsel->attr.config;
|
||||
|
||||
fd[1] = sys_perf_event_open(&pe, pid, -1, fd[0], flag);
|
||||
if (fd[1] < 0) {
|
||||
pr_debug("failed to open event\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pick a power-of-two number of pages + 1 for the meta-data
|
||||
* page (struct perf_event_mmap_page). See tools/perf/design.txt.
|
||||
*/
|
||||
mmap_len = page_size * 65;
|
||||
|
||||
event = mmap(NULL, mmap_len, PROT_READ, MAP_SHARED, fd[0], 0);
|
||||
if (event == (void *)(-1)) {
|
||||
pr_debug("failed to mmap %d\n", errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
err = TEST_OK;
|
||||
|
||||
munmap(event, mmap_len);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
close(fd[i]);
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
wait(NULL);
|
||||
out:
|
||||
evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
@ -375,7 +375,7 @@ static int get_pt_caps(int cpu, struct pt_caps *caps)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_hydrid(void)
|
||||
static bool is_hybrid(void)
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx = 0;
|
||||
bool result;
|
||||
@ -441,7 +441,7 @@ int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest)
|
||||
int ret = TEST_OK;
|
||||
int cpu;
|
||||
|
||||
if (!is_hydrid()) {
|
||||
if (!is_hybrid()) {
|
||||
test->test_cases[subtest].skip_reason = "not hybrid";
|
||||
return TEST_SKIP;
|
||||
}
|
||||
|
@ -10,10 +10,6 @@ perf-util-y += evlist.o
|
||||
perf-util-y += mem-events.o
|
||||
perf-util-y += evsel.o
|
||||
perf-util-y += iostat.o
|
||||
perf-util-y += env.o
|
||||
|
||||
perf-util-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-util-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o
|
||||
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
@ -55,11 +55,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
|
||||
int *err)
|
||||
{
|
||||
char buffer[64];
|
||||
struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus);
|
||||
int ret;
|
||||
|
||||
*err = 0;
|
||||
|
||||
ret = get_cpuid(buffer, sizeof(buffer));
|
||||
ret = get_cpuid(buffer, sizeof(buffer), cpu);
|
||||
if (ret) {
|
||||
*err = ret;
|
||||
return NULL;
|
||||
|
@ -1,153 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
* Extracted from probe-finder.c
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h> /* for EINVAL */
|
||||
#include <string.h> /* for strcmp */
|
||||
#include <linux/ptrace.h> /* for struct pt_regs */
|
||||
#include <linux/kernel.h> /* for offsetof */
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* See arch/x86/kernel/ptrace.c.
|
||||
* Different from it:
|
||||
*
|
||||
* - Since struct pt_regs is defined differently for user and kernel,
|
||||
* but we want to use 'ax, bx' instead of 'rax, rbx' (which is struct
|
||||
* field name of user's pt_regs), we make REG_OFFSET_NAME to accept
|
||||
* both string name and reg field name.
|
||||
*
|
||||
* - Since accessing x86_32's pt_regs from x86_64 building is difficult
|
||||
* and vise versa, we simply fill offset with -1, so
|
||||
* get_arch_regstr() still works but regs_query_register_offset()
|
||||
* returns error.
|
||||
* The only inconvenience caused by it now is that we are not allowed
|
||||
* to generate BPF prologue for a x86_64 kernel if perf is built for
|
||||
* x86_32. This is really a rare usecase.
|
||||
*
|
||||
* - Order is different from kernel's ptrace.c for get_arch_regstr(). Use
|
||||
* the order defined by dwarf.
|
||||
*/
|
||||
|
||||
struct pt_regs_offset {
|
||||
const char *name;
|
||||
int offset;
|
||||
};
|
||||
|
||||
#define REG_OFFSET_END {.name = NULL, .offset = 0}
|
||||
|
||||
#ifdef __x86_64__
|
||||
# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
|
||||
# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1}
|
||||
#else
|
||||
# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1}
|
||||
# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
|
||||
#endif
|
||||
|
||||
/* TODO: switching by dwarf address size */
|
||||
#ifndef __x86_64__
|
||||
static const struct pt_regs_offset x86_32_regoffset_table[] = {
|
||||
REG_OFFSET_NAME_32("%ax", eax),
|
||||
REG_OFFSET_NAME_32("%cx", ecx),
|
||||
REG_OFFSET_NAME_32("%dx", edx),
|
||||
REG_OFFSET_NAME_32("%bx", ebx),
|
||||
REG_OFFSET_NAME_32("$stack", esp), /* Stack address instead of %sp */
|
||||
REG_OFFSET_NAME_32("%bp", ebp),
|
||||
REG_OFFSET_NAME_32("%si", esi),
|
||||
REG_OFFSET_NAME_32("%di", edi),
|
||||
REG_OFFSET_END,
|
||||
};
|
||||
|
||||
#define regoffset_table x86_32_regoffset_table
|
||||
#else
|
||||
static const struct pt_regs_offset x86_64_regoffset_table[] = {
|
||||
REG_OFFSET_NAME_64("%ax", rax),
|
||||
REG_OFFSET_NAME_64("%dx", rdx),
|
||||
REG_OFFSET_NAME_64("%cx", rcx),
|
||||
REG_OFFSET_NAME_64("%bx", rbx),
|
||||
REG_OFFSET_NAME_64("%si", rsi),
|
||||
REG_OFFSET_NAME_64("%di", rdi),
|
||||
REG_OFFSET_NAME_64("%bp", rbp),
|
||||
REG_OFFSET_NAME_64("%sp", rsp),
|
||||
REG_OFFSET_NAME_64("%r8", r8),
|
||||
REG_OFFSET_NAME_64("%r9", r9),
|
||||
REG_OFFSET_NAME_64("%r10", r10),
|
||||
REG_OFFSET_NAME_64("%r11", r11),
|
||||
REG_OFFSET_NAME_64("%r12", r12),
|
||||
REG_OFFSET_NAME_64("%r13", r13),
|
||||
REG_OFFSET_NAME_64("%r14", r14),
|
||||
REG_OFFSET_NAME_64("%r15", r15),
|
||||
REG_OFFSET_END,
|
||||
};
|
||||
|
||||
#define regoffset_table x86_64_regoffset_table
|
||||
#endif
|
||||
|
||||
/* Minus 1 for the ending REG_OFFSET_END */
|
||||
#define ARCH_MAX_REGS ((sizeof(regoffset_table) / sizeof(regoffset_table[0])) - 1)
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n < ARCH_MAX_REGS) ? regoffset_table[n].name : NULL;
|
||||
}
|
||||
|
||||
/* Reuse code from arch/x86/kernel/ptrace.c */
|
||||
/**
|
||||
* regs_query_register_offset() - query register offset from its name
|
||||
* @name: the name of a register
|
||||
*
|
||||
* regs_query_register_offset() returns the offset of a register in struct
|
||||
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
|
||||
*/
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_offset *roff;
|
||||
for (roff = regoffset_table; roff->name != NULL; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return roff->offset;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct dwarf_regs_idx {
|
||||
const char *name;
|
||||
int idx;
|
||||
};
|
||||
|
||||
static const struct dwarf_regs_idx x86_regidx_table[] = {
|
||||
{ "rax", 0 }, { "eax", 0 }, { "ax", 0 }, { "al", 0 },
|
||||
{ "rdx", 1 }, { "edx", 1 }, { "dx", 1 }, { "dl", 1 },
|
||||
{ "rcx", 2 }, { "ecx", 2 }, { "cx", 2 }, { "cl", 2 },
|
||||
{ "rbx", 3 }, { "edx", 3 }, { "bx", 3 }, { "bl", 3 },
|
||||
{ "rsi", 4 }, { "esi", 4 }, { "si", 4 }, { "sil", 4 },
|
||||
{ "rdi", 5 }, { "edi", 5 }, { "di", 5 }, { "dil", 5 },
|
||||
{ "rbp", 6 }, { "ebp", 6 }, { "bp", 6 }, { "bpl", 6 },
|
||||
{ "rsp", 7 }, { "esp", 7 }, { "sp", 7 }, { "spl", 7 },
|
||||
{ "r8", 8 }, { "r8d", 8 }, { "r8w", 8 }, { "r8b", 8 },
|
||||
{ "r9", 9 }, { "r9d", 9 }, { "r9w", 9 }, { "r9b", 9 },
|
||||
{ "r10", 10 }, { "r10d", 10 }, { "r10w", 10 }, { "r10b", 10 },
|
||||
{ "r11", 11 }, { "r11d", 11 }, { "r11w", 11 }, { "r11b", 11 },
|
||||
{ "r12", 12 }, { "r12d", 12 }, { "r12w", 12 }, { "r12b", 12 },
|
||||
{ "r13", 13 }, { "r13d", 13 }, { "r13w", 13 }, { "r13b", 13 },
|
||||
{ "r14", 14 }, { "r14d", 14 }, { "r14w", 14 }, { "r14b", 14 },
|
||||
{ "r15", 15 }, { "r15d", 15 }, { "r15w", 15 }, { "r15b", 15 },
|
||||
{ "rip", DWARF_REG_PC },
|
||||
};
|
||||
|
||||
int get_arch_regnum(const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (*name != '%')
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(x86_regidx_table); i++)
|
||||
if (!strcmp(x86_regidx_table[i].name, name + 1))
|
||||
return x86_regidx_table[i].idx;
|
||||
return -ENOENT;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "linux/string.h"
|
||||
#include "util/env.h"
|
||||
#include "env.h"
|
||||
|
||||
bool x86__is_amd_cpu(void)
|
||||
{
|
||||
struct perf_env env = { .total_mem = 0, };
|
||||
static int is_amd; /* 0: Uninitialized, 1: Yes, -1: No */
|
||||
|
||||
if (is_amd)
|
||||
goto ret;
|
||||
|
||||
perf_env__cpuid(&env);
|
||||
is_amd = env.cpuid && strstarts(env.cpuid, "AuthenticAMD") ? 1 : -1;
|
||||
perf_env__exit(&env);
|
||||
ret:
|
||||
return is_amd >= 1 ? true : false;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _X86_ENV_H
|
||||
#define _X86_ENV_H
|
||||
|
||||
bool x86__is_amd_cpu(void);
|
||||
|
||||
#endif /* _X86_ENV_H */
|
@ -1,91 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdio.h>
|
||||
#include "util/pmu.h"
|
||||
#include "util/pmus.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/event.h"
|
||||
#include <string.h>
|
||||
#include "../../../util/evlist.h"
|
||||
#include "../../../util/evsel.h"
|
||||
#include "topdown.h"
|
||||
#include "evsel.h"
|
||||
|
||||
static int ___evlist__add_default_attrs(struct evlist *evlist,
|
||||
struct perf_event_attr *attrs,
|
||||
size_t nr_attrs)
|
||||
{
|
||||
LIST_HEAD(head);
|
||||
size_t i = 0;
|
||||
|
||||
for (i = 0; i < nr_attrs; i++)
|
||||
event_attr_init(attrs + i);
|
||||
|
||||
if (perf_pmus__num_core_pmus() == 1)
|
||||
return evlist__add_attrs(evlist, attrs, nr_attrs);
|
||||
|
||||
for (i = 0; i < nr_attrs; i++) {
|
||||
struct perf_pmu *pmu = NULL;
|
||||
|
||||
if (attrs[i].type == PERF_TYPE_SOFTWARE) {
|
||||
struct evsel *evsel = evsel__new(attrs + i);
|
||||
|
||||
if (evsel == NULL)
|
||||
goto out_delete_partial_list;
|
||||
list_add_tail(&evsel->core.node, &head);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
|
||||
struct perf_cpu_map *cpus;
|
||||
struct evsel *evsel;
|
||||
|
||||
evsel = evsel__new(attrs + i);
|
||||
if (evsel == NULL)
|
||||
goto out_delete_partial_list;
|
||||
evsel->core.attr.config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
|
||||
cpus = perf_cpu_map__get(pmu->cpus);
|
||||
evsel->core.cpus = cpus;
|
||||
evsel->core.own_cpus = perf_cpu_map__get(cpus);
|
||||
evsel->pmu_name = strdup(pmu->name);
|
||||
list_add_tail(&evsel->core.node, &head);
|
||||
}
|
||||
}
|
||||
|
||||
evlist__splice_list_tail(evlist, &head);
|
||||
|
||||
return 0;
|
||||
|
||||
out_delete_partial_list:
|
||||
{
|
||||
struct evsel *evsel, *n;
|
||||
|
||||
__evlist__for_each_entry_safe(&head, n, evsel)
|
||||
evsel__delete(evsel);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int arch_evlist__add_default_attrs(struct evlist *evlist,
|
||||
struct perf_event_attr *attrs,
|
||||
size_t nr_attrs)
|
||||
{
|
||||
if (!nr_attrs)
|
||||
return 0;
|
||||
|
||||
return ___evlist__add_default_attrs(evlist, attrs, nr_attrs);
|
||||
}
|
||||
|
||||
int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
|
||||
{
|
||||
/*
|
||||
* Currently the following topdown events sequence are supported to
|
||||
* move and regroup correctly.
|
||||
*
|
||||
* a. all events in a group
|
||||
* perf stat -e "{instructions,topdown-retiring,slots}" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Performance counter stats for 'CPU(s) 0':
|
||||
* 15,066,240 slots
|
||||
* 1,899,760 instructions
|
||||
* 2,126,998 topdown-retiring
|
||||
* b. all events not in a group
|
||||
* perf stat -e "instructions,topdown-retiring,slots" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Performance counter stats for 'CPU(s) 0':
|
||||
* 2,045,561 instructions
|
||||
* 17,108,370 slots
|
||||
* 2,281,116 topdown-retiring
|
||||
* c. slots event in a group but topdown metrics events outside the group
|
||||
* perf stat -e "{instructions,slots},topdown-retiring" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Performance counter stats for 'CPU(s) 0':
|
||||
* 20,323,878 slots
|
||||
* 2,634,884 instructions
|
||||
* 3,028,656 topdown-retiring
|
||||
* d. slots event and topdown metrics events in two groups
|
||||
* perf stat -e "{instructions,slots},{topdown-retiring}" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Performance counter stats for 'CPU(s) 0':
|
||||
* 26,319,024 slots
|
||||
* 2,427,791 instructions
|
||||
* 2,683,508 topdown-retiring
|
||||
*
|
||||
* If slots event and topdown metrics events are not in same group, the
|
||||
* topdown metrics events must be first event after the slots event group,
|
||||
* otherwise topdown metrics events can't be regrouped correctly, e.g.
|
||||
*
|
||||
* a. perf stat -e "{instructions,slots},cycles,topdown-retiring" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Performance counter stats for 'CPU(s) 0':
|
||||
* 17,923,134 slots
|
||||
* 2,154,855 instructions
|
||||
* 3,015,058 cycles
|
||||
* <not supported> topdown-retiring
|
||||
*
|
||||
* If slots event and topdown metrics events are in two groups, the group which
|
||||
* has topdown metrics events must contain only the topdown metrics event,
|
||||
* otherwise topdown metrics event can't be regrouped correctly as well, e.g.
|
||||
*
|
||||
* a. perf stat -e "{instructions,slots},{topdown-retiring,cycles}" -C0 sleep 1
|
||||
* WARNING: events were regrouped to match PMUs
|
||||
* Error:
|
||||
* The sys_perf_event_open() syscall returned with 22 (Invalid argument) for
|
||||
* event (topdown-retiring)
|
||||
*/
|
||||
if (topdown_sys_has_perf_metrics() &&
|
||||
(arch_evsel__must_be_in_group(lhs) || arch_evsel__must_be_in_group(rhs))) {
|
||||
/* Ensure the topdown slots comes first. */
|
||||
if (strcasestr(lhs->name, "slots") && !strcasestr(lhs->name, "uops_retired.slots"))
|
||||
if (arch_is_topdown_slots(lhs))
|
||||
return -1;
|
||||
if (strcasestr(rhs->name, "slots") && !strcasestr(rhs->name, "uops_retired.slots"))
|
||||
if (arch_is_topdown_slots(rhs))
|
||||
return 1;
|
||||
/* Followed by topdown events. */
|
||||
if (strcasestr(lhs->name, "topdown") && !strcasestr(rhs->name, "topdown"))
|
||||
|
||||
/*
|
||||
* Move topdown metrics events forward only when topdown metrics
|
||||
* events are not in same group with previous slots event. If
|
||||
* topdown metrics events are already in same group with slots
|
||||
* event, do nothing.
|
||||
*/
|
||||
if (arch_is_topdown_metrics(lhs) && !arch_is_topdown_metrics(rhs) &&
|
||||
lhs->core.leader != rhs->core.leader)
|
||||
return -1;
|
||||
if (!strcasestr(lhs->name, "topdown") && strcasestr(rhs->name, "topdown"))
|
||||
if (!arch_is_topdown_metrics(lhs) && arch_is_topdown_metrics(rhs) &&
|
||||
lhs->core.leader != rhs->core.leader)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "util/pmu.h"
|
||||
#include "util/pmus.h"
|
||||
#include "linux/string.h"
|
||||
#include "topdown.h"
|
||||
#include "evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "env.h"
|
||||
@ -21,7 +22,8 @@ void arch_evsel__set_sample_weight(struct evsel *evsel)
|
||||
/* Check whether the evsel's PMU supports the perf metrics */
|
||||
bool evsel__sys_has_perf_metrics(const struct evsel *evsel)
|
||||
{
|
||||
const char *pmu_name = evsel->pmu_name ? evsel->pmu_name : "cpu";
|
||||
struct perf_pmu *pmu;
|
||||
u32 type = evsel->core.attr.type;
|
||||
|
||||
/*
|
||||
* The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU
|
||||
@ -31,11 +33,31 @@ bool evsel__sys_has_perf_metrics(const struct evsel *evsel)
|
||||
* Checking both the PERF_TYPE_RAW type and the slots event
|
||||
* should be good enough to detect the perf metrics feature.
|
||||
*/
|
||||
if ((evsel->core.attr.type == PERF_TYPE_RAW) &&
|
||||
perf_pmus__have_event(pmu_name, "slots"))
|
||||
return true;
|
||||
again:
|
||||
switch (type) {
|
||||
case PERF_TYPE_HARDWARE:
|
||||
case PERF_TYPE_HW_CACHE:
|
||||
type = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT;
|
||||
if (type)
|
||||
goto again;
|
||||
break;
|
||||
case PERF_TYPE_RAW:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
pmu = evsel->pmu;
|
||||
if (pmu && perf_pmu__is_fake(pmu))
|
||||
pmu = NULL;
|
||||
|
||||
if (!pmu) {
|
||||
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
|
||||
if (pmu->type == PERF_TYPE_RAW)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pmu && perf_pmu__have_event(pmu, "slots");
|
||||
}
|
||||
|
||||
bool arch_evsel__must_be_in_group(const struct evsel *evsel)
|
||||
@ -44,7 +66,7 @@ bool arch_evsel__must_be_in_group(const struct evsel *evsel)
|
||||
strcasestr(evsel->name, "uops_retired.slots"))
|
||||
return false;
|
||||
|
||||
return strcasestr(evsel->name, "topdown") || strcasestr(evsel->name, "slots");
|
||||
return arch_is_topdown_metrics(evsel) || arch_is_topdown_slots(evsel);
|
||||
}
|
||||
|
||||
int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
|
||||
@ -63,7 +85,7 @@ int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
|
||||
return scnprintf(bf, size, "%s", event_name);
|
||||
|
||||
return scnprintf(bf, size, "%s/%s/",
|
||||
evsel->pmu_name ? evsel->pmu_name : "cpu",
|
||||
evsel->pmu ? evsel->pmu->name : "cpu",
|
||||
event_name);
|
||||
}
|
||||
|
||||
@ -108,7 +130,7 @@ int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size)
|
||||
return 0;
|
||||
|
||||
if (!evsel->core.attr.precise_ip &&
|
||||
!(evsel->pmu_name && !strncmp(evsel->pmu_name, "ibs", 3)))
|
||||
!(evsel->pmu && !strncmp(evsel->pmu->name, "ibs", 3)))
|
||||
return 0;
|
||||
|
||||
/* More verbose IBS errors. */
|
||||
|
@ -58,13 +58,12 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt)
|
||||
}
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
return __get_cpuid(buffer, sz, "%s,%u,%u,%u$");
|
||||
}
|
||||
|
||||
char *
|
||||
get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||
char *get_cpuid_str(struct perf_cpu cpu __maybe_unused)
|
||||
{
|
||||
char *buf = malloc(128);
|
||||
|
||||
|
@ -75,7 +75,8 @@ static int intel_pt_parse_terms_with_default(const struct perf_pmu *pmu,
|
||||
goto out_free;
|
||||
|
||||
attr.config = *config;
|
||||
err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*err=*/NULL);
|
||||
err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*apply_hardcoded=*/false,
|
||||
/*err=*/NULL);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
@ -444,7 +444,7 @@ void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel,
|
||||
iostat_value = (count->val - prev_count_val) /
|
||||
((double) count->run / count->ena);
|
||||
}
|
||||
out->print_metric(config, out->ctx, NULL, "%8.0f", iostat_metric,
|
||||
out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, "%8.0f", iostat_metric,
|
||||
iostat_value / (256 * 1024));
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../../../util/fncache.h"
|
||||
#include "../../../util/pmus.h"
|
||||
#include "mem-events.h"
|
||||
#include "env.h"
|
||||
#include "util/env.h"
|
||||
|
||||
void perf_pmu__arch_init(struct perf_pmu *pmu __maybe_unused)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "api/fs/fs.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/pmu.h"
|
||||
#include "util/pmus.h"
|
||||
#include "util/topdown.h"
|
||||
@ -32,6 +33,31 @@ bool topdown_sys_has_perf_metrics(void)
|
||||
}
|
||||
|
||||
#define TOPDOWN_SLOTS 0x0400
|
||||
bool arch_is_topdown_slots(const struct evsel *evsel)
|
||||
{
|
||||
if (evsel->core.attr.config == TOPDOWN_SLOTS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool arch_is_topdown_metrics(const struct evsel *evsel)
|
||||
{
|
||||
int config = evsel->core.attr.config;
|
||||
const char *name_from_config;
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
/* All topdown events have an event code of 0. */
|
||||
if ((config & 0xFF) != 0)
|
||||
return false;
|
||||
|
||||
pmu = evsel__find_pmu(evsel);
|
||||
if (!pmu || !pmu->is_core)
|
||||
return false;
|
||||
|
||||
name_from_config = perf_pmu__name_from_config(pmu, config);
|
||||
return name_from_config && strcasestr(name_from_config, "topdown");
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a topdown group supports sample-read.
|
||||
@ -41,11 +67,24 @@ bool topdown_sys_has_perf_metrics(void)
|
||||
*/
|
||||
bool arch_topdown_sample_read(struct evsel *leader)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
|
||||
if (!evsel__sys_has_perf_metrics(leader))
|
||||
return false;
|
||||
|
||||
if (leader->core.attr.config == TOPDOWN_SLOTS)
|
||||
return true;
|
||||
if (!arch_is_topdown_slots(leader))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If slots event as leader event but no topdown metric events
|
||||
* in group, slots event should still sample as leader.
|
||||
*/
|
||||
evlist__for_each_entry(leader->evlist, evsel) {
|
||||
if (evsel->core.leader != leader->core.leader)
|
||||
return false;
|
||||
if (evsel != leader && arch_is_topdown_metrics(evsel))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -3,5 +3,7 @@
|
||||
#define _TOPDOWN_H 1
|
||||
|
||||
bool topdown_sys_has_perf_metrics(void);
|
||||
bool arch_is_topdown_slots(const struct evsel *evsel);
|
||||
bool arch_is_topdown_metrics(const struct evsel *evsel);
|
||||
|
||||
#endif
|
||||
|
@ -24,9 +24,9 @@ u64 rdtsc(void)
|
||||
* ...
|
||||
* will return 3000000000.
|
||||
*/
|
||||
static double cpuinfo_tsc_freq(void)
|
||||
static u64 cpuinfo_tsc_freq(void)
|
||||
{
|
||||
double result = 0;
|
||||
u64 result = 0;
|
||||
FILE *cpuinfo;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
@ -34,20 +34,22 @@ static double cpuinfo_tsc_freq(void)
|
||||
cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if (!cpuinfo) {
|
||||
pr_err("Failed to read /proc/cpuinfo for TSC frequency\n");
|
||||
return NAN;
|
||||
return 0;
|
||||
}
|
||||
while (getline(&line, &len, cpuinfo) > 0) {
|
||||
if (!strncmp(line, "model name", 10)) {
|
||||
char *pos = strstr(line + 11, " @ ");
|
||||
double float_result;
|
||||
|
||||
if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
|
||||
result *= 1000000000;
|
||||
if (pos && sscanf(pos, " @ %lfGHz", &float_result) == 1) {
|
||||
float_result *= 1000000000;
|
||||
result = (u64)float_result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (fpclassify(result) == FP_ZERO)
|
||||
if (result == 0)
|
||||
pr_err("Failed to find TSC frequency in /proc/cpuinfo\n");
|
||||
|
||||
free(line);
|
||||
@ -55,7 +57,7 @@ static double cpuinfo_tsc_freq(void)
|
||||
return result;
|
||||
}
|
||||
|
||||
double arch_get_tsc_freq(void)
|
||||
u64 arch_get_tsc_freq(void)
|
||||
{
|
||||
unsigned int a, b, c, d, lvl;
|
||||
static bool cached;
|
||||
@ -86,6 +88,6 @@ double arch_get_tsc_freq(void)
|
||||
return tsc;
|
||||
}
|
||||
|
||||
tsc = (double)c * (double)b / (double)a;
|
||||
tsc = (u64)c * (u64)b / (u64)a;
|
||||
return tsc;
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
perf-util-y += util/
|
@ -1,4 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
@ -1 +0,0 @@
|
||||
perf-$(CONFIG_DWARF) += dwarf-regs.o
|
@ -1,21 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (c) 2015 Cadence Design Systems Inc.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define XTENSA_MAX_REGS 16
|
||||
|
||||
const char *xtensa_regs_table[XTENSA_MAX_REGS] = {
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||
"a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15",
|
||||
};
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return n < XTENSA_MAX_REGS ? xtensa_regs_table[n] : NULL;
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time64.h>
|
||||
@ -35,6 +36,7 @@
|
||||
|
||||
#include "../util/header.h"
|
||||
#include "../util/mutex.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
|
||||
@ -533,6 +535,57 @@ static int parse_cpu_list(const char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a CPU is online
|
||||
*
|
||||
* Returns:
|
||||
* 1 -> if CPU is online
|
||||
* 0 -> if CPU is offline
|
||||
* -1 -> error case
|
||||
*/
|
||||
static int is_cpu_online(unsigned int cpu)
|
||||
{
|
||||
char *str;
|
||||
size_t strlen;
|
||||
char buf[256];
|
||||
int status = -1;
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"/sys/devices/system/cpu/cpu%d", cpu);
|
||||
if (stat(buf, &statbuf) != 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check if /sys/devices/system/cpu/cpux/online file
|
||||
* exists. Some cases cpu0 won't have online file since
|
||||
* it is not expected to be turned off generally.
|
||||
* In kernels without CONFIG_HOTPLUG_CPU, this
|
||||
* file won't exist
|
||||
*/
|
||||
snprintf(buf, sizeof(buf),
|
||||
"/sys/devices/system/cpu/cpu%d/online", cpu);
|
||||
if (stat(buf, &statbuf) != 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Read online file using sysfs__read_str.
|
||||
* If read or open fails, return -1.
|
||||
* If read succeeds, return value from file
|
||||
* which gets stored in "str"
|
||||
*/
|
||||
snprintf(buf, sizeof(buf),
|
||||
"devices/system/cpu/cpu%d/online", cpu);
|
||||
|
||||
if (sysfs__read_str(buf, &str, &strlen) < 0)
|
||||
return status;
|
||||
|
||||
status = atoi(str);
|
||||
|
||||
free(str);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int parse_setup_cpu_list(void)
|
||||
{
|
||||
struct thread_data *td;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user