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;
struct guest_session guest_session;
struct strlist *known_build_ids;
const struct evsel *mmap_evsel;
+ struct ip_callchain *raw_callchain;
};
struct event_entry {
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)
{
/* 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)
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,
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 = {
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[] = {
#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);
}
}
+ 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)) {
+ 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;
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;
}