83a0944fa9
Related to a shadowed variable bug fix Valdis Kletnieks noticed that perf does not get built with -Wshadow, which could have helped us avoid the bug. So enable -Wshadow and also enable the following warnings on perf builds, in addition to the already enabled -Wall -Wextra -std=gnu99 warnings: -Wcast-align -Wformat=2 -Wshadow -Winit-self -Wpacked -Wredundant-decls -Wstack-protector -Wstrict-aliasing=3 -Wswitch-default -Wswitch-enum -Wno-system-headers -Wundef -Wvolatile-register-var -Wwrite-strings -Wbad-function-cast -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wstrict-prototypes -Wdeclaration-after-statement And change/fix the perf code to build cleanly under GCC 4.3.2. The list of warnings enablement is rather arbitrary: it's based on my (quick) reading of the GCC manpages and trying them on perf. I categorized the warnings based on individually enabling them and looking whether they trigger something in the perf build. If i liked those warnings (i.e. if they trigger for something that arguably could be improved) i enabled the warning. If the warnings seemed to come from language laywers spamming the build with tons of nuisance warnings i generally kept them off. Most of the sign conversion related warnings were in this category. (A second patch enabling some of the sign warnings might be welcome - sign bugs can be nasty.) I also kept warnings that seem to make sense from their manpage description and which produced no actual warnings on our code base. These warnings might still be turned off if they end up being a nuisance. I also left out a few warnings that are not supported in older compilers. [ Note that these changes might break the build on older compilers i did not test, or on non-x86 architectures that produce different warnings, so more testing would be welcome. ] Reported-by: Valdis.Kletnieks@vt.edu Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
683 lines
15 KiB
C
683 lines
15 KiB
C
|
|
#include "../perf.h"
|
|
#include "util.h"
|
|
#include "parse-options.h"
|
|
#include "parse-events.h"
|
|
#include "exec_cmd.h"
|
|
#include "string.h"
|
|
#include "cache.h"
|
|
|
|
extern char *strcasestr(const char *haystack, const char *needle);
|
|
|
|
int nr_counters;
|
|
|
|
struct perf_counter_attr attrs[MAX_COUNTERS];
|
|
|
|
struct event_symbol {
|
|
u8 type;
|
|
u64 config;
|
|
const char *symbol;
|
|
const char *alias;
|
|
};
|
|
|
|
char debugfs_path[MAXPATHLEN];
|
|
|
|
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
|
|
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
|
|
|
|
static struct event_symbol event_symbols[] = {
|
|
{ CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
|
|
{ CHW(INSTRUCTIONS), "instructions", "" },
|
|
{ CHW(CACHE_REFERENCES), "cache-references", "" },
|
|
{ CHW(CACHE_MISSES), "cache-misses", "" },
|
|
{ CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
|
|
{ CHW(BRANCH_MISSES), "branch-misses", "" },
|
|
{ CHW(BUS_CYCLES), "bus-cycles", "" },
|
|
|
|
{ CSW(CPU_CLOCK), "cpu-clock", "" },
|
|
{ CSW(TASK_CLOCK), "task-clock", "" },
|
|
{ CSW(PAGE_FAULTS), "page-faults", "faults" },
|
|
{ CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
|
|
{ CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
|
|
{ CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
|
|
{ CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
|
|
};
|
|
|
|
#define __PERF_COUNTER_FIELD(config, name) \
|
|
((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT)
|
|
|
|
#define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW)
|
|
#define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG)
|
|
#define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE)
|
|
#define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT)
|
|
|
|
static const char *hw_event_names[] = {
|
|
"cycles",
|
|
"instructions",
|
|
"cache-references",
|
|
"cache-misses",
|
|
"branches",
|
|
"branch-misses",
|
|
"bus-cycles",
|
|
};
|
|
|
|
static const char *sw_event_names[] = {
|
|
"cpu-clock-msecs",
|
|
"task-clock-msecs",
|
|
"page-faults",
|
|
"context-switches",
|
|
"CPU-migrations",
|
|
"minor-faults",
|
|
"major-faults",
|
|
};
|
|
|
|
#define MAX_ALIASES 8
|
|
|
|
static const char *hw_cache[][MAX_ALIASES] = {
|
|
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
|
|
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
|
|
{ "LLC", "L2" },
|
|
{ "dTLB", "d-tlb", "Data-TLB", },
|
|
{ "iTLB", "i-tlb", "Instruction-TLB", },
|
|
{ "branch", "branches", "bpu", "btb", "bpc", },
|
|
};
|
|
|
|
static const char *hw_cache_op[][MAX_ALIASES] = {
|
|
{ "load", "loads", "read", },
|
|
{ "store", "stores", "write", },
|
|
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
|
};
|
|
|
|
static const char *hw_cache_result[][MAX_ALIASES] = {
|
|
{ "refs", "Reference", "ops", "access", },
|
|
{ "misses", "miss", },
|
|
};
|
|
|
|
#define C(x) PERF_COUNT_HW_CACHE_##x
|
|
#define CACHE_READ (1 << C(OP_READ))
|
|
#define CACHE_WRITE (1 << C(OP_WRITE))
|
|
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
|
|
#define COP(x) (1 << x)
|
|
|
|
/*
|
|
* cache operartion stat
|
|
* L1I : Read and prefetch only
|
|
* ITLB and BPU : Read-only
|
|
*/
|
|
static unsigned long hw_cache_stat[C(MAX)] = {
|
|
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
|
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
|
|
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
|
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
|
[C(ITLB)] = (CACHE_READ),
|
|
[C(BPU)] = (CACHE_READ),
|
|
};
|
|
|
|
#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \
|
|
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
|
|
if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \
|
|
sys_dirent.d_name) && \
|
|
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
|
|
(strcmp(sys_dirent.d_name, ".")) && \
|
|
(strcmp(sys_dirent.d_name, "..")))
|
|
|
|
static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
|
|
{
|
|
char evt_path[MAXPATHLEN];
|
|
int fd;
|
|
|
|
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
|
|
sys_dir->d_name, evt_dir->d_name);
|
|
fd = open(evt_path, O_RDONLY);
|
|
if (fd < 0)
|
|
return -EINVAL;
|
|
close(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \
|
|
while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
|
|
if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \
|
|
sys_dirent.d_name, evt_dirent.d_name) && \
|
|
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
|
|
(strcmp(evt_dirent.d_name, ".")) && \
|
|
(strcmp(evt_dirent.d_name, "..")) && \
|
|
(!tp_event_has_id(&sys_dirent, &evt_dirent)))
|
|
|
|
#define MAX_EVENT_LENGTH 30
|
|
|
|
int valid_debugfs_mount(const char *debugfs)
|
|
{
|
|
struct statfs st_fs;
|
|
|
|
if (statfs(debugfs, &st_fs) < 0)
|
|
return -ENOENT;
|
|
else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
|
|
return -ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
static const char *tracepoint_id_to_name(u64 config)
|
|
{
|
|
static char tracepoint_name[2 * MAX_EVENT_LENGTH];
|
|
DIR *sys_dir, *evt_dir;
|
|
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
|
|
struct stat st;
|
|
char id_buf[4];
|
|
int fd;
|
|
u64 id;
|
|
char evt_path[MAXPATHLEN];
|
|
|
|
if (valid_debugfs_mount(debugfs_path))
|
|
return "unkown";
|
|
|
|
sys_dir = opendir(debugfs_path);
|
|
if (!sys_dir)
|
|
goto cleanup;
|
|
|
|
for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
|
|
evt_dir = opendir(evt_path);
|
|
if (!evt_dir)
|
|
goto cleanup;
|
|
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
|
|
evt_path, st) {
|
|
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id",
|
|
debugfs_path, sys_dirent.d_name,
|
|
evt_dirent.d_name);
|
|
fd = open(evt_path, O_RDONLY);
|
|
if (fd < 0)
|
|
continue;
|
|
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
close(fd);
|
|
id = atoll(id_buf);
|
|
if (id == config) {
|
|
closedir(evt_dir);
|
|
closedir(sys_dir);
|
|
snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH,
|
|
"%s:%s", sys_dirent.d_name,
|
|
evt_dirent.d_name);
|
|
return tracepoint_name;
|
|
}
|
|
}
|
|
closedir(evt_dir);
|
|
}
|
|
|
|
cleanup:
|
|
closedir(sys_dir);
|
|
return "unkown";
|
|
}
|
|
|
|
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
|
|
{
|
|
if (hw_cache_stat[cache_type] & COP(cache_op))
|
|
return 1; /* valid */
|
|
else
|
|
return 0; /* invalid */
|
|
}
|
|
|
|
static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
|
|
{
|
|
static char name[50];
|
|
|
|
if (cache_result) {
|
|
sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
|
|
hw_cache_op[cache_op][0],
|
|
hw_cache_result[cache_result][0]);
|
|
} else {
|
|
sprintf(name, "%s-%s", hw_cache[cache_type][0],
|
|
hw_cache_op[cache_op][1]);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
const char *event_name(int counter)
|
|
{
|
|
u64 config = attrs[counter].config;
|
|
int type = attrs[counter].type;
|
|
|
|
return __event_name(type, config);
|
|
}
|
|
|
|
const char *__event_name(int type, u64 config)
|
|
{
|
|
static char buf[32];
|
|
|
|
if (type == PERF_TYPE_RAW) {
|
|
sprintf(buf, "raw 0x%llx", config);
|
|
return buf;
|
|
}
|
|
|
|
switch (type) {
|
|
case PERF_TYPE_HARDWARE:
|
|
if (config < PERF_COUNT_HW_MAX)
|
|
return hw_event_names[config];
|
|
return "unknown-hardware";
|
|
|
|
case PERF_TYPE_HW_CACHE: {
|
|
u8 cache_type, cache_op, cache_result;
|
|
|
|
cache_type = (config >> 0) & 0xff;
|
|
if (cache_type > PERF_COUNT_HW_CACHE_MAX)
|
|
return "unknown-ext-hardware-cache-type";
|
|
|
|
cache_op = (config >> 8) & 0xff;
|
|
if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
|
|
return "unknown-ext-hardware-cache-op";
|
|
|
|
cache_result = (config >> 16) & 0xff;
|
|
if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
|
|
return "unknown-ext-hardware-cache-result";
|
|
|
|
if (!is_cache_op_valid(cache_type, cache_op))
|
|
return "invalid-cache";
|
|
|
|
return event_cache_name(cache_type, cache_op, cache_result);
|
|
}
|
|
|
|
case PERF_TYPE_SOFTWARE:
|
|
if (config < PERF_COUNT_SW_MAX)
|
|
return sw_event_names[config];
|
|
return "unknown-software";
|
|
|
|
case PERF_TYPE_TRACEPOINT:
|
|
return tracepoint_id_to_name(config);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
|
|
{
|
|
int i, j;
|
|
int n, longest = -1;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
|
|
n = strlen(names[i][j]);
|
|
if (n > longest && !strncasecmp(*str, names[i][j], n))
|
|
longest = n;
|
|
}
|
|
if (longest > 0) {
|
|
*str += longest;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
|
|
{
|
|
const char *s = *str;
|
|
int cache_type = -1, cache_op = -1, cache_result = -1;
|
|
|
|
cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
|
|
/*
|
|
* No fallback - if we cannot get a clear cache type
|
|
* then bail out:
|
|
*/
|
|
if (cache_type == -1)
|
|
return 0;
|
|
|
|
while ((cache_op == -1 || cache_result == -1) && *s == '-') {
|
|
++s;
|
|
|
|
if (cache_op == -1) {
|
|
cache_op = parse_aliases(&s, hw_cache_op,
|
|
PERF_COUNT_HW_CACHE_OP_MAX);
|
|
if (cache_op >= 0) {
|
|
if (!is_cache_op_valid(cache_type, cache_op))
|
|
return 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (cache_result == -1) {
|
|
cache_result = parse_aliases(&s, hw_cache_result,
|
|
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
|
if (cache_result >= 0)
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Can't parse this as a cache op or result, so back up
|
|
* to the '-'.
|
|
*/
|
|
--s;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Fall back to reads:
|
|
*/
|
|
if (cache_op == -1)
|
|
cache_op = PERF_COUNT_HW_CACHE_OP_READ;
|
|
|
|
/*
|
|
* Fall back to accesses:
|
|
*/
|
|
if (cache_result == -1)
|
|
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
|
|
|
|
attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
|
|
attr->type = PERF_TYPE_HW_CACHE;
|
|
|
|
*str = s;
|
|
return 1;
|
|
}
|
|
|
|
static int parse_tracepoint_event(const char **strp,
|
|
struct perf_counter_attr *attr)
|
|
{
|
|
const char *evt_name;
|
|
char *flags;
|
|
char sys_name[MAX_EVENT_LENGTH];
|
|
char id_buf[4];
|
|
int fd;
|
|
unsigned int sys_length, evt_length;
|
|
u64 id;
|
|
char evt_path[MAXPATHLEN];
|
|
|
|
if (valid_debugfs_mount(debugfs_path))
|
|
return 0;
|
|
|
|
evt_name = strchr(*strp, ':');
|
|
if (!evt_name)
|
|
return 0;
|
|
|
|
sys_length = evt_name - *strp;
|
|
if (sys_length >= MAX_EVENT_LENGTH)
|
|
return 0;
|
|
|
|
strncpy(sys_name, *strp, sys_length);
|
|
sys_name[sys_length] = '\0';
|
|
evt_name = evt_name + 1;
|
|
|
|
flags = strchr(evt_name, ':');
|
|
if (flags) {
|
|
*flags = '\0';
|
|
flags++;
|
|
if (!strncmp(flags, "record", strlen(flags)))
|
|
attr->sample_type |= PERF_SAMPLE_RAW;
|
|
}
|
|
|
|
evt_length = strlen(evt_name);
|
|
if (evt_length >= MAX_EVENT_LENGTH)
|
|
return 0;
|
|
|
|
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
|
|
sys_name, evt_name);
|
|
fd = open(evt_path, O_RDONLY);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
close(fd);
|
|
id = atoll(id_buf);
|
|
attr->config = id;
|
|
attr->type = PERF_TYPE_TRACEPOINT;
|
|
*strp = evt_name + evt_length;
|
|
return 1;
|
|
}
|
|
|
|
static int check_events(const char *str, unsigned int i)
|
|
{
|
|
int n;
|
|
|
|
n = strlen(event_symbols[i].symbol);
|
|
if (!strncmp(str, event_symbols[i].symbol, n))
|
|
return n;
|
|
|
|
n = strlen(event_symbols[i].alias);
|
|
if (n)
|
|
if (!strncmp(str, event_symbols[i].alias, n))
|
|
return n;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
|
|
{
|
|
const char *str = *strp;
|
|
unsigned int i;
|
|
int n;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
|
|
n = check_events(str, i);
|
|
if (n > 0) {
|
|
attr->type = event_symbols[i].type;
|
|
attr->config = event_symbols[i].config;
|
|
*strp = str + n;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int parse_raw_event(const char **strp, struct perf_counter_attr *attr)
|
|
{
|
|
const char *str = *strp;
|
|
u64 config;
|
|
int n;
|
|
|
|
if (*str != 'r')
|
|
return 0;
|
|
n = hex2u64(str + 1, &config);
|
|
if (n > 0) {
|
|
*strp = str + n + 1;
|
|
attr->type = PERF_TYPE_RAW;
|
|
attr->config = config;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
|
|
{
|
|
const char *str = *strp;
|
|
char *endp;
|
|
unsigned long type;
|
|
u64 config;
|
|
|
|
type = strtoul(str, &endp, 0);
|
|
if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
|
|
str = endp + 1;
|
|
config = strtoul(str, &endp, 0);
|
|
if (endp > str) {
|
|
attr->type = type;
|
|
attr->config = config;
|
|
*strp = endp;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
|
|
{
|
|
const char *str = *strp;
|
|
int eu = 1, ek = 1, eh = 1;
|
|
|
|
if (*str++ != ':')
|
|
return 0;
|
|
while (*str) {
|
|
if (*str == 'u')
|
|
eu = 0;
|
|
else if (*str == 'k')
|
|
ek = 0;
|
|
else if (*str == 'h')
|
|
eh = 0;
|
|
else
|
|
break;
|
|
++str;
|
|
}
|
|
if (str >= *strp + 2) {
|
|
*strp = str;
|
|
attr->exclude_user = eu;
|
|
attr->exclude_kernel = ek;
|
|
attr->exclude_hv = eh;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Each event can have multiple symbolic names.
|
|
* Symbolic names are (almost) exactly matched.
|
|
*/
|
|
static int parse_event_symbols(const char **str, struct perf_counter_attr *attr)
|
|
{
|
|
if (!(parse_tracepoint_event(str, attr) ||
|
|
parse_raw_event(str, attr) ||
|
|
parse_numeric_event(str, attr) ||
|
|
parse_symbolic_event(str, attr) ||
|
|
parse_generic_hw_event(str, attr)))
|
|
return 0;
|
|
|
|
parse_event_modifier(str, attr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int parse_events(const struct option *opt __used, const char *str, int unset __used)
|
|
{
|
|
struct perf_counter_attr attr;
|
|
|
|
for (;;) {
|
|
if (nr_counters == MAX_COUNTERS)
|
|
return -1;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
if (!parse_event_symbols(&str, &attr))
|
|
return -1;
|
|
|
|
if (!(*str == 0 || *str == ',' || isspace(*str)))
|
|
return -1;
|
|
|
|
attrs[nr_counters] = attr;
|
|
nr_counters++;
|
|
|
|
if (*str == 0)
|
|
break;
|
|
if (*str == ',')
|
|
++str;
|
|
while (isspace(*str))
|
|
++str;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char * const event_type_descriptors[] = {
|
|
"",
|
|
"Hardware event",
|
|
"Software event",
|
|
"Tracepoint event",
|
|
"Hardware cache event",
|
|
};
|
|
|
|
/*
|
|
* Print the events from <debugfs_mount_point>/tracing/events
|
|
*/
|
|
|
|
static void print_tracepoint_events(void)
|
|
{
|
|
DIR *sys_dir, *evt_dir;
|
|
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
|
|
struct stat st;
|
|
char evt_path[MAXPATHLEN];
|
|
|
|
if (valid_debugfs_mount(debugfs_path))
|
|
return;
|
|
|
|
sys_dir = opendir(debugfs_path);
|
|
if (!sys_dir)
|
|
goto cleanup;
|
|
|
|
for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
|
|
evt_dir = opendir(evt_path);
|
|
if (!evt_dir)
|
|
goto cleanup;
|
|
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
|
|
evt_path, st) {
|
|
snprintf(evt_path, MAXPATHLEN, "%s:%s",
|
|
sys_dirent.d_name, evt_dirent.d_name);
|
|
fprintf(stderr, " %-40s [%s]\n", evt_path,
|
|
event_type_descriptors[PERF_TYPE_TRACEPOINT+1]);
|
|
}
|
|
closedir(evt_dir);
|
|
}
|
|
|
|
cleanup:
|
|
closedir(sys_dir);
|
|
}
|
|
|
|
/*
|
|
* Print the help text for the event symbols:
|
|
*/
|
|
void print_events(void)
|
|
{
|
|
struct event_symbol *syms = event_symbols;
|
|
unsigned int i, type, op, prev_type = -1;
|
|
char name[40];
|
|
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "List of pre-defined events (to be used in -e):\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
|
|
type = syms->type + 1;
|
|
if (type >= ARRAY_SIZE(event_type_descriptors))
|
|
type = 0;
|
|
|
|
if (type != prev_type)
|
|
fprintf(stderr, "\n");
|
|
|
|
if (strlen(syms->alias))
|
|
sprintf(name, "%s OR %s", syms->symbol, syms->alias);
|
|
else
|
|
strcpy(name, syms->symbol);
|
|
fprintf(stderr, " %-40s [%s]\n", name,
|
|
event_type_descriptors[type]);
|
|
|
|
prev_type = type;
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
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 (!is_cache_op_valid(type, op))
|
|
continue;
|
|
|
|
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
|
fprintf(stderr, " %-40s [%s]\n",
|
|
event_cache_name(type, op, i),
|
|
event_type_descriptors[4]);
|
|
}
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, " %-40s [raw hardware event descriptor]\n",
|
|
"rNNN");
|
|
fprintf(stderr, "\n");
|
|
|
|
print_tracepoint_events();
|
|
|
|
exit(129);
|
|
}
|