Skip to content

Commit f0fbb11

Browse files
Andi Kleenacmel
Andi Kleen
authored andcommitted
perf stat: Implement duration_time as a proper event
The perf metric expression use 'duration_time' internally to normalize events. Normal 'perf stat' without -x also prints the duration time. But when using -x, the interval is not output anywhere, which is inconvenient for any post processing which often wants to normalize values to time. So implement 'duration_time' as a proper perf event that can be specified explicitely with -e. The previous implementation of 'duration_time' only worked for metric processing. This adds the concept of a tool event that is handled by the tool. On the kernel level it is still mapped to the dummy software event, but the values are not read anymore, but instead computed by the tool. Add proper plumbing to handle this in the event parser, and display it in 'perf stat'. We don't want 'duration_time' to be added up, so it's only printed for the first CPU. % perf stat -e duration_time,cycles true Performance counter stats for 'true': 555,476 ns duration_time 771,958 cycles 0.000555476 seconds time elapsed 0.000644000 seconds user 0.000000000 seconds sys Signed-off-by: Andi Kleen <ak@linux.intel.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: http://lkml.kernel.org/r/20190326221823.11518-3-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent c2b3c17 commit f0fbb11

File tree

6 files changed

+86
-13
lines changed

6 files changed

+86
-13
lines changed

tools/perf/builtin-stat.c

+21-7
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,25 @@ perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread,
244244
process_synthesized_event, NULL);
245245
}
246246

247+
static int read_single_counter(struct perf_evsel *counter, int cpu,
248+
int thread, struct timespec *rs)
249+
{
250+
if (counter->tool_event == PERF_TOOL_DURATION_TIME) {
251+
u64 val = rs->tv_nsec + rs->tv_sec*1000000000ULL;
252+
struct perf_counts_values *count =
253+
perf_counts(counter->counts, cpu, thread);
254+
count->ena = count->run = val;
255+
count->val = val;
256+
return 0;
257+
}
258+
return perf_evsel__read_counter(counter, cpu, thread);
259+
}
260+
247261
/*
248262
* Read out the results of a single counter:
249263
* do not aggregate counts across CPUs in system-wide mode
250264
*/
251-
static int read_counter(struct perf_evsel *counter)
265+
static int read_counter(struct perf_evsel *counter, struct timespec *rs)
252266
{
253267
int nthreads = thread_map__nr(evsel_list->threads);
254268
int ncpus, cpu, thread;
@@ -275,7 +289,7 @@ static int read_counter(struct perf_evsel *counter)
275289
* (via perf_evsel__read_counter) and sets threir count->loaded.
276290
*/
277291
if (!count->loaded &&
278-
perf_evsel__read_counter(counter, cpu, thread)) {
292+
read_single_counter(counter, cpu, thread, rs)) {
279293
counter->counts->scaled = -1;
280294
perf_counts(counter->counts, cpu, thread)->ena = 0;
281295
perf_counts(counter->counts, cpu, thread)->run = 0;
@@ -304,13 +318,13 @@ static int read_counter(struct perf_evsel *counter)
304318
return 0;
305319
}
306320

307-
static void read_counters(void)
321+
static void read_counters(struct timespec *rs)
308322
{
309323
struct perf_evsel *counter;
310324
int ret;
311325

312326
evlist__for_each_entry(evsel_list, counter) {
313-
ret = read_counter(counter);
327+
ret = read_counter(counter, rs);
314328
if (ret)
315329
pr_debug("failed to read counter %s\n", counter->name);
316330

@@ -323,11 +337,11 @@ static void process_interval(void)
323337
{
324338
struct timespec ts, rs;
325339

326-
read_counters();
327-
328340
clock_gettime(CLOCK_MONOTONIC, &ts);
329341
diff_timespec(&rs, &ts, &ref_time);
330342

343+
read_counters(&rs);
344+
331345
if (STAT_RECORD) {
332346
if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSEC_PER_SEC + rs.tv_nsec, INTERVAL))
333347
pr_err("failed to write stat round event\n");
@@ -593,7 +607,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
593607
* avoid arbitrary skew, we must read all counters before closing any
594608
* group leaders.
595609
*/
596-
read_counters();
610+
read_counters(&(struct timespec) { .tv_nsec = t1-t0 });
597611
perf_evlist__close(evsel_list);
598612

599613
return WEXITSTATUS(status);

tools/perf/util/evsel.h

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ struct perf_stat_evsel;
7575

7676
typedef int (perf_evsel__sb_cb_t)(union perf_event *event, void *data);
7777

78+
enum perf_tool_event {
79+
PERF_TOOL_NONE = 0,
80+
PERF_TOOL_DURATION_TIME = 1,
81+
};
82+
7883
/** struct perf_evsel - event selector
7984
*
8085
* @evlist - evlist this evsel is in, if it is in one.
@@ -121,6 +126,7 @@ struct perf_evsel {
121126
unsigned int sample_size;
122127
int id_pos;
123128
int is_pos;
129+
enum perf_tool_event tool_event;
124130
bool uniquified_name;
125131
bool snapshot;
126132
bool supported;

tools/perf/util/parse-events.c

+33-5
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,12 @@ static struct perf_evsel *
317317
__add_event(struct list_head *list, int *idx,
318318
struct perf_event_attr *attr,
319319
char *name, struct perf_pmu *pmu,
320-
struct list_head *config_terms, bool auto_merge_stats)
320+
struct list_head *config_terms, bool auto_merge_stats,
321+
const char *cpu_list)
321322
{
322323
struct perf_evsel *evsel;
323-
struct cpu_map *cpus = pmu ? pmu->cpus : NULL;
324+
struct cpu_map *cpus = pmu ? pmu->cpus :
325+
cpu_list ? cpu_map__new(cpu_list) : NULL;
324326

325327
event_attr_init(attr);
326328

@@ -348,7 +350,25 @@ static int add_event(struct list_head *list, int *idx,
348350
struct perf_event_attr *attr, char *name,
349351
struct list_head *config_terms)
350352
{
351-
return __add_event(list, idx, attr, name, NULL, config_terms, false) ? 0 : -ENOMEM;
353+
return __add_event(list, idx, attr, name, NULL, config_terms, false, NULL) ? 0 : -ENOMEM;
354+
}
355+
356+
static int add_event_tool(struct list_head *list, int *idx,
357+
enum perf_tool_event tool_event)
358+
{
359+
struct perf_evsel *evsel;
360+
struct perf_event_attr attr = {
361+
.type = PERF_TYPE_SOFTWARE,
362+
.config = PERF_COUNT_SW_DUMMY,
363+
};
364+
365+
evsel = __add_event(list, idx, &attr, NULL, NULL, NULL, false, "0");
366+
if (!evsel)
367+
return -ENOMEM;
368+
evsel->tool_event = tool_event;
369+
if (tool_event == PERF_TOOL_DURATION_TIME)
370+
evsel->unit = strdup("ns");
371+
return 0;
352372
}
353373

354374
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -1233,6 +1253,13 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
12331253
get_config_name(head_config), &config_terms);
12341254
}
12351255

1256+
int parse_events_add_tool(struct parse_events_state *parse_state,
1257+
struct list_head *list,
1258+
enum perf_tool_event tool_event)
1259+
{
1260+
return add_event_tool(list, &parse_state->idx, tool_event);
1261+
}
1262+
12361263
int parse_events_add_pmu(struct parse_events_state *parse_state,
12371264
struct list_head *list, char *name,
12381265
struct list_head *head_config,
@@ -1267,7 +1294,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
12671294

12681295
if (!head_config) {
12691296
attr.type = pmu->type;
1270-
evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats);
1297+
evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL,
1298+
auto_merge_stats, NULL);
12711299
if (evsel) {
12721300
evsel->pmu_name = name;
12731301
evsel->use_uncore_alias = use_uncore_alias;
@@ -1295,7 +1323,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
12951323

12961324
evsel = __add_event(list, &parse_state->idx, &attr,
12971325
get_config_name(head_config), pmu,
1298-
&config_terms, auto_merge_stats);
1326+
&config_terms, auto_merge_stats, NULL);
12991327
if (evsel) {
13001328
evsel->unit = info.unit;
13011329
evsel->scale = info.scale;

tools/perf/util/parse-events.h

+4
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
160160
struct list_head *list,
161161
u32 type, u64 config,
162162
struct list_head *head_config);
163+
enum perf_tool_event;
164+
int parse_events_add_tool(struct parse_events_state *parse_state,
165+
struct list_head *list,
166+
enum perf_tool_event tool_event);
163167
int parse_events_add_cache(struct list_head *list, int *idx,
164168
char *type, char *op_result1, char *op_result2,
165169
struct parse_events_error *error,

tools/perf/util/parse-events.l

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "../perf.h"
1616
#include "parse-events.h"
1717
#include "parse-events-bison.h"
18+
#include "evsel.h"
1819

1920
char *parse_events_get_text(yyscan_t yyscanner);
2021
YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
@@ -154,6 +155,14 @@ static int sym(yyscan_t scanner, int type, int config)
154155
return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
155156
}
156157

158+
static int tool(yyscan_t scanner, enum perf_tool_event event)
159+
{
160+
YYSTYPE *yylval = parse_events_get_lval(scanner);
161+
162+
yylval->num = event;
163+
return PE_VALUE_SYM_TOOL;
164+
}
165+
157166
static int term(yyscan_t scanner, int type)
158167
{
159168
YYSTYPE *yylval = parse_events_get_lval(scanner);
@@ -322,7 +331,7 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU
322331
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
323332
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
324333
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
325-
duration_time { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
334+
duration_time { return tool(yyscanner, PERF_TOOL_DURATION_TIME); }
326335
bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
327336

328337
/*

tools/perf/util/parse-events.y

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/types.h>
1515
#include "util.h"
1616
#include "pmu.h"
17+
#include "evsel.h"
1718
#include "debug.h"
1819
#include "parse-events.h"
1920
#include "parse-events-bison.h"
@@ -45,6 +46,7 @@ static void inc_group_count(struct list_head *list,
4546

4647
%token PE_START_EVENTS PE_START_TERMS
4748
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
49+
%token PE_VALUE_SYM_TOOL
4850
%token PE_EVENT_NAME
4951
%token PE_NAME
5052
%token PE_BPF_OBJECT PE_BPF_SOURCE
@@ -58,6 +60,7 @@ static void inc_group_count(struct list_head *list,
5860
%type <num> PE_VALUE
5961
%type <num> PE_VALUE_SYM_HW
6062
%type <num> PE_VALUE_SYM_SW
63+
%type <num> PE_VALUE_SYM_TOOL
6164
%type <num> PE_RAW
6265
%type <num> PE_TERM
6366
%type <str> PE_NAME
@@ -321,6 +324,15 @@ value_sym sep_slash_slash_dc
321324
ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config, NULL));
322325
$$ = list;
323326
}
327+
|
328+
PE_VALUE_SYM_TOOL sep_slash_slash_dc
329+
{
330+
struct list_head *list;
331+
332+
ALLOC_LIST(list);
333+
ABORT_ON(parse_events_add_tool(_parse_state, list, $1));
334+
$$ = list;
335+
}
324336

325337
event_legacy_cache:
326338
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config

0 commit comments

Comments
 (0)