perf tools: Reconstruct hw cache event with modifiers from perf_event_attr
[root@sandy ~]# perf record -a -e dTLB-load-misses:u usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.486 MB perf.data (~21216 samples) ] Before: [root@sandy ~]# perf evlist dTLB-load-misses [root@sandy ~]# After: [root@sandy ~]# perf evlist dTLB-load-misses:u [root@sandy ~]# Ditto for other tools. Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/n/tip-7x1b0e6jthkr93lfjzsuakk5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Родитель
27f18617b0
Коммит
0b668bc9a7
|
@ -128,6 +128,105 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
|||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__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", },
|
||||
{ "node", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
{ "load", "loads", "read", },
|
||||
{ "store", "stores", "write", },
|
||||
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__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 perf_evsel__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),
|
||||
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
};
|
||||
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
|
||||
{
|
||||
if (perf_evsel__hw_cache_stat[type] & COP(op))
|
||||
return true; /* valid */
|
||||
else
|
||||
return false; /* invalid */
|
||||
}
|
||||
|
||||
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
|
||||
char *bf, size_t size)
|
||||
{
|
||||
if (result) {
|
||||
return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][0],
|
||||
perf_evsel__hw_cache_result[result][0]);
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][1]);
|
||||
}
|
||||
|
||||
int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
|
||||
{
|
||||
u8 op, result, type = (config >> 0) & 0xff;
|
||||
const char *err = "unknown-ext-hardware-cache-type";
|
||||
|
||||
if (type > PERF_COUNT_HW_CACHE_MAX)
|
||||
goto out_err;
|
||||
|
||||
op = (config >> 8) & 0xff;
|
||||
err = "unknown-ext-hardware-cache-op";
|
||||
if (op > PERF_COUNT_HW_CACHE_OP_MAX)
|
||||
goto out_err;
|
||||
|
||||
result = (config >> 16) & 0xff;
|
||||
err = "unknown-ext-hardware-cache-result";
|
||||
if (result > PERF_COUNT_HW_CACHE_RESULT_MAX)
|
||||
goto out_err;
|
||||
|
||||
err = "invalid-cache";
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
goto out_err;
|
||||
|
||||
return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
|
||||
out_err:
|
||||
return scnprintf(bf, size, "%s", err);
|
||||
}
|
||||
|
||||
static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size);
|
||||
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
|
||||
}
|
||||
|
||||
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
@ -140,6 +239,11 @@ int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size)
|
|||
case PERF_TYPE_HARDWARE:
|
||||
ret = perf_evsel__hw_name(evsel, bf, size);
|
||||
break;
|
||||
|
||||
case PERF_TYPE_HW_CACHE:
|
||||
ret = perf_evsel__hw_cache_name(evsel, bf, size);
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* FIXME
|
||||
|
|
|
@ -83,7 +83,21 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
|||
struct perf_record_opts *opts,
|
||||
struct perf_evsel *first);
|
||||
|
||||
const char* __perf_evsel__hw_name(u64 config);
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
#define PERF_EVSEL__MAX_ALIASES 8
|
||||
|
||||
extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
|
||||
char *bf, size_t size);
|
||||
int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size);
|
||||
|
||||
const char *__perf_evsel__hw_name(u64 config);
|
||||
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
|
|
|
@ -74,51 +74,6 @@ static const char *sw_event_names[PERF_COUNT_SW_MAX] = {
|
|||
"emulation-faults",
|
||||
};
|
||||
|
||||
#define MAX_ALIASES 8
|
||||
|
||||
static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][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", },
|
||||
{ "node", },
|
||||
};
|
||||
|
||||
static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = {
|
||||
{ "load", "loads", "read", },
|
||||
{ "store", "stores", "write", },
|
||||
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
||||
};
|
||||
|
||||
static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[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),
|
||||
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
};
|
||||
|
||||
#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
|
||||
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
|
||||
if (sys_dirent.d_type == DT_DIR && \
|
||||
|
@ -236,30 +191,6 @@ static const char *tracepoint_id_to_name(u64 config)
|
|||
return buf;
|
||||
}
|
||||
|
||||
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_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
|
@ -287,7 +218,7 @@ const char *event_name(struct perf_evsel *evsel)
|
|||
u64 config = evsel->attr.config;
|
||||
int type = evsel->attr.type;
|
||||
|
||||
if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) {
|
||||
if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) {
|
||||
/*
|
||||
* XXX minimal fix, see comment on perf_evsen__name, this static buffer
|
||||
* will go away together with event_name in the next devel cycle.
|
||||
|
@ -316,26 +247,9 @@ const char *__event_name(int type, u64 config)
|
|||
case PERF_TYPE_HARDWARE:
|
||||
return __perf_evsel__hw_name(config);
|
||||
|
||||
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_HW_CACHE:
|
||||
__perf_evsel__hw_cache_name(config, buf, sizeof(buf));
|
||||
return buf;
|
||||
|
||||
case PERF_TYPE_SOFTWARE:
|
||||
if (config < PERF_COUNT_SW_MAX && sw_event_names[config])
|
||||
|
@ -379,13 +293,13 @@ static int add_event(struct list_head **_list, int *idx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
|
||||
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__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++) {
|
||||
for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {
|
||||
n = strlen(names[i][j]);
|
||||
if (n > longest && !strncasecmp(str, names[i][j], n))
|
||||
longest = n;
|
||||
|
@ -410,7 +324,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
|||
* No fallback - if we cannot get a clear cache type
|
||||
* then bail out:
|
||||
*/
|
||||
cache_type = parse_aliases(type, hw_cache,
|
||||
cache_type = parse_aliases(type, perf_evsel__hw_cache,
|
||||
PERF_COUNT_HW_CACHE_MAX);
|
||||
if (cache_type == -1)
|
||||
return -EINVAL;
|
||||
|
@ -423,18 +337,18 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
|||
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
|
||||
|
||||
if (cache_op == -1) {
|
||||
cache_op = parse_aliases(str, hw_cache_op,
|
||||
cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
|
||||
PERF_COUNT_HW_CACHE_OP_MAX);
|
||||
if (cache_op >= 0) {
|
||||
if (!is_cache_op_valid(cache_type, cache_op))
|
||||
if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache_result == -1) {
|
||||
cache_result = parse_aliases(str, hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
if (cache_result >= 0)
|
||||
continue;
|
||||
}
|
||||
|
@ -970,16 +884,17 @@ void print_events_type(u8 type)
|
|||
int print_hwcache_events(const char *event_glob)
|
||||
{
|
||||
unsigned int type, op, i, printed = 0;
|
||||
char name[64];
|
||||
|
||||
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))
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
char *name = event_cache_name(type, op, i);
|
||||
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
if (event_glob != NULL && !strglobmatch(name, event_glob))
|
||||
continue;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче