summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-inject.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-inject.c')
-rw-r--r--tools/perf/builtin-inject.c319
1 files changed, 248 insertions, 71 deletions
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 11e49cafa3af..f174bc69cec4 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -122,6 +122,7 @@ struct perf_inject {
bool in_place_update;
bool in_place_update_dry_run;
bool copy_kcore_dir;
+ bool convert_callchain;
const char *input_name;
struct perf_data output;
u64 bytes_written;
@@ -132,7 +133,8 @@ struct perf_inject {
struct perf_file_section secs[HEADER_FEAT_BITS];
struct guest_session guest_session;
struct strlist *known_build_ids;
- const struct evsel *mmap_evsel;
+ struct evsel *mmap_evsel;
+ struct ip_callchain *raw_callchain;
};
struct event_entry {
@@ -197,18 +199,20 @@ static int perf_event__drop_oe(const struct perf_tool *tool __maybe_unused,
}
#endif
-static int perf_event__repipe_op2_synth(struct perf_session *session,
+static int perf_event__repipe_op2_synth(const struct perf_tool *tool,
+ struct perf_session *session __maybe_unused,
union perf_event *event)
{
- return perf_event__repipe_synth(session->tool, event);
+ return perf_event__repipe_synth(tool, event);
}
-static int perf_event__repipe_op4_synth(struct perf_session *session,
+static int perf_event__repipe_op4_synth(const struct perf_tool *tool,
+ struct perf_session *session __maybe_unused,
union perf_event *event,
u64 data __maybe_unused,
const char *str __maybe_unused)
{
- return perf_event__repipe_synth(session->tool, event);
+ return perf_event__repipe_synth(tool, event);
}
static int perf_event__repipe_attr(const struct perf_tool *tool,
@@ -237,8 +241,6 @@ static int perf_event__repipe_event_update(const struct perf_tool *tool,
return perf_event__repipe_synth(tool, event);
}
-#ifdef HAVE_AUXTRACE_SUPPORT
-
static int copy_bytes(struct perf_inject *inject, struct perf_data *data, off_t size)
{
char buf[4096];
@@ -258,20 +260,18 @@ static int copy_bytes(struct perf_inject *inject, struct perf_data *data, off_t
return 0;
}
-static s64 perf_event__repipe_auxtrace(struct perf_session *session,
+static s64 perf_event__repipe_auxtrace(const struct perf_tool *tool,
+ struct perf_session *session,
union perf_event *event)
{
- const struct perf_tool *tool = session->tool;
- struct perf_inject *inject = container_of(tool, struct perf_inject,
- tool);
+ struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
int ret;
inject->have_auxtrace = true;
if (!inject->output.is_pipe) {
- off_t offset;
+ off_t offset = perf_data__seek(&inject->output, 0, SEEK_CUR);
- offset = lseek(inject->output.file.fd, 0, SEEK_CUR);
if (offset == -1)
return -errno;
ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
@@ -296,18 +296,6 @@ static s64 perf_event__repipe_auxtrace(struct perf_session *session,
return event->auxtrace.size;
}
-#else
-
-static s64
-perf_event__repipe_auxtrace(struct perf_session *session __maybe_unused,
- union perf_event *event __maybe_unused)
-{
- pr_err("AUX area tracing not supported\n");
- return -EINVAL;
-}
-
-#endif
-
static int perf_event__repipe(const struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
@@ -396,6 +384,90 @@ static int perf_event__repipe_sample(const struct perf_tool *tool,
return perf_event__repipe_synth(tool, event);
}
+static int perf_event__convert_sample_callchain(const struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct evsel *evsel,
+ struct machine *machine)
+{
+ struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
+ struct callchain_cursor *cursor = get_tls_callchain_cursor();
+ union perf_event *event_copy = (void *)inject->event_copy;
+ struct callchain_cursor_node *node;
+ struct thread *thread;
+ u64 sample_type = evsel->core.attr.sample_type;
+ u32 sample_size = event->header.size;
+ u64 i, k;
+ int ret;
+
+ if (event_copy == NULL) {
+ inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE);
+ if (!inject->event_copy)
+ return -ENOMEM;
+
+ event_copy = (void *)inject->event_copy;
+ }
+
+ if (cursor == NULL)
+ return -ENOMEM;
+
+ callchain_cursor_reset(cursor);
+
+ thread = machine__find_thread(machine, sample->tid, sample->pid);
+ if (thread == NULL)
+ goto out;
+
+ /* this will parse DWARF using stack and register data */
+ ret = thread__resolve_callchain(thread, cursor, evsel, sample,
+ /*parent=*/NULL, /*root_al=*/NULL,
+ PERF_MAX_STACK_DEPTH);
+ thread__put(thread);
+ if (ret != 0)
+ goto out;
+
+ /* copy kernel callchain and context entries */
+ for (i = 0; i < sample->callchain->nr; i++) {
+ inject->raw_callchain->ips[i] = sample->callchain->ips[i];
+ if (sample->callchain->ips[i] == PERF_CONTEXT_USER) {
+ i++;
+ break;
+ }
+ }
+ if (i == 0 || inject->raw_callchain->ips[i - 1] != PERF_CONTEXT_USER)
+ inject->raw_callchain->ips[i++] = PERF_CONTEXT_USER;
+
+ node = cursor->first;
+ for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) {
+ if (machine__kernel_ip(machine, node->ip))
+ /* kernel IPs were added already */;
+ else if (node->ms.sym && node->ms.sym->inlined)
+ /* we can't handle inlined callchains */;
+ else
+ inject->raw_callchain->ips[i++] = node->ip;
+
+ node = node->next;
+ }
+
+ inject->raw_callchain->nr = i;
+ sample->callchain = inject->raw_callchain;
+
+out:
+ memcpy(event_copy, event, sizeof(event->header));
+
+ /* adjust sample size for stack and regs */
+ sample_size -= sample->user_stack.size;
+ sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64);
+ sample_size += (sample->callchain->nr + 1) * sizeof(u64);
+ event_copy->header.size = sample_size;
+
+ /* remove sample_type {STACK,REGS}_USER for synthesize */
+ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER);
+
+ perf_event__synthesize_sample(event_copy, sample_type,
+ evsel->core.attr.read_format, sample);
+ return perf_event__repipe_synth(tool, event_copy);
+}
+
static struct dso *findnew_dso(int pid, int tid, const char *filename,
const struct dso_id *id, struct machine *machine)
{
@@ -446,7 +518,7 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename,
* processing mmap events. If not stashed, search the evlist for the first mmap
* gathering event.
*/
-static const struct evsel *inject__mmap_evsel(struct perf_inject *inject)
+static struct evsel *inject__mmap_evsel(struct perf_inject *inject)
{
struct evsel *pos;
@@ -587,15 +659,17 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool,
struct perf_sample *sample,
struct machine *machine)
{
- struct dso_id id;
- struct dso_id *dso_id = NULL;
+ struct dso_id id = dso_id_empty;
- if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) {
+ if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) {
+ build_id__init(&id.build_id, event->mmap2.build_id, event->mmap2.build_id_size);
+ } else {
id.maj = event->mmap2.maj;
id.min = event->mmap2.min;
id.ino = event->mmap2.ino;
id.ino_generation = event->mmap2.ino_generation;
- dso_id = &id;
+ id.mmap2_valid = true;
+ id.mmap2_ino_generation_valid = true;
}
return perf_event__repipe_common_mmap(
@@ -603,7 +677,7 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool,
event->mmap2.pid, event->mmap2.tid,
event->mmap2.start, event->mmap2.len, event->mmap2.pgoff,
event->mmap2.flags, event->mmap2.prot,
- event->mmap2.filename, dso_id,
+ event->mmap2.filename, &id,
perf_event__process_mmap2);
}
@@ -659,31 +733,33 @@ static int perf_event__repipe_exit(const struct perf_tool *tool,
}
#ifdef HAVE_LIBTRACEEVENT
-static int perf_event__repipe_tracing_data(struct perf_session *session,
+static int perf_event__repipe_tracing_data(const struct perf_tool *tool,
+ struct perf_session *session,
union perf_event *event)
{
- perf_event__repipe_synth(session->tool, event);
+ perf_event__repipe_synth(tool, event);
- return perf_event__process_tracing_data(session, event);
+ return perf_event__process_tracing_data(tool, session, event);
}
#endif
static int dso__read_build_id(struct dso *dso)
{
struct nscookie nsc;
+ struct build_id bid = { .size = 0, };
if (dso__has_build_id(dso))
return 0;
mutex_lock(dso__lock(dso));
nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- if (filename__read_build_id(dso__long_name(dso), dso__bid(dso)) > 0)
- dso__set_has_build_id(dso);
+ if (filename__read_build_id(dso__long_name(dso), &bid) > 0)
+ dso__set_build_id(dso, &bid);
else if (dso__nsinfo(dso)) {
char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso));
- if (new_name && filename__read_build_id(new_name, dso__bid(dso)) > 0)
- dso__set_has_build_id(dso);
+ if (new_name && filename__read_build_id(new_name, &bid) > 0)
+ dso__set_build_id(dso, &bid);
free(new_name);
}
nsinfo__mountns_exit(&nsc);
@@ -732,23 +808,26 @@ static bool perf_inject__lookup_known_build_id(struct perf_inject *inject,
struct dso *dso)
{
struct str_node *pos;
- int bid_len;
strlist__for_each_entry(pos, inject->known_build_ids) {
+ struct build_id bid;
const char *build_id, *dso_name;
+ size_t bid_len;
build_id = skip_spaces(pos->s);
dso_name = strchr(build_id, ' ');
bid_len = dso_name - pos->s;
+ if (bid_len > sizeof(bid.data))
+ bid_len = sizeof(bid.data);
dso_name = skip_spaces(dso_name);
if (strcmp(dso__long_name(dso), dso_name))
continue;
- for (int ix = 0; 2 * ix + 1 < bid_len; ++ix) {
- dso__bid(dso)->data[ix] = (hex(build_id[2 * ix]) << 4 |
- hex(build_id[2 * ix + 1]));
+ for (size_t ix = 0; 2 * ix + 1 < bid_len; ++ix) {
+ bid.data[ix] = (hex(build_id[2 * ix]) << 4 |
+ hex(build_id[2 * ix + 1]));
}
- dso__bid(dso)->size = bid_len / 2;
- dso__set_has_build_id(dso);
+ bid.size = bid_len / 2;
+ dso__set_build_id(dso, &bid);
return true;
}
return false;
@@ -943,7 +1022,6 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e
sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH,
/*symbols=*/false, mark_dso_hit_callback, &args);
-
thread__put(thread);
repipe:
perf_event__repipe(tool, event, sample, machine);
@@ -1007,6 +1085,7 @@ static int perf_inject__sched_stat(const struct perf_tool *tool,
struct perf_sample sample_sw;
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
u32 pid = evsel__intval(evsel, sample, "pid");
+ int ret;
list_for_each_entry(ent, &inject->samples, node) {
if (pid == ent->tid)
@@ -1023,7 +1102,9 @@ found:
perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type,
evsel->core.attr.read_format, &sample_sw);
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
- return perf_event__repipe(tool, event_sw, &sample_sw, machine);
+ ret = perf_event__repipe(tool, event_sw, &sample_sw, machine);
+ perf_sample__exit(&sample_sw);
+ return ret;
}
#endif
@@ -1342,13 +1423,14 @@ static int process_build_id(const struct perf_tool *tool,
{
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
- return perf_event__process_build_id(inject->session, event);
+ return perf_event__process_build_id(tool, inject->session, event);
}
static int synthesize_build_id(struct perf_inject *inject, struct dso *dso, pid_t machine_pid)
{
struct machine *machine = perf_session__findnew_machine(inject->session, machine_pid);
struct perf_sample synth_sample = {
+ .evsel = inject__mmap_evsel(inject),
.pid = -1,
.tid = -1,
.time = -1,
@@ -1568,6 +1650,7 @@ static int guest_session__fetch(struct guest_session *gs)
size_t hdr_sz = sizeof(*hdr);
ssize_t ret;
+ perf_sample__init(&gs->ev.sample, /*all=*/false);
buf = gs->ev.event_buf;
if (!buf) {
buf = malloc(PERF_SAMPLE_MAX_SIZE);
@@ -1665,18 +1748,24 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp)
if (!gs->fetched) {
ret = guest_session__fetch(gs);
if (ret)
- return ret;
+ break;
gs->fetched = true;
}
ev = gs->ev.event;
sample = &gs->ev.sample;
- if (!ev->header.size)
- return 0; /* EOF */
-
- if (sample->time > timestamp)
- return 0;
+ if (!ev->header.size) {
+ /* EOF */
+ perf_sample__exit(&gs->ev.sample);
+ gs->fetched = false;
+ ret = 0;
+ break;
+ }
+ if (sample->time > timestamp) {
+ ret = 0;
+ break;
+ }
/* Change cpumode to guest */
cpumode = ev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
@@ -1699,12 +1788,14 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp)
if (id_hdr_size & 7) {
pr_err("Bad id_hdr_size %u\n", id_hdr_size);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
if (ev->header.size & 7) {
pr_err("Bad event size %u\n", ev->header.size);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
/* Remove guest id sample */
@@ -1712,14 +1803,16 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp)
if (ev->header.size & 7) {
pr_err("Bad raw event size %u\n", ev->header.size);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
guest_id = guest_session__lookup_id(gs, id);
if (!guest_id) {
pr_err("Guest event with unknown id %llu\n",
(unsigned long long)id);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
/* Change to host ID to avoid conflicting ID values */
@@ -1739,19 +1832,28 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp)
/* New id sample with new ID and CPU */
ret = evlist__append_id_sample(inject->session->evlist, ev, sample);
if (ret)
- return ret;
+ break;
if (ev->header.size & 7) {
pr_err("Bad new event size %u\n", ev->header.size);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
- gs->fetched = false;
-
ret = output_bytes(inject, ev, ev->header.size);
if (ret)
- return ret;
+ break;
+
+ /* Reset for next guest session event fetch. */
+ perf_sample__exit(sample);
+ gs->fetched = false;
+ }
+ if (ret && gs->fetched) {
+ /* Clear saved sample state on error. */
+ perf_sample__exit(&gs->ev.sample);
+ gs->fetched = false;
}
+ return ret;
}
static int guest_session__flush_events(struct guest_session *gs)
@@ -1774,9 +1876,10 @@ static int host__repipe(const struct perf_tool *tool,
return perf_event__repipe(tool, event, sample, machine);
}
-static int host__finished_init(struct perf_session *session, union perf_event *event)
+static int host__finished_init(const struct perf_tool *tool, struct perf_session *session,
+ union perf_event *event)
{
- struct perf_inject *inject = container_of(session->tool, struct perf_inject, tool);
+ struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
struct guest_session *gs = &inject->guest_session;
int ret;
@@ -1823,7 +1926,7 @@ static int host__finished_init(struct perf_session *session, union perf_event *e
if (ret)
return ret;
- return perf_event__repipe_op2_synth(session, event);
+ return perf_event__repipe_op2_synth(tool, session, event);
}
/*
@@ -2027,7 +2130,7 @@ static int save_section_info(struct perf_inject *inject)
return perf_header__process_sections(header, fd, inject, save_section_info_cb);
}
-static bool keep_feat(int feat)
+static bool keep_feat(struct perf_inject *inject, int feat)
{
switch (feat) {
/* Keep original information that describes the machine or software */
@@ -2052,9 +2155,12 @@ static bool keep_feat(int feat)
case HEADER_CLOCK_DATA:
case HEADER_HYBRID_TOPOLOGY:
case HEADER_PMU_CAPS:
+ case HEADER_CPU_DOMAIN_INFO:
+ case HEADER_CLN_SIZE:
return true;
/* Information that can be updated */
case HEADER_BUILD_ID:
+ return inject->build_id_style == BID_RWS__NONE;
case HEADER_CMDLINE:
case HEADER_EVENT_DESC:
case HEADER_BRANCH_STACK:
@@ -2113,7 +2219,7 @@ static int feat_copy_cb(struct feat_copier *fc, int feat, struct feat_writer *fw
int ret;
if (!inject->secs[feat].offset ||
- !keep_feat(feat))
+ !keep_feat(inject, feat))
return 0;
ret = feat_copy(inject, feat, fw);
@@ -2274,6 +2380,15 @@ static int __cmd_inject(struct perf_inject *inject)
/* Allow space in the header for guest attributes */
output_data_offset += gs->session->header.data_offset;
output_data_offset = roundup(output_data_offset, 4096);
+ } else if (inject->convert_callchain) {
+ inject->tool.sample = perf_event__convert_sample_callchain;
+ inject->tool.fork = perf_event__repipe_fork;
+ inject->tool.comm = perf_event__repipe_comm;
+ inject->tool.exit = perf_event__repipe_exit;
+ inject->tool.mmap = perf_event__repipe_mmap;
+ inject->tool.mmap2 = perf_event__repipe_mmap2;
+ inject->tool.ordered_events = true;
+ inject->tool.ordering_requires_timestamps = true;
}
if (!inject->itrace_synth_opts.set)
@@ -2326,6 +2441,23 @@ static int __cmd_inject(struct perf_inject *inject)
perf_header__set_feat(&session->header,
HEADER_BRANCH_STACK);
}
+
+ /*
+ * The converted data file won't have stack and registers.
+ * Update the perf_event_attr to remove them before writing.
+ */
+ if (inject->convert_callchain) {
+ struct evsel *evsel;
+
+ evlist__for_each_entry(session->evlist, evsel) {
+ evsel__reset_sample_bit(evsel, REGS_USER);
+ evsel__reset_sample_bit(evsel, STACK_USER);
+ evsel->core.attr.sample_regs_user = 0;
+ evsel->core.attr.sample_stack_user = 0;
+ evsel->core.attr.exclude_callchain_user = 0;
+ }
+ }
+
session->header.data_offset = output_data_offset;
session->header.data_size = inject->bytes_written;
perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc,
@@ -2350,6 +2482,18 @@ static int __cmd_inject(struct perf_inject *inject)
return ret;
}
+static bool evsel__has_dwarf_callchain(struct evsel *evsel)
+{
+ struct perf_event_attr *attr = &evsel->core.attr;
+ const u64 dwarf_callchain_flags =
+ PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN;
+
+ if (!attr->exclude_callchain_user)
+ return false;
+
+ return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags;
+}
+
int cmd_inject(int argc, const char **argv)
{
struct perf_inject inject = {
@@ -2358,12 +2502,12 @@ int cmd_inject(int argc, const char **argv)
.output = {
.path = "-",
.mode = PERF_DATA_MODE_WRITE,
- .use_stdio = true,
+ .file.use_stdio = true,
},
};
struct perf_data data = {
.mode = PERF_DATA_MODE_READ,
- .use_stdio = true,
+ .file.use_stdio = true,
};
int ret;
const char *known_build_ids = NULL;
@@ -2418,6 +2562,8 @@ int cmd_inject(int argc, const char **argv)
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
"guest mount directory under which every guest os"
" instance has a subdir"),
+ OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain,
+ "Generate callchains using DWARF and drop register/stack data"),
OPT_END()
};
const char * const inject_usage[] = {
@@ -2434,6 +2580,9 @@ int cmd_inject(int argc, const char **argv)
#ifndef HAVE_JITDUMP
set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true);
#endif
+#ifndef HAVE_LIBDW_SUPPORT
+ set_option_nobuild(options, 0, "convert-callchain", "NO_LIBDW=1", true);
+#endif
argc = parse_options(argc, argv, options, inject_usage, 0);
/*
@@ -2530,9 +2679,14 @@ int cmd_inject(int argc, const char **argv)
inject.tool.finished_init = perf_event__repipe_op2_synth;
inject.tool.compressed = perf_event__repipe_op4_synth;
inject.tool.auxtrace = perf_event__repipe_auxtrace;
+ inject.tool.bpf_metadata = perf_event__repipe_op2_synth;
+ inject.tool.schedstat_cpu = perf_event__repipe_op2_synth;
+ inject.tool.schedstat_domain = perf_event__repipe_op2_synth;
inject.tool.dont_split_sample_group = true;
+ inject.tool.merge_deferred_callchains = false;
inject.session = __perf_session__new(&data, &inject.tool,
- /*trace_event_repipe=*/inject.output.is_pipe);
+ /*trace_event_repipe=*/inject.output.is_pipe,
+ /*host_env=*/NULL);
if (IS_ERR(inject.session)) {
ret = PTR_ERR(inject.session);
@@ -2589,6 +2743,28 @@ int cmd_inject(int argc, const char **argv)
}
}
+ if (inject.convert_callchain) {
+ struct evsel *evsel;
+
+ if (inject.output.is_pipe || inject.session->data->is_pipe) {
+ pr_err("--convert-callchain cannot work with pipe\n");
+ goto out_delete;
+ }
+
+ evlist__for_each_entry(inject.session->evlist, evsel) {
+ if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) {
+ pr_err("--convert-callchain requires DWARF call graph.\n");
+ goto out_delete;
+ }
+ }
+
+ inject.raw_callchain = calloc(PERF_MAX_STACK_DEPTH, sizeof(u64));
+ if (inject.raw_callchain == NULL) {
+ pr_err("callchain allocation failed\n");
+ goto out_delete;
+ }
+ }
+
#ifdef HAVE_JITDUMP
if (inject.jit_mode) {
inject.tool.mmap2 = perf_event__repipe_mmap2;
@@ -2601,7 +2777,7 @@ int cmd_inject(int argc, const char **argv)
inject.tool.finished_round = perf_event__drop_oe;
}
#endif
- ret = symbol__init(&inject.session->header.env);
+ ret = symbol__init(perf_session__env(inject.session));
if (ret < 0)
goto out_delete;
@@ -2619,5 +2795,6 @@ out_close_output:
free(inject.itrace_synth_opts.vm_tm_corr_args);
free(inject.event_copy);
free(inject.guest_session.ev.event_buf);
+ free(inject.raw_callchain);
return ret;
}