perf/core improvements and fixes
. UAPI fixes, from David Howels . Separate perf tests into multiple objects, one per test, from Jiri Olsa. . Fixes to /proc/pid/maps parsing, preparatory to supporting data maps, from Namhyung Kim . Fix compile error for non-NEWT builds, from Namhyung Kim . Implement ui_progress for GTK, from Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQqO+jAAoJENZQFvNTUqpATGIP/juZF47Al1+4So5KfdOTNPKD X9H23T31HrcK4z0Yk4upbi0qWW1do7RkoJOdwVbvMndYK8Mh0QEmZcmcJg7hzhJq v86/qGAbnWUi8H5g8VVhGCFRBbkdN2U6tKzBe3KzmgQnjq48/2EYsi4AlnfoyWoX u1VuBd1K3dQAvl4TqypxXeB84HJQ/nMtDtT6YMuMGfAPP4aCvCjEX365rgux8bGW nCA/ERppfvfj7QG/Z3Da/nI/qlHYPYjyZBuaj/X5qvF3GzPAcVQ5Qs5JlkQ9daiO RS3rR7eDfx7DTidTobIagfxgj0gYoY3/1gZVrLLAB1ByUJcpehbBgKxN6PvEvJFh FXfrXrZ8AtnxMBBN0Wnkqwm0z40RGe65bu0JqU8YroTVUUH+ZvO6U0WvDH4KVcpL iMJTWIpcpsgPWFEieW6rArSdYuuvAivrNxm2Sl1RNguOlxftHT4ZCTvxVPEwrOl9 PWnoJhc5YxTfdaoJ8xHQaD7TyYj6RtycZw9hhBYD5mdDwpzBZdccJ2vgMlkBgeuL VOHCG0NxJVH6myexhfjrFqbJljAo024/nkqwTm3JHJr3WJv0qqOIFbmb8SA2kCpP QXnLCrfUk/MXdU69paWGMpFyG6+iuSDhyecy0iX/+xqV3EGOdVxVM1x/mxfBSC2p xU/aCDZvX0+YYv5797UK =9k43 -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: - UAPI fixes, from David Howels - Separate perf tests into multiple objects, one per test, from Jiri Olsa. - Fixes to /proc/pid/maps parsing, preparatory to supporting data maps, from Namhyung Kim - Fix compile error for non-NEWT builds, from Namhyung Kim - Implement ui_progress for GTK, from Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
adc1ef1e37
6
Makefile
6
Makefile
@ -1321,10 +1321,12 @@ kernelversion:
|
||||
|
||||
# Clear a bunch of variables before executing the submake
|
||||
tools/: FORCE
|
||||
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS= -C $(src)/tools/
|
||||
$(Q)mkdir -p $(objtree)/tools
|
||||
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS= O=$(objtree) subdir=tools -C $(src)/tools/
|
||||
|
||||
tools/%: FORCE
|
||||
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS= -C $(src)/tools/ $*
|
||||
$(Q)mkdir -p $(objtree)/tools
|
||||
$(Q)$(MAKE) LDFLAGS= MAKEFLAGS= O=$(objtree) subdir=tools -C $(src)/tools/ $*
|
||||
|
||||
# Single targets
|
||||
# ---------------------------------------------------------------------------
|
||||
|
@ -31,44 +31,44 @@ help:
|
||||
@echo ' clean: a summary clean target to clean _all_ folders'
|
||||
|
||||
cpupower: FORCE
|
||||
$(QUIET_SUBDIR0)power/$@/ $(QUIET_SUBDIR1)
|
||||
$(call descend,power/$@)
|
||||
|
||||
firewire lguest perf usb virtio vm: FORCE
|
||||
$(QUIET_SUBDIR0)$@/ $(QUIET_SUBDIR1)
|
||||
$(call descend,$@)
|
||||
|
||||
selftests: FORCE
|
||||
$(QUIET_SUBDIR0)testing/$@/ $(QUIET_SUBDIR1)
|
||||
$(call descend,testing/$@)
|
||||
|
||||
turbostat x86_energy_perf_policy: FORCE
|
||||
$(QUIET_SUBDIR0)power/x86/$@/ $(QUIET_SUBDIR1)
|
||||
$(call descend,power/x86/$@)
|
||||
|
||||
cpupower_install:
|
||||
$(QUIET_SUBDIR0)power/$(@:_install=)/ $(QUIET_SUBDIR1) install
|
||||
$(call descend,power/$(@:_install=),install)
|
||||
|
||||
firewire_install lguest_install perf_install usb_install virtio_install vm_install:
|
||||
$(QUIET_SUBDIR0)$(@:_install=)/ $(QUIET_SUBDIR1) install
|
||||
$(call descend,$(@:_install=),install)
|
||||
|
||||
selftests_install:
|
||||
$(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) install
|
||||
$(call descend,testing/$(@:_clean=),install)
|
||||
|
||||
turbostat_install x86_energy_perf_policy_install:
|
||||
$(QUIET_SUBDIR0)power/x86/$(@:_install=)/ $(QUIET_SUBDIR1) install
|
||||
$(call descend,power/x86/$(@:_install=),install)
|
||||
|
||||
install: cpupower_install firewire_install lguest_install perf_install \
|
||||
selftests_install turbostat_install usb_install virtio_install \
|
||||
vm_install x86_energy_perf_policy_install
|
||||
|
||||
cpupower_clean:
|
||||
$(QUIET_SUBDIR0)power/cpupower/ $(QUIET_SUBDIR1) clean
|
||||
$(call descend,power/cpupower,clean)
|
||||
|
||||
firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean:
|
||||
$(QUIET_SUBDIR0)$(@:_clean=)/ $(QUIET_SUBDIR1) clean
|
||||
$(call descend,$(@:_clean=),clean)
|
||||
|
||||
selftests_clean:
|
||||
$(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) clean
|
||||
$(call descend,testing/$(@:_clean=),clean)
|
||||
|
||||
turbostat_clean x86_energy_perf_policy_clean:
|
||||
$(QUIET_SUBDIR0)power/x86/$(@:_clean=)/ $(QUIET_SUBDIR1) clean
|
||||
$(call descend,power/x86/$(@:_clean=),clean)
|
||||
|
||||
clean: cpupower_clean firewire_clean lguest_clean perf_clean selftests_clean \
|
||||
turbostat_clean usb_clean virtio_clean vm_clean \
|
||||
|
@ -422,7 +422,9 @@ LIB_OBJS += $(OUTPUT)util/intlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/vdso.o
|
||||
LIB_OBJS += $(OUTPUT)util/stat.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/hist.o
|
||||
LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
|
||||
|
||||
@ -431,6 +433,17 @@ LIB_OBJS += $(OUTPUT)arch/common.o
|
||||
LIB_OBJS += $(OUTPUT)tests/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)tests/dso-data.o
|
||||
LIB_OBJS += $(OUTPUT)tests/attr.o
|
||||
LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o
|
||||
LIB_OBJS += $(OUTPUT)tests/mmap-basic.o
|
||||
LIB_OBJS += $(OUTPUT)tests/perf-record.o
|
||||
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
|
||||
LIB_OBJS += $(OUTPUT)tests/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)tests/util.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
@ -600,17 +613,16 @@ ifndef NO_NEWT
|
||||
BASIC_CFLAGS += -I/usr/include/slang
|
||||
BASIC_CFLAGS += -DNEWT_SUPPORT
|
||||
EXTLIBS += -lnewt -lslang
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/map.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/progress.o
|
||||
LIB_H += ui/browser.h
|
||||
LIB_H += ui/browsers/map.h
|
||||
LIB_H += ui/keysyms.h
|
||||
@ -636,9 +648,9 @@ ifndef NO_GTK2
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/progress.o
|
||||
# Make sure that it'd be included only once.
|
||||
ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
endif
|
||||
endif
|
||||
|
@ -230,11 +230,15 @@ static int perf_record__open(struct perf_record *rec)
|
||||
struct perf_record_opts *opts = &rec->opts;
|
||||
int rc = 0;
|
||||
|
||||
perf_evlist__config_attrs(evlist, opts);
|
||||
|
||||
/*
|
||||
* Set the evsel leader links before we configure attributes,
|
||||
* since some might depend on this info.
|
||||
*/
|
||||
if (opts->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
|
||||
perf_evlist__config_attrs(evlist, opts);
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &pos->attr;
|
||||
/*
|
||||
@ -498,6 +502,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
struct perf_evlist *evsel_list = rec->evlist;
|
||||
const char *output_name = rec->output_name;
|
||||
struct perf_session *session;
|
||||
bool disabled = false;
|
||||
|
||||
rec->progname = argv[0];
|
||||
|
||||
@ -697,7 +702,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
perf_evlist__enable(evsel_list);
|
||||
/*
|
||||
* When perf is starting the traced process, all the events
|
||||
* (apart from group members) have enable_on_exec=1 set,
|
||||
* so don't spoil it by prematurely enabling them.
|
||||
*/
|
||||
if (!perf_target__none(&opts->target))
|
||||
perf_evlist__enable(evsel_list);
|
||||
|
||||
/*
|
||||
* Let the child rip
|
||||
@ -720,8 +731,15 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
waking++;
|
||||
}
|
||||
|
||||
if (done)
|
||||
/*
|
||||
* When perf is starting the traced process, at the end events
|
||||
* die with the process and we wait for that. Thus no need to
|
||||
* disable events in this case.
|
||||
*/
|
||||
if (done && !disabled && !perf_target__none(&opts->target)) {
|
||||
perf_evlist__disable(evsel_list);
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (quiet || signr == SIGUSR1)
|
||||
|
@ -129,8 +129,7 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
|
||||
static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
|
||||
static struct stats walltime_nsecs_stats;
|
||||
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel,
|
||||
struct perf_evsel *first)
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
bool exclude_guest_missing = false;
|
||||
@ -153,7 +152,8 @@ retry:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!perf_target__has_task(&target) && (!group || evsel == first)) {
|
||||
if (!perf_target__has_task(&target) &&
|
||||
!perf_evsel__is_group_member(evsel)) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
@ -272,7 +272,7 @@ static int read_counter(struct perf_evsel *counter)
|
||||
static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
{
|
||||
unsigned long long t0, t1;
|
||||
struct perf_evsel *counter, *first;
|
||||
struct perf_evsel *counter;
|
||||
int status = 0;
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
const bool forks = (argc > 0);
|
||||
@ -332,10 +332,8 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
if (group)
|
||||
perf_evlist__set_leader(evsel_list);
|
||||
|
||||
first = perf_evlist__first(evsel_list);
|
||||
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
if (create_perf_stat_counter(counter, first) < 0) {
|
||||
if (create_perf_stat_counter(counter) < 0) {
|
||||
/*
|
||||
* PPC returns ENXIO for HW counters until 2.6.37
|
||||
* (behavior changed with commit b0a873e).
|
||||
|
@ -85,21 +85,26 @@ int check_pager_config(const char *cmd)
|
||||
return c.val;
|
||||
}
|
||||
|
||||
static int tui_command_config(const char *var, const char *value, void *data)
|
||||
static int browser_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
|
||||
c->val = perf_config_bool(var, value);
|
||||
if (!prefixcmp(var, "gtk.") && !strcmp(var + 4, c->cmd))
|
||||
c->val = perf_config_bool(var, value) ? 2 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
|
||||
static int check_tui_config(const char *cmd)
|
||||
/*
|
||||
* returns 0 for "no tui", 1 for "use tui", 2 for "use gtk",
|
||||
* and -1 for "not specified"
|
||||
*/
|
||||
static int check_browser_config(const char *cmd)
|
||||
{
|
||||
struct pager_config c;
|
||||
c.cmd = cmd;
|
||||
c.val = -1;
|
||||
perf_config(tui_command_config, &c);
|
||||
perf_config(browser_command_config, &c);
|
||||
return c.val;
|
||||
}
|
||||
|
||||
@ -302,7 +307,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
prefix = NULL; /* setup_perf_directory(); */
|
||||
|
||||
if (use_browser == -1)
|
||||
use_browser = check_tui_config(p->cmd);
|
||||
use_browser = check_browser_config(p->cmd);
|
||||
|
||||
if (use_pager == -1 && p->option & RUN_SETUP)
|
||||
use_pager = check_pager_config(p->cmd);
|
||||
|
@ -26,7 +26,7 @@ void get_term_dimensions(struct winsize *ws);
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
#include "../../arch/powerpc/include/asm/unistd.h"
|
||||
#include "../../arch/powerpc/include/uapi/asm/unistd.h"
|
||||
#define rmb() asm volatile ("sync" ::: "memory")
|
||||
#define cpu_relax() asm volatile ("" ::: "memory");
|
||||
#define CPUINFO_PROC "cpu"
|
||||
@ -178,7 +178,6 @@ extern bool test_attr__enabled;
|
||||
void test_attr__init(void);
|
||||
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags);
|
||||
int test_attr__run(void);
|
||||
|
||||
static inline int
|
||||
sys_perf_event_open(struct perf_event_attr *attr,
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define ENV "PERF_TEST_ATTR"
|
||||
|
||||
@ -151,7 +152,7 @@ static int run_dir(const char *d, const char *perf)
|
||||
return system(cmd);
|
||||
}
|
||||
|
||||
int test_attr__run(void)
|
||||
int test__attr(void)
|
||||
{
|
||||
struct stat st;
|
||||
char path_perf[PATH_MAX];
|
||||
|
@ -15,3 +15,4 @@ sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
||||
disabled=0
|
||||
|
@ -15,6 +15,5 @@ config=1
|
||||
sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
# TODO this is disabled for --group option, enabled otherwise
|
||||
# check why..
|
||||
enable_on_exec=1
|
||||
enable_on_exec=0
|
||||
disabled=0
|
||||
|
@ -11,7 +11,5 @@ group_fd=-1
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
# TODO both disabled and enable_on_exec are disabled for --group option,
|
||||
# enabled otherwise, check why..
|
||||
disabled=1
|
||||
enable_on_exec=1
|
||||
disabled=0
|
||||
enable_on_exec=0
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
||||
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
@ -25,6 +26,10 @@ static char *test_file(int size)
|
||||
unsigned char *buf;
|
||||
|
||||
fd = mkstemp(templ);
|
||||
if (fd < 0) {
|
||||
perror("mkstemp failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
@ -95,7 +100,7 @@ struct test_data_offset offsets[] = {
|
||||
},
|
||||
};
|
||||
|
||||
int dso__test_data(void)
|
||||
int test__dso_data(void)
|
||||
{
|
||||
struct machine machine;
|
||||
struct dso *dso;
|
||||
|
114
tools/perf/tests/evsel-roundtrip-name.c
Normal file
114
tools/perf/tests/evsel-roundtrip-name.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "parse-events.h"
|
||||
#include "tests.h"
|
||||
|
||||
static int perf_evsel__roundtrip_cache_name_test(void)
|
||||
{
|
||||
char name[128];
|
||||
int type, op, err = 0, ret = 0, i, idx;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
err = parse_events(evlist, name, 0);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
if (evsel->idx != idx)
|
||||
continue;
|
||||
|
||||
++idx;
|
||||
|
||||
if (strcmp(perf_evsel__name(evsel), name)) {
|
||||
pr_debug("%s != %s\n", perf_evsel__name(evsel), name);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__next(evsel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
perf_evlist__delete(evlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __perf_evsel__name_array_test(const char *names[], int nr_names)
|
||||
{
|
||||
int i, err;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_names; ++i) {
|
||||
err = parse_events(evlist, names[i], 0);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
names[i], err);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) {
|
||||
--err;
|
||||
pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]);
|
||||
}
|
||||
}
|
||||
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
#define perf_evsel__name_array_test(names) \
|
||||
__perf_evsel__name_array_test(names, ARRAY_SIZE(names))
|
||||
|
||||
int test__perf_evsel__roundtrip_name_test(void)
|
||||
{
|
||||
int err = 0, ret = 0;
|
||||
|
||||
err = perf_evsel__name_array_test(perf_evsel__hw_names);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = perf_evsel__name_array_test(perf_evsel__sw_names);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = perf_evsel__roundtrip_cache_name_test();
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
return ret;
|
||||
}
|
84
tools/perf/tests/evsel-tp-sched.c
Normal file
84
tools/perf/tests/evsel-tp-sched.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "event-parse.h"
|
||||
|
||||
static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
|
||||
int size, bool should_be_signed)
|
||||
{
|
||||
struct format_field *field = perf_evsel__field(evsel, name);
|
||||
int is_signed;
|
||||
int ret = 0;
|
||||
|
||||
if (field == NULL) {
|
||||
pr_debug("%s: \"%s\" field not found!\n", evsel->name, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
is_signed = !!(field->flags | FIELD_IS_SIGNED);
|
||||
if (should_be_signed && !is_signed) {
|
||||
pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n",
|
||||
evsel->name, name, is_signed, should_be_signed);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (field->size != size) {
|
||||
pr_debug("%s: \"%s\" size (%d) should be %d!\n",
|
||||
evsel->name, name, field->size, size);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test__perf_evsel__tp_sched_test(void)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0);
|
||||
int ret = 0;
|
||||
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_state", 8, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
perf_evsel__delete(evsel);
|
||||
|
||||
evsel = perf_evsel__newtp("sched", "sched_wakeup", 0);
|
||||
|
||||
if (perf_evsel__test_field(evsel, "comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "success", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "target_cpu", 4, true))
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
162
tools/perf/tests/mmap-basic.c
Normal file
162
tools/perf/tests/mmap-basic.c
Normal file
@ -0,0 +1,162 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
|
||||
/*
|
||||
* This test will generate random numbers of calls to some getpid syscalls,
|
||||
* then establish an mmap for a group of events that are created to monitor
|
||||
* the syscalls.
|
||||
*
|
||||
* It will receive the events, using mmap, use its PERF_SAMPLE_ID generated
|
||||
* sample.id field to map back to its respective perf_evsel instance.
|
||||
*
|
||||
* Then it checks if the number of syscalls reported as perf events by
|
||||
* the kernel corresponds to the number of syscalls made.
|
||||
*/
|
||||
int test__basic_mmap(void)
|
||||
{
|
||||
int err = -1;
|
||||
union perf_event *event;
|
||||
struct thread_map *threads;
|
||||
struct cpu_map *cpus;
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_TRACEPOINT,
|
||||
.read_format = PERF_FORMAT_ID,
|
||||
.sample_type = PERF_SAMPLE_ID,
|
||||
.watermark = 0,
|
||||
};
|
||||
cpu_set_t cpu_set;
|
||||
const char *syscall_names[] = { "getsid", "getppid", "getpgrp",
|
||||
"getpgid", };
|
||||
pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp,
|
||||
(void*)getpgid };
|
||||
#define nsyscalls ARRAY_SIZE(syscall_names)
|
||||
int ids[nsyscalls];
|
||||
unsigned int nr_events[nsyscalls],
|
||||
expected_nr_events[nsyscalls], i, j;
|
||||
struct perf_evsel *evsels[nsyscalls], *evsel;
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i) {
|
||||
char name[64];
|
||||
|
||||
snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
|
||||
ids[i] = trace_event__id(name);
|
||||
if (ids[i] < 0) {
|
||||
pr_debug("Is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
nr_events[i] = 0;
|
||||
expected_nr_events[i] = random() % 257;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (cpus == NULL) {
|
||||
pr_debug("cpu_map__new\n");
|
||||
goto out_free_threads;
|
||||
}
|
||||
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(cpus->map[0], &cpu_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[0], strerror(errno));
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
evlist = perf_evlist__new(cpus, threads);
|
||||
if (evlist == NULL) {
|
||||
pr_debug("perf_evlist__new\n");
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
/* anonymous union fields, can't be initialized above */
|
||||
attr.wakeup_events = 1;
|
||||
attr.sample_period = 1;
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i) {
|
||||
attr.config = ids[i];
|
||||
evsels[i] = perf_evsel__new(&attr, i);
|
||||
if (evsels[i] == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_free_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsels[i]);
|
||||
|
||||
if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
pr_debug("failed to mmap events: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i)
|
||||
for (j = 0; j < expected_nr_events[i]; ++j) {
|
||||
int foo = syscalls[i]();
|
||||
++foo;
|
||||
}
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
|
||||
struct perf_sample sample;
|
||||
|
||||
if (event->header.type != PERF_RECORD_SAMPLE) {
|
||||
pr_debug("unexpected %s event\n",
|
||||
perf_event__name(event->header.type));
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err) {
|
||||
pr_err("Can't parse sample, err = %d\n", err);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__id2evsel(evlist, sample.id);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("event with id %" PRIu64
|
||||
" doesn't map to an evsel\n", sample.id);
|
||||
goto out_munmap;
|
||||
}
|
||||
nr_events[evsel->idx]++;
|
||||
}
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
|
||||
pr_debug("expected %d %s events, got %d\n",
|
||||
expected_nr_events[evsel->idx],
|
||||
perf_evsel__name(evsel), nr_events[evsel->idx]);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_fd:
|
||||
for (i = 0; i < nsyscalls; ++i)
|
||||
perf_evsel__close_fd(evsels[i], 1, threads->nr);
|
||||
out_free_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out_free_cpus:
|
||||
cpu_map__delete(cpus);
|
||||
out_free_threads:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
120
tools/perf/tests/open-syscall-all-cpus.c
Normal file
120
tools/perf/tests/open-syscall-all-cpus.c
Normal file
@ -0,0 +1,120 @@
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "debug.h"
|
||||
|
||||
int test__open_syscall_event_on_all_cpus(void)
|
||||
{
|
||||
int err = -1, fd, cpu;
|
||||
struct thread_map *threads;
|
||||
struct cpu_map *cpus;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr attr;
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
cpu_set_t cpu_set;
|
||||
int id = trace_event__id("sys_enter_open");
|
||||
|
||||
if (id < 0) {
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (cpus == NULL) {
|
||||
pr_debug("cpu_map__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
if (perf_evsel__open(evsel, cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; ++cpu) {
|
||||
unsigned int ncalls = nr_open_calls + cpu;
|
||||
/*
|
||||
* XXX eventually lift this restriction in a way that
|
||||
* keeps perf building on older glibc installations
|
||||
* without CPU_ALLOC. 1024 cpus in 2010 still seems
|
||||
* a reasonable upper limit tho :-)
|
||||
*/
|
||||
if (cpus->map[cpu] >= CPU_SETSIZE) {
|
||||
pr_debug("Ignoring CPU %d\n", cpus->map[cpu]);
|
||||
continue;
|
||||
}
|
||||
|
||||
CPU_SET(cpus->map[cpu], &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[cpu],
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
for (i = 0; i < ncalls; ++i) {
|
||||
fd = open("/etc/passwd", O_RDONLY);
|
||||
close(fd);
|
||||
}
|
||||
CPU_CLR(cpus->map[cpu], &cpu_set);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we need to explicitely preallocate the counts, as if
|
||||
* we use the auto allocation it will allocate just for 1 cpu,
|
||||
* as we start by cpu 0.
|
||||
*/
|
||||
if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
|
||||
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; ++cpu) {
|
||||
unsigned int expected;
|
||||
|
||||
if (cpus->map[cpu] >= CPU_SETSIZE)
|
||||
continue;
|
||||
|
||||
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
|
||||
pr_debug("perf_evsel__read_on_cpu\n");
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
expected = nr_open_calls + cpu;
|
||||
if (evsel->counts->cpu[cpu].val != expected) {
|
||||
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);
|
||||
err = -1;
|
||||
}
|
||||
}
|
||||
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
perf_evsel__delete(evsel);
|
||||
out_thread_map_delete:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
117
tools/perf/tests/open-syscall-tp-fields.c
Normal file
117
tools/perf/tests/open-syscall-tp-fields.c
Normal file
@ -0,0 +1,117 @@
|
||||
#include "perf.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "tests.h"
|
||||
|
||||
int test__syscall_open_tp_fields(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
},
|
||||
.no_delay = true,
|
||||
.freq = 1,
|
||||
.mmap_pages = 256,
|
||||
.raw_samples = true,
|
||||
};
|
||||
const char *filename = "/etc/passwd";
|
||||
int flags = O_RDONLY | O_DIRECTORY;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i, nr_events = 0, nr_polls = 0;
|
||||
|
||||
if (evlist == NULL) {
|
||||
pr_debug("%s: perf_evlist__new\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("%s: perf_evsel__newtp\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsel);
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &opts.target);
|
||||
if (err < 0) {
|
||||
pr_debug("%s: perf_evlist__create_maps\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evsel__config(evsel, &opts);
|
||||
|
||||
evlist->threads->map[0] = getpid();
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Generate the event:
|
||||
*/
|
||||
open(filename, flags);
|
||||
|
||||
while (1) {
|
||||
int before = nr_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
int tp_flags;
|
||||
struct perf_sample sample;
|
||||
|
||||
++nr_events;
|
||||
|
||||
if (type != PERF_RECORD_SAMPLE)
|
||||
continue;
|
||||
|
||||
err = perf_evsel__parse_sample(evsel, event, &sample);
|
||||
if (err) {
|
||||
pr_err("Can't parse sample, err = %d\n", err);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
tp_flags = perf_evsel__intval(evsel, &sample, "flags");
|
||||
|
||||
if (flags != tp_flags) {
|
||||
pr_debug("%s: Expected flags=%#x, got %#x\n",
|
||||
__func__, flags, tp_flags);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
goto out_ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_events == before)
|
||||
poll(evlist->pollfd, evlist->nr_fds, 10);
|
||||
|
||||
if (++nr_polls > 5) {
|
||||
pr_debug("%s: no events!\n", __func__);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
out_ok:
|
||||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
return err;
|
||||
}
|
66
tools/perf/tests/open-syscall.c
Normal file
66
tools/perf/tests/open-syscall.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include "thread_map.h"
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
int test__open_syscall_event(void)
|
||||
{
|
||||
int err = -1, fd;
|
||||
struct thread_map *threads;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr attr;
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
int id = trace_event__id("sys_enter_open");
|
||||
|
||||
if (id < 0) {
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
if (perf_evsel__open_per_thread(evsel, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_open_calls; ++i) {
|
||||
fd = open("/etc/passwd", O_RDONLY);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
|
||||
pr_debug("perf_evsel__read_on_cpu\n");
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
if (evsel->counts->cpu[0].val != nr_open_calls) {
|
||||
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
|
||||
nr_open_calls, evsel->counts->cpu[0].val);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
perf_evsel__delete(evsel);
|
||||
out_thread_map_delete:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
#include "evlist.h"
|
||||
#include "sysfs.h"
|
||||
#include "../../../include/linux/hw_breakpoint.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
@ -520,7 +521,7 @@ static int test__group1(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* cycles:upp */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@ -556,7 +557,7 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* cache-references + :u modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@ -582,7 +583,7 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -605,7 +606,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group1"));
|
||||
|
||||
@ -635,7 +636,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group2"));
|
||||
|
||||
@ -662,7 +663,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -686,7 +687,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions:kp + p */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@ -723,7 +724,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions + G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@ -750,7 +751,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions:G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@ -776,7 +777,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1086,7 +1087,7 @@ static int test_pmu_events(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_events__test(void)
|
||||
int test__parse_events(void)
|
||||
{
|
||||
int ret1, ret2 = 0;
|
||||
|
||||
|
312
tools/perf/tests/perf-record.c
Normal file
312
tools/perf/tests/perf-record.c
Normal file
@ -0,0 +1,312 @@
|
||||
#include <sched.h>
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
|
||||
{
|
||||
int i, cpu = -1, nrcpus = 1024;
|
||||
realloc:
|
||||
CPU_ZERO(maskp);
|
||||
|
||||
if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
|
||||
if (errno == EINVAL && nrcpus < (1024 << 8)) {
|
||||
nrcpus = nrcpus << 2;
|
||||
goto realloc;
|
||||
}
|
||||
perror("sched_getaffinity");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nrcpus; i++) {
|
||||
if (CPU_ISSET(i, maskp)) {
|
||||
if (cpu == -1)
|
||||
cpu = i;
|
||||
else
|
||||
CPU_CLR(i, maskp);
|
||||
}
|
||||
}
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
int test__PERF_RECORD(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
},
|
||||
.no_delay = true,
|
||||
.freq = 10,
|
||||
.mmap_pages = 256,
|
||||
};
|
||||
cpu_set_t cpu_mask;
|
||||
size_t cpu_mask_size = sizeof(cpu_mask);
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_sample sample;
|
||||
const char *cmd = "sleep";
|
||||
const char *argv[] = { cmd, "1", NULL, };
|
||||
char *bname;
|
||||
u64 prev_time = 0;
|
||||
bool found_cmd_mmap = false,
|
||||
found_libc_mmap = false,
|
||||
found_vdso_mmap = false,
|
||||
found_ld_mmap = false;
|
||||
int err = -1, errs = 0, i, wakeups = 0;
|
||||
u32 cpu;
|
||||
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
|
||||
|
||||
if (evlist == NULL || argv == NULL) {
|
||||
pr_debug("Not enough memory to create evlist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need at least one evsel in the evlist, use the default
|
||||
* one: "cycles".
|
||||
*/
|
||||
err = perf_evlist__add_default(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create evsel\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create maps of threads and cpus to monitor. In this case
|
||||
* we start with all threads and cpus (-1, -1) but then in
|
||||
* perf_evlist__prepare_workload we'll fill in the only thread
|
||||
* we're monitoring, the one forked there.
|
||||
*/
|
||||
err = perf_evlist__create_maps(evlist, &opts.target);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the workload in argv[] to run, it'll fork it, and then wait
|
||||
* for perf_evlist__start_workload() to exec it. This is done this way
|
||||
* so that we have time to open the evlist (calling sys_perf_event_open
|
||||
* on all the fds) and then mmap them.
|
||||
*/
|
||||
err = perf_evlist__prepare_workload(evlist, &opts, argv);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config the evsels, setting attr->comm on the first one, etc.
|
||||
*/
|
||||
evsel = perf_evlist__first(evlist);
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_CPU;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TID;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
perf_evlist__config_attrs(evlist, &opts);
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
|
||||
if (err < 0) {
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
cpu = err;
|
||||
|
||||
/*
|
||||
* So that we can check perf_sample.cpu on all the samples.
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call sys_perf_event_open on all the fds on all the evsels,
|
||||
* grouping them if asked to.
|
||||
*/
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* mmap the first fd on a given CPU and ask for events for the other
|
||||
* fds in the same CPU to be injected in the same mmap ring buffer
|
||||
* (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
|
||||
*/
|
||||
err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that all is properly set up, enable the events, they will
|
||||
* count just on workload.pid, which will start...
|
||||
*/
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Now!
|
||||
*/
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
while (1) {
|
||||
int before = total_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
const char *name = perf_event__name(type);
|
||||
|
||||
++total_events;
|
||||
if (type < PERF_RECORD_MAX)
|
||||
nr_events[type]++;
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err < 0) {
|
||||
if (verbose)
|
||||
perf_event__fprintf(event, stderr);
|
||||
pr_debug("Couldn't parse sample\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
|
||||
perf_event__fprintf(event, stderr);
|
||||
}
|
||||
|
||||
if (prev_time > sample.time) {
|
||||
pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
|
||||
name, prev_time, sample.time);
|
||||
++errs;
|
||||
}
|
||||
|
||||
prev_time = sample.time;
|
||||
|
||||
if (sample.cpu != cpu) {
|
||||
pr_debug("%s with unexpected cpu, expected %d, got %d\n",
|
||||
name, cpu, sample.cpu);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.pid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.tid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected tid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.tid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP ||
|
||||
type == PERF_RECORD_FORK ||
|
||||
type == PERF_RECORD_EXIT) &&
|
||||
(pid_t)event->comm.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid/tid\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP) &&
|
||||
event->comm.pid != event->comm.tid) {
|
||||
pr_debug("%s with different pid/tid!\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PERF_RECORD_COMM:
|
||||
if (strcmp(event->comm.comm, cmd)) {
|
||||
pr_debug("%s with unexpected comm!\n", name);
|
||||
++errs;
|
||||
}
|
||||
break;
|
||||
case PERF_RECORD_EXIT:
|
||||
goto found_exit;
|
||||
case PERF_RECORD_MMAP:
|
||||
bname = strrchr(event->mmap.filename, '/');
|
||||
if (bname != NULL) {
|
||||
if (!found_cmd_mmap)
|
||||
found_cmd_mmap = !strcmp(bname + 1, cmd);
|
||||
if (!found_libc_mmap)
|
||||
found_libc_mmap = !strncmp(bname + 1, "libc", 4);
|
||||
if (!found_ld_mmap)
|
||||
found_ld_mmap = !strncmp(bname + 1, "ld", 2);
|
||||
} else if (!found_vdso_mmap)
|
||||
found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]");
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SAMPLE:
|
||||
/* Just ignore samples for now */
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unexpected perf_event->header.type %d!\n",
|
||||
type);
|
||||
++errs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't use poll here because at least at 3.1 times the
|
||||
* PERF_RECORD_{!SAMPLE} events don't honour
|
||||
* perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
|
||||
*/
|
||||
if (total_events == before && false)
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
|
||||
sleep(1);
|
||||
if (++wakeups > 5) {
|
||||
pr_debug("No PERF_RECORD_EXIT event!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found_exit:
|
||||
if (nr_events[PERF_RECORD_COMM] > 1) {
|
||||
pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (nr_events[PERF_RECORD_COMM] == 0) {
|
||||
pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_cmd_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_libc_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_ld_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_vdso_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
|
||||
++errs;
|
||||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
return (err < 0 || errs > 0) ? -1 : 0;
|
||||
}
|
178
tools/perf/tests/pmu.c
Normal file
178
tools/perf/tests/pmu.c
Normal file
@ -0,0 +1,178 @@
|
||||
#include "parse-events.h"
|
||||
#include "pmu.h"
|
||||
#include "util.h"
|
||||
#include "tests.h"
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} test_formats[] = {
|
||||
{ "krava01", "config:0-1,62-63\n", },
|
||||
{ "krava02", "config:10-17\n", },
|
||||
{ "krava03", "config:5\n", },
|
||||
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
|
||||
{ "krava12", "config1:63\n", },
|
||||
{ "krava13", "config1:45-47\n", },
|
||||
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
|
||||
{ "krava22", "config2:8,18,48,58\n", },
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava02",
|
||||
.val.num = 170,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava03",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava11",
|
||||
.val.num = 27,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava12",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava13",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava21",
|
||||
.val.num = 119,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava22",
|
||||
.val.num = 11,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava23",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
};
|
||||
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
|
||||
|
||||
/*
|
||||
* Prepare format directory data, exported by kernel
|
||||
* at /sys/bus/event_source/devices/<dev>/format.
|
||||
*/
|
||||
static char *test_format_dir_get(void)
|
||||
{
|
||||
static char dir[PATH_MAX];
|
||||
unsigned int i;
|
||||
|
||||
snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
|
||||
snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
|
||||
|
||||
file = fopen(name, "w");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (1 != fwrite(format->value, strlen(format->value), 1, file))
|
||||
break;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Cleanup format directory. */
|
||||
static int test_format_dir_put(char *dir)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
|
||||
if (system(buf))
|
||||
return -1;
|
||||
|
||||
snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
|
||||
return system(buf);
|
||||
}
|
||||
|
||||
static struct list_head *test_terms_list(void)
|
||||
{
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int test__pmu(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
LIST_HEAD(formats);
|
||||
struct list_head *terms = test_terms_list();
|
||||
int ret;
|
||||
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
ret = perf_pmu__format_parse(format, &formats);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = perf_pmu__config_terms(&formats, &attr, terms);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (attr.config != 0xc00000000002a823)
|
||||
break;
|
||||
if (attr.config1 != 0x8000400000000145)
|
||||
break;
|
||||
if (attr.config2 != 0x0400000020041d07)
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
test_format_dir_put(format);
|
||||
return ret;
|
||||
}
|
175
tools/perf/tests/rdpmc.c
Normal file
175
tools/perf/tests/rdpmc.c
Normal file
@ -0,0 +1,175 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include "types.h"
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
|
||||
#define barrier() asm volatile("" ::: "memory")
|
||||
|
||||
static u64 rdpmc(unsigned int counter)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
|
||||
static u64 rdtsc(void)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
|
||||
static u64 mmap_read_self(void *addr)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = addr;
|
||||
u32 seq, idx, time_mult = 0, time_shift = 0;
|
||||
u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
|
||||
|
||||
do {
|
||||
seq = pc->lock;
|
||||
barrier();
|
||||
|
||||
enabled = pc->time_enabled;
|
||||
running = pc->time_running;
|
||||
|
||||
if (enabled != running) {
|
||||
cyc = rdtsc();
|
||||
time_mult = pc->time_mult;
|
||||
time_shift = pc->time_shift;
|
||||
time_offset = pc->time_offset;
|
||||
}
|
||||
|
||||
idx = pc->index;
|
||||
count = pc->offset;
|
||||
if (idx)
|
||||
count += rdpmc(idx - 1);
|
||||
|
||||
barrier();
|
||||
} while (pc->lock != seq);
|
||||
|
||||
if (enabled != running) {
|
||||
u64 quot, rem;
|
||||
|
||||
quot = (cyc >> time_shift);
|
||||
rem = cyc & ((1 << time_shift) - 1);
|
||||
delta = time_offset + quot * time_mult +
|
||||
((rem * time_mult) >> time_shift);
|
||||
|
||||
enabled += delta;
|
||||
if (idx)
|
||||
running += delta;
|
||||
|
||||
quot = count / running;
|
||||
rem = count % running;
|
||||
count = quot * enabled + (rem * enabled) / running;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the RDPMC instruction faults then signal this back to the test parent task:
|
||||
*/
|
||||
static void segfault_handler(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static int __test__rdpmc(void)
|
||||
{
|
||||
volatile int tmp = 0;
|
||||
u64 i, loops = 1000;
|
||||
int n;
|
||||
int fd;
|
||||
void *addr;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_INSTRUCTIONS,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
u64 delta_sum = 0;
|
||||
struct sigaction sa;
|
||||
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_sigaction = segfault_handler;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd < 0) {
|
||||
pr_err("Error: sys_perf_event_open() syscall returned "
|
||||
"with %d (%s)\n", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (addr == (void *)(-1)) {
|
||||
pr_err("Error: mmap() syscall returned with (%s)\n",
|
||||
strerror(errno));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
for (n = 0; n < 6; n++) {
|
||||
u64 stamp, now, delta;
|
||||
|
||||
stamp = mmap_read_self(addr);
|
||||
|
||||
for (i = 0; i < loops; i++)
|
||||
tmp++;
|
||||
|
||||
now = mmap_read_self(addr);
|
||||
loops *= 10;
|
||||
|
||||
delta = now - stamp;
|
||||
pr_debug("%14d: %14Lu\n", n, (long long)delta);
|
||||
|
||||
delta_sum += delta;
|
||||
}
|
||||
|
||||
munmap(addr, page_size);
|
||||
pr_debug(" ");
|
||||
out_close:
|
||||
close(fd);
|
||||
|
||||
if (!delta_sum)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__rdpmc(void)
|
||||
{
|
||||
int status = 0;
|
||||
int wret = 0;
|
||||
int ret;
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
|
||||
if (!pid) {
|
||||
ret = __test__rdpmc();
|
||||
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
wret = waitpid(pid, &status, 0);
|
||||
if (wret < 0 || status)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
22
tools/perf/tests/tests.h
Normal file
22
tools/perf/tests/tests.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
/* Tests */
|
||||
int test__vmlinux_matches_kallsyms(void);
|
||||
int test__open_syscall_event(void);
|
||||
int test__open_syscall_event_on_all_cpus(void);
|
||||
int test__basic_mmap(void);
|
||||
int test__PERF_RECORD(void);
|
||||
int test__rdpmc(void);
|
||||
int test__perf_evsel__roundtrip_name_test(void);
|
||||
int test__perf_evsel__tp_sched_test(void);
|
||||
int test__syscall_open_tp_fields(void);
|
||||
int test__pmu(void);
|
||||
int test__attr(void);
|
||||
int test__dso_data(void);
|
||||
int test__parse_events(void);
|
||||
|
||||
/* Util */
|
||||
int trace_event__id(const char *evname);
|
||||
|
||||
#endif /* TESTS_H */
|
30
tools/perf/tests/util.c
Normal file
30
tools/perf/tests/util.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "tests.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
int trace_event__id(const char *evname)
|
||||
{
|
||||
char *filename;
|
||||
int err = -1, fd;
|
||||
|
||||
if (asprintf(&filename,
|
||||
"%s/syscalls/%s/id",
|
||||
tracing_events_path, evname) < 0)
|
||||
return -1;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
char id[16];
|
||||
if (read(fd, id, sizeof(id)) > 0)
|
||||
err = atoi(id);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
return err;
|
||||
}
|
230
tools/perf/tests/vmlinux-kallsyms.c
Normal file
230
tools/perf/tests/vmlinux-kallsyms.c
Normal file
@ -0,0 +1,230 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <string.h>
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "util.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "machine.h"
|
||||
|
||||
static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
|
||||
struct symbol *sym)
|
||||
{
|
||||
bool *visited = symbol__priv(sym);
|
||||
*visited = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__vmlinux_matches_kallsyms(void)
|
||||
{
|
||||
int err = -1;
|
||||
struct rb_node *nd;
|
||||
struct symbol *sym;
|
||||
struct map *kallsyms_map, *vmlinux_map;
|
||||
struct machine kallsyms, vmlinux;
|
||||
enum map_type type = MAP__FUNCTION;
|
||||
struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
|
||||
|
||||
/*
|
||||
* Step 1:
|
||||
*
|
||||
* Init the machines that will hold kernel, modules obtained from
|
||||
* both vmlinux + .ko files and from /proc/kallsyms split by modules.
|
||||
*/
|
||||
machine__init(&kallsyms, "", HOST_KERNEL_ID);
|
||||
machine__init(&vmlinux, "", HOST_KERNEL_ID);
|
||||
|
||||
/*
|
||||
* Step 2:
|
||||
*
|
||||
* Create the kernel maps for kallsyms and the DSO where we will then
|
||||
* load /proc/kallsyms. Also create the modules maps from /proc/modules
|
||||
* and find the .ko files that match them in /lib/modules/`uname -r`/.
|
||||
*/
|
||||
if (machine__create_kernel_maps(&kallsyms) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 3:
|
||||
*
|
||||
* Load and split /proc/kallsyms into multiple maps, one per module.
|
||||
*/
|
||||
if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
|
||||
pr_debug("dso__load_kallsyms ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 4:
|
||||
*
|
||||
* kallsyms will be internally on demand sorted by name so that we can
|
||||
* find the reference relocation * symbol, i.e. the symbol we will use
|
||||
* to see if the running kernel was relocated by checking if it has the
|
||||
* same value in the vmlinux file we load.
|
||||
*/
|
||||
kallsyms_map = machine__kernel_map(&kallsyms, type);
|
||||
|
||||
sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL);
|
||||
if (sym == NULL) {
|
||||
pr_debug("dso__find_symbol_by_name ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ref_reloc_sym.addr = sym->start;
|
||||
|
||||
/*
|
||||
* Step 5:
|
||||
*
|
||||
* Now repeat step 2, this time for the vmlinux file we'll auto-locate.
|
||||
*/
|
||||
if (machine__create_kernel_maps(&vmlinux) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
vmlinux_map = machine__kernel_map(&vmlinux, type);
|
||||
map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;
|
||||
|
||||
/*
|
||||
* Step 6:
|
||||
*
|
||||
* Locate a vmlinux file in the vmlinux path that has a buildid that
|
||||
* matches the one of the running kernel.
|
||||
*
|
||||
* While doing that look if we find the ref reloc symbol, if we find it
|
||||
* we'll have its ref_reloc_symbol.unrelocated_addr and then
|
||||
* maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
|
||||
* to fixup the symbols.
|
||||
*/
|
||||
if (machine__load_vmlinux_path(&vmlinux, type,
|
||||
vmlinux_matches_kallsyms_filter) <= 0) {
|
||||
pr_debug("machine__load_vmlinux_path ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
/*
|
||||
* Step 7:
|
||||
*
|
||||
* Now look at the symbols in the vmlinux DSO and check if we find all of them
|
||||
* in the kallsyms dso. For the ones that are in both, check its names and
|
||||
* end addresses too.
|
||||
*/
|
||||
for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
|
||||
struct symbol *pair, *first_pair;
|
||||
bool backwards = true;
|
||||
|
||||
sym = rb_entry(nd, struct symbol, rb_node);
|
||||
|
||||
if (sym->start == sym->end)
|
||||
continue;
|
||||
|
||||
first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
|
||||
pair = first_pair;
|
||||
|
||||
if (pair && pair->start == sym->start) {
|
||||
next_pair:
|
||||
if (strcmp(sym->name, pair->name) == 0) {
|
||||
/*
|
||||
* kallsyms don't have the symbol end, so we
|
||||
* set that by using the next symbol start - 1,
|
||||
* in some cases we get this up to a page
|
||||
* wrong, trace_kmalloc when I was developing
|
||||
* this code was one such example, 2106 bytes
|
||||
* off the real size. More than that and we
|
||||
* _really_ have a problem.
|
||||
*/
|
||||
s64 skew = sym->end - pair->end;
|
||||
if (llabs(skew) < page_size)
|
||||
continue;
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
|
||||
sym->start, sym->name, sym->end, pair->end);
|
||||
} else {
|
||||
struct rb_node *nnd;
|
||||
detour:
|
||||
nnd = backwards ? rb_prev(&pair->rb_node) :
|
||||
rb_next(&pair->rb_node);
|
||||
if (nnd) {
|
||||
struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
|
||||
|
||||
if (next->start == sym->start) {
|
||||
pair = next;
|
||||
goto next_pair;
|
||||
}
|
||||
}
|
||||
|
||||
if (backwards) {
|
||||
backwards = false;
|
||||
pair = first_pair;
|
||||
goto detour;
|
||||
}
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
|
||||
sym->start, sym->name, pair->name);
|
||||
}
|
||||
} else
|
||||
pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name);
|
||||
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
goto out;
|
||||
|
||||
pr_info("Maps only in vmlinux:\n");
|
||||
|
||||
for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
|
||||
/*
|
||||
* If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
|
||||
* the kernel will have the path for the vmlinux file being used,
|
||||
* so use the short name, less descriptive but the same ("[kernel]" in
|
||||
* both cases.
|
||||
*/
|
||||
pair = map_groups__find_by_name(&kallsyms.kmaps, type,
|
||||
(pos->dso->kernel ?
|
||||
pos->dso->short_name :
|
||||
pos->dso->name));
|
||||
if (pair)
|
||||
pair->priv = 1;
|
||||
else
|
||||
map__fprintf(pos, stderr);
|
||||
}
|
||||
|
||||
pr_info("Maps in vmlinux with a different name in kallsyms:\n");
|
||||
|
||||
for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
|
||||
|
||||
pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
|
||||
if (pair == NULL || pair->priv)
|
||||
continue;
|
||||
|
||||
if (pair->start == pos->start) {
|
||||
pair->priv = 1;
|
||||
pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
|
||||
pos->start, pos->end, pos->pgoff, pos->dso->name);
|
||||
if (pos->pgoff != pair->pgoff || pos->end != pair->end)
|
||||
pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "",
|
||||
pair->start, pair->end, pair->pgoff);
|
||||
pr_info(" %s\n", pair->dso->name);
|
||||
pair->priv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("Maps only in kallsyms:\n");
|
||||
|
||||
for (nd = rb_first(&kallsyms.kmaps.maps[type]);
|
||||
nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node);
|
||||
|
||||
if (!pos->priv)
|
||||
map__fprintf(pos, stderr);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
@ -30,6 +30,7 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
|
||||
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
|
||||
|
||||
void perf_gtk__init_helpline(void);
|
||||
void perf_gtk__init_progress(void);
|
||||
void perf_gtk__init_hpp(void);
|
||||
|
||||
#ifndef HAVE_GTK_INFO_BAR
|
||||
|
59
tools/perf/ui/gtk/progress.c
Normal file
59
tools/perf/ui/gtk/progress.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "gtk.h"
|
||||
#include "../progress.h"
|
||||
#include "util.h"
|
||||
|
||||
static GtkWidget *dialog;
|
||||
static GtkWidget *progress;
|
||||
|
||||
static void gtk_progress_update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
double fraction = total ? 1.0 * curr / total : 0.0;
|
||||
char buf[1024];
|
||||
|
||||
if (dialog == NULL) {
|
||||
GtkWidget *vbox = gtk_vbox_new(TRUE, 5);
|
||||
GtkWidget *label = gtk_label_new(title);
|
||||
|
||||
dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
progress = gtk_progress_bar_new();
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(dialog), vbox);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(dialog), "perf");
|
||||
gtk_window_resize(GTK_WINDOW(dialog), 300, 80);
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
|
||||
|
||||
gtk_widget_show_all(dialog);
|
||||
}
|
||||
|
||||
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);
|
||||
snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total);
|
||||
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
|
||||
|
||||
/* we didn't call gtk_main yet, so do it manually */
|
||||
while (gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
static void gtk_progress_finish(void)
|
||||
{
|
||||
/* this will also destroy all of its children */
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
dialog = NULL;
|
||||
}
|
||||
|
||||
static struct ui_progress gtk_progress_fns = {
|
||||
.update = gtk_progress_update,
|
||||
.finish = gtk_progress_finish,
|
||||
};
|
||||
|
||||
void perf_gtk__init_progress(void)
|
||||
{
|
||||
progress_fns = >k_progress_fns;
|
||||
}
|
@ -8,7 +8,9 @@ int perf_gtk__init(void)
|
||||
{
|
||||
perf_error__register(&perf_gtk_eops);
|
||||
perf_gtk__init_helpline();
|
||||
perf_gtk__init_progress();
|
||||
perf_gtk__init_hpp();
|
||||
|
||||
return gtk_init_check(NULL, NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
|
@ -111,14 +111,3 @@ struct perf_error_ops perf_gtk_eops = {
|
||||
.warning = perf_gtk__warning_statusbar,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME: Functions below should be implemented properly.
|
||||
* For now, just add stubs for NO_NEWT=1 build.
|
||||
*/
|
||||
#ifndef NEWT_SUPPORT
|
||||
void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -1,32 +1,26 @@
|
||||
#include "../cache.h"
|
||||
#include "progress.h"
|
||||
#include "libslang.h"
|
||||
#include "ui.h"
|
||||
#include "browser.h"
|
||||
|
||||
static void nop_progress_update(u64 curr __maybe_unused,
|
||||
u64 total __maybe_unused,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ui_progress default_progress_fns =
|
||||
{
|
||||
.update = nop_progress_update,
|
||||
};
|
||||
|
||||
struct ui_progress *progress_fns = &default_progress_fns;
|
||||
|
||||
void ui_progress__update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
int bar, y;
|
||||
/*
|
||||
* FIXME: We should have a per UI backend way of showing progress,
|
||||
* stdio will just show a percentage as NN%, etc.
|
||||
*/
|
||||
if (use_browser <= 0)
|
||||
return;
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(true);
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
y = SLtt_Screen_Rows / 2 - 2;
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
|
||||
SLsmg_gotorc(y++, 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
SLsmg_set_color(HE_COLORSET_SELECTED);
|
||||
bar = ((SLtt_Screen_Cols - 2) * curr) / total;
|
||||
SLsmg_fill_region(y, 1, 1, bar, ' ');
|
||||
SLsmg_refresh();
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
return progress_fns->update(curr, total, title);
|
||||
}
|
||||
|
||||
void ui_progress__finish(void)
|
||||
{
|
||||
if (progress_fns->finish)
|
||||
progress_fns->finish();
|
||||
}
|
||||
|
@ -3,6 +3,16 @@
|
||||
|
||||
#include <../types.h>
|
||||
|
||||
struct ui_progress {
|
||||
void (*update)(u64, u64, const char *);
|
||||
void (*finish)(void);
|
||||
};
|
||||
|
||||
extern struct ui_progress *progress_fns;
|
||||
|
||||
void ui_progress__init(void);
|
||||
|
||||
void ui_progress__update(u64 curr, u64 total, const char *title);
|
||||
void ui_progress__finish(void);
|
||||
|
||||
#endif
|
||||
|
42
tools/perf/ui/tui/progress.c
Normal file
42
tools/perf/ui/tui/progress.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include "../cache.h"
|
||||
#include "../progress.h"
|
||||
#include "../libslang.h"
|
||||
#include "../ui.h"
|
||||
#include "../browser.h"
|
||||
|
||||
static void tui_progress__update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
int bar, y;
|
||||
/*
|
||||
* FIXME: We should have a per UI backend way of showing progress,
|
||||
* stdio will just show a percentage as NN%, etc.
|
||||
*/
|
||||
if (use_browser <= 0)
|
||||
return;
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(true);
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
y = SLtt_Screen_Rows / 2 - 2;
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
|
||||
SLsmg_gotorc(y++, 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
SLsmg_set_color(HE_COLORSET_SELECTED);
|
||||
bar = ((SLtt_Screen_Cols - 2) * curr) / total;
|
||||
SLsmg_fill_region(y, 1, 1, bar, ' ');
|
||||
SLsmg_refresh();
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
}
|
||||
|
||||
static struct ui_progress tui_progress_fns =
|
||||
{
|
||||
.update = tui_progress__update,
|
||||
};
|
||||
|
||||
void ui_progress__init(void)
|
||||
{
|
||||
progress_fns = &tui_progress_fns;
|
||||
}
|
@ -118,6 +118,7 @@ int ui__init(void)
|
||||
newtSetSuspendCallback(newt_suspend, NULL);
|
||||
ui_helpline__init();
|
||||
ui_browser__init();
|
||||
ui_progress__init();
|
||||
|
||||
signal(SIGSEGV, ui__signal);
|
||||
signal(SIGFPE, ui__signal);
|
||||
|
@ -3,9 +3,37 @@
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
extern pthread_mutex_t ui__lock;
|
||||
|
||||
extern int use_browser;
|
||||
|
||||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
int ui__init(void);
|
||||
void ui__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int ui__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_gtk__init(void);
|
||||
void perf_gtk__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int perf_gtk__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
void ui__refresh_dimensions(bool force);
|
||||
|
||||
#endif /* _PERF_UI_H_ */
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
#include "../perf.h"
|
||||
#include "../ui/ui.h"
|
||||
|
||||
#define CMD_EXEC_PATH "--exec-path"
|
||||
#define CMD_PERF_DIR "--perf-dir="
|
||||
@ -31,44 +32,6 @@ extern const char *pager_program;
|
||||
extern int pager_in_use(void);
|
||||
extern int pager_use_color;
|
||||
|
||||
extern int use_browser;
|
||||
|
||||
#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT)
|
||||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
int ui__init(void);
|
||||
void ui__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int ui__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_gtk__init(void);
|
||||
void perf_gtk__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int perf_gtk__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
#else /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
static inline void setup_browser(bool fallback_to_pager)
|
||||
{
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
}
|
||||
static inline void exit_browser(bool wait_for_ok __maybe_unused) {}
|
||||
#endif /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
|
@ -26,6 +26,7 @@ int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
static inline void ui_progress__update(u64 curr __maybe_unused,
|
||||
u64 total __maybe_unused,
|
||||
const char *title __maybe_unused) {}
|
||||
static inline void ui_progress__finish(void) {}
|
||||
|
||||
#define ui__error(format, arg...) ui__warning(format, ##arg)
|
||||
|
||||
|
@ -193,55 +193,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
|
||||
event->header.misc = PERF_RECORD_MISC_USER;
|
||||
|
||||
while (1) {
|
||||
char bf[BUFSIZ], *pbf = bf;
|
||||
int n;
|
||||
char bf[BUFSIZ];
|
||||
char prot[5];
|
||||
char execname[PATH_MAX];
|
||||
char anonstr[] = "//anon";
|
||||
size_t size;
|
||||
|
||||
if (fgets(bf, sizeof(bf), fp) == NULL)
|
||||
break;
|
||||
|
||||
/* ensure null termination since stack will be reused. */
|
||||
strcpy(execname, "");
|
||||
|
||||
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
|
||||
n = hex2u64(pbf, &event->mmap.start);
|
||||
if (n < 0)
|
||||
sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n",
|
||||
&event->mmap.start, &event->mmap.len, prot,
|
||||
&event->mmap.pgoff, execname);
|
||||
|
||||
if (prot[2] != 'x')
|
||||
continue;
|
||||
pbf += n + 1;
|
||||
n = hex2u64(pbf, &event->mmap.len);
|
||||
if (n < 0)
|
||||
continue;
|
||||
pbf += n + 3;
|
||||
if (*pbf == 'x') { /* vm_exec */
|
||||
char anonstr[] = "//anon\n";
|
||||
char *execname = strchr(bf, '/');
|
||||
|
||||
/* Catch VDSO */
|
||||
if (execname == NULL)
|
||||
execname = strstr(bf, "[vdso]");
|
||||
if (!strcmp(execname, ""))
|
||||
strcpy(execname, anonstr);
|
||||
|
||||
/* Catch anonymous mmaps */
|
||||
if ((execname == NULL) && !strstr(bf, "["))
|
||||
execname = anonstr;
|
||||
size = strlen(execname) + 1;
|
||||
memcpy(event->mmap.filename, execname, size);
|
||||
size = PERF_ALIGN(size, sizeof(u64));
|
||||
event->mmap.len -= event->mmap.start;
|
||||
event->mmap.header.size = (sizeof(event->mmap) -
|
||||
(sizeof(event->mmap.filename) - size));
|
||||
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
|
||||
event->mmap.header.size += machine->id_hdr_size;
|
||||
event->mmap.pid = tgid;
|
||||
event->mmap.tid = pid;
|
||||
|
||||
if (execname == NULL)
|
||||
continue;
|
||||
|
||||
pbf += 3;
|
||||
n = hex2u64(pbf, &event->mmap.pgoff);
|
||||
|
||||
size = strlen(execname);
|
||||
execname[size - 1] = '\0'; /* Remove \n */
|
||||
memcpy(event->mmap.filename, execname, size);
|
||||
size = PERF_ALIGN(size, sizeof(u64));
|
||||
event->mmap.len -= event->mmap.start;
|
||||
event->mmap.header.size = (sizeof(event->mmap) -
|
||||
(sizeof(event->mmap.filename) - size));
|
||||
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
|
||||
event->mmap.header.size += machine->id_hdr_size;
|
||||
event->mmap.pid = tgid;
|
||||
event->mmap.tid = pid;
|
||||
|
||||
if (process(tool, event, &synth_sample, machine) != 0) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (process(tool, event, &synth_sample, machine) != 0) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,15 +52,13 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *evsel, *first;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
|
||||
first = perf_evlist__first(evlist);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__config(evsel, opts, first);
|
||||
perf_evsel__config(evsel, opts);
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_ID;
|
||||
@ -224,6 +222,8 @@ void perf_evlist__disable(struct perf_evlist *evlist)
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (perf_evsel__is_group_member(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_DISABLE, 0);
|
||||
@ -238,6 +238,8 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
||||
|
||||
for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (perf_evsel__is_group_member(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
|
@ -404,13 +404,40 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
|
||||
return evsel->name ?: "unknown";
|
||||
}
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
|
||||
struct perf_evsel *first)
|
||||
/*
|
||||
* The enable_on_exec/disabled value strategy:
|
||||
*
|
||||
* 1) For any type of traced program:
|
||||
* - all independent events and group leaders are disabled
|
||||
* - all group members are enabled
|
||||
*
|
||||
* Group members are ruled by group leaders. They need to
|
||||
* be enabled, because the group scheduling relies on that.
|
||||
*
|
||||
* 2) For traced programs executed by perf:
|
||||
* - all independent events and group leaders have
|
||||
* enable_on_exec set
|
||||
* - we don't specifically enable or disable any event during
|
||||
* the record command
|
||||
*
|
||||
* Independent events and group leaders are initially disabled
|
||||
* and get enabled by exec. Group members are ruled by group
|
||||
* leaders as stated in 1).
|
||||
*
|
||||
* 3) For traced programs attached by perf (pid/tid):
|
||||
* - we specifically enable or disable all events during
|
||||
* the record command
|
||||
*
|
||||
* When attaching events to already running traced we
|
||||
* enable/disable events specifically, as there's no
|
||||
* initial traced exec call.
|
||||
*/
|
||||
void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
|
||||
attr->disabled = 1;
|
||||
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
|
||||
attr->inherit = !opts->no_inherit;
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
@ -486,10 +513,21 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
if (perf_target__none(&opts->target) &&
|
||||
(!opts->group || evsel == first)) {
|
||||
/*
|
||||
* XXX see the function comment above
|
||||
*
|
||||
* Disabling only independent events or group leaders,
|
||||
* keeping group members enabled.
|
||||
*/
|
||||
if (!perf_evsel__is_group_member(evsel))
|
||||
attr->disabled = 1;
|
||||
|
||||
/*
|
||||
* Setting enable_on_exec for independent events and
|
||||
* group leaders for traced executed by perf.
|
||||
*/
|
||||
if (perf_target__none(&opts->target) && !perf_evsel__is_group_member(evsel))
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
@ -669,7 +707,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread)
|
||||
struct perf_evsel *leader = evsel->leader;
|
||||
int fd;
|
||||
|
||||
if (!leader)
|
||||
if (!perf_evsel__is_group_member(evsel))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "../../../include/uapi/linux/perf_event.h"
|
||||
#include "types.h"
|
||||
#include "xyarray.h"
|
||||
@ -92,8 +93,7 @@ void perf_evsel__exit(struct perf_evsel *evsel);
|
||||
void perf_evsel__delete(struct perf_evsel *evsel);
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts,
|
||||
struct perf_evsel *first);
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
@ -225,4 +225,9 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
|
||||
{
|
||||
return list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
}
|
||||
|
||||
static inline bool perf_evsel__is_group_member(const struct perf_evsel *evsel)
|
||||
{
|
||||
return evsel->leader != NULL;
|
||||
}
|
||||
#endif /* __PERF_EVSEL_H */
|
||||
|
@ -742,9 +742,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
|
||||
he = hist_entry__new(pair);
|
||||
if (he) {
|
||||
he->stat.nr_events = 0;
|
||||
he->stat.period = 0;
|
||||
he->hists = hists;
|
||||
memset(&he->stat, 0, sizeof(he->stat));
|
||||
he->hists = hists;
|
||||
rb_link_node(&he->rb_node, parent, p);
|
||||
rb_insert_color(&he->rb_node, &hists->entries);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
|
@ -195,7 +195,7 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int script_browse(const char *script_opt)
|
||||
static inline int script_browse(const char *script_opt __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -722,6 +722,27 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic modifier sanity check to validate it contains only one
|
||||
* instance of any modifier (apart from 'p') present.
|
||||
*/
|
||||
static int check_modifier(char *str)
|
||||
{
|
||||
char *p = str;
|
||||
|
||||
/* The sizeof includes 0 byte as well. */
|
||||
if (strlen(str) > (sizeof("ukhGHppp") - 1))
|
||||
return -1;
|
||||
|
||||
while (*p) {
|
||||
if (*p != 'p' && strchr(p + 1, *p))
|
||||
return -1;
|
||||
p++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_events__modifier_event(struct list_head *list, char *str, bool add)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
@ -730,6 +751,9 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
if (check_modifier(str))
|
||||
return -EINVAL;
|
||||
|
||||
if (!add && get_event_modifier(&mod, str, NULL))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -99,7 +99,6 @@ void parse_events__set_leader(char *name, struct list_head *list);
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all);
|
||||
void parse_events_error(void *data, void *scanner, char const *msg);
|
||||
int parse_events__test(void);
|
||||
|
||||
void print_events(const char *event_glob, bool name_only);
|
||||
void print_events_type(u8 type);
|
||||
|
@ -82,7 +82,7 @@ num_hex 0x[a-fA-F0-9]+
|
||||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
||||
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
|
||||
modifier_event [ukhpGH]{1,8}
|
||||
modifier_event [ukhpGH]+
|
||||
modifier_bp [rwx]{1,3}
|
||||
|
||||
%%
|
||||
|
@ -22,7 +22,7 @@ static LIST_HEAD(pmus);
|
||||
* Parse & process all the sysfs attributes located under
|
||||
* the directory specified in 'dir' parameter.
|
||||
*/
|
||||
static int pmu_format_parse(char *dir, struct list_head *head)
|
||||
int perf_pmu__format_parse(char *dir, struct list_head *head)
|
||||
{
|
||||
struct dirent *evt_ent;
|
||||
DIR *format_dir;
|
||||
@ -77,7 +77,7 @@ static int pmu_format(char *name, struct list_head *format)
|
||||
if (stat(path, &st) < 0)
|
||||
return 0; /* no error if format does not exist */
|
||||
|
||||
if (pmu_format_parse(path, format))
|
||||
if (perf_pmu__format_parse(path, format))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@ -446,8 +446,9 @@ static int pmu_config_term(struct list_head *formats,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
int perf_pmu__config_terms(struct list_head *formats,
|
||||
struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
@ -467,7 +468,7 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
attr->type = pmu->type;
|
||||
return pmu_config(&pmu->format, attr, head_terms);
|
||||
return perf_pmu__config_terms(&pmu->format, attr, head_terms);
|
||||
}
|
||||
|
||||
static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
|
||||
@ -551,177 +552,3 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to)
|
||||
for (b = from; b <= to; b++)
|
||||
set_bit(b, bits);
|
||||
}
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} test_formats[] = {
|
||||
{ "krava01", "config:0-1,62-63\n", },
|
||||
{ "krava02", "config:10-17\n", },
|
||||
{ "krava03", "config:5\n", },
|
||||
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
|
||||
{ "krava12", "config1:63\n", },
|
||||
{ "krava13", "config1:45-47\n", },
|
||||
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
|
||||
{ "krava22", "config2:8,18,48,58\n", },
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava02",
|
||||
.val.num = 170,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava03",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava11",
|
||||
.val.num = 27,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava12",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava13",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava21",
|
||||
.val.num = 119,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava22",
|
||||
.val.num = 11,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava23",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
};
|
||||
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
|
||||
|
||||
/*
|
||||
* Prepare format directory data, exported by kernel
|
||||
* at /sys/bus/event_source/devices/<dev>/format.
|
||||
*/
|
||||
static char *test_format_dir_get(void)
|
||||
{
|
||||
static char dir[PATH_MAX];
|
||||
unsigned int i;
|
||||
|
||||
snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
|
||||
snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
|
||||
|
||||
file = fopen(name, "w");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (1 != fwrite(format->value, strlen(format->value), 1, file))
|
||||
break;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Cleanup format directory. */
|
||||
static int test_format_dir_put(char *dir)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
|
||||
if (system(buf))
|
||||
return -1;
|
||||
|
||||
snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
|
||||
return system(buf);
|
||||
}
|
||||
|
||||
static struct list_head *test_terms_list(void)
|
||||
{
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int perf_pmu__test(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
LIST_HEAD(formats);
|
||||
struct list_head *terms = test_terms_list();
|
||||
int ret;
|
||||
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
ret = pmu_format_parse(format, &formats);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = pmu_config(&formats, &attr, terms);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (attr.config != 0xc00000000002a823)
|
||||
break;
|
||||
if (attr.config1 != 0x8000400000000145)
|
||||
break;
|
||||
if (attr.config2 != 0x0400000020041d07)
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
test_format_dir_put(format);
|
||||
return ret;
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ struct perf_pmu {
|
||||
struct perf_pmu *perf_pmu__find(char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
int perf_pmu__config_terms(struct list_head *formats,
|
||||
struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
|
||||
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
|
||||
struct list_head *head_terms);
|
||||
@ -46,6 +49,7 @@ void perf_pmu_error(struct list_head *list, char *name, char const *msg);
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits);
|
||||
void perf_pmu__set_format(unsigned long *bits, long from, long to);
|
||||
int perf_pmu__format_parse(char *dir, struct list_head *head);
|
||||
|
||||
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
|
||||
|
||||
|
@ -17,59 +17,59 @@ struct pstack {
|
||||
|
||||
struct pstack *pstack__new(unsigned short max_nr_entries)
|
||||
{
|
||||
struct pstack *self = zalloc((sizeof(*self) +
|
||||
max_nr_entries * sizeof(void *)));
|
||||
if (self != NULL)
|
||||
self->max_nr_entries = max_nr_entries;
|
||||
return self;
|
||||
struct pstack *pstack = zalloc((sizeof(*pstack) +
|
||||
max_nr_entries * sizeof(void *)));
|
||||
if (pstack != NULL)
|
||||
pstack->max_nr_entries = max_nr_entries;
|
||||
return pstack;
|
||||
}
|
||||
|
||||
void pstack__delete(struct pstack *self)
|
||||
void pstack__delete(struct pstack *pstack)
|
||||
{
|
||||
free(self);
|
||||
free(pstack);
|
||||
}
|
||||
|
||||
bool pstack__empty(const struct pstack *self)
|
||||
bool pstack__empty(const struct pstack *pstack)
|
||||
{
|
||||
return self->top == 0;
|
||||
return pstack->top == 0;
|
||||
}
|
||||
|
||||
void pstack__remove(struct pstack *self, void *key)
|
||||
void pstack__remove(struct pstack *pstack, void *key)
|
||||
{
|
||||
unsigned short i = self->top, last_index = self->top - 1;
|
||||
unsigned short i = pstack->top, last_index = pstack->top - 1;
|
||||
|
||||
while (i-- != 0) {
|
||||
if (self->entries[i] == key) {
|
||||
if (pstack->entries[i] == key) {
|
||||
if (i < last_index)
|
||||
memmove(self->entries + i,
|
||||
self->entries + i + 1,
|
||||
memmove(pstack->entries + i,
|
||||
pstack->entries + i + 1,
|
||||
(last_index - i) * sizeof(void *));
|
||||
--self->top;
|
||||
--pstack->top;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pr_err("%s: %p not on the pstack!\n", __func__, key);
|
||||
}
|
||||
|
||||
void pstack__push(struct pstack *self, void *key)
|
||||
void pstack__push(struct pstack *pstack, void *key)
|
||||
{
|
||||
if (self->top == self->max_nr_entries) {
|
||||
pr_err("%s: top=%d, overflow!\n", __func__, self->top);
|
||||
if (pstack->top == pstack->max_nr_entries) {
|
||||
pr_err("%s: top=%d, overflow!\n", __func__, pstack->top);
|
||||
return;
|
||||
}
|
||||
self->entries[self->top++] = key;
|
||||
pstack->entries[pstack->top++] = key;
|
||||
}
|
||||
|
||||
void *pstack__pop(struct pstack *self)
|
||||
void *pstack__pop(struct pstack *pstack)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (self->top == 0) {
|
||||
if (pstack->top == 0) {
|
||||
pr_err("%s: underflow!\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = self->entries[--self->top];
|
||||
self->entries[self->top] = NULL;
|
||||
ret = pstack->entries[--pstack->top];
|
||||
pstack->entries[pstack->top] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
@ -1458,6 +1458,7 @@ more:
|
||||
session->ordered_samples.next_flush = ULLONG_MAX;
|
||||
err = flush_sample_queue(session, tool);
|
||||
out_err:
|
||||
ui_progress__finish();
|
||||
perf_session__warn_about_errors(session, tool);
|
||||
perf_session_free_sample_buffers(session);
|
||||
return err;
|
||||
|
@ -224,7 +224,6 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp);
|
||||
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
|
||||
|
||||
int dso__test_data(void);
|
||||
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
||||
struct symsrc *runtime_ss, symbol_filter_t filter,
|
||||
int kmodule);
|
||||
|
@ -1,8 +1,11 @@
|
||||
ifeq ("$(origin O)", "command line")
|
||||
ifeq ($(origin O), command line)
|
||||
dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),)
|
||||
ABSOLUTE_O := $(shell cd $(O) ; pwd)
|
||||
OUTPUT := $(ABSOLUTE_O)/
|
||||
OUTPUT := $(ABSOLUTE_O)/$(if $(subdir),$(subdir)/)
|
||||
COMMAND_O := O=$(ABSOLUTE_O)
|
||||
ifeq ($(objtree),)
|
||||
objtree := $(O)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
@ -41,7 +44,16 @@ else
|
||||
NO_SUBDIR = :
|
||||
endif
|
||||
|
||||
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
|
||||
#
|
||||
# Define a callable command for descending to a new directory
|
||||
#
|
||||
# Call by doing: $(call descend,directory[,target])
|
||||
#
|
||||
descend = \
|
||||
+mkdir -p $(OUTPUT)$(1) && \
|
||||
$(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2)
|
||||
|
||||
QUIET_SUBDIR0 = +$(MAKE) $(COMMAND_O) -C # space to separate -C and subdir
|
||||
QUIET_SUBDIR1 =
|
||||
|
||||
ifneq ($(findstring $(MAKEFLAGS),s),s)
|
||||
@ -56,5 +68,10 @@ ifndef V
|
||||
$(MAKE) $(PRINT_DIR) -C $$subdir
|
||||
QUIET_FLEX = @echo ' ' FLEX $@;
|
||||
QUIET_BISON = @echo ' ' BISON $@;
|
||||
|
||||
descend = \
|
||||
@echo ' ' DESCEND $(1); \
|
||||
mkdir -p $(OUTPUT)$(1) && \
|
||||
$(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2)
|
||||
endif
|
||||
endif
|
||||
|
Loading…
Reference in New Issue
Block a user