perf record: Add finished init event
authorAdrian Hunter <adrian.hunter@intel.com>
Fri, 10 Jun 2022 11:33:15 +0000 (14:33 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 23 Jun 2022 14:54:22 +0000 (11:54 -0300)
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: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo 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.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/lib/perf/include/perf/event.h
tools/perf/Documentation/perf.data-file-format.txt
tools/perf/builtin-inject.c
tools/perf/builtin-record.c
tools/perf/util/event.c
tools/perf/util/session.c
tools/perf/util/tool.h

index e7758707cadd6ab29da815c1b27a79b7acb2bc2a..9f7ca070da877078406b0a611e0523084edbfd98 100644 (file)
@@ -389,6 +389,7 @@ enum perf_user_event_type { /* above any possible kernel type */
        PERF_RECORD_TIME_CONV                   = 79,
        PERF_RECORD_HEADER_FEATURE              = 80,
        PERF_RECORD_COMPRESSED                  = 81,
+       PERF_RECORD_FINISHED_INIT               = 82,
        PERF_RECORD_HEADER_MAX
 };
 
index f56d0e0fbff6ef66b74ad6f359f819434913d349..bc9f0aa113d8b6d431219716e10ddbd9de3b70d0 100644 (file)
@@ -607,6 +607,16 @@ struct compressed_event {
        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
 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.
index a75bf11585b5c91915170336e0f2f8fecce9edfe..42e2918fd1cc131d9c94594691da820e1557286d 100644 (file)
@@ -1059,6 +1059,7 @@ int cmd_inject(int argc, const char **argv)
                        .stat           = perf_event__repipe_op2_synth,
                        .stat_round     = perf_event__repipe_op2_synth,
                        .feature        = perf_event__repipe_op2_synth,
+                       .finished_init  = perf_event__repipe_op2_synth,
                        .compressed     = perf_event__repipe_op4_synth,
                        .auxtrace       = perf_event__repipe_auxtrace,
                },
index 40dca1fba4e3be687dc535124a58f4087b088320..cf5c5379ceaa3bd5cc546c325b8e996407c1ad09 100644 (file)
@@ -1388,6 +1388,11 @@ static struct perf_event_header finished_round_event = {
        .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)
 {
        if (rec->opts.affinity != PERF_AFFINITY_SYS &&
@@ -1696,6 +1701,14 @@ static int record__synthesize_workload(struct record *rec, bool tail)
        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
@@ -1710,6 +1723,8 @@ record__switch_output(struct record *rec, bool at_exit)
 
        record__aio_mmap_read_sync(rec);
 
+       write_finished_init(rec, true);
+
        record__synthesize(rec, true);
        if (target__none(&rec->opts.target))
                record__synthesize_workload(rec, true);
@@ -1764,6 +1779,7 @@ record__switch_output(struct record *rec, bool at_exit)
                 */
                if (target__none(&rec->opts.target))
                        record__synthesize_workload(rec, false);
+               write_finished_init(rec, false);
        }
        return fd;
 }
@@ -2419,6 +2435,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
        trigger_ready(&auxtrace_snapshot_trigger);
        trigger_ready(&switch_output_trigger);
        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 (;;) {
                unsigned long long hits = thread->samples;
 
@@ -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",
                        record__waking(rec));
 
+       write_finished_init(rec, true);
+
        if (target__none(&rec->opts.target))
                record__synthesize_workload(rec, true);
 
index 0476bb3a4188a656723aa50448b9f2caec99db38..1fa14598b91669d9f5469fd9264d525a4b7938ff 100644 (file)
@@ -76,6 +76,7 @@ static const char *perf_event__names[] = {
        [PERF_RECORD_TIME_CONV]                 = "TIME_CONV",
        [PERF_RECORD_HEADER_FEATURE]            = "FEATURE",
        [PERF_RECORD_COMPRESSED]                = "COMPRESSED",
+       [PERF_RECORD_FINISHED_INIT]             = "FINISHED_INIT",
 };
 
 const char *perf_event__name(unsigned int id)
index 0aa818977d2bcb8bf1baa3914f467cb2c69ca6a0..37f833c3c81b63da0b65fdb6202580c82cbde4b3 100644 (file)
@@ -562,6 +562,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
                tool->feature = process_event_op2_stub;
        if (tool->compressed == NULL)
                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)
@@ -1706,6 +1708,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
                if (err)
                        dump_event(session->evlist, event, file_offset, &sample, file_path);
                return err;
+       case PERF_RECORD_FINISHED_INIT:
+               return tool->finished_init(session, event);
        default:
                return -EINVAL;
        }
index f2352dba1875c124168fb9235710fa20d16fa252..c957fb849ac63309437f0892a62d7dc475862f9b 100644 (file)
@@ -76,7 +76,8 @@ struct perf_tool {
                        stat_config,
                        stat,
                        stat_round,
-                       feature;
+                       feature,
+                       finished_init;
        event_op4       compressed;
        event_op3       auxtrace;
        bool            ordered_events;