Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "This tree includes an x86 PMU scheduling fix, but most changes are late breaking tooling fixes and updates: User visible fixes: - Create config.detected into OUTPUT directory, fixing parallel builds sharing the same source directory (Aaro Kiskinen) - Allow to specify custom linker command, fixing some MIPS64 builds. (Aaro Kiskinen) - Fix to show proper convergence stats in 'perf bench numa' (Srikar Dronamraju) User visible changes: - Validate syscall list passed via -e argument to 'perf trace'. (Arnaldo Carvalho de Melo) - Introduce 'perf stat --per-thread' (Jiri Olsa) - Check access permission for --kallsyms and --vmlinux (Li Zhang) - Move toggling event logic from 'perf top' and into hists browser, allowing freeze/unfreeze with event lists with more than one entry (Namhyung Kim) - Add missing newlines when dumping PERF_RECORD_FINISHED_ROUND and showing the Aggregated stats in 'perf report -D' (Adrian Hunter) Infrastructure fixes: - Add missing break for PERF_RECORD_ITRACE_START, which caused those events samples to be parsed as well as PERF_RECORD_LOST_SAMPLES. ITRACE_START only appears when Intel PT or BTS are present, so.. (Jiri Olsa) - Call the perf_session destructor when bailing out in the inject, kmem, report, kvm and mem tools (Taeung Song) Infrastructure changes: - Move stuff out of 'perf stat' and into the lib for further use (Jiri Olsa) - Reference count the cpu_map and thread_map classes (Jiri Olsa) - Set evsel->{cpus,threads} from the evlist, if not set, allowing the generalization of some 'perf stat' functions that previously were accessing private static evlist variable (Jiri Olsa) - Delete an unnecessary check before the calling free_event_desc() (Markus Elfring) - Allow auxtrace data alignment (Adrian Hunter) - Allow events with dot (Andi Kleen) - Fix failure to 'perf probe' events on arm (He Kuang) - Add testing for Makefile.perf (Jiri Olsa) - Add test for make install with prefix (Jiri Olsa) - Fix single target build dependency check (Jiri Olsa) - Access thread_map entries via accessors, prep patch to hold more info per entry, for ongoing 'perf stat --per-thread' work (Jiri Olsa) - Use __weak definition from compiler.h (Sukadev Bhattiprolu) - Split perf_pmu__new_alias() (Sukadev Bhattiprolu)" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (54 commits) perf tools: Allow to specify custom linker command perf tools: Create config.detected into OUTPUT directory perf mem: Fill in the missing session freeing after an error occurs perf kvm: Fill in the missing session freeing after an error occurs perf report: Fill in the missing session freeing after an error occurs perf kmem: Fill in the missing session freeing after an error occurs perf inject: Fill in the missing session freeing after an error occurs perf tools: Add missing break for PERF_RECORD_ITRACE_START perf/x86: Fix 'active_events' imbalance perf symbols: Check access permission when reading symbol files perf stat: Introduce --per-thread option perf stat: Introduce print_counters function perf stat: Using init_stats instead of memset perf stat: Rename print_interval to process_interval perf stat: Remove perf_evsel__read_cb function perf stat: Move perf_stat initialization counter process code perf stat: Move zero_per_pkg into counter process code perf stat: Separate counters reading and processing perf stat: Introduce read_counters function perf stat: Introduce perf_evsel__read function ...
This commit is contained in:
commit
c1776a18e3
arch/x86/kernel/cpu
tools
build
perf
Documentation
MakefileMakefile.perfbuiltin-inject.cbuiltin-kmem.cbuiltin-kvm.cbuiltin-mem.cbuiltin-report.cbuiltin-stat.cbuiltin-top.cbuiltin-trace.cconfig
tests
Buildbuiltin-test.ccode-reading.ckeep-tracking.cmakemmap-basic.cmmap-thread-lookup.copenat-syscall-all-cpus.copenat-syscall-tp-fields.copenat-syscall.cswitch-tracking.ctests.hthread-map.c
ui/browsers
util
|
@ -357,34 +357,24 @@ void x86_release_hardware(void)
|
||||||
*/
|
*/
|
||||||
int x86_add_exclusive(unsigned int what)
|
int x86_add_exclusive(unsigned int what)
|
||||||
{
|
{
|
||||||
int ret = -EBUSY, i;
|
int i;
|
||||||
|
|
||||||
if (atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what]))
|
if (!atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) {
|
||||||
return 0;
|
mutex_lock(&pmc_reserve_mutex);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) {
|
||||||
mutex_lock(&pmc_reserve_mutex);
|
if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
|
||||||
for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) {
|
goto fail_unlock;
|
||||||
if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
|
}
|
||||||
goto out;
|
atomic_inc(&x86_pmu.lbr_exclusive[what]);
|
||||||
|
mutex_unlock(&pmc_reserve_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_inc(&x86_pmu.lbr_exclusive[what]);
|
atomic_inc(&active_events);
|
||||||
ret = 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
fail_unlock:
|
||||||
mutex_unlock(&pmc_reserve_mutex);
|
mutex_unlock(&pmc_reserve_mutex);
|
||||||
|
return -EBUSY;
|
||||||
/*
|
|
||||||
* Assuming that all exclusive events will share the PMI handler
|
|
||||||
* (which checks active_events for whether there is work to do),
|
|
||||||
* we can bump active_events counter right here, except for
|
|
||||||
* x86_lbr_exclusive_lbr events that go through x86_pmu_event_init()
|
|
||||||
* path, which already bumps active_events for them.
|
|
||||||
*/
|
|
||||||
if (!ret && what != x86_lbr_exclusive_lbr)
|
|
||||||
atomic_inc(&active_events);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void x86_del_exclusive(unsigned int what)
|
void x86_del_exclusive(unsigned int what)
|
||||||
|
|
|
@ -25,7 +25,7 @@ build-dir := $(srctree)/tools/build
|
||||||
include $(build-dir)/Build.include
|
include $(build-dir)/Build.include
|
||||||
|
|
||||||
# do not force detected configuration
|
# do not force detected configuration
|
||||||
-include .config-detected
|
-include $(OUTPUT).config-detected
|
||||||
|
|
||||||
# Init all relevant variables used in build files so
|
# Init all relevant variables used in build files so
|
||||||
# 1) they have correct type
|
# 1) they have correct type
|
||||||
|
|
|
@ -144,6 +144,10 @@ is a useful mode to detect imbalance between physical cores. To enable this mod
|
||||||
use --per-core in addition to -a. (system-wide). The output includes the
|
use --per-core in addition to -a. (system-wide). The output includes the
|
||||||
core number and the number of online logical processors on that physical processor.
|
core number and the number of online logical processors on that physical processor.
|
||||||
|
|
||||||
|
--per-thread::
|
||||||
|
Aggregate counts per monitored threads, when monitoring threads (-t option)
|
||||||
|
or processes (-p option).
|
||||||
|
|
||||||
-D msecs::
|
-D msecs::
|
||||||
--delay msecs::
|
--delay msecs::
|
||||||
After starting the program, wait msecs before measuring. This is useful to
|
After starting the program, wait msecs before measuring. This is useful to
|
||||||
|
|
|
@ -83,8 +83,8 @@ build-test:
|
||||||
#
|
#
|
||||||
# All other targets get passed through:
|
# All other targets get passed through:
|
||||||
#
|
#
|
||||||
%:
|
%: FORCE
|
||||||
$(print_msg)
|
$(print_msg)
|
||||||
$(make)
|
$(make)
|
||||||
|
|
||||||
.PHONY: tags TAGS
|
.PHONY: tags TAGS FORCE Makefile
|
||||||
|
|
|
@ -110,7 +110,7 @@ $(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD
|
||||||
$(Q)touch $(OUTPUT)PERF-VERSION-FILE
|
$(Q)touch $(OUTPUT)PERF-VERSION-FILE
|
||||||
|
|
||||||
CC = $(CROSS_COMPILE)gcc
|
CC = $(CROSS_COMPILE)gcc
|
||||||
LD = $(CROSS_COMPILE)ld
|
LD ?= $(CROSS_COMPILE)ld
|
||||||
AR = $(CROSS_COMPILE)ar
|
AR = $(CROSS_COMPILE)ar
|
||||||
PKG_CONFIG = $(CROSS_COMPILE)pkg-config
|
PKG_CONFIG = $(CROSS_COMPILE)pkg-config
|
||||||
|
|
||||||
|
@ -545,7 +545,7 @@ config-clean:
|
||||||
clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
|
clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
|
||||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
|
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
|
||||||
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||||
$(Q)$(RM) .config-detected
|
$(Q)$(RM) $(OUTPUT).config-detected
|
||||||
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
|
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
|
||||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
|
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
|
||||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
||||||
|
|
|
@ -630,12 +630,13 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
if (inject.session == NULL)
|
if (inject.session == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (symbol__init(&inject.session->header.env) < 0)
|
ret = symbol__init(&inject.session->header.env);
|
||||||
return -1;
|
if (ret < 0)
|
||||||
|
goto out_delete;
|
||||||
|
|
||||||
ret = __cmd_inject(&inject);
|
ret = __cmd_inject(&inject);
|
||||||
|
|
||||||
|
out_delete:
|
||||||
perf_session__delete(inject.session);
|
perf_session__delete(inject.session);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1916,7 +1916,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
if (!perf_evlist__find_tracepoint_by_name(session->evlist,
|
if (!perf_evlist__find_tracepoint_by_name(session->evlist,
|
||||||
"kmem:kmalloc")) {
|
"kmem:kmalloc")) {
|
||||||
pr_err(errmsg, "slab", "slab");
|
pr_err(errmsg, "slab", "slab");
|
||||||
return -1;
|
goto out_delete;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1927,7 +1927,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
"kmem:mm_page_alloc");
|
"kmem:mm_page_alloc");
|
||||||
if (evsel == NULL) {
|
if (evsel == NULL) {
|
||||||
pr_err(errmsg, "page", "page");
|
pr_err(errmsg, "page", "page");
|
||||||
return -1;
|
goto out_delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
|
kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
|
||||||
|
|
|
@ -1061,8 +1061,10 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||||
|
|
||||||
symbol__init(&kvm->session->header.env);
|
symbol__init(&kvm->session->header.env);
|
||||||
|
|
||||||
if (!perf_session__has_traces(kvm->session, "kvm record"))
|
if (!perf_session__has_traces(kvm->session, "kvm record")) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out_delete;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not use 'isa' recorded in kvm_exit tracepoint since it is not
|
* Do not use 'isa' recorded in kvm_exit tracepoint since it is not
|
||||||
|
@ -1070,9 +1072,13 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||||
*/
|
*/
|
||||||
ret = cpu_isa_config(kvm);
|
ret = cpu_isa_config(kvm);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
goto out_delete;
|
||||||
|
|
||||||
return perf_session__process_events(kvm->session);
|
ret = perf_session__process_events(kvm->session);
|
||||||
|
|
||||||
|
out_delete:
|
||||||
|
perf_session__delete(kvm->session);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_target_str(struct perf_kvm_stat *kvm)
|
static int parse_target_str(struct perf_kvm_stat *kvm)
|
||||||
|
|
|
@ -124,7 +124,6 @@ static int report_raw_events(struct perf_mem *mem)
|
||||||
.mode = PERF_DATA_MODE_READ,
|
.mode = PERF_DATA_MODE_READ,
|
||||||
.force = mem->force,
|
.force = mem->force,
|
||||||
};
|
};
|
||||||
int err = -EINVAL;
|
|
||||||
int ret;
|
int ret;
|
||||||
struct perf_session *session = perf_session__new(&file, false,
|
struct perf_session *session = perf_session__new(&file, false,
|
||||||
&mem->tool);
|
&mem->tool);
|
||||||
|
@ -135,24 +134,21 @@ static int report_raw_events(struct perf_mem *mem)
|
||||||
if (mem->cpu_list) {
|
if (mem->cpu_list) {
|
||||||
ret = perf_session__cpu_bitmap(session, mem->cpu_list,
|
ret = perf_session__cpu_bitmap(session, mem->cpu_list,
|
||||||
mem->cpu_bitmap);
|
mem->cpu_bitmap);
|
||||||
if (ret)
|
if (ret < 0)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol__init(&session->header.env) < 0)
|
ret = symbol__init(&session->header.env);
|
||||||
return -1;
|
if (ret < 0)
|
||||||
|
goto out_delete;
|
||||||
|
|
||||||
printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
|
printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
|
||||||
|
|
||||||
err = perf_session__process_events(session);
|
ret = perf_session__process_events(session);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_delete:
|
out_delete:
|
||||||
perf_session__delete(session);
|
perf_session__delete(session);
|
||||||
return err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||||
|
|
|
@ -742,6 +742,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
|
|
||||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||||
|
|
||||||
|
if (symbol_conf.vmlinux_name &&
|
||||||
|
access(symbol_conf.vmlinux_name, R_OK)) {
|
||||||
|
pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (symbol_conf.kallsyms_name &&
|
||||||
|
access(symbol_conf.kallsyms_name, R_OK)) {
|
||||||
|
pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (report.use_stdio)
|
if (report.use_stdio)
|
||||||
use_browser = 0;
|
use_browser = 0;
|
||||||
else if (report.use_tui)
|
else if (report.use_tui)
|
||||||
|
@ -828,8 +839,10 @@ repeat:
|
||||||
if (report.header || report.header_only) {
|
if (report.header || report.header_only) {
|
||||||
perf_session__fprintf_info(session, stdout,
|
perf_session__fprintf_info(session, stdout,
|
||||||
report.show_full_info);
|
report.show_full_info);
|
||||||
if (report.header_only)
|
if (report.header_only) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
} else if (use_browser == 0) {
|
} else if (use_browser == 0) {
|
||||||
fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
|
fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
|
||||||
stdout);
|
stdout);
|
||||||
|
|
|
@ -67,10 +67,7 @@
|
||||||
#define CNTR_NOT_SUPPORTED "<not supported>"
|
#define CNTR_NOT_SUPPORTED "<not supported>"
|
||||||
#define CNTR_NOT_COUNTED "<not counted>"
|
#define CNTR_NOT_COUNTED "<not counted>"
|
||||||
|
|
||||||
static void print_stat(int argc, const char **argv);
|
static void print_counters(struct timespec *ts, int argc, const char **argv);
|
||||||
static void print_counter_aggr(struct perf_evsel *counter, char *prefix);
|
|
||||||
static void print_counter(struct perf_evsel *counter, char *prefix);
|
|
||||||
static void print_aggr(char *prefix);
|
|
||||||
|
|
||||||
/* Default events used for perf stat -T */
|
/* Default events used for perf stat -T */
|
||||||
static const char *transaction_attrs = {
|
static const char *transaction_attrs = {
|
||||||
|
@ -141,96 +138,9 @@ static inline void diff_timespec(struct timespec *r, struct timespec *a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
|
static void perf_stat__reset_stats(void)
|
||||||
{
|
{
|
||||||
return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus;
|
perf_evlist__reset_stats(evsel_list);
|
||||||
}
|
|
||||||
|
|
||||||
static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
return perf_evsel__cpus(evsel)->nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct perf_stat *ps = evsel->priv;
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
|
||||||
init_stats(&ps->res_stats[i]);
|
|
||||||
|
|
||||||
perf_stat_evsel_id_init(evsel);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
evsel->priv = zalloc(sizeof(struct perf_stat));
|
|
||||||
if (evsel->priv == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
perf_evsel__reset_stat_priv(evsel);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
zfree(&evsel->priv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
struct perf_counts *counts;
|
|
||||||
|
|
||||||
counts = perf_counts__new(perf_evsel__nr_cpus(evsel));
|
|
||||||
if (counts)
|
|
||||||
evsel->prev_raw_counts = counts;
|
|
||||||
|
|
||||||
return counts ? 0 : -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
|
|
||||||
{
|
|
||||||
perf_counts__delete(evsel->prev_raw_counts);
|
|
||||||
evsel->prev_raw_counts = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_evlist__free_stats(struct perf_evlist *evlist)
|
|
||||||
{
|
|
||||||
struct perf_evsel *evsel;
|
|
||||||
|
|
||||||
evlist__for_each(evlist, evsel) {
|
|
||||||
perf_evsel__free_stat_priv(evsel);
|
|
||||||
perf_evsel__free_counts(evsel);
|
|
||||||
perf_evsel__free_prev_raw_counts(evsel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
|
|
||||||
{
|
|
||||||
struct perf_evsel *evsel;
|
|
||||||
|
|
||||||
evlist__for_each(evlist, evsel) {
|
|
||||||
if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
|
|
||||||
perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 ||
|
|
||||||
(alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0))
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
perf_evlist__free_stats(evlist);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_stat__reset_stats(struct perf_evlist *evlist)
|
|
||||||
{
|
|
||||||
struct perf_evsel *evsel;
|
|
||||||
|
|
||||||
evlist__for_each(evlist, evsel) {
|
|
||||||
perf_evsel__reset_stat_priv(evsel);
|
|
||||||
perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel));
|
|
||||||
}
|
|
||||||
|
|
||||||
perf_stat__reset_shadow_stats();
|
perf_stat__reset_shadow_stats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,8 +214,9 @@ static int check_per_pkg(struct perf_evsel *counter, int cpu, bool *skip)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused,
|
static int
|
||||||
struct perf_counts_values *count)
|
process_counter_values(struct perf_evsel *evsel, int cpu, int thread,
|
||||||
|
struct perf_counts_values *count)
|
||||||
{
|
{
|
||||||
struct perf_counts_values *aggr = &evsel->counts->aggr;
|
struct perf_counts_values *aggr = &evsel->counts->aggr;
|
||||||
static struct perf_counts_values zero;
|
static struct perf_counts_values zero;
|
||||||
|
@ -320,13 +231,13 @@ static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused,
|
||||||
count = &zero;
|
count = &zero;
|
||||||
|
|
||||||
switch (aggr_mode) {
|
switch (aggr_mode) {
|
||||||
|
case AGGR_THREAD:
|
||||||
case AGGR_CORE:
|
case AGGR_CORE:
|
||||||
case AGGR_SOCKET:
|
case AGGR_SOCKET:
|
||||||
case AGGR_NONE:
|
case AGGR_NONE:
|
||||||
if (!evsel->snapshot)
|
if (!evsel->snapshot)
|
||||||
perf_evsel__compute_deltas(evsel, cpu, count);
|
perf_evsel__compute_deltas(evsel, cpu, thread, count);
|
||||||
perf_counts_values__scale(count, scale, NULL);
|
perf_counts_values__scale(count, scale, NULL);
|
||||||
evsel->counts->cpu[cpu] = *count;
|
|
||||||
if (aggr_mode == AGGR_NONE)
|
if (aggr_mode == AGGR_NONE)
|
||||||
perf_stat__update_shadow_stats(evsel, count->values, cpu);
|
perf_stat__update_shadow_stats(evsel, count->values, cpu);
|
||||||
break;
|
break;
|
||||||
|
@ -343,26 +254,48 @@ static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_counter(struct perf_evsel *counter);
|
static int process_counter_maps(struct perf_evsel *counter)
|
||||||
|
{
|
||||||
|
int nthreads = thread_map__nr(counter->threads);
|
||||||
|
int ncpus = perf_evsel__nr_cpus(counter);
|
||||||
|
int cpu, thread;
|
||||||
|
|
||||||
/*
|
if (counter->system_wide)
|
||||||
* Read out the results of a single counter:
|
nthreads = 1;
|
||||||
* aggregate counts across CPUs in system-wide mode
|
|
||||||
*/
|
for (thread = 0; thread < nthreads; thread++) {
|
||||||
static int read_counter_aggr(struct perf_evsel *counter)
|
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||||
|
if (process_counter_values(counter, cpu, thread,
|
||||||
|
perf_counts(counter->counts, cpu, thread)))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_counter(struct perf_evsel *counter)
|
||||||
{
|
{
|
||||||
struct perf_counts_values *aggr = &counter->counts->aggr;
|
struct perf_counts_values *aggr = &counter->counts->aggr;
|
||||||
struct perf_stat *ps = counter->priv;
|
struct perf_stat *ps = counter->priv;
|
||||||
u64 *count = counter->counts->aggr.values;
|
u64 *count = counter->counts->aggr.values;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
aggr->val = aggr->ena = aggr->run = 0;
|
aggr->val = aggr->ena = aggr->run = 0;
|
||||||
|
init_stats(ps->res_stats);
|
||||||
|
|
||||||
if (read_counter(counter))
|
if (counter->per_pkg)
|
||||||
return -1;
|
zero_per_pkg(counter);
|
||||||
|
|
||||||
|
ret = process_counter_maps(counter);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (aggr_mode != AGGR_GLOBAL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!counter->snapshot)
|
if (!counter->snapshot)
|
||||||
perf_evsel__compute_deltas(counter, -1, aggr);
|
perf_evsel__compute_deltas(counter, -1, -1, aggr);
|
||||||
perf_counts_values__scale(aggr, scale, &counter->counts->scaled);
|
perf_counts_values__scale(aggr, scale, &counter->counts->scaled);
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
|
@ -397,12 +330,12 @@ static int read_counter(struct perf_evsel *counter)
|
||||||
if (counter->system_wide)
|
if (counter->system_wide)
|
||||||
nthreads = 1;
|
nthreads = 1;
|
||||||
|
|
||||||
if (counter->per_pkg)
|
|
||||||
zero_per_pkg(counter);
|
|
||||||
|
|
||||||
for (thread = 0; thread < nthreads; thread++) {
|
for (thread = 0; thread < nthreads; thread++) {
|
||||||
for (cpu = 0; cpu < ncpus; cpu++) {
|
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||||
if (perf_evsel__read_cb(counter, cpu, thread, read_cb))
|
struct perf_counts_values *count;
|
||||||
|
|
||||||
|
count = perf_counts(counter->counts, cpu, thread);
|
||||||
|
if (perf_evsel__read(counter, cpu, thread, count))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,68 +343,34 @@ static int read_counter(struct perf_evsel *counter)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_interval(void)
|
static void read_counters(bool close)
|
||||||
{
|
{
|
||||||
static int num_print_interval;
|
|
||||||
struct perf_evsel *counter;
|
struct perf_evsel *counter;
|
||||||
struct perf_stat *ps;
|
|
||||||
struct timespec ts, rs;
|
|
||||||
char prefix[64];
|
|
||||||
|
|
||||||
if (aggr_mode == AGGR_GLOBAL) {
|
evlist__for_each(evsel_list, counter) {
|
||||||
evlist__for_each(evsel_list, counter) {
|
if (read_counter(counter))
|
||||||
ps = counter->priv;
|
pr_warning("failed to read counter %s\n", counter->name);
|
||||||
memset(ps->res_stats, 0, sizeof(ps->res_stats));
|
|
||||||
read_counter_aggr(counter);
|
if (process_counter(counter))
|
||||||
}
|
pr_warning("failed to process counter %s\n", counter->name);
|
||||||
} else {
|
|
||||||
evlist__for_each(evsel_list, counter) {
|
if (close) {
|
||||||
ps = counter->priv;
|
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter),
|
||||||
memset(ps->res_stats, 0, sizeof(ps->res_stats));
|
thread_map__nr(evsel_list->threads));
|
||||||
read_counter(counter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_interval(void)
|
||||||
|
{
|
||||||
|
struct timespec ts, rs;
|
||||||
|
|
||||||
|
read_counters(false);
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
diff_timespec(&rs, &ts, &ref_time);
|
diff_timespec(&rs, &ts, &ref_time);
|
||||||
sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep);
|
|
||||||
|
|
||||||
if (num_print_interval == 0 && !csv_output) {
|
print_counters(&rs, 0, NULL);
|
||||||
switch (aggr_mode) {
|
|
||||||
case AGGR_SOCKET:
|
|
||||||
fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit");
|
|
||||||
break;
|
|
||||||
case AGGR_CORE:
|
|
||||||
fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit");
|
|
||||||
break;
|
|
||||||
case AGGR_NONE:
|
|
||||||
fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit");
|
|
||||||
break;
|
|
||||||
case AGGR_GLOBAL:
|
|
||||||
default:
|
|
||||||
fprintf(output, "# time counts %*s events\n", unit_width, "unit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++num_print_interval == 25)
|
|
||||||
num_print_interval = 0;
|
|
||||||
|
|
||||||
switch (aggr_mode) {
|
|
||||||
case AGGR_CORE:
|
|
||||||
case AGGR_SOCKET:
|
|
||||||
print_aggr(prefix);
|
|
||||||
break;
|
|
||||||
case AGGR_NONE:
|
|
||||||
evlist__for_each(evsel_list, counter)
|
|
||||||
print_counter(counter, prefix);
|
|
||||||
break;
|
|
||||||
case AGGR_GLOBAL:
|
|
||||||
default:
|
|
||||||
evlist__for_each(evsel_list, counter)
|
|
||||||
print_counter_aggr(counter, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
fflush(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_initial_delay(void)
|
static void handle_initial_delay(void)
|
||||||
|
@ -586,7 +485,7 @@ static int __run_perf_stat(int argc, const char **argv)
|
||||||
if (interval) {
|
if (interval) {
|
||||||
while (!waitpid(child_pid, &status, WNOHANG)) {
|
while (!waitpid(child_pid, &status, WNOHANG)) {
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
print_interval();
|
process_interval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wait(&status);
|
wait(&status);
|
||||||
|
@ -604,7 +503,7 @@ static int __run_perf_stat(int argc, const char **argv)
|
||||||
while (!done) {
|
while (!done) {
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
if (interval)
|
if (interval)
|
||||||
print_interval();
|
process_interval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,18 +511,7 @@ static int __run_perf_stat(int argc, const char **argv)
|
||||||
|
|
||||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||||
|
|
||||||
if (aggr_mode == AGGR_GLOBAL) {
|
read_counters(true);
|
||||||
evlist__for_each(evsel_list, counter) {
|
|
||||||
read_counter_aggr(counter);
|
|
||||||
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter),
|
|
||||||
thread_map__nr(evsel_list->threads));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
evlist__for_each(evsel_list, counter) {
|
|
||||||
read_counter(counter);
|
|
||||||
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WEXITSTATUS(status);
|
return WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
|
@ -715,6 +603,14 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
|
||||||
csv_output ? 0 : -4,
|
csv_output ? 0 : -4,
|
||||||
perf_evsel__cpus(evsel)->map[id], csv_sep);
|
perf_evsel__cpus(evsel)->map[id], csv_sep);
|
||||||
break;
|
break;
|
||||||
|
case AGGR_THREAD:
|
||||||
|
fprintf(output, "%*s-%*d%s",
|
||||||
|
csv_output ? 0 : 16,
|
||||||
|
thread_map__comm(evsel->threads, id),
|
||||||
|
csv_output ? 0 : -8,
|
||||||
|
thread_map__pid(evsel->threads, id),
|
||||||
|
csv_sep);
|
||||||
|
break;
|
||||||
case AGGR_GLOBAL:
|
case AGGR_GLOBAL:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -815,9 +711,9 @@ static void print_aggr(char *prefix)
|
||||||
s2 = aggr_get_id(evsel_list->cpus, cpu2);
|
s2 = aggr_get_id(evsel_list->cpus, cpu2);
|
||||||
if (s2 != id)
|
if (s2 != id)
|
||||||
continue;
|
continue;
|
||||||
val += counter->counts->cpu[cpu].val;
|
val += perf_counts(counter->counts, cpu, 0)->val;
|
||||||
ena += counter->counts->cpu[cpu].ena;
|
ena += perf_counts(counter->counts, cpu, 0)->ena;
|
||||||
run += counter->counts->cpu[cpu].run;
|
run += perf_counts(counter->counts, cpu, 0)->run;
|
||||||
nr++;
|
nr++;
|
||||||
}
|
}
|
||||||
if (prefix)
|
if (prefix)
|
||||||
|
@ -863,6 +759,40 @@ static void print_aggr(char *prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_aggr_thread(struct perf_evsel *counter, char *prefix)
|
||||||
|
{
|
||||||
|
int nthreads = thread_map__nr(counter->threads);
|
||||||
|
int ncpus = cpu_map__nr(counter->cpus);
|
||||||
|
int cpu, thread;
|
||||||
|
double uval;
|
||||||
|
|
||||||
|
for (thread = 0; thread < nthreads; thread++) {
|
||||||
|
u64 ena = 0, run = 0, val = 0;
|
||||||
|
|
||||||
|
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||||
|
val += perf_counts(counter->counts, cpu, thread)->val;
|
||||||
|
ena += perf_counts(counter->counts, cpu, thread)->ena;
|
||||||
|
run += perf_counts(counter->counts, cpu, thread)->run;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix)
|
||||||
|
fprintf(output, "%s", prefix);
|
||||||
|
|
||||||
|
uval = val * counter->scale;
|
||||||
|
|
||||||
|
if (nsec_counter(counter))
|
||||||
|
nsec_printout(thread, 0, counter, uval);
|
||||||
|
else
|
||||||
|
abs_printout(thread, 0, counter, uval);
|
||||||
|
|
||||||
|
if (!csv_output)
|
||||||
|
print_noise(counter, 1.0);
|
||||||
|
|
||||||
|
print_running(run, ena);
|
||||||
|
fputc('\n', output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print out the results of a single counter:
|
* Print out the results of a single counter:
|
||||||
* aggregated counts in system-wide mode
|
* aggregated counts in system-wide mode
|
||||||
|
@ -925,9 +855,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
|
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
|
||||||
val = counter->counts->cpu[cpu].val;
|
val = perf_counts(counter->counts, cpu, 0)->val;
|
||||||
ena = counter->counts->cpu[cpu].ena;
|
ena = perf_counts(counter->counts, cpu, 0)->ena;
|
||||||
run = counter->counts->cpu[cpu].run;
|
run = perf_counts(counter->counts, cpu, 0)->run;
|
||||||
|
|
||||||
if (prefix)
|
if (prefix)
|
||||||
fprintf(output, "%s", prefix);
|
fprintf(output, "%s", prefix);
|
||||||
|
@ -972,9 +902,38 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_stat(int argc, const char **argv)
|
static void print_interval(char *prefix, struct timespec *ts)
|
||||||
|
{
|
||||||
|
static int num_print_interval;
|
||||||
|
|
||||||
|
sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep);
|
||||||
|
|
||||||
|
if (num_print_interval == 0 && !csv_output) {
|
||||||
|
switch (aggr_mode) {
|
||||||
|
case AGGR_SOCKET:
|
||||||
|
fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit");
|
||||||
|
break;
|
||||||
|
case AGGR_CORE:
|
||||||
|
fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit");
|
||||||
|
break;
|
||||||
|
case AGGR_NONE:
|
||||||
|
fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit");
|
||||||
|
break;
|
||||||
|
case AGGR_THREAD:
|
||||||
|
fprintf(output, "# time comm-pid counts %*s events\n", unit_width, "unit");
|
||||||
|
break;
|
||||||
|
case AGGR_GLOBAL:
|
||||||
|
default:
|
||||||
|
fprintf(output, "# time counts %*s events\n", unit_width, "unit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++num_print_interval == 25)
|
||||||
|
num_print_interval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_header(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
struct perf_evsel *counter;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -1000,36 +959,57 @@ static void print_stat(int argc, const char **argv)
|
||||||
fprintf(output, " (%d runs)", run_count);
|
fprintf(output, " (%d runs)", run_count);
|
||||||
fprintf(output, ":\n\n");
|
fprintf(output, ":\n\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_footer(void)
|
||||||
|
{
|
||||||
|
if (!null_run)
|
||||||
|
fprintf(output, "\n");
|
||||||
|
fprintf(output, " %17.9f seconds time elapsed",
|
||||||
|
avg_stats(&walltime_nsecs_stats)/1e9);
|
||||||
|
if (run_count > 1) {
|
||||||
|
fprintf(output, " ");
|
||||||
|
print_noise_pct(stddev_stats(&walltime_nsecs_stats),
|
||||||
|
avg_stats(&walltime_nsecs_stats));
|
||||||
|
}
|
||||||
|
fprintf(output, "\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_counters(struct timespec *ts, int argc, const char **argv)
|
||||||
|
{
|
||||||
|
struct perf_evsel *counter;
|
||||||
|
char buf[64], *prefix = NULL;
|
||||||
|
|
||||||
|
if (interval)
|
||||||
|
print_interval(prefix = buf, ts);
|
||||||
|
else
|
||||||
|
print_header(argc, argv);
|
||||||
|
|
||||||
switch (aggr_mode) {
|
switch (aggr_mode) {
|
||||||
case AGGR_CORE:
|
case AGGR_CORE:
|
||||||
case AGGR_SOCKET:
|
case AGGR_SOCKET:
|
||||||
print_aggr(NULL);
|
print_aggr(prefix);
|
||||||
|
break;
|
||||||
|
case AGGR_THREAD:
|
||||||
|
evlist__for_each(evsel_list, counter)
|
||||||
|
print_aggr_thread(counter, prefix);
|
||||||
break;
|
break;
|
||||||
case AGGR_GLOBAL:
|
case AGGR_GLOBAL:
|
||||||
evlist__for_each(evsel_list, counter)
|
evlist__for_each(evsel_list, counter)
|
||||||
print_counter_aggr(counter, NULL);
|
print_counter_aggr(counter, prefix);
|
||||||
break;
|
break;
|
||||||
case AGGR_NONE:
|
case AGGR_NONE:
|
||||||
evlist__for_each(evsel_list, counter)
|
evlist__for_each(evsel_list, counter)
|
||||||
print_counter(counter, NULL);
|
print_counter(counter, prefix);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!csv_output) {
|
if (!interval && !csv_output)
|
||||||
if (!null_run)
|
print_footer();
|
||||||
fprintf(output, "\n");
|
|
||||||
fprintf(output, " %17.9f seconds time elapsed",
|
fflush(output);
|
||||||
avg_stats(&walltime_nsecs_stats)/1e9);
|
|
||||||
if (run_count > 1) {
|
|
||||||
fprintf(output, " ");
|
|
||||||
print_noise_pct(stddev_stats(&walltime_nsecs_stats),
|
|
||||||
avg_stats(&walltime_nsecs_stats));
|
|
||||||
}
|
|
||||||
fprintf(output, "\n\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static volatile int signr = -1;
|
static volatile int signr = -1;
|
||||||
|
@ -1101,6 +1081,7 @@ static int perf_stat_init_aggr_mode(void)
|
||||||
break;
|
break;
|
||||||
case AGGR_NONE:
|
case AGGR_NONE:
|
||||||
case AGGR_GLOBAL:
|
case AGGR_GLOBAL:
|
||||||
|
case AGGR_THREAD:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1325,6 +1306,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
"aggregate counts per processor socket", AGGR_SOCKET),
|
"aggregate counts per processor socket", AGGR_SOCKET),
|
||||||
OPT_SET_UINT(0, "per-core", &aggr_mode,
|
OPT_SET_UINT(0, "per-core", &aggr_mode,
|
||||||
"aggregate counts per physical processor core", AGGR_CORE),
|
"aggregate counts per physical processor core", AGGR_CORE),
|
||||||
|
OPT_SET_UINT(0, "per-thread", &aggr_mode,
|
||||||
|
"aggregate counts per thread", AGGR_THREAD),
|
||||||
OPT_UINTEGER('D', "delay", &initial_delay,
|
OPT_UINTEGER('D', "delay", &initial_delay,
|
||||||
"ms to wait before starting measurement after program start"),
|
"ms to wait before starting measurement after program start"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
|
@ -1416,8 +1399,19 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
run_count = 1;
|
run_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no_aggr, cgroup are for system-wide only */
|
if ((aggr_mode == AGGR_THREAD) && !target__has_task(&target)) {
|
||||||
if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) &&
|
fprintf(stderr, "The --per-thread option is only available "
|
||||||
|
"when monitoring via -p -t options.\n");
|
||||||
|
parse_options_usage(NULL, options, "p", 1);
|
||||||
|
parse_options_usage(NULL, options, "t", 1);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* no_aggr, cgroup are for system-wide only
|
||||||
|
* --per-thread is aggregated per thread, we dont mix it with cpu mode
|
||||||
|
*/
|
||||||
|
if (((aggr_mode != AGGR_GLOBAL && aggr_mode != AGGR_THREAD) || nr_cgroups) &&
|
||||||
!target__has_cpu(&target)) {
|
!target__has_cpu(&target)) {
|
||||||
fprintf(stderr, "both cgroup and no-aggregation "
|
fprintf(stderr, "both cgroup and no-aggregation "
|
||||||
"modes only available in system-wide mode\n");
|
"modes only available in system-wide mode\n");
|
||||||
|
@ -1445,6 +1439,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize thread_map with comm names,
|
||||||
|
* so we could print it out on output.
|
||||||
|
*/
|
||||||
|
if (aggr_mode == AGGR_THREAD)
|
||||||
|
thread_map__read_comms(evsel_list->threads);
|
||||||
|
|
||||||
if (interval && interval < 100) {
|
if (interval && interval < 100) {
|
||||||
pr_err("print interval must be >= 100ms\n");
|
pr_err("print interval must be >= 100ms\n");
|
||||||
parse_options_usage(stat_usage, options, "I", 1);
|
parse_options_usage(stat_usage, options, "I", 1);
|
||||||
|
@ -1478,13 +1480,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
|
|
||||||
status = run_perf_stat(argc, argv);
|
status = run_perf_stat(argc, argv);
|
||||||
if (forever && status != -1) {
|
if (forever && status != -1) {
|
||||||
print_stat(argc, argv);
|
print_counters(NULL, argc, argv);
|
||||||
perf_stat__reset_stats(evsel_list);
|
perf_stat__reset_stats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!forever && status != -1 && !interval)
|
if (!forever && status != -1 && !interval)
|
||||||
print_stat(argc, argv);
|
print_counters(NULL, argc, argv);
|
||||||
|
|
||||||
perf_evlist__free_stats(evsel_list);
|
perf_evlist__free_stats(evsel_list);
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -586,27 +586,9 @@ static void *display_thread_tui(void *arg)
|
||||||
hists->uid_filter_str = top->record_opts.target.uid_str;
|
hists->uid_filter_str = top->record_opts.target.uid_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
||||||
int key = perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
top->min_percent,
|
||||||
top->min_percent,
|
&top->session->header.env);
|
||||||
&top->session->header.env);
|
|
||||||
|
|
||||||
if (key != 'f')
|
|
||||||
break;
|
|
||||||
|
|
||||||
perf_evlist__toggle_enable(top->evlist);
|
|
||||||
/*
|
|
||||||
* No need to refresh, resort/decay histogram entries
|
|
||||||
* if we are not collecting samples:
|
|
||||||
*/
|
|
||||||
if (top->evlist->enabled) {
|
|
||||||
hbt.refresh = top->delay_secs;
|
|
||||||
help = "Press 'f' to disable the events or 'h' to see other hotkeys";
|
|
||||||
} else {
|
|
||||||
help = "Press 'f' again to re-enable the events";
|
|
||||||
hbt.refresh = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
done = 1;
|
done = 1;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -1617,6 +1617,34 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||||
return syscall__set_arg_fmts(sc);
|
return syscall__set_arg_fmts(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trace__validate_ev_qualifier(struct trace *trace)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct str_node *pos;
|
||||||
|
|
||||||
|
strlist__for_each(pos, trace->ev_qualifier) {
|
||||||
|
const char *sc = pos->s;
|
||||||
|
|
||||||
|
if (audit_name_to_syscall(sc, trace->audit.machine) < 0) {
|
||||||
|
if (err == 0) {
|
||||||
|
fputs("Error:\tInvalid syscall ", trace->output);
|
||||||
|
err = -EINVAL;
|
||||||
|
} else {
|
||||||
|
fputs(", ", trace->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(sc, trace->output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err < 0) {
|
||||||
|
fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'"
|
||||||
|
"\nHint:\tand: 'man syscalls'\n", trace->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* args is to be interpreted as a series of longs but we need to handle
|
* args is to be interpreted as a series of longs but we need to handle
|
||||||
* 8-byte unaligned accesses. args points to raw_data within the event
|
* 8-byte unaligned accesses. args points to raw_data within the event
|
||||||
|
@ -2325,7 +2353,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||||
*/
|
*/
|
||||||
if (trace->filter_pids.nr > 0)
|
if (trace->filter_pids.nr > 0)
|
||||||
err = perf_evlist__set_filter_pids(evlist, trace->filter_pids.nr, trace->filter_pids.entries);
|
err = perf_evlist__set_filter_pids(evlist, trace->filter_pids.nr, trace->filter_pids.entries);
|
||||||
else if (evlist->threads->map[0] == -1)
|
else if (thread_map__pid(evlist->threads, 0) == -1)
|
||||||
err = perf_evlist__set_filter_pid(evlist, getpid());
|
err = perf_evlist__set_filter_pid(evlist, getpid());
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
@ -2343,7 +2371,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||||
if (forks)
|
if (forks)
|
||||||
perf_evlist__start_workload(evlist);
|
perf_evlist__start_workload(evlist);
|
||||||
|
|
||||||
trace->multiple_threads = evlist->threads->map[0] == -1 ||
|
trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 ||
|
||||||
evlist->threads->nr > 1 ||
|
evlist->threads->nr > 1 ||
|
||||||
perf_evlist__first(evlist)->attr.inherit;
|
perf_evlist__first(evlist)->attr.inherit;
|
||||||
again:
|
again:
|
||||||
|
@ -2862,6 +2890,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = trace__validate_ev_qualifier(&trace);
|
||||||
|
if (err)
|
||||||
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = target__validate(&trace.opts.target);
|
err = target__validate(&trace.opts.target);
|
||||||
|
|
|
@ -11,9 +11,9 @@ ifneq ($(obj-perf),)
|
||||||
obj-perf := $(abspath $(obj-perf))/
|
obj-perf := $(abspath $(obj-perf))/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(shell echo -n > .config-detected)
|
$(shell echo -n > $(OUTPUT).config-detected)
|
||||||
detected = $(shell echo "$(1)=y" >> .config-detected)
|
detected = $(shell echo "$(1)=y" >> $(OUTPUT).config-detected)
|
||||||
detected_var = $(shell echo "$(1)=$($(1))" >> .config-detected)
|
detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
|
||||||
|
|
||||||
CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS)
|
CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS)
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ perf-y += code-reading.o
|
||||||
perf-y += sample-parsing.o
|
perf-y += sample-parsing.o
|
||||||
perf-y += parse-no-sample-id-all.o
|
perf-y += parse-no-sample-id-all.o
|
||||||
perf-y += kmod-path.o
|
perf-y += kmod-path.o
|
||||||
|
perf-y += thread-map.o
|
||||||
|
|
||||||
perf-$(CONFIG_X86) += perf-time-to-tsc.o
|
perf-$(CONFIG_X86) += perf-time-to-tsc.o
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,10 @@ static struct test {
|
||||||
.desc = "Test kmod_path__parse function",
|
.desc = "Test kmod_path__parse function",
|
||||||
.func = test__kmod_path__parse,
|
.func = test__kmod_path__parse,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.desc = "Test thread map",
|
||||||
|
.func = test__thread_map,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.func = NULL,
|
.func = NULL,
|
||||||
},
|
},
|
||||||
|
|
|
@ -545,8 +545,8 @@ out_err:
|
||||||
if (evlist) {
|
if (evlist) {
|
||||||
perf_evlist__delete(evlist);
|
perf_evlist__delete(evlist);
|
||||||
} else {
|
} else {
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
}
|
}
|
||||||
machines__destroy_kernel_maps(&machines);
|
machines__destroy_kernel_maps(&machines);
|
||||||
machine__delete_threads(machine);
|
machine__delete_threads(machine);
|
||||||
|
|
|
@ -144,8 +144,8 @@ out_err:
|
||||||
perf_evlist__disable(evlist);
|
perf_evlist__disable(evlist);
|
||||||
perf_evlist__delete(evlist);
|
perf_evlist__delete(evlist);
|
||||||
} else {
|
} else {
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
|
ifndef MK
|
||||||
|
ifeq ($(MAKECMDGOALS),)
|
||||||
|
# no target specified, trigger the whole suite
|
||||||
|
all:
|
||||||
|
@echo "Testing Makefile"; $(MAKE) -sf tests/make MK=Makefile
|
||||||
|
@echo "Testing Makefile.perf"; $(MAKE) -sf tests/make MK=Makefile.perf
|
||||||
|
else
|
||||||
|
# run only specific test over 'Makefile'
|
||||||
|
%:
|
||||||
|
@echo "Testing Makefile"; $(MAKE) -sf tests/make MK=Makefile $@
|
||||||
|
endif
|
||||||
|
else
|
||||||
PERF := .
|
PERF := .
|
||||||
MK := Makefile
|
|
||||||
|
|
||||||
include config/Makefile.arch
|
include config/Makefile.arch
|
||||||
|
|
||||||
|
@ -47,6 +58,7 @@ make_install_man := install-man
|
||||||
make_install_html := install-html
|
make_install_html := install-html
|
||||||
make_install_info := install-info
|
make_install_info := install-info
|
||||||
make_install_pdf := install-pdf
|
make_install_pdf := install-pdf
|
||||||
|
make_install_prefix := install prefix=/tmp/krava
|
||||||
make_static := LDFLAGS=-static
|
make_static := LDFLAGS=-static
|
||||||
|
|
||||||
# all the NO_* variable combined
|
# all the NO_* variable combined
|
||||||
|
@ -57,7 +69,12 @@ make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1
|
||||||
|
|
||||||
# $(run) contains all available tests
|
# $(run) contains all available tests
|
||||||
run := make_pure
|
run := make_pure
|
||||||
|
# Targets 'clean all' can be run together only through top level
|
||||||
|
# Makefile because we detect clean target in Makefile.perf and
|
||||||
|
# disable features detection
|
||||||
|
ifeq ($(MK),Makefile)
|
||||||
run += make_clean_all
|
run += make_clean_all
|
||||||
|
endif
|
||||||
run += make_python_perf_so
|
run += make_python_perf_so
|
||||||
run += make_debug
|
run += make_debug
|
||||||
run += make_no_libperl
|
run += make_no_libperl
|
||||||
|
@ -83,6 +100,7 @@ run += make_util_map_o
|
||||||
run += make_util_pmu_bison_o
|
run += make_util_pmu_bison_o
|
||||||
run += make_install
|
run += make_install
|
||||||
run += make_install_bin
|
run += make_install_bin
|
||||||
|
run += make_install_prefix
|
||||||
# FIXME 'install-*' commented out till they're fixed
|
# FIXME 'install-*' commented out till they're fixed
|
||||||
# run += make_install_doc
|
# run += make_install_doc
|
||||||
# run += make_install_man
|
# run += make_install_man
|
||||||
|
@ -157,6 +175,12 @@ test_make_install_O := $(call test_dest_files,$(installed_files_all))
|
||||||
test_make_install_bin := $(call test_dest_files,$(installed_files_bin))
|
test_make_install_bin := $(call test_dest_files,$(installed_files_bin))
|
||||||
test_make_install_bin_O := $(call test_dest_files,$(installed_files_bin))
|
test_make_install_bin_O := $(call test_dest_files,$(installed_files_bin))
|
||||||
|
|
||||||
|
# We prefix all installed files for make_install_prefix
|
||||||
|
# with '/tmp/krava' to match installed/prefix-ed files.
|
||||||
|
installed_files_all_prefix := $(addprefix /tmp/krava/,$(installed_files_all))
|
||||||
|
test_make_install_prefix := $(call test_dest_files,$(installed_files_all_prefix))
|
||||||
|
test_make_install_prefix_O := $(call test_dest_files,$(installed_files_all_prefix))
|
||||||
|
|
||||||
# FIXME nothing gets installed
|
# FIXME nothing gets installed
|
||||||
test_make_install_man := test -f $$TMP_DEST/share/man/man1/perf.1
|
test_make_install_man := test -f $$TMP_DEST/share/man/man1/perf.1
|
||||||
test_make_install_man_O := $(test_make_install_man)
|
test_make_install_man_O := $(test_make_install_man)
|
||||||
|
@ -226,13 +250,13 @@ tarpkg:
|
||||||
( eval $$cmd ) >> $@ 2>&1
|
( eval $$cmd ) >> $@ 2>&1
|
||||||
|
|
||||||
make_kernelsrc:
|
make_kernelsrc:
|
||||||
@echo " - make -C <kernelsrc> tools/perf"
|
@echo "- make -C <kernelsrc> tools/perf"
|
||||||
$(call clean); \
|
$(call clean); \
|
||||||
(make -C ../.. tools/perf) > $@ 2>&1 && \
|
(make -C ../.. tools/perf) > $@ 2>&1 && \
|
||||||
test -x perf && rm -f $@ || (cat $@ ; false)
|
test -x perf && rm -f $@ || (cat $@ ; false)
|
||||||
|
|
||||||
make_kernelsrc_tools:
|
make_kernelsrc_tools:
|
||||||
@echo " - make -C <kernelsrc>/tools perf"
|
@echo "- make -C <kernelsrc>/tools perf"
|
||||||
$(call clean); \
|
$(call clean); \
|
||||||
(make -C ../../tools perf) > $@ 2>&1 && \
|
(make -C ../../tools perf) > $@ 2>&1 && \
|
||||||
test -x perf && rm -f $@ || (cat $@ ; false)
|
test -x perf && rm -f $@ || (cat $@ ; false)
|
||||||
|
@ -244,3 +268,4 @@ out: $(run_O)
|
||||||
@echo OK
|
@echo OK
|
||||||
|
|
||||||
.PHONY: all $(run) $(run_O) tarpkg clean
|
.PHONY: all $(run) $(run_O) tarpkg clean
|
||||||
|
endif # ifndef MK
|
||||||
|
|
|
@ -140,8 +140,8 @@ out_delete_evlist:
|
||||||
cpus = NULL;
|
cpus = NULL;
|
||||||
threads = NULL;
|
threads = NULL;
|
||||||
out_free_cpus:
|
out_free_cpus:
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
out_free_threads:
|
out_free_threads:
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ static int synth_process(struct machine *machine)
|
||||||
perf_event__process,
|
perf_event__process,
|
||||||
machine, 0, 500);
|
machine, 0, 500);
|
||||||
|
|
||||||
thread_map__delete(map);
|
thread_map__put(map);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ int test__openat_syscall_event_on_all_cpus(void)
|
||||||
* we use the auto allocation it will allocate just for 1 cpu,
|
* we use the auto allocation it will allocate just for 1 cpu,
|
||||||
* as we start by cpu 0.
|
* as we start by cpu 0.
|
||||||
*/
|
*/
|
||||||
if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
|
if (perf_evsel__alloc_counts(evsel, cpus->nr, 1) < 0) {
|
||||||
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
||||||
goto out_close_fd;
|
goto out_close_fd;
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,9 @@ int test__openat_syscall_event_on_all_cpus(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = nr_openat_calls + cpu;
|
expected = nr_openat_calls + cpu;
|
||||||
if (evsel->counts->cpu[cpu].val != expected) {
|
if (perf_counts(evsel->counts, cpu, 0)->val != expected) {
|
||||||
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
|
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
|
||||||
expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
|
expected, cpus->map[cpu], perf_counts(evsel->counts, cpu, 0)->val);
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,6 @@ out_close_fd:
|
||||||
out_evsel_delete:
|
out_evsel_delete:
|
||||||
perf_evsel__delete(evsel);
|
perf_evsel__delete(evsel);
|
||||||
out_thread_map_delete:
|
out_thread_map_delete:
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ int test__syscall_openat_tp_fields(void)
|
||||||
|
|
||||||
perf_evsel__config(evsel, &opts);
|
perf_evsel__config(evsel, &opts);
|
||||||
|
|
||||||
evlist->threads->map[0] = getpid();
|
thread_map__set_pid(evlist->threads, 0, getpid());
|
||||||
|
|
||||||
err = perf_evlist__open(evlist);
|
err = perf_evlist__open(evlist);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
|
@ -44,9 +44,9 @@ int test__openat_syscall_event(void)
|
||||||
goto out_close_fd;
|
goto out_close_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evsel->counts->cpu[0].val != nr_openat_calls) {
|
if (perf_counts(evsel->counts, 0, 0)->val != nr_openat_calls) {
|
||||||
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
|
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
|
||||||
nr_openat_calls, evsel->counts->cpu[0].val);
|
nr_openat_calls, perf_counts(evsel->counts, 0, 0)->val);
|
||||||
goto out_close_fd;
|
goto out_close_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,6 @@ out_close_fd:
|
||||||
out_evsel_delete:
|
out_evsel_delete:
|
||||||
perf_evsel__delete(evsel);
|
perf_evsel__delete(evsel);
|
||||||
out_thread_map_delete:
|
out_thread_map_delete:
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -560,8 +560,8 @@ out:
|
||||||
perf_evlist__disable(evlist);
|
perf_evlist__disable(evlist);
|
||||||
perf_evlist__delete(evlist);
|
perf_evlist__delete(evlist);
|
||||||
} else {
|
} else {
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
thread_map__delete(threads);
|
thread_map__put(threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -61,6 +61,7 @@ int test__switch_tracking(void);
|
||||||
int test__fdarray__filter(void);
|
int test__fdarray__filter(void);
|
||||||
int test__fdarray__add(void);
|
int test__fdarray__add(void);
|
||||||
int test__kmod_path__parse(void);
|
int test__kmod_path__parse(void);
|
||||||
|
int test__thread_map(void);
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
|
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
|
||||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "tests.h"
|
||||||
|
#include "thread_map.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
int test__thread_map(void)
|
||||||
|
{
|
||||||
|
struct thread_map *map;
|
||||||
|
|
||||||
|
/* test map on current pid */
|
||||||
|
map = thread_map__new_by_pid(getpid());
|
||||||
|
TEST_ASSERT_VAL("failed to alloc map", map);
|
||||||
|
|
||||||
|
thread_map__read_comms(map);
|
||||||
|
|
||||||
|
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
|
||||||
|
TEST_ASSERT_VAL("wrong pid",
|
||||||
|
thread_map__pid(map, 0) == getpid());
|
||||||
|
TEST_ASSERT_VAL("wrong comm",
|
||||||
|
thread_map__comm(map, 0) &&
|
||||||
|
!strcmp(thread_map__comm(map, 0), "perf"));
|
||||||
|
thread_map__put(map);
|
||||||
|
|
||||||
|
/* test dummy pid */
|
||||||
|
map = thread_map__new_dummy();
|
||||||
|
TEST_ASSERT_VAL("failed to alloc map", map);
|
||||||
|
|
||||||
|
thread_map__read_comms(map);
|
||||||
|
|
||||||
|
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
|
||||||
|
TEST_ASSERT_VAL("wrong pid", thread_map__pid(map, 0) == -1);
|
||||||
|
TEST_ASSERT_VAL("wrong comm",
|
||||||
|
thread_map__comm(map, 0) &&
|
||||||
|
!strcmp(thread_map__comm(map, 0), "dummy"));
|
||||||
|
thread_map__put(map);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1902,8 +1902,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||||
case CTRL('c'):
|
case CTRL('c'):
|
||||||
goto out_free_stack;
|
goto out_free_stack;
|
||||||
case 'f':
|
case 'f':
|
||||||
if (!is_report_browser(hbt))
|
if (!is_report_browser(hbt)) {
|
||||||
goto out_free_stack;
|
struct perf_top *top = hbt->arg;
|
||||||
|
|
||||||
|
perf_evlist__toggle_enable(top->evlist);
|
||||||
|
/*
|
||||||
|
* No need to refresh, resort/decay histogram
|
||||||
|
* entries if we are not collecting samples:
|
||||||
|
*/
|
||||||
|
if (top->evlist->enabled) {
|
||||||
|
helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
|
||||||
|
hbt->refresh = delay_secs;
|
||||||
|
} else {
|
||||||
|
helpline = "Press 'f' again to re-enable the events";
|
||||||
|
hbt->refresh = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
/* Fall thru */
|
/* Fall thru */
|
||||||
default:
|
default:
|
||||||
helpline = "Press '?' for help on key bindings";
|
helpline = "Press '?' for help on key bindings";
|
||||||
|
|
|
@ -119,12 +119,12 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
|
||||||
if (per_cpu) {
|
if (per_cpu) {
|
||||||
mp->cpu = evlist->cpus->map[idx];
|
mp->cpu = evlist->cpus->map[idx];
|
||||||
if (evlist->threads)
|
if (evlist->threads)
|
||||||
mp->tid = evlist->threads->map[0];
|
mp->tid = thread_map__pid(evlist->threads, 0);
|
||||||
else
|
else
|
||||||
mp->tid = -1;
|
mp->tid = -1;
|
||||||
} else {
|
} else {
|
||||||
mp->cpu = -1;
|
mp->cpu = -1;
|
||||||
mp->tid = evlist->threads->map[idx];
|
mp->tid = thread_map__pid(evlist->threads, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1182,6 +1182,13 @@ static int __auxtrace_mmap__read(struct auxtrace_mmap *mm,
|
||||||
data2 = NULL;
|
data2 = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (itr->alignment) {
|
||||||
|
unsigned int unwanted = len1 % itr->alignment;
|
||||||
|
|
||||||
|
len1 -= unwanted;
|
||||||
|
size -= unwanted;
|
||||||
|
}
|
||||||
|
|
||||||
/* padding must be written by fn() e.g. record__process_auxtrace() */
|
/* padding must be written by fn() e.g. record__process_auxtrace() */
|
||||||
padding = size & 7;
|
padding = size & 7;
|
||||||
if (padding)
|
if (padding)
|
||||||
|
|
|
@ -303,6 +303,7 @@ struct auxtrace_record {
|
||||||
const char *str);
|
const char *str);
|
||||||
u64 (*reference)(struct auxtrace_record *itr);
|
u64 (*reference)(struct auxtrace_record *itr);
|
||||||
int (*read_finish)(struct auxtrace_record *itr, int idx);
|
int (*read_finish)(struct auxtrace_record *itr, int idx);
|
||||||
|
unsigned int alignment;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_AUXTRACE_SUPPORT
|
#ifdef HAVE_AUXTRACE_SUPPORT
|
||||||
|
|
|
@ -7,11 +7,15 @@
|
||||||
|
|
||||||
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
|
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
|
||||||
|
|
||||||
|
#ifdef __GLIBC_PREREQ
|
||||||
|
#if !__GLIBC_PREREQ(2, 6)
|
||||||
int __weak sched_getcpu(void)
|
int __weak sched_getcpu(void)
|
||||||
{
|
{
|
||||||
errno = ENOSYS;
|
errno = ENOSYS;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static int perf_flag_probe(void)
|
static int perf_flag_probe(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "asm/bug.h"
|
||||||
|
|
||||||
static struct cpu_map *cpu_map__default_new(void)
|
static struct cpu_map *cpu_map__default_new(void)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +23,7 @@ static struct cpu_map *cpu_map__default_new(void)
|
||||||
cpus->map[i] = i;
|
cpus->map[i] = i;
|
||||||
|
|
||||||
cpus->nr = nr_cpus;
|
cpus->nr = nr_cpus;
|
||||||
|
atomic_set(&cpus->refcnt, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpus;
|
return cpus;
|
||||||
|
@ -35,6 +37,7 @@ static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
|
||||||
if (cpus != NULL) {
|
if (cpus != NULL) {
|
||||||
cpus->nr = nr_cpus;
|
cpus->nr = nr_cpus;
|
||||||
memcpy(cpus->map, tmp_cpus, payload_size);
|
memcpy(cpus->map, tmp_cpus, payload_size);
|
||||||
|
atomic_set(&cpus->refcnt, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpus;
|
return cpus;
|
||||||
|
@ -194,14 +197,32 @@ struct cpu_map *cpu_map__dummy_new(void)
|
||||||
if (cpus != NULL) {
|
if (cpus != NULL) {
|
||||||
cpus->nr = 1;
|
cpus->nr = 1;
|
||||||
cpus->map[0] = -1;
|
cpus->map[0] = -1;
|
||||||
|
atomic_set(&cpus->refcnt, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpus;
|
return cpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_map__delete(struct cpu_map *map)
|
static void cpu_map__delete(struct cpu_map *map)
|
||||||
{
|
{
|
||||||
free(map);
|
if (map) {
|
||||||
|
WARN_ONCE(atomic_read(&map->refcnt) != 0,
|
||||||
|
"cpu_map refcnt unbalanced\n");
|
||||||
|
free(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cpu_map *cpu_map__get(struct cpu_map *map)
|
||||||
|
{
|
||||||
|
if (map)
|
||||||
|
atomic_inc(&map->refcnt);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_map__put(struct cpu_map *map)
|
||||||
|
{
|
||||||
|
if (map && atomic_dec_and_test(&map->refcnt))
|
||||||
|
cpu_map__delete(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cpu_map__get_socket(struct cpu_map *map, int idx)
|
int cpu_map__get_socket(struct cpu_map *map, int idx)
|
||||||
|
@ -263,6 +284,7 @@ static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
|
||||||
/* ensure we process id in increasing order */
|
/* ensure we process id in increasing order */
|
||||||
qsort(c->map, c->nr, sizeof(int), cmp_ids);
|
qsort(c->map, c->nr, sizeof(int), cmp_ids);
|
||||||
|
|
||||||
|
atomic_set(&cpus->refcnt, 1);
|
||||||
*res = c;
|
*res = c;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,19 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <linux/atomic.h>
|
||||||
|
|
||||||
#include "perf.h"
|
#include "perf.h"
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
|
|
||||||
struct cpu_map {
|
struct cpu_map {
|
||||||
|
atomic_t refcnt;
|
||||||
int nr;
|
int nr;
|
||||||
int map[];
|
int map[];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cpu_map *cpu_map__new(const char *cpu_list);
|
struct cpu_map *cpu_map__new(const char *cpu_list);
|
||||||
struct cpu_map *cpu_map__dummy_new(void);
|
struct cpu_map *cpu_map__dummy_new(void);
|
||||||
void cpu_map__delete(struct cpu_map *map);
|
|
||||||
struct cpu_map *cpu_map__read(FILE *file);
|
struct cpu_map *cpu_map__read(FILE *file);
|
||||||
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
||||||
int cpu_map__get_socket(struct cpu_map *map, int idx);
|
int cpu_map__get_socket(struct cpu_map *map, int idx);
|
||||||
|
@ -22,6 +23,9 @@ int cpu_map__get_core(struct cpu_map *map, int idx);
|
||||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
||||||
int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
|
int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
|
||||||
|
|
||||||
|
struct cpu_map *cpu_map__get(struct cpu_map *map);
|
||||||
|
void cpu_map__put(struct cpu_map *map);
|
||||||
|
|
||||||
static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
||||||
{
|
{
|
||||||
if (!sock || s > sock->nr || s < 0)
|
if (!sock || s > sock->nr || s < 0)
|
||||||
|
|
|
@ -504,7 +504,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||||
for (thread = 0; thread < threads->nr; ++thread) {
|
for (thread = 0; thread < threads->nr; ++thread) {
|
||||||
if (__event__synthesize_thread(comm_event, mmap_event,
|
if (__event__synthesize_thread(comm_event, mmap_event,
|
||||||
fork_event,
|
fork_event,
|
||||||
threads->map[thread], 0,
|
thread_map__pid(threads, thread), 0,
|
||||||
process, tool, machine,
|
process, tool, machine,
|
||||||
mmap_data, proc_map_timeout)) {
|
mmap_data, proc_map_timeout)) {
|
||||||
err = -1;
|
err = -1;
|
||||||
|
@ -515,12 +515,12 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||||
* comm.pid is set to thread group id by
|
* comm.pid is set to thread group id by
|
||||||
* perf_event__synthesize_comm
|
* perf_event__synthesize_comm
|
||||||
*/
|
*/
|
||||||
if ((int) comm_event->comm.pid != threads->map[thread]) {
|
if ((int) comm_event->comm.pid != thread_map__pid(threads, thread)) {
|
||||||
bool need_leader = true;
|
bool need_leader = true;
|
||||||
|
|
||||||
/* is thread group leader in thread_map? */
|
/* is thread group leader in thread_map? */
|
||||||
for (j = 0; j < threads->nr; ++j) {
|
for (j = 0; j < threads->nr; ++j) {
|
||||||
if ((int) comm_event->comm.pid == threads->map[j]) {
|
if ((int) comm_event->comm.pid == thread_map__pid(threads, j)) {
|
||||||
need_leader = false;
|
need_leader = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,8 +114,8 @@ void perf_evlist__delete(struct perf_evlist *evlist)
|
||||||
{
|
{
|
||||||
perf_evlist__munmap(evlist);
|
perf_evlist__munmap(evlist);
|
||||||
perf_evlist__close(evlist);
|
perf_evlist__close(evlist);
|
||||||
cpu_map__delete(evlist->cpus);
|
cpu_map__put(evlist->cpus);
|
||||||
thread_map__delete(evlist->threads);
|
thread_map__put(evlist->threads);
|
||||||
evlist->cpus = NULL;
|
evlist->cpus = NULL;
|
||||||
evlist->threads = NULL;
|
evlist->threads = NULL;
|
||||||
perf_evlist__purge(evlist);
|
perf_evlist__purge(evlist);
|
||||||
|
@ -548,7 +548,7 @@ static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
|
||||||
else
|
else
|
||||||
sid->cpu = -1;
|
sid->cpu = -1;
|
||||||
if (!evsel->system_wide && evlist->threads && thread >= 0)
|
if (!evsel->system_wide && evlist->threads && thread >= 0)
|
||||||
sid->tid = evlist->threads->map[thread];
|
sid->tid = thread_map__pid(evlist->threads, thread);
|
||||||
else
|
else
|
||||||
sid->tid = -1;
|
sid->tid = -1;
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1101,31 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||||
return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false);
|
return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int perf_evlist__propagate_maps(struct perf_evlist *evlist,
|
||||||
|
struct target *target)
|
||||||
|
{
|
||||||
|
struct perf_evsel *evsel;
|
||||||
|
|
||||||
|
evlist__for_each(evlist, evsel) {
|
||||||
|
/*
|
||||||
|
* We already have cpus for evsel (via PMU sysfs) so
|
||||||
|
* keep it, if there's no target cpu list defined.
|
||||||
|
*/
|
||||||
|
if (evsel->cpus && target->cpu_list)
|
||||||
|
cpu_map__put(evsel->cpus);
|
||||||
|
|
||||||
|
if (!evsel->cpus || target->cpu_list)
|
||||||
|
evsel->cpus = cpu_map__get(evlist->cpus);
|
||||||
|
|
||||||
|
evsel->threads = thread_map__get(evlist->threads);
|
||||||
|
|
||||||
|
if (!evsel->cpus || !evsel->threads)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
|
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
|
||||||
{
|
{
|
||||||
evlist->threads = thread_map__new_str(target->pid, target->tid,
|
evlist->threads = thread_map__new_str(target->pid, target->tid,
|
||||||
|
@ -1117,10 +1142,10 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
|
||||||
if (evlist->cpus == NULL)
|
if (evlist->cpus == NULL)
|
||||||
goto out_delete_threads;
|
goto out_delete_threads;
|
||||||
|
|
||||||
return 0;
|
return perf_evlist__propagate_maps(evlist, target);
|
||||||
|
|
||||||
out_delete_threads:
|
out_delete_threads:
|
||||||
thread_map__delete(evlist->threads);
|
thread_map__put(evlist->threads);
|
||||||
evlist->threads = NULL;
|
evlist->threads = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1353,7 +1378,7 @@ static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
out_free_cpus:
|
out_free_cpus:
|
||||||
cpu_map__delete(evlist->cpus);
|
cpu_map__put(evlist->cpus);
|
||||||
evlist->cpus = NULL;
|
evlist->cpus = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1475,7 +1500,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
|
||||||
__func__, __LINE__);
|
__func__, __LINE__);
|
||||||
goto out_close_pipes;
|
goto out_close_pipes;
|
||||||
}
|
}
|
||||||
evlist->threads->map[0] = evlist->workload.pid;
|
thread_map__set_pid(evlist->threads, 0, evlist->workload.pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(child_ready_pipe[1]);
|
close(child_ready_pipe[1]);
|
||||||
|
|
|
@ -289,5 +289,4 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
|
||||||
|
|
||||||
void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
|
void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
|
||||||
struct perf_evsel *tracking_evsel);
|
struct perf_evsel *tracking_evsel);
|
||||||
|
|
||||||
#endif /* __PERF_EVLIST_H */
|
#endif /* __PERF_EVLIST_H */
|
||||||
|
|
|
@ -885,6 +885,8 @@ void perf_evsel__exit(struct perf_evsel *evsel)
|
||||||
perf_evsel__free_fd(evsel);
|
perf_evsel__free_fd(evsel);
|
||||||
perf_evsel__free_id(evsel);
|
perf_evsel__free_id(evsel);
|
||||||
close_cgroup(evsel->cgrp);
|
close_cgroup(evsel->cgrp);
|
||||||
|
cpu_map__put(evsel->cpus);
|
||||||
|
thread_map__put(evsel->threads);
|
||||||
zfree(&evsel->group_name);
|
zfree(&evsel->group_name);
|
||||||
zfree(&evsel->name);
|
zfree(&evsel->name);
|
||||||
perf_evsel__object.fini(evsel);
|
perf_evsel__object.fini(evsel);
|
||||||
|
@ -896,7 +898,7 @@ void perf_evsel__delete(struct perf_evsel *evsel)
|
||||||
free(evsel);
|
free(evsel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu,
|
void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread,
|
||||||
struct perf_counts_values *count)
|
struct perf_counts_values *count)
|
||||||
{
|
{
|
||||||
struct perf_counts_values tmp;
|
struct perf_counts_values tmp;
|
||||||
|
@ -908,8 +910,8 @@ void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu,
|
||||||
tmp = evsel->prev_raw_counts->aggr;
|
tmp = evsel->prev_raw_counts->aggr;
|
||||||
evsel->prev_raw_counts->aggr = *count;
|
evsel->prev_raw_counts->aggr = *count;
|
||||||
} else {
|
} else {
|
||||||
tmp = evsel->prev_raw_counts->cpu[cpu];
|
tmp = *perf_counts(evsel->prev_raw_counts, cpu, thread);
|
||||||
evsel->prev_raw_counts->cpu[cpu] = *count;
|
*perf_counts(evsel->prev_raw_counts, cpu, thread) = *count;
|
||||||
}
|
}
|
||||||
|
|
||||||
count->val = count->val - tmp.val;
|
count->val = count->val - tmp.val;
|
||||||
|
@ -937,20 +939,18 @@ void perf_counts_values__scale(struct perf_counts_values *count,
|
||||||
*pscaled = scaled;
|
*pscaled = scaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread,
|
int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
|
||||||
perf_evsel__read_cb_t cb)
|
struct perf_counts_values *count)
|
||||||
{
|
{
|
||||||
struct perf_counts_values count;
|
memset(count, 0, sizeof(*count));
|
||||||
|
|
||||||
memset(&count, 0, sizeof(count));
|
|
||||||
|
|
||||||
if (FD(evsel, cpu, thread) < 0)
|
if (FD(evsel, cpu, thread) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (readn(FD(evsel, cpu, thread), &count, sizeof(count)) < 0)
|
if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
return cb(evsel, cpu, thread, &count);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
||||||
|
@ -962,15 +962,15 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
||||||
if (FD(evsel, cpu, thread) < 0)
|
if (FD(evsel, cpu, thread) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
|
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
|
if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
perf_evsel__compute_deltas(evsel, cpu, &count);
|
perf_evsel__compute_deltas(evsel, cpu, thread, &count);
|
||||||
perf_counts_values__scale(&count, scale, NULL);
|
perf_counts_values__scale(&count, scale, NULL);
|
||||||
evsel->counts->cpu[cpu] = count;
|
*perf_counts(evsel->counts, cpu, thread) = count;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,7 +1167,7 @@ retry_sample_id:
|
||||||
int group_fd;
|
int group_fd;
|
||||||
|
|
||||||
if (!evsel->cgrp && !evsel->system_wide)
|
if (!evsel->cgrp && !evsel->system_wide)
|
||||||
pid = threads->map[thread];
|
pid = thread_map__pid(threads, thread);
|
||||||
|
|
||||||
group_fd = get_group_fd(evsel, cpu, thread);
|
group_fd = get_group_fd(evsel, cpu, thread);
|
||||||
retry_open:
|
retry_open:
|
||||||
|
|
|
@ -8,23 +8,8 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "xyarray.h"
|
#include "xyarray.h"
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
|
#include "cpumap.h"
|
||||||
struct perf_counts_values {
|
#include "stat.h"
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
u64 val;
|
|
||||||
u64 ena;
|
|
||||||
u64 run;
|
|
||||||
};
|
|
||||||
u64 values[3];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct perf_counts {
|
|
||||||
s8 scaled;
|
|
||||||
struct perf_counts_values aggr;
|
|
||||||
struct perf_counts_values cpu[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct perf_evsel;
|
struct perf_evsel;
|
||||||
|
|
||||||
|
@ -82,6 +67,7 @@ struct perf_evsel {
|
||||||
struct cgroup_sel *cgrp;
|
struct cgroup_sel *cgrp;
|
||||||
void *handler;
|
void *handler;
|
||||||
struct cpu_map *cpus;
|
struct cpu_map *cpus;
|
||||||
|
struct thread_map *threads;
|
||||||
unsigned int sample_size;
|
unsigned int sample_size;
|
||||||
int id_pos;
|
int id_pos;
|
||||||
int is_pos;
|
int is_pos;
|
||||||
|
@ -113,10 +99,20 @@ struct thread_map;
|
||||||
struct perf_evlist;
|
struct perf_evlist;
|
||||||
struct record_opts;
|
struct record_opts;
|
||||||
|
|
||||||
|
static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
return evsel->cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
return perf_evsel__cpus(evsel)->nr;
|
||||||
|
}
|
||||||
|
|
||||||
void perf_counts_values__scale(struct perf_counts_values *count,
|
void perf_counts_values__scale(struct perf_counts_values *count,
|
||||||
bool scale, s8 *pscaled);
|
bool scale, s8 *pscaled);
|
||||||
|
|
||||||
void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu,
|
void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread,
|
||||||
struct perf_counts_values *count);
|
struct perf_counts_values *count);
|
||||||
|
|
||||||
int perf_evsel__object_config(size_t object_size,
|
int perf_evsel__object_config(size_t object_size,
|
||||||
|
@ -233,12 +229,8 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1,
|
||||||
(a)->attr.type == (b)->attr.type && \
|
(a)->attr.type == (b)->attr.type && \
|
||||||
(a)->attr.config == (b)->attr.config)
|
(a)->attr.config == (b)->attr.config)
|
||||||
|
|
||||||
typedef int (perf_evsel__read_cb_t)(struct perf_evsel *evsel,
|
int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
|
||||||
int cpu, int thread,
|
struct perf_counts_values *count);
|
||||||
struct perf_counts_values *count);
|
|
||||||
|
|
||||||
int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread,
|
|
||||||
perf_evsel__read_cb_t cb);
|
|
||||||
|
|
||||||
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
||||||
int cpu, int thread, bool scale);
|
int cpu, int thread, bool scale);
|
||||||
|
|
|
@ -1063,8 +1063,7 @@ out:
|
||||||
free(buf);
|
free(buf);
|
||||||
return events;
|
return events;
|
||||||
error:
|
error:
|
||||||
if (events)
|
free_event_desc(events);
|
||||||
free_event_desc(events);
|
|
||||||
events = NULL;
|
events = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1448,10 +1448,9 @@ int machine__process_event(struct machine *machine, union perf_event *event,
|
||||||
case PERF_RECORD_AUX:
|
case PERF_RECORD_AUX:
|
||||||
ret = machine__process_aux_event(machine, event); break;
|
ret = machine__process_aux_event(machine, event); break;
|
||||||
case PERF_RECORD_ITRACE_START:
|
case PERF_RECORD_ITRACE_START:
|
||||||
ret = machine__process_itrace_start_event(machine, event);
|
ret = machine__process_itrace_start_event(machine, event); break;
|
||||||
case PERF_RECORD_LOST_SAMPLES:
|
case PERF_RECORD_LOST_SAMPLES:
|
||||||
ret = machine__process_lost_samples_event(machine, event, sample); break;
|
ret = machine__process_lost_samples_event(machine, event, sample); break;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "parse-events-flex.h"
|
#include "parse-events-flex.h"
|
||||||
#include "pmu.h"
|
#include "pmu.h"
|
||||||
#include "thread_map.h"
|
#include "thread_map.h"
|
||||||
|
#include "cpumap.h"
|
||||||
#include "asm/bug.h"
|
#include "asm/bug.h"
|
||||||
|
|
||||||
#define MAX_NAME_LEN 100
|
#define MAX_NAME_LEN 100
|
||||||
|
@ -285,7 +286,9 @@ __add_event(struct list_head *list, int *idx,
|
||||||
if (!evsel)
|
if (!evsel)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
evsel->cpus = cpus;
|
if (cpus)
|
||||||
|
evsel->cpus = cpu_map__get(cpus);
|
||||||
|
|
||||||
if (name)
|
if (name)
|
||||||
evsel->name = strdup(name);
|
evsel->name = strdup(name);
|
||||||
list_add_tail(&evsel->node, list);
|
list_add_tail(&evsel->node, list);
|
||||||
|
|
|
@ -119,8 +119,8 @@ event [^,{}/]+
|
||||||
num_dec [0-9]+
|
num_dec [0-9]+
|
||||||
num_hex 0x[a-fA-F0-9]+
|
num_hex 0x[a-fA-F0-9]+
|
||||||
num_raw_hex [a-fA-F0-9]+
|
num_raw_hex [a-fA-F0-9]+
|
||||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
|
||||||
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
|
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
|
||||||
/* If you add a modifier you need to update check_modifier() */
|
/* If you add a modifier you need to update check_modifier() */
|
||||||
modifier_event [ukhpGHSDI]+
|
modifier_event [ukhpGHSDI]+
|
||||||
modifier_bp [rwx]{1,3}
|
modifier_bp [rwx]{1,3}
|
||||||
|
@ -165,7 +165,6 @@ modifier_bp [rwx]{1,3}
|
||||||
return PE_EVENT_NAME;
|
return PE_EVENT_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
. |
|
|
||||||
<<EOF>> {
|
<<EOF>> {
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
REWIND(0);
|
REWIND(0);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -205,17 +206,12 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
|
static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
|
||||||
|
char *desc __maybe_unused, char *val)
|
||||||
{
|
{
|
||||||
struct perf_pmu_alias *alias;
|
struct perf_pmu_alias *alias;
|
||||||
char buf[256];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = fread(buf, 1, sizeof(buf), file);
|
|
||||||
if (ret == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
buf[ret] = 0;
|
|
||||||
|
|
||||||
alias = malloc(sizeof(*alias));
|
alias = malloc(sizeof(*alias));
|
||||||
if (!alias)
|
if (!alias)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -225,26 +221,43 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
|
||||||
alias->unit[0] = '\0';
|
alias->unit[0] = '\0';
|
||||||
alias->per_pkg = false;
|
alias->per_pkg = false;
|
||||||
|
|
||||||
ret = parse_events_terms(&alias->terms, buf);
|
ret = parse_events_terms(&alias->terms, val);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
pr_err("Cannot parse alias %s: %d\n", val, ret);
|
||||||
free(alias);
|
free(alias);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias->name = strdup(name);
|
alias->name = strdup(name);
|
||||||
/*
|
if (dir) {
|
||||||
* load unit name and scale if available
|
/*
|
||||||
*/
|
* load unit name and scale if available
|
||||||
perf_pmu__parse_unit(alias, dir, name);
|
*/
|
||||||
perf_pmu__parse_scale(alias, dir, name);
|
perf_pmu__parse_unit(alias, dir, name);
|
||||||
perf_pmu__parse_per_pkg(alias, dir, name);
|
perf_pmu__parse_scale(alias, dir, name);
|
||||||
perf_pmu__parse_snapshot(alias, dir, name);
|
perf_pmu__parse_per_pkg(alias, dir, name);
|
||||||
|
perf_pmu__parse_snapshot(alias, dir, name);
|
||||||
|
}
|
||||||
|
|
||||||
list_add_tail(&alias->list, list);
|
list_add_tail(&alias->list, list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = fread(buf, 1, sizeof(buf), file);
|
||||||
|
if (ret == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
buf[ret] = 0;
|
||||||
|
|
||||||
|
return __perf_pmu__new_alias(list, dir, name, NULL, buf);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool pmu_alias_info_file(char *name)
|
static inline bool pmu_alias_info_file(char *name)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -436,7 +449,7 @@ static struct cpu_map *pmu_cpumask(const char *name)
|
||||||
return cpus;
|
return cpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perf_event_attr *__attribute__((weak))
|
struct perf_event_attr * __weak
|
||||||
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
|
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -249,8 +249,12 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
|
||||||
static bool kprobe_blacklist__listed(unsigned long address);
|
static bool kprobe_blacklist__listed(unsigned long address);
|
||||||
static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
|
static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
|
||||||
{
|
{
|
||||||
|
u64 etext_addr;
|
||||||
|
|
||||||
/* Get the address of _etext for checking non-probable text symbol */
|
/* Get the address of _etext for checking non-probable text symbol */
|
||||||
if (kernel_get_symbol_address_by_name("_etext", false) < address)
|
etext_addr = kernel_get_symbol_address_by_name("_etext", false);
|
||||||
|
|
||||||
|
if (etext_addr != 0 && etext_addr < address)
|
||||||
pr_warning("%s is out of .text, skip it.\n", symbol);
|
pr_warning("%s is out of .text, skip it.\n", symbol);
|
||||||
else if (kprobe_blacklist__listed(address))
|
else if (kprobe_blacklist__listed(address))
|
||||||
pr_warning("%s is blacklisted function, skip it.\n", symbol);
|
pr_warning("%s is blacklisted function, skip it.\n", symbol);
|
||||||
|
|
|
@ -20,3 +20,4 @@ util/stat.c
|
||||||
util/strlist.c
|
util/strlist.c
|
||||||
util/trace-event.c
|
util/trace-event.c
|
||||||
../../lib/rbtree.c
|
../../lib/rbtree.c
|
||||||
|
util/string.c
|
||||||
|
|
|
@ -384,7 +384,7 @@ static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus,
|
||||||
|
|
||||||
static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus)
|
static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus)
|
||||||
{
|
{
|
||||||
cpu_map__delete(pcpus->cpus);
|
cpu_map__put(pcpus->cpus);
|
||||||
pcpus->ob_type->tp_free((PyObject*)pcpus);
|
pcpus->ob_type->tp_free((PyObject*)pcpus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,
|
||||||
|
|
||||||
static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads)
|
static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads)
|
||||||
{
|
{
|
||||||
thread_map__delete(pthreads->threads);
|
thread_map__put(pthreads->threads);
|
||||||
pthreads->ob_type->tp_free((PyObject*)pthreads);
|
pthreads->ob_type->tp_free((PyObject*)pthreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ static bool perf_probe_api(setup_probe_fn_t fn)
|
||||||
if (!cpus)
|
if (!cpus)
|
||||||
return false;
|
return false;
|
||||||
cpu = cpus->map[0];
|
cpu = cpus->map[0];
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ret = perf_do_probe_api(fn, cpu, try[i++]);
|
ret = perf_do_probe_api(fn, cpu, try[i++]);
|
||||||
|
@ -226,7 +226,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
|
||||||
struct cpu_map *cpus = cpu_map__new(NULL);
|
struct cpu_map *cpus = cpu_map__new(NULL);
|
||||||
|
|
||||||
cpu = cpus ? cpus->map[0] : 0;
|
cpu = cpus ? cpus->map[0] : 0;
|
||||||
cpu_map__delete(cpus);
|
cpu_map__put(cpus);
|
||||||
} else {
|
} else {
|
||||||
cpu = evlist->cpus->map[0];
|
cpu = evlist->cpus->map[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -686,6 +686,8 @@ static int process_finished_round(struct perf_tool *tool __maybe_unused,
|
||||||
union perf_event *event __maybe_unused,
|
union perf_event *event __maybe_unused,
|
||||||
struct ordered_events *oe)
|
struct ordered_events *oe)
|
||||||
{
|
{
|
||||||
|
if (dump_trace)
|
||||||
|
fprintf(stdout, "\n");
|
||||||
return ordered_events__flush(oe, OE_FLUSH__ROUND);
|
return ordered_events__flush(oe, OE_FLUSH__ROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1726,7 +1728,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
|
||||||
if (perf_header__has_feat(&session->header, HEADER_AUXTRACE))
|
if (perf_header__has_feat(&session->header, HEADER_AUXTRACE))
|
||||||
msg = " (excludes AUX area (e.g. instruction trace) decoded / synthesized events)";
|
msg = " (excludes AUX area (e.g. instruction trace) decoded / synthesized events)";
|
||||||
|
|
||||||
ret = fprintf(fp, "Aggregated stats:%s\n", msg);
|
ret = fprintf(fp, "\nAggregated stats:%s\n", msg);
|
||||||
|
|
||||||
ret += events_stats__fprintf(&session->evlist->stats, fp);
|
ret += events_stats__fprintf(&session->evlist->stats, fp);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1893,7 +1895,7 @@ int perf_session__cpu_bitmap(struct perf_session *session,
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
out_delete_map:
|
out_delete_map:
|
||||||
cpu_map__delete(map);
|
cpu_map__put(map);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "stat.h"
|
#include "stat.h"
|
||||||
|
#include "evlist.h"
|
||||||
#include "evsel.h"
|
#include "evsel.h"
|
||||||
|
#include "thread_map.h"
|
||||||
|
|
||||||
void update_stats(struct stats *stats, u64 val)
|
void update_stats(struct stats *stats, u64 val)
|
||||||
{
|
{
|
||||||
|
@ -95,33 +97,46 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perf_counts *perf_counts__new(int ncpus)
|
struct perf_counts *perf_counts__new(int ncpus, int nthreads)
|
||||||
{
|
{
|
||||||
int size = sizeof(struct perf_counts) +
|
struct perf_counts *counts = zalloc(sizeof(*counts));
|
||||||
ncpus * sizeof(struct perf_counts_values);
|
|
||||||
|
|
||||||
return zalloc(size);
|
if (counts) {
|
||||||
|
struct xyarray *values;
|
||||||
|
|
||||||
|
values = xyarray__new(ncpus, nthreads, sizeof(struct perf_counts_values));
|
||||||
|
if (!values) {
|
||||||
|
free(counts);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
counts->values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void perf_counts__delete(struct perf_counts *counts)
|
void perf_counts__delete(struct perf_counts *counts)
|
||||||
{
|
{
|
||||||
free(counts);
|
if (counts) {
|
||||||
|
xyarray__delete(counts->values);
|
||||||
|
free(counts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perf_counts__reset(struct perf_counts *counts, int ncpus)
|
static void perf_counts__reset(struct perf_counts *counts)
|
||||||
{
|
{
|
||||||
memset(counts, 0, (sizeof(*counts) +
|
xyarray__reset(counts->values);
|
||||||
(ncpus * sizeof(struct perf_counts_values))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
|
void perf_evsel__reset_counts(struct perf_evsel *evsel)
|
||||||
{
|
{
|
||||||
perf_counts__reset(evsel->counts, ncpus);
|
perf_counts__reset(evsel->counts);
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
|
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||||
{
|
{
|
||||||
evsel->counts = perf_counts__new(ncpus);
|
evsel->counts = perf_counts__new(ncpus, nthreads);
|
||||||
return evsel->counts != NULL ? 0 : -ENOMEM;
|
return evsel->counts != NULL ? 0 : -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,3 +145,96 @@ void perf_evsel__free_counts(struct perf_evsel *evsel)
|
||||||
perf_counts__delete(evsel->counts);
|
perf_counts__delete(evsel->counts);
|
||||||
evsel->counts = NULL;
|
evsel->counts = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct perf_stat *ps = evsel->priv;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
init_stats(&ps->res_stats[i]);
|
||||||
|
|
||||||
|
perf_stat_evsel_id_init(evsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
evsel->priv = zalloc(sizeof(struct perf_stat));
|
||||||
|
if (evsel->priv == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
perf_evsel__reset_stat_priv(evsel);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
zfree(&evsel->priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
|
||||||
|
int ncpus, int nthreads)
|
||||||
|
{
|
||||||
|
struct perf_counts *counts;
|
||||||
|
|
||||||
|
counts = perf_counts__new(ncpus, nthreads);
|
||||||
|
if (counts)
|
||||||
|
evsel->prev_raw_counts = counts;
|
||||||
|
|
||||||
|
return counts ? 0 : -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
|
||||||
|
{
|
||||||
|
perf_counts__delete(evsel->prev_raw_counts);
|
||||||
|
evsel->prev_raw_counts = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw)
|
||||||
|
{
|
||||||
|
int ncpus = perf_evsel__nr_cpus(evsel);
|
||||||
|
int nthreads = thread_map__nr(evsel->threads);
|
||||||
|
|
||||||
|
if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
|
||||||
|
perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 ||
|
||||||
|
(alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel, ncpus, nthreads) < 0))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
|
||||||
|
{
|
||||||
|
struct perf_evsel *evsel;
|
||||||
|
|
||||||
|
evlist__for_each(evlist, evsel) {
|
||||||
|
if (perf_evsel__alloc_stats(evsel, alloc_raw))
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
perf_evlist__free_stats(evlist);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_evlist__free_stats(struct perf_evlist *evlist)
|
||||||
|
{
|
||||||
|
struct perf_evsel *evsel;
|
||||||
|
|
||||||
|
evlist__for_each(evlist, evsel) {
|
||||||
|
perf_evsel__free_stat_priv(evsel);
|
||||||
|
perf_evsel__free_counts(evsel);
|
||||||
|
perf_evsel__free_prev_raw_counts(evsel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_evlist__reset_stats(struct perf_evlist *evlist)
|
||||||
|
{
|
||||||
|
struct perf_evsel *evsel;
|
||||||
|
|
||||||
|
evlist__for_each(evlist, evsel) {
|
||||||
|
perf_evsel__reset_stat_priv(evsel);
|
||||||
|
perf_evsel__reset_counts(evsel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "xyarray.h"
|
||||||
|
|
||||||
struct stats
|
struct stats
|
||||||
{
|
{
|
||||||
|
@ -29,8 +30,32 @@ enum aggr_mode {
|
||||||
AGGR_GLOBAL,
|
AGGR_GLOBAL,
|
||||||
AGGR_SOCKET,
|
AGGR_SOCKET,
|
||||||
AGGR_CORE,
|
AGGR_CORE,
|
||||||
|
AGGR_THREAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct perf_counts_values {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u64 val;
|
||||||
|
u64 ena;
|
||||||
|
u64 run;
|
||||||
|
};
|
||||||
|
u64 values[3];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct perf_counts {
|
||||||
|
s8 scaled;
|
||||||
|
struct perf_counts_values aggr;
|
||||||
|
struct xyarray *values;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct perf_counts_values*
|
||||||
|
perf_counts(struct perf_counts *counts, int cpu, int thread)
|
||||||
|
{
|
||||||
|
return xyarray__entry(counts->values, cpu, thread);
|
||||||
|
}
|
||||||
|
|
||||||
void update_stats(struct stats *stats, u64 val);
|
void update_stats(struct stats *stats, u64 val);
|
||||||
double avg_stats(struct stats *stats);
|
double avg_stats(struct stats *stats);
|
||||||
double stddev_stats(struct stats *stats);
|
double stddev_stats(struct stats *stats);
|
||||||
|
@ -46,6 +71,8 @@ static inline void init_stats(struct stats *stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perf_evsel;
|
struct perf_evsel;
|
||||||
|
struct perf_evlist;
|
||||||
|
|
||||||
bool __perf_evsel_stat__is(struct perf_evsel *evsel,
|
bool __perf_evsel_stat__is(struct perf_evsel *evsel,
|
||||||
enum perf_stat_evsel_id id);
|
enum perf_stat_evsel_id id);
|
||||||
|
|
||||||
|
@ -62,10 +89,24 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count,
|
||||||
void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
|
void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
|
||||||
double avg, int cpu, enum aggr_mode aggr);
|
double avg, int cpu, enum aggr_mode aggr);
|
||||||
|
|
||||||
struct perf_counts *perf_counts__new(int ncpus);
|
struct perf_counts *perf_counts__new(int ncpus, int nthreads);
|
||||||
void perf_counts__delete(struct perf_counts *counts);
|
void perf_counts__delete(struct perf_counts *counts);
|
||||||
|
|
||||||
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
|
void perf_evsel__reset_counts(struct perf_evsel *evsel);
|
||||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
|
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||||
void perf_evsel__free_counts(struct perf_evsel *evsel);
|
void perf_evsel__free_counts(struct perf_evsel *evsel);
|
||||||
|
|
||||||
|
void perf_evsel__reset_stat_priv(struct perf_evsel *evsel);
|
||||||
|
int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel);
|
||||||
|
void perf_evsel__free_stat_priv(struct perf_evsel *evsel);
|
||||||
|
|
||||||
|
int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
|
||||||
|
int ncpus, int nthreads);
|
||||||
|
void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel);
|
||||||
|
|
||||||
|
int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw);
|
||||||
|
|
||||||
|
int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw);
|
||||||
|
void perf_evlist__free_stats(struct perf_evlist *evlist);
|
||||||
|
void perf_evlist__reset_stats(struct perf_evlist *evlist);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -748,7 +748,7 @@ static int str_to_bitmap(char *s, cpumask_t *b)
|
||||||
set_bit(c, cpumask_bits(b));
|
set_bit(c, cpumask_bits(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_map__delete(m);
|
cpu_map__put(m);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1132,8 +1132,11 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
|
||||||
INIT_LIST_HEAD(&md.maps);
|
INIT_LIST_HEAD(&md.maps);
|
||||||
|
|
||||||
fd = open(kcore_filename, O_RDONLY);
|
fd = open(kcore_filename, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0) {
|
||||||
|
pr_err("%s requires CAP_SYS_RAWIO capability to access.\n",
|
||||||
|
kcore_filename);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read new maps into temporary lists */
|
/* Read new maps into temporary lists */
|
||||||
err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md,
|
err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md,
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "strlist.h"
|
#include "strlist.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <api/fs/fs.h>
|
||||||
|
#include "asm/bug.h"
|
||||||
#include "thread_map.h"
|
#include "thread_map.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
/* Skip "." and ".." directories */
|
/* Skip "." and ".." directories */
|
||||||
static int filter(const struct dirent *dir)
|
static int filter(const struct dirent *dir)
|
||||||
|
@ -20,11 +23,26 @@ static int filter(const struct dirent *dir)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void thread_map__reset(struct thread_map *map, int start, int nr)
|
||||||
|
{
|
||||||
|
size_t size = (nr - start) * sizeof(map->map[0]);
|
||||||
|
|
||||||
|
memset(&map->map[start], 0, size);
|
||||||
|
}
|
||||||
|
|
||||||
static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
|
static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
|
||||||
{
|
{
|
||||||
size_t size = sizeof(*map) + sizeof(pid_t) * nr;
|
size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
|
||||||
|
int start = map ? map->nr : 0;
|
||||||
|
|
||||||
return realloc(map, size);
|
map = realloc(map, size);
|
||||||
|
/*
|
||||||
|
* We only realloc to add more items, let's reset new items.
|
||||||
|
*/
|
||||||
|
if (map)
|
||||||
|
thread_map__reset(map, start, nr);
|
||||||
|
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
|
#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
|
||||||
|
@ -45,8 +63,9 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
|
||||||
threads = thread_map__alloc(items);
|
threads = thread_map__alloc(items);
|
||||||
if (threads != NULL) {
|
if (threads != NULL) {
|
||||||
for (i = 0; i < items; i++)
|
for (i = 0; i < items; i++)
|
||||||
threads->map[i] = atoi(namelist[i]->d_name);
|
thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
|
||||||
threads->nr = items;
|
threads->nr = items;
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<items; i++)
|
for (i=0; i<items; i++)
|
||||||
|
@ -61,8 +80,9 @@ struct thread_map *thread_map__new_by_tid(pid_t tid)
|
||||||
struct thread_map *threads = thread_map__alloc(1);
|
struct thread_map *threads = thread_map__alloc(1);
|
||||||
|
|
||||||
if (threads != NULL) {
|
if (threads != NULL) {
|
||||||
threads->map[0] = tid;
|
thread_map__set_pid(threads, 0, tid);
|
||||||
threads->nr = 1;
|
threads->nr = 1;
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return threads;
|
return threads;
|
||||||
|
@ -84,6 +104,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
|
||||||
goto out_free_threads;
|
goto out_free_threads;
|
||||||
|
|
||||||
threads->nr = 0;
|
threads->nr = 0;
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
|
|
||||||
while (!readdir_r(proc, &dirent, &next) && next) {
|
while (!readdir_r(proc, &dirent, &next) && next) {
|
||||||
char *end;
|
char *end;
|
||||||
|
@ -123,8 +144,10 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
|
||||||
threads = tmp;
|
threads = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < items; i++)
|
for (i = 0; i < items; i++) {
|
||||||
threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
|
thread_map__set_pid(threads, threads->nr + i,
|
||||||
|
atoi(namelist[i]->d_name));
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < items; i++)
|
for (i = 0; i < items; i++)
|
||||||
zfree(&namelist[i]);
|
zfree(&namelist[i]);
|
||||||
|
@ -201,7 +224,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
|
||||||
threads = nt;
|
threads = nt;
|
||||||
|
|
||||||
for (i = 0; i < items; i++) {
|
for (i = 0; i < items; i++) {
|
||||||
threads->map[j++] = atoi(namelist[i]->d_name);
|
thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
|
||||||
zfree(&namelist[i]);
|
zfree(&namelist[i]);
|
||||||
}
|
}
|
||||||
threads->nr = total_tasks;
|
threads->nr = total_tasks;
|
||||||
|
@ -210,6 +233,8 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
strlist__delete(slist);
|
strlist__delete(slist);
|
||||||
|
if (threads)
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
return threads;
|
return threads;
|
||||||
|
|
||||||
out_free_namelist:
|
out_free_namelist:
|
||||||
|
@ -227,8 +252,9 @@ struct thread_map *thread_map__new_dummy(void)
|
||||||
struct thread_map *threads = thread_map__alloc(1);
|
struct thread_map *threads = thread_map__alloc(1);
|
||||||
|
|
||||||
if (threads != NULL) {
|
if (threads != NULL) {
|
||||||
threads->map[0] = -1;
|
thread_map__set_pid(threads, 0, -1);
|
||||||
threads->nr = 1;
|
threads->nr = 1;
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
}
|
}
|
||||||
return threads;
|
return threads;
|
||||||
}
|
}
|
||||||
|
@ -267,10 +293,12 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
|
||||||
goto out_free_threads;
|
goto out_free_threads;
|
||||||
|
|
||||||
threads = nt;
|
threads = nt;
|
||||||
threads->map[ntasks - 1] = tid;
|
thread_map__set_pid(threads, ntasks - 1, tid);
|
||||||
threads->nr = ntasks;
|
threads->nr = ntasks;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (threads)
|
||||||
|
atomic_set(&threads->refcnt, 1);
|
||||||
return threads;
|
return threads;
|
||||||
|
|
||||||
out_free_threads:
|
out_free_threads:
|
||||||
|
@ -290,9 +318,30 @@ struct thread_map *thread_map__new_str(const char *pid, const char *tid,
|
||||||
return thread_map__new_by_tid_str(tid);
|
return thread_map__new_by_tid_str(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_map__delete(struct thread_map *threads)
|
static void thread_map__delete(struct thread_map *threads)
|
||||||
{
|
{
|
||||||
free(threads);
|
if (threads) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
WARN_ONCE(atomic_read(&threads->refcnt) != 0,
|
||||||
|
"thread map refcnt unbalanced\n");
|
||||||
|
for (i = 0; i < threads->nr; i++)
|
||||||
|
free(thread_map__comm(threads, i));
|
||||||
|
free(threads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread_map *thread_map__get(struct thread_map *map)
|
||||||
|
{
|
||||||
|
if (map)
|
||||||
|
atomic_inc(&map->refcnt);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_map__put(struct thread_map *map)
|
||||||
|
{
|
||||||
|
if (map && atomic_dec_and_test(&map->refcnt))
|
||||||
|
thread_map__delete(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
|
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
|
||||||
|
@ -301,7 +350,60 @@ size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
|
||||||
size_t printed = fprintf(fp, "%d thread%s: ",
|
size_t printed = fprintf(fp, "%d thread%s: ",
|
||||||
threads->nr, threads->nr > 1 ? "s" : "");
|
threads->nr, threads->nr > 1 ? "s" : "");
|
||||||
for (i = 0; i < threads->nr; ++i)
|
for (i = 0; i < threads->nr; ++i)
|
||||||
printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
|
printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
|
||||||
|
|
||||||
return printed + fprintf(fp, "\n");
|
return printed + fprintf(fp, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_comm(char **comm, pid_t pid)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
size_t size;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
err = filename__read_str(path, comm, &size);
|
||||||
|
if (!err) {
|
||||||
|
/*
|
||||||
|
* We're reading 16 bytes, while filename__read_str
|
||||||
|
* allocates data per BUFSIZ bytes, so we can safely
|
||||||
|
* mark the end of the string.
|
||||||
|
*/
|
||||||
|
(*comm)[size] = 0;
|
||||||
|
rtrim(*comm);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void comm_init(struct thread_map *map, int i)
|
||||||
|
{
|
||||||
|
pid_t pid = thread_map__pid(map, i);
|
||||||
|
char *comm = NULL;
|
||||||
|
|
||||||
|
/* dummy pid comm initialization */
|
||||||
|
if (pid == -1) {
|
||||||
|
map->map[i].comm = strdup("dummy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The comm name is like extra bonus ;-),
|
||||||
|
* so just warn if we fail for any reason.
|
||||||
|
*/
|
||||||
|
if (get_comm(&comm, pid))
|
||||||
|
pr_warning("Couldn't resolve comm name for pid %d\n", pid);
|
||||||
|
|
||||||
|
map->map[i].comm = comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_map__read_comms(struct thread_map *threads)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < threads->nr; ++i)
|
||||||
|
comm_init(threads, i);
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,17 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <linux/atomic.h>
|
||||||
|
|
||||||
|
struct thread_map_data {
|
||||||
|
pid_t pid;
|
||||||
|
char *comm;
|
||||||
|
};
|
||||||
|
|
||||||
struct thread_map {
|
struct thread_map {
|
||||||
|
atomic_t refcnt;
|
||||||
int nr;
|
int nr;
|
||||||
pid_t map[];
|
struct thread_map_data map[];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct thread_map *thread_map__new_dummy(void);
|
struct thread_map *thread_map__new_dummy(void);
|
||||||
|
@ -15,11 +22,12 @@ struct thread_map *thread_map__new_by_tid(pid_t tid);
|
||||||
struct thread_map *thread_map__new_by_uid(uid_t uid);
|
struct thread_map *thread_map__new_by_uid(uid_t uid);
|
||||||
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
|
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
|
||||||
|
|
||||||
|
struct thread_map *thread_map__get(struct thread_map *map);
|
||||||
|
void thread_map__put(struct thread_map *map);
|
||||||
|
|
||||||
struct thread_map *thread_map__new_str(const char *pid,
|
struct thread_map *thread_map__new_str(const char *pid,
|
||||||
const char *tid, uid_t uid);
|
const char *tid, uid_t uid);
|
||||||
|
|
||||||
void thread_map__delete(struct thread_map *threads);
|
|
||||||
|
|
||||||
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
|
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
|
||||||
|
|
||||||
static inline int thread_map__nr(struct thread_map *threads)
|
static inline int thread_map__nr(struct thread_map *threads)
|
||||||
|
@ -27,4 +35,21 @@ static inline int thread_map__nr(struct thread_map *threads)
|
||||||
return threads ? threads->nr : 1;
|
return threads ? threads->nr : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline pid_t thread_map__pid(struct thread_map *map, int thread)
|
||||||
|
{
|
||||||
|
return map->map[thread].pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
thread_map__set_pid(struct thread_map *map, int thread, pid_t pid)
|
||||||
|
{
|
||||||
|
map->map[thread].pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *thread_map__comm(struct thread_map *map, int thread)
|
||||||
|
{
|
||||||
|
return map->map[thread].comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_map__read_comms(struct thread_map *threads);
|
||||||
#endif /* __PERF_THREAD_MAP_H */
|
#endif /* __PERF_THREAD_MAP_H */
|
||||||
|
|
Loading…
Reference in New Issue