mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
perf tools changes for v7.0:
- Introduce 'perf sched stats' tool with record/report/diff workflows
using schedstat counters.
- Add a faster libdw based addr2line implementation and allow selecting
it or its alternatives via 'perf config addr2line.style='.
- Data-type profiling fixes and improvements including the ability
to select fields using 'perf report''s -F/-fields, e.g.:
'perf report --fields overhead,type'
- Add 'perf test' regression tests for Data-type profiling with
C and Rust workloads.
- Fix srcline printing with inlines in callchains, make sure this has
coverage in 'perf test'.
- Fix printing of leaf IP in LBR callchains.
- Fix display of metrics without sufficient permission in 'perf stat'.
- Print all machines in 'perf kvm report -vvv', not just the host.
- Switch from SHA-1 to BLAKE2s for build ID generation, remove SHA-1
code.
- Fix 'perf report's histogram entry collapsing with '-F' option.
- Use system's cacheline size instead of a hardcoded value in 'perf
report'.
- Allow filtering conversion by time range in 'perf data'.
- Cover conversion to CTF using 'perf data' in 'perf test'.
- Address newer glibc const-correctness (-Werror=discarded-qualifiers)
issues.
- Fixes and improvements for ARM's CoreSight support, simplify ARM SPE
event config in 'perf mem', update docs for 'perf c2c' including the
ARM events it can be used with.
- Build support for generating metrics from arch specific python script,
add extra AMD, Intel, ARM64 metrics using it.
- Add AMD Zen 6 events and metrics.
- Add JSON file with OpenHW Risc-V CVA6 hardware counters.
- Add 'perf kvm' stats live testing.
- Add more 'perf stat' tests to 'perf test'.
- Fix segfault in `perf lock contention -b/--use-bpf`
- Fix various 'perf test' cases for s390.
- Build system cleanups, bump minimum shellcheck version to 0.7.2
- Support building the capstone based annotation routines as a plugin.
- Allow passing extra Clang flags via EXTRA_BPF_FLAGS.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCaZn25QAKCRCyPKLppCJ+
J9MbAQCTKChBwDsqVh5iPr0UAc+mez9LOPJFa580SYH9nmd1jgD+Oqip7xCf514G
ZtYPNh+Mz0se0Mcb++syLUEjxvbrQQY=
=y2VY
-----END PGP SIGNATURE-----
Merge tag 'perf-tools-for-v7.0-1-2026-02-21' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools
Pull perf tools updates from Arnaldo Carvalho de Melo:
- Introduce 'perf sched stats' tool with record/report/diff workflows
using schedstat counters
- Add a faster libdw based addr2line implementation and allow selecting
it or its alternatives via 'perf config addr2line.style='
- Data-type profiling fixes and improvements including the ability to
select fields using 'perf report''s -F/-fields, e.g.:
'perf report --fields overhead,type'
- Add 'perf test' regression tests for Data-type profiling with C and
Rust workloads
- Fix srcline printing with inlines in callchains, make sure this has
coverage in 'perf test'
- Fix printing of leaf IP in LBR callchains
- Fix display of metrics without sufficient permission in 'perf stat'
- Print all machines in 'perf kvm report -vvv', not just the host
- Switch from SHA-1 to BLAKE2s for build ID generation, remove SHA-1
code
- Fix 'perf report's histogram entry collapsing with '-F' option
- Use system's cacheline size instead of a hardcoded value in 'perf
report'
- Allow filtering conversion by time range in 'perf data'
- Cover conversion to CTF using 'perf data' in 'perf test'
- Address newer glibc const-correctness (-Werror=discarded-qualifiers)
issues
- Fixes and improvements for ARM's CoreSight support, simplify ARM SPE
event config in 'perf mem', update docs for 'perf c2c' including the
ARM events it can be used with
- Build support for generating metrics from arch specific python
script, add extra AMD, Intel, ARM64 metrics using it
- Add AMD Zen 6 events and metrics
- Add JSON file with OpenHW Risc-V CVA6 hardware counters
- Add 'perf kvm' stats live testing
- Add more 'perf stat' tests to 'perf test'
- Fix segfault in `perf lock contention -b/--use-bpf`
- Fix various 'perf test' cases for s390
- Build system cleanups, bump minimum shellcheck version to 0.7.2
- Support building the capstone based annotation routines as a plugin
- Allow passing extra Clang flags via EXTRA_BPF_FLAGS
* tag 'perf-tools-for-v7.0-1-2026-02-21' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools: (255 commits)
perf test script: Add python script testing support
perf test script: Add perl script testing support
perf script: Allow the generated script to be a path
perf test: perf data --to-ctf testing
perf test: Test pipe mode with data conversion --to-json
perf json: Pipe mode --to-ctf support
perf json: Pipe mode --to-json support
perf check: Add libbabeltrace to the listed features
perf build: Allow passing extra Clang flags via EXTRA_BPF_FLAGS
perf test data_type_profiling.sh: Skip just the Rust tests if code_with_type workload is missing
tools build: Fix feature test for rust compiler
perf libunwind: Fix calls to thread__e_machine()
perf stat: Add no-affinity flag
perf evlist: Reduce affinity use and move into iterator, fix no affinity
perf evlist: Missing TPEBS close in evlist__close()
perf evlist: Special map propagation for tool events that read on 1 CPU
perf stat-shadow: In prepare_metric fix guard on reading NULL perf_stat_evsel
Revert "perf tool_pmu: More accurately set the cpus for tool events"
tools build: Emit dependencies file for test-rust.bin
tools build: Make test-rust.bin be removed by the 'clean' target
...
This commit is contained in:
commit
c7decec2f2
324 changed files with 15622 additions and 4400 deletions
|
|
@ -1,2 +1,24 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#include <asm/unistd_64.h>
|
||||
/*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ARCH_WANT_RENAMEAT
|
||||
#define __ARCH_WANT_NEW_STAT
|
||||
#define __ARCH_WANT_SET_GET_RLIMIT
|
||||
#define __ARCH_WANT_TIME32_SYSCALLS
|
||||
#define __ARCH_WANT_MEMFD_SECRET
|
||||
|
||||
#include <asm-generic/unistd.h>
|
||||
|
|
|
|||
|
|
@ -76,6 +76,14 @@ quiet_cmd_host_ld_multi = HOSTLD $@
|
|||
cmd_host_ld_multi = $(if $(strip $(obj-y)),\
|
||||
$(HOSTLD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(HOSTAR) rcs $@)
|
||||
|
||||
rust_common_cmd = \
|
||||
$(RUSTC) $(rust_flags) \
|
||||
--crate-type staticlib -L $(objtree)/rust/ \
|
||||
--emit=dep-info=$(depfile),link
|
||||
|
||||
quiet_cmd_rustc_a_rs = $(RUSTC) $(quiet_modtag) $@
|
||||
cmd_rustc_a_rs = $(rust_common_cmd) -o $@ -g $< $(cmd_objtool)
|
||||
|
||||
ifneq ($(filter $(obj),$(hostprogs)),)
|
||||
host = host_
|
||||
endif
|
||||
|
|
@ -105,6 +113,12 @@ $(OUTPUT)%.s: %.c FORCE
|
|||
$(call rule_mkdir)
|
||||
$(call if_changed_dep,cc_s_c)
|
||||
|
||||
# it's recommended to build a static rust library, when a foreight (to rust)
|
||||
# linker is used.
|
||||
$(OUTPUT)%.a: %.rs FORCE
|
||||
$(call rule_mkdir)
|
||||
$(call if_changed_dep,rustc_a_rs)
|
||||
|
||||
# bison and flex files are generated in the OUTPUT directory
|
||||
# so it needs a separate rule to depend on them properly
|
||||
$(OUTPUT)%-bison.o: $(OUTPUT)%-bison.c FORCE
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ FEATURE_TESTS_BASIC := \
|
|||
gettid \
|
||||
glibc \
|
||||
libbfd \
|
||||
libbfd-buildid \
|
||||
libbfd-threadsafe \
|
||||
libelf \
|
||||
libelf-getphdrnum \
|
||||
libelf-gelf_getnote \
|
||||
|
|
@ -149,7 +149,8 @@ FEATURE_DISPLAY ?= \
|
|||
bpf \
|
||||
libaio \
|
||||
libzstd \
|
||||
libopenssl
|
||||
libopenssl \
|
||||
rust
|
||||
|
||||
#
|
||||
# Declare group members of a feature to display the logical OR of the detection
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ FILES= \
|
|||
test-gtk2-infobar.bin \
|
||||
test-hello.bin \
|
||||
test-libbfd.bin \
|
||||
test-libbfd-buildid.bin \
|
||||
test-libbfd-threadsafe.bin \
|
||||
test-disassembler-four-args.bin \
|
||||
test-disassembler-init-styled.bin \
|
||||
test-reallocarray.bin \
|
||||
|
|
@ -73,6 +73,7 @@ FILES= \
|
|||
test-clang-bpf-co-re.bin \
|
||||
test-file-handle.bin \
|
||||
test-libpfm4.bin \
|
||||
test-rust.bin \
|
||||
test-libopenssl.bin
|
||||
|
||||
FILES := $(addprefix $(OUTPUT),$(FILES))
|
||||
|
|
@ -268,7 +269,7 @@ $(OUTPUT)test-libpython.bin:
|
|||
$(OUTPUT)test-libbfd.bin:
|
||||
$(BUILD_BFD)
|
||||
|
||||
$(OUTPUT)test-libbfd-buildid.bin:
|
||||
$(OUTPUT)test-libbfd-threadsafe.bin:
|
||||
$(BUILD_BFD) || $(BUILD_BFD) -liberty || $(BUILD_BFD) -liberty -lz
|
||||
|
||||
$(OUTPUT)test-disassembler-four-args.bin:
|
||||
|
|
@ -388,6 +389,15 @@ $(OUTPUT)test-libopenssl.bin:
|
|||
$(OUTPUT)test-bpftool-skeletons.bin:
|
||||
$(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons' \
|
||||
> $(@:.bin=.make.output) 2>&1
|
||||
|
||||
# Testing Rust is special: we don't compile anything, it's enough to check the
|
||||
# compiler presence. Compiling a test code for this purposes is problematic,
|
||||
# because Rust will emit a dependency file without any external references,
|
||||
# meaning that if rustc will be removed the build process will still think it's
|
||||
# there.
|
||||
$(OUTPUT)test-rust.bin:
|
||||
$(RUSTC) --version > /dev/null 2>&1
|
||||
|
||||
###############################
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <bfd.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bfd *abfd = bfd_openr("Pedro", 0);
|
||||
return abfd && (!abfd->build_id || abfd->build_id->size > 0x506564726f);
|
||||
}
|
||||
18
tools/build/feature/test-libbfd-threadsafe.c
Normal file
18
tools/build/feature/test-libbfd-threadsafe.c
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <bfd.h>
|
||||
|
||||
static bool lock(void *unused)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool unlock(void *unused)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Check for presence of new thread safety API (version 2.42) */
|
||||
return !bfd_thread_init(lock, unlock, NULL);
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#define _LINUX_BITFIELD_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -169,6 +169,16 @@ static inline void list_move_tail(struct list_head *list,
|
|||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_first -- tests whether @list is the first entry in list @head
|
||||
* @list: the entry to test
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline int list_is_first(const struct list_head *list, const struct list_head *head)
|
||||
{
|
||||
return list->prev == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_last - tests whether @list is the last entry in list @head
|
||||
* @list: the entry to test
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ SYNOPSIS
|
|||
struct perf_record_header_feature;
|
||||
struct perf_record_compressed;
|
||||
struct perf_record_compressed2;
|
||||
struct perf_record_schedstat_cpu;
|
||||
struct perf_record_schedstat_domain;
|
||||
--
|
||||
|
||||
DESCRIPTION
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
|
|||
TEST_ARGS := $(if $(V),-v)
|
||||
|
||||
INCLUDES = \
|
||||
-I$(OUTPUT)arch/$(SRCARCH)/include/generated/uapi \
|
||||
-I$(srctree)/tools/lib/perf/include \
|
||||
-I$(srctree)/tools/lib/ \
|
||||
-I$(srctree)/tools/include \
|
||||
|
|
@ -51,9 +50,9 @@ INCLUDES = \
|
|||
-I$(srctree)/tools/include/uapi
|
||||
|
||||
# Append required CFLAGS
|
||||
override CFLAGS := $(INCLUDES) $(CFLAGS)
|
||||
override CFLAGS += -g -Werror -Wall
|
||||
override CFLAGS += -fPIC
|
||||
override CFLAGS += $(INCLUDES)
|
||||
override CFLAGS += -fvisibility=hidden
|
||||
override CFLAGS += $(EXTRA_WARNINGS)
|
||||
override CFLAGS += $(EXTRA_CFLAGS)
|
||||
|
|
@ -100,16 +99,7 @@ $(LIBAPI)-clean:
|
|||
$(call QUIET_CLEAN, libapi)
|
||||
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
|
||||
|
||||
uapi-asm := $(OUTPUT)arch/$(SRCARCH)/include/generated/uapi/asm
|
||||
ifeq ($(SRCARCH),arm64)
|
||||
syscall-y := $(uapi-asm)/unistd_64.h
|
||||
endif
|
||||
uapi-asm-generic:
|
||||
$(if $(syscall-y),\
|
||||
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-headers obj=$(uapi-asm) \
|
||||
generic=include/uapi/asm-generic $(syscall-y),)
|
||||
|
||||
$(LIBPERF_IN): uapi-asm-generic FORCE
|
||||
$(LIBPERF_IN): FORCE
|
||||
$(Q)$(MAKE) $(build)=libperf
|
||||
|
||||
$(LIBPERF_A): $(LIBPERF_IN)
|
||||
|
|
@ -130,7 +120,7 @@ all: fixdep
|
|||
clean: $(LIBAPI)-clean
|
||||
$(call QUIET_CLEAN, libperf) $(RM) $(LIBPERF_A) \
|
||||
*.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBPERF_VERSION) .*.d .*.cmd tests/*.o LIBPERF-CFLAGS $(LIBPERF_PC) \
|
||||
$(TESTS_STATIC) $(TESTS_SHARED) $(syscall-y)
|
||||
$(TESTS_STATIC) $(TESTS_SHARED)
|
||||
|
||||
TESTS_IN = tests-in.o
|
||||
|
||||
|
|
@ -179,6 +169,7 @@ install_lib: libs
|
|||
cp -fpR $(LIBPERF_ALL) $(DESTDIR)$(libdir_SQ)
|
||||
|
||||
HDRS := bpf_perf.h core.h cpumap.h threadmap.h evlist.h evsel.h event.h mmap.h
|
||||
HDRS += schedstat-v15.h schedstat-v16.h schedstat-v17.h
|
||||
INTERNAL_HDRS := cpumap.h evlist.h evsel.h lib.h mmap.h rc_check.h threadmap.h xyarray.h
|
||||
|
||||
INSTALL_HDRS_PFX := $(DESTDIR)$(prefix)/include/perf
|
||||
|
|
|
|||
|
|
@ -101,6 +101,28 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
|
|||
evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tool events may only read on the first CPU index to avoid double
|
||||
* counting things like duration_time. Make the evsel->cpus contain just
|
||||
* that single entry otherwise we may spend time changing affinity to
|
||||
* CPUs that just have tool events, etc.
|
||||
*/
|
||||
if (evsel->reads_only_on_cpu_idx0 && perf_cpu_map__nr(evsel->cpus) > 0) {
|
||||
struct perf_cpu_map *srcs[3] = {
|
||||
evlist->all_cpus,
|
||||
evlist->user_requested_cpus,
|
||||
evsel->pmu_cpus,
|
||||
};
|
||||
for (size_t i = 0; i < ARRAY_SIZE(srcs); i++) {
|
||||
if (!srcs[i])
|
||||
continue;
|
||||
|
||||
perf_cpu_map__put(evsel->cpus);
|
||||
evsel->cpus = perf_cpu_map__new_int(perf_cpu_map__cpu(srcs[i], 0).cpu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check assert before the evsel is potentially removed. */
|
||||
assert(!evsel->requires_cpu || !perf_cpu_map__has_any_cpu(evsel->cpus));
|
||||
|
||||
|
|
@ -133,16 +155,22 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
|
|||
|
||||
static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *n;
|
||||
|
||||
evlist->needs_map_propagation = true;
|
||||
|
||||
/* Clear the all_cpus set which will be merged into during propagation. */
|
||||
perf_cpu_map__put(evlist->all_cpus);
|
||||
evlist->all_cpus = NULL;
|
||||
|
||||
list_for_each_entry_safe(evsel, n, &evlist->entries, node)
|
||||
/* 2 rounds so that reads_only_on_cpu_idx0 benefit from knowing the other CPU maps. */
|
||||
for (int round = 0; round < 2; round++) {
|
||||
struct perf_evsel *evsel, *n;
|
||||
|
||||
list_for_each_entry_safe(evsel, n, &evlist->entries, node) {
|
||||
if ((!evsel->reads_only_on_cpu_idx0 && round == 0) ||
|
||||
(evsel->reads_only_on_cpu_idx0 && round == 1))
|
||||
__perf_evlist__propagate_maps(evlist, evsel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evlist__add(struct perf_evlist *evlist,
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ struct perf_evsel {
|
|||
bool requires_cpu;
|
||||
/** Is the PMU for the event a core one? Effects the handling of own_cpus. */
|
||||
bool is_pmu_core;
|
||||
/** Does the evsel on read on the first CPU index such as tool time events? */
|
||||
bool reads_only_on_cpu_idx0;
|
||||
int idx;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -496,6 +496,71 @@ struct perf_record_bpf_metadata {
|
|||
struct perf_record_bpf_metadata_entry entries[];
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_cpu_v15 {
|
||||
#define CPU_FIELD(_type, _name, _desc, _format, _is_pct, _pct_of, _ver) _type _name
|
||||
#include "schedstat-v15.h"
|
||||
#undef CPU_FIELD
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_cpu_v16 {
|
||||
#define CPU_FIELD(_type, _name, _desc, _format, _is_pct, _pct_of, _ver) _type _name
|
||||
#include "schedstat-v16.h"
|
||||
#undef CPU_FIELD
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_cpu_v17 {
|
||||
#define CPU_FIELD(_type, _name, _desc, _format, _is_pct, _pct_of, _ver) _type _name
|
||||
#include "schedstat-v17.h"
|
||||
#undef CPU_FIELD
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_cpu {
|
||||
struct perf_event_header header;
|
||||
__u64 timestamp;
|
||||
__u32 cpu;
|
||||
__u16 version;
|
||||
/* Padding */
|
||||
char __pad[2];
|
||||
union {
|
||||
struct perf_record_schedstat_cpu_v15 v15;
|
||||
struct perf_record_schedstat_cpu_v16 v16;
|
||||
struct perf_record_schedstat_cpu_v17 v17;
|
||||
};
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_domain_v15 {
|
||||
#define DOMAIN_FIELD(_type, _name, _desc, _format, _is_jiffies, _ver) _type _name
|
||||
#include "schedstat-v15.h"
|
||||
#undef DOMAIN_FIELD
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_domain_v16 {
|
||||
#define DOMAIN_FIELD(_type, _name, _desc, _format, _is_jiffies, _ver) _type _name
|
||||
#include "schedstat-v16.h"
|
||||
#undef DOMAIN_FIELD
|
||||
};
|
||||
|
||||
struct perf_record_schedstat_domain_v17 {
|
||||
#define DOMAIN_FIELD(_type, _name, _desc, _format, _is_jiffies, _ver) _type _name
|
||||
#include "schedstat-v17.h"
|
||||
#undef DOMAIN_FIELD
|
||||
};
|
||||
|
||||
#define DOMAIN_NAME_LEN 16
|
||||
|
||||
struct perf_record_schedstat_domain {
|
||||
struct perf_event_header header;
|
||||
__u64 timestamp;
|
||||
__u32 cpu;
|
||||
__u16 version;
|
||||
__u16 domain;
|
||||
union {
|
||||
struct perf_record_schedstat_domain_v15 v15;
|
||||
struct perf_record_schedstat_domain_v16 v16;
|
||||
struct perf_record_schedstat_domain_v17 v17;
|
||||
};
|
||||
};
|
||||
|
||||
enum perf_user_event_type { /* above any possible kernel type */
|
||||
PERF_RECORD_USER_TYPE_START = 64,
|
||||
PERF_RECORD_HEADER_ATTR = 64,
|
||||
|
|
@ -519,6 +584,8 @@ enum perf_user_event_type { /* above any possible kernel type */
|
|||
PERF_RECORD_FINISHED_INIT = 82,
|
||||
PERF_RECORD_COMPRESSED2 = 83,
|
||||
PERF_RECORD_BPF_METADATA = 84,
|
||||
PERF_RECORD_SCHEDSTAT_CPU = 85,
|
||||
PERF_RECORD_SCHEDSTAT_DOMAIN = 86,
|
||||
PERF_RECORD_HEADER_MAX
|
||||
};
|
||||
|
||||
|
|
@ -562,6 +629,8 @@ union perf_event {
|
|||
struct perf_record_compressed pack;
|
||||
struct perf_record_compressed2 pack2;
|
||||
struct perf_record_bpf_metadata bpf_metadata;
|
||||
struct perf_record_schedstat_cpu schedstat_cpu;
|
||||
struct perf_record_schedstat_domain schedstat_domain;
|
||||
};
|
||||
|
||||
#endif /* __LIBPERF_EVENT_H */
|
||||
|
|
|
|||
146
tools/lib/perf/include/perf/schedstat-v15.h
Normal file
146
tools/lib/perf/include/perf/schedstat-v15.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifdef CPU_FIELD
|
||||
CPU_FIELD(__u32, yld_count, "sched_yield() count",
|
||||
"%11u", false, yld_count, v15);
|
||||
CPU_FIELD(__u32, array_exp, "Legacy counter can be ignored",
|
||||
"%11u", false, array_exp, v15);
|
||||
CPU_FIELD(__u32, sched_count, "schedule() called",
|
||||
"%11u", false, sched_count, v15);
|
||||
CPU_FIELD(__u32, sched_goidle, "schedule() left the processor idle",
|
||||
"%11u", true, sched_count, v15);
|
||||
CPU_FIELD(__u32, ttwu_count, "try_to_wake_up() was called",
|
||||
"%11u", false, ttwu_count, v15);
|
||||
CPU_FIELD(__u32, ttwu_local, "try_to_wake_up() was called to wake up the local cpu",
|
||||
"%11u", true, ttwu_count, v15);
|
||||
CPU_FIELD(__u64, rq_cpu_time, "total runtime by tasks on this processor (in jiffies)",
|
||||
"%11llu", false, rq_cpu_time, v15);
|
||||
CPU_FIELD(__u64, run_delay, "total waittime by tasks on this processor (in jiffies)",
|
||||
"%11llu", true, rq_cpu_time, v15);
|
||||
CPU_FIELD(__u64, pcount, "total timeslices run on this cpu",
|
||||
"%11llu", false, pcount, v15);
|
||||
#endif
|
||||
|
||||
#ifdef DOMAIN_FIELD
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category idle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, idle_lb_count,
|
||||
"load_balance() count on cpu idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_balanced,
|
||||
"load_balance() found balanced on cpu idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_failed,
|
||||
"load_balance() move task failed on cpu idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance,
|
||||
"imbalance sum on cpu idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_gained,
|
||||
"pull_task() count on cpu idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu idle", "%11u", true, v15);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(idle_lb_success_count, "load_balance() success count on cpu idle", "%11u",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, v15);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(idle_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu idle)", "%11.2Lf",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, idle_lb_gained, v15);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category busy> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, busy_lb_count,
|
||||
"load_balance() count on cpu busy", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_balanced,
|
||||
"load_balance() found balanced on cpu busy", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_failed,
|
||||
"load_balance() move task failed on cpu busy", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance,
|
||||
"imbalance sum on cpu busy", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_gained,
|
||||
"pull_task() count on cpu busy", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu busy", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu busy", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu busy", "%11u", true, v15);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(busy_lb_success_count, "load_balance() success count on cpu busy", "%11u",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, v15);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(busy_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu busy)", "%11.2Lf",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, busy_lb_gained, v15);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category newidle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, newidle_lb_count,
|
||||
"load_balance() count on cpu newly idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_balanced,
|
||||
"load_balance() found balanced on cpu newly idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_failed,
|
||||
"load_balance() move task failed on cpu newly idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance,
|
||||
"imbalance sum on cpu newly idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_gained,
|
||||
"pull_task() count on cpu newly idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu newly idle", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu newly idle", "%11u", true, v15);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu newly idle", "%11u", true, v15);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(newidle_lb_success_count,
|
||||
"load_balance() success count on cpu newly idle", "%11u",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, v15);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(newidle_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu newly idle)", "%11.2Lf",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, newidle_lb_gained, v15);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category active_load_balance()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, alb_count,
|
||||
"active_load_balance() count", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, alb_failed,
|
||||
"active_load_balance() move task failed", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, alb_pushed,
|
||||
"active_load_balance() successfully moved a task", "%11u", false, v15);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_exec()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbe_count,
|
||||
"sbe_count is not used", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, sbe_balanced,
|
||||
"sbe_balanced is not used", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, sbe_pushed,
|
||||
"sbe_pushed is not used", "%11u", false, v15);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_fork()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbf_count,
|
||||
"sbf_count is not used", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, sbf_balanced,
|
||||
"sbf_balanced is not used", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, sbf_pushed,
|
||||
"sbf_pushed is not used", "%11u", false, v15);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Wakeup Info> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, ttwu_wake_remote,
|
||||
"try_to_wake_up() awoke a task that last ran on a diff cpu", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_affine,
|
||||
"try_to_wake_up() moved task because cache-cold on own cpu", "%11u", false, v15);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_balance,
|
||||
"try_to_wake_up() started passive balancing", "%11u", false, v15);
|
||||
#endif /* DOMAIN_FIELD */
|
||||
146
tools/lib/perf/include/perf/schedstat-v16.h
Normal file
146
tools/lib/perf/include/perf/schedstat-v16.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifdef CPU_FIELD
|
||||
CPU_FIELD(__u32, yld_count, "sched_yield() count",
|
||||
"%11u", false, yld_count, v16);
|
||||
CPU_FIELD(__u32, array_exp, "Legacy counter can be ignored",
|
||||
"%11u", false, array_exp, v16);
|
||||
CPU_FIELD(__u32, sched_count, "schedule() called",
|
||||
"%11u", false, sched_count, v16);
|
||||
CPU_FIELD(__u32, sched_goidle, "schedule() left the processor idle",
|
||||
"%11u", true, sched_count, v16);
|
||||
CPU_FIELD(__u32, ttwu_count, "try_to_wake_up() was called",
|
||||
"%11u", false, ttwu_count, v16);
|
||||
CPU_FIELD(__u32, ttwu_local, "try_to_wake_up() was called to wake up the local cpu",
|
||||
"%11u", true, ttwu_count, v16);
|
||||
CPU_FIELD(__u64, rq_cpu_time, "total runtime by tasks on this processor (in jiffies)",
|
||||
"%11llu", false, rq_cpu_time, v16);
|
||||
CPU_FIELD(__u64, run_delay, "total waittime by tasks on this processor (in jiffies)",
|
||||
"%11llu", true, rq_cpu_time, v16);
|
||||
CPU_FIELD(__u64, pcount, "total timeslices run on this cpu",
|
||||
"%11llu", false, pcount, v16);
|
||||
#endif /* CPU_FIELD */
|
||||
|
||||
#ifdef DOMAIN_FIELD
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category busy> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, busy_lb_count,
|
||||
"load_balance() count on cpu busy", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_balanced,
|
||||
"load_balance() found balanced on cpu busy", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_failed,
|
||||
"load_balance() move task failed on cpu busy", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance,
|
||||
"imbalance sum on cpu busy", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_gained,
|
||||
"pull_task() count on cpu busy", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu busy", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu busy", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu busy", "%11u", true, v16);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(busy_lb_success_count, "load_balance() success count on cpu busy", "%11u",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, v16);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(busy_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu busy)", "%11.2Lf",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, busy_lb_gained, v16);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category idle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, idle_lb_count,
|
||||
"load_balance() count on cpu idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_balanced,
|
||||
"load_balance() found balanced on cpu idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_failed,
|
||||
"load_balance() move task failed on cpu idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance,
|
||||
"imbalance sum on cpu idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_gained,
|
||||
"pull_task() count on cpu idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu idle", "%11u", true, v16);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(idle_lb_success_count, "load_balance() success count on cpu idle", "%11u",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, v16);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(idle_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu idle)", "%11.2Lf",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, idle_lb_gained, v16);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category newidle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, newidle_lb_count,
|
||||
"load_balance() count on cpu newly idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_balanced,
|
||||
"load_balance() found balanced on cpu newly idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_failed,
|
||||
"load_balance() move task failed on cpu newly idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance,
|
||||
"imbalance sum on cpu newly idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_gained,
|
||||
"pull_task() count on cpu newly idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu newly idle", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu newly idle", "%11u", true, v16);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu newly idle", "%11u", true, v16);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(newidle_lb_success_count,
|
||||
"load_balance() success count on cpu newly idle", "%11u",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, v16);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(newidle_lb_avg_count,
|
||||
"avg task pulled per successful lb attempt (cpu newly idle)", "%11.2Lf",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, newidle_lb_gained, v16);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category active_load_balance()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, alb_count,
|
||||
"active_load_balance() count", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, alb_failed,
|
||||
"active_load_balance() move task failed", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, alb_pushed,
|
||||
"active_load_balance() successfully moved a task", "%11u", false, v16);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_exec()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbe_count,
|
||||
"sbe_count is not used", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, sbe_balanced,
|
||||
"sbe_balanced is not used", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, sbe_pushed,
|
||||
"sbe_pushed is not used", "%11u", false, v16);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_fork()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbf_count,
|
||||
"sbf_count is not used", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, sbf_balanced,
|
||||
"sbf_balanced is not used", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, sbf_pushed,
|
||||
"sbf_pushed is not used", "%11u", false, v16);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Wakeup Info> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, ttwu_wake_remote,
|
||||
"try_to_wake_up() awoke a task that last ran on a diff cpu", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_affine,
|
||||
"try_to_wake_up() moved task because cache-cold on own cpu", "%11u", false, v16);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_balance,
|
||||
"try_to_wake_up() started passive balancing", "%11u", false, v16);
|
||||
#endif /* DOMAIN_FIELD */
|
||||
164
tools/lib/perf/include/perf/schedstat-v17.h
Normal file
164
tools/lib/perf/include/perf/schedstat-v17.h
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifdef CPU_FIELD
|
||||
CPU_FIELD(__u32, yld_count, "sched_yield() count",
|
||||
"%11u", false, yld_count, v17);
|
||||
CPU_FIELD(__u32, array_exp, "Legacy counter can be ignored",
|
||||
"%11u", false, array_exp, v17);
|
||||
CPU_FIELD(__u32, sched_count, "schedule() called",
|
||||
"%11u", false, sched_count, v17);
|
||||
CPU_FIELD(__u32, sched_goidle, "schedule() left the processor idle",
|
||||
"%11u", true, sched_count, v17);
|
||||
CPU_FIELD(__u32, ttwu_count, "try_to_wake_up() was called",
|
||||
"%11u", false, ttwu_count, v17);
|
||||
CPU_FIELD(__u32, ttwu_local, "try_to_wake_up() was called to wake up the local cpu",
|
||||
"%11u", true, ttwu_count, v17);
|
||||
CPU_FIELD(__u64, rq_cpu_time, "total runtime by tasks on this processor (in jiffies)",
|
||||
"%11llu", false, rq_cpu_time, v17);
|
||||
CPU_FIELD(__u64, run_delay, "total waittime by tasks on this processor (in jiffies)",
|
||||
"%11llu", true, rq_cpu_time, v17);
|
||||
CPU_FIELD(__u64, pcount, "total timeslices run on this cpu",
|
||||
"%11llu", false, pcount, v17);
|
||||
#endif /* CPU_FIELD */
|
||||
|
||||
#ifdef DOMAIN_FIELD
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category busy> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, busy_lb_count,
|
||||
"load_balance() count on cpu busy", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_balanced,
|
||||
"load_balance() found balanced on cpu busy", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_failed,
|
||||
"load_balance() move task failed on cpu busy", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance_load,
|
||||
"imbalance in load on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance_util,
|
||||
"imbalance in utilization on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance_task,
|
||||
"imbalance in number of tasks on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_imbalance_misfit,
|
||||
"imbalance in misfit tasks on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_gained,
|
||||
"pull_task() count on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu busy", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu busy", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, busy_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu busy", "%11u", true, v17);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(busy_lb_success_count, "load_balance() success count on cpu busy", "%11u",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, v17);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(busy_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu busy)", "%11.2Lf",
|
||||
busy_lb_count, busy_lb_balanced, busy_lb_failed, busy_lb_gained, v17);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category idle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, idle_lb_count,
|
||||
"load_balance() count on cpu idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_balanced,
|
||||
"load_balance() found balanced on cpu idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_failed,
|
||||
"load_balance() move task failed on cpu idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance_load,
|
||||
"imbalance in load on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance_util,
|
||||
"imbalance in utilization on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance_task,
|
||||
"imbalance in number of tasks on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_imbalance_misfit,
|
||||
"imbalance in misfit tasks on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_gained,
|
||||
"pull_task() count on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, idle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu idle", "%11u", true, v17);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(idle_lb_success_count, "load_balance() success count on cpu idle", "%11u",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, v17);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(idle_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu idle)", "%11.2Lf",
|
||||
idle_lb_count, idle_lb_balanced, idle_lb_failed, idle_lb_gained, v17);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category newidle> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, newidle_lb_count,
|
||||
"load_balance() count on cpu newly idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_balanced,
|
||||
"load_balance() found balanced on cpu newly idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_failed,
|
||||
"load_balance() move task failed on cpu newly idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance_load,
|
||||
"imbalance in load on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance_util,
|
||||
"imbalance in utilization on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance_task,
|
||||
"imbalance in number of tasks on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_imbalance_misfit,
|
||||
"imbalance in misfit tasks on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_gained,
|
||||
"pull_task() count on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_hot_gained,
|
||||
"pull_task() when target task was cache-hot on cpu newly idle", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyq,
|
||||
"load_balance() failed to find busier queue on cpu newly idle", "%11u", true, v17);
|
||||
DOMAIN_FIELD(__u32, newidle_lb_nobusyg,
|
||||
"load_balance() failed to find busier group on cpu newly idle", "%11u", true, v17);
|
||||
#ifdef DERIVED_CNT_FIELD
|
||||
DERIVED_CNT_FIELD(newidle_lb_success_count,
|
||||
"load_balance() success count on cpu newly idle", "%11u",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, v17);
|
||||
#endif
|
||||
#ifdef DERIVED_AVG_FIELD
|
||||
DERIVED_AVG_FIELD(newidle_lb_avg_pulled,
|
||||
"avg task pulled per successful lb attempt (cpu newly idle)", "%11.2Lf",
|
||||
newidle_lb_count, newidle_lb_balanced, newidle_lb_failed, newidle_lb_gained, v17);
|
||||
#endif
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category active_load_balance()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, alb_count,
|
||||
"active_load_balance() count", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, alb_failed,
|
||||
"active_load_balance() move task failed", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, alb_pushed,
|
||||
"active_load_balance() successfully moved a task", "%11u", false, v17);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_exec()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbe_count,
|
||||
"sbe_count is not used", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, sbe_balanced,
|
||||
"sbe_balanced is not used", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, sbe_pushed,
|
||||
"sbe_pushed is not used", "%11u", false, v17);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Category sched_balance_fork()> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, sbf_count,
|
||||
"sbf_count is not used", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, sbf_balanced,
|
||||
"sbf_balanced is not used", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, sbf_pushed,
|
||||
"sbf_pushed is not used", "%11u", false, v17);
|
||||
#ifdef DOMAIN_CATEGORY
|
||||
DOMAIN_CATEGORY(" <Wakeup Info> ");
|
||||
#endif
|
||||
DOMAIN_FIELD(__u32, ttwu_wake_remote,
|
||||
"try_to_wake_up() awoke a task that last ran on a diff cpu", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_affine,
|
||||
"try_to_wake_up() moved task because cache-cold on own cpu", "%11u", false, v17);
|
||||
DOMAIN_FIELD(__u32, ttwu_move_balance,
|
||||
"try_to_wake_up() started passive balancing", "%11u", false, v17);
|
||||
#endif /* DOMAIN_FIELD */
|
||||
|
|
@ -97,11 +97,13 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
|
|||
ei++;
|
||||
}
|
||||
}
|
||||
if (ci != cj) {
|
||||
while (ci < cmds->cnt) {
|
||||
cmds->names[cj++] = cmds->names[ci];
|
||||
cmds->names[ci++] = NULL;
|
||||
if (ci != cj) {
|
||||
cmds->names[cj] = cmds->names[ci];
|
||||
cmds->names[ci] = NULL;
|
||||
}
|
||||
ci++;
|
||||
cj++;
|
||||
}
|
||||
for (ci = cj; ci < cmds->cnt; ci++)
|
||||
assert(cmds->names[ci] == NULL);
|
||||
|
|
|
|||
6
tools/perf/.gitignore
vendored
6
tools/perf/.gitignore
vendored
|
|
@ -36,12 +36,18 @@ config.mak.autogen
|
|||
util/intel-pt-decoder/inat-tables.c
|
||||
arch/*/include/generated/
|
||||
trace/beauty/generated/
|
||||
pmu-events/arch/common/common/legacy-cache.json
|
||||
pmu-events/pmu-events.c
|
||||
pmu-events/jevents
|
||||
pmu-events/metric_test.log
|
||||
pmu-events/empty-pmu-events.log
|
||||
pmu-events/test-empty-pmu-events.c
|
||||
*.shellcheck_log
|
||||
pmu-events/arch/**/extra-metrics.json
|
||||
pmu-events/arch/**/extra-metricgroups.json
|
||||
tests/shell/*.shellcheck_log
|
||||
tests/shell/coresight/*.shellcheck_log
|
||||
tests/shell/lib/*.shellcheck_log
|
||||
feature/
|
||||
libapi/
|
||||
libbpf/
|
||||
|
|
|
|||
|
|
@ -160,20 +160,43 @@ Following perf record options are configured by default:
|
|||
|
||||
-W,-d,--phys-data,--sample-cpu
|
||||
|
||||
Unless specified otherwise with '-e' option, following events are monitored by
|
||||
default on Intel:
|
||||
The following table lists the events monitored on different architectures.
|
||||
Unless specified otherwise with the -e option, the tool will select the
|
||||
default events.
|
||||
|
||||
cpu/mem-loads,ldlat=30/P
|
||||
cpu/mem-stores/P
|
||||
|
||||
following on AMD:
|
||||
|
||||
ibs_op//
|
||||
|
||||
and following on PowerPC:
|
||||
|
||||
cpu/mem-loads/
|
||||
cpu/mem-stores/
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| Arch | Configuration | Options | Events |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| Intel | Default | -e ldlat-loads | cpu/mem-loads,ldlat=30/P |
|
||||
| | | -e ldlat-stores | cpu/mem-stores/P |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Load only | -e ldlat-loads | cpu/mem-loads,ldlat=30/P |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Store only | -e ldlat-stores | cpu/mem-stores/P |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| Intel | Default | -e ldlat-loads | {cpu/mem-loads-aux/,cpu/mem-loads,ldlat=30/}:P |
|
||||
| with | | -e ldlat-stores | cpu/mem-stores/P |
|
||||
| AUX |--------------+------------------+--------------------------------------------------------------------------------+
|
||||
| | Load only | -e ldlat-loads | {cpu/mem-loads-aux/,cpu/mem-loads,ldlat=30/}:P |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Store only | -e ldlat-stores | cpu/mem-stores/P |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| AMD | Default | -e mem-ldst | ibs_op// (without latency support) |
|
||||
| | | | ibs_op/ldlat=30/ (with latency support) |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| PowerPC| Default | -e ldlat-loads | cpu/mem-loads/ |
|
||||
| | | -e ldlat-stores | cpu/mem-stores/ |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Load only | -e ldlat-loads | cpu/mem-loads/ |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Store only | -e ldlat-stores | cpu/mem-stores/ |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| Arm | Default | -e spe-ldst | arm_spe_0/ts_enable=1,pa_enable=1,load_filter=1,store_filter=1,min_latency=30/ |
|
||||
| SPE |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Load only | -e spe-load | arm_spe_0/ts_enable=1,pa_enable=1,load_filter=1,min_latency=30/ |
|
||||
| |---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
| | Store only | -e spe-store | arm_spe_0/ts_enable=1,pa_enable=1,store_filter=1/ |
|
||||
+--------+---------------+-----------------+--------------------------------------------------------------------------------+
|
||||
|
||||
User can pass any 'perf record' option behind '--' mark, like (to enable
|
||||
callchains and system wide monitoring):
|
||||
|
|
|
|||
|
|
@ -40,6 +40,34 @@ OPTIONS for 'convert'
|
|||
--force::
|
||||
Don't complain, do it.
|
||||
|
||||
--time::
|
||||
Only convert samples within given time window: <start>,<stop>. Times
|
||||
have the format seconds.nanoseconds. If start is not given (i.e. time
|
||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||
stop time is not given (i.e. time string is 'x.y,') then analysis goes
|
||||
to end of file. Multiple ranges can be separated by spaces, which
|
||||
requires the argument to be quoted e.g. --time "1234.567,1234.789 1235,"
|
||||
|
||||
Also support time percent with multiple time ranges. Time string is
|
||||
'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'.
|
||||
|
||||
For example:
|
||||
Select the second 10% time slice:
|
||||
|
||||
perf data convert --to-json out.json --time 10%/2
|
||||
|
||||
Select from 0% to 10% time slice:
|
||||
|
||||
perf data convert --to-json out.json --time 0%-10%
|
||||
|
||||
Select the first and second 10% time slices:
|
||||
|
||||
perf data convert --to-json out.json --time 10%/1,10%/2
|
||||
|
||||
Select from 0% to 10% and 30% to 40% slices:
|
||||
|
||||
perf data convert --to-json out.json --time 0%-10%,30%-40%
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc).
|
||||
|
|
|
|||
|
|
@ -109,6 +109,11 @@ include::itrace.txt[]
|
|||
should be used, and also --buildid-all and --switch-events may be
|
||||
useful.
|
||||
|
||||
--convert-callchain::
|
||||
Parse DWARF callchains and convert them to usual callchains. This also
|
||||
discards stack and register data from the samples. This will lose
|
||||
inlined callchain entries.
|
||||
|
||||
:GMEXAMPLECMD: inject
|
||||
:GMEXAMPLESUBCMD:
|
||||
include::guestmount.txt[]
|
||||
|
|
|
|||
|
|
@ -344,7 +344,8 @@ OPTIONS
|
|||
|
||||
-d::
|
||||
--data::
|
||||
Record the sample virtual addresses. Implies --sample-mem-info.
|
||||
Record the sample virtual addresses. Implies --sample-mem-info and
|
||||
--data-mmap.
|
||||
|
||||
--phys-data::
|
||||
Record the sample physical addresses.
|
||||
|
|
@ -454,7 +455,7 @@ following filters are defined:
|
|||
- no_tx: only when the target is not in a hardware transaction
|
||||
- abort_tx: only when the target is a hardware transaction abort
|
||||
- cond: conditional branches
|
||||
- call_stack: save call stack
|
||||
- stack: save call stack
|
||||
- no_flags: don't save branch flags e.g prediction, misprediction etc
|
||||
- no_cycles: don't save branch cycles
|
||||
- hw_index: save branch hardware index
|
||||
|
|
@ -861,6 +862,11 @@ filtered through the mask provided by -C option.
|
|||
Prepare BPF filter to be used by regular users. The action should be
|
||||
either "pin" or "unpin". The filter can be used after it's pinned.
|
||||
|
||||
--data-mmap::
|
||||
Enable recording MMAP events for non-executable mappings. Basically
|
||||
perf only records executable mappings but data mmaping can be useful
|
||||
when you analyze data access with sample addresses. So using -d option
|
||||
would enable this unless you specify --no-data-mmap manually.
|
||||
|
||||
include::intel-hybrid.txt[]
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ perf-sched - Tool to trace/measure scheduler properties (latencies)
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf sched' {record|latency|map|replay|script|timehist}
|
||||
'perf sched' {record|latency|map|replay|script|timehist|stats}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
|
@ -80,8 +80,267 @@ There are several variants of 'perf sched':
|
|||
|
||||
Times are in msec.usec.
|
||||
|
||||
'perf sched stats {record | report | diff} <command>' to capture, report the diff
|
||||
in schedstat counters and show the difference between perf sched stats report
|
||||
respectively. schedstat counters which are present in the linux kernel and are
|
||||
exposed through the file ``/proc/schedstat``. These counters are enabled or disabled
|
||||
via the sysctl governed by the file ``/proc/sys/kernel/sched_schedstats``. These
|
||||
counters accounts for many scheduler events such as ``schedule()`` calls, load-balancing
|
||||
events, ``try_to_wakeup()`` call among others. This is useful in understanding the
|
||||
scheduler behavior for the workload.
|
||||
|
||||
Note: The tool will not give correct results if there is topological reordering or
|
||||
online/offline of cpus in between capturing snapshots of `/proc/schedstat`.
|
||||
|
||||
Example usage:
|
||||
perf sched stats record -- sleep 1
|
||||
perf sched stats report
|
||||
perf sched stats diff
|
||||
|
||||
A detailed description of the schedstats can be found in the Kernel Documentation:
|
||||
https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html
|
||||
|
||||
The result can be interpreted as follows:
|
||||
|
||||
The `perf sched stats report` starts with description of the columns present in
|
||||
the report. These column names are given before cpu and domain stats to improve
|
||||
the readability of the report.
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
DESC -> Description of the field
|
||||
COUNT -> Value of the field
|
||||
PCT_CHANGE -> Percent change with corresponding base value
|
||||
AVG_JIFFIES -> Avg time in jiffies between two consecutive occurrence of event
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Next is the total profiling time in terms of jiffies:
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Time elapsed (in jiffies) : 2323
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Next is CPU scheduling statistics. These are simple diffs of /proc/schedstat CPU lines
|
||||
along with description. The report also prints % relative to base stat.
|
||||
|
||||
In the example below, schedule() left the CPU0 idle 36.58% of the time. 0.45% of total
|
||||
try_to_wake_up() was to wakeup local CPU. And, the total waittime by tasks on CPU0 is
|
||||
48.70% of the total runtime by tasks on the same CPU.
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
CPU 0
|
||||
----------------------------------------------------------------------------------------------------
|
||||
DESC COUNT PCT_CHANGE
|
||||
----------------------------------------------------------------------------------------------------
|
||||
yld_count : 0
|
||||
array_exp : 0
|
||||
sched_count : 402267
|
||||
sched_goidle : 147161 ( 36.58% )
|
||||
ttwu_count : 236309
|
||||
ttwu_local : 1062 ( 0.45% )
|
||||
rq_cpu_time : 7083791148
|
||||
run_delay : 3449973971 ( 48.70% )
|
||||
pcount : 255035
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Next is load balancing statistics. For each of the sched domains
|
||||
(eg: `SMT`, `MC`, `DIE`...), the scheduler computes statistics under
|
||||
the following three categories:
|
||||
|
||||
1) Idle Load Balance: Load balancing performed on behalf of a long
|
||||
idling CPU by some other CPU.
|
||||
2) Busy Load Balance: Load balancing performed when the CPU was busy.
|
||||
3) New Idle Balance : Load balancing performed when a CPU just became
|
||||
idle.
|
||||
|
||||
Under each of these three categories, sched stats report provides
|
||||
different load balancing statistics. Along with direct stats, the
|
||||
report also contains derived metrics prefixed with *. Example:
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
CPU 0, DOMAIN SMT CPUS 0,64
|
||||
----------------------------------------------------------------------------------------------------
|
||||
DESC COUNT AVG_JIFFIES
|
||||
----------------------------------------- <Category busy> ------------------------------------------
|
||||
busy_lb_count : 136 $ 17.08 $
|
||||
busy_lb_balanced : 131 $ 17.73 $
|
||||
busy_lb_failed : 0 $ 0.00 $
|
||||
busy_lb_imbalance_load : 58
|
||||
busy_lb_imbalance_util : 0
|
||||
busy_lb_imbalance_task : 0
|
||||
busy_lb_imbalance_misfit : 0
|
||||
busy_lb_gained : 7
|
||||
busy_lb_hot_gained : 0
|
||||
busy_lb_nobusyq : 2 $ 1161.50 $
|
||||
busy_lb_nobusyg : 129 $ 18.01 $
|
||||
*busy_lb_success_count : 5
|
||||
*busy_lb_avg_pulled : 1.40
|
||||
----------------------------------------- <Category idle> ------------------------------------------
|
||||
idle_lb_count : 449 $ 5.17 $
|
||||
idle_lb_balanced : 382 $ 6.08 $
|
||||
idle_lb_failed : 3 $ 774.33 $
|
||||
idle_lb_imbalance_load : 0
|
||||
idle_lb_imbalance_util : 0
|
||||
idle_lb_imbalance_task : 71
|
||||
idle_lb_imbalance_misfit : 0
|
||||
idle_lb_gained : 67
|
||||
idle_lb_hot_gained : 0
|
||||
idle_lb_nobusyq : 0 $ 0.00 $
|
||||
idle_lb_nobusyg : 382 $ 6.08 $
|
||||
*idle_lb_success_count : 64
|
||||
*idle_lb_avg_pulled : 1.05
|
||||
---------------------------------------- <Category newidle> ----------------------------------------
|
||||
newidle_lb_count : 30471 $ 0.08 $
|
||||
newidle_lb_balanced : 28490 $ 0.08 $
|
||||
newidle_lb_failed : 633 $ 3.67 $
|
||||
newidle_lb_imbalance_load : 0
|
||||
newidle_lb_imbalance_util : 0
|
||||
newidle_lb_imbalance_task : 2040
|
||||
newidle_lb_imbalance_misfit : 0
|
||||
newidle_lb_gained : 1348
|
||||
newidle_lb_hot_gained : 0
|
||||
newidle_lb_nobusyq : 6 $ 387.17 $
|
||||
newidle_lb_nobusyg : 26634 $ 0.09 $
|
||||
*newidle_lb_success_count : 1348
|
||||
*newidle_lb_avg_pulled : 1.00
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Consider following line:
|
||||
|
||||
newidle_lb_balanced : 28490 $ 0.08 $
|
||||
|
||||
While profiling was active, the load-balancer found 28490 times the load
|
||||
needs to be balanced on a newly idle CPU 0. Following value encapsulated
|
||||
inside $ is average jiffies between two events (2323 / 28490 = 0.08).
|
||||
|
||||
Next are active_load_balance() stats. alb did not trigger while the
|
||||
profiling was active, hence it's all 0s.
|
||||
|
||||
--------------------------------- <Category active_load_balance()> ---------------------------------
|
||||
alb_count : 0
|
||||
alb_failed : 0
|
||||
alb_pushed : 0
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Next are sched_balance_exec() and sched_balance_fork() stats. They are
|
||||
not used but we kept it in RFC just for legacy purpose. Unless opposed,
|
||||
we plan to remove them in next revision.
|
||||
|
||||
Next are wakeup statistics. For every domain, the report also shows
|
||||
task-wakeup statistics. Example:
|
||||
|
||||
------------------------------------------ <Wakeup Info> -------------------------------------------
|
||||
ttwu_wake_remote : 1590
|
||||
ttwu_move_affine : 84
|
||||
ttwu_move_balance : 0
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Same set of stats are reported for each CPU and each domain level.
|
||||
|
||||
How to interpret the diff
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The `perf sched stats diff` will also start with explaining the columns
|
||||
present in the diff. Then it will show the diff in time in terms of
|
||||
jiffies. The order of the values depends on the order of input data
|
||||
files. It will take `perf.data.old` and `perf.data` respectively as the
|
||||
defaults for comparison. Example:
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Time elapsed (in jiffies) : 2009, 2001
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Below is the sample representing the difference in cpu and domain stats of
|
||||
two runs. Here third column or the values enclosed in `|...|` shows the
|
||||
percent change between the two. Second and fourth columns shows the
|
||||
side-by-side representions of the corresponding fields from `perf sched
|
||||
stats report`.
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
CPU <ALL CPUS SUMMARY>
|
||||
----------------------------------------------------------------------------------------------------
|
||||
DESC COUNT1 COUNT2 PCT_CHANG>
|
||||
----------------------------------------------------------------------------------------------------
|
||||
yld_count : 0, 0 | 0.00>
|
||||
array_exp : 0, 0 | 0.00>
|
||||
sched_count : 528533, 412573 | -21.94>
|
||||
sched_goidle : 193426, 146082 | -24.48>
|
||||
ttwu_count : 313134, 385975 | 23.26>
|
||||
ttwu_local : 1126, 1282 | 13.85>
|
||||
rq_cpu_time : 8257200244, 8301250047 | 0.53>
|
||||
run_delay : 4728347053, 3997100703 | -15.47>
|
||||
pcount : 335031, 266396 | -20.49>
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
Below is the sample of domain stats diff:
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
CPU <ALL CPUS SUMMARY>, DOMAIN SMT
|
||||
----------------------------------------------------------------------------------------------------
|
||||
DESC COUNT1 COUNT2 PCT_CHANG>
|
||||
----------------------------------------- <Category busy> ------------------------------------------
|
||||
busy_lb_count : 122, 80 | -34.43>
|
||||
busy_lb_balanced : 115, 76 | -33.91>
|
||||
busy_lb_failed : 1, 3 | 200.00>
|
||||
busy_lb_imbalance_load : 35, 49 | 40.00>
|
||||
busy_lb_imbalance_util : 0, 0 | 0.00>
|
||||
busy_lb_imbalance_task : 0, 0 | 0.00>
|
||||
busy_lb_imbalance_misfit : 0, 0 | 0.00>
|
||||
busy_lb_gained : 7, 2 | -71.43>
|
||||
busy_lb_hot_gained : 0, 0 | 0.00>
|
||||
busy_lb_nobusyq : 0, 0 | 0.00>
|
||||
busy_lb_nobusyg : 115, 76 | -33.91>
|
||||
*busy_lb_success_count : 6, 1 | -83.33>
|
||||
*busy_lb_avg_pulled : 1.17, 2.00 | 71.43>
|
||||
----------------------------------------- <Category idle> ------------------------------------------
|
||||
idle_lb_count : 568, 620 | 9.15>
|
||||
idle_lb_balanced : 462, 449 | -2.81>
|
||||
idle_lb_failed : 11, 21 | 90.91>
|
||||
idle_lb_imbalance_load : 0, 0 | 0.00>
|
||||
idle_lb_imbalance_util : 0, 0 | 0.00>
|
||||
idle_lb_imbalance_task : 115, 189 | 64.35>
|
||||
idle_lb_imbalance_misfit : 0, 0 | 0.00>
|
||||
idle_lb_gained : 103, 169 | 64.08>
|
||||
idle_lb_hot_gained : 0, 0 | 0.00>
|
||||
idle_lb_nobusyq : 0, 0 | 0.00>
|
||||
idle_lb_nobusyg : 462, 449 | -2.81>
|
||||
*idle_lb_success_count : 95, 150 | 57.89>
|
||||
*idle_lb_avg_pulled : 1.08, 1.13 | 3.92>
|
||||
---------------------------------------- <Category newidle> ----------------------------------------
|
||||
newidle_lb_count : 16961, 3155 | -81.40>
|
||||
newidle_lb_balanced : 15646, 2556 | -83.66>
|
||||
newidle_lb_failed : 397, 142 | -64.23>
|
||||
newidle_lb_imbalance_load : 0, 0 | 0.00>
|
||||
newidle_lb_imbalance_util : 0, 0 | 0.00>
|
||||
newidle_lb_imbalance_task : 1376, 655 | -52.40>
|
||||
newidle_lb_imbalance_misfit : 0, 0 | 0.00>
|
||||
newidle_lb_gained : 917, 457 | -50.16>
|
||||
newidle_lb_hot_gained : 0, 0 | 0.00>
|
||||
newidle_lb_nobusyq : 3, 1 | -66.67>
|
||||
newidle_lb_nobusyg : 14480, 2103 | -85.48>
|
||||
*newidle_lb_success_count : 918, 457 | -50.22>
|
||||
*newidle_lb_avg_pulled : 1.00, 1.00 | 0.11>
|
||||
--------------------------------- <Category active_load_balance()> ---------------------------------
|
||||
alb_count : 0, 1 | 0.00>
|
||||
alb_failed : 0, 0 | 0.00>
|
||||
alb_pushed : 0, 1 | 0.00>
|
||||
--------------------------------- <Category sched_balance_exec()> ----------------------------------
|
||||
sbe_count : 0, 0 | 0.00>
|
||||
sbe_balanced : 0, 0 | 0.00>
|
||||
sbe_pushed : 0, 0 | 0.00>
|
||||
--------------------------------- <Category sched_balance_fork()> ----------------------------------
|
||||
sbf_count : 0, 0 | 0.00>
|
||||
sbf_balanced : 0, 0 | 0.00>
|
||||
sbf_pushed : 0, 0 | 0.00>
|
||||
------------------------------------------ <Wakeup Info> -------------------------------------------
|
||||
ttwu_wake_remote : 2031, 2914 | 43.48>
|
||||
ttwu_move_affine : 73, 124 | 69.86>
|
||||
ttwu_move_balance : 0, 0 | 0.00>
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
Applicable to {record|latency|map|replay|script}
|
||||
|
||||
-i::
|
||||
--input=<file>::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
|
|
|||
|
|
@ -98,8 +98,10 @@ OPTIONS
|
|||
|
||||
-g::
|
||||
--gen-script=::
|
||||
Generate perf-script.[ext] starter script for given language,
|
||||
using current perf.data.
|
||||
Generate a starter script. If a language is given then the
|
||||
script is named perf-script.[ext] according to the
|
||||
language. If a file path is given then python is used for
|
||||
files ending '.py' and perl used for files ending '.pl'.
|
||||
|
||||
--dlfilter=<file>::
|
||||
Filter sample events using the given shared object file.
|
||||
|
|
|
|||
|
|
@ -382,6 +382,11 @@ color the metric's computed value.
|
|||
Don't print output, warnings or messages. This is useful with perf stat
|
||||
record below to only write data to the perf.data file.
|
||||
|
||||
--no-affinity::
|
||||
Don't change scheduler CPU affinities when iterating over
|
||||
CPUs. Disables an optimization aimed at minimizing interprocessor
|
||||
interrupts.
|
||||
|
||||
STAT RECORD
|
||||
-----------
|
||||
Stores stat data into perf data file.
|
||||
|
|
|
|||
|
|
@ -447,6 +447,23 @@ struct {
|
|||
} [nr_pmu];
|
||||
};
|
||||
|
||||
HEADER_CPU_DOMAIN_INFO = 32,
|
||||
|
||||
List of cpu-domain relation info. The format of the data is as below.
|
||||
|
||||
struct domain_info {
|
||||
int domain;
|
||||
char dname[];
|
||||
char cpumask[];
|
||||
char cpulist[];
|
||||
};
|
||||
|
||||
struct cpu_domain_info {
|
||||
int cpu;
|
||||
int nr_domains;
|
||||
struct domain_info domains[];
|
||||
};
|
||||
|
||||
other bits are reserved and should ignored for now
|
||||
HEADER_FEAT_BITS = 256,
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ include $(srctree)/tools/scripts/Makefile.arch
|
|||
$(call detected_var,SRCARCH)
|
||||
|
||||
CFLAGS += -I$(OUTPUT)arch/$(SRCARCH)/include/generated
|
||||
CFLAGS += -I$(OUTPUT)libperf/arch/$(SRCARCH)/include/generated/uapi
|
||||
|
||||
# Additional ARCH settings for ppc
|
||||
ifeq ($(SRCARCH),powerpc)
|
||||
|
|
@ -118,14 +117,6 @@ ifeq ($(ARCH),mips)
|
|||
endif
|
||||
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
|
||||
# to the check.
|
||||
ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv loongarch))
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
ifneq ($(LIBUNWIND),1)
|
||||
NO_LIBUNWIND := 1
|
||||
endif
|
||||
|
|
@ -379,8 +370,8 @@ ifneq ($(TCMALLOC),)
|
|||
endif
|
||||
|
||||
ifeq ($(FEATURES_DUMP),)
|
||||
# We will display at the end of this Makefile.config, using $(call feature_display_entries)
|
||||
# As we may retry some feature detection here, see the disassembler-four-args case, for instance
|
||||
# We will display at the end of this Makefile.config, using $(call feature_display_entries),
|
||||
# as we may retry some feature detection here.
|
||||
FEATURE_DISPLAY_DEFERRED := 1
|
||||
include $(srctree)/tools/build/Makefile.feature
|
||||
else
|
||||
|
|
@ -456,7 +447,6 @@ endif
|
|||
ifdef NO_LIBELF
|
||||
NO_LIBDW := 1
|
||||
NO_LIBUNWIND := 1
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
NO_LIBBPF := 1
|
||||
NO_JVMTI := 1
|
||||
else
|
||||
|
|
@ -504,10 +494,6 @@ ifeq ($(feature-libaio), 1)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_LIBDW
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
ifeq ($(feature-scandirat), 1)
|
||||
# Ignore having scandirat with memory sanitizer that lacks an interceptor.
|
||||
ifeq ($(filter s% -fsanitize=memory%,$(EXTRA_CFLAGS),),)
|
||||
|
|
@ -757,7 +743,7 @@ dwarf-post-unwind-text := BUG
|
|||
|
||||
# setup DWARF post unwinder
|
||||
ifdef NO_LIBUNWIND
|
||||
ifdef NO_LIBDW_DWARF_UNWIND
|
||||
ifdef NO_LIBDW
|
||||
$(warning Disabling post unwind, no support found.)
|
||||
dwarf-post-unwind := 0
|
||||
else
|
||||
|
|
@ -767,10 +753,6 @@ ifdef NO_LIBUNWIND
|
|||
else
|
||||
dwarf-post-unwind-text := libunwind
|
||||
$(call detected,CONFIG_LIBUNWIND)
|
||||
# Enable libunwind support by default.
|
||||
ifndef NO_LIBDW_DWARF_UNWIND
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(dwarf-post-unwind),1)
|
||||
|
|
@ -931,48 +913,32 @@ ifneq ($(NO_JEVENTS),1)
|
|||
endif
|
||||
|
||||
ifdef BUILD_NONDISTRO
|
||||
# call all detections now so we get correct status in VF output
|
||||
$(call feature_check,libbfd)
|
||||
|
||||
ifeq ($(feature-libbfd), 1)
|
||||
EXTLIBS += -lbfd -lopcodes
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-four-args = -lbfd -lopcodes -ldl
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-init-styled = -lbfd -lopcodes -ldl
|
||||
else
|
||||
# we are on a system that requires -liberty and (maybe) -lz
|
||||
# to link against -lbfd; test each case individually here
|
||||
|
||||
# call all detections now so we get correct
|
||||
# status in VF output
|
||||
$(call feature_check,disassembler-four-args)
|
||||
$(call feature_check,disassembler-init-styled)
|
||||
$(call feature_check,libbfd-threadsafe)
|
||||
$(call feature_check,libbfd-liberty)
|
||||
$(call feature_check,libbfd-liberty-z)
|
||||
|
||||
ifeq ($(feature-libbfd-liberty), 1)
|
||||
ifneq ($(feature-libbfd-threadsafe), 1)
|
||||
$(error binutils 2.42 or later is required for non-distro builds)
|
||||
endif
|
||||
|
||||
# we may be on a system that requires -liberty and (maybe) -lz
|
||||
# to link against -lbfd; test each case individually here
|
||||
ifeq ($(feature-libbfd), 1)
|
||||
EXTLIBS += -lbfd -lopcodes
|
||||
else ifeq ($(feature-libbfd-liberty), 1)
|
||||
EXTLIBS += -lbfd -lopcodes -liberty
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -ldl
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -ldl
|
||||
else
|
||||
ifeq ($(feature-libbfd-liberty-z), 1)
|
||||
else ifeq ($(feature-libbfd-liberty-z), 1)
|
||||
EXTLIBS += -lbfd -lopcodes -liberty -lz
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -lz -ldl
|
||||
FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -lz -ldl
|
||||
endif
|
||||
endif
|
||||
$(call feature_check,disassembler-four-args)
|
||||
$(call feature_check,disassembler-init-styled)
|
||||
endif
|
||||
|
||||
CFLAGS += -DHAVE_LIBBFD_SUPPORT
|
||||
CXXFLAGS += -DHAVE_LIBBFD_SUPPORT
|
||||
$(call detected,CONFIG_LIBBFD)
|
||||
|
||||
$(call feature_check,libbfd-buildid)
|
||||
|
||||
ifeq ($(feature-libbfd-buildid), 1)
|
||||
CFLAGS += -DHAVE_LIBBFD_BUILDID_SUPPORT
|
||||
else
|
||||
$(warning Old version of libbfd/binutils things like PE executable profiling will not be available)
|
||||
endif
|
||||
|
||||
ifeq ($(feature-disassembler-four-args), 1)
|
||||
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
|
||||
endif
|
||||
|
|
@ -1067,10 +1033,6 @@ ifndef NO_LIBNUMA
|
|||
endif
|
||||
endif
|
||||
|
||||
ifdef HAVE_KVM_STAT_SUPPORT
|
||||
CFLAGS += -DHAVE_KVM_STAT_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
ifndef NO_PERF_READ_VDSO32
|
||||
$(call feature_check,compile-32)
|
||||
|
|
@ -1112,8 +1074,12 @@ ifndef NO_CAPSTONE
|
|||
$(call feature_check,libcapstone)
|
||||
ifeq ($(feature-libcapstone), 1)
|
||||
CFLAGS += -DHAVE_LIBCAPSTONE_SUPPORT $(LIBCAPSTONE_CFLAGS)
|
||||
LDFLAGS += $(LICAPSTONE_LDFLAGS)
|
||||
ifdef LIBCAPSTONE_DLOPEN
|
||||
CFLAGS += -DLIBCAPSTONE_DLOPEN
|
||||
else
|
||||
LDFLAGS += $(LIBCAPSTONE_LDFLAGS)
|
||||
EXTLIBS += -lcapstone
|
||||
endif
|
||||
$(call detected,CONFIG_LIBCAPSTONE)
|
||||
else
|
||||
msg := $(warning No libcapstone found, disables disasm engine support for 'perf script', please install libcapstone-dev/capstone-devel);
|
||||
|
|
@ -1187,6 +1153,18 @@ ifneq ($(NO_LIBTRACEEVENT),1)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_RUST
|
||||
$(call feature_check,rust)
|
||||
ifneq ($(feature-rust), 1)
|
||||
$(warning Rust is not found. Test workloads with rust are disabled.)
|
||||
NO_RUST := 1
|
||||
else
|
||||
NO_RUST := 0
|
||||
CFLAGS += -DHAVE_RUST_SUPPORT
|
||||
$(call detected,CONFIG_RUST_SUPPORT)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Among the variables below, these:
|
||||
# perfexecdir
|
||||
# libbpf_include_dir
|
||||
|
|
@ -1332,6 +1310,6 @@ endif
|
|||
|
||||
# re-generate FEATURE-DUMP as we may have called feature_check, found out
|
||||
# extra libraries to add to LDFLAGS of some other test and then redo those
|
||||
# tests, see the block about libbfd, disassembler-four-args, for instance.
|
||||
# tests.
|
||||
$(shell rm -f $(FEATURE_DUMP_FILENAME))
|
||||
$(foreach feat,$(FEATURE_TESTS),$(shell echo "$(call feature_assign,$(feat))" >> $(FEATURE_DUMP_FILENAME)))
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ include ../scripts/utilities.mak
|
|||
#
|
||||
# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
|
||||
#
|
||||
# Define EXTRA_BPF_FLAGS="--sysroot=<path>" or other custom include paths for
|
||||
# cross-compiling BPF skeletons
|
||||
#
|
||||
# Define EXCLUDE_EXTLIBS=-lmylib to exclude libmylib from the auto-generated
|
||||
# EXTLIBS.
|
||||
#
|
||||
|
|
@ -86,8 +89,6 @@ include ../scripts/utilities.mak
|
|||
#
|
||||
# Define NO_LIBBPF if you do not want BPF support
|
||||
#
|
||||
# Define NO_LIBCAP if you do not want process capabilities considered by perf
|
||||
#
|
||||
# Define NO_SDT if you do not want to define SDT event in perf tools,
|
||||
# note that it doesn't disable SDT scanning support.
|
||||
#
|
||||
|
|
@ -251,11 +252,12 @@ else
|
|||
endif
|
||||
|
||||
# shellcheck is using in tools/perf/tests/Build with option -a/--check-sourced (
|
||||
# introduced in v0.4.7) and -S/--severity (introduced in v0.6.0). So make the
|
||||
# minimal shellcheck version as v0.6.0.
|
||||
# introduced in v0.4.7) and -S/--severity (introduced in v0.6.0) as well as
|
||||
# dynamic source inclusions (properly handled since v0.7.2).
|
||||
# So make the minimal shellcheck version as v0.7.2.
|
||||
ifneq ($(SHELLCHECK),)
|
||||
ifeq ($(shell expr $(shell $(SHELLCHECK) --version | grep version: | \
|
||||
sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \< 060), 1)
|
||||
sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \< 072), 1)
|
||||
SHELLCHECK :=
|
||||
else
|
||||
SHELLCHECK := $(SHELLCHECK) -s bash -a -S warning
|
||||
|
|
@ -272,7 +274,7 @@ ifeq ($(PYLINT),1)
|
|||
PYLINT := $(shell which pylint 2> /dev/null)
|
||||
endif
|
||||
|
||||
export srctree OUTPUT RM CC CXX LD AR CFLAGS CXXFLAGS V BISON FLEX AWK
|
||||
export srctree OUTPUT RM CC CXX RUSTC LD AR CFLAGS CXXFLAGS V BISON FLEX AWK
|
||||
export HOSTCC HOSTLD HOSTAR HOSTCFLAGS SHELLCHECK MYPY PYLINT
|
||||
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
|
@ -807,11 +809,6 @@ $(GTK_IN): FORCE prepare
|
|||
$(OUTPUT)libperf-gtk.so: $(GTK_IN) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) -o $@ -shared $(LDFLAGS) $(filter %.o,$^) $(GTK_LIBS)
|
||||
|
||||
$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt
|
||||
|
||||
$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
|
||||
$(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
|
||||
|
||||
$(SCRIPTS) : % : %.sh
|
||||
$(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@'
|
||||
|
||||
|
|
@ -849,7 +846,7 @@ endif
|
|||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||
build-dir = $(or $(__build-dir),.)
|
||||
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders \
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE archheaders \
|
||||
arm64-sysreg-defs \
|
||||
$(syscall_array) \
|
||||
$(fs_at_flags_array) \
|
||||
|
|
@ -1053,7 +1050,7 @@ cscope:
|
|||
# However, the environment gets quite big, and some programs have problems
|
||||
# with that.
|
||||
|
||||
check: $(OUTPUT)common-cmds.h
|
||||
check: prepare
|
||||
if sparse; \
|
||||
then \
|
||||
for i in *.c */*.c; \
|
||||
|
|
@ -1250,7 +1247,7 @@ endif
|
|||
$(SKEL_TMP_OUT)/%.bpf.o: $(OUTPUT)PERF-VERSION-FILE util/bpf_skel/perf_version.h | $(SKEL_TMP_OUT)
|
||||
$(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) $(SKEL_OUT)/vmlinux.h
|
||||
$(QUIET_CLANG)$(CLANG) -g -O2 -fno-stack-protector --target=bpf \
|
||||
$(CLANG_OPTIONS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \
|
||||
$(CLANG_OPTIONS) $(EXTRA_BPF_FLAGS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \
|
||||
-include $(OUTPUT)PERF-VERSION-FILE -include util/bpf_skel/perf_version.h \
|
||||
-c $(filter util/bpf_skel/%.bpf.c,$^) -o $@
|
||||
|
||||
|
|
@ -1277,6 +1274,8 @@ ifeq ($(OUTPUT),)
|
|||
pmu-events/metric_test.log \
|
||||
pmu-events/test-empty-pmu-events.c \
|
||||
pmu-events/empty-pmu-events.log
|
||||
$(Q)find pmu-events/arch -name 'extra-metrics.json' -delete -o \
|
||||
-name 'extra-metricgroups.json' -delete
|
||||
else # When an OUTPUT directory is present, clean up the copied pmu-events/arch directory.
|
||||
$(call QUIET_CLEAN, pmu-events) $(RM) -r $(OUTPUT)pmu-events/arch \
|
||||
$(OUTPUT)pmu-events/pmu-events.c \
|
||||
|
|
@ -1296,7 +1295,7 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(
|
|||
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 \
|
||||
perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI).so
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo \
|
||||
$(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE \
|
||||
TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE \
|
||||
$(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \
|
||||
$(OUTPUT)util/intel-pt-decoder/inat-tables.c \
|
||||
$(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/compiler.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/arm/include/uapi/asm/perf_regs.h"
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
perf-util-y += pmu.o auxtrace.o cs-etm.o
|
||||
|
|
|
|||
|
|
@ -68,6 +68,20 @@ static const char * const metadata_ete_ro[] = {
|
|||
|
||||
enum cs_etm_version { CS_NOT_PRESENT, CS_ETMV3, CS_ETMV4, CS_ETE };
|
||||
|
||||
/* ETMv4 CONFIGR register bits */
|
||||
#define TRCCONFIGR_BB BIT(3)
|
||||
#define TRCCONFIGR_CCI BIT(4)
|
||||
#define TRCCONFIGR_CID BIT(6)
|
||||
#define TRCCONFIGR_VMID BIT(7)
|
||||
#define TRCCONFIGR_TS BIT(11)
|
||||
#define TRCCONFIGR_RS BIT(12)
|
||||
#define TRCCONFIGR_VMIDOPT BIT(15)
|
||||
|
||||
/* ETMv3 ETMCR register bits */
|
||||
#define ETMCR_CYC_ACC BIT(12)
|
||||
#define ETMCR_TIMESTAMP_EN BIT(28)
|
||||
#define ETMCR_RETURN_STACK BIT(29)
|
||||
|
||||
static bool cs_etm_is_ete(struct perf_pmu *cs_etm_pmu, struct perf_cpu cpu);
|
||||
static int cs_etm_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val);
|
||||
static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path);
|
||||
|
|
@ -89,13 +103,14 @@ static int cs_etm_validate_context_id(struct perf_pmu *cs_etm_pmu, struct evsel
|
|||
struct perf_cpu cpu)
|
||||
{
|
||||
int err;
|
||||
__u64 val;
|
||||
u64 contextid = evsel->core.attr.config &
|
||||
(perf_pmu__format_bits(cs_etm_pmu, "contextid") |
|
||||
perf_pmu__format_bits(cs_etm_pmu, "contextid1") |
|
||||
perf_pmu__format_bits(cs_etm_pmu, "contextid2"));
|
||||
u64 ctxt, ctxt1, ctxt2;
|
||||
__u64 trcidr2;
|
||||
|
||||
if (!contextid)
|
||||
evsel__get_config_val(evsel, "contextid", &ctxt);
|
||||
evsel__get_config_val(evsel, "contextid1", &ctxt1);
|
||||
evsel__get_config_val(evsel, "contextid2", &ctxt2);
|
||||
|
||||
if (!ctxt && !ctxt1 && !ctxt2)
|
||||
return 0;
|
||||
|
||||
/* Not supported in etmv3 */
|
||||
|
|
@ -106,12 +121,11 @@ static int cs_etm_validate_context_id(struct perf_pmu *cs_etm_pmu, struct evsel
|
|||
}
|
||||
|
||||
/* Get a handle on TRCIDR2 */
|
||||
err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2], &val);
|
||||
err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2], &trcidr2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (contextid &
|
||||
perf_pmu__format_bits(cs_etm_pmu, "contextid1")) {
|
||||
if (ctxt1) {
|
||||
/*
|
||||
* TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID
|
||||
* tracing is supported:
|
||||
|
|
@ -119,15 +133,14 @@ static int cs_etm_validate_context_id(struct perf_pmu *cs_etm_pmu, struct evsel
|
|||
* 0b00100 Maximum of 32-bit Context ID size.
|
||||
* All other values are reserved.
|
||||
*/
|
||||
if (BMVAL(val, 5, 9) != 0x4) {
|
||||
if (BMVAL(trcidr2, 5, 9) != 0x4) {
|
||||
pr_err("%s: CONTEXTIDR_EL1 isn't supported, disable with %s/contextid1=0/\n",
|
||||
CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (contextid &
|
||||
perf_pmu__format_bits(cs_etm_pmu, "contextid2")) {
|
||||
if (ctxt2) {
|
||||
/*
|
||||
* TRCIDR2.VMIDOPT[30:29] != 0 and
|
||||
* TRCIDR2.VMIDSIZE[14:10] == 0b00100 (32bit virtual contextid)
|
||||
|
|
@ -135,7 +148,7 @@ static int cs_etm_validate_context_id(struct perf_pmu *cs_etm_pmu, struct evsel
|
|||
* virtual context id is < 32bit.
|
||||
* Any value of VMIDSIZE >= 4 (i.e, > 32bit) is fine for us.
|
||||
*/
|
||||
if (!BMVAL(val, 29, 30) || BMVAL(val, 10, 14) < 4) {
|
||||
if (!BMVAL(trcidr2, 29, 30) || BMVAL(trcidr2, 10, 14) < 4) {
|
||||
pr_err("%s: CONTEXTIDR_EL2 isn't supported, disable with %s/contextid2=0/\n",
|
||||
CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME);
|
||||
return -EINVAL;
|
||||
|
|
@ -149,10 +162,11 @@ static int cs_etm_validate_timestamp(struct perf_pmu *cs_etm_pmu, struct evsel *
|
|||
struct perf_cpu cpu)
|
||||
{
|
||||
int err;
|
||||
__u64 val;
|
||||
u64 val;
|
||||
__u64 trcidr0;
|
||||
|
||||
if (!(evsel->core.attr.config &
|
||||
perf_pmu__format_bits(cs_etm_pmu, "timestamp")))
|
||||
evsel__get_config_val(evsel, "timestamp", &val);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
if (cs_etm_get_version(cs_etm_pmu, cpu) == CS_ETMV3) {
|
||||
|
|
@ -162,7 +176,7 @@ static int cs_etm_validate_timestamp(struct perf_pmu *cs_etm_pmu, struct evsel *
|
|||
}
|
||||
|
||||
/* Get a handle on TRCIRD0 */
|
||||
err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0], &val);
|
||||
err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0], &trcidr0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -173,10 +187,9 @@ static int cs_etm_validate_timestamp(struct perf_pmu *cs_etm_pmu, struct evsel *
|
|||
* 0b00110 Implementation supports a maximum timestamp of 48bits.
|
||||
* 0b01000 Implementation supports a maximum timestamp of 64bits.
|
||||
*/
|
||||
val &= GENMASK(28, 24);
|
||||
if (!val) {
|
||||
trcidr0 &= GENMASK(28, 24);
|
||||
if (!trcidr0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -259,16 +272,19 @@ static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the sink name format "@sink_name" is used, lookup the sink by name to convert to
|
||||
* "sinkid=sink_hash" format. If the user has already manually provided a hash then
|
||||
* "sinkid" isn't overwritten. If neither are provided then the driver will pick the best
|
||||
* sink.
|
||||
*/
|
||||
static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
||||
struct evsel *evsel)
|
||||
{
|
||||
char msg[BUFSIZ], path[PATH_MAX], *sink;
|
||||
struct evsel_config_term *term;
|
||||
int ret = -EINVAL;
|
||||
u32 hash;
|
||||
|
||||
if (evsel->core.attr.config2 & GENMASK(31, 0))
|
||||
return 0;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(term, &evsel->config_terms, list) {
|
||||
if (term->type != EVSEL__CONFIG_TERM_DRV_CFG)
|
||||
|
|
@ -291,17 +307,26 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
|||
return ret;
|
||||
}
|
||||
|
||||
evsel->core.attr.config2 |= hash;
|
||||
evsel__set_config_if_unset(evsel, "sinkid", hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* No sink was provided on the command line - allow the CoreSight
|
||||
* system to look for a default
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct evsel *cs_etm_get_evsel(struct evlist *evlist,
|
||||
struct perf_pmu *cs_etm_pmu)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.attr.type == cs_etm_pmu->type)
|
||||
return evsel;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int cs_etm_recording_options(struct auxtrace_record *itr,
|
||||
struct evlist *evlist,
|
||||
struct record_opts *opts)
|
||||
|
|
@ -441,10 +466,8 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
|
|||
* when a context switch happened.
|
||||
*/
|
||||
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
|
||||
evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel,
|
||||
"timestamp", 1);
|
||||
evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel,
|
||||
"contextid", 1);
|
||||
evsel__set_config_if_unset(cs_etm_evsel, "timestamp", 1);
|
||||
evsel__set_config_if_unset(cs_etm_evsel, "contextid", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -453,8 +476,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
|
|||
* timestamp tracing.
|
||||
*/
|
||||
if (opts->sample_time_set)
|
||||
evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel,
|
||||
"timestamp", 1);
|
||||
evsel__set_config_if_unset(cs_etm_evsel, "timestamp", 1);
|
||||
|
||||
/* Add dummy event to keep tracking */
|
||||
err = parse_event(evlist, "dummy:u");
|
||||
|
|
@ -474,64 +496,64 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static u64 cs_etm_get_config(struct auxtrace_record *itr)
|
||||
static u64 cs_etm_synth_etmcr(struct auxtrace_record *itr)
|
||||
{
|
||||
u64 config = 0;
|
||||
struct cs_etm_recording *ptr =
|
||||
container_of(itr, struct cs_etm_recording, itr);
|
||||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||
struct evlist *evlist = ptr->evlist;
|
||||
struct evsel *evsel;
|
||||
struct evsel *evsel = cs_etm_get_evsel(ptr->evlist, cs_etm_pmu);
|
||||
u64 etmcr = 0;
|
||||
u64 val;
|
||||
|
||||
if (!evsel)
|
||||
return 0;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.attr.type == cs_etm_pmu->type) {
|
||||
/*
|
||||
* Variable perf_event_attr::config is assigned to
|
||||
* ETMv3/PTM. The bit fields have been made to match
|
||||
* the ETMv3.5 ETRMCR register specification. See the
|
||||
* PMU_FORMAT_ATTR() declarations in
|
||||
* drivers/hwtracing/coresight/coresight-perf.c for
|
||||
* details.
|
||||
* Synthesize what the kernel programmed into ETMCR based on
|
||||
* what options the event was opened with. This doesn't have to be
|
||||
* complete or 100% accurate, not all bits used by OpenCSD anyway.
|
||||
*/
|
||||
config = evsel->core.attr.config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!evsel__get_config_val(evsel, "cycacc", &val) && val)
|
||||
etmcr |= ETMCR_CYC_ACC;
|
||||
if (!evsel__get_config_val(evsel, "timestamp", &val) && val)
|
||||
etmcr |= ETMCR_TIMESTAMP_EN;
|
||||
if (!evsel__get_config_val(evsel, "retstack", &val) && val)
|
||||
etmcr |= ETMCR_RETURN_STACK;
|
||||
|
||||
return config;
|
||||
return etmcr;
|
||||
}
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(N) (1UL << (N))
|
||||
#endif
|
||||
|
||||
static u64 cs_etmv4_get_config(struct auxtrace_record *itr)
|
||||
static u64 cs_etmv4_synth_trcconfigr(struct auxtrace_record *itr)
|
||||
{
|
||||
u64 config = 0;
|
||||
u64 config_opts = 0;
|
||||
u64 trcconfigr = 0;
|
||||
struct cs_etm_recording *ptr =
|
||||
container_of(itr, struct cs_etm_recording, itr);
|
||||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||
struct evsel *evsel = cs_etm_get_evsel(ptr->evlist, cs_etm_pmu);
|
||||
u64 val;
|
||||
|
||||
if (!evsel)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The perf event variable config bits represent both
|
||||
* the command line options and register programming
|
||||
* bits in ETMv3/PTM. For ETMv4 we must remap options
|
||||
* to real bits
|
||||
* Synthesize what the kernel programmed into TRCCONFIGR based on
|
||||
* what options the event was opened with. This doesn't have to be
|
||||
* complete or 100% accurate, not all bits used by OpenCSD anyway.
|
||||
*/
|
||||
config_opts = cs_etm_get_config(itr);
|
||||
if (config_opts & BIT(ETM_OPT_CYCACC))
|
||||
config |= BIT(ETM4_CFG_BIT_CYCACC);
|
||||
if (config_opts & BIT(ETM_OPT_CTXTID))
|
||||
config |= BIT(ETM4_CFG_BIT_CTXTID);
|
||||
if (config_opts & BIT(ETM_OPT_TS))
|
||||
config |= BIT(ETM4_CFG_BIT_TS);
|
||||
if (config_opts & BIT(ETM_OPT_RETSTK))
|
||||
config |= BIT(ETM4_CFG_BIT_RETSTK);
|
||||
if (config_opts & BIT(ETM_OPT_CTXTID2))
|
||||
config |= BIT(ETM4_CFG_BIT_VMID) |
|
||||
BIT(ETM4_CFG_BIT_VMID_OPT);
|
||||
if (config_opts & BIT(ETM_OPT_BRANCH_BROADCAST))
|
||||
config |= BIT(ETM4_CFG_BIT_BB);
|
||||
if (!evsel__get_config_val(evsel, "cycacc", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_CCI;
|
||||
if (!evsel__get_config_val(evsel, "contextid1", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_CID;
|
||||
if (!evsel__get_config_val(evsel, "timestamp", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_TS;
|
||||
if (!evsel__get_config_val(evsel, "retstack", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_RS;
|
||||
if (!evsel__get_config_val(evsel, "contextid2", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_VMID | TRCCONFIGR_VMIDOPT;
|
||||
if (!evsel__get_config_val(evsel, "branch_broadcast", &val) && val)
|
||||
trcconfigr |= TRCCONFIGR_BB;
|
||||
|
||||
return config;
|
||||
return trcconfigr;
|
||||
}
|
||||
|
||||
static size_t
|
||||
|
|
@ -653,7 +675,7 @@ static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr,
|
|||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||
|
||||
/* Get trace configuration register */
|
||||
data[CS_ETMV4_TRCCONFIGR] = cs_etmv4_get_config(itr);
|
||||
data[CS_ETMV4_TRCCONFIGR] = cs_etmv4_synth_trcconfigr(itr);
|
||||
/* traceID set to legacy version, in case new perf running on older system */
|
||||
data[CS_ETMV4_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
|
||||
|
|
@ -685,7 +707,7 @@ static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, st
|
|||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||
|
||||
/* Get trace configuration register */
|
||||
data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr);
|
||||
data[CS_ETE_TRCCONFIGR] = cs_etmv4_synth_trcconfigr(itr);
|
||||
/* traceID set to legacy version, in case new perf running on older system */
|
||||
data[CS_ETE_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
|
||||
|
|
@ -741,7 +763,7 @@ static void cs_etm_get_metadata(struct perf_cpu cpu, u32 *offset,
|
|||
case CS_ETMV3:
|
||||
magic = __perf_cs_etmv3_magic;
|
||||
/* Get configuration register */
|
||||
info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
|
||||
info->priv[*offset + CS_ETM_ETMCR] = cs_etm_synth_etmcr(itr);
|
||||
/* traceID set to legacy value in case new perf running on old system */
|
||||
info->priv[*offset + CS_ETM_ETMTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
/* Get read-only information from sysFS */
|
||||
|
|
@ -832,12 +854,11 @@ static int cs_etm_snapshot_start(struct auxtrace_record *itr)
|
|||
{
|
||||
struct cs_etm_recording *ptr =
|
||||
container_of(itr, struct cs_etm_recording, itr);
|
||||
struct evsel *evsel;
|
||||
struct evsel *evsel = cs_etm_get_evsel(ptr->evlist, ptr->cs_etm_pmu);
|
||||
|
||||
evlist__for_each_entry(ptr->evlist, evsel) {
|
||||
if (evsel->core.attr.type == ptr->cs_etm_pmu->type)
|
||||
if (evsel)
|
||||
return evsel__disable(evsel);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/sample.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_ARM_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = REG(R0);
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(FP);
|
||||
dwarf_regs[12] = REG(IP);
|
||||
dwarf_regs[13] = REG(SP);
|
||||
dwarf_regs[14] = REG(LR);
|
||||
dwarf_regs[15] = REG(PC);
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX,
|
||||
dwarf_regs);
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#define perf_event_arm_regs perf_event_arm64_regs
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/arm64/include/uapi/asm/perf_regs.h"
|
||||
#undef perf_event_arm_regs
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-y += ../../arm/util/auxtrace.o
|
||||
perf-util-y += ../../arm/util/cs-etm.o
|
||||
|
|
@ -9,6 +7,5 @@ perf-util-y += header.o
|
|||
perf-util-y += hisi-ptt.o
|
||||
perf-util-y += machine.o
|
||||
perf-util-y += mem-events.o
|
||||
perf-util-y += perf_regs.o
|
||||
perf-util-y += pmu.o
|
||||
perf-util-y += tsc.o
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ static __u64 arm_spe_pmu__sample_period(const struct perf_pmu *arm_spe_pmu)
|
|||
|
||||
static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus)
|
||||
{
|
||||
u64 bit;
|
||||
u64 pa_enable_bit;
|
||||
|
||||
evsel->core.attr.freq = 0;
|
||||
evsel->core.attr.sample_period = arm_spe_pmu__sample_period(evsel->pmu);
|
||||
|
|
@ -274,7 +274,7 @@ static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus)
|
|||
*/
|
||||
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
|
||||
evsel__set_sample_bit(evsel, CPU);
|
||||
evsel__set_config_if_unset(evsel->pmu, evsel, "ts_enable", 1);
|
||||
evsel__set_config_if_unset(evsel, "ts_enable", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -288,8 +288,9 @@ static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus)
|
|||
* inform that the resulting output's SPE samples contain physical addresses
|
||||
* where applicable.
|
||||
*/
|
||||
bit = perf_pmu__format_bits(evsel->pmu, "pa_enable");
|
||||
if (evsel->core.attr.config & bit)
|
||||
|
||||
if (!evsel__get_config_val(evsel, "pa_enable", &pa_enable_bit))
|
||||
if (pa_enable_bit)
|
||||
evsel__set_sample_bit(evsel, PHYS_ADDR);
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +398,7 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
|
|||
struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
|
||||
bool discard = false;
|
||||
int err;
|
||||
u64 discard_bit;
|
||||
|
||||
sper->evlist = evlist;
|
||||
|
||||
|
|
@ -425,9 +427,8 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
|
|||
evlist__for_each_entry_safe(evlist, tmp, evsel) {
|
||||
if (evsel__is_aux_event(evsel)) {
|
||||
arm_spe_setup_evsel(evsel, cpus);
|
||||
if (evsel->core.attr.config &
|
||||
perf_pmu__format_bits(evsel->pmu, "discard"))
|
||||
discard = true;
|
||||
if (!evsel__get_config_val(evsel, "discard", &discard_bit))
|
||||
discard = !!discard_bit;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include "symbol.h"
|
||||
#include "callchain.h"
|
||||
#include "callchain.h" // prototype of arch__add_leaf_frame_record_opts
|
||||
#include "perf_regs.h"
|
||||
#include "record.h"
|
||||
#include "util/perf_regs.h"
|
||||
|
||||
#define SMPL_REG_MASK(b) (1ULL << (b))
|
||||
|
||||
void arch__add_leaf_frame_record_opts(struct record_opts *opts)
|
||||
{
|
||||
const struct sample_reg *sample_reg_masks = arch__sample_reg_masks();
|
||||
|
||||
opts->sample_user_regs |= sample_reg_masks[PERF_REG_ARM64_LR].mask;
|
||||
opts->sample_user_regs |= SMPL_REG_MASK(PERF_REG_ARM64_LR);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#define E(t, n, s, l, a) { .tag = t, .name = n, .event_name = s, .ldlat = l, .aux_event = a }
|
||||
|
||||
struct perf_mem_event perf_mem_events_arm[PERF_MEM_EVENTS__MAX] = {
|
||||
E("spe-load", "%s/ts_enable=1,pa_enable=1,load_filter=1,store_filter=0,min_latency=%u/", NULL, true, 0),
|
||||
E("spe-store", "%s/ts_enable=1,pa_enable=1,load_filter=0,store_filter=1/", NULL, false, 0),
|
||||
E("spe-load", "%s/ts_enable=1,pa_enable=1,load_filter=1,min_latency=%u/", NULL, true, 0),
|
||||
E("spe-store", "%s/ts_enable=1,pa_enable=1,store_filter=1/", NULL, false, 0),
|
||||
E("spe-ldst", "%s/ts_enable=1,pa_enable=1,load_filter=1,store_filter=1,min_latency=%u/", NULL, true, 0),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,182 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <string.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/zalloc.h>
|
||||
|
||||
#include "perf_regs.h"
|
||||
#include "../../../perf-sys.h"
|
||||
#include "../../../util/debug.h"
|
||||
#include "../../../util/event.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
|
||||
#ifndef HWCAP_SVE
|
||||
#define HWCAP_SVE (1 << 22)
|
||||
#endif
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG(x0, PERF_REG_ARM64_X0),
|
||||
SMPL_REG(x1, PERF_REG_ARM64_X1),
|
||||
SMPL_REG(x2, PERF_REG_ARM64_X2),
|
||||
SMPL_REG(x3, PERF_REG_ARM64_X3),
|
||||
SMPL_REG(x4, PERF_REG_ARM64_X4),
|
||||
SMPL_REG(x5, PERF_REG_ARM64_X5),
|
||||
SMPL_REG(x6, PERF_REG_ARM64_X6),
|
||||
SMPL_REG(x7, PERF_REG_ARM64_X7),
|
||||
SMPL_REG(x8, PERF_REG_ARM64_X8),
|
||||
SMPL_REG(x9, PERF_REG_ARM64_X9),
|
||||
SMPL_REG(x10, PERF_REG_ARM64_X10),
|
||||
SMPL_REG(x11, PERF_REG_ARM64_X11),
|
||||
SMPL_REG(x12, PERF_REG_ARM64_X12),
|
||||
SMPL_REG(x13, PERF_REG_ARM64_X13),
|
||||
SMPL_REG(x14, PERF_REG_ARM64_X14),
|
||||
SMPL_REG(x15, PERF_REG_ARM64_X15),
|
||||
SMPL_REG(x16, PERF_REG_ARM64_X16),
|
||||
SMPL_REG(x17, PERF_REG_ARM64_X17),
|
||||
SMPL_REG(x18, PERF_REG_ARM64_X18),
|
||||
SMPL_REG(x19, PERF_REG_ARM64_X19),
|
||||
SMPL_REG(x20, PERF_REG_ARM64_X20),
|
||||
SMPL_REG(x21, PERF_REG_ARM64_X21),
|
||||
SMPL_REG(x22, PERF_REG_ARM64_X22),
|
||||
SMPL_REG(x23, PERF_REG_ARM64_X23),
|
||||
SMPL_REG(x24, PERF_REG_ARM64_X24),
|
||||
SMPL_REG(x25, PERF_REG_ARM64_X25),
|
||||
SMPL_REG(x26, PERF_REG_ARM64_X26),
|
||||
SMPL_REG(x27, PERF_REG_ARM64_X27),
|
||||
SMPL_REG(x28, PERF_REG_ARM64_X28),
|
||||
SMPL_REG(x29, PERF_REG_ARM64_X29),
|
||||
SMPL_REG(lr, PERF_REG_ARM64_LR),
|
||||
SMPL_REG(sp, PERF_REG_ARM64_SP),
|
||||
SMPL_REG(pc, PERF_REG_ARM64_PC),
|
||||
SMPL_REG(vg, PERF_REG_ARM64_VG),
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
/* %xNUM */
|
||||
#define SDT_OP_REGEX1 "^(x[1-2]?[0-9]|3[0-1])$"
|
||||
|
||||
/* [sp], [sp, NUM] */
|
||||
#define SDT_OP_REGEX2 "^\\[sp(, )?([0-9]+)?\\]$"
|
||||
|
||||
static regex_t sdt_op_regex1, sdt_op_regex2;
|
||||
|
||||
static int sdt_init_op_regex(void)
|
||||
{
|
||||
static int initialized;
|
||||
int ret = 0;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
|
||||
if (ret)
|
||||
goto free_regex1;
|
||||
|
||||
initialized = 1;
|
||||
return 0;
|
||||
|
||||
free_regex1:
|
||||
regfree(&sdt_op_regex1);
|
||||
error:
|
||||
pr_debug4("Regex compilation error.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* SDT marker arguments on Arm64 uses %xREG or [sp, NUM], currently
|
||||
* support these two formats.
|
||||
*/
|
||||
int arch_sdt_arg_parse_op(char *old_op, char **new_op)
|
||||
{
|
||||
int ret, new_len;
|
||||
regmatch_t rm[5];
|
||||
|
||||
ret = sdt_init_op_regex();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
|
||||
/* Extract xNUM */
|
||||
new_len = 2; /* % NULL */
|
||||
new_len += (int)(rm[1].rm_eo - rm[1].rm_so);
|
||||
|
||||
*new_op = zalloc(new_len);
|
||||
if (!*new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(*new_op, new_len, "%%%.*s",
|
||||
(int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so);
|
||||
} else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
|
||||
/* [sp], [sp, NUM] or [sp,NUM] */
|
||||
new_len = 7; /* + ( % s p ) NULL */
|
||||
|
||||
/* If the argument is [sp], need to fill offset '0' */
|
||||
if (rm[2].rm_so == -1)
|
||||
new_len += 1;
|
||||
else
|
||||
new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
|
||||
|
||||
*new_op = zalloc(new_len);
|
||||
if (!*new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
if (rm[2].rm_so == -1)
|
||||
scnprintf(*new_op, new_len, "+0(%%sp)");
|
||||
else
|
||||
scnprintf(*new_op, new_len, "+%.*s(%%sp)",
|
||||
(int)(rm[2].rm_eo - rm[2].rm_so),
|
||||
old_op + rm[2].rm_so);
|
||||
} else {
|
||||
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
|
||||
return SDT_ARG_SKIP;
|
||||
}
|
||||
|
||||
return SDT_ARG_VALID;
|
||||
}
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.sample_type = PERF_SAMPLE_REGS_USER,
|
||||
.disabled = 1,
|
||||
.exclude_kernel = 1,
|
||||
.sample_period = 1,
|
||||
.sample_regs_user = PERF_REGS_MASK
|
||||
};
|
||||
int fd;
|
||||
|
||||
if (getauxval(AT_HWCAP) & HWCAP_SVE)
|
||||
attr.sample_regs_user |= SMPL_REG_MASK(PERF_REG_ARM64_VG);
|
||||
|
||||
/*
|
||||
* Check if the pmu supports perf extended regs, before
|
||||
* returning the register mask to sample.
|
||||
*/
|
||||
if (attr.sample_regs_user != PERF_REGS_MASK) {
|
||||
event_attr_init(&attr);
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
return attr.sample_regs_user;
|
||||
}
|
||||
}
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/sample.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[PERF_REG_ARM64_MAX], dwarf_pc;
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_ARM64_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = REG(X0);
|
||||
dwarf_regs[1] = REG(X1);
|
||||
dwarf_regs[2] = REG(X2);
|
||||
dwarf_regs[3] = REG(X3);
|
||||
dwarf_regs[4] = REG(X4);
|
||||
dwarf_regs[5] = REG(X5);
|
||||
dwarf_regs[6] = REG(X6);
|
||||
dwarf_regs[7] = REG(X7);
|
||||
dwarf_regs[8] = REG(X8);
|
||||
dwarf_regs[9] = REG(X9);
|
||||
dwarf_regs[10] = REG(X10);
|
||||
dwarf_regs[11] = REG(X11);
|
||||
dwarf_regs[12] = REG(X12);
|
||||
dwarf_regs[13] = REG(X13);
|
||||
dwarf_regs[14] = REG(X14);
|
||||
dwarf_regs[15] = REG(X15);
|
||||
dwarf_regs[16] = REG(X16);
|
||||
dwarf_regs[17] = REG(X17);
|
||||
dwarf_regs[18] = REG(X18);
|
||||
dwarf_regs[19] = REG(X19);
|
||||
dwarf_regs[20] = REG(X20);
|
||||
dwarf_regs[21] = REG(X21);
|
||||
dwarf_regs[22] = REG(X22);
|
||||
dwarf_regs[23] = REG(X23);
|
||||
dwarf_regs[24] = REG(X24);
|
||||
dwarf_regs[25] = REG(X25);
|
||||
dwarf_regs[26] = REG(X26);
|
||||
dwarf_regs[27] = REG(X27);
|
||||
dwarf_regs[28] = REG(X28);
|
||||
dwarf_regs[29] = REG(X29);
|
||||
dwarf_regs[30] = REG(LR);
|
||||
dwarf_regs[31] = REG(SP);
|
||||
|
||||
if (!dwfl_thread_state_registers(thread, 0, PERF_REG_ARM64_MAX,
|
||||
dwarf_regs))
|
||||
return false;
|
||||
|
||||
dwarf_pc = REG(PC);
|
||||
dwfl_thread_state_register_pc(thread, dwarf_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
perf-util-y += util/
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/csky/include/uapi/asm/perf_regs.h"
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_CSKY_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_CSKY_MAX
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[PERF_REG_CSKY_MAX];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_CSKY_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
#if defined(__CSKYABIV2__)
|
||||
dwarf_regs[0] = REG(A0);
|
||||
dwarf_regs[1] = REG(A1);
|
||||
dwarf_regs[2] = REG(A2);
|
||||
dwarf_regs[3] = REG(A3);
|
||||
dwarf_regs[4] = REG(REGS0);
|
||||
dwarf_regs[5] = REG(REGS1);
|
||||
dwarf_regs[6] = REG(REGS2);
|
||||
dwarf_regs[7] = REG(REGS3);
|
||||
dwarf_regs[8] = REG(REGS4);
|
||||
dwarf_regs[9] = REG(REGS5);
|
||||
dwarf_regs[10] = REG(REGS6);
|
||||
dwarf_regs[11] = REG(REGS7);
|
||||
dwarf_regs[12] = REG(REGS8);
|
||||
dwarf_regs[13] = REG(REGS9);
|
||||
dwarf_regs[14] = REG(SP);
|
||||
dwarf_regs[15] = REG(LR);
|
||||
dwarf_regs[16] = REG(EXREGS0);
|
||||
dwarf_regs[17] = REG(EXREGS1);
|
||||
dwarf_regs[18] = REG(EXREGS2);
|
||||
dwarf_regs[19] = REG(EXREGS3);
|
||||
dwarf_regs[20] = REG(EXREGS4);
|
||||
dwarf_regs[21] = REG(EXREGS5);
|
||||
dwarf_regs[22] = REG(EXREGS6);
|
||||
dwarf_regs[23] = REG(EXREGS7);
|
||||
dwarf_regs[24] = REG(EXREGS8);
|
||||
dwarf_regs[25] = REG(EXREGS9);
|
||||
dwarf_regs[26] = REG(EXREGS10);
|
||||
dwarf_regs[27] = REG(EXREGS11);
|
||||
dwarf_regs[28] = REG(EXREGS12);
|
||||
dwarf_regs[29] = REG(EXREGS13);
|
||||
dwarf_regs[30] = REG(EXREGS14);
|
||||
dwarf_regs[31] = REG(TLS);
|
||||
dwarf_regs[32] = REG(PC);
|
||||
#else
|
||||
dwarf_regs[0] = REG(SP);
|
||||
dwarf_regs[1] = REG(REGS9);
|
||||
dwarf_regs[2] = REG(A0);
|
||||
dwarf_regs[3] = REG(A1);
|
||||
dwarf_regs[4] = REG(A2);
|
||||
dwarf_regs[5] = REG(A3);
|
||||
dwarf_regs[6] = REG(REGS0);
|
||||
dwarf_regs[7] = REG(REGS1);
|
||||
dwarf_regs[8] = REG(REGS2);
|
||||
dwarf_regs[9] = REG(REGS3);
|
||||
dwarf_regs[10] = REG(REGS4);
|
||||
dwarf_regs[11] = REG(REGS5);
|
||||
dwarf_regs[12] = REG(REGS6);
|
||||
dwarf_regs[13] = REG(REGS7);
|
||||
dwarf_regs[14] = REG(REGS8);
|
||||
dwarf_regs[15] = REG(LR);
|
||||
#endif
|
||||
dwfl_thread_state_register_pc(thread, REG(PC));
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, PERF_REG_CSKY_MAX,
|
||||
dwarf_regs);
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/loongarch/include/uapi/asm/perf_regs.h"
|
||||
|
||||
#define PERF_REGS_MAX PERF_REG_LOONGARCH_MAX
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
perf-util-y += header.o
|
||||
perf-util-y += perf_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,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/sample.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[PERF_REG_LOONGARCH_MAX];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_LOONGARCH_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = 0;
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
dwarf_regs[16] = REG(R16);
|
||||
dwarf_regs[17] = REG(R17);
|
||||
dwarf_regs[18] = REG(R18);
|
||||
dwarf_regs[19] = REG(R19);
|
||||
dwarf_regs[20] = REG(R20);
|
||||
dwarf_regs[21] = REG(R21);
|
||||
dwarf_regs[22] = REG(R22);
|
||||
dwarf_regs[23] = REG(R23);
|
||||
dwarf_regs[24] = REG(R24);
|
||||
dwarf_regs[25] = REG(R25);
|
||||
dwarf_regs[26] = REG(R26);
|
||||
dwarf_regs[27] = REG(R27);
|
||||
dwarf_regs[28] = REG(R28);
|
||||
dwarf_regs[29] = REG(R29);
|
||||
dwarf_regs[30] = REG(R30);
|
||||
dwarf_regs[31] = REG(R31);
|
||||
dwfl_thread_state_register_pc(thread, REG(PC));
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, PERF_REG_LOONGARCH_MAX, dwarf_regs);
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/mips/include/uapi/asm/perf_regs.h"
|
||||
|
||||
#define PERF_REGS_MAX PERF_REG_MIPS_MAX
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
perf-util-y += perf_regs.o
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/powerpc/include/uapi/asm/perf_regs.h"
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
perf-util-y += header.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-y += perf_regs.o
|
||||
perf-util-y += mem-events.o
|
||||
perf-util-y += pmu.o
|
||||
perf-util-y += sym-handling.o
|
||||
|
|
@ -9,5 +7,4 @@ perf-util-y += evsel.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
|
||||
perf-util-y += auxtrace.o
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
#include <linux/zalloc.h>
|
||||
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/debug.h"
|
||||
#include "../../../util/event.h"
|
||||
#include "../../../util/header.h"
|
||||
#include "../../../perf-sys.h"
|
||||
#include "utils_header.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define PVR_POWER9 0x004E
|
||||
#define PVR_POWER10 0x0080
|
||||
#define PVR_POWER11 0x0082
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG(r0, PERF_REG_POWERPC_R0),
|
||||
SMPL_REG(r1, PERF_REG_POWERPC_R1),
|
||||
SMPL_REG(r2, PERF_REG_POWERPC_R2),
|
||||
SMPL_REG(r3, PERF_REG_POWERPC_R3),
|
||||
SMPL_REG(r4, PERF_REG_POWERPC_R4),
|
||||
SMPL_REG(r5, PERF_REG_POWERPC_R5),
|
||||
SMPL_REG(r6, PERF_REG_POWERPC_R6),
|
||||
SMPL_REG(r7, PERF_REG_POWERPC_R7),
|
||||
SMPL_REG(r8, PERF_REG_POWERPC_R8),
|
||||
SMPL_REG(r9, PERF_REG_POWERPC_R9),
|
||||
SMPL_REG(r10, PERF_REG_POWERPC_R10),
|
||||
SMPL_REG(r11, PERF_REG_POWERPC_R11),
|
||||
SMPL_REG(r12, PERF_REG_POWERPC_R12),
|
||||
SMPL_REG(r13, PERF_REG_POWERPC_R13),
|
||||
SMPL_REG(r14, PERF_REG_POWERPC_R14),
|
||||
SMPL_REG(r15, PERF_REG_POWERPC_R15),
|
||||
SMPL_REG(r16, PERF_REG_POWERPC_R16),
|
||||
SMPL_REG(r17, PERF_REG_POWERPC_R17),
|
||||
SMPL_REG(r18, PERF_REG_POWERPC_R18),
|
||||
SMPL_REG(r19, PERF_REG_POWERPC_R19),
|
||||
SMPL_REG(r20, PERF_REG_POWERPC_R20),
|
||||
SMPL_REG(r21, PERF_REG_POWERPC_R21),
|
||||
SMPL_REG(r22, PERF_REG_POWERPC_R22),
|
||||
SMPL_REG(r23, PERF_REG_POWERPC_R23),
|
||||
SMPL_REG(r24, PERF_REG_POWERPC_R24),
|
||||
SMPL_REG(r25, PERF_REG_POWERPC_R25),
|
||||
SMPL_REG(r26, PERF_REG_POWERPC_R26),
|
||||
SMPL_REG(r27, PERF_REG_POWERPC_R27),
|
||||
SMPL_REG(r28, PERF_REG_POWERPC_R28),
|
||||
SMPL_REG(r29, PERF_REG_POWERPC_R29),
|
||||
SMPL_REG(r30, PERF_REG_POWERPC_R30),
|
||||
SMPL_REG(r31, PERF_REG_POWERPC_R31),
|
||||
SMPL_REG(nip, PERF_REG_POWERPC_NIP),
|
||||
SMPL_REG(msr, PERF_REG_POWERPC_MSR),
|
||||
SMPL_REG(orig_r3, PERF_REG_POWERPC_ORIG_R3),
|
||||
SMPL_REG(ctr, PERF_REG_POWERPC_CTR),
|
||||
SMPL_REG(link, PERF_REG_POWERPC_LINK),
|
||||
SMPL_REG(xer, PERF_REG_POWERPC_XER),
|
||||
SMPL_REG(ccr, PERF_REG_POWERPC_CCR),
|
||||
SMPL_REG(softe, PERF_REG_POWERPC_SOFTE),
|
||||
SMPL_REG(trap, PERF_REG_POWERPC_TRAP),
|
||||
SMPL_REG(dar, PERF_REG_POWERPC_DAR),
|
||||
SMPL_REG(dsisr, PERF_REG_POWERPC_DSISR),
|
||||
SMPL_REG(sier, PERF_REG_POWERPC_SIER),
|
||||
SMPL_REG(mmcra, PERF_REG_POWERPC_MMCRA),
|
||||
SMPL_REG(mmcr0, PERF_REG_POWERPC_MMCR0),
|
||||
SMPL_REG(mmcr1, PERF_REG_POWERPC_MMCR1),
|
||||
SMPL_REG(mmcr2, PERF_REG_POWERPC_MMCR2),
|
||||
SMPL_REG(mmcr3, PERF_REG_POWERPC_MMCR3),
|
||||
SMPL_REG(sier2, PERF_REG_POWERPC_SIER2),
|
||||
SMPL_REG(sier3, PERF_REG_POWERPC_SIER3),
|
||||
SMPL_REG(pmc1, PERF_REG_POWERPC_PMC1),
|
||||
SMPL_REG(pmc2, PERF_REG_POWERPC_PMC2),
|
||||
SMPL_REG(pmc3, PERF_REG_POWERPC_PMC3),
|
||||
SMPL_REG(pmc4, PERF_REG_POWERPC_PMC4),
|
||||
SMPL_REG(pmc5, PERF_REG_POWERPC_PMC5),
|
||||
SMPL_REG(pmc6, PERF_REG_POWERPC_PMC6),
|
||||
SMPL_REG(sdar, PERF_REG_POWERPC_SDAR),
|
||||
SMPL_REG(siar, PERF_REG_POWERPC_SIAR),
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
/* REG or %rREG */
|
||||
#define SDT_OP_REGEX1 "^(%r)?([1-2]?[0-9]|3[0-1])$"
|
||||
|
||||
/* -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) */
|
||||
#define SDT_OP_REGEX2 "^(\\-)?([0-9]+)\\((%r)?([1-2]?[0-9]|3[0-1])\\)$"
|
||||
|
||||
static regex_t sdt_op_regex1, sdt_op_regex2;
|
||||
|
||||
static int sdt_init_op_regex(void)
|
||||
{
|
||||
static int initialized;
|
||||
int ret = 0;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
|
||||
if (ret)
|
||||
goto free_regex1;
|
||||
|
||||
initialized = 1;
|
||||
return 0;
|
||||
|
||||
free_regex1:
|
||||
regfree(&sdt_op_regex1);
|
||||
error:
|
||||
pr_debug4("Regex compilation error.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse OP and convert it into uprobe format, which is, +/-NUM(%gprREG).
|
||||
* Possible variants of OP are:
|
||||
* Format Example
|
||||
* -------------------------
|
||||
* NUM(REG) 48(18)
|
||||
* -NUM(REG) -48(18)
|
||||
* NUM(%rREG) 48(%r18)
|
||||
* -NUM(%rREG) -48(%r18)
|
||||
* REG 18
|
||||
* %rREG %r18
|
||||
* iNUM i0
|
||||
* i-NUM i-1
|
||||
*
|
||||
* SDT marker arguments on Powerpc uses %rREG form with -mregnames flag
|
||||
* and REG form with -mno-regnames. Here REG is general purpose register,
|
||||
* which is in 0 to 31 range.
|
||||
*/
|
||||
int arch_sdt_arg_parse_op(char *old_op, char **new_op)
|
||||
{
|
||||
int ret, new_len;
|
||||
regmatch_t rm[5];
|
||||
char prefix;
|
||||
|
||||
/* Constant argument. Uprobe does not support it */
|
||||
if (old_op[0] == 'i') {
|
||||
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
|
||||
return SDT_ARG_SKIP;
|
||||
}
|
||||
|
||||
ret = sdt_init_op_regex();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
|
||||
/* REG or %rREG --> %gprREG */
|
||||
|
||||
new_len = 5; /* % g p r NULL */
|
||||
new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
|
||||
|
||||
*new_op = zalloc(new_len);
|
||||
if (!*new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(*new_op, new_len, "%%gpr%.*s",
|
||||
(int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so);
|
||||
} else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
|
||||
/*
|
||||
* -NUM(REG) or NUM(REG) or -NUM(%rREG) or NUM(%rREG) -->
|
||||
* +/-NUM(%gprREG)
|
||||
*/
|
||||
prefix = (rm[1].rm_so == -1) ? '+' : '-';
|
||||
|
||||
new_len = 8; /* +/- ( % g p r ) NULL */
|
||||
new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
|
||||
new_len += (int)(rm[4].rm_eo - rm[4].rm_so);
|
||||
|
||||
*new_op = zalloc(new_len);
|
||||
if (!*new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(*new_op, new_len, "%c%.*s(%%gpr%.*s)", prefix,
|
||||
(int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
|
||||
(int)(rm[4].rm_eo - rm[4].rm_so), old_op + rm[4].rm_so);
|
||||
} else {
|
||||
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
|
||||
return SDT_ARG_SKIP;
|
||||
}
|
||||
|
||||
return SDT_ARG_VALID;
|
||||
}
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.sample_type = PERF_SAMPLE_REGS_INTR,
|
||||
.precise_ip = 1,
|
||||
.disabled = 1,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
int fd;
|
||||
u32 version;
|
||||
u64 extended_mask = 0, mask = PERF_REGS_MASK;
|
||||
|
||||
/*
|
||||
* Get the PVR value to set the extended
|
||||
* mask specific to platform.
|
||||
*/
|
||||
version = (((mfspr(SPRN_PVR)) >> 16) & 0xFFFF);
|
||||
if (version == PVR_POWER9)
|
||||
extended_mask = PERF_REG_PMU_MASK_300;
|
||||
else if ((version == PVR_POWER10) || (version == PVR_POWER11))
|
||||
extended_mask = PERF_REG_PMU_MASK_31;
|
||||
else
|
||||
return mask;
|
||||
|
||||
attr.sample_regs_intr = extended_mask;
|
||||
attr.sample_period = 1;
|
||||
event_attr_init(&attr);
|
||||
|
||||
/*
|
||||
* check if the pmu supports perf extended regs, before
|
||||
* returning the register mask to sample.
|
||||
*/
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
mask |= extended_mask;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -30,14 +30,6 @@
|
|||
* The libdwfl code in this file is based on code from elfutils
|
||||
* (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
|
||||
*/
|
||||
static char *debuginfo_path;
|
||||
|
||||
static const Dwfl_Callbacks offline_callbacks = {
|
||||
.debuginfo_path = &debuginfo_path,
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
.section_address = dwfl_offline_section_address,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Use the DWARF expression for the Call-frame-address and determine
|
||||
|
|
@ -149,44 +141,22 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
|
|||
* yet used)
|
||||
* -1 in case of errors
|
||||
*/
|
||||
static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc)
|
||||
static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc)
|
||||
{
|
||||
int rc = -1;
|
||||
Dwfl *dwfl;
|
||||
Dwfl_Module *mod;
|
||||
Dwarf_Frame *frame;
|
||||
int ra_regno;
|
||||
Dwarf_Addr start = pc;
|
||||
Dwarf_Addr end = pc;
|
||||
Dwarf_Addr start = mapped_pc;
|
||||
Dwarf_Addr end = mapped_pc;
|
||||
bool signalp;
|
||||
const char *exec_file = dso__long_name(dso);
|
||||
|
||||
dwfl = RC_CHK_ACCESS(dso)->dwfl;
|
||||
|
||||
if (!dwfl) {
|
||||
dwfl = dwfl_begin(&offline_callbacks);
|
||||
if (!dwfl) {
|
||||
pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
|
||||
dwfl = dso__libdw_dwfl(dso);
|
||||
if (!dwfl)
|
||||
return -1;
|
||||
}
|
||||
|
||||
mod = dwfl_report_elf(dwfl, exec_file, exec_file, -1,
|
||||
map_start, false);
|
||||
if (!mod) {
|
||||
pr_debug("dwfl_report_elf() failed %s\n",
|
||||
dwarf_errmsg(-1));
|
||||
/*
|
||||
* We normally cache the DWARF debug info and never
|
||||
* call dwfl_end(). But to prevent fd leak, free in
|
||||
* case of error.
|
||||
*/
|
||||
dwfl_end(dwfl);
|
||||
goto out;
|
||||
}
|
||||
RC_CHK_ACCESS(dso)->dwfl = dwfl;
|
||||
}
|
||||
|
||||
mod = dwfl_addrmodule(dwfl, pc);
|
||||
mod = dwfl_addrmodule(dwfl, mapped_pc);
|
||||
if (!mod) {
|
||||
pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
|
||||
goto out;
|
||||
|
|
@ -196,9 +166,9 @@ static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc)
|
|||
* To work with split debug info files (eg: glibc), check both
|
||||
* .eh_frame and .debug_frame sections of the ELF header.
|
||||
*/
|
||||
frame = get_eh_frame(mod, pc);
|
||||
frame = get_eh_frame(mod, mapped_pc);
|
||||
if (!frame) {
|
||||
frame = get_dwarf_frame(mod, pc);
|
||||
frame = get_dwarf_frame(mod, mapped_pc);
|
||||
if (!frame)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -264,7 +234,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
|
|||
return skip_slot;
|
||||
}
|
||||
|
||||
rc = check_return_addr(dso, map__start(al.map), ip);
|
||||
rc = check_return_addr(dso, map__map_ip(al.map, ip));
|
||||
|
||||
pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
|
||||
dso__long_name(dso), al.sym->name, ip, rc);
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/sample.h"
|
||||
|
||||
/* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils. */
|
||||
static const int special_regs[3][2] = {
|
||||
{ 65, PERF_REG_POWERPC_LINK },
|
||||
{ 101, PERF_REG_POWERPC_XER },
|
||||
{ 109, PERF_REG_POWERPC_CTR },
|
||||
};
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[32], dwarf_nip;
|
||||
size_t i;
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_POWERPC_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = REG(R0);
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
dwarf_regs[16] = REG(R16);
|
||||
dwarf_regs[17] = REG(R17);
|
||||
dwarf_regs[18] = REG(R18);
|
||||
dwarf_regs[19] = REG(R19);
|
||||
dwarf_regs[20] = REG(R20);
|
||||
dwarf_regs[21] = REG(R21);
|
||||
dwarf_regs[22] = REG(R22);
|
||||
dwarf_regs[23] = REG(R23);
|
||||
dwarf_regs[24] = REG(R24);
|
||||
dwarf_regs[25] = REG(R25);
|
||||
dwarf_regs[26] = REG(R26);
|
||||
dwarf_regs[27] = REG(R27);
|
||||
dwarf_regs[28] = REG(R28);
|
||||
dwarf_regs[29] = REG(R29);
|
||||
dwarf_regs[30] = REG(R30);
|
||||
dwarf_regs[31] = REG(R31);
|
||||
if (!dwfl_thread_state_registers(thread, 0, 32, dwarf_regs))
|
||||
return false;
|
||||
|
||||
dwarf_nip = REG(NIP);
|
||||
dwfl_thread_state_register_pc(thread, dwarf_nip);
|
||||
for (i = 0; i < ARRAY_SIZE(special_regs); i++) {
|
||||
Dwarf_Word val = 0;
|
||||
perf_reg_value(&val, user_regs, special_regs[i][1]);
|
||||
if (!dwfl_thread_state_registers(thread,
|
||||
special_regs[i][0], 1,
|
||||
&val))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
|
|
|
|||
|
|
@ -6,14 +6,19 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/riscv/include/uapi/asm/perf_regs.h"
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_RISCV_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_RISCV_MAX
|
||||
|
||||
#if defined(__riscv_xlen)
|
||||
#if __riscv_xlen == 64
|
||||
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
|
||||
#else
|
||||
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
|
||||
#endif
|
||||
#else
|
||||
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_NONE
|
||||
#endif
|
||||
|
||||
#endif /* ARCH_PERF_REGS_H */
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
perf-util-y += perf_regs.o
|
||||
perf-util-y += header.o
|
||||
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/sample.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[32];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_RISCV_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = 0;
|
||||
dwarf_regs[1] = REG(RA);
|
||||
dwarf_regs[2] = REG(SP);
|
||||
dwarf_regs[3] = REG(GP);
|
||||
dwarf_regs[4] = REG(TP);
|
||||
dwarf_regs[5] = REG(T0);
|
||||
dwarf_regs[6] = REG(T1);
|
||||
dwarf_regs[7] = REG(T2);
|
||||
dwarf_regs[8] = REG(S0);
|
||||
dwarf_regs[9] = REG(S1);
|
||||
dwarf_regs[10] = REG(A0);
|
||||
dwarf_regs[11] = REG(A1);
|
||||
dwarf_regs[12] = REG(A2);
|
||||
dwarf_regs[13] = REG(A3);
|
||||
dwarf_regs[14] = REG(A4);
|
||||
dwarf_regs[15] = REG(A5);
|
||||
dwarf_regs[16] = REG(A6);
|
||||
dwarf_regs[17] = REG(A7);
|
||||
dwarf_regs[18] = REG(S2);
|
||||
dwarf_regs[19] = REG(S3);
|
||||
dwarf_regs[20] = REG(S4);
|
||||
dwarf_regs[21] = REG(S5);
|
||||
dwarf_regs[22] = REG(S6);
|
||||
dwarf_regs[23] = REG(S7);
|
||||
dwarf_regs[24] = REG(S8);
|
||||
dwarf_regs[25] = REG(S9);
|
||||
dwarf_regs[26] = REG(S10);
|
||||
dwarf_regs[27] = REG(S11);
|
||||
dwarf_regs[28] = REG(T3);
|
||||
dwarf_regs[29] = REG(T4);
|
||||
dwarf_regs[30] = REG(T5);
|
||||
dwarf_regs[31] = REG(T6);
|
||||
dwfl_thread_state_register_pc(thread, REG(PC));
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, PERF_REG_RISCV_MAX,
|
||||
dwarf_regs);
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
static
|
||||
struct ins_ops *riscv64__associate_ins_ops(struct arch *arch, const char *name)
|
||||
{
|
||||
struct ins_ops *ops = NULL;
|
||||
|
||||
if (!strncmp(name, "jal", 3) ||
|
||||
!strncmp(name, "jr", 2) ||
|
||||
!strncmp(name, "call", 4))
|
||||
ops = &call_ops;
|
||||
else if (!strncmp(name, "ret", 3))
|
||||
ops = &ret_ops;
|
||||
else if (name[0] == 'j' || name[0] == 'b')
|
||||
ops = &jump_ops;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
arch__associate_ins_ops(arch, name, ops);
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
static
|
||||
int riscv64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
{
|
||||
if (!arch->initialized) {
|
||||
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,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/s390/include/uapi/asm/perf_regs.h"
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
perf-util-y += header.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-y += perf_regs.o
|
||||
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
perf-util-y += machine.o
|
||||
perf-util-y += pmu.o
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
#include "../../util/sample.h"
|
||||
#include "dwarf-regs-table.h"
|
||||
#include "perf_regs.h"
|
||||
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[ARRAY_SIZE(s390_dwarf_regs)];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_S390_##r); \
|
||||
val; \
|
||||
})
|
||||
/*
|
||||
* For DWARF register mapping details,
|
||||
* see also perf/arch/s390/include/dwarf-regs-table.h
|
||||
*/
|
||||
dwarf_regs[0] = REG(R0);
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
|
||||
dwarf_regs[16] = REG(FP0);
|
||||
dwarf_regs[17] = REG(FP2);
|
||||
dwarf_regs[18] = REG(FP4);
|
||||
dwarf_regs[19] = REG(FP6);
|
||||
dwarf_regs[20] = REG(FP1);
|
||||
dwarf_regs[21] = REG(FP3);
|
||||
dwarf_regs[22] = REG(FP5);
|
||||
dwarf_regs[23] = REG(FP7);
|
||||
dwarf_regs[24] = REG(FP8);
|
||||
dwarf_regs[25] = REG(FP10);
|
||||
dwarf_regs[26] = REG(FP12);
|
||||
dwarf_regs[27] = REG(FP14);
|
||||
dwarf_regs[28] = REG(FP9);
|
||||
dwarf_regs[29] = REG(FP11);
|
||||
dwarf_regs[30] = REG(FP13);
|
||||
dwarf_regs[31] = REG(FP15);
|
||||
|
||||
dwarf_regs[64] = REG(MASK);
|
||||
dwarf_regs[65] = REG(PC);
|
||||
|
||||
dwfl_thread_state_register_pc(thread, dwarf_regs[65]);
|
||||
return dwfl_thread_state_registers(thread, 0, 32, dwarf_regs);
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include "../../../../arch/x86/include/uapi/asm/perf_regs.h"
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,26 +80,24 @@ static int bp_modify1(void)
|
|||
*/
|
||||
if (ptrace(PTRACE_POKEUSER, child,
|
||||
offsetof(struct user, u_debugreg[0]), bp_2)) {
|
||||
pr_debug("failed to set breakpoint, 1st time: %s\n",
|
||||
strerror(errno));
|
||||
pr_debug("failed to set breakpoint, 1st time: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_POKEUSER, child,
|
||||
offsetof(struct user, u_debugreg[0]), bp_1)) {
|
||||
pr_debug("failed to set breakpoint, 2nd time: %s\n",
|
||||
strerror(errno));
|
||||
pr_debug("failed to set breakpoint, 2nd time: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_POKEUSER, child,
|
||||
offsetof(struct user, u_debugreg[7]), dr7)) {
|
||||
pr_debug("failed to set dr7: %s\n", strerror(errno));
|
||||
pr_debug("failed to set dr7: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
|
||||
pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
|
||||
pr_debug("failed to PTRACE_CONT: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -112,19 +110,17 @@ static int bp_modify1(void)
|
|||
rip = ptrace(PTRACE_PEEKUSER, child,
|
||||
offsetof(struct user_regs_struct, rip), NULL);
|
||||
if (rip == (unsigned long) -1) {
|
||||
pr_debug("failed to PTRACE_PEEKUSER: %s\n",
|
||||
strerror(errno));
|
||||
pr_debug("failed to PTRACE_PEEKUSER: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
|
||||
|
||||
out:
|
||||
if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
|
||||
pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
|
||||
pr_debug("failed to PTRACE_DETACH: %m\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
|
|
@ -157,14 +153,13 @@ static int bp_modify2(void)
|
|||
*/
|
||||
if (ptrace(PTRACE_POKEUSER, child,
|
||||
offsetof(struct user, u_debugreg[0]), bp_1)) {
|
||||
pr_debug("failed to set breakpoint: %s\n",
|
||||
strerror(errno));
|
||||
pr_debug("failed to set breakpoint: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_POKEUSER, child,
|
||||
offsetof(struct user, u_debugreg[7]), dr7)) {
|
||||
pr_debug("failed to set dr7: %s\n", strerror(errno));
|
||||
pr_debug("failed to set dr7: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +170,7 @@ static int bp_modify2(void)
|
|||
}
|
||||
|
||||
if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
|
||||
pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
|
||||
pr_debug("failed to PTRACE_CONT: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -188,8 +183,7 @@ static int bp_modify2(void)
|
|||
rip = ptrace(PTRACE_PEEKUSER, child,
|
||||
offsetof(struct user_regs_struct, rip), NULL);
|
||||
if (rip == (unsigned long) -1) {
|
||||
pr_debug("failed to PTRACE_PEEKUSER: %s\n",
|
||||
strerror(errno));
|
||||
pr_debug("failed to PTRACE_PEEKUSER: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +191,7 @@ static int bp_modify2(void)
|
|||
|
||||
out:
|
||||
if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
|
||||
pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
|
||||
pr_debug("failed to PTRACE_DETACH: %m\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
perf-util-y += header.o
|
||||
perf-util-y += tsc.o
|
||||
perf-util-y += pmu.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
|
||||
perf-util-y += perf_regs.o
|
||||
perf-util-y += topdown.o
|
||||
perf-util-y += machine.o
|
||||
perf-util-y += event.o
|
||||
|
|
@ -12,9 +10,7 @@ perf-util-y += evsel.o
|
|||
perf-util-y += iostat.o
|
||||
|
||||
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
perf-util-y += auxtrace.o
|
||||
perf-util-y += archinsn.o
|
||||
perf-util-y += intel-pt.o
|
||||
perf-util-y += intel-bts.o
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "archinsn.h"
|
||||
#include "event.h"
|
||||
#include "machine.h"
|
||||
#include "thread.h"
|
||||
#include "symbol.h"
|
||||
#include "../../../../arch/x86/include/asm/insn.h"
|
||||
|
||||
void arch_fetch_insn(struct perf_sample *sample,
|
||||
struct thread *thread,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct insn insn;
|
||||
int len, ret;
|
||||
bool is64bit = false;
|
||||
|
||||
if (!sample->ip)
|
||||
return;
|
||||
len = thread__memcpy(thread, machine, sample->insn, sample->ip, sizeof(sample->insn), &is64bit);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
ret = insn_decode(&insn, sample->insn, len,
|
||||
is64bit ? INSN_MODE_64 : INSN_MODE_32);
|
||||
if (ret >= 0 && insn.length <= len)
|
||||
sample->insn_len = insn.length;
|
||||
}
|
||||
|
|
@ -664,8 +664,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
|
|||
return 0;
|
||||
|
||||
if (opts->auxtrace_sample_mode)
|
||||
evsel__set_config_if_unset(intel_pt_pmu, intel_pt_evsel,
|
||||
"psb_period", 0);
|
||||
evsel__set_config_if_unset(intel_pt_evsel, "psb_period", 0);
|
||||
|
||||
err = intel_pt_validate_config(intel_pt_pmu, intel_pt_evsel);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -1,330 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/zalloc.h>
|
||||
|
||||
#include "perf_regs.h"
|
||||
#include "../../../perf-sys.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/debug.h"
|
||||
#include "../../../util/event.h"
|
||||
#include "../../../util/pmu.h"
|
||||
#include "../../../util/pmus.h"
|
||||
|
||||
static const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG(AX, PERF_REG_X86_AX),
|
||||
SMPL_REG(BX, PERF_REG_X86_BX),
|
||||
SMPL_REG(CX, PERF_REG_X86_CX),
|
||||
SMPL_REG(DX, PERF_REG_X86_DX),
|
||||
SMPL_REG(SI, PERF_REG_X86_SI),
|
||||
SMPL_REG(DI, PERF_REG_X86_DI),
|
||||
SMPL_REG(BP, PERF_REG_X86_BP),
|
||||
SMPL_REG(SP, PERF_REG_X86_SP),
|
||||
SMPL_REG(IP, PERF_REG_X86_IP),
|
||||
SMPL_REG(FLAGS, PERF_REG_X86_FLAGS),
|
||||
SMPL_REG(CS, PERF_REG_X86_CS),
|
||||
SMPL_REG(SS, PERF_REG_X86_SS),
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
SMPL_REG(R8, PERF_REG_X86_R8),
|
||||
SMPL_REG(R9, PERF_REG_X86_R9),
|
||||
SMPL_REG(R10, PERF_REG_X86_R10),
|
||||
SMPL_REG(R11, PERF_REG_X86_R11),
|
||||
SMPL_REG(R12, PERF_REG_X86_R12),
|
||||
SMPL_REG(R13, PERF_REG_X86_R13),
|
||||
SMPL_REG(R14, PERF_REG_X86_R14),
|
||||
SMPL_REG(R15, PERF_REG_X86_R15),
|
||||
#endif
|
||||
SMPL_REG2(XMM0, PERF_REG_X86_XMM0),
|
||||
SMPL_REG2(XMM1, PERF_REG_X86_XMM1),
|
||||
SMPL_REG2(XMM2, PERF_REG_X86_XMM2),
|
||||
SMPL_REG2(XMM3, PERF_REG_X86_XMM3),
|
||||
SMPL_REG2(XMM4, PERF_REG_X86_XMM4),
|
||||
SMPL_REG2(XMM5, PERF_REG_X86_XMM5),
|
||||
SMPL_REG2(XMM6, PERF_REG_X86_XMM6),
|
||||
SMPL_REG2(XMM7, PERF_REG_X86_XMM7),
|
||||
SMPL_REG2(XMM8, PERF_REG_X86_XMM8),
|
||||
SMPL_REG2(XMM9, PERF_REG_X86_XMM9),
|
||||
SMPL_REG2(XMM10, PERF_REG_X86_XMM10),
|
||||
SMPL_REG2(XMM11, PERF_REG_X86_XMM11),
|
||||
SMPL_REG2(XMM12, PERF_REG_X86_XMM12),
|
||||
SMPL_REG2(XMM13, PERF_REG_X86_XMM13),
|
||||
SMPL_REG2(XMM14, PERF_REG_X86_XMM14),
|
||||
SMPL_REG2(XMM15, PERF_REG_X86_XMM15),
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
struct sdt_name_reg {
|
||||
const char *sdt_name;
|
||||
const char *uprobe_name;
|
||||
};
|
||||
#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
|
||||
#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
|
||||
|
||||
static const struct sdt_name_reg sdt_reg_tbl[] = {
|
||||
SDT_NAME_REG(eax, ax),
|
||||
SDT_NAME_REG(rax, ax),
|
||||
SDT_NAME_REG(al, ax),
|
||||
SDT_NAME_REG(ah, ax),
|
||||
SDT_NAME_REG(ebx, bx),
|
||||
SDT_NAME_REG(rbx, bx),
|
||||
SDT_NAME_REG(bl, bx),
|
||||
SDT_NAME_REG(bh, bx),
|
||||
SDT_NAME_REG(ecx, cx),
|
||||
SDT_NAME_REG(rcx, cx),
|
||||
SDT_NAME_REG(cl, cx),
|
||||
SDT_NAME_REG(ch, cx),
|
||||
SDT_NAME_REG(edx, dx),
|
||||
SDT_NAME_REG(rdx, dx),
|
||||
SDT_NAME_REG(dl, dx),
|
||||
SDT_NAME_REG(dh, dx),
|
||||
SDT_NAME_REG(esi, si),
|
||||
SDT_NAME_REG(rsi, si),
|
||||
SDT_NAME_REG(sil, si),
|
||||
SDT_NAME_REG(edi, di),
|
||||
SDT_NAME_REG(rdi, di),
|
||||
SDT_NAME_REG(dil, di),
|
||||
SDT_NAME_REG(ebp, bp),
|
||||
SDT_NAME_REG(rbp, bp),
|
||||
SDT_NAME_REG(bpl, bp),
|
||||
SDT_NAME_REG(rsp, sp),
|
||||
SDT_NAME_REG(esp, sp),
|
||||
SDT_NAME_REG(spl, sp),
|
||||
|
||||
/* rNN registers */
|
||||
SDT_NAME_REG(r8b, r8),
|
||||
SDT_NAME_REG(r8w, r8),
|
||||
SDT_NAME_REG(r8d, r8),
|
||||
SDT_NAME_REG(r9b, r9),
|
||||
SDT_NAME_REG(r9w, r9),
|
||||
SDT_NAME_REG(r9d, r9),
|
||||
SDT_NAME_REG(r10b, r10),
|
||||
SDT_NAME_REG(r10w, r10),
|
||||
SDT_NAME_REG(r10d, r10),
|
||||
SDT_NAME_REG(r11b, r11),
|
||||
SDT_NAME_REG(r11w, r11),
|
||||
SDT_NAME_REG(r11d, r11),
|
||||
SDT_NAME_REG(r12b, r12),
|
||||
SDT_NAME_REG(r12w, r12),
|
||||
SDT_NAME_REG(r12d, r12),
|
||||
SDT_NAME_REG(r13b, r13),
|
||||
SDT_NAME_REG(r13w, r13),
|
||||
SDT_NAME_REG(r13d, r13),
|
||||
SDT_NAME_REG(r14b, r14),
|
||||
SDT_NAME_REG(r14w, r14),
|
||||
SDT_NAME_REG(r14d, r14),
|
||||
SDT_NAME_REG(r15b, r15),
|
||||
SDT_NAME_REG(r15w, r15),
|
||||
SDT_NAME_REG(r15d, r15),
|
||||
SDT_NAME_REG_END,
|
||||
};
|
||||
|
||||
/*
|
||||
* Perf only supports OP which is in +/-NUM(REG) form.
|
||||
* Here plus-minus sign, NUM and parenthesis are optional,
|
||||
* only REG is mandatory.
|
||||
*
|
||||
* SDT events also supports indirect addressing mode with a
|
||||
* symbol as offset, scaled mode and constants in OP. But
|
||||
* perf does not support them yet. Below are few examples.
|
||||
*
|
||||
* OP with scaled mode:
|
||||
* (%rax,%rsi,8)
|
||||
* 10(%ras,%rsi,8)
|
||||
*
|
||||
* OP with indirect addressing mode:
|
||||
* check_action(%rip)
|
||||
* mp_+52(%rip)
|
||||
* 44+mp_(%rip)
|
||||
*
|
||||
* OP with constant values:
|
||||
* $0
|
||||
* $123
|
||||
* $-1
|
||||
*/
|
||||
#define SDT_OP_REGEX "^([+\\-]?)([0-9]*)(\\(?)(%[a-z][a-z0-9]+)(\\)?)$"
|
||||
|
||||
static regex_t sdt_op_regex;
|
||||
|
||||
static int sdt_init_op_regex(void)
|
||||
{
|
||||
static int initialized;
|
||||
int ret = 0;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
|
||||
if (ret < 0) {
|
||||
pr_debug4("Regex compilation error.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Max x86 register name length is 5(ex: %r15d). So, 6th char
|
||||
* should always contain NULL. This helps to find register name
|
||||
* length using strlen, instead of maintaining one more variable.
|
||||
*/
|
||||
#define SDT_REG_NAME_SIZE 6
|
||||
|
||||
/*
|
||||
* The uprobe parser does not support all gas register names;
|
||||
* so, we have to replace them (ex. for x86_64: %rax -> %ax).
|
||||
* Note: If register does not require renaming, just copy
|
||||
* paste as it is, but don't leave it empty.
|
||||
*/
|
||||
static void sdt_rename_register(char *sdt_reg, int sdt_len, char *uprobe_reg)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; sdt_reg_tbl[i].sdt_name != NULL; i++) {
|
||||
if (!strncmp(sdt_reg_tbl[i].sdt_name, sdt_reg, sdt_len)) {
|
||||
strcpy(uprobe_reg, sdt_reg_tbl[i].uprobe_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(uprobe_reg, sdt_reg, sdt_len);
|
||||
}
|
||||
|
||||
int arch_sdt_arg_parse_op(char *old_op, char **new_op)
|
||||
{
|
||||
char new_reg[SDT_REG_NAME_SIZE] = {0};
|
||||
int new_len = 0, ret;
|
||||
/*
|
||||
* rm[0]: +/-NUM(REG)
|
||||
* rm[1]: +/-
|
||||
* rm[2]: NUM
|
||||
* rm[3]: (
|
||||
* rm[4]: REG
|
||||
* rm[5]: )
|
||||
*/
|
||||
regmatch_t rm[6];
|
||||
/*
|
||||
* Max prefix length is 2 as it may contains sign(+/-)
|
||||
* and displacement 0 (Both sign and displacement 0 are
|
||||
* optional so it may be empty). Use one more character
|
||||
* to hold last NULL so that strlen can be used to find
|
||||
* prefix length, instead of maintaining one more variable.
|
||||
*/
|
||||
char prefix[3] = {0};
|
||||
|
||||
ret = sdt_init_op_regex();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If unsupported OR does not match with regex OR
|
||||
* register name too long, skip it.
|
||||
*/
|
||||
if (strchr(old_op, ',') || strchr(old_op, '$') ||
|
||||
regexec(&sdt_op_regex, old_op, 6, rm, 0) ||
|
||||
rm[4].rm_eo - rm[4].rm_so > SDT_REG_NAME_SIZE) {
|
||||
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
|
||||
return SDT_ARG_SKIP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare prefix.
|
||||
* If SDT OP has parenthesis but does not provide
|
||||
* displacement, add 0 for displacement.
|
||||
* SDT Uprobe Prefix
|
||||
* -----------------------------
|
||||
* +24(%rdi) +24(%di) +
|
||||
* 24(%rdi) +24(%di) +
|
||||
* %rdi %di
|
||||
* (%rdi) +0(%di) +0
|
||||
* -80(%rbx) -80(%bx) -
|
||||
*/
|
||||
if (rm[3].rm_so != rm[3].rm_eo) {
|
||||
if (rm[1].rm_so != rm[1].rm_eo)
|
||||
prefix[0] = *(old_op + rm[1].rm_so);
|
||||
else if (rm[2].rm_so != rm[2].rm_eo)
|
||||
prefix[0] = '+';
|
||||
else
|
||||
scnprintf(prefix, sizeof(prefix), "+0");
|
||||
}
|
||||
|
||||
/* Rename register */
|
||||
sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
|
||||
new_reg);
|
||||
|
||||
/* Prepare final OP which should be valid for uprobe_events */
|
||||
new_len = strlen(prefix) +
|
||||
(rm[2].rm_eo - rm[2].rm_so) +
|
||||
(rm[3].rm_eo - rm[3].rm_so) +
|
||||
strlen(new_reg) +
|
||||
(rm[5].rm_eo - rm[5].rm_so) +
|
||||
1; /* NULL */
|
||||
|
||||
*new_op = zalloc(new_len);
|
||||
if (!*new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(*new_op, new_len, "%.*s%.*s%.*s%.*s%.*s",
|
||||
strlen(prefix), prefix,
|
||||
(int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
|
||||
(int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so,
|
||||
strlen(new_reg), new_reg,
|
||||
(int)(rm[5].rm_eo - rm[5].rm_so), old_op + rm[5].rm_so);
|
||||
|
||||
return SDT_ARG_VALID;
|
||||
}
|
||||
|
||||
const struct sample_reg *arch__sample_reg_masks(void)
|
||||
{
|
||||
return sample_reg_masks;
|
||||
}
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.sample_type = PERF_SAMPLE_REGS_INTR,
|
||||
.sample_regs_intr = PERF_REG_EXTENDED_MASK,
|
||||
.precise_ip = 1,
|
||||
.disabled = 1,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
int fd;
|
||||
/*
|
||||
* In an unnamed union, init it here to build on older gcc versions
|
||||
*/
|
||||
attr.sample_period = 1;
|
||||
|
||||
if (perf_pmus__num_core_pmus() > 1) {
|
||||
struct perf_pmu *pmu = NULL;
|
||||
__u64 type = PERF_TYPE_RAW;
|
||||
|
||||
/*
|
||||
* The same register set is supported among different hybrid PMUs.
|
||||
* Only check the first available one.
|
||||
*/
|
||||
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
|
||||
type = pmu->type;
|
||||
break;
|
||||
}
|
||||
attr.config |= type << PERF_PMU_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
event_attr_init(&attr);
|
||||
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK);
|
||||
}
|
||||
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
||||
uint64_t arch__user_reg_mask(void)
|
||||
{
|
||||
return PERF_REGS_MASK;
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "util/sample.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
|
||||
Dwarf_Word dwarf_regs[17];
|
||||
unsigned nregs;
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_X86_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(CX);
|
||||
dwarf_regs[2] = REG(DX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SP);
|
||||
dwarf_regs[5] = REG(BP);
|
||||
dwarf_regs[6] = REG(SI);
|
||||
dwarf_regs[7] = REG(DI);
|
||||
dwarf_regs[8] = REG(IP);
|
||||
nregs = 9;
|
||||
} else {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(DX);
|
||||
dwarf_regs[2] = REG(CX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SI);
|
||||
dwarf_regs[5] = REG(DI);
|
||||
dwarf_regs[6] = REG(BP);
|
||||
dwarf_regs[7] = REG(SP);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
dwarf_regs[16] = REG(IP);
|
||||
nregs = 17;
|
||||
}
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ static const char * const bench_uprobe_usage[] = {
|
|||
/*opts=*/&uprobe_opts); \
|
||||
if (!skel->links.prog) { \
|
||||
err = -errno; \
|
||||
fprintf(stderr, "Failed to attach bench uprobe \"%s\": %s\n", #prog, strerror(errno)); \
|
||||
fprintf(stderr, "Failed to attach bench uprobe \"%s\": %m\n", #prog); \
|
||||
goto cleanup; \
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ struct feature_status supported_features[] = {
|
|||
FEATURE_STATUS("dwarf_getlocations", HAVE_LIBDW_SUPPORT),
|
||||
FEATURE_STATUS("dwarf-unwind", HAVE_DWARF_UNWIND_SUPPORT),
|
||||
FEATURE_STATUS_TIP("libbfd", HAVE_LIBBFD_SUPPORT, "Deprecated, license incompatibility, use BUILD_NONDISTRO=1 and install binutils-dev[el]"),
|
||||
FEATURE_STATUS("libbabeltrace", HAVE_LIBBABELTRACE_SUPPORT),
|
||||
FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT),
|
||||
FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT),
|
||||
FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT),
|
||||
|
|
@ -60,6 +61,7 @@ struct feature_status supported_features[] = {
|
|||
FEATURE_STATUS("numa_num_possible_cpus", HAVE_LIBNUMA_SUPPORT),
|
||||
FEATURE_STATUS("zlib", HAVE_ZLIB_SUPPORT),
|
||||
FEATURE_STATUS("zstd", HAVE_ZSTD_SUPPORT),
|
||||
FEATURE_STATUS("rust", HAVE_RUST_SUPPORT),
|
||||
|
||||
/* this should remain at end, to know the array end */
|
||||
FEATURE_STATUS(NULL, _)
|
||||
|
|
|
|||
|
|
@ -265,8 +265,7 @@ static int check_base(struct daemon *daemon)
|
|||
daemon->base);
|
||||
return -EACCES;
|
||||
default:
|
||||
pr_err("failed: can't access base '%s': %s\n",
|
||||
daemon->base, strerror(errno));
|
||||
pr_err("failed: can't access base '%s': %m\n", daemon->base);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
|
@ -544,8 +543,7 @@ static int daemon_session__control(struct daemon_session *session,
|
|||
|
||||
err = writen(control, msg, len);
|
||||
if (err != len) {
|
||||
pr_err("failed: write to control pipe: %d (%s)\n",
|
||||
errno, control_path);
|
||||
pr_err("failed: write to control pipe: %m (%s)\n", control_path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -586,7 +584,7 @@ static int setup_server_socket(struct daemon *daemon)
|
|||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "socket: %s\n", strerror(errno));
|
||||
fprintf(stderr, "socket: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ const char *to_ctf;
|
|||
struct perf_data_convert_opts opts = {
|
||||
.force = false,
|
||||
.all = false,
|
||||
.time_str = NULL,
|
||||
};
|
||||
|
||||
const struct option data_options[] = {
|
||||
|
|
@ -45,6 +46,8 @@ const struct option data_options[] = {
|
|||
#endif
|
||||
OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"),
|
||||
OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"),
|
||||
OPT_STRING(0, "time", &opts.time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -178,10 +178,9 @@ static struct header_column {
|
|||
}
|
||||
};
|
||||
|
||||
static int setup_compute_opt_wdiff(char *opt)
|
||||
static int setup_compute_opt_wdiff(const char *opt)
|
||||
{
|
||||
char *w1_str = opt;
|
||||
char *w2_str;
|
||||
const char *w1_str = opt, *w2_str;
|
||||
|
||||
int ret = -EINVAL;
|
||||
|
||||
|
|
@ -192,8 +191,7 @@ static int setup_compute_opt_wdiff(char *opt)
|
|||
if (!w2_str)
|
||||
goto out;
|
||||
|
||||
*w2_str++ = 0x0;
|
||||
if (!*w2_str)
|
||||
if (!*++w2_str)
|
||||
goto out;
|
||||
|
||||
compute_wdiff_w1 = strtol(w1_str, NULL, 10);
|
||||
|
|
@ -214,7 +212,7 @@ static int setup_compute_opt_wdiff(char *opt)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int setup_compute_opt(char *opt)
|
||||
static int setup_compute_opt(const char *opt)
|
||||
{
|
||||
if (compute == COMPUTE_WEIGHTED_DIFF)
|
||||
return setup_compute_opt_wdiff(opt);
|
||||
|
|
@ -234,7 +232,7 @@ static int setup_compute(const struct option *opt, const char *str,
|
|||
char *cstr = (char *) str;
|
||||
char buf[50];
|
||||
unsigned i;
|
||||
char *option;
|
||||
const char *option;
|
||||
|
||||
if (!str) {
|
||||
*cp = COMPUTE_DELTA;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#include "util/strbuf.h"
|
||||
#include "builtin.h"
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include "common-cmds.h"
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <subcmd/run-command.h>
|
||||
#include <subcmd/help.h>
|
||||
|
|
@ -301,16 +300,58 @@ static struct cmdnames main_cmds, other_cmds;
|
|||
|
||||
void list_common_cmds_help(void)
|
||||
{
|
||||
unsigned int i, longest = 0;
|
||||
const struct cmdname_help {
|
||||
const char *name;
|
||||
const char *help;
|
||||
} common_cmds[] = {
|
||||
{"annotate", "Read perf.data (created by perf record) and display annotated code"},
|
||||
{"archive",
|
||||
"Create archive with object files with build-ids found in perf.data file"},
|
||||
{"bench", "General framework for benchmark suites"},
|
||||
{"buildid-cache", "Manage build-id cache."},
|
||||
{"buildid-list", "List the buildids in a perf.data file"},
|
||||
{"c2c", "Shared Data C2C/HITM Analyzer."},
|
||||
{"config", "Get and set variables in a configuration file."},
|
||||
{"daemon", "Run record sessions on background"},
|
||||
{"data", "Data file related processing"},
|
||||
{"diff", "Read perf.data files and display the differential profile"},
|
||||
{"evlist", "List the event names in a perf.data file"},
|
||||
{"ftrace", "simple wrapper for kernel's ftrace functionality"},
|
||||
{"inject", "Filter to augment the events stream with additional information"},
|
||||
{"iostat", "Show I/O performance metrics"},
|
||||
{"kallsyms", "Searches running kernel for symbols"},
|
||||
{"kvm", "Tool to trace/measure kvm guest os"},
|
||||
{"list", "List all symbolic event types"},
|
||||
{"mem", "Profile memory accesses"},
|
||||
{"record", "Run a command and record its profile into perf.data"},
|
||||
{"report", "Read perf.data (created by perf record) and display the profile"},
|
||||
{"script", "Read perf.data (created by perf record) and display trace output"},
|
||||
{"stat", "Run a command and gather performance counter statistics"},
|
||||
{"test", "Runs sanity tests."},
|
||||
{"top", "System profiling tool."},
|
||||
{"version", "display the version of perf binary"},
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
{"probe", "Define new dynamic tracepoints"},
|
||||
#endif /* HAVE_LIBELF_SUPPORT */
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
{"trace", "strace inspired tool"},
|
||||
{"kmem", "Tool to trace/measure kernel memory properties"},
|
||||
{"kwork", "Tool to trace/measure kernel work properties (latencies)"},
|
||||
{"lock", "Analyze lock events"},
|
||||
{"sched", "Tool to trace/measure scheduler properties (latencies)"},
|
||||
{"timechart", "Tool to visualize total system behavior during a workload"},
|
||||
#endif /* HAVE_LIBTRACEEVENT */
|
||||
};
|
||||
size_t longest = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
if (longest < strlen(common_cmds[i].name))
|
||||
longest = strlen(common_cmds[i].name);
|
||||
}
|
||||
|
||||
puts(" The most commonly used perf commands are:");
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
printf(" %-*s ", longest, common_cmds[i].name);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
printf(" %-*s ", (int)longest, common_cmds[i].name);
|
||||
puts(common_cmds[i].help);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ struct perf_inject {
|
|||
bool in_place_update;
|
||||
bool in_place_update_dry_run;
|
||||
bool copy_kcore_dir;
|
||||
bool convert_callchain;
|
||||
const char *input_name;
|
||||
struct perf_data output;
|
||||
u64 bytes_written;
|
||||
|
|
@ -133,6 +134,7 @@ struct perf_inject {
|
|||
struct guest_session guest_session;
|
||||
struct strlist *known_build_ids;
|
||||
const struct evsel *mmap_evsel;
|
||||
struct ip_callchain *raw_callchain;
|
||||
};
|
||||
|
||||
struct event_entry {
|
||||
|
|
@ -383,6 +385,90 @@ static int perf_event__repipe_sample(const struct perf_tool *tool,
|
|||
return perf_event__repipe_synth(tool, event);
|
||||
}
|
||||
|
||||
static int perf_event__convert_sample_callchain(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||
union perf_event *event_copy = (void *)inject->event_copy;
|
||||
struct callchain_cursor_node *node;
|
||||
struct thread *thread;
|
||||
u64 sample_type = evsel->core.attr.sample_type;
|
||||
u32 sample_size = event->header.size;
|
||||
u64 i, k;
|
||||
int ret;
|
||||
|
||||
if (event_copy == NULL) {
|
||||
inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE);
|
||||
if (!inject->event_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
event_copy = (void *)inject->event_copy;
|
||||
}
|
||||
|
||||
if (cursor == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
callchain_cursor_reset(cursor);
|
||||
|
||||
thread = machine__find_thread(machine, sample->tid, sample->pid);
|
||||
if (thread == NULL)
|
||||
goto out;
|
||||
|
||||
/* this will parse DWARF using stack and register data */
|
||||
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||
/*parent=*/NULL, /*root_al=*/NULL,
|
||||
PERF_MAX_STACK_DEPTH);
|
||||
thread__put(thread);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
/* copy kernel callchain and context entries */
|
||||
for (i = 0; i < sample->callchain->nr; i++) {
|
||||
inject->raw_callchain->ips[i] = sample->callchain->ips[i];
|
||||
if (sample->callchain->ips[i] == PERF_CONTEXT_USER) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0 || inject->raw_callchain->ips[i - 1] != PERF_CONTEXT_USER)
|
||||
inject->raw_callchain->ips[i++] = PERF_CONTEXT_USER;
|
||||
|
||||
node = cursor->first;
|
||||
for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) {
|
||||
if (machine__kernel_ip(machine, node->ip))
|
||||
/* kernel IPs were added already */;
|
||||
else if (node->ms.sym && node->ms.sym->inlined)
|
||||
/* we can't handle inlined callchains */;
|
||||
else
|
||||
inject->raw_callchain->ips[i++] = node->ip;
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
inject->raw_callchain->nr = i;
|
||||
sample->callchain = inject->raw_callchain;
|
||||
|
||||
out:
|
||||
memcpy(event_copy, event, sizeof(event->header));
|
||||
|
||||
/* adjust sample size for stack and regs */
|
||||
sample_size -= sample->user_stack.size;
|
||||
sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64);
|
||||
sample_size += (sample->callchain->nr + 1) * sizeof(u64);
|
||||
event_copy->header.size = sample_size;
|
||||
|
||||
/* remove sample_type {STACK,REGS}_USER for synthesize */
|
||||
sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER);
|
||||
|
||||
perf_event__synthesize_sample(event_copy, sample_type,
|
||||
evsel->core.attr.read_format, sample);
|
||||
return perf_event__repipe_synth(tool, event_copy);
|
||||
}
|
||||
|
||||
static struct dso *findnew_dso(int pid, int tid, const char *filename,
|
||||
const struct dso_id *id, struct machine *machine)
|
||||
{
|
||||
|
|
@ -2022,7 +2108,7 @@ static int save_section_info(struct perf_inject *inject)
|
|||
return perf_header__process_sections(header, fd, inject, save_section_info_cb);
|
||||
}
|
||||
|
||||
static bool keep_feat(int feat)
|
||||
static bool keep_feat(struct perf_inject *inject, int feat)
|
||||
{
|
||||
switch (feat) {
|
||||
/* Keep original information that describes the machine or software */
|
||||
|
|
@ -2047,9 +2133,11 @@ static bool keep_feat(int feat)
|
|||
case HEADER_CLOCK_DATA:
|
||||
case HEADER_HYBRID_TOPOLOGY:
|
||||
case HEADER_PMU_CAPS:
|
||||
case HEADER_CPU_DOMAIN_INFO:
|
||||
return true;
|
||||
/* Information that can be updated */
|
||||
case HEADER_BUILD_ID:
|
||||
return inject->build_id_style == BID_RWS__NONE;
|
||||
case HEADER_CMDLINE:
|
||||
case HEADER_EVENT_DESC:
|
||||
case HEADER_BRANCH_STACK:
|
||||
|
|
@ -2108,7 +2196,7 @@ static int feat_copy_cb(struct feat_copier *fc, int feat, struct feat_writer *fw
|
|||
int ret;
|
||||
|
||||
if (!inject->secs[feat].offset ||
|
||||
!keep_feat(feat))
|
||||
!keep_feat(inject, feat))
|
||||
return 0;
|
||||
|
||||
ret = feat_copy(inject, feat, fw);
|
||||
|
|
@ -2269,6 +2357,15 @@ static int __cmd_inject(struct perf_inject *inject)
|
|||
/* Allow space in the header for guest attributes */
|
||||
output_data_offset += gs->session->header.data_offset;
|
||||
output_data_offset = roundup(output_data_offset, 4096);
|
||||
} else if (inject->convert_callchain) {
|
||||
inject->tool.sample = perf_event__convert_sample_callchain;
|
||||
inject->tool.fork = perf_event__repipe_fork;
|
||||
inject->tool.comm = perf_event__repipe_comm;
|
||||
inject->tool.exit = perf_event__repipe_exit;
|
||||
inject->tool.mmap = perf_event__repipe_mmap;
|
||||
inject->tool.mmap2 = perf_event__repipe_mmap2;
|
||||
inject->tool.ordered_events = true;
|
||||
inject->tool.ordering_requires_timestamps = true;
|
||||
}
|
||||
|
||||
if (!inject->itrace_synth_opts.set)
|
||||
|
|
@ -2321,6 +2418,23 @@ static int __cmd_inject(struct perf_inject *inject)
|
|||
perf_header__set_feat(&session->header,
|
||||
HEADER_BRANCH_STACK);
|
||||
}
|
||||
|
||||
/*
|
||||
* The converted data file won't have stack and registers.
|
||||
* Update the perf_event_attr to remove them before writing.
|
||||
*/
|
||||
if (inject->convert_callchain) {
|
||||
struct evsel *evsel;
|
||||
|
||||
evlist__for_each_entry(session->evlist, evsel) {
|
||||
evsel__reset_sample_bit(evsel, REGS_USER);
|
||||
evsel__reset_sample_bit(evsel, STACK_USER);
|
||||
evsel->core.attr.sample_regs_user = 0;
|
||||
evsel->core.attr.sample_stack_user = 0;
|
||||
evsel->core.attr.exclude_callchain_user = 0;
|
||||
}
|
||||
}
|
||||
|
||||
session->header.data_offset = output_data_offset;
|
||||
session->header.data_size = inject->bytes_written;
|
||||
perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc,
|
||||
|
|
@ -2345,6 +2459,18 @@ static int __cmd_inject(struct perf_inject *inject)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool evsel__has_dwarf_callchain(struct evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->core.attr;
|
||||
const u64 dwarf_callchain_flags =
|
||||
PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN;
|
||||
|
||||
if (!attr->exclude_callchain_user)
|
||||
return false;
|
||||
|
||||
return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags;
|
||||
}
|
||||
|
||||
int cmd_inject(int argc, const char **argv)
|
||||
{
|
||||
struct perf_inject inject = {
|
||||
|
|
@ -2413,6 +2539,8 @@ int cmd_inject(int argc, const char **argv)
|
|||
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
|
||||
"guest mount directory under which every guest os"
|
||||
" instance has a subdir"),
|
||||
OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain,
|
||||
"Generate callchains using DWARF and drop register/stack data"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const inject_usage[] = {
|
||||
|
|
@ -2428,6 +2556,9 @@ int cmd_inject(int argc, const char **argv)
|
|||
|
||||
#ifndef HAVE_JITDUMP
|
||||
set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true);
|
||||
#endif
|
||||
#ifndef HAVE_LIBDW_SUPPORT
|
||||
set_option_nobuild(options, 0, "convert-callchain", "NO_LIBDW=1", true);
|
||||
#endif
|
||||
argc = parse_options(argc, argv, options, inject_usage, 0);
|
||||
|
||||
|
|
@ -2526,6 +2657,8 @@ int cmd_inject(int argc, const char **argv)
|
|||
inject.tool.compressed = perf_event__repipe_op4_synth;
|
||||
inject.tool.auxtrace = perf_event__repipe_auxtrace;
|
||||
inject.tool.bpf_metadata = perf_event__repipe_op2_synth;
|
||||
inject.tool.schedstat_cpu = perf_event__repipe_op2_synth;
|
||||
inject.tool.schedstat_domain = perf_event__repipe_op2_synth;
|
||||
inject.tool.dont_split_sample_group = true;
|
||||
inject.tool.merge_deferred_callchains = false;
|
||||
inject.session = __perf_session__new(&data, &inject.tool,
|
||||
|
|
@ -2587,6 +2720,28 @@ int cmd_inject(int argc, const char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (inject.convert_callchain) {
|
||||
struct evsel *evsel;
|
||||
|
||||
if (inject.output.is_pipe || inject.session->data->is_pipe) {
|
||||
pr_err("--convert-callchain cannot work with pipe\n");
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
evlist__for_each_entry(inject.session->evlist, evsel) {
|
||||
if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) {
|
||||
pr_err("--convert-callchain requires DWARF call graph.\n");
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
inject.raw_callchain = calloc(PERF_MAX_STACK_DEPTH, sizeof(u64));
|
||||
if (inject.raw_callchain == NULL) {
|
||||
pr_err("callchain allocation failed\n");
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_JITDUMP
|
||||
if (inject.jit_mode) {
|
||||
inject.tool.mmap2 = perf_event__repipe_mmap2;
|
||||
|
|
@ -2617,5 +2772,6 @@ out_close_output:
|
|||
free(inject.itrace_synth_opts.vm_tm_corr_args);
|
||||
free(inject.event_copy);
|
||||
free(inject.guest_session.ev.event_buf);
|
||||
free(inject.raw_callchain);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "builtin.h"
|
||||
#include "perf.h"
|
||||
|
||||
#include <dwarf-regs.h>
|
||||
#include "util/build-id.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/evlist.h"
|
||||
|
|
@ -52,7 +53,7 @@
|
|||
#include <math.h>
|
||||
#include <perf/mmap.h>
|
||||
|
||||
#if defined(HAVE_KVM_STAT_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
|
||||
#if defined(HAVE_LIBTRACEEVENT)
|
||||
#define GET_EVENT_KEY(func, field) \
|
||||
static u64 get_event_ ##func(struct kvm_event *event, int vcpu) \
|
||||
{ \
|
||||
|
|
@ -597,7 +598,7 @@ static void kvm_display(struct perf_kvm_stat *kvm)
|
|||
|
||||
#endif /* HAVE_SLANG_SUPPORT */
|
||||
|
||||
#endif // defined(HAVE_KVM_STAT_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
|
||||
#endif // defined(HAVE_LIBTRACEEVENT)
|
||||
|
||||
static const char *get_filename_for_perf_kvm(void)
|
||||
{
|
||||
|
|
@ -613,13 +614,13 @@ static const char *get_filename_for_perf_kvm(void)
|
|||
return filename;
|
||||
}
|
||||
|
||||
#if defined(HAVE_KVM_STAT_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
|
||||
#if defined(HAVE_LIBTRACEEVENT)
|
||||
|
||||
static bool register_kvm_events_ops(struct perf_kvm_stat *kvm)
|
||||
static bool register_kvm_events_ops(struct perf_kvm_stat *kvm, uint16_t e_machine)
|
||||
{
|
||||
struct kvm_reg_events_ops *events_ops = kvm_reg_events_ops;
|
||||
const struct kvm_reg_events_ops *events_ops;
|
||||
|
||||
for (events_ops = kvm_reg_events_ops; events_ops->name; events_ops++) {
|
||||
for (events_ops = kvm_reg_events_ops(e_machine); events_ops->name; events_ops++) {
|
||||
if (!strcmp(events_ops->name, kvm->report_event)) {
|
||||
kvm->events_ops = events_ops->ops;
|
||||
return true;
|
||||
|
|
@ -809,7 +810,7 @@ static bool is_child_event(struct perf_kvm_stat *kvm,
|
|||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
struct child_event_ops *child_ops;
|
||||
const struct child_event_ops *child_ops;
|
||||
|
||||
child_ops = kvm->events_ops->child_ops;
|
||||
|
||||
|
|
@ -841,11 +842,11 @@ static bool handle_child_event(struct perf_kvm_stat *kvm,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool skip_event(const char *event)
|
||||
static bool skip_event(uint16_t e_machine, const char *event)
|
||||
{
|
||||
const char * const *skip_events;
|
||||
|
||||
for (skip_events = kvm_skip_events; *skip_events; skip_events++)
|
||||
for (skip_events = kvm_skip_events(e_machine); *skip_events; skip_events++)
|
||||
if (!strcmp(event, *skip_events))
|
||||
return true;
|
||||
|
||||
|
|
@ -901,9 +902,10 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
|
|||
|
||||
if (kvm->duration && time_diff > kvm->duration) {
|
||||
char decode[KVM_EVENT_NAME_LEN];
|
||||
uint16_t e_machine = perf_session__e_machine(kvm->session, /*e_flags=*/NULL);
|
||||
|
||||
kvm->events_ops->decode_key(kvm, &event->key, decode);
|
||||
if (!skip_event(decode)) {
|
||||
if (!skip_event(e_machine, decode)) {
|
||||
pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n",
|
||||
sample->time, sample->pid, vcpu_record->vcpu_id,
|
||||
decode, time_diff / NSEC_PER_USEC);
|
||||
|
|
@ -921,6 +923,8 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
|
|||
/* Only kvm_entry records vcpu id. */
|
||||
if (!thread__priv(thread) && kvm_entry_event(evsel)) {
|
||||
struct vcpu_event_record *vcpu_record;
|
||||
struct machine *machine = maps__machine(thread__maps(thread));
|
||||
uint16_t e_machine = thread__e_machine(thread, machine, /*e_flags=*/NULL);
|
||||
|
||||
vcpu_record = zalloc(sizeof(*vcpu_record));
|
||||
if (!vcpu_record) {
|
||||
|
|
@ -928,7 +932,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
vcpu_record->vcpu_id = evsel__intval(evsel, sample, vcpu_id_str);
|
||||
vcpu_record->vcpu_id = evsel__intval(evsel, sample, vcpu_id_str(e_machine));
|
||||
thread__set_priv(thread, vcpu_record);
|
||||
}
|
||||
|
||||
|
|
@ -1163,6 +1167,7 @@ static int cpu_isa_config(struct perf_kvm_stat *kvm)
|
|||
{
|
||||
char buf[128], *cpuid;
|
||||
int err;
|
||||
uint16_t e_machine;
|
||||
|
||||
if (kvm->live) {
|
||||
struct perf_cpu cpu = {-1};
|
||||
|
|
@ -1182,7 +1187,8 @@ static int cpu_isa_config(struct perf_kvm_stat *kvm)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = cpu_isa_init(kvm, cpuid);
|
||||
e_machine = perf_session__e_machine(kvm->session, /*e_flags=*/NULL);
|
||||
err = cpu_isa_init(kvm, e_machine, cpuid);
|
||||
if (err == -ENOTSUP)
|
||||
pr_err("CPU %s is not supported.\n", cpuid);
|
||||
|
||||
|
|
@ -1413,7 +1419,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
|
|||
|
||||
if (!verify_vcpu(kvm->trace_vcpu) ||
|
||||
!is_valid_key(kvm) ||
|
||||
!register_kvm_events_ops(kvm)) {
|
||||
!register_kvm_events_ops(kvm, EM_HOST)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -1543,7 +1549,7 @@ out:
|
|||
static int read_events(struct perf_kvm_stat *kvm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
uint16_t e_machine;
|
||||
struct perf_data file = {
|
||||
.path = kvm->file_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
|
|
@ -1568,6 +1574,12 @@ static int read_events(struct perf_kvm_stat *kvm)
|
|||
goto out_delete;
|
||||
}
|
||||
|
||||
e_machine = perf_session__e_machine(kvm->session, /*e_flags=*/NULL);
|
||||
if (!register_kvm_events_ops(kvm, e_machine)) {
|
||||
ret = -EINVAL;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not use 'isa' recorded in kvm_exit tracepoint since it is not
|
||||
* traced in the old kernel.
|
||||
|
|
@ -1610,9 +1622,6 @@ static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm)
|
|||
if (!is_valid_key(kvm))
|
||||
goto exit;
|
||||
|
||||
if (!register_kvm_events_ops(kvm))
|
||||
goto exit;
|
||||
|
||||
if (kvm->use_stdio) {
|
||||
use_browser = 0;
|
||||
setup_pager();
|
||||
|
|
@ -1636,11 +1645,6 @@ exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int __weak setup_kvm_events_tp(struct perf_kvm_stat *kvm __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
{
|
||||
|
|
@ -1658,15 +1662,16 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
|||
};
|
||||
const char * const *events_tp;
|
||||
int ret;
|
||||
uint16_t e_machine = EM_HOST;
|
||||
|
||||
events_tp_size = 0;
|
||||
ret = setup_kvm_events_tp(kvm);
|
||||
ret = setup_kvm_events_tp(kvm, e_machine);
|
||||
if (ret < 0) {
|
||||
pr_err("Unable to setup the kvm tracepoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (events_tp = kvm_events_tp; *events_tp; events_tp++)
|
||||
for (events_tp = kvm_events_tp(e_machine); *events_tp; events_tp++)
|
||||
events_tp_size++;
|
||||
|
||||
rec_argc = ARRAY_SIZE(record_args) + argc + 2 +
|
||||
|
|
@ -1681,7 +1686,7 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
|||
|
||||
for (j = 0; j < events_tp_size; j++) {
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT("-e");
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]);
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp(e_machine)[j]);
|
||||
}
|
||||
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
|
||||
|
|
@ -1775,7 +1780,7 @@ static struct evlist *kvm_live_event_list(void)
|
|||
if (evlist == NULL)
|
||||
return NULL;
|
||||
|
||||
for (events_tp = kvm_events_tp; *events_tp; events_tp++) {
|
||||
for (events_tp = kvm_events_tp(EM_HOST); *events_tp; events_tp++) {
|
||||
|
||||
tp = strdup(*events_tp);
|
||||
if (tp == NULL)
|
||||
|
|
@ -1900,7 +1905,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
|||
/*
|
||||
* generate the event list
|
||||
*/
|
||||
err = setup_kvm_events_tp(kvm);
|
||||
err = setup_kvm_events_tp(kvm, EM_HOST);
|
||||
if (err < 0) {
|
||||
pr_err("Unable to setup the kvm tracepoints\n");
|
||||
return err;
|
||||
|
|
@ -1985,13 +1990,7 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)
|
|||
perf_stat:
|
||||
return cmd_stat(argc, argv);
|
||||
}
|
||||
#endif /* HAVE_KVM_STAT_SUPPORT */
|
||||
|
||||
int __weak kvm_add_default_arch_event(int *argc __maybe_unused,
|
||||
const char **argv __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_LIBTRACEEVENT */
|
||||
|
||||
static int __cmd_record(const char *file_name, int argc, const char **argv)
|
||||
{
|
||||
|
|
@ -2016,7 +2015,7 @@ static int __cmd_record(const char *file_name, int argc, const char **argv)
|
|||
|
||||
BUG_ON(i + 2 != rec_argc);
|
||||
|
||||
ret = kvm_add_default_arch_event(&i, rec_argv);
|
||||
ret = kvm_add_default_arch_event(EM_HOST, &i, rec_argv);
|
||||
if (ret)
|
||||
goto EXIT;
|
||||
|
||||
|
|
@ -2103,7 +2102,7 @@ static int __cmd_top(int argc, const char **argv)
|
|||
|
||||
BUG_ON(i != argc);
|
||||
|
||||
ret = kvm_add_default_arch_event(&i, rec_argv);
|
||||
ret = kvm_add_default_arch_event(EM_HOST, &i, rec_argv);
|
||||
if (ret)
|
||||
goto EXIT;
|
||||
|
||||
|
|
@ -2179,7 +2178,7 @@ int cmd_kvm(int argc, const char **argv)
|
|||
return __cmd_top(argc, argv);
|
||||
else if (strlen(argv[0]) > 2 && strstarts("buildid-list", argv[0]))
|
||||
return __cmd_buildid_list(file_name, argc, argv);
|
||||
#if defined(HAVE_KVM_STAT_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
|
||||
#if defined(HAVE_LIBTRACEEVENT)
|
||||
else if (strlen(argv[0]) > 2 && strstarts("stat", argv[0]))
|
||||
return kvm_cmd_stat(file_name, argc, argv);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -648,7 +648,7 @@ int cmd_list(int argc, const char **argv)
|
|||
}
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char *sep, *s;
|
||||
char *s;
|
||||
|
||||
if (strcmp(argv[i], "tracepoint") == 0) {
|
||||
char *old_pmu_glob = default_ps.pmu_glob;
|
||||
|
|
@ -720,7 +720,7 @@ int cmd_list(int argc, const char **argv)
|
|||
else if (strcmp(argv[i], "pfm") == 0)
|
||||
print_libpfm_events(&print_cb, ps);
|
||||
#endif
|
||||
else if ((sep = strchr(argv[i], ':')) != NULL) {
|
||||
else if (strchr(argv[i], ':') != NULL) {
|
||||
char *old_pmu_glob = ps->pmu_glob;
|
||||
char *old_event_glob = ps->event_glob;
|
||||
|
||||
|
|
|
|||
|
|
@ -211,8 +211,7 @@ static int opt_set_target_ns(const struct option *opt __maybe_unused,
|
|||
ns_pid = (pid_t)strtol(str, NULL, 10);
|
||||
if (errno != 0) {
|
||||
ret = -errno;
|
||||
pr_warning("Failed to parse %s as a pid: %s\n", str,
|
||||
strerror(errno));
|
||||
pr_warning("Failed to parse %s as a pid: %m\n", str);
|
||||
return ret;
|
||||
}
|
||||
nsip = nsinfo__new(ns_pid);
|
||||
|
|
|
|||
|
|
@ -1286,7 +1286,6 @@ static int record__mmap_evlist(struct record *rec,
|
|||
struct record_opts *opts = &rec->opts;
|
||||
bool auxtrace_overwrite = opts->auxtrace_snapshot_mode ||
|
||||
opts->auxtrace_sample_mode;
|
||||
char msg[512];
|
||||
|
||||
if (opts->affinity != PERF_AFFINITY_SYS)
|
||||
cpu__setup_cpunode_map();
|
||||
|
|
@ -1305,8 +1304,7 @@ static int record__mmap_evlist(struct record *rec,
|
|||
opts->mmap_pages, opts->auxtrace_mmap_pages);
|
||||
return -errno;
|
||||
} else {
|
||||
pr_err("failed to mmap with %d (%s)\n", errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
pr_err("failed to mmap: %m\n");
|
||||
if (errno)
|
||||
return -errno;
|
||||
else
|
||||
|
|
@ -1324,7 +1322,8 @@ static int record__mmap_evlist(struct record *rec,
|
|||
if (record__threads_enabled(rec)) {
|
||||
ret = perf_data__create_dir(&rec->data, evlist->core.nr_mmaps);
|
||||
if (ret) {
|
||||
pr_err("Failed to create data directory: %s\n", strerror(-ret));
|
||||
errno = -ret;
|
||||
pr_err("Failed to create data directory: %m\n");
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < evlist->core.nr_mmaps; i++) {
|
||||
|
|
@ -1404,6 +1403,7 @@ try_again:
|
|||
}
|
||||
#endif
|
||||
if (report_error || verbose > 0) {
|
||||
evsel__open_strerror(pos, &opts->target, errno, msg, sizeof(msg));
|
||||
ui__error("Failure to open event '%s' on PMU '%s' which will be "
|
||||
"removed.\n%s\n",
|
||||
evsel__name(pos), evsel__pmu_name(pos), msg);
|
||||
|
|
@ -1461,9 +1461,8 @@ try_again:
|
|||
}
|
||||
|
||||
if (evlist__apply_filters(evlist, &pos, &opts->target)) {
|
||||
pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
pos->filter ?: "BPF", evsel__name(pos), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
pr_err("failed to set filter \"%s\" on event %s: %m\n",
|
||||
pos->filter ?: "BPF", evsel__name(pos));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1511,6 +1510,8 @@ static int process_buildids(struct record *rec)
|
|||
if (perf_data__size(&rec->data) == 0)
|
||||
return 0;
|
||||
|
||||
/* A single DSO is needed and not all inline frames. */
|
||||
symbol_conf.inline_name = false;
|
||||
/*
|
||||
* During this process, it'll load kernel map and replace the
|
||||
* dso->long_name to a real pathname it found. In this case
|
||||
|
|
@ -1521,7 +1522,6 @@ static int process_buildids(struct record *rec)
|
|||
* $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551
|
||||
*/
|
||||
symbol_conf.ignore_vmlinux_buildid = true;
|
||||
|
||||
/*
|
||||
* If --buildid-all is given, it marks all DSO regardless of hits,
|
||||
* so no need to process samples. But if timestamp_boundary is enabled,
|
||||
|
|
@ -1748,8 +1748,7 @@ static void *record__thread(void *arg)
|
|||
|
||||
err = write(thread->pipes.ack[1], &msg, sizeof(msg));
|
||||
if (err == -1)
|
||||
pr_warning("threads[%d]: failed to notify on start: %s\n",
|
||||
thread->tid, strerror(errno));
|
||||
pr_warning("threads[%d]: failed to notify on start: %m\n", thread->tid);
|
||||
|
||||
pr_debug("threads[%d]: started on cpu%d\n", thread->tid, sched_getcpu());
|
||||
|
||||
|
|
@ -1792,8 +1791,7 @@ static void *record__thread(void *arg)
|
|||
|
||||
err = write(thread->pipes.ack[1], &msg, sizeof(msg));
|
||||
if (err == -1)
|
||||
pr_warning("threads[%d]: failed to notify on termination: %s\n",
|
||||
thread->tid, strerror(errno));
|
||||
pr_warning("threads[%d]: failed to notify on termination: %m\n", thread->tid);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1881,7 +1879,7 @@ static int record__synthesize_workload(struct record *rec, bool tail)
|
|||
process_synthesized_event,
|
||||
&rec->session->machines.host,
|
||||
needs_mmap,
|
||||
rec->opts.sample_address);
|
||||
rec->opts.record_data_mmap);
|
||||
perf_thread_map__put(thread_map);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2191,7 +2189,7 @@ static int record__synthesize(struct record *rec, bool tail)
|
|||
|
||||
err = __machine__synthesize_threads(machine, tool, &opts->target,
|
||||
rec->evlist->core.threads,
|
||||
f, needs_mmap, opts->sample_address,
|
||||
f, needs_mmap, opts->record_data_mmap,
|
||||
rec->opts.nr_threads_synthesize);
|
||||
}
|
||||
|
||||
|
|
@ -2338,7 +2336,7 @@ static int record__start_threads(struct record *rec)
|
|||
|
||||
sigfillset(&full);
|
||||
if (sigprocmask(SIG_SETMASK, &full, &mask)) {
|
||||
pr_err("Failed to block signals on threads start: %s\n", strerror(errno));
|
||||
pr_err("Failed to block signals on threads start: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -2356,7 +2354,7 @@ static int record__start_threads(struct record *rec)
|
|||
if (pthread_create(&handle, &attrs, record__thread, &thread_data[t])) {
|
||||
for (tt = 1; tt < t; tt++)
|
||||
record__terminate_thread(&thread_data[t]);
|
||||
pr_err("Failed to start threads: %s\n", strerror(errno));
|
||||
pr_err("Failed to start threads: %m\n");
|
||||
ret = -1;
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -2379,7 +2377,7 @@ out_err:
|
|||
pthread_attr_destroy(&attrs);
|
||||
|
||||
if (sigprocmask(SIG_SETMASK, &mask, NULL)) {
|
||||
pr_err("Failed to unblock signals on threads start: %s\n", strerror(errno));
|
||||
pr_err("Failed to unblock signals on threads start: %m\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
|
|
@ -3006,8 +3004,9 @@ int record_opts__parse_callchain(struct record_opts *record,
|
|||
ret = parse_callchain_record_opt(arg, callchain);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
record->sample_address = true;
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF &&
|
||||
!record->record_data_mmap_set)
|
||||
record->record_data_mmap = true;
|
||||
callchain_debug(callchain);
|
||||
}
|
||||
|
||||
|
|
@ -3686,6 +3685,9 @@ static struct option __record_options[] = {
|
|||
OPT_CALLBACK(0, "off-cpu-thresh", &record.opts, "ms",
|
||||
"Dump off-cpu samples if off-cpu time exceeds this threshold (in milliseconds). (Default: 500ms)",
|
||||
record__parse_off_cpu_thresh),
|
||||
OPT_BOOLEAN_SET(0, "data-mmap", &record.opts.record_data_mmap,
|
||||
&record.opts.record_data_mmap_set,
|
||||
"Record mmap events for non-executable mappings"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
|
@ -4249,9 +4251,12 @@ int cmd_record(int argc, const char **argv)
|
|||
goto out_opts;
|
||||
}
|
||||
|
||||
/* For backward compatibility, -d implies --mem-info */
|
||||
if (rec->opts.sample_address)
|
||||
/* For backward compatibility, -d implies --mem-info and --data-mmap */
|
||||
if (rec->opts.sample_address) {
|
||||
rec->opts.sample_data_src = true;
|
||||
if (!rec->opts.record_data_mmap_set)
|
||||
rec->opts.record_data_mmap = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow aliases to facilitate the lookup of symbols for address
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ static int report__setup_sample_type(struct report *rep)
|
|||
}
|
||||
}
|
||||
|
||||
callchain_param_setup(sample_type, perf_env__arch(perf_session__env(rep->session)));
|
||||
callchain_param_setup(sample_type, perf_session__e_machine(session, /*e_flags=*/NULL));
|
||||
|
||||
if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
|
||||
ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
|
||||
|
|
@ -1271,12 +1271,18 @@ parse_percent_limit(const struct option *opt, const char *str,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
report_parse_addr2line_config(const struct option *opt __maybe_unused,
|
||||
const char *arg, int unset __maybe_unused)
|
||||
{
|
||||
return addr2line_configure("addr2line.style", arg, NULL);
|
||||
}
|
||||
|
||||
static int process_attr(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct evlist **pevlist)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_env *env;
|
||||
u64 sample_type;
|
||||
int err;
|
||||
|
||||
|
|
@ -1290,8 +1296,7 @@ static int process_attr(const struct perf_tool *tool __maybe_unused,
|
|||
*/
|
||||
sample_type = evlist__combined_sample_type(*pevlist);
|
||||
session = (*pevlist)->session;
|
||||
env = perf_session__env(session);
|
||||
callchain_param_setup(sample_type, perf_env__arch(env));
|
||||
callchain_param_setup(sample_type, perf_session__e_machine(session, /*e_flags=*/NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1447,6 +1452,9 @@ int cmd_report(int argc, const char **argv)
|
|||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_STRING(0, "addr2line", &addr2line_path, "path",
|
||||
"addr2line binary to use for line numbers"),
|
||||
OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style",
|
||||
"addr2line styles (libdw,llvm,libbfd,addr2line)",
|
||||
report_parse_addr2line_config),
|
||||
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
||||
"Symbol demangling. Enabled by default, use --no-demangle to disable."),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
|
|
@ -1727,7 +1735,8 @@ repeat:
|
|||
sort_order = NULL;
|
||||
}
|
||||
|
||||
if (sort_order && strstr(sort_order, "type")) {
|
||||
if ((sort_order && strstr(sort_order, "type")) ||
|
||||
(field_order && strstr(field_order, "type"))) {
|
||||
report.data_type = true;
|
||||
annotate_opts.annotate_src = false;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,7 +37,6 @@
|
|||
#include "ui/ui.h"
|
||||
#include "print_binary.h"
|
||||
#include "print_insn.h"
|
||||
#include "archinsn.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -90,7 +89,6 @@ static bool print_flags;
|
|||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
static int max_blocks;
|
||||
static bool native_arch;
|
||||
static struct dlfilter *dlfilter;
|
||||
static int dlargc;
|
||||
static char **dlargv;
|
||||
|
|
@ -717,7 +715,8 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch,
|
||||
static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
|
||||
uint16_t e_machine, uint32_t e_flags,
|
||||
FILE *fp)
|
||||
{
|
||||
unsigned i = 0, r;
|
||||
|
|
@ -730,7 +729,9 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, cons
|
|||
|
||||
for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) {
|
||||
u64 val = regs->regs[i++];
|
||||
printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val);
|
||||
printed += fprintf(fp, "%5s:0x%"PRIx64" ",
|
||||
perf_reg_name(r, e_machine, e_flags),
|
||||
val);
|
||||
}
|
||||
|
||||
return printed;
|
||||
|
|
@ -787,23 +788,29 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen,
|
|||
}
|
||||
|
||||
static int perf_sample__fprintf_iregs(struct perf_sample *sample,
|
||||
struct perf_event_attr *attr, const char *arch, FILE *fp)
|
||||
struct perf_event_attr *attr,
|
||||
uint16_t e_machine,
|
||||
uint32_t e_flags,
|
||||
FILE *fp)
|
||||
{
|
||||
if (!sample->intr_regs)
|
||||
return 0;
|
||||
|
||||
return perf_sample__fprintf_regs(perf_sample__intr_regs(sample),
|
||||
attr->sample_regs_intr, arch, fp);
|
||||
attr->sample_regs_intr, e_machine, e_flags, fp);
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_uregs(struct perf_sample *sample,
|
||||
struct perf_event_attr *attr, const char *arch, FILE *fp)
|
||||
struct perf_event_attr *attr,
|
||||
uint16_t e_machine,
|
||||
uint32_t e_flags,
|
||||
FILE *fp)
|
||||
{
|
||||
if (!sample->user_regs)
|
||||
return 0;
|
||||
|
||||
return perf_sample__fprintf_regs(perf_sample__user_regs(sample),
|
||||
attr->sample_regs_user, arch, fp);
|
||||
attr->sample_regs_user, e_machine, e_flags, fp);
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_start(struct perf_script *script,
|
||||
|
|
@ -1618,7 +1625,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
|
|||
{
|
||||
int printed = 0;
|
||||
|
||||
script_fetch_insn(sample, thread, machine, native_arch);
|
||||
perf_sample__fetch_insn(sample, thread, machine);
|
||||
|
||||
if (PRINT_FIELD(INSNLEN))
|
||||
printed += fprintf(fp, " ilen: %d", sample->insn_len);
|
||||
|
|
@ -2418,7 +2425,7 @@ static void process_event(struct perf_script *script,
|
|||
struct evsel_script *es = evsel->priv;
|
||||
FILE *fp = es->fp;
|
||||
char str[PAGE_SIZE_NAME_LEN];
|
||||
const char *arch = perf_env__arch(machine->env);
|
||||
uint32_t e_flags;
|
||||
|
||||
if (output[type].fields == 0)
|
||||
return;
|
||||
|
|
@ -2505,11 +2512,19 @@ static void process_event(struct perf_script *script,
|
|||
symbol_conf.bt_stop_list, fp);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(IREGS))
|
||||
perf_sample__fprintf_iregs(sample, attr, arch, fp);
|
||||
if (PRINT_FIELD(IREGS)) {
|
||||
perf_sample__fprintf_iregs(sample, attr,
|
||||
thread__e_machine(thread, machine, &e_flags),
|
||||
e_flags,
|
||||
fp);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(UREGS))
|
||||
perf_sample__fprintf_uregs(sample, attr, arch, fp);
|
||||
if (PRINT_FIELD(UREGS)) {
|
||||
perf_sample__fprintf_uregs(sample, attr,
|
||||
thread__e_machine(thread, machine, &e_flags),
|
||||
e_flags,
|
||||
fp);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(BRSTACK))
|
||||
perf_sample__fprintf_brstack(sample, thread, evsel, fp);
|
||||
|
|
@ -2803,6 +2818,7 @@ static int process_attr(const struct perf_tool *tool, union perf_event *event,
|
|||
struct perf_script *scr = container_of(tool, struct perf_script, tool);
|
||||
struct evlist *evlist;
|
||||
struct evsel *evsel, *pos;
|
||||
uint16_t e_machine;
|
||||
u64 sample_type;
|
||||
int err;
|
||||
|
||||
|
|
@ -2844,7 +2860,8 @@ static int process_attr(const struct perf_tool *tool, union perf_event *event,
|
|||
* on events sample_type.
|
||||
*/
|
||||
sample_type = evlist__combined_sample_type(evlist);
|
||||
callchain_param_setup(sample_type, perf_env__arch(perf_session__env(scr->session)));
|
||||
e_machine = perf_session__e_machine(evsel__session(evsel), /*e_flags=*/NULL);
|
||||
callchain_param_setup(sample_type, e_machine);
|
||||
|
||||
/* Enable fields for callchain entries */
|
||||
if (symbol_conf.use_callchain &&
|
||||
|
|
@ -3819,7 +3836,7 @@ static void script__setup_sample_type(struct perf_script *script)
|
|||
struct perf_session *session = script->session;
|
||||
u64 sample_type = evlist__combined_sample_type(session->evlist);
|
||||
|
||||
callchain_param_setup(sample_type, perf_env__arch(session->machines.host.env));
|
||||
callchain_param_setup(sample_type, perf_session__e_machine(session, /*e_flags=*/NULL));
|
||||
|
||||
if (script->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
|
||||
pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
|
||||
|
|
@ -4017,7 +4034,6 @@ int cmd_script(int argc, const char **argv)
|
|||
.set = false,
|
||||
.default_no_sample = true,
|
||||
};
|
||||
struct utsname uts;
|
||||
char *script_path = NULL;
|
||||
const char *dlfilter_file = NULL;
|
||||
const char **__argv;
|
||||
|
|
@ -4439,17 +4455,6 @@ script_found:
|
|||
if (symbol__init(env) < 0)
|
||||
goto out_delete;
|
||||
|
||||
uname(&uts);
|
||||
if (data.is_pipe) { /* Assume pipe_mode indicates native_arch */
|
||||
native_arch = true;
|
||||
} else if (env->arch) {
|
||||
if (!strcmp(uts.machine, env->arch))
|
||||
native_arch = true;
|
||||
else if (!strcmp(uts.machine, "x86_64") &&
|
||||
!strcmp(env->arch, "i386"))
|
||||
native_arch = true;
|
||||
}
|
||||
|
||||
script.session = session;
|
||||
script__setup_sample_type(&script);
|
||||
|
||||
|
|
@ -4484,6 +4489,7 @@ script_found:
|
|||
if (generate_script_lang) {
|
||||
struct stat perf_stat;
|
||||
int input;
|
||||
char *filename = strdup("perf-script");
|
||||
|
||||
if (output_set_by_user()) {
|
||||
fprintf(stderr,
|
||||
|
|
@ -4511,17 +4517,32 @@ script_found:
|
|||
}
|
||||
|
||||
scripting_ops = script_spec__lookup(generate_script_lang);
|
||||
if (!scripting_ops && ends_with(generate_script_lang, ".py")) {
|
||||
scripting_ops = script_spec__lookup("python");
|
||||
free(filename);
|
||||
filename = strdup(generate_script_lang);
|
||||
filename[strlen(filename) - 3] = '\0';
|
||||
} else if (!scripting_ops && ends_with(generate_script_lang, ".pl")) {
|
||||
scripting_ops = script_spec__lookup("perl");
|
||||
free(filename);
|
||||
filename = strdup(generate_script_lang);
|
||||
filename[strlen(filename) - 3] = '\0';
|
||||
}
|
||||
if (!scripting_ops) {
|
||||
fprintf(stderr, "invalid language specifier");
|
||||
fprintf(stderr, "invalid language specifier '%s'\n", generate_script_lang);
|
||||
err = -ENOENT;
|
||||
goto out_delete;
|
||||
}
|
||||
if (!filename) {
|
||||
err = -ENOMEM;
|
||||
goto out_delete;
|
||||
}
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
err = scripting_ops->generate_script(session->tevent.pevent,
|
||||
"perf-script");
|
||||
err = scripting_ops->generate_script(session->tevent.pevent, filename);
|
||||
#else
|
||||
err = scripting_ops->generate_script(NULL, "perf-script");
|
||||
err = scripting_ops->generate_script(NULL, filename);
|
||||
#endif
|
||||
free(filename);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -369,19 +369,11 @@ static int read_counter_cpu(struct evsel *counter, int cpu_map_idx)
|
|||
static int read_counters_with_affinity(void)
|
||||
{
|
||||
struct evlist_cpu_iterator evlist_cpu_itr;
|
||||
struct affinity saved_affinity, *affinity;
|
||||
|
||||
if (all_counters_use_bpf)
|
||||
return 0;
|
||||
|
||||
if (!target__has_cpu(&target) || target__has_per_thread(&target))
|
||||
affinity = NULL;
|
||||
else if (affinity__setup(&saved_affinity) < 0)
|
||||
return -1;
|
||||
else
|
||||
affinity = &saved_affinity;
|
||||
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) {
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list) {
|
||||
struct evsel *counter = evlist_cpu_itr.evsel;
|
||||
|
||||
if (evsel__is_bpf(counter))
|
||||
|
|
@ -393,8 +385,6 @@ static int read_counters_with_affinity(void)
|
|||
if (!counter->err)
|
||||
counter->err = read_counter_cpu(counter, evlist_cpu_itr.cpu_map_idx);
|
||||
}
|
||||
if (affinity)
|
||||
affinity__cleanup(&saved_affinity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -793,7 +783,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
const bool forks = (argc > 0);
|
||||
bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false;
|
||||
struct evlist_cpu_iterator evlist_cpu_itr;
|
||||
struct affinity saved_affinity, *affinity = NULL;
|
||||
int err, open_err = 0;
|
||||
bool second_pass = false, has_supported_counters;
|
||||
|
||||
|
|
@ -805,14 +794,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
child_pid = evsel_list->workload.pid;
|
||||
}
|
||||
|
||||
if (!cpu_map__is_dummy(evsel_list->core.user_requested_cpus)) {
|
||||
if (affinity__setup(&saved_affinity) < 0) {
|
||||
err = -1;
|
||||
goto err_out;
|
||||
}
|
||||
affinity = &saved_affinity;
|
||||
}
|
||||
|
||||
evlist__for_each_entry(evsel_list, counter) {
|
||||
counter->reset_group = false;
|
||||
if (bpf_counter__load(counter, &target)) {
|
||||
|
|
@ -825,15 +806,13 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
|
||||
evlist__reset_aggr_stats(evsel_list);
|
||||
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) {
|
||||
counter = evlist_cpu_itr.evsel;
|
||||
|
||||
/*
|
||||
* bperf calls evsel__open_per_cpu() in bperf__load(), so
|
||||
* no need to call it again here.
|
||||
*/
|
||||
if (target.use_bpf)
|
||||
break;
|
||||
if (!target.use_bpf) {
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list) {
|
||||
counter = evlist_cpu_itr.evsel;
|
||||
|
||||
if (counter->reset_group || !counter->supported)
|
||||
continue;
|
||||
|
|
@ -847,11 +826,12 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
|
||||
open_err = errno;
|
||||
/*
|
||||
* Weak group failed. We cannot just undo this here
|
||||
* because earlier CPUs might be in group mode, and the kernel
|
||||
* doesn't support mixing group and non group reads. Defer
|
||||
* it to later.
|
||||
* Don't close here because we're in the wrong affinity.
|
||||
* Weak group failed. We cannot just undo this
|
||||
* here because earlier CPUs might be in group
|
||||
* mode, and the kernel doesn't support mixing
|
||||
* group and non group reads. Defer it to later.
|
||||
* Don't close here because we're in the wrong
|
||||
* affinity.
|
||||
*/
|
||||
if ((open_err == EINVAL || open_err == EBADF) &&
|
||||
evsel__leader(counter) != counter &&
|
||||
|
|
@ -867,7 +847,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (second_pass) {
|
||||
/*
|
||||
* Now redo all the weak group after closing them,
|
||||
|
|
@ -875,7 +855,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
*/
|
||||
|
||||
/* First close errored or weak retry */
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) {
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list) {
|
||||
counter = evlist_cpu_itr.evsel;
|
||||
|
||||
if (!counter->reset_group && counter->supported)
|
||||
|
|
@ -884,7 +864,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
perf_evsel__close_cpu(&counter->core, evlist_cpu_itr.cpu_map_idx);
|
||||
}
|
||||
/* Now reopen weak */
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) {
|
||||
evlist__for_each_cpu(evlist_cpu_itr, evsel_list) {
|
||||
counter = evlist_cpu_itr.evsel;
|
||||
|
||||
if (!counter->reset_group)
|
||||
|
|
@ -893,17 +873,18 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
while (true) {
|
||||
pr_debug2("reopening weak %s\n", evsel__name(counter));
|
||||
if (create_perf_stat_counter(counter, &stat_config,
|
||||
evlist_cpu_itr.cpu_map_idx) == 0)
|
||||
evlist_cpu_itr.cpu_map_idx) == 0) {
|
||||
evlist_cpu_iterator__exit(&evlist_cpu_itr);
|
||||
break;
|
||||
|
||||
}
|
||||
open_err = errno;
|
||||
if (stat_handle_error(counter, open_err) != COUNTER_RETRY)
|
||||
if (stat_handle_error(counter, open_err) != COUNTER_RETRY) {
|
||||
evlist_cpu_iterator__exit(&evlist_cpu_itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
affinity__cleanup(affinity);
|
||||
affinity = NULL;
|
||||
}
|
||||
|
||||
has_supported_counters = false;
|
||||
evlist__for_each_entry(evsel_list, counter) {
|
||||
|
|
@ -937,9 +918,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
}
|
||||
|
||||
if (evlist__apply_filters(evsel_list, &counter, &target)) {
|
||||
pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
counter->filter, evsel__name(counter), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
pr_err("failed to set filter \"%s\" on event %s: %m\n",
|
||||
counter->filter, evsel__name(counter));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1001,8 +981,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
}
|
||||
|
||||
if (workload_exec_errno) {
|
||||
const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg));
|
||||
pr_err("Workload failed: %s\n", emsg);
|
||||
errno = workload_exec_errno;
|
||||
pr_err("Workload failed: %m\n");
|
||||
err = -1;
|
||||
goto err_out;
|
||||
}
|
||||
|
|
@ -1066,7 +1046,6 @@ err_out:
|
|||
if (forks)
|
||||
evlist__cancel_workload(evsel_list);
|
||||
|
||||
affinity__cleanup(affinity);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2447,6 +2426,7 @@ static int parse_tpebs_mode(const struct option *opt, const char *str,
|
|||
int cmd_stat(int argc, const char **argv)
|
||||
{
|
||||
struct opt_aggr_mode opt_mode = {};
|
||||
bool affinity = true, affinity_set = false;
|
||||
struct option stat_options[] = {
|
||||
OPT_BOOLEAN('T', "transaction", &transaction_run,
|
||||
"hardware transaction statistics"),
|
||||
|
|
@ -2575,6 +2555,8 @@ int cmd_stat(int argc, const char **argv)
|
|||
"don't print 'summary' for CSV summary output"),
|
||||
OPT_BOOLEAN(0, "quiet", &quiet,
|
||||
"don't print any output, messages or warnings (useful with record)"),
|
||||
OPT_BOOLEAN_SET(0, "affinity", &affinity, &affinity_set,
|
||||
"enable (default) or disable affinity optimizations to reduce IPIs"),
|
||||
OPT_CALLBACK(0, "cputype", &evsel_list, "hybrid cpu type",
|
||||
"Only enable events on applying cpu with this type "
|
||||
"for hybrid platform (e.g. core or atom)",
|
||||
|
|
@ -2632,6 +2614,9 @@ int cmd_stat(int argc, const char **argv)
|
|||
} else
|
||||
stat_config.csv_sep = DEFAULT_SEPARATOR;
|
||||
|
||||
if (affinity_set)
|
||||
evsel_list->no_affinity = !affinity;
|
||||
|
||||
if (argc && strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
|
||||
argc = __cmd_record(stat_options, &opt_mode, argc, argv);
|
||||
if (argc < 0)
|
||||
|
|
|
|||
|
|
@ -2616,12 +2616,10 @@ static struct syscall *trace__syscall_info(struct trace *trace, struct evsel *ev
|
|||
err = syscall__read_info(sc, trace);
|
||||
|
||||
if (err && verbose > 0) {
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err,
|
||||
str_error_r(-err, sbuf, sizeof(sbuf)));
|
||||
errno = -err;
|
||||
fprintf(trace->output, "Problems reading syscall %d: %m", id);
|
||||
if (sc && sc->name)
|
||||
fprintf(trace->output, "(%s)", sc->name);
|
||||
fprintf(trace->output, " (%s)", sc->name);
|
||||
fputs(" information\n", trace->output);
|
||||
}
|
||||
return err ? NULL : sc;
|
||||
|
|
@ -2791,7 +2789,7 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel,
|
|||
struct thread_trace *ttrace;
|
||||
|
||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||
e_machine = thread__e_machine(thread, trace->host);
|
||||
e_machine = thread__e_machine(thread, trace->host, /*e_flags=*/NULL);
|
||||
sc = trace__syscall_info(trace, evsel, e_machine, id);
|
||||
if (sc == NULL)
|
||||
goto out_put;
|
||||
|
|
@ -2870,7 +2868,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel,
|
|||
|
||||
|
||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||
e_machine = thread__e_machine(thread, trace->host);
|
||||
e_machine = thread__e_machine(thread, trace->host, /*e_flags=*/NULL);
|
||||
sc = trace__syscall_info(trace, evsel, e_machine, id);
|
||||
if (sc == NULL)
|
||||
goto out_put;
|
||||
|
|
@ -2936,7 +2934,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel,
|
|||
struct thread_trace *ttrace;
|
||||
|
||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||
e_machine = thread__e_machine(thread, trace->host);
|
||||
e_machine = thread__e_machine(thread, trace->host, /*e_flags=*/NULL);
|
||||
sc = trace__syscall_info(trace, evsel, e_machine, id);
|
||||
if (sc == NULL)
|
||||
goto out_put;
|
||||
|
|
@ -3287,7 +3285,9 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel,
|
|||
|
||||
if (evsel == trace->syscalls.events.bpf_output) {
|
||||
int id = perf_evsel__sc_tp_uint(evsel, id, sample);
|
||||
int e_machine = thread ? thread__e_machine(thread, trace->host) : EM_HOST;
|
||||
int e_machine = thread
|
||||
? thread__e_machine(thread, trace->host, /*e_flags=*/NULL)
|
||||
: EM_HOST;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, e_machine, id);
|
||||
|
||||
if (sc) {
|
||||
|
|
@ -4673,9 +4673,8 @@ out_error:
|
|||
|
||||
out_error_apply_filters:
|
||||
fprintf(trace->output,
|
||||
"Failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
evsel->filter, evsel__name(evsel), errno,
|
||||
str_error_r(errno, errbuf, sizeof(errbuf)));
|
||||
"Failed to set filter \"%s\" on event %s: %m\n",
|
||||
evsel->filter, evsel__name(evsel));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
out_error_mem:
|
||||
|
|
@ -4683,7 +4682,7 @@ out_error_mem:
|
|||
goto out_delete_evlist;
|
||||
|
||||
out_errno:
|
||||
fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno));
|
||||
fprintf(trace->output, "%m\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
|
|
@ -4919,7 +4918,7 @@ static size_t trace__fprintf_thread(FILE *fp, struct thread *thread, struct trac
|
|||
{
|
||||
size_t printed = 0;
|
||||
struct thread_trace *ttrace = thread__priv(thread);
|
||||
int e_machine = thread__e_machine(thread, trace->host);
|
||||
int e_machine = thread__e_machine(thread, trace->host, /*e_flags=*/NULL);
|
||||
double ratio;
|
||||
|
||||
if (ttrace == NULL)
|
||||
|
|
@ -5173,8 +5172,8 @@ static int trace__parse_events_option(const struct option *opt, const char *str,
|
|||
int unset __maybe_unused)
|
||||
{
|
||||
struct trace *trace = (struct trace *)opt->value;
|
||||
const char *s = str;
|
||||
char *sep = NULL, *lists[2] = { NULL, NULL, };
|
||||
const char *s;
|
||||
char *strd, *sep = NULL, *lists[2] = { NULL, NULL, };
|
||||
int len = strlen(str) + 1, err = -1, list, idx;
|
||||
char *strace_groups_dir = system_path(STRACE_GROUPS_DIR);
|
||||
char group_name[PATH_MAX];
|
||||
|
|
@ -5183,13 +5182,17 @@ static int trace__parse_events_option(const struct option *opt, const char *str,
|
|||
if (strace_groups_dir == NULL)
|
||||
return -1;
|
||||
|
||||
s = strd = strdup(str);
|
||||
if (strd == NULL)
|
||||
return -1;
|
||||
|
||||
if (*s == '!') {
|
||||
++s;
|
||||
trace->not_ev_qualifier = true;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if ((sep = strchr(s, ',')) != NULL)
|
||||
if ((sep = strchr((char *)s, ',')) != NULL)
|
||||
*sep = '\0';
|
||||
|
||||
list = 0;
|
||||
|
|
@ -5257,8 +5260,7 @@ out:
|
|||
free(strace_groups_dir);
|
||||
free(lists[0]);
|
||||
free(lists[1]);
|
||||
if (sep)
|
||||
*sep = ',';
|
||||
free(strd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ declare -a FILES=(
|
|||
"arch/s390/include/uapi/asm/kvm.h"
|
||||
"arch/s390/include/uapi/asm/sie.h"
|
||||
"arch/arm64/include/uapi/asm/kvm.h"
|
||||
"arch/arm64/include/uapi/asm/unistd.h"
|
||||
"arch/alpha/include/uapi/asm/errno.h"
|
||||
"arch/mips/include/asm/errno.h"
|
||||
"arch/mips/include/uapi/asm/errno.h"
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
#
|
||||
# List of known perf commands.
|
||||
# command name category [deprecated] [common]
|
||||
#
|
||||
perf-annotate mainporcelain common
|
||||
perf-archive mainporcelain common
|
||||
perf-bench mainporcelain common
|
||||
perf-buildid-cache mainporcelain common
|
||||
perf-buildid-list mainporcelain common
|
||||
perf-data mainporcelain common
|
||||
perf-diff mainporcelain common
|
||||
perf-c2c mainporcelain common
|
||||
perf-config mainporcelain common
|
||||
perf-evlist mainporcelain common
|
||||
perf-ftrace mainporcelain common
|
||||
perf-inject mainporcelain common
|
||||
perf-iostat mainporcelain common
|
||||
perf-kallsyms mainporcelain common
|
||||
perf-kmem mainporcelain traceevent
|
||||
perf-kvm mainporcelain common
|
||||
perf-kwork mainporcelain traceevent
|
||||
perf-list mainporcelain common
|
||||
perf-lock mainporcelain traceevent
|
||||
perf-mem mainporcelain common
|
||||
perf-probe mainporcelain full
|
||||
perf-record mainporcelain common
|
||||
perf-report mainporcelain common
|
||||
perf-sched mainporcelain traceevent
|
||||
perf-script mainporcelain common
|
||||
perf-stat mainporcelain common
|
||||
perf-test mainporcelain common
|
||||
perf-timechart mainporcelain traceevent
|
||||
perf-top mainporcelain common
|
||||
perf-trace mainporcelain audit
|
||||
perf-version mainporcelain common
|
||||
perf-daemon mainporcelain common
|
||||
|
|
@ -142,7 +142,7 @@ copy_class_filename(const char * class_sign, const char * file_name, char * resu
|
|||
*/
|
||||
if (*class_sign == 'L') {
|
||||
size_t j, i = 0;
|
||||
char *p = strrchr(class_sign, '/');
|
||||
const char *p = strrchr(class_sign, '/');
|
||||
if (p) {
|
||||
/* drop the 'L' prefix and copy up to the final '/' */
|
||||
for (i = 0; i < (size_t)(p - class_sign); i++)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue