Skip to content

Commit 26a031e

Browse files
avaginacmel
authored andcommitted
perf inject: Merge sched_stat_* and sched_switch events
You may want to know where and how long a task is sleeping. A callchain may be found in sched_switch and a time slice in stat_iowait, so I add handler in perf inject for merging this events. My code saves sched_switch event for each process and when it meets stat_iowait, it reports the sched_switch event, because this event contains a correct callchain. By another words it replaces all stat_iowait events on proper sched_switch events. I use the next sequence of commands for testing: perf record -e sched:sched_stat_sleep -e sched:sched_switch \ -e sched:sched_process_exit -g -o ~/perf.data.raw \ ~/test-program perf inject -v -s -i ~/perf.data.raw -o ~/perf.data perf report --stdio -i ~/perf.data 100.00% foo [kernel.kallsyms] [k] __schedule | --- __schedule schedule | |--79.75%-- schedule_hrtimeout_range_clock | schedule_hrtimeout_range | poll_schedule_timeout | do_select | core_sys_select | sys_select | system_call_fastpath | __select | __libc_start_main | --20.25%-- do_nanosleep hrtimer_nanosleep sys_nanosleep system_call_fastpath __GI___libc_nanosleep __libc_start_main And here is test-program.c: #include<unistd.h> #include<time.h> #include<sys/select.h> int main() { struct timespec ts1; struct timeval tv1; int i; long s; for (i = 0; i < 10; i++) { ts1.tv_sec = 0; ts1.tv_nsec = 10000000; nanosleep(&ts1, NULL); tv1.tv_sec = 0; tv1.tv_usec = 40000; select(0, NULL, NULL, NULL,&tv1); } return 1; } Signed-off-by: Andrew Vagin <[email protected]> Acked-by: Frederic Weisbecker <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Paul Mackerras <[email protected]> Cc: Peter Zijlstra <[email protected]> Link: http://lkml.kernel.org/r/[email protected] [ committer note: Made it use evsel->handler ] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent e558a5b commit 26a031e

File tree

2 files changed

+144
-3
lines changed

2 files changed

+144
-3
lines changed

tools/perf/Documentation/perf-inject.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ OPTIONS
3535
-o::
3636
--output=::
3737
Output file name. (default: stdout)
38+
-s::
39+
--sched-stat::
40+
Merge sched_stat and sched_switch for getting events where and how long
41+
tasks slept. sched_switch contains a callchain where a task slept and
42+
sched_stat contains a timeslice how long a task slept.
3843

3944
SEE ALSO
4045
--------

tools/perf/builtin-inject.c

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,32 @@
88
#include "builtin.h"
99

1010
#include "perf.h"
11+
#include "util/color.h"
12+
#include "util/evlist.h"
13+
#include "util/evsel.h"
1114
#include "util/session.h"
1215
#include "util/tool.h"
1316
#include "util/debug.h"
1417

1518
#include "util/parse-options.h"
1619

20+
#include <linux/list.h>
21+
1722
struct perf_inject {
1823
struct perf_tool tool;
1924
bool build_ids;
25+
bool sched_stat;
2026
const char *input_name;
2127
int pipe_output,
2228
output;
2329
u64 bytes_written;
30+
struct list_head samples;
31+
};
32+
33+
struct event_entry {
34+
struct list_head node;
35+
u32 tid;
36+
union perf_event event[0];
2437
};
2538

2639
static int perf_event__repipe_synth(struct perf_tool *tool,
@@ -86,12 +99,23 @@ static int perf_event__repipe(struct perf_tool *tool,
8699
return perf_event__repipe_synth(tool, event, machine);
87100
}
88101

102+
typedef int (*inject_handler)(struct perf_tool *tool,
103+
union perf_event *event,
104+
struct perf_sample *sample,
105+
struct perf_evsel *evsel,
106+
struct machine *machine);
107+
89108
static int perf_event__repipe_sample(struct perf_tool *tool,
90109
union perf_event *event,
91-
struct perf_sample *sample __maybe_unused,
92-
struct perf_evsel *evsel __maybe_unused,
93-
struct machine *machine)
110+
struct perf_sample *sample,
111+
struct perf_evsel *evsel,
112+
struct machine *machine)
94113
{
114+
if (evsel->handler.func) {
115+
inject_handler f = evsel->handler.func;
116+
return f(tool, event, sample, evsel, machine);
117+
}
118+
95119
return perf_event__repipe_synth(tool, event, machine);
96120
}
97121

@@ -216,13 +240,101 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
216240
return 0;
217241
}
218242

243+
static int perf_inject__sched_process_exit(struct perf_tool *tool,
244+
union perf_event *event __maybe_unused,
245+
struct perf_sample *sample,
246+
struct perf_evsel *evsel __maybe_unused,
247+
struct machine *machine __maybe_unused)
248+
{
249+
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
250+
struct event_entry *ent;
251+
252+
list_for_each_entry(ent, &inject->samples, node) {
253+
if (sample->tid == ent->tid) {
254+
list_del_init(&ent->node);
255+
free(ent);
256+
break;
257+
}
258+
}
259+
260+
return 0;
261+
}
262+
263+
static int perf_inject__sched_switch(struct perf_tool *tool,
264+
union perf_event *event,
265+
struct perf_sample *sample,
266+
struct perf_evsel *evsel,
267+
struct machine *machine)
268+
{
269+
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
270+
struct event_entry *ent;
271+
272+
perf_inject__sched_process_exit(tool, event, sample, evsel, machine);
273+
274+
ent = malloc(event->header.size + sizeof(struct event_entry));
275+
if (ent == NULL) {
276+
color_fprintf(stderr, PERF_COLOR_RED,
277+
"Not enough memory to process sched switch event!");
278+
return -1;
279+
}
280+
281+
ent->tid = sample->tid;
282+
memcpy(&ent->event, event, event->header.size);
283+
list_add(&ent->node, &inject->samples);
284+
return 0;
285+
}
286+
287+
static int perf_inject__sched_stat(struct perf_tool *tool,
288+
union perf_event *event __maybe_unused,
289+
struct perf_sample *sample,
290+
struct perf_evsel *evsel,
291+
struct machine *machine)
292+
{
293+
struct event_entry *ent;
294+
union perf_event *event_sw;
295+
struct perf_sample sample_sw;
296+
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
297+
u32 pid = perf_evsel__intval(evsel, sample, "pid");
298+
299+
list_for_each_entry(ent, &inject->samples, node) {
300+
if (pid == ent->tid)
301+
goto found;
302+
}
303+
304+
return 0;
305+
found:
306+
event_sw = &ent->event[0];
307+
perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
308+
309+
sample_sw.period = sample->period;
310+
sample_sw.time = sample->time;
311+
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
312+
&sample_sw, false);
313+
return perf_event__repipe(tool, event_sw, &sample_sw, machine);
314+
}
315+
219316
extern volatile int session_done;
220317

221318
static void sig_handler(int sig __maybe_unused)
222319
{
223320
session_done = 1;
224321
}
225322

323+
static int perf_evsel__check_stype(struct perf_evsel *evsel,
324+
u64 sample_type, const char *sample_msg)
325+
{
326+
struct perf_event_attr *attr = &evsel->attr;
327+
const char *name = perf_evsel__name(evsel);
328+
329+
if (!(attr->sample_type & sample_type)) {
330+
pr_err("Samples for %s event do not have %s attribute set.",
331+
name, sample_msg);
332+
return -EINVAL;
333+
}
334+
335+
return 0;
336+
}
337+
226338
static int __cmd_inject(struct perf_inject *inject)
227339
{
228340
struct perf_session *session;
@@ -241,6 +353,26 @@ static int __cmd_inject(struct perf_inject *inject)
241353
if (session == NULL)
242354
return -ENOMEM;
243355

356+
if (inject->sched_stat) {
357+
struct perf_evsel *evsel;
358+
359+
inject->tool.ordered_samples = true;
360+
361+
list_for_each_entry(evsel, &session->evlist->entries, node) {
362+
const char *name = perf_evsel__name(evsel);
363+
364+
if (!strcmp(name, "sched:sched_switch")) {
365+
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
366+
return -EINVAL;
367+
368+
evsel->handler.func = perf_inject__sched_switch;
369+
} else if (!strcmp(name, "sched:sched_process_exit"))
370+
evsel->handler.func = perf_inject__sched_process_exit;
371+
else if (!strncmp(name, "sched:sched_stat_", 17))
372+
evsel->handler.func = perf_inject__sched_stat;
373+
}
374+
}
375+
244376
if (!inject->pipe_output)
245377
lseek(inject->output, session->header.data_offset, SEEK_SET);
246378

@@ -275,6 +407,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
275407
.build_id = perf_event__repipe_op2_synth,
276408
},
277409
.input_name = "-",
410+
.samples = LIST_HEAD_INIT(inject.samples),
278411
};
279412
const char *output_name = "-";
280413
const struct option options[] = {
@@ -284,6 +417,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
284417
"input file name"),
285418
OPT_STRING('o', "output", &output_name, "file",
286419
"output file name"),
420+
OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
421+
"Merge sched-stat and sched-switch for getting events "
422+
"where and how long tasks slept"),
287423
OPT_INCR('v', "verbose", &verbose,
288424
"be more verbose (show build ids, etc)"),
289425
OPT_END()

0 commit comments

Comments
 (0)