Commit 3812d298 authored by Adrian Hunter's avatar Adrian Hunter Committed by Arnaldo Carvalho de Melo

perf record: Add finished init event

In preparation for recording sideband events in a virtual machine guest so
that they can be injected into a host perf.data file.

This is needed to enable injecting events after the initial synthesized
user events (that have an all zero id sample) but before regular events.

Committer notes:

Add entry about PERF_RECORD_FINISHED_INIT to
tools/perf/Documentation/perf.data-file-format.txt.

Committer testing:

Before:

  # perf report -D | grep FINISHED
  0 0x5910 [0x8]: PERF_RECORD_FINISHED_ROUND
    FINISHED_ROUND events:          1  ( 0.5%)
  #

After:

  # perf record -- sleep 1
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.020 MB perf.data (7 samples) ]
  # perf report -D | grep FINISHED
  0 0x5068 [0x8]: PERF_RECORD_FINISHED_INIT: unhandled!
  0 0x5390 [0x8]: PERF_RECORD_FINISHED_ROUND
    FINISHED_ROUND events:          1  ( 0.5%)
     FINISHED_INIT events:          1  ( 0.5%)
  #
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Acked-by: default avatarIan Rogers <irogers@google.com>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20220610113316.6682-5-adrian.hunter@intel.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 61110883
...@@ -389,6 +389,7 @@ enum perf_user_event_type { /* above any possible kernel type */ ...@@ -389,6 +389,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_TIME_CONV = 79, PERF_RECORD_TIME_CONV = 79,
PERF_RECORD_HEADER_FEATURE = 80, PERF_RECORD_HEADER_FEATURE = 80,
PERF_RECORD_COMPRESSED = 81, PERF_RECORD_COMPRESSED = 81,
PERF_RECORD_FINISHED_INIT = 82,
PERF_RECORD_HEADER_MAX PERF_RECORD_HEADER_MAX
}; };
......
...@@ -607,6 +607,16 @@ struct compressed_event { ...@@ -607,6 +607,16 @@ struct compressed_event {
char data[]; char data[];
}; };
PERF_RECORD_FINISHED_INIT = 82,
Marks the end of records for the system, pre-existing threads in system wide
sessions, etc. Those are the ones prefixed PERF_RECORD_USER_*.
This is used, for instance, to 'perf inject' events after init and before
regular events, those emitted by the kernel, to support combining guest and
host records.
The header is followed by compressed data frame that can be decompressed The header is followed by compressed data frame that can be decompressed
into array of perf trace records. The size of the entire compressed event into array of perf trace records. The size of the entire compressed event
record including the header is limited by the max value of header.size. record including the header is limited by the max value of header.size.
......
...@@ -1059,6 +1059,7 @@ int cmd_inject(int argc, const char **argv) ...@@ -1059,6 +1059,7 @@ int cmd_inject(int argc, const char **argv)
.stat = perf_event__repipe_op2_synth, .stat = perf_event__repipe_op2_synth,
.stat_round = perf_event__repipe_op2_synth, .stat_round = perf_event__repipe_op2_synth,
.feature = perf_event__repipe_op2_synth, .feature = perf_event__repipe_op2_synth,
.finished_init = perf_event__repipe_op2_synth,
.compressed = perf_event__repipe_op4_synth, .compressed = perf_event__repipe_op4_synth,
.auxtrace = perf_event__repipe_auxtrace, .auxtrace = perf_event__repipe_auxtrace,
}, },
......
...@@ -1388,6 +1388,11 @@ static struct perf_event_header finished_round_event = { ...@@ -1388,6 +1388,11 @@ static struct perf_event_header finished_round_event = {
.type = PERF_RECORD_FINISHED_ROUND, .type = PERF_RECORD_FINISHED_ROUND,
}; };
static struct perf_event_header finished_init_event = {
.size = sizeof(struct perf_event_header),
.type = PERF_RECORD_FINISHED_INIT,
};
static void record__adjust_affinity(struct record *rec, struct mmap *map) static void record__adjust_affinity(struct record *rec, struct mmap *map)
{ {
if (rec->opts.affinity != PERF_AFFINITY_SYS && if (rec->opts.affinity != PERF_AFFINITY_SYS &&
...@@ -1696,6 +1701,14 @@ static int record__synthesize_workload(struct record *rec, bool tail) ...@@ -1696,6 +1701,14 @@ static int record__synthesize_workload(struct record *rec, bool tail)
return err; return err;
} }
static int write_finished_init(struct record *rec, bool tail)
{
if (rec->opts.tail_synthesize != tail)
return 0;
return record__write(rec, NULL, &finished_init_event, sizeof(finished_init_event));
}
static int record__synthesize(struct record *rec, bool tail); static int record__synthesize(struct record *rec, bool tail);
static int static int
...@@ -1710,6 +1723,8 @@ record__switch_output(struct record *rec, bool at_exit) ...@@ -1710,6 +1723,8 @@ record__switch_output(struct record *rec, bool at_exit)
record__aio_mmap_read_sync(rec); record__aio_mmap_read_sync(rec);
write_finished_init(rec, true);
record__synthesize(rec, true); record__synthesize(rec, true);
if (target__none(&rec->opts.target)) if (target__none(&rec->opts.target))
record__synthesize_workload(rec, true); record__synthesize_workload(rec, true);
...@@ -1764,6 +1779,7 @@ record__switch_output(struct record *rec, bool at_exit) ...@@ -1764,6 +1779,7 @@ record__switch_output(struct record *rec, bool at_exit)
*/ */
if (target__none(&rec->opts.target)) if (target__none(&rec->opts.target))
record__synthesize_workload(rec, false); record__synthesize_workload(rec, false);
write_finished_init(rec, false);
} }
return fd; return fd;
} }
...@@ -2419,6 +2435,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -2419,6 +2435,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
trigger_ready(&auxtrace_snapshot_trigger); trigger_ready(&auxtrace_snapshot_trigger);
trigger_ready(&switch_output_trigger); trigger_ready(&switch_output_trigger);
perf_hooks__invoke_record_start(); perf_hooks__invoke_record_start();
/*
* Must write FINISHED_INIT so it will be seen after all other
* synthesized user events, but before any regular events.
*/
err = write_finished_init(rec, false);
if (err < 0)
goto out_child;
for (;;) { for (;;) {
unsigned long long hits = thread->samples; unsigned long long hits = thread->samples;
...@@ -2563,6 +2588,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -2563,6 +2588,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n",
record__waking(rec)); record__waking(rec));
write_finished_init(rec, true);
if (target__none(&rec->opts.target)) if (target__none(&rec->opts.target))
record__synthesize_workload(rec, true); record__synthesize_workload(rec, true);
......
...@@ -76,6 +76,7 @@ static const char *perf_event__names[] = { ...@@ -76,6 +76,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_TIME_CONV] = "TIME_CONV", [PERF_RECORD_TIME_CONV] = "TIME_CONV",
[PERF_RECORD_HEADER_FEATURE] = "FEATURE", [PERF_RECORD_HEADER_FEATURE] = "FEATURE",
[PERF_RECORD_COMPRESSED] = "COMPRESSED", [PERF_RECORD_COMPRESSED] = "COMPRESSED",
[PERF_RECORD_FINISHED_INIT] = "FINISHED_INIT",
}; };
const char *perf_event__name(unsigned int id) const char *perf_event__name(unsigned int id)
......
...@@ -562,6 +562,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) ...@@ -562,6 +562,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->feature = process_event_op2_stub; tool->feature = process_event_op2_stub;
if (tool->compressed == NULL) if (tool->compressed == NULL)
tool->compressed = perf_session__process_compressed_event; tool->compressed = perf_session__process_compressed_event;
if (tool->finished_init == NULL)
tool->finished_init = process_event_op2_stub;
} }
static void swap_sample_id_all(union perf_event *event, void *data) static void swap_sample_id_all(union perf_event *event, void *data)
...@@ -1706,6 +1708,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, ...@@ -1706,6 +1708,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
if (err) if (err)
dump_event(session->evlist, event, file_offset, &sample, file_path); dump_event(session->evlist, event, file_offset, &sample, file_path);
return err; return err;
case PERF_RECORD_FINISHED_INIT:
return tool->finished_init(session, event);
default: default:
return -EINVAL; return -EINVAL;
} }
......
...@@ -76,7 +76,8 @@ struct perf_tool { ...@@ -76,7 +76,8 @@ struct perf_tool {
stat_config, stat_config,
stat, stat,
stat_round, stat_round,
feature; feature,
finished_init;
event_op4 compressed; event_op4 compressed;
event_op3 auxtrace; event_op3 auxtrace;
bool ordered_events; bool ordered_events;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment