summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/build-id.c3
-rw-r--r--tools/perf/util/callchain.c4
-rw-r--r--tools/perf/util/callchain.h6
-rw-r--r--tools/perf/util/comm.h4
-rw-r--r--tools/perf/util/db-export.c270
-rw-r--r--tools/perf/util/db-export.h86
-rw-r--r--tools/perf/util/dso.h6
-rw-r--r--tools/perf/util/event.c20
-rw-r--r--tools/perf/util/event.h16
-rw-r--r--tools/perf/util/evlist.c28
-rw-r--r--tools/perf/util/evsel.h5
-rw-r--r--tools/perf/util/find-vdso-map.c30
-rw-r--r--tools/perf/util/header.c7
-rw-r--r--tools/perf/util/machine.c55
-rw-r--r--tools/perf/util/machine.h17
-rw-r--r--tools/perf/util/map.c8
-rw-r--r--tools/perf/util/map.h4
-rw-r--r--tools/perf/util/parse-options.c78
-rw-r--r--tools/perf/util/parse-options.h4
-rw-r--r--tools/perf/util/pmu.c41
-rw-r--r--tools/perf/util/pmu.h1
-rw-r--r--tools/perf/util/probe-event.c18
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c292
-rw-r--r--tools/perf/util/session.c141
-rw-r--r--tools/perf/util/session.h15
-rw-r--r--tools/perf/util/thread.c6
-rw-r--r--tools/perf/util/thread.h6
-rw-r--r--tools/perf/util/tool.h3
-rw-r--r--tools/perf/util/unwind-libdw.c8
-rw-r--r--tools/perf/util/unwind-libunwind.c17
-rw-r--r--tools/perf/util/unwind.h2
-rw-r--r--tools/perf/util/vdso.c217
-rw-r--r--tools/perf/util/vdso.h4
33 files changed, 1256 insertions, 166 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index a904a4cfe7d3..2e7c68e39330 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -33,8 +33,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
return -1;
}
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->ip, &al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
if (al.map != NULL)
al.map->dso->hit = 1;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index c84d3f8dcb75..00229809a904 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -754,8 +754,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
sort__has_parent) {
- return machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, parent, al, max_stack);
+ return thread__resolve_callchain(al->thread, evsel, sample,
+ parent, al, max_stack);
}
return 0;
}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 94cfefddf4db..3caccc2c173c 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -184,11 +184,9 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
}
#ifdef HAVE_SKIP_CALLCHAIN_IDX
-extern int arch_skip_callchain_idx(struct machine *machine,
- struct thread *thread, struct ip_callchain *chain);
+extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain);
#else
-static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused,
- struct thread *thread __maybe_unused,
+static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused,
struct ip_callchain *chain __maybe_unused)
{
return -1;
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index 51c10ab257f8..71c9c39340d4 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -12,6 +12,10 @@ struct comm {
u64 start;
struct list_head list;
bool exec;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
};
void comm__free(struct comm *comm);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
new file mode 100644
index 000000000000..be128b075a32
--- /dev/null
+++ b/tools/perf/util/db-export.c
@@ -0,0 +1,270 @@
+/*
+ * db-export.c: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <errno.h>
+
+#include "evsel.h"
+#include "machine.h"
+#include "thread.h"
+#include "comm.h"
+#include "symbol.h"
+#include "event.h"
+#include "db-export.h"
+
+int db_export__init(struct db_export *dbe)
+{
+ memset(dbe, 0, sizeof(struct db_export));
+ return 0;
+}
+
+void db_export__exit(struct db_export *dbe __maybe_unused)
+{
+}
+
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ if (evsel->db_id)
+ return 0;
+
+ evsel->db_id = ++dbe->evsel_last_db_id;
+
+ if (dbe->export_evsel)
+ return dbe->export_evsel(dbe, evsel);
+
+ return 0;
+}
+
+int db_export__machine(struct db_export *dbe, struct machine *machine)
+{
+ if (machine->db_id)
+ return 0;
+
+ machine->db_id = ++dbe->machine_last_db_id;
+
+ if (dbe->export_machine)
+ return dbe->export_machine(dbe, machine);
+
+ return 0;
+}
+
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm)
+{
+ u64 main_thread_db_id = 0;
+ int err;
+
+ if (thread->db_id)
+ return 0;
+
+ thread->db_id = ++dbe->thread_last_db_id;
+
+ if (thread->pid_ != -1) {
+ struct thread *main_thread;
+
+ if (thread->pid_ == thread->tid) {
+ main_thread = thread;
+ } else {
+ main_thread = machine__findnew_thread(machine,
+ thread->pid_,
+ thread->pid_);
+ if (!main_thread)
+ return -ENOMEM;
+ err = db_export__thread(dbe, main_thread, machine,
+ comm);
+ if (err)
+ return err;
+ if (comm) {
+ err = db_export__comm_thread(dbe, comm, thread);
+ if (err)
+ return err;
+ }
+ }
+ main_thread_db_id = main_thread->db_id;
+ }
+
+ if (dbe->export_thread)
+ return dbe->export_thread(dbe, thread, main_thread_db_id,
+ machine);
+
+ return 0;
+}
+
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread)
+{
+ int err;
+
+ if (comm->db_id)
+ return 0;
+
+ comm->db_id = ++dbe->comm_last_db_id;
+
+ if (dbe->export_comm) {
+ err = dbe->export_comm(dbe, comm);
+ if (err)
+ return err;
+ }
+
+ return db_export__comm_thread(dbe, comm, main_thread);
+}
+
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread)
+{
+ u64 db_id;
+
+ db_id = ++dbe->comm_thread_last_db_id;
+
+ if (dbe->export_comm_thread)
+ return dbe->export_comm_thread(dbe, db_id, comm, thread);
+
+ return 0;
+}
+
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ if (dso->db_id)
+ return 0;
+
+ dso->db_id = ++dbe->dso_last_db_id;
+
+ if (dbe->export_dso)
+ return dbe->export_dso(dbe, dso, machine);
+
+ return 0;
+}
+
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ u64 *sym_db_id = symbol__priv(sym);
+
+ if (*sym_db_id)
+ return 0;
+
+ *sym_db_id = ++dbe->symbol_last_db_id;
+
+ if (dbe->export_symbol)
+ return dbe->export_symbol(dbe, sym, dso);
+
+ return 0;
+}
+
+static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
+{
+ if (thread->pid_ == thread->tid)
+ return thread;
+
+ if (thread->pid_ == -1)
+ return NULL;
+
+ return machine__find_thread(machine, thread->pid_, thread->pid_);
+}
+
+static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
+ u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
+{
+ int err;
+
+ if (al->map) {
+ struct dso *dso = al->map->dso;
+
+ err = db_export__dso(dbe, dso, al->machine);
+ if (err)
+ return err;
+ *dso_db_id = dso->db_id;
+
+ if (!al->sym) {
+ al->sym = symbol__new(al->addr, 0, 0, "unknown");
+ if (al->sym)
+ symbols__insert(&dso->symbols[al->map->type],
+ al->sym);
+ }
+
+ if (al->sym) {
+ u64 *db_id = symbol__priv(al->sym);
+
+ err = db_export__symbol(dbe, al->sym, dso);
+ if (err)
+ return err;
+ *sym_db_id = *db_id;
+ *offset = al->addr - al->sym->start;
+ }
+ }
+
+ return 0;
+}
+
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al)
+{
+ struct export_sample es = {
+ .event = event,
+ .sample = sample,
+ .evsel = evsel,
+ .thread = thread,
+ .al = al,
+ };
+ struct thread *main_thread;
+ struct comm *comm = NULL;
+ int err;
+
+ err = db_export__evsel(dbe, evsel);
+ if (err)
+ return err;
+
+ err = db_export__machine(dbe, al->machine);
+ if (err)
+ return err;
+
+ main_thread = get_main_thread(al->machine, thread);
+ if (main_thread)
+ comm = machine__thread_exec_comm(al->machine, main_thread);
+
+ err = db_export__thread(dbe, thread, al->machine, comm);
+ if (err)
+ return err;
+
+ if (comm) {
+ err = db_export__comm(dbe, comm, main_thread);
+ if (err)
+ return err;
+ es.comm_db_id = comm->db_id;
+ }
+
+ es.db_id = ++dbe->sample_last_db_id;
+
+ err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
+ if (err)
+ return err;
+
+ if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
+ sample_addr_correlates_sym(&evsel->attr)) {
+ struct addr_location addr_al;
+
+ perf_event__preprocess_sample_addr(event, sample, thread, &addr_al);
+ err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
+ &es.addr_sym_db_id, &es.addr_offset);
+ if (err)
+ return err;
+ }
+
+ if (dbe->export_sample)
+ return dbe->export_sample(dbe, &es);
+
+ return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
new file mode 100644
index 000000000000..b3643e8e5750
--- /dev/null
+++ b/tools/perf/util/db-export.h
@@ -0,0 +1,86 @@
+/*
+ * db-export.h: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_DB_EXPORT_H
+#define __PERF_DB_EXPORT_H
+
+#include <linux/types.h>
+
+struct perf_evsel;
+struct machine;
+struct thread;
+struct comm;
+struct dso;
+struct perf_sample;
+struct addr_location;
+
+struct export_sample {
+ union perf_event *event;
+ struct perf_sample *sample;
+ struct perf_evsel *evsel;
+ struct thread *thread;
+ struct addr_location *al;
+ u64 db_id;
+ u64 comm_db_id;
+ u64 dso_db_id;
+ u64 sym_db_id;
+ u64 offset; /* ip offset from symbol start */
+ u64 addr_dso_db_id;
+ u64 addr_sym_db_id;
+ u64 addr_offset; /* addr offset from symbol start */
+};
+
+struct db_export {
+ int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+ int (*export_machine)(struct db_export *dbe, struct machine *machine);
+ int (*export_thread)(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine);
+ int (*export_comm)(struct db_export *dbe, struct comm *comm);
+ int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread);
+ int (*export_dso)(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+ int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+ int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+ u64 evsel_last_db_id;
+ u64 machine_last_db_id;
+ u64 thread_last_db_id;
+ u64 comm_last_db_id;
+ u64 comm_thread_last_db_id;
+ u64 dso_last_db_id;
+ u64 symbol_last_db_id;
+ u64 sample_last_db_id;
+};
+
+int db_export__init(struct db_export *dbe);
+void db_export__exit(struct db_export *dbe);
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__machine(struct db_export *dbe, struct machine *machine);
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm);
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread);
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread);
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al);
+
+#endif
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index acb651acc7fd..a316e4af321f 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -127,6 +127,7 @@ struct dso {
const char *long_name;
u16 long_name_len;
u16 short_name_len;
+ void *dwfl; /* DWARF debug info */
/* dso data file */
struct {
@@ -138,6 +139,11 @@ struct dso {
struct list_head open_entry;
} data;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
+
char name[0];
};
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 4af6b279e34a..6c6d044e959a 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
[PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
[PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
+ [PERF_RECORD_ID_INDEX] = "ID_INDEX",
};
const char *perf_event__name(unsigned int id)
@@ -730,12 +731,12 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,
return machine__process_event(machine, event, sample);
}
-void thread__find_addr_map(struct thread *thread,
- struct machine *machine, u8 cpumode,
+void thread__find_addr_map(struct thread *thread, u8 cpumode,
enum map_type type, u64 addr,
struct addr_location *al)
{
struct map_groups *mg = thread->mg;
+ struct machine *machine = mg->machine;
bool load_map = false;
al->machine = machine;
@@ -806,14 +807,14 @@ try_again:
}
}
-void thread__find_addr_location(struct thread *thread, struct machine *machine,
+void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al)
{
- thread__find_addr_map(thread, machine, cpumode, type, addr, al);
+ thread__find_addr_map(thread, cpumode, type, addr, al);
if (al->map != NULL)
al->sym = map__find_symbol(al->map, al->addr,
- machine->symbol_filter);
+ thread->mg->machine->symbol_filter);
else
al->sym = NULL;
}
@@ -842,8 +843,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
machine->vmlinux_maps[MAP__FUNCTION] == NULL)
machine__create_kernel_maps(machine);
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->ip, al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al);
dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>");
@@ -902,16 +902,14 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr)
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
- struct machine *machine,
struct thread *thread,
struct addr_location *al)
{
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
- sample->addr, al);
+ thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al);
if (!al->map)
- thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
+ thread__find_addr_map(thread, cpumode, MAP__VARIABLE,
sample->addr, al);
al->cpu = sample->cpu;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 5699e7e2a790..8c7fe9d64e79 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_BUILD_ID = 67,
PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_ID_INDEX = 69,
PERF_RECORD_HEADER_MAX
};
@@ -239,6 +240,19 @@ struct tracing_data_event {
u32 size;
};
+struct id_index_entry {
+ u64 id;
+ u64 idx;
+ u64 cpu;
+ u64 tid;
+};
+
+struct id_index_event {
+ struct perf_event_header header;
+ u64 nr;
+ struct id_index_entry entries[0];
+};
+
union perf_event {
struct perf_event_header header;
struct mmap_event mmap;
@@ -253,6 +267,7 @@ union perf_event {
struct event_type_event event_type;
struct tracing_data_event tracing_data;
struct build_id_event build_id;
+ struct id_index_event id_index;
};
void perf_event__print_totals(void);
@@ -322,7 +337,6 @@ bool is_bts_event(struct perf_event_attr *attr);
bool sample_addr_correlates_sym(struct perf_event_attr *attr);
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
- struct machine *machine,
struct thread *thread,
struct addr_location *al);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 3c9e77d6b4c2..7e23dae54f1d 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -413,7 +413,7 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
int nfds = 0;
struct perf_evsel *evsel;
- list_for_each_entry(evsel, &evlist->entries, node) {
+ evlist__for_each(evlist, evsel) {
if (evsel->system_wide)
nfds += nr_cpus;
else
@@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
return 0;
}
+static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
+ struct perf_evsel *evsel, int idx, int cpu,
+ int thread)
+{
+ struct perf_sample_id *sid = SID(evsel, cpu, thread);
+ sid->idx = idx;
+ if (evlist->cpus && cpu >= 0)
+ sid->cpu = evlist->cpus->map[cpu];
+ else
+ sid->cpu = -1;
+ if (!evsel->system_wide && evlist->threads && thread >= 0)
+ sid->tid = evlist->threads->map[thread];
+ else
+ sid->tid = -1;
+}
+
struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
{
struct hlist_head *head;
@@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
return -1;
}
- if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
- perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
- return -1;
+ if (evsel->attr.read_format & PERF_FORMAT_ID) {
+ if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
+ fd) < 0)
+ return -1;
+ perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
+ thread);
+ }
}
return 0;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 163c5604e5d1..979790951bfb 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -36,6 +36,9 @@ struct perf_sample_id {
struct hlist_node node;
u64 id;
struct perf_evsel *evsel;
+ int idx;
+ int cpu;
+ pid_t tid;
/* Holds total ID period value for PERF_SAMPLE_READ processing. */
u64 period;
@@ -54,6 +57,7 @@ struct cgroup_sel;
* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
* is used there is an id sample appended to non-sample events
+ * @priv: And what is in its containing unnamed union are tool specific
*/
struct perf_evsel {
struct list_head node;
@@ -73,6 +77,7 @@ struct perf_evsel {
union {
void *priv;
off_t id_offset;
+ u64 db_id;
};
struct cgroup_sel *cgrp;
void *handler;
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c
new file mode 100644
index 000000000000..95ef1cffc056
--- /dev/null
+++ b/tools/perf/util/find-vdso-map.c
@@ -0,0 +1,30 @@
+static int find_vdso_map(void **start, void **end)
+{
+ FILE *maps;
+ char line[128];
+ int found = 0;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ fprintf(stderr, "vdso: cannot open maps\n");
+ return -1;
+ }
+
+ while (!found && fgets(line, sizeof(line), maps)) {
+ int m = -1;
+
+ /* We care only about private r-x mappings. */
+ if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+ start, end, &m))
+ continue;
+ if (m < 0)
+ continue;
+
+ if (!strncmp(&line[m], VDSO__MAP_NAME,
+ sizeof(VDSO__MAP_NAME) - 1))
+ found = 1;
+ }
+
+ fclose(maps);
+ return !found;
+}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 26f5b2fe5dc8..0ecf4a304cbc 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -601,8 +601,10 @@ static int __write_cpudesc(int fd, const char *cpuinfo_proc)
break;
}
- if (ret)
+ if (ret) {
+ ret = -1;
goto done;
+ }
s = buf;
@@ -965,7 +967,8 @@ static int write_total_mem(int fd, struct perf_header *h __maybe_unused,
n = sscanf(buf, "%*s %"PRIu64, &mem);
if (n == 1)
ret = do_write(fd, &mem, sizeof(mem));
- }
+ } else
+ ret = -1;
free(buf);
fclose(fp);
return ret;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 34fc7c8672e4..51a630301afa 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -21,7 +21,7 @@ static void dsos__init(struct dsos *dsos)
int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
{
- map_groups__init(&machine->kmaps);
+ map_groups__init(&machine->kmaps, machine);
RB_CLEAR_NODE(&machine->rb_node);
dsos__init(&machine->user_dsos);
dsos__init(&machine->kernel_dsos);
@@ -32,7 +32,6 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->vdso_info = NULL;
- machine->kmaps.machine = machine;
machine->pid = pid;
machine->symbol_filter = NULL;
@@ -319,7 +318,7 @@ static void machine__update_thread_pid(struct machine *machine,
goto out_err;
if (!leader->mg)
- leader->mg = map_groups__new();
+ leader->mg = map_groups__new(machine);
if (!leader->mg)
goto out_err;
@@ -1290,7 +1289,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
return 0;
}
-static void ip__resolve_ams(struct machine *machine, struct thread *thread,
+static void ip__resolve_ams(struct thread *thread,
struct addr_map_symbol *ams,
u64 ip)
{
@@ -1304,7 +1303,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
* Thus, we have to try consecutively until we find a match
* or else, the symbol is unknown
*/
- thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al);
+ thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al);
ams->addr = ip;
ams->al_addr = al.addr;
@@ -1312,23 +1311,21 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
ams->map = al.map;
}
-static void ip__resolve_data(struct machine *machine, struct thread *thread,
+static void ip__resolve_data(struct thread *thread,
u8 m, struct addr_map_symbol *ams, u64 addr)
{
struct addr_location al;
memset(&al, 0, sizeof(al));
- thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
- &al);
+ thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al);
if (al.map == NULL) {
/*
* some shared data regions have execute bit set which puts
* their mapping in the MAP__FUNCTION type array.
* Check there as a fallback option before dropping the sample.
*/
- thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr,
- &al);
+ thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al);
}
ams->addr = addr;
@@ -1345,9 +1342,8 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
if (!mi)
return NULL;
- ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip);
- ip__resolve_data(al->machine, al->thread, al->cpumode,
- &mi->daddr, sample->addr);
+ ip__resolve_ams(al->thread, &mi->iaddr, sample->ip);
+ ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr);
mi->data_src.val = sample->data_src;
return mi;
@@ -1364,15 +1360,14 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
return NULL;
for (i = 0; i < bs->nr; i++) {
- ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to);
- ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from);
+ ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to);
+ ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags;
}
return bi;
}
-static int machine__resolve_callchain_sample(struct machine *machine,
- struct thread *thread,
+static int thread__resolve_callchain_sample(struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent,
struct addr_location *root_al,
@@ -1396,7 +1391,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,
* Based on DWARF debug information, some architectures skip
* a callchain entry saved by the kernel.
*/
- skip_idx = arch_skip_callchain_idx(machine, thread, chain);
+ skip_idx = arch_skip_callchain_idx(thread, chain);
for (i = 0; i < chain_nr; i++) {
u64 ip;
@@ -1438,7 +1433,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,
}
al.filtered = 0;
- thread__find_addr_location(thread, machine, cpumode,
+ thread__find_addr_location(thread, cpumode,
MAP__FUNCTION, ip, &al);
if (al.sym != NULL) {
if (sort__has_parent && !*parent &&
@@ -1469,19 +1464,15 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
entry->map, entry->sym);
}
-int machine__resolve_callchain(struct machine *machine,
- struct perf_evsel *evsel,
- struct thread *thread,
- struct perf_sample *sample,
- struct symbol **parent,
- struct addr_location *root_al,
- int max_stack)
+int thread__resolve_callchain(struct thread *thread,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ int max_stack)
{
- int ret;
-
- ret = machine__resolve_callchain_sample(machine, thread,
- sample->callchain, parent,
- root_al, max_stack);
+ int ret = thread__resolve_callchain_sample(thread, sample->callchain,
+ parent, root_al, max_stack);
if (ret)
return ret;
@@ -1495,7 +1486,7 @@ int machine__resolve_callchain(struct machine *machine,
(!sample->user_stack.size))
return 0;
- return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
+ return unwind__get_entries(unwind_entry, &callchain_cursor,
thread, sample, max_stack);
}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 2b651a7f5d0d..e8b7779a0a3f 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -40,6 +40,10 @@ struct machine {
u64 kernel_start;
symbol_filter_t symbol_filter;
pid_t *current_tid;
+ union { /* Tool specific area */
+ void *priv;
+ u64 db_id;
+ };
};
static inline
@@ -122,13 +126,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al);
struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al);
-int machine__resolve_callchain(struct machine *machine,
- struct perf_evsel *evsel,
- struct thread *thread,
- struct perf_sample *sample,
- struct symbol **parent,
- struct addr_location *root_al,
- int max_stack);
+int thread__resolve_callchain(struct thread *thread,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct symbol **parent,
+ struct addr_location *root_al,
+ int max_stack);
/*
* Default guest kernel is defined by parameter --guestkallsyms
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 2137c4596ec7..040a785c857b 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -413,14 +413,14 @@ u64 map__objdump_2mem(struct map *map, u64 ip)
return ip + map->reloc;
}
-void map_groups__init(struct map_groups *mg)
+void map_groups__init(struct map_groups *mg, struct machine *machine)
{
int i;
for (i = 0; i < MAP__NR_TYPES; ++i) {
mg->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&mg->removed_maps[i]);
}
- mg->machine = NULL;
+ mg->machine = machine;
mg->refcnt = 1;
}
@@ -471,12 +471,12 @@ bool map_groups__empty(struct map_groups *mg)
return true;
}
-struct map_groups *map_groups__new(void)
+struct map_groups *map_groups__new(struct machine *machine)
{
struct map_groups *mg = malloc(sizeof(*mg));
if (mg != NULL)
- map_groups__init(mg);
+ map_groups__init(mg, machine);
return mg;
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 2f83954af050..6951a9d42339 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -64,7 +64,7 @@ struct map_groups {
int refcnt;
};
-struct map_groups *map_groups__new(void);
+struct map_groups *map_groups__new(struct machine *machine);
void map_groups__delete(struct map_groups *mg);
bool map_groups__empty(struct map_groups *mg);
@@ -150,7 +150,7 @@ void maps__remove(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 addr);
struct map *maps__first(struct rb_root *maps);
struct map *maps__next(struct map *map);
-void map_groups__init(struct map_groups *mg);
+void map_groups__init(struct map_groups *mg, struct machine *machine);
void map_groups__exit(struct map_groups *mg);
int map_groups__clone(struct map_groups *mg,
struct map_groups *parent, enum map_type type);
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index bf48092983c6..f62dee7bd924 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -42,7 +42,26 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "takes no value", flags);
if (unset && (opt->flags & PARSE_OPT_NONEG))
return opterror(opt, "isn't available", flags);
-
+ if (opt->flags & PARSE_OPT_DISABLED)
+ return opterror(opt, "is not usable", flags);
+
+ if (opt->flags & PARSE_OPT_EXCLUSIVE) {
+ if (p->excl_opt) {
+ char msg[128];
+
+ if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
+ p->excl_opt->long_name == NULL) {
+ scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
+ p->excl_opt->short_name);
+ } else {
+ scnprintf(msg, sizeof(msg), "cannot be used with %s",
+ p->excl_opt->long_name);
+ }
+ opterror(opt, msg, flags);
+ return -3;
+ }
+ p->excl_opt = opt;
+ }
if (!(flags & OPT_SHORT) && p->opt) {
switch (opt->type) {
case OPTION_CALLBACK:
@@ -343,13 +362,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+ int excl_short_opt = 1;
+ const char *arg;
/* we must reset ->opt, unknown short option leave it dangling */
ctx->opt = NULL;
for (; ctx->argc; ctx->argc--, ctx->argv++) {
- const char *arg = ctx->argv[0];
-
+ arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) {
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
@@ -358,19 +378,21 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
}
if (arg[1] != '-') {
- ctx->opt = arg + 1;
+ ctx->opt = ++arg;
if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options, arg + 1, 1);
+ return parse_options_usage(usagestr, options, arg, 1);
case -2:
goto unknown;
+ case -3:
+ goto exclusive;
default:
break;
}
if (ctx->opt)
- check_typos(arg + 1, options);
+ check_typos(arg, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0);
@@ -387,6 +409,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
ctx->argv[0] = strdup(ctx->opt - 1);
*(char *)ctx->argv[0] = '-';
goto unknown;
+ case -3:
+ goto exclusive;
default:
break;
}
@@ -402,19 +426,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
break;
}
- if (internal_help && !strcmp(arg + 2, "help-all"))
+ arg += 2;
+ if (internal_help && !strcmp(arg, "help-all"))
return usage_with_options_internal(usagestr, options, 1);
- if (internal_help && !strcmp(arg + 2, "help"))
+ if (internal_help && !strcmp(arg, "help"))
return usage_with_options_internal(usagestr, options, 0);
- if (!strcmp(arg + 2, "list-opts"))
+ if (!strcmp(arg, "list-opts"))
return PARSE_OPT_LIST_OPTS;
- if (!strcmp(arg + 2, "list-cmds"))
+ if (!strcmp(arg, "list-cmds"))
return PARSE_OPT_LIST_SUBCMDS;
- switch (parse_long_opt(ctx, arg + 2, options)) {
+ switch (parse_long_opt(ctx, arg, options)) {
case -1:
- return parse_options_usage(usagestr, options, arg + 2, 0);
+ return parse_options_usage(usagestr, options, arg, 0);
case -2:
goto unknown;
+ case -3:
+ excl_short_opt = 0;
+ goto exclusive;
default:
break;
}
@@ -426,6 +454,17 @@ unknown:
ctx->opt = NULL;
}
return PARSE_OPT_DONE;
+
+exclusive:
+ parse_options_usage(usagestr, options, arg, excl_short_opt);
+ if ((excl_short_opt && ctx->excl_opt->short_name) ||
+ ctx->excl_opt->long_name == NULL) {
+ char opt = ctx->excl_opt->short_name;
+ parse_options_usage(NULL, options, &opt, 1);
+ } else {
+ parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
+ }
+ return PARSE_OPT_HELP;
}
int parse_options_end(struct parse_opt_ctx_t *ctx)
@@ -509,6 +548,8 @@ static void print_option_help(const struct option *opts, int full)
}
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
return;
+ if (opts->flags & PARSE_OPT_DISABLED)
+ return;
pos = fprintf(stderr, " ");
if (opts->short_name)
@@ -679,3 +720,16 @@ int parse_opt_verbosity_cb(const struct option *opt,
}
return 0;
}
+
+void set_option_flag(struct option *opts, int shortopt, const char *longopt,
+ int flag)
+{
+ for (; opts->type != OPTION_END; opts++) {
+ if ((shortopt && opts->short_name == shortopt) ||
+ (opts->long_name && longopt &&
+ !strcmp(opts->long_name, longopt))) {
+ opts->flags |= flag;
+ break;
+ }
+ }
+}
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index b59ba858e73d..97b153fb4999 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -38,6 +38,8 @@ enum parse_opt_option_flags {
PARSE_OPT_NONEG = 4,
PARSE_OPT_HIDDEN = 8,
PARSE_OPT_LASTARG_DEFAULT = 16,
+ PARSE_OPT_DISABLED = 32,
+ PARSE_OPT_EXCLUSIVE = 64,
};
struct option;
@@ -173,6 +175,7 @@ struct parse_opt_ctx_t {
const char **out;
int argc, cpidx;
const char *opt;
+ const struct option *excl_opt;
int flags;
};
@@ -211,4 +214,5 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
+void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag);
#endif /* __PERF_PARSE_OPTIONS_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e243ad962a4d..881b75490533 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
pmu = NULL;
len = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list)
len++;
- aliases = malloc(sizeof(char *) * len);
+ if (pmu->selectable)
+ len++;
+ }
+ aliases = zalloc(sizeof(char *) * len);
if (!aliases)
- return;
+ goto out_enomem;
pmu = NULL;
j = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) {
char *name = format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu");
@@ -765,13 +768,23 @@ void print_pmu_events(const char *event_glob, bool name_only)
(!is_cpu && strglobmatch(alias->name,
event_glob))))
continue;
- aliases[j] = name;
+
if (is_cpu && !name_only)
- aliases[j] = format_alias_or(buf, sizeof(buf),
- pmu, alias);
- aliases[j] = strdup(aliases[j]);
+ name = format_alias_or(buf, sizeof(buf), pmu, alias);
+
+ aliases[j] = strdup(name);
+ if (aliases[j] == NULL)
+ goto out_enomem;
j++;
}
+ if (pmu->selectable) {
+ char *s;
+ if (asprintf(&s, "%s//", pmu->name) < 0)
+ goto out_enomem;
+ aliases[j] = s;
+ j++;
+ }
+ }
len = j;
qsort(aliases, len, sizeof(char *), cmp_string);
for (j = 0; j < len; j++) {
@@ -780,12 +793,20 @@ void print_pmu_events(const char *event_glob, bool name_only)
continue;
}
printf(" %-50s [Kernel PMU event]\n", aliases[j]);
- zfree(&aliases[j]);
printed++;
}
if (printed)
printf("\n");
- free(aliases);
+out_free:
+ for (j = 0; j < len; j++)
+ zfree(&aliases[j]);
+ zfree(&aliases);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print PMU events\n");
+ if (aliases)
+ goto out_free;
}
bool pmu_have_event(const char *pname, const char *name)
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index fe9dfbee8eed..8092de78e818 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -18,6 +18,7 @@ struct perf_event_attr;
struct perf_pmu {
char *name;
__u32 type;
+ bool selectable;
struct perf_event_attr *default_config;
struct cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index c150ca4343eb..28eb1417cb2a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1910,21 +1910,21 @@ static int show_perf_probe_event(struct perf_probe_event *pev,
if (ret < 0)
return ret;
- printf(" %-20s (on %s", buf, place);
+ pr_info(" %-20s (on %s", buf, place);
if (module)
- printf(" in %s", module);
+ pr_info(" in %s", module);
if (pev->nargs > 0) {
- printf(" with");
+ pr_info(" with");
for (i = 0; i < pev->nargs; i++) {
ret = synthesize_perf_probe_arg(&pev->args[i],
buf, 128);
if (ret < 0)
break;
- printf(" %s", buf);
+ pr_info(" %s", buf);
}
}
- printf(")\n");
+ pr_info(")\n");
free(place);
return ret;
}
@@ -2124,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
}
ret = 0;
- printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
+ pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
if (pev->event)
@@ -2179,8 +2179,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (ret >= 0) {
/* Show how to use the event. */
- printf("\nYou can now use it in all perf tools, such as:\n\n");
- printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
+ pr_info("\nYou can now use it in all perf tools, such as:\n\n");
+ pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event);
}
@@ -2444,7 +2444,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
goto error;
}
- printf("Removed event: %s\n", ent->s);
+ pr_info("Removed event: %s\n", ent->s);
return 0;
error:
pr_warning("Failed to delete event: %s\n",
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 496f21cadd97..2fd7ee8f18c7 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include <errno.h>
#include "../../perf.h"
@@ -33,6 +34,9 @@
#include "../util.h"
#include "../event.h"
#include "../thread.h"
+#include "../comm.h"
+#include "../machine.h"
+#include "../db-export.h"
#include "../trace-event.h"
#include "../machine.h"
@@ -53,6 +57,21 @@ static int zero_flag_atom;
static PyObject *main_module, *main_dict;
+struct tables {
+ struct db_export dbe;
+ PyObject *evsel_handler;
+ PyObject *machine_handler;
+ PyObject *thread_handler;
+ PyObject *comm_handler;
+ PyObject *comm_thread_handler;
+ PyObject *dso_handler;
+ PyObject *symbol_handler;
+ PyObject *sample_handler;
+ bool db_export_mode;
+};
+
+static struct tables tables_global;
+
static void handler_call_die(const char *handler_name) NORETURN;
static void handler_call_die(const char *handler_name)
{
@@ -312,9 +331,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
if (!symbol_conf.use_callchain || !sample->callchain)
goto exit;
- if (machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, NULL, NULL,
- PERF_MAX_STACK_DEPTH) != 0) {
+ if (thread__resolve_callchain(al->thread, evsel,
+ sample, NULL, NULL,
+ PERF_MAX_STACK_DEPTH) != 0) {
pr_err("Failed to resolve callchain. Skipping\n");
goto exit;
}
@@ -475,6 +494,211 @@ static void python_process_tracepoint(struct perf_sample *sample,
Py_DECREF(t);
}
+static PyObject *tuple_new(unsigned int sz)
+{
+ PyObject *t;
+
+ t = PyTuple_New(sz);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+ return t;
+}
+
+static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val)
+{
+#if BITS_PER_LONG == 64
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+#endif
+#if BITS_PER_LONG == 32
+ return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val));
+#endif
+}
+
+static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val)
+{
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+}
+
+static int tuple_set_string(PyObject *t, unsigned int pos, const char *s)
+{
+ return PyTuple_SetItem(t, pos, PyString_FromString(s));
+}
+
+static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, evsel->db_id);
+ tuple_set_string(t, 1, perf_evsel__name(evsel));
+
+ call_object(tables->evsel_handler, t, "evsel_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_machine(struct db_export *dbe,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, machine->db_id);
+ tuple_set_s32(t, 1, machine->pid);
+ tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : "");
+
+ call_object(tables->machine_handler, t, "machine_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_thread(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, thread->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_u64(t, 2, main_thread_db_id);
+ tuple_set_s32(t, 3, thread->pid_);
+ tuple_set_s32(t, 4, thread->tid);
+
+ call_object(tables->thread_handler, t, "thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm(struct db_export *dbe, struct comm *comm)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, comm->db_id);
+ tuple_set_string(t, 1, comm__str(comm));
+
+ call_object(tables->comm_handler, t, "comm_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm_thread(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, db_id);
+ tuple_set_u64(t, 1, comm->db_id);
+ tuple_set_u64(t, 2, thread->db_id);
+
+ call_object(tables->comm_thread_handler, t, "comm_thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+ PyObject *t;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, dso->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_string(t, 2, dso->short_name);
+ tuple_set_string(t, 3, dso->long_name);
+ tuple_set_string(t, 4, sbuild_id);
+
+ call_object(tables->dso_handler, t, "dso_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ u64 *sym_db_id = symbol__priv(sym);
+ PyObject *t;
+
+ t = tuple_new(6);
+
+ tuple_set_u64(t, 0, *sym_db_id);
+ tuple_set_u64(t, 1, dso->db_id);
+ tuple_set_u64(t, 2, sym->start);
+ tuple_set_u64(t, 3, sym->end);
+ tuple_set_s32(t, 4, sym->binding);
+ tuple_set_string(t, 5, sym->name);
+
+ call_object(tables->symbol_handler, t, "symbol_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_sample(struct db_export *dbe,
+ struct export_sample *es)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(19);
+
+ tuple_set_u64(t, 0, es->db_id);
+ tuple_set_u64(t, 1, es->evsel->db_id);
+ tuple_set_u64(t, 2, es->al->machine->db_id);
+ tuple_set_u64(t, 3, es->thread->db_id);
+ tuple_set_u64(t, 4, es->comm_db_id);
+ tuple_set_u64(t, 5, es->dso_db_id);
+ tuple_set_u64(t, 6, es->sym_db_id);
+ tuple_set_u64(t, 7, es->offset);
+ tuple_set_u64(t, 8, es->sample->ip);
+ tuple_set_u64(t, 9, es->sample->time);
+ tuple_set_s32(t, 10, es->sample->cpu);
+ tuple_set_u64(t, 11, es->addr_dso_db_id);
+ tuple_set_u64(t, 12, es->addr_sym_db_id);
+ tuple_set_u64(t, 13, es->addr_offset);
+ tuple_set_u64(t, 14, es->sample->addr);
+ tuple_set_u64(t, 15, es->sample->period);
+ tuple_set_u64(t, 16, es->sample->weight);
+ tuple_set_u64(t, 17, es->sample->transaction);
+ tuple_set_u64(t, 18, es->sample->data_src);
+
+ call_object(tables->sample_handler, t, "sample_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
static void python_process_general_event(struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
@@ -551,19 +775,25 @@ exit:
Py_DECREF(t);
}
-static void python_process_event(union perf_event *event __maybe_unused,
+static void python_process_event(union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
struct addr_location *al)
{
+ struct tables *tables = &tables_global;
+
switch (evsel->attr.type) {
case PERF_TYPE_TRACEPOINT:
python_process_tracepoint(sample, evsel, thread, al);
break;
/* Reserve for future process_hw/sw/raw APIs */
default:
- python_process_general_event(sample, evsel, thread, al);
+ if (tables->db_export_mode)
+ db_export__sample(&tables->dbe, event, sample, evsel,
+ thread, al);
+ else
+ python_process_general_event(sample, evsel, thread, al);
}
}
@@ -589,11 +819,57 @@ error:
return -1;
}
+#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \
+ tables->handler_name = get_handler(#table_name); \
+ if (tables->handler_name) \
+ tables->dbe.export_ ## name = python_export_ ## name; \
+} while (0)
+
+#define SET_TABLE_HANDLER(name) \
+ SET_TABLE_HANDLER_(name, name ## _handler, name ## _table)
+
+static void set_table_handlers(struct tables *tables)
+{
+ const char *perf_db_export_mode = "perf_db_export_mode";
+ PyObject *db_export_mode;
+ int ret;
+
+ memset(tables, 0, sizeof(struct tables));
+ if (db_export__init(&tables->dbe))
+ Py_FatalError("failed to initialize export");
+
+ db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode);
+ if (!db_export_mode)
+ return;
+
+ ret = PyObject_IsTrue(db_export_mode);
+ if (ret == -1)
+ handler_call_die(perf_db_export_mode);
+ if (!ret)
+ return;
+
+ tables->db_export_mode = true;
+ /*
+ * Reserve per symbol space for symbol->db_id via symbol__priv()
+ */
+ symbol_conf.priv_size = sizeof(u64);
+
+ SET_TABLE_HANDLER(evsel);
+ SET_TABLE_HANDLER(machine);
+ SET_TABLE_HANDLER(thread);
+ SET_TABLE_HANDLER(comm);
+ SET_TABLE_HANDLER(comm_thread);
+ SET_TABLE_HANDLER(dso);
+ SET_TABLE_HANDLER(symbol);
+ SET_TABLE_HANDLER(sample);
+}
+
/*
* Start trace script
*/
static int python_start_script(const char *script, int argc, const char **argv)
{
+ struct tables *tables = &tables_global;
const char **command_line;
char buf[PATH_MAX];
int i, err = 0;
@@ -632,6 +908,8 @@ static int python_start_script(const char *script, int argc, const char **argv)
free(command_line);
+ set_table_handlers(tables);
+
return err;
error:
Py_Finalize();
@@ -650,8 +928,12 @@ static int python_flush_script(void)
*/
static int python_stop_script(void)
{
+ struct tables *tables = &tables_global;
+
try_call_object("trace_end", NULL);
+ db_export__exit(&tables->dbe);
+
Py_XDECREF(main_dict);
Py_XDECREF(main_module);
Py_Finalize();
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 6702ac28754b..f4478ce72fdb 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
+static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
void perf_tool__fill_defaults(struct perf_tool *tool)
{
if (tool->sample == NULL)
@@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
else
tool->finished_round = process_finished_round_stub;
}
+ if (tool->id_index == NULL)
+ tool->id_index = process_id_index_stub;
}
static void swap_sample_id_all(union perf_event *event, void *data)
@@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
[PERF_RECORD_HEADER_BUILD_ID] = NULL,
+ [PERF_RECORD_ID_INDEX] = perf_event__all64_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -888,11 +900,26 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->build_id(tool, event, session);
case PERF_RECORD_FINISHED_ROUND:
return tool->finished_round(tool, event, session);
+ case PERF_RECORD_ID_INDEX:
+ return tool->id_index(tool, event, session);
default:
return -EINVAL;
}
}
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool)
+{
+ events_stats__inc(&session->stats, event->header.type);
+
+ if (event->header.type >= PERF_RECORD_USER_TYPE_START)
+ return perf_session__process_user_event(session, event, tool, 0);
+
+ return perf_session__deliver_event(session, event, sample, tool, 0);
+}
+
static void event_swap(union perf_event *event, bool sample_id_all)
{
perf_event__swap_op swap;
@@ -1417,9 +1444,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) {
struct addr_location node_al;
- if (machine__resolve_callchain(al->machine, evsel, al->thread,
- sample, NULL, NULL,
- PERF_MAX_STACK_DEPTH) != 0) {
+ if (thread__resolve_callchain(al->thread, evsel,
+ sample, NULL, NULL,
+ PERF_MAX_STACK_DEPTH) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");
return;
@@ -1594,3 +1621,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
out:
return err;
}
+
+int perf_event__process_id_index(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct perf_evlist *evlist = session->evlist;
+ struct id_index_event *ie = &event->id_index;
+ size_t i, nr, max_nr;
+
+ max_nr = (ie->header.size - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+ nr = ie->nr;
+ if (nr > max_nr)
+ return -EINVAL;
+
+ if (dump_trace)
+ fprintf(stdout, " nr: %zu\n", nr);
+
+ for (i = 0; i < nr; i++) {
+ struct id_index_entry *e = &ie->entries[i];
+ struct perf_sample_id *sid;
+
+ if (dump_trace) {
+ fprintf(stdout, " ... id: %"PRIu64, e->id);
+ fprintf(stdout, " idx: %"PRIu64, e->idx);
+ fprintf(stdout, " cpu: %"PRId64, e->cpu);
+ fprintf(stdout, " tid: %"PRId64"\n", e->tid);
+ }
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid)
+ return -ENOENT;
+ sid->idx = e->idx;
+ sid->cpu = e->cpu;
+ sid->tid = e->tid;
+ }
+ return 0;
+}
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine)
+{
+ union perf_event *ev;
+ struct perf_evsel *evsel;
+ size_t nr = 0, i = 0, sz, max_nr, n;
+ int err;
+
+ pr_debug2("Synthesizing id index\n");
+
+ max_nr = (UINT16_MAX - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+
+ evlist__for_each(evlist, evsel)
+ nr += evsel->ids;
+
+ n = nr > max_nr ? max_nr : nr;
+ sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry);
+ ev = zalloc(sz);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->id_index.header.type = PERF_RECORD_ID_INDEX;
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = n;
+
+ evlist__for_each(evlist, evsel) {
+ u32 j;
+
+ for (j = 0; j < evsel->ids; j++) {
+ struct id_index_entry *e;
+ struct perf_sample_id *sid;
+
+ if (i >= n) {
+ err = process(tool, ev, NULL, machine);
+ if (err)
+ goto out_err;
+ nr -= n;
+ i = 0;
+ }
+
+ e = &ev->id_index.entries[i++];
+
+ e->id = evsel->id[j];
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid) {
+ free(ev);
+ return -ENOENT;
+ }
+
+ e->idx = sid->idx;
+ e->cpu = sid->cpu;
+ e->tid = sid->tid;
+ }
+ }
+
+ sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry);
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = nr;
+
+ err = process(tool, ev, NULL, machine);
+out_err:
+ free(ev);
+
+ return err;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index a4be851f1a90..dc26ebf60fe4 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -126,4 +126,19 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done;
#define session_done() ACCESS_ONCE(session_done)
+
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool);
+
+int perf_event__process_id_index(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index c41411726c7a..bf5bf858b7f6 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -15,7 +15,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine)
pid_t pid = thread->pid_;
if (pid == thread->tid || pid == -1) {
- thread->mg = map_groups__new();
+ thread->mg = map_groups__new(machine);
} else {
leader = machine__findnew_thread(machine, pid, pid);
if (leader)
@@ -198,7 +198,6 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
}
void thread__find_cpumode_addr_location(struct thread *thread,
- struct machine *machine,
enum map_type type, u64 addr,
struct addr_location *al)
{
@@ -211,8 +210,7 @@ void thread__find_cpumode_addr_location(struct thread *thread,
};
for (i = 0; i < ARRAY_SIZE(cpumodes); i++) {
- thread__find_addr_location(thread, machine, cpumodes[i], type,
- addr, al);
+ thread__find_addr_location(thread, cpumodes[i], type, addr, al);
if (al->map)
break;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 8c75fa774706..d34cf5c0d0d9 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -23,6 +23,7 @@ struct thread {
bool dead; /* if set thread has exited */
struct list_head comm_list;
int comm_len;
+ u64 db_id;
void *priv;
};
@@ -54,16 +55,15 @@ void thread__insert_map(struct thread *thread, struct map *map);
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);
size_t thread__fprintf(struct thread *thread, FILE *fp);
-void thread__find_addr_map(struct thread *thread, struct machine *machine,
+void thread__find_addr_map(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al);
-void thread__find_addr_location(struct thread *thread, struct machine *machine,
+void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al);
void thread__find_cpumode_addr_location(struct thread *thread,
- struct machine *machine,
enum map_type type, u64 addr,
struct addr_location *al);
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index f11636966a0f..bb2708bbfaca 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -39,7 +39,8 @@ struct perf_tool {
event_attr_op attr;
event_op2 tracing_data;
event_op2 finished_round,
- build_id;
+ build_id,
+ id_index;
bool ordered_events;
bool ordering_requires_timestamps;
};
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 7419768c38b1..2dcfe9a7c8d0 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -26,7 +26,7 @@ static int __report_module(struct addr_location *al, u64 ip,
Dwfl_Module *mod;
struct dso *dso = NULL;
- thread__find_addr_location(ui->thread, ui->machine,
+ thread__find_addr_location(ui->thread,
PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, al);
@@ -89,7 +89,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
struct addr_location al;
ssize_t size;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al);
if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@@ -164,14 +164,14 @@ frame_callback(Dwfl_Frame *state, void *arg)
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine, struct thread *thread,
+ struct thread *thread,
struct perf_sample *data,
int max_stack)
{
struct unwind_info ui = {
.sample = data,
.thread = thread,
- .machine = machine,
+ .machine = thread->mg->machine,
.cb = cb,
.arg = arg,
.max_stack = max_stack,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 4d45c0dfe343..371219a6daf1 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -284,7 +284,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
{
struct addr_location al;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
return al.map;
}
@@ -374,7 +374,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
struct addr_location al;
ssize_t size;
- thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al);
if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@@ -476,14 +476,13 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as,
pr_debug("unwind: put_unwind_info called\n");
}
-static int entry(u64 ip, struct thread *thread, struct machine *machine,
+static int entry(u64 ip, struct thread *thread,
unwind_entry_cb_t cb, void *arg)
{
struct unwind_entry e;
struct addr_location al;
- thread__find_addr_location(thread, machine,
- PERF_RECORD_MISC_USER,
+ thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
e.ip = ip;
@@ -586,21 +585,21 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
unw_word_t ip;
unw_get_reg(&c, UNW_REG_IP, &ip);
- ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;
+ ret = ip ? entry(ip, ui->thread, cb, arg) : 0;
}
return ret;
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine, struct thread *thread,
+ struct thread *thread,
struct perf_sample *data, int max_stack)
{
u64 ip;
struct unwind_info ui = {
.sample = data,
.thread = thread,
- .machine = machine,
+ .machine = thread->mg->machine,
};
int ret;
@@ -611,7 +610,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (ret)
return ret;
- ret = entry(ip, thread, machine, cb, arg);
+ ret = entry(ip, thread, cb, arg);
if (ret)
return -ENOMEM;
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index f50b737235eb..12790cf94618 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -16,7 +16,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
#ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct machine *machine,
struct thread *thread,
struct perf_sample *data, int max_stack);
/* libunwind specific */
@@ -38,7 +37,6 @@ static inline void unwind__finish_access(struct thread *thread __maybe_unused) {
static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
void *arg __maybe_unused,
- struct machine *machine __maybe_unused,
struct thread *thread __maybe_unused,
struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused)
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index adca69384fcc..5c7dd796979d 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -12,9 +12,16 @@
#include "util.h"
#include "symbol.h"
#include "machine.h"
+#include "thread.h"
#include "linux/string.h"
#include "debug.h"
+/*
+ * Include definition of find_vdso_map() also used in perf-read-vdso.c for
+ * building perf-read-vdso32 and perf-read-vdsox32.
+ */
+#include "find-vdso-map.c"
+
#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
struct vdso_file {
@@ -22,10 +29,15 @@ struct vdso_file {
bool error;
char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
const char *dso_name;
+ const char *read_prog;
};
struct vdso_info {
struct vdso_file vdso;
+#if BITS_PER_LONG == 64
+ struct vdso_file vdso32;
+ struct vdso_file vdsox32;
+#endif
};
static struct vdso_info *vdso_info__new(void)
@@ -35,42 +47,23 @@ static struct vdso_info *vdso_info__new(void)
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO,
},
+#if BITS_PER_LONG == 64
+ .vdso32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSO32,
+ .read_prog = "perf-read-vdso32",
+ },
+ .vdsox32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSOX32,
+ .read_prog = "perf-read-vdsox32",
+ },
+#endif
};
return memdup(&vdso_info_init, sizeof(vdso_info_init));
}
-static int find_vdso_map(void **start, void **end)
-{
- FILE *maps;
- char line[128];
- int found = 0;
-
- maps = fopen("/proc/self/maps", "r");
- if (!maps) {
- pr_err("vdso: cannot open maps\n");
- return -1;
- }
-
- while (!found && fgets(line, sizeof(line), maps)) {
- int m = -1;
-
- /* We care only about private r-x mappings. */
- if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
- start, end, &m))
- continue;
- if (m < 0)
- continue;
-
- if (!strncmp(&line[m], VDSO__MAP_NAME,
- sizeof(VDSO__MAP_NAME) - 1))
- found = 1;
- }
-
- fclose(maps);
- return !found;
-}
-
static char *get_file(struct vdso_file *vdso_file)
{
char *vdso = NULL;
@@ -117,6 +110,12 @@ void vdso__exit(struct machine *machine)
if (vdso_info->vdso.found)
unlink(vdso_info->vdso.temp_file_name);
+#if BITS_PER_LONG == 64
+ if (vdso_info->vdso32.found)
+ unlink(vdso_info->vdso32.temp_file_name);
+ if (vdso_info->vdsox32.found)
+ unlink(vdso_info->vdsox32.temp_file_name);
+#endif
zfree(&machine->vdso_info);
}
@@ -135,6 +134,153 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,
return dso;
}
+#if BITS_PER_LONG == 64
+
+static enum dso_type machine__thread_dso_type(struct machine *machine,
+ struct thread *thread)
+{
+ enum dso_type dso_type = DSO__TYPE_UNKNOWN;
+ struct map *map;
+ struct dso *dso;
+
+ map = map_groups__first(thread->mg, MAP__FUNCTION);
+ for (; map ; map = map_groups__next(map)) {
+ dso = map->dso;
+ if (!dso || dso->long_name[0] != '/')
+ continue;
+ dso_type = dso__type(dso, machine);
+ if (dso_type != DSO__TYPE_UNKNOWN)
+ break;
+ }
+
+ return dso_type;
+}
+
+static int vdso__do_copy_compat(FILE *f, int fd)
+{
+ char buf[4096];
+ size_t count;
+
+ while (1) {
+ count = fread(buf, 1, sizeof(buf), f);
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
+ break;
+ if (count && writen(fd, buf, count) != (ssize_t)count)
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int vdso__copy_compat(const char *prog, int fd)
+{
+ FILE *f;
+ int err;
+
+ f = popen(prog, "r");
+ if (!f)
+ return -errno;
+
+ err = vdso__do_copy_compat(f, fd);
+
+ if (pclose(f) == -1)
+ return -errno;
+
+ return err;
+}
+
+static int vdso__create_compat_file(const char *prog, char *temp_name)
+{
+ int fd, err;
+
+ fd = mkstemp(temp_name);
+ if (fd < 0)
+ return -errno;
+
+ err = vdso__copy_compat(prog, fd);
+
+ if (close(fd) == -1)
+ return -errno;
+
+ return err;
+}
+
+static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
+{
+ int err;
+
+ if (vdso_file->found)
+ return vdso_file->temp_file_name;
+
+ if (vdso_file->error)
+ return NULL;
+
+ err = vdso__create_compat_file(vdso_file->read_prog,
+ vdso_file->temp_file_name);
+ if (err) {
+ pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
+ vdso_file->error = true;
+ return NULL;
+ }
+
+ vdso_file->found = true;
+
+ return vdso_file->temp_file_name;
+}
+
+static struct dso *vdso__findnew_compat(struct machine *machine,
+ struct vdso_file *vdso_file)
+{
+ const char *file_name;
+ struct dso *dso;
+
+ dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true);
+ if (dso)
+ return dso;
+
+ file_name = vdso__get_compat_file(vdso_file);
+ if (!file_name)
+ return NULL;
+
+ return vdso__new(machine, vdso_file->dso_name, file_name);
+}
+
+static int vdso__dso_findnew_compat(struct machine *machine,
+ struct thread *thread,
+ struct vdso_info *vdso_info,
+ struct dso **dso)
+{
+ enum dso_type dso_type;
+
+ dso_type = machine__thread_dso_type(machine, thread);
+
+#ifndef HAVE_PERF_READ_VDSO32
+ if (dso_type == DSO__TYPE_32BIT)
+ return 0;
+#endif
+#ifndef HAVE_PERF_READ_VDSOX32
+ if (dso_type == DSO__TYPE_X32BIT)
+ return 0;
+#endif
+
+ switch (dso_type) {
+ case DSO__TYPE_32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdso32);
+ return 1;
+ case DSO__TYPE_X32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32);
+ return 1;
+ case DSO__TYPE_UNKNOWN:
+ case DSO__TYPE_64BIT:
+ default:
+ return 0;
+ }
+}
+
+#endif
+
struct dso *vdso__dso_findnew(struct machine *machine,
struct thread *thread __maybe_unused)
{
@@ -148,6 +294,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,
if (!vdso_info)
return NULL;
+#if BITS_PER_LONG == 64
+ if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso))
+ return dso;
+#endif
+
dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
if (!dso) {
char *file;
@@ -164,5 +315,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,
bool dso__is_vdso(struct dso *dso)
{
- return !strcmp(dso->short_name, DSO__NAME_VDSO);
+ return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSOX32);
}
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index af9d6929a215..d97da1616f0c 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -7,7 +7,9 @@
#define VDSO__MAP_NAME "[vdso]"
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO32 "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
static inline bool is_vdso_map(const char *filename)
{