Skip to contentSkip to conversation

perf tools: Intel PT preparation continued

PATCH In review Adrian Hunter 5 participants 55 messages Oct 23, 2014 – Oct 30, 2014

Subsystems (2)
lore
Share & tools
lore

Adrian Hunter

author reviewer
#0 lore raw

cover perf tools: Intel PT preparation continued

Hi

Here are the remaining Intel PT preparation patches. They are all patches that have been posted before, although "perf tools:
Do not attempt to run perf-read-vdso32 if it wasn't built" wasn't sent as a separate email.

With regard to "32-bit compatibility VDSOs", you may recall that Ingo had a problem getting "sh: perf-read-vdso32: command not found". That message is inevitable when using popen to run perf-read-vdso32 and perf-read-vdso32 cannot be found. At least with the patch "perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built" one reason for the message is eliminated.

It would be nice to target 3.19 for Intel PT, but there are still the real patches of which there are about 48.

Adrian Hunter (16):
perf tools: Add id index
perf pmu: Let pmu's with no events show up on perf list
perf session: Add perf_session__deliver_synth_event()
perf tools: Add a thread stack for synthesizing call chains
perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Add branch_type and in_tx to Python export
perf tools: Enhance the thread stack to output call/return data
perf tools: Add call information to the database export API
perf tools: Add call information to Python export
perf tools: Defer export of comms that were not 'set'
perf tools: Build programs to copy 32-bit compatibility VDSOs
perf tools: Add support for 32-bit compatibility VDSOs
perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

 tools/perf/Makefile.perf                           |  42 +-
 tools/perf/builtin-inject.c                        |   1 +
 tools/perf/config/Makefile                         |  29 +-
 tools/perf/config/Makefile.arch                    |   8 +
 tools/perf/config/feature-checks/Makefile          |  10 +-
 tools/perf/config/feature-checks/test-compile.c    |   4 +
 tools/perf/perf-read-vdso.c                        |  34 +
 .../scripts/python/bin/export-to-postgresql-record |   8 +
 .../scripts/python/bin/export-to-postgresql-report |  29 +
 tools/perf/scripts/python/export-to-postgresql.py  | 444 +++++++++++++
 tools/perf/util/comm.h                             |   1 +
 tools/perf/util/db-export.c                        | 426 +++++++++++++
 tools/perf/util/db-export.h                        | 107 ++++
 tools/perf/util/dso.h                              |   1 +
 tools/perf/util/event.c                            |   1 +
 tools/perf/util/event.h                            |  41 ++
 tools/perf/util/evlist.c                           |  26 +-
 tools/perf/util/evsel.h                            |   4 +
 tools/perf/util/find-vdso-map.c                    |  30 +
 tools/perf/util/machine.h                          |   1 +
 tools/perf/util/pmu.c                              |  13 +-
 tools/perf/util/pmu.h                              |   1 +
 .../util/scripting-engines/trace-event-python.c    | 395 +++++++++++-
 tools/perf/util/session.c                          | 136 ++++
 tools/perf/util/session.h                          |  15 +
 tools/perf/util/symbol.h                           |   1 +
 tools/perf/util/thread-stack.c                     | 690 +++++++++++++++++++++
 tools/perf/util/thread-stack.h                     |  79 +++
 tools/perf/util/thread.c                           |   3 +
 tools/perf/util/thread.h                           |   4 +
 tools/perf/util/tool.h                             |   3 +-
 tools/perf/util/vdso.c                             | 217 ++++++-
 tools/perf/util/vdso.h                             |   4 +-
 33 files changed, 2760 insertions(+), 48 deletions(-)
 create mode 100644 tools/perf/config/feature-checks/test-compile.c
 create mode 100644 tools/perf/perf-read-vdso.c
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report
 create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
 create mode 100644 tools/perf/util/db-export.c
 create mode 100644 tools/perf/util/db-export.h
 create mode 100644 tools/perf/util/find-vdso-map.c
 create mode 100644 tools/perf/util/thread-stack.c
 create mode 100644 tools/perf/util/thread-stack.h

Regards
Adrian

author reviewer
#1 lore raw

PATCH 1/16 perf tools: Add id index

Add an index of the event identifiers.

This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.

---
 tools/perf/builtin-inject.c |   1 +
 tools/perf/util/event.c     |   1 +
 tools/perf/util/event.h     |  15 ++++++
 tools/perf/util/evlist.c    |  26 ++++++++--
 tools/perf/util/evsel.h     |   3 ++
 tools/perf/util/session.c   | 122 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/session.h   |  10 ++++
 tools/perf/util/tool.h      |   3 +-
 8 files changed, 177 insertions(+), 4 deletions(-)
tools/perf/builtin-inject.c +1 -0
index de99ca1..046c719 100644
@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
 			.tracing_data	= perf_event__repipe_op2_synth,
 			.finished_round	= perf_event__repipe_op2_synth,
 			.build_id	= perf_event__repipe_op2_synth,
+			.id_index	= perf_event__repipe_op2_synth,
 		},
 		.input_name  = "-",
 		.samples = LIST_HEAD_INIT(inject.samples),
tools/perf/util/event.c +1 -0
index 4af6b27..bbf6705 100644
@@ -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)
tools/perf/util/event.h +15 -0
index 5699e7e..c89518e 100644
@@ -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);
tools/perf/util/evlist.c +23 -3
index 3c9e77d..0babd39 100644
@@ -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;
tools/perf/util/evsel.h +3 -0
index 163c560..4861e8c 100644
@@ -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;
tools/perf/util/session.c +122 -0

149 lines not on this page · raw message

tools/perf/util/session.h +10 -0
index a4be851..d8521ac 100644
@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
 extern volatile int session_done;
 
 #define session_done()	ACCESS_ONCE(session_done)
+
+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 */
tools/perf/util/tool.h +2 -1
index f116369..bb2708b 100644
@@ -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;
 };
Signature · 1 line
1.9.1
#2 lore raw

Re: [PATCH 01/16] perf tools: Add id index

Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:

Quoted 5 lines

Add an index of the event identifiers.

This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.

This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...

Can you try to explain like to a seven year old?

Sigh.

'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.

- Arnaldo

Quoted 327 lines
---
 tools/perf/builtin-inject.c |   1 +
 tools/perf/util/event.c     |   1 +
 tools/perf/util/event.h     |  15 ++++++
 tools/perf/util/evlist.c    |  26 ++++++++--
 tools/perf/util/evsel.h     |   3 ++
 tools/perf/util/session.c   | 122 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/session.h   |  10 ++++
 tools/perf/util/tool.h      |   3 +-
 8 files changed, 177 insertions(+), 4 deletions(-)
tools/perf/builtin-inject.c +1 -0
index de99ca1..046c719 100644
@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
 			.tracing_data	= perf_event__repipe_op2_synth,
 			.finished_round	= perf_event__repipe_op2_synth,
 			.build_id	= perf_event__repipe_op2_synth,
+			.id_index	= perf_event__repipe_op2_synth,
 		},
 		.input_name  = "-",
 		.samples = LIST_HEAD_INIT(inject.samples),
tools/perf/util/event.c +1 -0
index 4af6b27..bbf6705 100644
@@ -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)
tools/perf/util/event.h +15 -0
index 5699e7e..c89518e 100644
@@ -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);
tools/perf/util/evlist.c +23 -3
index 3c9e77d..0babd39 100644
@@ -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;
tools/perf/util/evsel.h +3 -0
index 163c560..4861e8c 100644
@@ -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;
tools/perf/util/session.c +122 -0
index 6702ac2..d70e37d 100644
@@ -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,6 +900,8 @@ 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;
 	}
@@ -1594,3 +1608,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);
+
+	list_for_each_entry(evsel, &evlist->entries, node)
+		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;
+
+	list_for_each_entry(evsel, &evlist->entries, node) {
+		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;
+}
tools/perf/util/session.h +10 -0
index a4be851..d8521ac 100644
@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
 extern volatile int session_done;
 
 #define session_done()	ACCESS_ONCE(session_done)
+
+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 */
tools/perf/util/tool.h +2 -1
index f116369..bb2708b 100644
@@ -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;
 };
Signature · 1 line
1.9.1
maintainer
#3 lore raw

Re: [PATCH 01/16] perf tools: Add id index

Quoted 11 lines · Arnaldo Carvalho de Melo

Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:

Quoted 5 lines

Add an index of the event identifiers.

This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.

This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...

Quoted 7 lines

Can you try to explain like to a seven year old?

Sigh.

'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.

I also often get confused when I saw an id and/or idx. ;-)

Thanks,
Namhyung

author reviewer
#4 lore raw

Re: [PATCH 01/16] perf tools: Add id index

In reply to Arnaldo Carvalho de Melo
Quoted 11 lines · Arnaldo Carvalho de Melo

Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:

Quoted 5 lines

Add an index of the event identifiers.

This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.

This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...

Quoted 7 lines

Can you try to explain like to a seven year old?

Sigh.

'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.

This is the event id (also called the sample id) which is a unique number allocated by the kernel to the event created by perf_event_open(). Events can include the event id by having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER.

Currently the main use of the event id is to match an event back to the evsel to which it belongs i.e. perf_evlist__id2evsel()

The purpose of this patch is to make it possible to match an event back to the mmap from which it was read. The reason that is useful is because the mmap represents a time-ordered context (either for a cpu or for a thread). Intel PT decodes trace information on that basis. In full-trace mode, that information can be recorded when the Intel PT trace is read, but in sample-mode the Intel PT trace data is embedded in a sample and it is in that case that the "id index" is needed.

So the mmaps are numbered (idx) and the cpu and tid recorded against the id by perf_evlist__set_sid_idx() which is called by perf_evlist__mmap_per_evsel().

That information is recorded on the perf.data file in the new "id index". idx, cpu and tid are added to struct perf_sample_id (which is the node of evlist's hash table to match ids to evsels). The information can be retrieved using perf_evlist__id2sid(). Note however this all depends on having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER, otherwise ids are not recorded.

The "id index" is a synthesized event record which will be created when Intel PT sampling is used by calling perf_event__synthesize_id_index().

Quoted 332 lines

- Arnaldo

Quoted 327 lines
---
 tools/perf/builtin-inject.c |   1 +
 tools/perf/util/event.c     |   1 +
 tools/perf/util/event.h     |  15 ++++++
 tools/perf/util/evlist.c    |  26 ++++++++--
 tools/perf/util/evsel.h     |   3 ++
 tools/perf/util/session.c   | 122 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/session.h   |  10 ++++
 tools/perf/util/tool.h      |   3 +-
 8 files changed, 177 insertions(+), 4 deletions(-)
tools/perf/builtin-inject.c +1 -0
index de99ca1..046c719 100644
@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
 			.tracing_data	= perf_event__repipe_op2_synth,
 			.finished_round	= perf_event__repipe_op2_synth,
 			.build_id	= perf_event__repipe_op2_synth,
+			.id_index	= perf_event__repipe_op2_synth,
 		},
 		.input_name  = "-",
 		.samples = LIST_HEAD_INIT(inject.samples),
tools/perf/util/event.c +1 -0
index 4af6b27..bbf6705 100644
@@ -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)
tools/perf/util/event.h +15 -0
index 5699e7e..c89518e 100644
@@ -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);
tools/perf/util/evlist.c +23 -3
index 3c9e77d..0babd39 100644
@@ -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;
tools/perf/util/evsel.h +3 -0
index 163c560..4861e8c 100644
@@ -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;
tools/perf/util/session.c +122 -0
index 6702ac2..d70e37d 100644
@@ -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,6 +900,8 @@ 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;
 	}
@@ -1594,3 +1608,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);
+
+	list_for_each_entry(evsel, &evlist->entries, node)
+		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;
+
+	list_for_each_entry(evsel, &evlist->entries, node) {
+		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;
+}
tools/perf/util/session.h +10 -0
index a4be851..d8521ac 100644
@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
 extern volatile int session_done;
 
 #define session_done()	ACCESS_ONCE(session_done)
+
+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 */
tools/perf/util/tool.h +2 -1
index f116369..bb2708b 100644
@@ -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;
 };
Signature · 1 line
1.9.1
Acked-by
#5 lore raw

Re: [PATCH 01/16] perf tools: Add id index

Quoted 46 lines · Adrian Hunter
Quoted 11 lines · Arnaldo Carvalho de Melo

Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:

Quoted 5 lines

Add an index of the event identifiers.

This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.

This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...

Quoted 7 lines

Can you try to explain like to a seven year old?

Sigh.

'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.

This is the event id (also called the sample id) which is a unique number allocated by the kernel to the event created by perf_event_open(). Events can include the event id by having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER.

Currently the main use of the event id is to match an event back to the evsel to which it belongs i.e. perf_evlist__id2evsel()

The purpose of this patch is to make it possible to match an event back to the mmap from which it was read. The reason that is useful is because the mmap represents a time-ordered context (either for a cpu or for a thread). Intel PT decodes trace information on that basis. In full-trace mode, that information can be recorded when the Intel PT trace is read, but in sample-mode the Intel PT trace data is embedded in a sample and it is in that case that the "id index" is needed.

So the mmaps are numbered (idx) and the cpu and tid recorded against the id by perf_evlist__set_sid_idx() which is called by perf_evlist__mmap_per_evsel().

That information is recorded on the perf.data file in the new "id index". idx, cpu and tid are added to struct perf_sample_id (which is the node of evlist's hash table to match ids to evsels). The information can be retrieved using perf_evlist__id2sid(). Note however this all depends on having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER, otherwise ids are not recorded.

Quoted 2 lines

The "id index" is a synthesized event record which will be created when Intel PT sampling is used by calling perf_event__synthesize_id_index().

with above text in changelog:

thanks,
jirka

author reviewer
#6 lore raw

PATCH 2/16 perf pmu: Let pmu's with no events show up on perf list

In reply to Adrian Hunter

perf list only lists PMUs with events. Add a
flag to cause a PMU to be also listed separately.

---
 tools/perf/util/pmu.c | 13 +++++++++++--
 tools/perf/util/pmu.h |  1 +
 2 files changed, 12 insertions(+), 2 deletions(-)
tools/perf/util/pmu.c +11 -2
index e243ad9..91dca60 100644
@@ -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++;
+		if (pmu->selectable)
+			len++;
+	}
 	aliases = malloc(sizeof(char *) * len);
 	if (!aliases)
 		return;
 	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");
@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only)
 			aliases[j] = strdup(aliases[j]);
 			j++;
 		}
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);
+			j++;
+		}
+	}
 	len = j;
 	qsort(aliases, len, sizeof(char *), cmp_string);
 	for (j = 0; j < len; j++) {
tools/perf/util/pmu.h +1 -0
index fe9dfbe..8092de7 100644
@@ -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 */
Signature · 1 line
1.9.1
maintainer
#7 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Quoted 35 lines · Adrian Hunter

perf list only lists PMUs with events. Add a
flag to cause a PMU to be also listed separately.

---
 tools/perf/util/pmu.c | 13 +++++++++++--
 tools/perf/util/pmu.h |  1 +
 2 files changed, 12 insertions(+), 2 deletions(-)
tools/perf/util/pmu.c +5 -2
index e243ad9..91dca60 100644
@@ -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++;
+		if (pmu->selectable)
+			len++;
+	}
 	aliases = malloc(sizeof(char *) * len);
 	if (!aliases)
 		return;
 	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");

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780

Quoted 6 lines
tools/perf/util/pmu.c +3 -0
@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only)
 			aliases[j] = strdup(aliases[j]);
 			j++;
 		}
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Quoted 11 lines
tools/perf/util/pmu.c +3 -0
+			j++;
+		}
+	}
 	len = j;
 	qsort(aliases, len, sizeof(char *), cmp_string);
 	for (j = 0; j < len; j++) {
tools/perf/util/pmu.h +0 -0
index fe9dfbe..8092de7 100644

tools/perf/util/pmu.h struct perf_event_attr; · line 21

Quoted 4 lines
tools/perf/util/pmu.h +1 -0
@@ -18,6 +18,7 @@ struct perf_event_attr;
 struct perf_pmu {
 	char *name;
 	__u32 type;
+	bool selectable;

I think it's a bit confusing. IIUC this 'selectable' means that this PMU doesn't contain any events but wants to be listed. So the normal PMUs which contain events are not selectable, right?

Thanks,
Namhyung

Quoted 3 lines
tools/perf/util/pmu.h +0 -0
 	struct perf_event_attr *default_config;
 	struct cpu_map *cpus;
 	struct list_head format;  /* HEAD struct perf_pmu_format -> list */
#8 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780

Quoted 3 lines
tools/perf/util/pmu.c +3 -0
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

- Arnado

#9 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 9 lines

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780

Quoted 3 lines
tools/perf/util/pmu.c +3 -0
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?

Ok, will do the fixes in a separate patch...

- Arnaldo

#10 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 11 lines

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 9 lines

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

Review

Quoted 3 lines
(quoted diff) +3 -0
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

Quoted 7 lines

Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?

Ok, will do the fixes in a separate patch...

- Arnaldo

The patch below should check everything and warn the user, even maintaining that void return...

Acked-by tags welcome as always :-)

- Arnaldo

tools/perf/util/pmu.c +22 -10
index 91dca60..881b754 100644
@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)
 		if (pmu->selectable)
 			len++;
 	}
-	aliases = malloc(sizeof(char *) * len);
+	aliases = zalloc(sizeof(char *) * len);
 	if (!aliases)
-		return;
+		goto out_enomem;
 	pmu = NULL;
 	j = 0;
 	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
@@ -768,16 +768,20 @@ 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) {
-			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
-			aliases[j] = strdup(buf);
+			char *s;
+			if (asprintf(&s, "%s//", pmu->name) < 0)
+				goto out_enomem;
+			aliases[j] = s;
 			j++;
 		}
 	}
@@ -789,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)
author reviewer
#11 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Quoted 33 lines · Arnaldo Carvalho de Melo

Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 11 lines

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 9 lines

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

Review

Quoted 3 lines
(quoted diff) +3 -0
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

Quoted 7 lines

Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?

Ok, will do the fixes in a separate patch...

- Arnaldo

The patch below should check everything and warn the user, even maintaining that void return...

Acked-by tags welcome as always :-)

- Arnaldo

tools/perf/util/pmu.c +0 -0
index 91dca60..881b754 100644

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758

Quoted 8 lines
tools/perf/util/pmu.c +2 -2
@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)
  		if (pmu->selectable)
  			len++;
  	}
-	aliases = malloc(sizeof(char *) * len);
+	aliases = zalloc(sizeof(char *) * len);
  	if (!aliases)
-		return;
+		goto out_enomem;

That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.

Quoted 54 lines
tools/perf/util/pmu.c +20 -8
  	pmu = NULL;
  	j = 0;
  	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
@@ -768,16 +768,20 @@ 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) {
-			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
-			aliases[j] = strdup(buf);
+			char *s;
+			if (asprintf(&s, "%s//", pmu->name) < 0)
+				goto out_enomem;
+			aliases[j] = s;
  			j++;
  		}
  	}
@@ -789,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)
author reviewer
#12 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Quoted 29 lines · Adrian Hunter
Quoted 28 lines · Arnaldo Carvalho de Melo

Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 11 lines

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 9 lines

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

Review

Quoted 3 lines
(quoted diff) +3 -0
+        if (pmu->selectable) {
+            scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+            aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

Quoted 7 lines

Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?

Ok, will do the fixes in a separate patch...

- Arnaldo

The patch below should check everything and warn the user, even maintaining that void return...

Acked-by tags welcome as always :-)

- Arnaldo

Quoted 15 lines

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758

Quoted 13 lines
tools/perf/util/pmu.c +2 -2
index 91dca60..881b754 100644
@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)
          if (pmu->selectable)
              len++;
      }
-    aliases = malloc(sizeof(char *) * len);
+    aliases = zalloc(sizeof(char *) * len);
      if (!aliases)
-        return;
+        goto out_enomem;

That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.

Than again, no it doesn't sorry for the noise.

Quoted 55 lines
Quoted 55 lines
tools/perf/util/pmu.c +20 -8
      pmu = NULL;
      j = 0;
      while ((pmu = perf_pmu__scan(pmu)) != NULL) {
@@ -768,16 +768,20 @@ 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) {
-            scnprintf(buf, sizeof(buf), "%s//", pmu->name);
-            aliases[j] = strdup(buf);
+            char *s;
+            if (asprintf(&s, "%s//", pmu->name) < 0)
+                goto out_enomem;
+            aliases[j] = s;
              j++;
          }
      }
@@ -789,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)
#13 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

In reply to Adrian Hunter

Em Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:

Quoted 29 lines · Arnaldo Carvalho de Melo
Quoted 28 lines

Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 11 lines

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 9 lines

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Adrian Hunter

Review

Quoted 3 lines
(quoted diff) +3 -0
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);

You need to check the return value here (and above too).

Well spotted, fixing this up.

Quoted 7 lines

Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?

Ok, will do the fixes in a separate patch...

- Arnaldo

The patch below should check everything and warn the user, even maintaining that void return...

Acked-by tags welcome as always :-)

- Arnaldo

Quoted 15 lines

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758

Quoted 13 lines
tools/perf/util/pmu.c +2 -2
index 91dca60..881b754 100644
@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)
 		if (pmu->selectable)
 			len++;
 	}
-	aliases = malloc(sizeof(char *) * len);
+	aliases = zalloc(sizeof(char *) * len);
 	if (!aliases)
-		return;
+		goto out_enomem;

That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.

Oops, yeah, my bad, it should not have the !, fixing...

Quoted 54 lines
Quoted 54 lines
tools/perf/util/pmu.c +20 -8
 	pmu = NULL;
 	j = 0;
 	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
@@ -768,16 +768,20 @@ 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) {
-			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
-			aliases[j] = strdup(buf);
+			char *s;
+			if (asprintf(&s, "%s//", pmu->name) < 0)
+				goto out_enomem;
+			aliases[j] = s;
 			j++;
 		}
 	}
@@ -789,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)
#14 lore raw

Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list

Em Fri, Oct 24, 2014 at 11:45:00AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 7 lines

Em Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:

Quoted 6 lines

tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758

Quoted 4 lines
tools/perf/util/pmu.c +2 -1
+	aliases = zalloc(sizeof(char *) * len);
 	if (!aliases)
-		return;
+		goto out_enomem;

That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.

You got me confused, yeah, it ended up a bit confusing, but the idea was to share the error message, i.e. have just one printf("FATAL:
enomem"...) for both alloc failures, the later will have to free the array elements, and it is ok to call free(NULL), thus the change from malloc to zalloc(aliases), so that all its entries were zeroed, yadda, yadda.

- Arnaldo

Quoted 57 lines

Oops, yeah, my bad, it should not have the !, fixing...

Quoted 54 lines
Quoted 54 lines
tools/perf/util/pmu.c +20 -8
 	pmu = NULL;
 	j = 0;
 	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
@@ -768,16 +768,20 @@ 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) {
-			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
-			aliases[j] = strdup(buf);
+			char *s;
+			if (asprintf(&s, "%s//", pmu->name) < 0)
+				goto out_enomem;
+			aliases[j] = s;
 			j++;
 		}
 	}
@@ -789,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)
#15 lore raw

[tip:perf/core] perf pmu: Let pmu' s with no events show up on perf list

In reply to Adrian Hunter
perf pmu: Let pmu's with no events show up on perf list
Commit-ID:  42634bc7a02ead59cf2d50e60d8b8f825de8a3b0
Gitweb:     http://git.kernel.org/tip/42634bc7a02ead59cf2d50e60d8b8f825de8a3b0
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:10 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:48 -0200

perf pmu: Let pmu's with no events show up on perf list

perf list only lists PMUs with events. Add a flag to cause a PMU to be also listed separately.

---
 tools/perf/util/pmu.c | 13 +++++++++++--
 tools/perf/util/pmu.h |  1 +
 2 files changed, 12 insertions(+), 2 deletions(-)
tools/perf/util/pmu.c +11 -2
index e243ad9..91dca60 100644
@@ -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++;
+		if (pmu->selectable)
+			len++;
+	}
 	aliases = malloc(sizeof(char *) * len);
 	if (!aliases)
 		return;
 	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");
@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only)
 			aliases[j] = strdup(aliases[j]);
 			j++;
 		}
+		if (pmu->selectable) {
+			scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+			aliases[j] = strdup(buf);
+			j++;
+		}
+	}
 	len = j;
 	qsort(aliases, len, sizeof(char *), cmp_string);
 	for (j = 0; j < len; j++) {
tools/perf/util/pmu.h +1 -0
index fe9dfbe..8092de7 100644
@@ -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 */
author reviewer
#16 lore raw

PATCH 3/16 perf session: Add perf_session__deliver_synth_event()

In reply to Adrian Hunter

Add a function to deliver synthesized events from
within a session.

---
 tools/perf/util/session.c | 14 ++++++++++++++
 tools/perf/util/session.h |  5 +++++
 2 files changed, 19 insertions(+)
tools/perf/util/session.c +14 -0
index d70e37d..09635d1 100644
@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session,
 	}
 }
 
+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;
tools/perf/util/session.h +5 -0
index d8521ac..dc26ebf 100644
@@ -127,6 +127,11 @@ 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);
Signature · 1 line
1.9.1
maintainer
#17 lore raw

Re: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()

Quoted 20 lines · Adrian Hunter

Add a function to deliver synthesized events from
within a session.

---
 tools/perf/util/session.c | 14 ++++++++++++++
 tools/perf/util/session.h |  5 +++++
 2 files changed, 19 insertions(+)
tools/perf/util/session.c +3 -0
index d70e37d..09635d1 100644
@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session,
 	}
 }
 
+int perf_session__deliver_synth_event(struct perf_session *session,
+				      union perf_event *event,
+				      struct perf_sample *sample,

tools/perf/util/session.c static s64 perf_session__process_user_event(struct perf_session *session, · line 922

Quoted 10 lines
tools/perf/util/session.c +10 -0
+				      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);
+}

It seems that it's basically same as what perf_session__process_event() does. So it might deserve a comment or better changelog why it's needed.

Thanks,
Namhyung

Quoted 20 lines
tools/perf/util/session.c +1 -0
+
 static void event_swap(union perf_event *event, bool sample_id_all)
 {
 	perf_event__swap_op swap;
tools/perf/util/session.h +5 -0
index d8521ac..dc26ebf 100644
@@ -127,6 +127,11 @@ 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);
author reviewer
#18 lore raw

Re: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()

Quoted 18 lines · Namhyung Kim
Quoted 17 lines · Adrian Hunter

Add a function to deliver synthesized events from
within a session.

---
 tools/perf/util/session.c | 14 ++++++++++++++
 tools/perf/util/session.h |  5 +++++
 2 files changed, 19 insertions(+)
tools/perf/util/session.c +0 -0
index d70e37d..09635d1 100644
@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session,
 	}
 }
 
Quoted 16 lines

tools/perf/util/session.c static s64 perf_session__process_user_event(struct perf_session *session, · line 922

Quoted 13 lines
tools/perf/util/session.c +13 -0
+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);
+}

It seems that it's basically same as what perf_session__process_event() does. So it might deserve a comment or better changelog why it's needed.

Intel PT decoding works by synthesizing events (primarily branch events) that can then be consumed by existing tools. This function will be used to deliver those events.

Quoted 27 lines

Thanks,
Namhyung

Quoted 20 lines
tools/perf/util/session.c +1 -0
+
 static void event_swap(union perf_event *event, bool sample_id_all)
 {
 	perf_event__swap_op swap;
tools/perf/util/session.h +5 -0
index d8521ac..dc26ebf 100644
@@ -127,6 +127,11 @@ 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);
author reviewer
#19 lore raw

PATCH 4/16 perf tools: Add a thread stack for synthesizing call chains

In reply to Adrian Hunter

Add a thread stack for synthesizing call chains from call and return events.

---
 tools/perf/Makefile.perf       |   2 +
 tools/perf/util/event.h        |  26 +++++++
 tools/perf/util/thread-stack.c | 151 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/thread-stack.h |  32 +++++++++
 tools/perf/util/thread.c       |   3 +
 tools/perf/util/thread.h       |   3 +
 6 files changed, 217 insertions(+)
 create mode 100644 tools/perf/util/thread-stack.c
 create mode 100644 tools/perf/util/thread-stack.h
tools/perf/Makefile.perf +2 -0
index 262916f..5bbe1ff 100644
@@ -304,6 +304,7 @@ LIB_H += ui/util.h
 LIB_H += ui/ui.h
 LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
+LIB_H += util/thread-stack.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -380,6 +381,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
 LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
 LIB_OBJS += $(OUTPUT)util/cloexec.o
+LIB_OBJS += $(OUTPUT)util/thread-stack.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/event.h +26 -0
index c89518e..e84f929 100644
@@ -143,6 +143,32 @@ struct branch_stack {
 	struct branch_entry	entries[0];
 };
 
+enum {
+	PERF_FLAG_BRANCH		= 1ULL << 0,
+	PERF_FLAG_CALL			= 1ULL << 1,
+	PERF_FLAG_RETURN		= 1ULL << 2,
+	PERF_FLAG_CONDITIONAL		= 1ULL << 3,
+	PERF_FLAG_SYSCALLRET		= 1ULL << 4,
+	PERF_FLAG_ASYNC			= 1ULL << 5,
+	PERF_FLAG_INTERRUPT		= 1ULL << 6,
+	PERF_FLAG_TX_ABORT		= 1ULL << 7,
+	PERF_FLAG_TRACE_BEGIN		= 1ULL << 8,
+	PERF_FLAG_TRACE_END		= 1ULL << 9,
+	PERF_FLAG_IN_TX			= 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK	(\
+	PERF_FLAG_BRANCH	|\
+	PERF_FLAG_CALL		|\
+	PERF_FLAG_RETURN	|\
+	PERF_FLAG_CONDITIONAL	|\
+	PERF_FLAG_SYSCALLRET	|\
+	PERF_FLAG_ASYNC		|\
+	PERF_FLAG_INTERRUPT	|\
+	PERF_FLAG_TX_ABORT	|\
+	PERF_FLAG_TRACE_BEGIN	|\
+	PERF_FLAG_TRACE_END)
+
 struct perf_sample {
 	u64 ip;
 	u32 pid, tid;
tools/perf/util/thread-stack.c new file +151 -0

151 lines not on this page · raw message

tools/perf/util/thread-stack.h new file +32 -0
new file mode 100644
index 0000000..c0ba4cf
@@ -0,0 +1,32 @@
+/*
+ * thread-stack.h: Synthesize a thread's stack using call / return events
+ * 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_THREAD_STACK_H
+#define __PERF_THREAD_STACK_H
+
+#include <sys/types.h>
+
+#include <linux/types.h>
+
+struct thread;
+struct ip_callchain;
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+			  size_t sz, u64 ip);
+void thread_stack__free(struct thread *thread);
+
+#endif
tools/perf/util/thread.c +3 -0
index 2b7b2d9..4e3418d 100644
@@ -4,6 +4,7 @@
 #include <string.h>
 #include "session.h"
 #include "thread.h"
+#include "thread-stack.h"
 #include "util.h"
 #include "debug.h"
 #include "comm.h"
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread)
 {
 	struct comm *comm, *tmp;
 
+	thread_stack__free(thread);
+
 	if (thread->mg) {
 		map_groups__put(thread->mg);
 		thread->mg = NULL;
tools/perf/util/thread.h +3 -0
index 8c75fa7..a057820 100644
@@ -8,6 +8,8 @@
 #include "symbol.h"
 #include <strlist.h>
 
+struct thread_stack;
+
 struct thread {
 	union {
 		struct rb_node	 rb_node;
@@ -25,6 +27,7 @@ struct thread {
 	int			comm_len;
 
 	void			*priv;
+	struct thread_stack	*ts;
 };
 
 struct machine;
Signature · 1 line
1.9.1
#20 lore raw

Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains

Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:

Quoted 142 lines

Add a thread stack for synthesizing call chains from call and return events.

---
 tools/perf/Makefile.perf       |   2 +
 tools/perf/util/event.h        |  26 +++++++
 tools/perf/util/thread-stack.c | 151 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/thread-stack.h |  32 +++++++++
 tools/perf/util/thread.c       |   3 +
 tools/perf/util/thread.h       |   3 +
 6 files changed, 217 insertions(+)
 create mode 100644 tools/perf/util/thread-stack.c
 create mode 100644 tools/perf/util/thread-stack.h
tools/perf/Makefile.perf +2 -0
index 262916f..5bbe1ff 100644
@@ -304,6 +304,7 @@ LIB_H += ui/util.h
 LIB_H += ui/ui.h
 LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
+LIB_H += util/thread-stack.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -380,6 +381,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
 LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
 LIB_OBJS += $(OUTPUT)util/cloexec.o
+LIB_OBJS += $(OUTPUT)util/thread-stack.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/event.h +26 -0
index c89518e..e84f929 100644
@@ -143,6 +143,32 @@ struct branch_stack {
 	struct branch_entry	entries[0];
 };
 
+enum {
+	PERF_FLAG_BRANCH		= 1ULL << 0,
+	PERF_FLAG_CALL			= 1ULL << 1,
+	PERF_FLAG_RETURN		= 1ULL << 2,
+	PERF_FLAG_CONDITIONAL		= 1ULL << 3,
+	PERF_FLAG_SYSCALLRET		= 1ULL << 4,
+	PERF_FLAG_ASYNC			= 1ULL << 5,
+	PERF_FLAG_INTERRUPT		= 1ULL << 6,
+	PERF_FLAG_TX_ABORT		= 1ULL << 7,
+	PERF_FLAG_TRACE_BEGIN		= 1ULL << 8,
+	PERF_FLAG_TRACE_END		= 1ULL << 9,
+	PERF_FLAG_IN_TX			= 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK	(\
+	PERF_FLAG_BRANCH	|\
+	PERF_FLAG_CALL		|\
+	PERF_FLAG_RETURN	|\
+	PERF_FLAG_CONDITIONAL	|\
+	PERF_FLAG_SYSCALLRET	|\
+	PERF_FLAG_ASYNC		|\
+	PERF_FLAG_INTERRUPT	|\
+	PERF_FLAG_TX_ABORT	|\
+	PERF_FLAG_TRACE_BEGIN	|\
+	PERF_FLAG_TRACE_END)
+
 struct perf_sample {
 	u64 ip;
 	u32 pid, tid;
tools/perf/util/thread-stack.c new file +64 -0
new file mode 100644
index 0000000..c1ca2a9
@@ -0,0 +1,151 @@
+/*
+ * thread-stack.c: Synthesize a thread's stack using call / return events
+ * 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 "thread.h"
+#include "event.h"
+#include "util.h"
+#include "thread-stack.h"
+
+#define STACK_GROWTH 4096
+
+struct thread_stack_entry {
+	u64 ret_addr;
+};
+
+struct thread_stack {
+	struct thread_stack_entry *stack;
+	size_t cnt;
+	size_t sz;
+	u64 trace_nr;
+};
+
+static void thread_stack__grow(struct thread_stack *ts)
+{
+	struct thread_stack_entry *new_stack;
+	size_t sz, new_sz;
+
+	new_sz = ts->sz + STACK_GROWTH;
+	sz = new_sz * sizeof(struct thread_stack_entry);
+	new_stack = realloc(ts->stack, sz);
+	if (new_stack) {
+		ts->stack = new_stack;
+		ts->sz = new_sz;
+	}
+}
+
+static struct thread_stack *thread_stack__new(void)
+{
+	struct thread_stack *ts;
+
+	ts = zalloc(sizeof(struct thread_stack));
+	if (!ts)
+		return NULL;
+
+	thread_stack__grow(ts);
+	if (!ts->stack) {
+		free(ts);
+		return NULL;
+	}
+
+	return ts;
+}
+

tools/perf/util/thread-stack.c line 74

Quoted 10 lines
tools/perf/util/thread-stack.c new file +10 -0
+static void thread_stack__push(struct thread_stack *ts, u64 ret_addr)
+{
+	if (ts->cnt == ts->sz) {
+		thread_stack__grow(ts);
+		if (ts->cnt == ts->sz)
+			ts->cnt = 0;
+	}
+
+	ts->stack[ts->cnt++].ret_addr = ret_addr;
+}

So can you elaborate on the use case, i.e. this silently trows the existing stack contents away if it doesn't grow, looks strange :-\

Merits some explanation about why this is OK, to say the least.

Quoted 159 lines
tools/perf/util/thread-stack.c new file +77 -0
+
+static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
+{
+	if (!ts->cnt)
+		return;
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		ts->cnt -= 1;
+	} else {
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr == ret_addr) {
+				ts->cnt = i;
+				return;
+			}
+		}
+	}
+}
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr)
+{
+	if (!thread)
+		return;
+
+	if (!thread->ts) {
+		thread->ts = thread_stack__new();
+		if (!thread->ts)
+			return;
+		thread->ts->trace_nr = trace_nr;
+	}
+
+	if (trace_nr != thread->ts->trace_nr) {
+		thread->ts->trace_nr = trace_nr;
+		thread->ts->cnt = 0;
+	}
+
+	if (flags & PERF_FLAG_CALL) {
+		u64 ret_addr;
+
+		if (!to_ip)
+			return;
+		ret_addr = from_ip + insn_len;
+		if (ret_addr == to_ip)
+			return; /* Zero-length calls are excluded */
+		thread_stack__push(thread->ts, ret_addr);
+	} else if (flags & PERF_FLAG_RETURN) {
+		if (!from_ip)
+			return;
+		thread_stack__pop(thread->ts, to_ip);
+	}
+}
+
+void thread_stack__free(struct thread *thread)
+{
+	if (thread->ts) {
+		zfree(&thread->ts->stack);
+		zfree(&thread->ts);
+	}
+}
+
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+			  size_t sz, u64 ip)
+{
+	size_t i;
+
+	if (!thread || !thread->ts)
+		chain->nr = 1;
+	else
+		chain->nr = min(sz, thread->ts->cnt + 1);
+
+	chain->ips[0] = ip;
+
+	for (i = 1; i < chain->nr; i++)
+		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
+}
tools/perf/util/thread-stack.h new file +32 -0
new file mode 100644
index 0000000..c0ba4cf
@@ -0,0 +1,32 @@
+/*
+ * thread-stack.h: Synthesize a thread's stack using call / return events
+ * 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_THREAD_STACK_H
+#define __PERF_THREAD_STACK_H
+
+#include <sys/types.h>
+
+#include <linux/types.h>
+
+struct thread;
+struct ip_callchain;
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+			  size_t sz, u64 ip);
+void thread_stack__free(struct thread *thread);
+
+#endif
tools/perf/util/thread.c +3 -0
index 2b7b2d9..4e3418d 100644
@@ -4,6 +4,7 @@
 #include <string.h>
 #include "session.h"
 #include "thread.h"
+#include "thread-stack.h"
 #include "util.h"
 #include "debug.h"
 #include "comm.h"
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread)
 {
 	struct comm *comm, *tmp;
 
+	thread_stack__free(thread);
+
 	if (thread->mg) {
 		map_groups__put(thread->mg);
 		thread->mg = NULL;
tools/perf/util/thread.h +3 -0
index 8c75fa7..a057820 100644
@@ -8,6 +8,8 @@
 #include "symbol.h"
 #include <strlist.h>
 
+struct thread_stack;
+
 struct thread {
 	union {
 		struct rb_node	 rb_node;
@@ -25,6 +27,7 @@ struct thread {
 	int			comm_len;
 
 	void			*priv;
+	struct thread_stack	*ts;
 };
 
 struct machine;
Signature · 1 line
1.9.1
author reviewer
#21 lore raw

Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains

Quoted 157 lines · Arnaldo Carvalho de Melo

Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:

Quoted 142 lines

Add a thread stack for synthesizing call chains from call and return events.

---
 tools/perf/Makefile.perf       |   2 +
 tools/perf/util/event.h        |  26 +++++++
 tools/perf/util/thread-stack.c | 151 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/thread-stack.h |  32 +++++++++
 tools/perf/util/thread.c       |   3 +
 tools/perf/util/thread.h       |   3 +
 6 files changed, 217 insertions(+)
 create mode 100644 tools/perf/util/thread-stack.c
 create mode 100644 tools/perf/util/thread-stack.h
tools/perf/Makefile.perf +2 -0
index 262916f..5bbe1ff 100644
@@ -304,6 +304,7 @@ LIB_H += ui/util.h
 LIB_H += ui/ui.h
 LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
+LIB_H += util/thread-stack.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -380,6 +381,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
 LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
 LIB_OBJS += $(OUTPUT)util/cloexec.o
+LIB_OBJS += $(OUTPUT)util/thread-stack.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/event.h +26 -0
index c89518e..e84f929 100644
@@ -143,6 +143,32 @@ struct branch_stack {
 	struct branch_entry	entries[0];
 };
 
+enum {
+	PERF_FLAG_BRANCH		= 1ULL << 0,
+	PERF_FLAG_CALL			= 1ULL << 1,
+	PERF_FLAG_RETURN		= 1ULL << 2,
+	PERF_FLAG_CONDITIONAL		= 1ULL << 3,
+	PERF_FLAG_SYSCALLRET		= 1ULL << 4,
+	PERF_FLAG_ASYNC			= 1ULL << 5,
+	PERF_FLAG_INTERRUPT		= 1ULL << 6,
+	PERF_FLAG_TX_ABORT		= 1ULL << 7,
+	PERF_FLAG_TRACE_BEGIN		= 1ULL << 8,
+	PERF_FLAG_TRACE_END		= 1ULL << 9,
+	PERF_FLAG_IN_TX			= 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK	(\
+	PERF_FLAG_BRANCH	|\
+	PERF_FLAG_CALL		|\
+	PERF_FLAG_RETURN	|\
+	PERF_FLAG_CONDITIONAL	|\
+	PERF_FLAG_SYSCALLRET	|\
+	PERF_FLAG_ASYNC		|\
+	PERF_FLAG_INTERRUPT	|\
+	PERF_FLAG_TX_ABORT	|\
+	PERF_FLAG_TRACE_BEGIN	|\
+	PERF_FLAG_TRACE_END)
+
 struct perf_sample {
 	u64 ip;
 	u32 pid, tid;
tools/perf/util/thread-stack.c new file +64 -0
new file mode 100644
index 0000000..c1ca2a9
@@ -0,0 +1,151 @@
+/*
+ * thread-stack.c: Synthesize a thread's stack using call / return events
+ * 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 "thread.h"
+#include "event.h"
+#include "util.h"
+#include "thread-stack.h"
+
+#define STACK_GROWTH 4096
+
+struct thread_stack_entry {
+	u64 ret_addr;
+};
+
+struct thread_stack {
+	struct thread_stack_entry *stack;
+	size_t cnt;
+	size_t sz;
+	u64 trace_nr;
+};
+
+static void thread_stack__grow(struct thread_stack *ts)
+{
+	struct thread_stack_entry *new_stack;
+	size_t sz, new_sz;
+
+	new_sz = ts->sz + STACK_GROWTH;
+	sz = new_sz * sizeof(struct thread_stack_entry);
+	new_stack = realloc(ts->stack, sz);
+	if (new_stack) {
+		ts->stack = new_stack;
+		ts->sz = new_sz;
+	}
+}
+
+static struct thread_stack *thread_stack__new(void)
+{
+	struct thread_stack *ts;
+
+	ts = zalloc(sizeof(struct thread_stack));
+	if (!ts)
+		return NULL;
+
+	thread_stack__grow(ts);
+	if (!ts->stack) {
+		free(ts);
+		return NULL;
+	}
+
+	return ts;
+}
+

tools/perf/util/thread-stack.c line 74

Quoted 10 lines
tools/perf/util/thread-stack.c new file +10 -0
+static void thread_stack__push(struct thread_stack *ts, u64 ret_addr)
+{
+	if (ts->cnt == ts->sz) {
+		thread_stack__grow(ts);
+		if (ts->cnt == ts->sz)
+			ts->cnt = 0;
+	}
+
+	ts->stack[ts->cnt++].ret_addr = ret_addr;
+}

So can you elaborate on the use case, i.e. this silently trows the existing stack contents away if it doesn't grow, looks strange :-\

Quoted 1 line

Merits some explanation about why this is OK, to say the least.

I considered the call stack to be augmenting the information so not worth failing the whole session over. Equally memory allocation failure is likely to be fatal anyway due to oom-killers or other allocations failing.

But it could certainly print a warning, so I will add that.

Quoted 162 lines
Quoted 160 lines
tools/perf/util/thread-stack.c new file +77 -0
+
+static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
+{
+	if (!ts->cnt)
+		return;
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		ts->cnt -= 1;
+	} else {
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr == ret_addr) {
+				ts->cnt = i;
+				return;
+			}
+		}
+	}
+}
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr)
+{
+	if (!thread)
+		return;
+
+	if (!thread->ts) {
+		thread->ts = thread_stack__new();
+		if (!thread->ts)
+			return;
+		thread->ts->trace_nr = trace_nr;
+	}
+
+	if (trace_nr != thread->ts->trace_nr) {
+		thread->ts->trace_nr = trace_nr;
+		thread->ts->cnt = 0;
+	}
+
+	if (flags & PERF_FLAG_CALL) {
+		u64 ret_addr;
+
+		if (!to_ip)
+			return;
+		ret_addr = from_ip + insn_len;
+		if (ret_addr == to_ip)
+			return; /* Zero-length calls are excluded */
+		thread_stack__push(thread->ts, ret_addr);
+	} else if (flags & PERF_FLAG_RETURN) {
+		if (!from_ip)
+			return;
+		thread_stack__pop(thread->ts, to_ip);
+	}
+}
+
+void thread_stack__free(struct thread *thread)
+{
+	if (thread->ts) {
+		zfree(&thread->ts->stack);
+		zfree(&thread->ts);
+	}
+}
+
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+			  size_t sz, u64 ip)
+{
+	size_t i;
+
+	if (!thread || !thread->ts)
+		chain->nr = 1;
+	else
+		chain->nr = min(sz, thread->ts->cnt + 1);
+
+	chain->ips[0] = ip;
+
+	for (i = 1; i < chain->nr; i++)
+		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
+}
tools/perf/util/thread-stack.h new file +32 -0
new file mode 100644
index 0000000..c0ba4cf
@@ -0,0 +1,32 @@
+/*
+ * thread-stack.h: Synthesize a thread's stack using call / return events
+ * 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_THREAD_STACK_H
+#define __PERF_THREAD_STACK_H
+
+#include <sys/types.h>
+
+#include <linux/types.h>
+
+struct thread;
+struct ip_callchain;
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+			  size_t sz, u64 ip);
+void thread_stack__free(struct thread *thread);
+
+#endif
tools/perf/util/thread.c +3 -0
index 2b7b2d9..4e3418d 100644
@@ -4,6 +4,7 @@
 #include <string.h>
 #include "session.h"
 #include "thread.h"
+#include "thread-stack.h"
 #include "util.h"
 #include "debug.h"
 #include "comm.h"
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread)
 {
 	struct comm *comm, *tmp;
 
+	thread_stack__free(thread);
+
 	if (thread->mg) {
 		map_groups__put(thread->mg);
 		thread->mg = NULL;
tools/perf/util/thread.h +3 -0
index 8c75fa7..a057820 100644
@@ -8,6 +8,8 @@
 #include "symbol.h"
 #include <strlist.h>
 
+struct thread_stack;
+
 struct thread {
 	union {
 		struct rb_node	 rb_node;
@@ -25,6 +27,7 @@ struct thread {
 	int			comm_len;
 
 	void			*priv;
+	struct thread_stack	*ts;
 };
 
 struct machine;
Signature · 1 line
1.9.1
maintainer
#22 lore raw

Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains

In reply to Adrian Hunter
Quoted 8 lines · Adrian Hunter
tools/perf/util/thread-stack.c new file +8 -0
+static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
+{
+	if (!ts->cnt)
+		return;
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		ts->cnt -= 1;
+	} else {

tools/perf/util/thread-stack.c line 93

Quoted 10 lines
tools/perf/util/thread-stack.c new file +10 -0
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr == ret_addr) {
+				ts->cnt = i;
+				return;
+			}
+		}
+	}
+}

Why not making it a single loop like:

	int i;

	for (i = ts->cnt - 1; i >= 0; i--) {
		if (ts->stack[i].ret_addr == ret_addr) {
			ts->cnt = i;
			return;
		}
        }

Thanks,
Namhyung

#23 lore raw

Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains

In reply to Adrian Hunter

On Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter wrote:

SNIP

Quoted 9 lines
tools/perf/util/thread-stack.c new file +9 -0
+		while (i--) {
+			if (ts->stack[i].ret_addr == ret_addr) {
+				ts->cnt = i;
+				return;
+			}
+		}
+	}
+}
+

tools/perf/util/thread-stack.c line 104

Quoted 10 lines
tools/perf/util/thread-stack.c new file +10 -0
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+			 u64 to_ip, u16 insn_len, u64 trace_nr)
+{
+	if (!thread)
+		return;
+
+	if (!thread->ts) {
+		thread->ts = thread_stack__new();
+		if (!thread->ts)
+			return;

should the function return 'int' then..? So the allocation error gets handled properly.

jirka

#24 lore raw

Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains

In reply to Adrian Hunter

On Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter wrote:

SNIP

Quoted 17 lines

};

tools/perf/util/event.h +15 -0
+enum {
+	PERF_FLAG_BRANCH		= 1ULL << 0,
+	PERF_FLAG_CALL			= 1ULL << 1,
+	PERF_FLAG_RETURN		= 1ULL << 2,
+	PERF_FLAG_CONDITIONAL		= 1ULL << 3,
+	PERF_FLAG_SYSCALLRET		= 1ULL << 4,
+	PERF_FLAG_ASYNC			= 1ULL << 5,
+	PERF_FLAG_INTERRUPT		= 1ULL << 6,
+	PERF_FLAG_TX_ABORT		= 1ULL << 7,
+	PERF_FLAG_TRACE_BEGIN		= 1ULL << 8,
+	PERF_FLAG_TRACE_END		= 1ULL << 9,
+	PERF_FLAG_IN_TX			= 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK	(\

tools/perf/util/event.h struct branch_stack { · line 170

Quoted 10 lines
tools/perf/util/event.h +10 -0
+	PERF_FLAG_BRANCH	|\
+	PERF_FLAG_CALL		|\
+	PERF_FLAG_RETURN	|\
+	PERF_FLAG_CONDITIONAL	|\
+	PERF_FLAG_SYSCALLRET	|\
+	PERF_FLAG_ASYNC		|\
+	PERF_FLAG_INTERRUPT	|\
+	PERF_FLAG_TX_ABORT	|\
+	PERF_FLAG_TRACE_BEGIN	|\
+	PERF_FLAG_TRACE_END)

PERF_FLAG_ prefix seems too generic,

should it be something like PERF_BRANCH_FLAG__* ?

jirka

author reviewer
#25 lore raw

PATCH 5/16 perf tools: Add facility to export data in database-friendly way

In reply to Adrian Hunter

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

---
 tools/perf/Makefile.perf    |   2 +
 tools/perf/util/comm.h      |   1 +
 tools/perf/util/db-export.c | 268 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/db-export.h |  86 ++++++++++++++
 tools/perf/util/dso.h       |   1 +
 tools/perf/util/evsel.h     |   1 +
 tools/perf/util/machine.h   |   1 +
 tools/perf/util/symbol.h    |   1 +
 tools/perf/util/thread.h    |   1 +
 9 files changed, 362 insertions(+)
 create mode 100644 tools/perf/util/db-export.c
 create mode 100644 tools/perf/util/db-export.h
tools/perf/Makefile.perf +2 -0
index 5bbe1ff..9134c93 100644
@@ -305,6 +305,7 @@ LIB_H += ui/ui.h
 LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
 LIB_H += util/thread-stack.h
+LIB_H += util/db-export.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -382,6 +383,7 @@ LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
 LIB_OBJS += $(OUTPUT)util/cloexec.o
 LIB_OBJS += $(OUTPUT)util/thread-stack.o
+LIB_OBJS += $(OUTPUT)util/db-export.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/comm.h +1 -0
index 51c10ab..99e7021 100644
@@ -10,6 +10,7 @@ struct comm_str;
 struct comm {
 	struct comm_str *comm_str;
 	u64 start;
+	u64 db_id;
 	struct list_head list;
 	bool exec;
 };
tools/perf/util/db-export.c new file +268 -0

268 lines not on this page · raw message

tools/perf/util/db-export.h new file +86 -0

86 lines not on this page · raw message

tools/perf/util/dso.h +1 -0
index acb651a..8463fc3 100644
@@ -111,6 +111,7 @@ struct dso {
 	enum dso_swap_type	needs_swap;
 	enum dso_binary_type	symtab_type;
 	enum dso_binary_type	binary_type;
+	u64		 db_id;
 	u8		 adjust_symbols:1;
 	u8		 has_build_id:1;
 	u8		 has_srcline:1;
tools/perf/util/evsel.h +1 -0
index 4861e8c..4777262 100644
@@ -95,6 +95,7 @@ struct perf_evsel {
 	int			sample_read;
 	struct perf_evsel	*leader;
 	char			*group_name;
+	u64			db_id;
 };
 
 union u64_swap {
tools/perf/util/machine.h +1 -0
index 2b651a7..4bc57e5 100644
@@ -40,6 +40,7 @@ struct machine {
 	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
+	u64		  db_id;
 };
 
 static inline
tools/perf/util/symbol.h +1 -0
index eb2c19b..6f54ade 100644
@@ -73,6 +73,7 @@ struct symbol {
 	struct rb_node	rb_node;
 	u64		start;
 	u64		end;
+	u64		db_id;
 	u16		namelen;
 	u8		binding;
 	bool		ignore;
tools/perf/util/thread.h +1 -0
index a057820..a3ebb21 100644
@@ -25,6 +25,7 @@ struct thread {
 	bool			dead; /* if set thread has exited */
 	struct list_head	comm_list;
 	int			comm_len;
+	u64			db_id;
 
 	void			*priv;
 	struct thread_stack	*ts;
Signature · 1 line
1.9.1
#26 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Em Thu, Oct 23, 2014 at 01:45:13PM +0300, Adrian Hunter escreveu:

Quoted 11 lines

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Quoted 3 lines

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

So this ends up bloating all data structures with 8 extra bytes, can't we use the priv pointer that some of the structures already have for tooling to put tool specific stuff?

In places like this:

tools/perf/util/thread.h struct thread { · line 31

Quoted 8 lines
tools/perf/util/thread.h +1 -0
@@ -25,6 +25,7 @@ struct thread {
 	bool			dead; /* if set thread has exited */
 	struct list_head	comm_list;
 	int			comm_len;
+	u64			db_id;
 
 	void			*priv;
 	struct thread_stack	*ts;

Instead we would have:

	union {
		void		*priv;
		u64		db_id;
	};

Using a unnamed union is all that is needed in struct thread case, I think, the rest of your patch that deals with 'struct thread' doesn't need to change and the end impact for other tools that have no use whatsoever fot this thread->db_id is zero.

Because after all you will end up using some tool to just process samples, etc, i.e. basically a 'perf report' like tool that instead of creating hist_entries, etc will end up just populating the

machines ->

	machine ->
		threads ->
			map_groups ->
				map ->
					dso ->
						symbols

Hierarchy, right?

Struct symbol even has a mechanism for reserving space for private use, that symbol_conf.priv_size + symbol__priv(), so that we don't incur even in the cost of the priv pointer.

struct perf_evsel already has:

        union {
                void            *priv;
                off_t           id_offset;
        };

Just add db_id there and the rest of your patch that deals with perf_evsel will not need to be changed and again the impact for tools that do not use this will be zero.

The other data structures that still don't have a ->priv area can have one added.

I.e. tool specific bloating of core data structures should be avoided.

Fixing up these things will allow a good chunk of this patchkit to be processed.

Thanks,

- Arnaldo

Quoted 475 lines
---
 tools/perf/Makefile.perf    |   2 +
 tools/perf/util/comm.h      |   1 +
 tools/perf/util/db-export.c | 268 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/db-export.h |  86 ++++++++++++++
 tools/perf/util/dso.h       |   1 +
 tools/perf/util/evsel.h     |   1 +
 tools/perf/util/machine.h   |   1 +
 tools/perf/util/symbol.h    |   1 +
 tools/perf/util/thread.h    |   1 +
 9 files changed, 362 insertions(+)
 create mode 100644 tools/perf/util/db-export.c
 create mode 100644 tools/perf/util/db-export.h
tools/perf/Makefile.perf +2 -0
index 5bbe1ff..9134c93 100644
@@ -305,6 +305,7 @@ LIB_H += ui/ui.h
 LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
 LIB_H += util/thread-stack.h
+LIB_H += util/db-export.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -382,6 +383,7 @@ LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
 LIB_OBJS += $(OUTPUT)util/cloexec.o
 LIB_OBJS += $(OUTPUT)util/thread-stack.o
+LIB_OBJS += $(OUTPUT)util/db-export.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/comm.h +1 -0
index 51c10ab..99e7021 100644
@@ -10,6 +10,7 @@ struct comm_str;
 struct comm {
 	struct comm_str *comm_str;
 	u64 start;
+	u64 db_id;
 	struct list_head list;
 	bool exec;
 };
tools/perf/util/db-export.c new file +268 -0
new file mode 100644
index 0000000..53d0e75
@@ -0,0 +1,268 @@
+/*
+ * 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)
+{
+	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) {
+			err = db_export__symbol(dbe, al->sym, dso);
+			if (err)
+				return err;
+			*sym_db_id = al->sym->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, al->machine,
+						   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;
+}
tools/perf/util/db-export.h new file +86 -0
new file mode 100644
index 0000000..b3643e8
@@ -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
tools/perf/util/dso.h +1 -0
index acb651a..8463fc3 100644
@@ -111,6 +111,7 @@ struct dso {
 	enum dso_swap_type	needs_swap;
 	enum dso_binary_type	symtab_type;
 	enum dso_binary_type	binary_type;
+	u64		 db_id;
 	u8		 adjust_symbols:1;
 	u8		 has_build_id:1;
 	u8		 has_srcline:1;
tools/perf/util/evsel.h +1 -0
index 4861e8c..4777262 100644
@@ -95,6 +95,7 @@ struct perf_evsel {
 	int			sample_read;
 	struct perf_evsel	*leader;
 	char			*group_name;
+	u64			db_id;
 };
 
 union u64_swap {
tools/perf/util/machine.h +1 -0
index 2b651a7..4bc57e5 100644
@@ -40,6 +40,7 @@ struct machine {
 	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
+	u64		  db_id;
 };
 
 static inline
tools/perf/util/symbol.h +1 -0
index eb2c19b..6f54ade 100644
@@ -73,6 +73,7 @@ struct symbol {
 	struct rb_node	rb_node;
 	u64		start;
 	u64		end;
+	u64		db_id;
 	u16		namelen;
 	u8		binding;
 	bool		ignore;
tools/perf/util/thread.h +1 -0
index a057820..a3ebb21 100644
@@ -25,6 +25,7 @@ struct thread {
 	bool			dead; /* if set thread has exited */
 	struct list_head	comm_list;
 	int			comm_len;
+	u64			db_id;
 
 	void			*priv;
 	struct thread_stack	*ts;
Signature · 1 line
1.9.1
maintainer
#27 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

In reply to Adrian Hunter
Quoted 10 lines · Adrian Hunter

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export meta events like task, mmap or some user events to the script optionally and let it process the data?

Thanks,
Namhyung

Quoted 4 lines

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

author reviewer
#28 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Quoted 6 lines · Namhyung Kim
Quoted 5 lines · Adrian Hunter

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

Quoted 8 lines
Quoted 5 lines

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Please explain what you mean by "comfortable".

Quoted 2 lines

meta events like task, mmap or some user events to the script optionally and let it process the data?

Intel PT decoding can generate a lot of data. Many millions of samples at least.

Each sample contains a lot of duplicated information like symbol names, dso names, comms, etc. So it is not optimal to export that for every sample. Even then, each piece of supporting information must be looked up - was it the same as the last sample, no then look it up, was it found, no then add a record. That is very very inefficient.

Exporting each piece of information once, instead of over and over again, is a reasonable thing to do.

Quoted 11 lines

Thanks,
Namhyung

Quoted 4 lines

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

author reviewer
#29 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Quoted 13 lines · Adrian Hunter
Quoted 12 lines · Namhyung Kim
Quoted 10 lines · Adrian Hunter

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Quoted 4 lines
Quoted 2 lines

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Please explain what you mean by "comfortable".

Or rather: What about it is wrong for core code?

Quoted 30 lines
Quoted 3 lines

meta events like task, mmap or some user events to the script optionally and let it process the data?

Intel PT decoding can generate a lot of data. Many millions of samples at least.

Each sample contains a lot of duplicated information like symbol names, dso names, comms, etc. So it is not optimal to export that for every sample. Even then, each piece of supporting information must be looked up - was it the same as the last sample, no then look it up, was it found, no then add a record. That is very very inefficient.

Exporting each piece of information once, instead of over and over again, is a reasonable thing to do.

Quoted 11 lines

Thanks,
Namhyung

Quoted 4 lines

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

maintainer
#30 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Hi Adrian,

Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Please explain what you mean by "comfortable".

Or rather: What about it is wrong for core code?

Well, there's nothing "wrong" about it. :)

But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.

Thanks,
Namhyung

author reviewer
#31 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Quoted 8 lines · Namhyung Kim

Hi Adrian,

Quoted 5 lines · Adrian Hunter
Quoted 4 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

Please explain what you mean by "comfortable".

Or rather: What about it is wrong for core code?

Well, there's nothing "wrong" about it. :)

But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.

It is pretty self-contained in db-export.[ch] and the scripting engines.

#32 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:

Quoted 13 lines · Namhyung Kim
Quoted 12 lines

Hi Adrian,

Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Please explain what you mean by "comfortable".

Or rather: What about it is wrong for core code?

Quoted 7 lines
Quoted 4 lines

Well, there's nothing "wrong" about it. :)

But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.

It is pretty self-contained in db-export.[ch] and the scripting engines.

So, what I meant was the patch below, on top of yours.

In some data structures where there were no provision for tool specific storage, I added an unnamed union with db_id and a void *priv pointer, where there were, be it a priv pointer, like in perf_evsel, I moved db_id to it, no extra costs for the other tools.

And in struct symbol, I made it use symbol__priv(), that will access the area reserved by setting symbol.priv_size at tool start time, like done for the annotation case in top, report, annotate. I.e. on the tool that will use symbol->db_id you will need to add this just before calling symbol__init():

	symbol_conf.priv_size = sizeof(u64);

Since this is all specific to your tool, there should be no problems and I'm now applying this change merged with your original patch + note about that and then moving on to the other patches that make use of it.

- Arnaldo

#33 lore raw

Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way

Em Fri, Oct 24, 2014 at 11:40:02AM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 35 lines

Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:

Quoted 13 lines · Namhyung Kim
Quoted 12 lines

Hi Adrian,

Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim

Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export

Please explain what you mean by "comfortable".

Or rather: What about it is wrong for core code?

Quoted 7 lines
Quoted 4 lines

Well, there's nothing "wrong" about it. :)

But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.

It is pretty self-contained in db-export.[ch] and the scripting engines.

So, what I meant was the patch below, on top of yours.

In some data structures where there were no provision for tool specific storage, I added an unnamed union with db_id and a void *priv pointer, where there were, be it a priv pointer, like in perf_evsel, I moved db_id to it, no extra costs for the other tools.

And in struct symbol, I made it use symbol__priv(), that will access the area reserved by setting symbol.priv_size at tool start time, like done for the annotation case in top, report, annotate. I.e. on the tool that will use symbol->db_id you will need to add this just before calling symbol__init():

Quoted 5 lines
	symbol_conf.priv_size = sizeof(u64);

Since this is all specific to your tool, there should be no problems and I'm now applying this change merged with your original patch + note about that and then moving on to the other patches that make use of it.

Oops, now with the patch:

tools/perf/util/comm.h +4 -1
index 99e7021..71c9c39 100644
@@ -10,9 +10,12 @@ struct comm_str;
 struct comm {
 	struct comm_str *comm_str;
 	u64 start;
-	u64 db_id;
 	struct list_head list;
 	bool exec;
+	union { /* Tool specific area */
+		void	*priv;
+		u64	db_id;
+	};
 };
 
 void comm__free(struct comm *comm);
tools/perf/util/db-export.c +9 -7
index 53d0e75..be128b0 100644
@@ -150,10 +150,12 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,
 int db_export__symbol(struct db_export *dbe, struct symbol *sym,
 		      struct dso *dso)
 {
-	if (sym->db_id)
+	u64 *sym_db_id = symbol__priv(sym);
+
+	if (*sym_db_id)
 		return 0;
 
-	sym->db_id = ++dbe->symbol_last_db_id;
+	*sym_db_id = ++dbe->symbol_last_db_id;
 
 	if (dbe->export_symbol)
 		return dbe->export_symbol(dbe, sym, dso);
@@ -161,8 +163,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
 	return 0;
 }
 
-static struct thread *get_main_thread(struct machine *machine,
-				      struct thread *thread)
+static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
 {
 	if (thread->pid_ == thread->tid)
 		return thread;
@@ -194,10 +195,12 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
 		}
 
 		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 = al->sym->db_id;
+			*sym_db_id = *db_id;
 			*offset = al->addr - al->sym->start;
 		}
 	}
@@ -253,8 +256,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 	    sample_addr_correlates_sym(&evsel->attr)) {
 		struct addr_location addr_al;
 
-		perf_event__preprocess_sample_addr(event, sample, al->machine,
-						   thread, &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)
tools/perf/util/dso.h +5 -1
index 66c99cc..a316e4a 100644
@@ -111,7 +111,6 @@ struct dso {
 	enum dso_swap_type	needs_swap;
 	enum dso_binary_type	symtab_type;
 	enum dso_binary_type	binary_type;
-	u64		 db_id;
 	u8		 adjust_symbols:1;
 	u8		 has_build_id:1;
 	u8		 has_srcline:1;
@@ -140,6 +139,11 @@ struct dso {
 		struct list_head open_entry;
 	} data;
 
+	union { /* Tool specific area */
+		void	 *priv;
+		u64	 db_id;
+	};
+
 	char		 name[0];
 };
 
tools/perf/util/evsel.h +2 -1
index 9459899..d3854c4 100644
@@ -54,6 +54,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 +74,7 @@ struct perf_evsel {
 	union {
 		void		*priv;
 		off_t		id_offset;
+		u64		db_id;
 	};
 	struct cgroup_sel	*cgrp;
 	void			*handler;
@@ -92,7 +94,6 @@ struct perf_evsel {
 	int			sample_read;
 	struct perf_evsel	*leader;
 	char			*group_name;
-	u64			db_id;
 };
 
 union u64_swap {
tools/perf/util/machine.h +4 -1
index 2e150a2..e8b7779 100644
@@ -40,7 +40,10 @@ struct machine {
 	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
-	u64		  db_id;
+	union { /* Tool specific area */
+		void	  *priv;
+		u64	  db_id;
+	};
 };
 
 static inline
tools/perf/util/symbol.h +0 -1
index 6f54ade..eb2c19b 100644
@@ -73,7 +73,6 @@ struct symbol {
 	struct rb_node	rb_node;
 	u64		start;
 	u64		end;
-	u64		db_id;
 	u16		namelen;
 	u8		binding;
 	bool		ignore;
#34 lore raw

[tip:perf/core] perf tools: Add facility to export data in database-friendly way

In reply to Adrian Hunter
perf tools: Add facility to export data in database-friendly way
Commit-ID:  0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92
Gitweb:     http://git.kernel.org/tip/0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:13 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:49 -0200

perf tools: Add facility to export data in database-friendly way

This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.

The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.

Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.

Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
[ committer note: Stash db_id using symbol_conf.priv_size + symbol__priv() and foo->priv areas ]
---
 tools/perf/Makefile.perf    |   2 +
 tools/perf/util/comm.h      |   4 +
 tools/perf/util/db-export.c | 270 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/db-export.h |  86 ++++++++++++++
 tools/perf/util/dso.h       |   5 +
 tools/perf/util/evsel.h     |   2 +
 tools/perf/util/machine.h   |   4 +
 tools/perf/util/thread.h    |   1 +
 8 files changed, 374 insertions(+)
tools/perf/Makefile.perf +2 -0
index 9c4ced0..3caf7da 100644
@@ -258,6 +258,7 @@ LIB_H += util/annotate.h
 LIB_H += util/cache.h
 LIB_H += util/callchain.h
 LIB_H += util/build-id.h
+LIB_H += util/db-export.h
 LIB_H += util/debug.h
 LIB_H += util/pmu.h
 LIB_H += util/event.h
@@ -323,6 +324,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o
 LIB_OBJS += $(OUTPUT)util/build-id.o
 LIB_OBJS += $(OUTPUT)util/config.o
 LIB_OBJS += $(OUTPUT)util/ctype.o
+LIB_OBJS += $(OUTPUT)util/db-export.o
 LIB_OBJS += $(OUTPUT)util/pmu.o
 LIB_OBJS += $(OUTPUT)util/environment.o
 LIB_OBJS += $(OUTPUT)util/event.o
tools/perf/util/comm.h +4 -0
index 51c10ab..71c9c39 100644
@@ -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);
tools/perf/util/db-export.c new file +270 -0

270 lines not on this page · raw message

tools/perf/util/db-export.h new file +86 -0

86 lines not on this page · raw message

tools/perf/util/dso.h +5 -0
index 3c9b391..a316e4a 100644
@@ -139,6 +139,11 @@ struct dso {
 		struct list_head open_entry;
 	} data;
 
+	union { /* Tool specific area */
+		void	 *priv;
+		u64	 db_id;
+	};
+
 	char		 name[0];
 };
 
tools/perf/util/evsel.h +2 -0
index 163c560..d3854c4 100644
@@ -54,6 +54,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 +74,7 @@ struct perf_evsel {
 	union {
 		void		*priv;
 		off_t		id_offset;
+		u64		db_id;
 	};
 	struct cgroup_sel	*cgrp;
 	void			*handler;
tools/perf/util/machine.h +4 -0
index 88ec74e..e8b7779 100644
@@ -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
tools/perf/util/thread.h +1 -0
index 6ef9fe6..d34cf5c 100644
@@ -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;
 };
author reviewer
#35 lore raw

PATCH 6/16 perf tools: Extend Python script interface to export data in a database-friendly way

In reply to Adrian Hunter

Use the new db_export facility to export data in a database-friendly way.

A Python script selects the db_export mode by setting a global variable 'perf_db_export_mode' to True. The script then optionally implements functions to receive table rows. The functions are:

evsel_table
machine_table
thread_table
comm_table
dso_table
symbol_table
sample_table

An example script is provided in a subsequent patch.

---
 .../util/scripting-engines/trace-event-python.c    | 281 ++++++++++++++++++++-
 1 file changed, 279 insertions(+), 2 deletions(-)
tools/perf/util/scripting-engines/trace-event-python.c +279 -2
index 496f21c..539e23d 100644
@@ -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)
 {
@@ -475,6 +494,210 @@ 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);
+	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 +774,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 +818,53 @@ 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;
+
+	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 +903,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 +923,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();
Signature · 1 line
1.9.1
#36 lore raw

[tip:perf/core] perf scripting python: Extend interface to export data in a database-friendly way

perf scripting python: Extend interface to export data in a database-friendly way
Commit-ID:  df919b400ad3f9e6aac392ce421d710207abf9be
Gitweb:     http://git.kernel.org/tip/df919b400ad3f9e6aac392ce421d710207abf9be
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:14 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:49 -0200

perf scripting python: Extend interface to export data in a database-friendly way

Use the new db_export facility to export data in a database-friendly way.

A Python script selects the db_export mode by setting a global variable 'perf_db_export_mode' to True. The script then optionally implements functions to receive table rows. The functions are:

evsel_table
machine_table
thread_table
comm_table
dso_table
symbol_table
sample_table

An example script is provided in a subsequent patch.

Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
[ Reserve space for per symbol db_id space when perf_db_export_mode is on ]
---
 .../util/scripting-engines/trace-event-python.c    | 286 ++++++++++++++++++++-
 1 file changed, 284 insertions(+), 2 deletions(-)
tools/perf/util/scripting-engines/trace-event-python.c +284 -2
index 25e5a23..2fd7ee8 100644
@@ -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)
 {
@@ -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();
author reviewer
#37 lore raw

PATCH 7/16 perf tools: Add Python script to export to postgresql

In reply to Adrian Hunter

Add a Python script to export to a postgresql database. The script requires the Python psycopg2 module. The caller of the script must be able to create postgresql databases.

The script takes the database name as a parameter. The database and database tables are created. Data is written to flat files which are then imported using SQL COPY FROM.

Example:

$ perf record ls
...
$ perf script report export-to-postgresql lsdb
2014-02-14 10:55:38.631431 Creating database...
2014-02-14 10:55:39.291958 Writing to intermediate files...
2014-02-14 10:55:39.350280 Copying to database...
2014-02-14 10:55:39.358536 Removing intermediate files...
2014-02-14 10:55:39.358665 Adding primary keys
2014-02-14 10:55:39.658697 Adding foreign keys
2014-02-14 10:55:39.667412 Done
$ psql lsdb
lsdb=# select * from samples_view;

id | time | cpu | pid | tid | command | event | ip_hex | symbol | sym_offset | dso_short_name | to_ip_hex | to_symbol | to_sym_offset | to_dso_short_name
----+-----------------+-----+-------+-------+---------+--------+------------------+------------------------------+------------+-------------------+-----------+-----------+---------------+-------------------

  1 | 592112202825015 |  -1 | 17339 | 17339 | :17339  | cycles | ffffffff8104d24a | native_write_msr_safe        |         10 | [kernel.kallsyms] | 0         | unknown   |             0 | unknown
  2 | 592112203258804 |  -1 | 17339 | 17339 | :17339  | cycles | ffffffff8104d24a | native_write_msr_safe        |         10 | [kernel.kallsyms] | 0         | unknown   |             0 | unknown
  3 | 592112203988119 |  -1 | 17339 | 17339 | :17339  | cycles | ffffffff8104d24a | native_write_msr_safe        |         10 | [kernel.kallsyms] | 0         | unknown   |             0 | unknown
---
 .../scripts/python/bin/export-to-postgresql-record |   8 +
 .../scripts/python/bin/export-to-postgresql-report |  24 ++
 tools/perf/scripts/python/export-to-postgresql.py  | 360 +++++++++++++++++++++
 3 files changed, 392 insertions(+)
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report
 create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
tools/perf/scripts/python/bin/export-to-postgresql-record new file +8 -0
new file mode 100644
index 0000000..221d66e
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#
+# export perf data to a postgresql database. Can cover
+# perf ip samples (excluding the tracepoints). No special
+# record requirements, just record what you want to export.
+#
+perf record $@
tools/perf/scripts/python/bin/export-to-postgresql-report new file +24 -0
new file mode 100644
index 0000000..a8fdd15
@@ -0,0 +1,24 @@
+#!/bin/bash
+# description: export perf data to a postgresql database
+# args: [database name] [columns]
+n_args=0
+for i in "$@"
+do
+    if expr match "$i" "-" > /dev/null ; then
+	break
+    fi
+    n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 2 ] ; then
+    echo "usage: export-to-postgresql-report [database name] [columns]"
+    exit
+fi
+if [ "$n_args" -gt 1 ] ; then
+    dbname=$1
+    columns=$2
+    shift 2
+elif [ "$n_args" -gt 0 ] ; then
+    dbname=$1
+    shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
tools/perf/scripts/python/export-to-postgresql.py new file +360 -0

360 lines not on this page · raw message

Signature · 1 line
1.9.1
#38 lore raw

[tip:perf/core] perf script: Add Python script to export to postgresql

perf script: Add Python script to export to postgresql
Commit-ID:  2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b
Gitweb:     http://git.kernel.org/tip/2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:15 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:49 -0200

perf script: Add Python script to export to postgresql

Add a Python script to export to a postgresql database.

The script requires the Python pyside module and the Qt PostgreSQL driver. The packages needed are probably named "python-pyside" and "libqt4-sql-psql"

The caller of the script must be able to create postgresql databases.

The script takes the database name as a parameter. The database and database tables are created. Data is written to flat files which are then imported using SQL COPY FROM.

Example:

  $ perf record ls
  ...
  $ perf script report export-to-postgresql lsdb
  2014-02-14 10:55:38.631431 Creating database...
  2014-02-14 10:55:39.291958 Writing to intermediate files...
  2014-02-14 10:55:39.350280 Copying to database...
  2014-02-14 10:55:39.358536 Removing intermediate files...
  2014-02-14 10:55:39.358665 Adding primary keys
  2014-02-14 10:55:39.658697 Adding foreign keys
  2014-02-14 10:55:39.667412 Done
  $ psql lsdb
  lsdb-# \d
              List of relations
   Schema |      Name       | Type  | Owner
  --------+-----------------+-------+-------
   public | comm_threads    | table | acme
   public | comms           | table | acme
   public | dsos            | table | acme
   public | machines        | table | acme
   public | samples         | table | acme
   public | samples_view    | view  | acme
   public | selected_events | table | acme
   public | symbols         | table | acme
   public | threads         | table | acme
  (9 rows)
  lsdb-# \d samples
         Table "public.samples"
      Column     |  Type   | Modifiers
  ---------------+---------+-----------
   id            | bigint  | not null
   evsel_id      | bigint  |
   machine_id    | bigint  |
   thread_id     | bigint  |
   comm_id       | bigint  |
   dso_id        | bigint  |
   symbol_id     | bigint  |
   sym_offset    | bigint  |
   ip            | bigint  |
   time          | bigint  |
   cpu           | integer |
   to_dso_id     | bigint  |
   to_symbol_id  | bigint  |
   to_sym_offset | bigint  |
   to_ip         | bigint  |
   period        | bigint  |
   weight        | bigint  |
   transaction   | bigint  |
   data_src      | bigint  |
  Indexes:
      "samples_pkey" PRIMARY KEY, btree (id)
  Foreign-key constraints:
      "commfk" FOREIGN KEY (comm_id) REFERENCES comms(id)
      "dsofk" FOREIGN KEY (dso_id) REFERENCES dsos(id)
      "evselfk" FOREIGN KEY (evsel_id) REFERENCES selected_events(id)
      "machinefk" FOREIGN KEY (machine_id) REFERENCES machines(id)
      "symbolfk" FOREIGN KEY (symbol_id) REFERENCES symbols(id)
      "threadfk" FOREIGN KEY (thread_id) REFERENCES threads(id)
      "todsofk" FOREIGN KEY (to_dso_id) REFERENCES dsos(id)
      "tosymbolfk" FOREIGN KEY (to_symbol_id) REFERENCES symbols(id)

lsdb-# \d samples_view
View "public.samples_view"
Column | Type | Modifiers
-------------------+-------------------------+-----------

   id                | bigint                  |
   time              | bigint                  |
   cpu               | integer                 |
   pid               | integer                 |
   tid               | integer                 |
   command           | character varying(16)   |
   event             | character varying(80)   |
   ip_hex            | text                    |
   symbol            | character varying(2048) |
   sym_offset        | bigint                  |
   dso_short_name    | character varying(256)  |
   to_ip_hex         | text                    |
   to_symbol         | character varying(2048) |
   to_sym_offset     | bigint                  |
   to_dso_short_name | character varying(256)  |
    lsdb=# select * from samples_view;

id| time |cpu | pid | tid |command| event | ip_hex | symbol |sym_off| dso_name|to_ip_hex|to_symbol|to_sym_off|to_dso_name
--+------------+----+------+------+-------+--------+---------------+---------------------+-------+---------+---------+---------+----------+----------

   1 |12202825015 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe|    10 | [kernel]| 0       | unknown |         0| unknown
   2 |12203258804 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe|    10 | [kernel]| 0       | unknown |         0| unknown
   3 |12203988119 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe|    10 | [kernel]| 0       | unknown |         0| unknown

My notes (which may be out-of-date) on setting up postgresql so you can create databases:

fedora:

        $ sudo yum install postgresql postgresql-server python-pyside qt-postgresql
        $ sudo su - postgres -c initdb
        $ sudo service postgresql start
        $ sudo su - postgres
        $ createuser -s <your username>

I used the the unix user name in createuser.

If it fails, try createuser without -s and answer the following question to allow your user to create tables:

Shall the new role be a superuser? (y/n) y

ubuntu:

        $ sudo apt-get install postgresql
        $ sudo su - postgres
        $ createuser <your username>
        Shall the new role be a superuser? (y/n) y

You may want to disable automatic startup. One way is to edit /etc/postgresql/9.3/main/start.conf. Another is to disable the init script e.g. sudo update-rc.d postgresql disable

---
 .../scripts/python/bin/export-to-postgresql-record |   8 +
 .../scripts/python/bin/export-to-postgresql-report |  24 ++
 tools/perf/scripts/python/export-to-postgresql.py  | 360 +++++++++++++++++++++
 3 files changed, 392 insertions(+)
tools/perf/scripts/python/bin/export-to-postgresql-record new file +8 -0
new file mode 100644
index 0000000..221d66e
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#
+# export perf data to a postgresql database. Can cover
+# perf ip samples (excluding the tracepoints). No special
+# record requirements, just record what you want to export.
+#
+perf record $@
tools/perf/scripts/python/bin/export-to-postgresql-report new file +24 -0
new file mode 100644
index 0000000..a8fdd15
@@ -0,0 +1,24 @@
+#!/bin/bash
+# description: export perf data to a postgresql database
+# args: [database name] [columns]
+n_args=0
+for i in "$@"
+do
+    if expr match "$i" "-" > /dev/null ; then
+	break
+    fi
+    n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 2 ] ; then
+    echo "usage: export-to-postgresql-report [database name] [columns]"
+    exit
+fi
+if [ "$n_args" -gt 1 ] ; then
+    dbname=$1
+    columns=$2
+    shift 2
+elif [ "$n_args" -gt 0 ] ; then
+    dbname=$1
+    shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
tools/perf/scripts/python/export-to-postgresql.py new file +360 -0

360 lines not on this page · raw message

author reviewer
#39 lore raw

PATCH 8/16 perf tools: Add branch type to db export

In reply to Adrian Hunter

Add the ability to export branch types through the database export facility.

---
 tools/perf/util/db-export.c | 48 +++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/db-export.h |  6 ++++++
 2 files changed, 54 insertions(+)
tools/perf/util/db-export.c +48 -0
index 53d0e75..c5b6e02 100644
@@ -205,6 +205,15 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
 	return 0;
 }
 
+int db_export__branch_type(struct db_export *dbe, u32 branch_type,
+			   const char *name)
+{
+	if (dbe->export_branch_type)
+		return dbe->export_branch_type(dbe, branch_type, name);
+
+	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)
@@ -266,3 +275,42 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 
 	return 0;
 }
+
+static struct {
+	u32 branch_type;
+	const char *name;
+} branch_types[] = {
+	{0, "no branch"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_CALL, "call"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_RETURN, "return"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_CONDITIONAL, "conditional jump"},
+	{PERF_FLAG_BRANCH, "unconditional jump"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_INTERRUPT,
+	 "software interrupt"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_RETURN | PERF_FLAG_INTERRUPT,
+	 "return from interrupt"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_SYSCALLRET,
+	 "system call"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_RETURN | PERF_FLAG_SYSCALLRET,
+	 "return from system call"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_ASYNC, "asynchronous branch"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_ASYNC |
+	 PERF_FLAG_INTERRUPT, "hardware interrupt"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_TX_ABORT, "transaction abort"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_TRACE_BEGIN, "trace begin"},
+	{PERF_FLAG_BRANCH | PERF_FLAG_TRACE_END, "trace end"},
+	{0, NULL}
+};
+
+int db_export__branch_types(struct db_export *dbe)
+{
+	int i, err = 0;
+
+	for (i = 0; branch_types[i].name ; i++) {
+		err = db_export__branch_type(dbe, branch_types[i].branch_type,
+					     branch_types[i].name);
+		if (err)
+			break;
+	}
+	return err;
+}
tools/perf/util/db-export.h +6 -0
index b3643e8..e4baa45 100644
@@ -54,6 +54,8 @@ struct db_export {
 			  struct machine *machine);
 	int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
 			     struct dso *dso);
+	int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
+				  const char *name);
 	int (*export_sample)(struct db_export *dbe, struct export_sample *es);
 	u64 evsel_last_db_id;
 	u64 machine_last_db_id;
@@ -79,8 +81,12 @@ 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__branch_type(struct db_export *dbe, u32 branch_type,
+			   const char *name);
 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);
 
+int db_export__branch_types(struct db_export *dbe);
+
 #endif
Signature · 1 line
1.9.1
author reviewer
#40 lore raw

PATCH 9/16 perf tools: Add branch_type and in_tx to Python export

In reply to Adrian Hunter

Add branch_type and in_tx to Python db export and
the export-to-postgresql.py script.

---
 tools/perf/scripts/python/export-to-postgresql.py  | 32 ++++++++++++++++++----
 .../util/scripting-engines/trace-event-python.c    | 30 +++++++++++++++++++-
 2 files changed, 55 insertions(+), 7 deletions(-)
tools/perf/scripts/python/export-to-postgresql.py +26 -6
index d8f6df0..bb79aec 100644
@@ -123,6 +123,10 @@ do_query(query, 'CREATE TABLE symbols ('
 		'sym_end	bigint,'
 		'binding	integer,'
 		'name		varchar(2048))')
+do_query(query, 'CREATE TABLE branch_types ('
+		'id		integer		NOT NULL,'
+		'name		varchar(80))')
+
 if branches:
 	do_query(query, 'CREATE TABLE samples ('
 		'id		bigint		NOT NULL,'
@@ -139,7 +143,9 @@ if branches:
 		'to_dso_id	bigint,'
 		'to_symbol_id	bigint,'
 		'to_sym_offset	bigint,'
-		'to_ip		bigint)')
+		'to_ip		bigint,'
+		'branch_type	integer,'
+		'in_tx		boolean)')
 else:
 	do_query(query, 'CREATE TABLE samples ('
 		'id		bigint		NOT NULL,'
@@ -160,7 +166,9 @@ else:
 		'period		bigint,'
 		'weight		bigint,'
 		'transaction	bigint,'
-		'data_src	bigint)')
+		'data_src	bigint,'
+		'branch_type	integer,'
+		'in_tx		boolean)')
 
 do_query(query, 'CREATE VIEW samples_view AS '
 	'SELECT '
@@ -178,7 +186,9 @@ do_query(query, 'CREATE VIEW samples_view AS '
 		'to_hex(to_ip) AS to_ip_hex,'
 		'(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
 		'to_sym_offset,'
-		'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name'
+		'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
+		'(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
+		'in_tx'
 	' FROM samples')
 
 
@@ -234,6 +244,7 @@ comm_file		= open_output_file("comm_table.bin")
 comm_thread_file	= open_output_file("comm_thread_table.bin")
 dso_file		= open_output_file("dso_table.bin")
 symbol_file		= open_output_file("symbol_table.bin")
+branch_type_file	= open_output_file("branch_type_table.bin")
 sample_file		= open_output_file("sample_table.bin")
 
 def trace_begin():
@@ -257,6 +268,7 @@ def trace_end():
 	copy_output_file(comm_thread_file,	"comm_threads")
 	copy_output_file(dso_file,		"dsos")
 	copy_output_file(symbol_file,		"symbols")
+	copy_output_file(branch_type_file,	"branch_types")
 	copy_output_file(sample_file,		"samples")
 
 	print datetime.datetime.today(), "Removing intermediate files..."
@@ -267,6 +279,7 @@ def trace_end():
 	remove_output_file(comm_thread_file)
 	remove_output_file(dso_file)
 	remove_output_file(symbol_file)
+	remove_output_file(branch_type_file)
 	remove_output_file(sample_file)
 	os.rmdir(output_dir_name)
 	print datetime.datetime.today(), "Adding primary keys"
@@ -277,6 +290,7 @@ def trace_end():
 	do_query(query, 'ALTER TABLE comm_threads    ADD PRIMARY KEY (id)')
 	do_query(query, 'ALTER TABLE dsos            ADD PRIMARY KEY (id)')
 	do_query(query, 'ALTER TABLE symbols         ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE branch_types    ADD PRIMARY KEY (id)')
 	do_query(query, 'ALTER TABLE samples         ADD PRIMARY KEY (id)')
 
 	print datetime.datetime.today(), "Adding foreign keys"
@@ -352,9 +366,15 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x
 	value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
 	symbol_file.write(value)
 
-def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x):
+def branch_type_table(branch_type, name, *x):
+	n = len(name)
+	fmt = "!hiii" + str(n) + "s"
+	value = struct.pack(fmt, 2, 4, branch_type, n, name)
+	branch_type_file.write(value)
+
+def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x):
 	if branches:
-		value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip)
+		value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx)
 	else:
-		value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src)
+		value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
 	sample_file.write(value)
tools/perf/util/scripting-engines/trace-event-python.c +29 -1
index 539e23d..553aded 100644
@@ -66,6 +66,7 @@ struct tables {
 	PyObject		*comm_thread_handler;
 	PyObject		*dso_handler;
 	PyObject		*symbol_handler;
+	PyObject		*branch_type_handler;
 	PyObject		*sample_handler;
 	bool			db_export_mode;
 };
@@ -663,13 +664,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
 	return 0;
 }
 
+static int python_export_branch_type(struct db_export *dbe, u32 branch_type,
+				     const char *name)
+{
+	struct tables *tables = container_of(dbe, struct tables, dbe);
+	PyObject *t;
+
+	t = tuple_new(2);
+
+	tuple_set_s32(t, 0, branch_type);
+	tuple_set_string(t, 1, name);
+
+	call_object(tables->branch_type_handler, t, "branch_type_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);
+	t = tuple_new(21);
 
 	tuple_set_u64(t, 0, es->db_id);
 	tuple_set_u64(t, 1, es->evsel->db_id);
@@ -690,6 +709,8 @@ static int python_export_sample(struct db_export *dbe,
 	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);
+	tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);
+	tuple_set_s32(t, 20, !!(es->sample->flags & PERF_FLAG_IN_TX));
 
 	call_object(tables->sample_handler, t, "sample_table");
 
@@ -856,6 +877,7 @@ static void set_table_handlers(struct tables *tables)
 	SET_TABLE_HANDLER(comm_thread);
 	SET_TABLE_HANDLER(dso);
 	SET_TABLE_HANDLER(symbol);
+	SET_TABLE_HANDLER(branch_type);
 	SET_TABLE_HANDLER(sample);
 }
 
@@ -905,6 +927,12 @@ static int python_start_script(const char *script, int argc, const char **argv)
 
 	set_table_handlers(tables);
 
+	if (tables->db_export_mode) {
+		err = db_export__branch_types(&tables->dbe);
+		if (err)
+			goto error;
+	}
+
 	return err;
 error:
 	Py_Finalize();
Signature · 1 line
1.9.1
author reviewer
#41 lore raw

PATCH 10/16 perf tools: Enhance the thread stack to output call/return data

In reply to Adrian Hunter

Enhance the thread stack to output detailed information about paired calls and returns.

---
 tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/thread-stack.h |  47 ++++
 2 files changed, 590 insertions(+), 4 deletions(-)
tools/perf/util/thread-stack.c +543 -4
index c1ca2a9..444d132 100644
@@ -13,15 +13,48 @@
  *
  */
 
+#include <linux/rbtree.h>
+#include <linux/list.h>
 #include "thread.h"
 #include "event.h"
+#include "machine.h"
 #include "util.h"
+#include "debug.h"
+#include "symbol.h"
+#include "comm.h"
 #include "thread-stack.h"
 
-#define STACK_GROWTH 4096
+#define CALL_PATH_BLOCK_SHIFT 8
+#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
+#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
+
+struct call_path_block {
+	struct call_path cp[CALL_PATH_BLOCK_SIZE];
+	struct list_head node;
+};
+
+struct call_path_root {
+	struct call_path call_path;
+	struct list_head blocks;
+	size_t next;
+	size_t sz;
+};
+
+struct call_return_processor {
+	struct call_path_root *cpr;
+	int (*process)(struct call_return *cr, void *data);
+	void *data;
+};
+
+#define STACK_GROWTH 2048
 
 struct thread_stack_entry {
 	u64 ret_addr;
+	u64 timestamp;
+	u64 ref;
+	u64 branch_count;
+	struct call_path *cp;
+	bool no_call;
 };
 
 struct thread_stack {
@@ -29,6 +62,11 @@ struct thread_stack {
 	size_t cnt;
 	size_t sz;
 	u64 trace_nr;
+	u64 branch_count;
+	u64 kernel_start;
+	u64 last_time;
+	struct call_return_processor *crp;
+	struct comm *comm;
 };
 
 static void thread_stack__grow(struct thread_stack *ts)
@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts)
 	}
 }
 
-static struct thread_stack *thread_stack__new(void)
+static struct thread_stack *thread_stack__new(struct thread *thread,
+					      struct call_return_processor *crp)
 {
 	struct thread_stack *ts;
 
@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)
 		return NULL;
 	}
 
+	if (thread->mg && thread->mg->machine)
+		ts->kernel_start = machine__kernel_start(thread->mg->machine);
+	else
+		ts->kernel_start = 1ULL << 63;
+	ts->crp = crp;
+
 	return ts;
 }
 
@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
 	}
 }
 
+static bool thread_stack__in_kernel(struct thread_stack *ts)
+{
+	if (!ts->cnt)
+		return false;
+
+	return ts->stack[ts->cnt - 1].cp->in_kernel;
+}
+
+static int thread_stack__call_return(struct thread *thread,
+				     struct thread_stack *ts, size_t idx,
+				     u64 timestamp, u64 ref, bool no_return)
+{
+	struct call_return_processor *crp = ts->crp;
+	struct thread_stack_entry *tse;
+	struct call_return cr = {
+		.thread = thread,
+		.comm = ts->comm,
+		.db_id = 0,
+	};
+
+	tse = &ts->stack[idx];
+	cr.cp = tse->cp;
+	cr.call_time = tse->timestamp;
+	cr.return_time = timestamp;
+	cr.branch_count = ts->branch_count - tse->branch_count;
+	cr.call_ref = tse->ref;
+	cr.return_ref = ref;
+	if (tse->no_call)
+		cr.flags |= CALL_RETURN_NO_CALL;
+	if (no_return)
+		cr.flags |= CALL_RETURN_NO_RETURN;
+
+	return crp->process(&cr, crp->data);
+}
+
+static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+{
+	struct call_return_processor *crp = ts->crp;
+	int err;
+
+	if (!crp) {
+		ts->cnt = 0;
+		return 0;
+	}
+
+	while (ts->cnt) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						ts->last_time, 0, true);
+		if (err) {
+			pr_err("Error flushing thread stack!\n");
+			ts->cnt = 0;
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 			 u64 to_ip, u16 insn_len, u64 trace_nr)
 {
@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 		return;
 
 	if (!thread->ts) {
-		thread->ts = thread_stack__new();
+		thread->ts = thread_stack__new(thread, NULL);
 		if (!thread->ts)
 			return;
 		thread->ts->trace_nr = trace_nr;
 	}
 
 	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
 		thread->ts->trace_nr = trace_nr;
-		thread->ts->cnt = 0;
 	}
 
+	/* Stop here if thread_stack__process() is in use */
+	if (thread->ts->crp)
+		return;
+
 	if (flags & PERF_FLAG_CALL) {
 		u64 ret_addr;
 
@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 	}
 }
 
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
+{
+	if (!thread || !thread->ts)
+		return;
+
+	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
+		thread->ts->trace_nr = trace_nr;
+	}
+}
+
 void thread_stack__free(struct thread *thread)
 {
 	if (thread->ts) {
+		thread_stack__flush(thread, thread->ts);
 		zfree(&thread->ts->stack);
 		zfree(&thread->ts);
 	}
@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
 	for (i = 1; i < chain->nr; i++)
 		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
 }
+
+static void call_path__init(struct call_path *cp, struct call_path *parent,
+			    struct symbol *sym, u64 ip, bool in_kernel)
+{
+	cp->parent = parent;
+	cp->sym = sym;
+	cp->ip = sym ? 0 : ip;
+	cp->db_id = 0;
+	cp->in_kernel = in_kernel;
+	RB_CLEAR_NODE(&cp->rb_node);
+	cp->children = RB_ROOT;
+}
+
+static struct call_path_root *call_path_root__new(void)
+{
+	struct call_path_root *cpr;
+
+	cpr = zalloc(sizeof(struct call_path_root));
+	if (!cpr)
+		return NULL;
+	call_path__init(&cpr->call_path, NULL, NULL, 0, false);
+	INIT_LIST_HEAD(&cpr->blocks);
+	return cpr;
+}
+
+static void call_path_root__free(struct call_path_root *cpr)
+{
+	struct call_path_block *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+		list_del(&pos->node);
+		free(pos);
+	}
+	free(cpr);
+}
+
+static struct call_path *call_path__new(struct call_path_root *cpr,
+					struct call_path *parent,
+					struct symbol *sym, u64 ip,
+					bool in_kernel)
+{
+	struct call_path_block *cpb;
+	struct call_path *cp;
+	size_t n;
+
+	if (cpr->next < cpr->sz) {
+		cpb = list_last_entry(&cpr->blocks, struct call_path_block,
+				      node);
+	} else {
+		cpb = zalloc(sizeof(struct call_path_block));
+		if (!cpb)
+			return NULL;
+		list_add_tail(&cpb->node, &cpr->blocks);
+		cpr->sz += CALL_PATH_BLOCK_SIZE;
+	}
+
+	n = cpr->next++ & CALL_PATH_BLOCK_MASK;
+	cp = &cpb->cp[n];
+
+	call_path__init(cp, parent, sym, ip, in_kernel);
+
+	return cp;
+}
+
+static struct call_path *call_path__findnew(struct call_path_root *cpr,
+					    struct call_path *parent,
+					    struct symbol *sym, u64 ip, u64 ks)
+{
+	struct rb_node **p;
+	struct rb_node *node_parent = NULL;
+	struct call_path *cp;
+	bool in_kernel = ip >= ks;
+
+	if (sym)
+		ip = 0;
+
+	if (!parent)
+		return call_path__new(cpr, parent, sym, ip, in_kernel);
+
+	p = &parent->children.rb_node;
+	while (*p != NULL) {
+		node_parent = *p;
+		cp = rb_entry(node_parent, struct call_path, rb_node);
+
+		if (cp->sym == sym && cp->ip == ip)
+			return cp;
+
+		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+
+	cp = call_path__new(cpr, parent, sym, ip, in_kernel);
+	if (!cp)
+		return NULL;
+
+	rb_link_node(&cp->rb_node, node_parent, p);
+	rb_insert_color(&cp->rb_node, &parent->children);
+
+	return cp;
+}
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+			   void *data)
+{
+	struct call_return_processor *crp;
+
+	crp = zalloc(sizeof(struct call_return_processor));
+	if (!crp)
+		return NULL;
+	crp->cpr = call_path_root__new();
+	if (!crp->cpr)
+		goto out_free;
+	crp->process = process;
+	crp->data = data;
+	return crp;
+
+out_free:
+	free(crp);
+	return NULL;
+}
+
+void call_return_processor__free(struct call_return_processor *crp)
+{
+	if (crp) {
+		call_path_root__free(crp->cpr);
+		free(crp);
+	}
+}
+
+static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
+				 u64 timestamp, u64 ref, struct call_path *cp,
+				 bool no_call)
+{
+	struct thread_stack_entry *tse;
+
+	if (ts->cnt == ts->sz) {
+		thread_stack__grow(ts);
+		if (ts->cnt == ts->sz)
+			return -ENOMEM;
+	}
+
+	tse = &ts->stack[ts->cnt++];
+	tse->ret_addr = ret_addr;
+	tse->timestamp = timestamp;
+	tse->ref = ref;
+	tse->branch_count = ts->branch_count;
+	tse->cp = cp;
+	tse->no_call = no_call;
+
+	return 0;
+}
+
+static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
+				u64 ret_addr, u64 timestamp, u64 ref,
+				struct symbol *sym)
+{
+	int err;
+
+	if (!ts->cnt)
+		return 1;
+
+	if (ts->cnt == 1) {
+		struct thread_stack_entry *tse = &ts->stack[0];
+
+		if (tse->cp->sym == sym)
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+	}
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		return thread_stack__call_return(thread, ts, --ts->cnt,
+						 timestamp, ref, false);
+	} else {
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr != ret_addr)
+				continue;
+			i += 1;
+			while (ts->cnt > i) {
+				err = thread_stack__call_return(thread, ts,
+								--ts->cnt,
+								timestamp, ref,
+								true);
+				if (err)
+					return err;
+			}
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+		}
+	}
+
+	return 1;
+}
+
+static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
+				struct perf_sample *sample,
+				struct addr_location *from_al,
+				struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	struct symbol *sym;
+	u64 ip;
+
+	if (sample->ip) {
+		ip = sample->ip;
+		sym = from_al->sym;
+	} else if (sample->addr) {
+		ip = sample->addr;
+		sym = to_al->sym;
+	} else {
+		return 0;
+	}
+
+	cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
+				     true);
+}
+
+static int thread_stack__no_call_return(struct thread *thread,
+					struct thread_stack *ts,
+					struct perf_sample *sample,
+					struct addr_location *from_al,
+					struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp, *parent;
+	u64 ks = ts->kernel_start;
+	int err;
+
+	if (sample->ip >= ks && sample->addr < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+
+		/* If the stack is empty, push the userspace address */
+		if (!ts->cnt) {
+			cp = call_path__findnew(cpr, &cpr->call_path,
+						to_al->sym, sample->addr,
+						ts->kernel_start);
+			if (!cp)
+				return -ENOMEM;
+			return thread_stack__push_cp(ts, 0, sample->time, ref,
+						     cp, true);
+		}
+	} else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+	}
+
+	if (ts->cnt)
+		parent = ts->stack[ts->cnt - 1].cp;
+	else
+		parent = &cpr->call_path;
+
+	/* This 'return' had no 'call', so push and pop top of stack */
+	cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
+				    true);
+	if (err)
+		return err;
+
+	return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
+				    to_al->sym);
+}
+
+static int thread_stack__trace_begin(struct thread *thread,
+				     struct thread_stack *ts, u64 timestamp,
+				     u64 ref)
+{
+	struct thread_stack_entry *tse;
+	int err;
+
+	if (!ts->cnt)
+		return 0;
+
+	/* Pop trace end */
+	tse = &ts->stack[ts->cnt - 1];
+	if (tse->cp->sym == NULL && tse->cp->ip == 0) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						timestamp, ref, false);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int thread_stack__trace_end(struct thread_stack *ts,
+				   struct perf_sample *sample, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	u64 ret_addr;
+
+	/* No point having 'trace end' on the bottom of the stack */
+	if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
+		return 0;
+
+	cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	ret_addr = sample->ip + sample->insn_len;
+
+	return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
+				     false);
+}
+
+int thread_stack__process(struct thread *thread, struct comm *comm,
+			  struct perf_sample *sample,
+			  struct addr_location *from_al,
+			  struct addr_location *to_al, u64 ref,
+			  struct call_return_processor *crp)
+{
+	struct thread_stack *ts = thread->ts;
+	int err = 0;
+
+	if (ts) {
+		if (!ts->crp) {
+			/* Supersede thread_stack__event() */
+			thread_stack__free(thread);
+			thread->ts = thread_stack__new(thread, crp);
+			if (!thread->ts)
+				return -ENOMEM;
+			ts = thread->ts;
+			ts->comm = comm;
+		}
+	} else {
+		thread->ts = thread_stack__new(thread, crp);
+		if (!thread->ts)
+			return -ENOMEM;
+		ts = thread->ts;
+		ts->comm = comm;
+	}
+
+	/* Flush stack on exec */
+	if (ts->comm != comm && thread->pid_ == thread->tid) {
+		err = thread_stack__flush(thread, ts);
+		if (err)
+			return err;
+		ts->comm = comm;
+	}
+
+	/* If the stack is empty, put the current symbol on the stack */
+	if (!ts->cnt) {
+		err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
+					   ref);
+		if (err)
+			return err;
+	}
+
+	ts->branch_count += 1;
+	ts->last_time = sample->time;
+
+	if (sample->flags & PERF_FLAG_CALL) {
+		struct call_path_root *cpr = ts->crp->cpr;
+		struct call_path *cp;
+		u64 ret_addr;
+
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		ret_addr = sample->ip + sample->insn_len;
+		if (ret_addr == sample->addr)
+			return 0; /* Zero-length calls are excluded */
+
+		cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
+					to_al->sym, sample->addr,
+					ts->kernel_start);
+		if (!cp)
+			return -ENOMEM;
+		err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
+					    cp, false);
+	} else if (sample->flags & PERF_FLAG_RETURN) {
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		err = thread_stack__pop_cp(thread, ts, sample->addr,
+					   sample->time, ref, from_al->sym);
+		if (err) {
+			if (err < 0)
+				return err;
+			err = thread_stack__no_call_return(thread, ts, sample,
+							   from_al, to_al, ref);
+		}
+	} else if (sample->flags & PERF_FLAG_TRACE_BEGIN) {
+		err = thread_stack__trace_begin(thread, ts, sample->time, ref);
+	} else if (sample->flags & PERF_FLAG_TRACE_END) {
+		err = thread_stack__trace_end(ts, sample, ref);
+	}
+
+	return err;
+}
tools/perf/util/thread-stack.h +47 -0

61 lines not on this page · raw message

Signature · 1 line
1.9.1
#42 lore raw

Re: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data

Quoted 2 lines · Adrian Hunter

Enhance the thread stack to output detailed information about paired calls and returns.

Any chance we could get more explanation for this one? IMHO it's pretty complex patch to have just 2 lines in changelog..

If you could describe the data structures and their purpose and the way they are/will be captured/hooked in perf.

thanks,
jirka

Quoted 700 lines
---
 tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/thread-stack.h |  47 ++++
 2 files changed, 590 insertions(+), 4 deletions(-)
tools/perf/util/thread-stack.c +543 -4
index c1ca2a9..444d132 100644
@@ -13,15 +13,48 @@
  *
  */
 
+#include <linux/rbtree.h>
+#include <linux/list.h>
 #include "thread.h"
 #include "event.h"
+#include "machine.h"
 #include "util.h"
+#include "debug.h"
+#include "symbol.h"
+#include "comm.h"
 #include "thread-stack.h"
 
-#define STACK_GROWTH 4096
+#define CALL_PATH_BLOCK_SHIFT 8
+#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
+#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
+
+struct call_path_block {
+	struct call_path cp[CALL_PATH_BLOCK_SIZE];
+	struct list_head node;
+};
+
+struct call_path_root {
+	struct call_path call_path;
+	struct list_head blocks;
+	size_t next;
+	size_t sz;
+};
+
+struct call_return_processor {
+	struct call_path_root *cpr;
+	int (*process)(struct call_return *cr, void *data);
+	void *data;
+};
+
+#define STACK_GROWTH 2048
 
 struct thread_stack_entry {
 	u64 ret_addr;
+	u64 timestamp;
+	u64 ref;
+	u64 branch_count;
+	struct call_path *cp;
+	bool no_call;
 };
 
 struct thread_stack {
@@ -29,6 +62,11 @@ struct thread_stack {
 	size_t cnt;
 	size_t sz;
 	u64 trace_nr;
+	u64 branch_count;
+	u64 kernel_start;
+	u64 last_time;
+	struct call_return_processor *crp;
+	struct comm *comm;
 };
 
 static void thread_stack__grow(struct thread_stack *ts)
@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts)
 	}
 }
 
-static struct thread_stack *thread_stack__new(void)
+static struct thread_stack *thread_stack__new(struct thread *thread,
+					      struct call_return_processor *crp)
 {
 	struct thread_stack *ts;
 
@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)
 		return NULL;
 	}
 
+	if (thread->mg && thread->mg->machine)
+		ts->kernel_start = machine__kernel_start(thread->mg->machine);
+	else
+		ts->kernel_start = 1ULL << 63;
+	ts->crp = crp;
+
 	return ts;
 }
 
@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
 	}
 }
 
+static bool thread_stack__in_kernel(struct thread_stack *ts)
+{
+	if (!ts->cnt)
+		return false;
+
+	return ts->stack[ts->cnt - 1].cp->in_kernel;
+}
+
+static int thread_stack__call_return(struct thread *thread,
+				     struct thread_stack *ts, size_t idx,
+				     u64 timestamp, u64 ref, bool no_return)
+{
+	struct call_return_processor *crp = ts->crp;
+	struct thread_stack_entry *tse;
+	struct call_return cr = {
+		.thread = thread,
+		.comm = ts->comm,
+		.db_id = 0,
+	};
+
+	tse = &ts->stack[idx];
+	cr.cp = tse->cp;
+	cr.call_time = tse->timestamp;
+	cr.return_time = timestamp;
+	cr.branch_count = ts->branch_count - tse->branch_count;
+	cr.call_ref = tse->ref;
+	cr.return_ref = ref;
+	if (tse->no_call)
+		cr.flags |= CALL_RETURN_NO_CALL;
+	if (no_return)
+		cr.flags |= CALL_RETURN_NO_RETURN;
+
+	return crp->process(&cr, crp->data);
+}
+
+static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+{
+	struct call_return_processor *crp = ts->crp;
+	int err;
+
+	if (!crp) {
+		ts->cnt = 0;
+		return 0;
+	}
+
+	while (ts->cnt) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						ts->last_time, 0, true);
+		if (err) {
+			pr_err("Error flushing thread stack!\n");
+			ts->cnt = 0;
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 			 u64 to_ip, u16 insn_len, u64 trace_nr)
 {
@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 		return;
 
 	if (!thread->ts) {
-		thread->ts = thread_stack__new();
+		thread->ts = thread_stack__new(thread, NULL);
 		if (!thread->ts)
 			return;
 		thread->ts->trace_nr = trace_nr;
 	}
 
 	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
 		thread->ts->trace_nr = trace_nr;
-		thread->ts->cnt = 0;
 	}
 
+	/* Stop here if thread_stack__process() is in use */
+	if (thread->ts->crp)
+		return;
+
 	if (flags & PERF_FLAG_CALL) {
 		u64 ret_addr;
 
@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 	}
 }
 
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
+{
+	if (!thread || !thread->ts)
+		return;
+
+	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
+		thread->ts->trace_nr = trace_nr;
+	}
+}
+
 void thread_stack__free(struct thread *thread)
 {
 	if (thread->ts) {
+		thread_stack__flush(thread, thread->ts);
 		zfree(&thread->ts->stack);
 		zfree(&thread->ts);
 	}
@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
 	for (i = 1; i < chain->nr; i++)
 		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
 }
+
+static void call_path__init(struct call_path *cp, struct call_path *parent,
+			    struct symbol *sym, u64 ip, bool in_kernel)
+{
+	cp->parent = parent;
+	cp->sym = sym;
+	cp->ip = sym ? 0 : ip;
+	cp->db_id = 0;
+	cp->in_kernel = in_kernel;
+	RB_CLEAR_NODE(&cp->rb_node);
+	cp->children = RB_ROOT;
+}
+
+static struct call_path_root *call_path_root__new(void)
+{
+	struct call_path_root *cpr;
+
+	cpr = zalloc(sizeof(struct call_path_root));
+	if (!cpr)
+		return NULL;
+	call_path__init(&cpr->call_path, NULL, NULL, 0, false);
+	INIT_LIST_HEAD(&cpr->blocks);
+	return cpr;
+}
+
+static void call_path_root__free(struct call_path_root *cpr)
+{
+	struct call_path_block *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+		list_del(&pos->node);
+		free(pos);
+	}
+	free(cpr);
+}
+
+static struct call_path *call_path__new(struct call_path_root *cpr,
+					struct call_path *parent,
+					struct symbol *sym, u64 ip,
+					bool in_kernel)
+{
+	struct call_path_block *cpb;
+	struct call_path *cp;
+	size_t n;
+
+	if (cpr->next < cpr->sz) {
+		cpb = list_last_entry(&cpr->blocks, struct call_path_block,
+				      node);
+	} else {
+		cpb = zalloc(sizeof(struct call_path_block));
+		if (!cpb)
+			return NULL;
+		list_add_tail(&cpb->node, &cpr->blocks);
+		cpr->sz += CALL_PATH_BLOCK_SIZE;
+	}
+
+	n = cpr->next++ & CALL_PATH_BLOCK_MASK;
+	cp = &cpb->cp[n];
+
+	call_path__init(cp, parent, sym, ip, in_kernel);
+
+	return cp;
+}
+
+static struct call_path *call_path__findnew(struct call_path_root *cpr,
+					    struct call_path *parent,
+					    struct symbol *sym, u64 ip, u64 ks)
+{
+	struct rb_node **p;
+	struct rb_node *node_parent = NULL;
+	struct call_path *cp;
+	bool in_kernel = ip >= ks;
+
+	if (sym)
+		ip = 0;
+
+	if (!parent)
+		return call_path__new(cpr, parent, sym, ip, in_kernel);
+
+	p = &parent->children.rb_node;
+	while (*p != NULL) {
+		node_parent = *p;
+		cp = rb_entry(node_parent, struct call_path, rb_node);
+
+		if (cp->sym == sym && cp->ip == ip)
+			return cp;
+
+		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+
+	cp = call_path__new(cpr, parent, sym, ip, in_kernel);
+	if (!cp)
+		return NULL;
+
+	rb_link_node(&cp->rb_node, node_parent, p);
+	rb_insert_color(&cp->rb_node, &parent->children);
+
+	return cp;
+}
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+			   void *data)
+{
+	struct call_return_processor *crp;
+
+	crp = zalloc(sizeof(struct call_return_processor));
+	if (!crp)
+		return NULL;
+	crp->cpr = call_path_root__new();
+	if (!crp->cpr)
+		goto out_free;
+	crp->process = process;
+	crp->data = data;
+	return crp;
+
+out_free:
+	free(crp);
+	return NULL;
+}
+
+void call_return_processor__free(struct call_return_processor *crp)
+{
+	if (crp) {
+		call_path_root__free(crp->cpr);
+		free(crp);
+	}
+}
+
+static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
+				 u64 timestamp, u64 ref, struct call_path *cp,
+				 bool no_call)
+{
+	struct thread_stack_entry *tse;
+
+	if (ts->cnt == ts->sz) {
+		thread_stack__grow(ts);
+		if (ts->cnt == ts->sz)
+			return -ENOMEM;
+	}
+
+	tse = &ts->stack[ts->cnt++];
+	tse->ret_addr = ret_addr;
+	tse->timestamp = timestamp;
+	tse->ref = ref;
+	tse->branch_count = ts->branch_count;
+	tse->cp = cp;
+	tse->no_call = no_call;
+
+	return 0;
+}
+
+static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
+				u64 ret_addr, u64 timestamp, u64 ref,
+				struct symbol *sym)
+{
+	int err;
+
+	if (!ts->cnt)
+		return 1;
+
+	if (ts->cnt == 1) {
+		struct thread_stack_entry *tse = &ts->stack[0];
+
+		if (tse->cp->sym == sym)
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+	}
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		return thread_stack__call_return(thread, ts, --ts->cnt,
+						 timestamp, ref, false);
+	} else {
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr != ret_addr)
+				continue;
+			i += 1;
+			while (ts->cnt > i) {
+				err = thread_stack__call_return(thread, ts,
+								--ts->cnt,
+								timestamp, ref,
+								true);
+				if (err)
+					return err;
+			}
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+		}
+	}
+
+	return 1;
+}
+
+static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
+				struct perf_sample *sample,
+				struct addr_location *from_al,
+				struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	struct symbol *sym;
+	u64 ip;
+
+	if (sample->ip) {
+		ip = sample->ip;
+		sym = from_al->sym;
+	} else if (sample->addr) {
+		ip = sample->addr;
+		sym = to_al->sym;
+	} else {
+		return 0;
+	}
+
+	cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
+				     true);
+}
+
+static int thread_stack__no_call_return(struct thread *thread,
+					struct thread_stack *ts,
+					struct perf_sample *sample,
+					struct addr_location *from_al,
+					struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp, *parent;
+	u64 ks = ts->kernel_start;
+	int err;
+
+	if (sample->ip >= ks && sample->addr < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+
+		/* If the stack is empty, push the userspace address */
+		if (!ts->cnt) {
+			cp = call_path__findnew(cpr, &cpr->call_path,
+						to_al->sym, sample->addr,
+						ts->kernel_start);
+			if (!cp)
+				return -ENOMEM;
+			return thread_stack__push_cp(ts, 0, sample->time, ref,
+						     cp, true);
+		}
+	} else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+	}
+
+	if (ts->cnt)
+		parent = ts->stack[ts->cnt - 1].cp;
+	else
+		parent = &cpr->call_path;
+
+	/* This 'return' had no 'call', so push and pop top of stack */
+	cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
+				    true);
+	if (err)
+		return err;
+
+	return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
+				    to_al->sym);
+}
+
+static int thread_stack__trace_begin(struct thread *thread,
+				     struct thread_stack *ts, u64 timestamp,
+				     u64 ref)
+{
+	struct thread_stack_entry *tse;
+	int err;
+
+	if (!ts->cnt)
+		return 0;
+
+	/* Pop trace end */
+	tse = &ts->stack[ts->cnt - 1];
+	if (tse->cp->sym == NULL && tse->cp->ip == 0) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						timestamp, ref, false);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int thread_stack__trace_end(struct thread_stack *ts,
+				   struct perf_sample *sample, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	u64 ret_addr;
+
+	/* No point having 'trace end' on the bottom of the stack */
+	if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
+		return 0;
+
+	cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	ret_addr = sample->ip + sample->insn_len;
+
+	return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
+				     false);
+}
+
+int thread_stack__process(struct thread *thread, struct comm *comm,
+			  struct perf_sample *sample,
+			  struct addr_location *from_al,
+			  struct addr_location *to_al, u64 ref,
+			  struct call_return_processor *crp)
+{
+	struct thread_stack *ts = thread->ts;
+	int err = 0;
+
+	if (ts) {
+		if (!ts->crp) {
+			/* Supersede thread_stack__event() */
+			thread_stack__free(thread);
+			thread->ts = thread_stack__new(thread, crp);
+			if (!thread->ts)
+				return -ENOMEM;
+			ts = thread->ts;
+			ts->comm = comm;
+		}
+	} else {
+		thread->ts = thread_stack__new(thread, crp);
+		if (!thread->ts)
+			return -ENOMEM;
+		ts = thread->ts;
+		ts->comm = comm;
+	}
+
+	/* Flush stack on exec */
+	if (ts->comm != comm && thread->pid_ == thread->tid) {
+		err = thread_stack__flush(thread, ts);
+		if (err)
+			return err;
+		ts->comm = comm;
+	}
+
+	/* If the stack is empty, put the current symbol on the stack */
+	if (!ts->cnt) {
+		err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
+					   ref);
+		if (err)
+			return err;
+	}
+
+	ts->branch_count += 1;
+	ts->last_time = sample->time;
+
+	if (sample->flags & PERF_FLAG_CALL) {
+		struct call_path_root *cpr = ts->crp->cpr;
+		struct call_path *cp;
+		u64 ret_addr;
+
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		ret_addr = sample->ip + sample->insn_len;
+		if (ret_addr == sample->addr)
+			return 0; /* Zero-length calls are excluded */
+
+		cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
+					to_al->sym, sample->addr,
+					ts->kernel_start);
+		if (!cp)
+			return -ENOMEM;
+		err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
+					    cp, false);
+	} else if (sample->flags & PERF_FLAG_RETURN) {
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		err = thread_stack__pop_cp(thread, ts, sample->addr,
+					   sample->time, ref, from_al->sym);
+		if (err) {
+			if (err < 0)
+				return err;
+			err = thread_stack__no_call_return(thread, ts, sample,
+							   from_al, to_al, ref);
+		}
+	} else if (sample->flags & PERF_FLAG_TRACE_BEGIN) {
+		err = thread_stack__trace_begin(thread, ts, sample->time, ref);
+	} else if (sample->flags & PERF_FLAG_TRACE_END) {
+		err = thread_stack__trace_end(ts, sample, ref);
+	}
+
+	return err;
+}
tools/perf/util/thread-stack.h +47 -0
index c0ba4cf..5bca14e 100644
@@ -19,14 +19,61 @@
 #include <sys/types.h>
 
 #include <linux/types.h>
+#include <linux/rbtree.h>
 
 struct thread;
+struct comm;
 struct ip_callchain;
+struct symbol;
+struct dso;
+struct call_return_processor;
+struct comm;
+struct perf_sample;
+struct addr_location;
+
+enum {
+	CALL_RETURN_NO_CALL	= 1 << 0,
+	CALL_RETURN_NO_RETURN	= 1 << 1,
+};
+
+struct call_return {
+	struct thread *thread;
+	struct comm *comm;
+	struct call_path *cp;
+	u64 call_time;
+	u64 return_time;
+	u64 branch_count;
+	u64 call_ref;
+	u64 return_ref;
+	u64 db_id;
+	u32 flags;
+};
+
+struct call_path {
+	struct call_path *parent;
+	struct symbol *sym;
+	u64 ip; /* Only if sym is null */
+	u64 db_id;
+	bool in_kernel;
+	struct rb_node rb_node;
+	struct rb_root children;
+};
 
 void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 			 u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
 void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
 			  size_t sz, u64 ip);
 void thread_stack__free(struct thread *thread);
 
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+			   void *data);
+void call_return_processor__free(struct call_return_processor *crp);
+int thread_stack__process(struct thread *thread, struct comm *comm,
+			  struct perf_sample *sample,
+			  struct addr_location *from_al,
+			  struct addr_location *to_al, u64 ref,
+			  struct call_return_processor *crp);
+
 #endif
Signature · 1 line
1.9.1
#43 lore raw

Re: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data

Em Wed, Oct 29, 2014 at 02:23:50PM +0100, Jiri Olsa escreveu:

Quoted 10 lines · Adrian Hunter
Quoted 2 lines

Enhance the thread stack to output detailed information about paired calls and returns.

Any chance we could get more explanation for this one? IMHO it's pretty complex patch to have just 2 lines in changelog..

If you could describe the data structures and their purpose and the way they are/will be captured/hooked in perf.

Right, I thought even to write some example code in 'perf test', so that by writing it I would become familiarized with this code, but never managed to find the time.

Also those functions not returning failure but instead overwriting stuff doesn't look right. They could conceivably be OK for the existing use, but I wonder if this can't be used in more scenarios where proper error handling could be required.

And in general, something new like this should have proper error reporting, if some particular user decides to ignore those errors, well, that is their problem, generic code should have proper error reporting.

- Arnaldo

Quoted 703 lines

thanks,
jirka

Quoted 700 lines
---
 tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/thread-stack.h |  47 ++++
 2 files changed, 590 insertions(+), 4 deletions(-)
tools/perf/util/thread-stack.c +543 -4
index c1ca2a9..444d132 100644
@@ -13,15 +13,48 @@
  *
  */
 
+#include <linux/rbtree.h>
+#include <linux/list.h>
 #include "thread.h"
 #include "event.h"
+#include "machine.h"
 #include "util.h"
+#include "debug.h"
+#include "symbol.h"
+#include "comm.h"
 #include "thread-stack.h"
 
-#define STACK_GROWTH 4096
+#define CALL_PATH_BLOCK_SHIFT 8
+#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
+#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
+
+struct call_path_block {
+	struct call_path cp[CALL_PATH_BLOCK_SIZE];
+	struct list_head node;
+};
+
+struct call_path_root {
+	struct call_path call_path;
+	struct list_head blocks;
+	size_t next;
+	size_t sz;
+};
+
+struct call_return_processor {
+	struct call_path_root *cpr;
+	int (*process)(struct call_return *cr, void *data);
+	void *data;
+};
+
+#define STACK_GROWTH 2048
 
 struct thread_stack_entry {
 	u64 ret_addr;
+	u64 timestamp;
+	u64 ref;
+	u64 branch_count;
+	struct call_path *cp;
+	bool no_call;
 };
 
 struct thread_stack {
@@ -29,6 +62,11 @@ struct thread_stack {
 	size_t cnt;
 	size_t sz;
 	u64 trace_nr;
+	u64 branch_count;
+	u64 kernel_start;
+	u64 last_time;
+	struct call_return_processor *crp;
+	struct comm *comm;
 };
 
 static void thread_stack__grow(struct thread_stack *ts)
@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts)
 	}
 }
 
-static struct thread_stack *thread_stack__new(void)
+static struct thread_stack *thread_stack__new(struct thread *thread,
+					      struct call_return_processor *crp)
 {
 	struct thread_stack *ts;
 
@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)
 		return NULL;
 	}
 
+	if (thread->mg && thread->mg->machine)
+		ts->kernel_start = machine__kernel_start(thread->mg->machine);
+	else
+		ts->kernel_start = 1ULL << 63;
+	ts->crp = crp;
+
 	return ts;
 }
 
@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
 	}
 }
 
+static bool thread_stack__in_kernel(struct thread_stack *ts)
+{
+	if (!ts->cnt)
+		return false;
+
+	return ts->stack[ts->cnt - 1].cp->in_kernel;
+}
+
+static int thread_stack__call_return(struct thread *thread,
+				     struct thread_stack *ts, size_t idx,
+				     u64 timestamp, u64 ref, bool no_return)
+{
+	struct call_return_processor *crp = ts->crp;
+	struct thread_stack_entry *tse;
+	struct call_return cr = {
+		.thread = thread,
+		.comm = ts->comm,
+		.db_id = 0,
+	};
+
+	tse = &ts->stack[idx];
+	cr.cp = tse->cp;
+	cr.call_time = tse->timestamp;
+	cr.return_time = timestamp;
+	cr.branch_count = ts->branch_count - tse->branch_count;
+	cr.call_ref = tse->ref;
+	cr.return_ref = ref;
+	if (tse->no_call)
+		cr.flags |= CALL_RETURN_NO_CALL;
+	if (no_return)
+		cr.flags |= CALL_RETURN_NO_RETURN;
+
+	return crp->process(&cr, crp->data);
+}
+
+static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+{
+	struct call_return_processor *crp = ts->crp;
+	int err;
+
+	if (!crp) {
+		ts->cnt = 0;
+		return 0;
+	}
+
+	while (ts->cnt) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						ts->last_time, 0, true);
+		if (err) {
+			pr_err("Error flushing thread stack!\n");
+			ts->cnt = 0;
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 			 u64 to_ip, u16 insn_len, u64 trace_nr)
 {
@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 		return;
 
 	if (!thread->ts) {
-		thread->ts = thread_stack__new();
+		thread->ts = thread_stack__new(thread, NULL);
 		if (!thread->ts)
 			return;
 		thread->ts->trace_nr = trace_nr;
 	}
 
 	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
 		thread->ts->trace_nr = trace_nr;
-		thread->ts->cnt = 0;
 	}
 
+	/* Stop here if thread_stack__process() is in use */
+	if (thread->ts->crp)
+		return;
+
 	if (flags & PERF_FLAG_CALL) {
 		u64 ret_addr;
 
@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 	}
 }
 
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
+{
+	if (!thread || !thread->ts)
+		return;
+
+	if (trace_nr != thread->ts->trace_nr) {
+		if (thread->ts->trace_nr)
+			thread_stack__flush(thread, thread->ts);
+		thread->ts->trace_nr = trace_nr;
+	}
+}
+
 void thread_stack__free(struct thread *thread)
 {
 	if (thread->ts) {
+		thread_stack__flush(thread, thread->ts);
 		zfree(&thread->ts->stack);
 		zfree(&thread->ts);
 	}
@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
 	for (i = 1; i < chain->nr; i++)
 		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
 }
+
+static void call_path__init(struct call_path *cp, struct call_path *parent,
+			    struct symbol *sym, u64 ip, bool in_kernel)
+{
+	cp->parent = parent;
+	cp->sym = sym;
+	cp->ip = sym ? 0 : ip;
+	cp->db_id = 0;
+	cp->in_kernel = in_kernel;
+	RB_CLEAR_NODE(&cp->rb_node);
+	cp->children = RB_ROOT;
+}
+
+static struct call_path_root *call_path_root__new(void)
+{
+	struct call_path_root *cpr;
+
+	cpr = zalloc(sizeof(struct call_path_root));
+	if (!cpr)
+		return NULL;
+	call_path__init(&cpr->call_path, NULL, NULL, 0, false);
+	INIT_LIST_HEAD(&cpr->blocks);
+	return cpr;
+}
+
+static void call_path_root__free(struct call_path_root *cpr)
+{
+	struct call_path_block *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+		list_del(&pos->node);
+		free(pos);
+	}
+	free(cpr);
+}
+
+static struct call_path *call_path__new(struct call_path_root *cpr,
+					struct call_path *parent,
+					struct symbol *sym, u64 ip,
+					bool in_kernel)
+{
+	struct call_path_block *cpb;
+	struct call_path *cp;
+	size_t n;
+
+	if (cpr->next < cpr->sz) {
+		cpb = list_last_entry(&cpr->blocks, struct call_path_block,
+				      node);
+	} else {
+		cpb = zalloc(sizeof(struct call_path_block));
+		if (!cpb)
+			return NULL;
+		list_add_tail(&cpb->node, &cpr->blocks);
+		cpr->sz += CALL_PATH_BLOCK_SIZE;
+	}
+
+	n = cpr->next++ & CALL_PATH_BLOCK_MASK;
+	cp = &cpb->cp[n];
+
+	call_path__init(cp, parent, sym, ip, in_kernel);
+
+	return cp;
+}
+
+static struct call_path *call_path__findnew(struct call_path_root *cpr,
+					    struct call_path *parent,
+					    struct symbol *sym, u64 ip, u64 ks)
+{
+	struct rb_node **p;
+	struct rb_node *node_parent = NULL;
+	struct call_path *cp;
+	bool in_kernel = ip >= ks;
+
+	if (sym)
+		ip = 0;
+
+	if (!parent)
+		return call_path__new(cpr, parent, sym, ip, in_kernel);
+
+	p = &parent->children.rb_node;
+	while (*p != NULL) {
+		node_parent = *p;
+		cp = rb_entry(node_parent, struct call_path, rb_node);
+
+		if (cp->sym == sym && cp->ip == ip)
+			return cp;
+
+		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+
+	cp = call_path__new(cpr, parent, sym, ip, in_kernel);
+	if (!cp)
+		return NULL;
+
+	rb_link_node(&cp->rb_node, node_parent, p);
+	rb_insert_color(&cp->rb_node, &parent->children);
+
+	return cp;
+}
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+			   void *data)
+{
+	struct call_return_processor *crp;
+
+	crp = zalloc(sizeof(struct call_return_processor));
+	if (!crp)
+		return NULL;
+	crp->cpr = call_path_root__new();
+	if (!crp->cpr)
+		goto out_free;
+	crp->process = process;
+	crp->data = data;
+	return crp;
+
+out_free:
+	free(crp);
+	return NULL;
+}
+
+void call_return_processor__free(struct call_return_processor *crp)
+{
+	if (crp) {
+		call_path_root__free(crp->cpr);
+		free(crp);
+	}
+}
+
+static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
+				 u64 timestamp, u64 ref, struct call_path *cp,
+				 bool no_call)
+{
+	struct thread_stack_entry *tse;
+
+	if (ts->cnt == ts->sz) {
+		thread_stack__grow(ts);
+		if (ts->cnt == ts->sz)
+			return -ENOMEM;
+	}
+
+	tse = &ts->stack[ts->cnt++];
+	tse->ret_addr = ret_addr;
+	tse->timestamp = timestamp;
+	tse->ref = ref;
+	tse->branch_count = ts->branch_count;
+	tse->cp = cp;
+	tse->no_call = no_call;
+
+	return 0;
+}
+
+static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
+				u64 ret_addr, u64 timestamp, u64 ref,
+				struct symbol *sym)
+{
+	int err;
+
+	if (!ts->cnt)
+		return 1;
+
+	if (ts->cnt == 1) {
+		struct thread_stack_entry *tse = &ts->stack[0];
+
+		if (tse->cp->sym == sym)
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+	}
+
+	if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+		return thread_stack__call_return(thread, ts, --ts->cnt,
+						 timestamp, ref, false);
+	} else {
+		size_t i = ts->cnt - 1;
+
+		while (i--) {
+			if (ts->stack[i].ret_addr != ret_addr)
+				continue;
+			i += 1;
+			while (ts->cnt > i) {
+				err = thread_stack__call_return(thread, ts,
+								--ts->cnt,
+								timestamp, ref,
+								true);
+				if (err)
+					return err;
+			}
+			return thread_stack__call_return(thread, ts, --ts->cnt,
+							 timestamp, ref, false);
+		}
+	}
+
+	return 1;
+}
+
+static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
+				struct perf_sample *sample,
+				struct addr_location *from_al,
+				struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	struct symbol *sym;
+	u64 ip;
+
+	if (sample->ip) {
+		ip = sample->ip;
+		sym = from_al->sym;
+	} else if (sample->addr) {
+		ip = sample->addr;
+		sym = to_al->sym;
+	} else {
+		return 0;
+	}
+
+	cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
+				     true);
+}
+
+static int thread_stack__no_call_return(struct thread *thread,
+					struct thread_stack *ts,
+					struct perf_sample *sample,
+					struct addr_location *from_al,
+					struct addr_location *to_al, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp, *parent;
+	u64 ks = ts->kernel_start;
+	int err;
+
+	if (sample->ip >= ks && sample->addr < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+
+		/* If the stack is empty, push the userspace address */
+		if (!ts->cnt) {
+			cp = call_path__findnew(cpr, &cpr->call_path,
+						to_al->sym, sample->addr,
+						ts->kernel_start);
+			if (!cp)
+				return -ENOMEM;
+			return thread_stack__push_cp(ts, 0, sample->time, ref,
+						     cp, true);
+		}
+	} else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
+		/* Return to userspace, so pop all kernel addresses */
+		while (thread_stack__in_kernel(ts)) {
+			err = thread_stack__call_return(thread, ts, --ts->cnt,
+							sample->time, ref,
+							true);
+			if (err)
+				return err;
+		}
+	}
+
+	if (ts->cnt)
+		parent = ts->stack[ts->cnt - 1].cp;
+	else
+		parent = &cpr->call_path;
+
+	/* This 'return' had no 'call', so push and pop top of stack */
+	cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
+				    true);
+	if (err)
+		return err;
+
+	return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
+				    to_al->sym);
+}
+
+static int thread_stack__trace_begin(struct thread *thread,
+				     struct thread_stack *ts, u64 timestamp,
+				     u64 ref)
+{
+	struct thread_stack_entry *tse;
+	int err;
+
+	if (!ts->cnt)
+		return 0;
+
+	/* Pop trace end */
+	tse = &ts->stack[ts->cnt - 1];
+	if (tse->cp->sym == NULL && tse->cp->ip == 0) {
+		err = thread_stack__call_return(thread, ts, --ts->cnt,
+						timestamp, ref, false);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int thread_stack__trace_end(struct thread_stack *ts,
+				   struct perf_sample *sample, u64 ref)
+{
+	struct call_path_root *cpr = ts->crp->cpr;
+	struct call_path *cp;
+	u64 ret_addr;
+
+	/* No point having 'trace end' on the bottom of the stack */
+	if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
+		return 0;
+
+	cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
+				ts->kernel_start);
+	if (!cp)
+		return -ENOMEM;
+
+	ret_addr = sample->ip + sample->insn_len;
+
+	return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
+				     false);
+}
+
+int thread_stack__process(struct thread *thread, struct comm *comm,
+			  struct perf_sample *sample,
+			  struct addr_location *from_al,
+			  struct addr_location *to_al, u64 ref,
+			  struct call_return_processor *crp)
+{
+	struct thread_stack *ts = thread->ts;
+	int err = 0;
+
+	if (ts) {
+		if (!ts->crp) {
+			/* Supersede thread_stack__event() */
+			thread_stack__free(thread);
+			thread->ts = thread_stack__new(thread, crp);
+			if (!thread->ts)
+				return -ENOMEM;
+			ts = thread->ts;
+			ts->comm = comm;
+		}
+	} else {
+		thread->ts = thread_stack__new(thread, crp);
+		if (!thread->ts)
+			return -ENOMEM;
+		ts = thread->ts;
+		ts->comm = comm;
+	}
+
+	/* Flush stack on exec */
+	if (ts->comm != comm && thread->pid_ == thread->tid) {
+		err = thread_stack__flush(thread, ts);
+		if (err)
+			return err;
+		ts->comm = comm;
+	}
+
+	/* If the stack is empty, put the current symbol on the stack */
+	if (!ts->cnt) {
+		err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
+					   ref);
+		if (err)
+			return err;
+	}
+
+	ts->branch_count += 1;
+	ts->last_time = sample->time;
+
+	if (sample->flags & PERF_FLAG_CALL) {
+		struct call_path_root *cpr = ts->crp->cpr;
+		struct call_path *cp;
+		u64 ret_addr;
+
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		ret_addr = sample->ip + sample->insn_len;
+		if (ret_addr == sample->addr)
+			return 0; /* Zero-length calls are excluded */
+
+		cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
+					to_al->sym, sample->addr,
+					ts->kernel_start);
+		if (!cp)
+			return -ENOMEM;
+		err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
+					    cp, false);
+	} else if (sample->flags & PERF_FLAG_RETURN) {
+		if (!sample->ip || !sample->addr)
+			return 0;
+
+		err = thread_stack__pop_cp(thread, ts, sample->addr,
+					   sample->time, ref, from_al->sym);
+		if (err) {
+			if (err < 0)
+				return err;
+			err = thread_stack__no_call_return(thread, ts, sample,
+							   from_al, to_al, ref);
+		}
+	} else if (sample->flags & PERF_FLAG_TRACE_BEGIN) {
+		err = thread_stack__trace_begin(thread, ts, sample->time, ref);
+	} else if (sample->flags & PERF_FLAG_TRACE_END) {
+		err = thread_stack__trace_end(ts, sample, ref);
+	}
+
+	return err;
+}
tools/perf/util/thread-stack.h +47 -0
index c0ba4cf..5bca14e 100644
@@ -19,14 +19,61 @@
 #include <sys/types.h>
 
 #include <linux/types.h>
+#include <linux/rbtree.h>
 
 struct thread;
+struct comm;
 struct ip_callchain;
+struct symbol;
+struct dso;
+struct call_return_processor;
+struct comm;
+struct perf_sample;
+struct addr_location;
+
+enum {
+	CALL_RETURN_NO_CALL	= 1 << 0,
+	CALL_RETURN_NO_RETURN	= 1 << 1,
+};
+
+struct call_return {
+	struct thread *thread;
+	struct comm *comm;
+	struct call_path *cp;
+	u64 call_time;
+	u64 return_time;
+	u64 branch_count;
+	u64 call_ref;
+	u64 return_ref;
+	u64 db_id;
+	u32 flags;
+};
+
+struct call_path {
+	struct call_path *parent;
+	struct symbol *sym;
+	u64 ip; /* Only if sym is null */
+	u64 db_id;
+	bool in_kernel;
+	struct rb_node rb_node;
+	struct rb_root children;
+};
 
 void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 			 u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
 void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
 			  size_t sz, u64 ip);
 void thread_stack__free(struct thread *thread);
 
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+			   void *data);
+void call_return_processor__free(struct call_return_processor *crp);
+int thread_stack__process(struct thread *thread, struct comm *comm,
+			  struct perf_sample *sample,
+			  struct addr_location *from_al,
+			  struct addr_location *to_al, u64 ref,
+			  struct call_return_processor *crp);
+
 #endif
Signature · 1 line
1.9.1
author reviewer
#44 lore raw

PATCH 11/16 perf tools: Add call information to the database export API

In reply to Adrian Hunter

Make it possible for the database export API to use the enhanced thread stack and export detailed information about paired calls and returns.

---
 tools/perf/util/db-export.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/db-export.h | 12 +++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)
tools/perf/util/db-export.c +51 -1
index c5b6e02..ca79616 100644
@@ -21,6 +21,7 @@
 #include "comm.h"
 #include "symbol.h"
 #include "event.h"
+#include "thread-stack.h"
 #include "db-export.h"
 
 int db_export__init(struct db_export *dbe)
@@ -29,8 +30,10 @@ int db_export__init(struct db_export *dbe)
 	return 0;
 }
 
-void db_export__exit(struct db_export *dbe __maybe_unused)
+void db_export__exit(struct db_export *dbe)
 {
+	call_return_processor__free(dbe->crp);
+	dbe->crp = NULL;
 }
 
 int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
@@ -268,6 +271,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 				     &es.addr_sym_db_id, &es.addr_offset);
 		if (err)
 			return err;
+		if (dbe->crp) {
+			err = thread_stack__process(thread, comm, sample, al,
+						    &addr_al, es.db_id,
+						    dbe->crp);
+			if (err)
+				return err;
+		}
 	}
 
 	if (dbe->export_sample)
@@ -314,3 +324,43 @@ int db_export__branch_types(struct db_export *dbe)
 	}
 	return err;
 }
+
+int db_export__call_path(struct db_export *dbe, struct call_path *cp)
+{
+	int err;
+
+	if (cp->db_id)
+		return 0;
+
+	if (cp->parent) {
+		err = db_export__call_path(dbe, cp->parent);
+		if (err)
+			return err;
+	}
+
+	cp->db_id = ++dbe->call_path_last_db_id;
+
+	if (dbe->export_call_path)
+		return dbe->export_call_path(dbe, cp);
+
+	return 0;
+}
+
+int db_export__call_return(struct db_export *dbe, struct call_return *cr)
+{
+	int err;
+
+	if (cr->db_id)
+		return 0;
+
+	err = db_export__call_path(dbe, cr->cp);
+	if (err)
+		return err;
+
+	cr->db_id = ++dbe->call_return_last_db_id;
+
+	if (dbe->export_call_return)
+		return dbe->export_call_return(dbe, cr);
+
+	return 0;
+}
tools/perf/util/db-export.h +12 -0
index e4baa45..dd5ac2a 100644
@@ -25,6 +25,9 @@ struct comm;
 struct dso;
 struct perf_sample;
 struct addr_location;
+struct call_return_processor;
+struct call_path;
+struct call_return;
 
 struct export_sample {
 	union perf_event	*event;
@@ -57,6 +60,10 @@ struct db_export {
 	int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
 				  const char *name);
 	int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+	int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
+	int (*export_call_return)(struct db_export *dbe,
+				  struct call_return *cr);
+	struct call_return_processor *crp;
 	u64 evsel_last_db_id;
 	u64 machine_last_db_id;
 	u64 thread_last_db_id;
@@ -65,6 +72,8 @@ struct db_export {
 	u64 dso_last_db_id;
 	u64 symbol_last_db_id;
 	u64 sample_last_db_id;
+	u64 call_path_last_db_id;
+	u64 call_return_last_db_id;
 };
 
 int db_export__init(struct db_export *dbe);
@@ -89,4 +98,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 
 int db_export__branch_types(struct db_export *dbe);
 
+int db_export__call_path(struct db_export *dbe, struct call_path *cp);
+int db_export__call_return(struct db_export *dbe, struct call_return *cr);
+
 #endif
Signature · 1 line
1.9.1
author reviewer
#45 lore raw

PATCH 12/16 perf tools: Add call information to Python export

In reply to Adrian Hunter

Add the ability to export detailed information about paired calls and returns to Python db export and the export-to-postgresql.py script.

---
 .../scripts/python/bin/export-to-postgresql-report | 15 ++--
 tools/perf/scripts/python/export-to-postgresql.py  | 66 ++++++++++++++++-
 .../util/scripting-engines/trace-event-python.c    | 84 +++++++++++++++++++++-
 3 files changed, 158 insertions(+), 7 deletions(-)
tools/perf/scripts/python/bin/export-to-postgresql-report +10 -5
index a8fdd15..cd335b6 100644
@@ -1,6 +1,6 @@
 #!/bin/bash
 # description: export perf data to a postgresql database
-# args: [database name] [columns]
+# args: [database name] [columns] [calls]
 n_args=0
 for i in "$@"
 do
@@ -9,11 +9,16 @@ do
     fi
     n_args=$(( $n_args + 1 ))
 done
-if [ "$n_args" -gt 2 ] ; then
-    echo "usage: export-to-postgresql-report [database name] [columns]"
+if [ "$n_args" -gt 3 ] ; then
+    echo "usage: export-to-postgresql-report [database name] [columns] [calls]"
     exit
 fi
-if [ "$n_args" -gt 1 ] ; then
+if [ "$n_args" -gt 2 ] ; then
+    dbname=$1
+    columns=$2
+    calls=$3
+    shift 3
+elif [ "$n_args" -gt 1 ] ; then
     dbname=$1
     columns=$2
     shift 2
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then
     dbname=$1
     shift
 fi
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
tools/perf/scripts/python/export-to-postgresql.py +65 -1

126 lines not on this page · raw message

tools/perf/util/scripting-engines/trace-event-python.c +83 -1

120 lines not on this page · raw message

Signature · 1 line
1.9.1
author reviewer
#46 lore raw

PATCH 13/16 perf tools: Defer export of comms that were not 'set'

In reply to Adrian Hunter

Tracing for a workload begins before the comm event is seen, which results in the initial comm having a string of the form ":<pid>" (e.g. ":12345"). In order to export the correct string, defer the export until the new script 'flush' callback.

---
 tools/perf/util/db-export.c                        | 62 +++++++++++++++++++++-
 tools/perf/util/db-export.h                        |  3 ++
 .../util/scripting-engines/trace-event-python.c    |  4 +-
 3 files changed, 67 insertions(+), 2 deletions(-)
tools/perf/util/db-export.c +61 -1
index ca79616..ef61cf7 100644
@@ -21,17 +21,74 @@
 #include "comm.h"
 #include "symbol.h"
 #include "event.h"
+#include "util.h"
 #include "thread-stack.h"
 #include "db-export.h"
 
+struct deferred_export {
+	struct list_head node;
+	struct comm *comm;
+};
+
+static int db_export__deferred(struct db_export *dbe)
+{
+	struct deferred_export *de;
+	int err;
+
+	while (!list_empty(&dbe->deferred)) {
+		de = list_entry(dbe->deferred.next, struct deferred_export,
+				node);
+		err = dbe->export_comm(dbe, de->comm);
+		list_del(&de->node);
+		free(de);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void db_export__free_deferred(struct db_export *dbe)
+{
+	struct deferred_export *de;
+
+	while (!list_empty(&dbe->deferred)) {
+		de = list_entry(dbe->deferred.next, struct deferred_export,
+				node);
+		list_del(&de->node);
+		free(de);
+	}
+}
+
+static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
+{
+	struct deferred_export *de;
+
+	de = zalloc(sizeof(struct deferred_export));
+	if (!de)
+		return -ENOMEM;
+
+	de->comm = comm;
+	list_add_tail(&de->node, &dbe->deferred);
+
+	return 0;
+}
+
 int db_export__init(struct db_export *dbe)
 {
 	memset(dbe, 0, sizeof(struct db_export));
+	INIT_LIST_HEAD(&dbe->deferred);
 	return 0;
 }
 
+int db_export__flush(struct db_export *dbe)
+{
+	return db_export__deferred(dbe);
+}
+
 void db_export__exit(struct db_export *dbe)
 {
+	db_export__free_deferred(dbe);
 	call_return_processor__free(dbe->crp);
 	dbe->crp = NULL;
 }
@@ -115,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm,
 	comm->db_id = ++dbe->comm_last_db_id;
 
 	if (dbe->export_comm) {
-		err = dbe->export_comm(dbe, comm);
+		if (main_thread->comm_set)
+			err = dbe->export_comm(dbe, comm);
+		else
+			err = db_export__defer_comm(dbe, comm);
 		if (err)
 			return err;
 	}
tools/perf/util/db-export.h +3 -0
index dd5ac2a..adbd22d 100644
@@ -17,6 +17,7 @@
 #define __PERF_DB_EXPORT_H
 
 #include <linux/types.h>
+#include <linux/list.h>
 
 struct perf_evsel;
 struct machine;
@@ -74,9 +75,11 @@ struct db_export {
 	u64 sample_last_db_id;
 	u64 call_path_last_db_id;
 	u64 call_return_last_db_id;
+	struct list_head deferred;
 };
 
 int db_export__init(struct db_export *dbe);
+int db_export__flush(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);
tools/perf/util/scripting-engines/trace-event-python.c +3 -1
index e7792b7..97a70d8 100644
@@ -1025,7 +1025,9 @@ error:
 
 static int python_flush_script(void)
 {
-	return 0;
+	struct tables *tables = &tables_global;
+
+	return db_export__flush(&tables->dbe);
 }
 
 /*
Signature · 1 line
1.9.1
author reviewer
#47 lore raw

PATCH 14/16 perf tools: Build programs to copy 32-bit compatibility VDSOs

In reply to Adrian Hunter

perf tools copy VDSO out of memory. However, on 64-bit machines there may be 32-bit compatibility VDOs also. To copy those requires separate 32-bit executables. This patch adds to the build additional programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32 respectively.

---
 tools/perf/Makefile.perf                        | 38 ++++++++++++++++++++++---
 tools/perf/config/Makefile                      | 25 +++++++++++++++-
 tools/perf/config/Makefile.arch                 |  8 ++++++
 tools/perf/config/feature-checks/Makefile       | 10 ++++++-
 tools/perf/config/feature-checks/test-compile.c |  4 +++
 tools/perf/perf-read-vdso.c                     | 34 ++++++++++++++++++++++
 tools/perf/util/find-vdso-map.c                 | 30 +++++++++++++++++++
 tools/perf/util/vdso.c                          | 37 ++++--------------------
 8 files changed, 149 insertions(+), 37 deletions(-)
 create mode 100644 tools/perf/config/feature-checks/test-compile.c
 create mode 100644 tools/perf/perf-read-vdso.c
 create mode 100644 tools/perf/util/find-vdso-map.c
tools/perf/Makefile.perf +34 -4
index 9134c93..208ff80 100644
@@ -60,6 +60,12 @@ include config/utilities.mak
 #
 # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
 # for dwarf backtrace post unwind.
+#
+# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32
+# for reading the 32-bit compatibility VDSO in 64-bit mode
+#
+# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32
+# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
 
 ifeq ($(srctree),)
 srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,11 +177,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
 
-#
-# Single 'perf' binary right now:
-#
 PROGRAMS += $(OUTPUT)perf
 
+ifndef NO_PERF_READ_VDSO32
+PROGRAMS += $(OUTPUT)perf-read-vdso32
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+PROGRAMS += $(OUTPUT)perf-read-vdsox32
+endif
+
 # what 'all' will build and 'install' will install, in perfexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
@@ -306,6 +317,7 @@ LIB_H += util/data.h
 LIB_H += util/kvm-stat.h
 LIB_H += util/thread-stack.h
 LIB_H += util/db-export.h
+LIB_H += util/find-vdso-map.c
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
 LIB_OBJS += $(OUTPUT)util/alias.o
@@ -736,6 +748,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti
 $(OUTPUT)perf-%: %.o $(PERFLIBS)
 	$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
 
+ifndef NO_PERF_READ_VDSO32
+$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c
+	$(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c
+	$(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
 $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 
@@ -880,6 +902,14 @@ install-bin: all install-gtk
 		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
 		$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
 		$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
+ifndef NO_PERF_READ_VDSO32
+	$(call QUIET_INSTALL, perf-read-vdso32) \
+		$(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
+ifndef NO_PERF_READ_VDSOX32
+	$(call QUIET_INSTALL, perf-read-vdsox32) \
+		$(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
 	$(call QUIET_INSTALL, libexec) \
 		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
 	$(call QUIET_INSTALL, perf-archive) \
@@ -932,7 +962,7 @@ config-clean:
 
 clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
 	$(call QUIET_CLEAN, core-objs)  $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
-	$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
+	$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
 	$(call QUIET_CLEAN, core-gen)   $(RM)  *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
 	$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
 	$(python-clean)
tools/perf/config/Makefile +24 -1
index 58f6091..3ba2382 100644
@@ -230,7 +230,9 @@ VF_FEATURE_TESTS =			\
 	bionic				\
 	liberty				\
 	liberty-z			\
-	cplus-demangle
+	cplus-demangle			\
+	compile-32			\
+	compile-x32
 
 # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features.
 # If in the future we need per-feature checks/flags for features not
@@ -622,6 +624,27 @@ ifdef HAVE_KVM_STAT_SUPPORT
     CFLAGS += -DHAVE_KVM_STAT_SUPPORT
 endif
 
+ifeq (${IS_64_BIT}, 1)
+  ifndef NO_PERF_READ_VDSO32
+    $(call feature_check,compile-32)
+    ifneq ($(feature-compile-32), 1)
+      NO_PERF_READ_VDSO32 := 1
+    endif
+  endif
+  ifneq (${IS_X86_64}, 1)
+    NO_PERF_READ_VDSOX32 := 1
+  endif
+  ifndef NO_PERF_READ_VDSOX32
+    $(call feature_check,compile-x32)
+    ifneq ($(feature-compile-x32), 1)
+      NO_PERF_READ_VDSOX32 := 1
+    endif
+  endif
+else
+  NO_PERF_READ_VDSO32 := 1
+  NO_PERF_READ_VDSOX32 := 1
+endif
+
 # Among the variables below, these:
 #   perfexecdir
 #   template_dir
tools/perf/config/Makefile.arch +8 -0
index 4b06719..851cd01 100644
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64)
     RAW_ARCH := x86_64
   endif
 endif
+
+ifeq (${IS_X86_64}, 1)
+  IS_64_BIT := 1
+else ifeq ($(ARCH),x86)
+  IS_64_BIT := 0
+else
+  IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+endif
tools/perf/config/feature-checks/Makefile +9 -1
index 72ab298..7c68ec7 100644
@@ -27,7 +27,9 @@ FILES=					\
 	test-libunwind-debug-frame.bin	\
 	test-stackprotector-all.bin	\
 	test-timerfd.bin		\
-	test-libdw-dwarf-unwind.bin
+	test-libdw-dwarf-unwind.bin	\
+	test-compile-32.bin		\
+	test-compile-x32.bin
 
 CC := $(CROSS_COMPILE)gcc -MD
 PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -131,6 +133,12 @@ test-libdw-dwarf-unwind.bin:
 test-sync-compare-and-swap.bin:
 	$(BUILD) -Werror
 
+test-compile-32.bin:
+	$(CC) -m32 -o $(OUTPUT)$@ test-compile.c
+
+test-compile-x32.bin:
+	$(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
+
 -include *.d
 
 ###############################
tools/perf/config/feature-checks/test-compile.c new file +4 -0
new file mode 100644
index 0000000..31dbf45
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return 0;
+}
tools/perf/perf-read-vdso.c new file +34 -0
new file mode 100644
index 0000000..764e254
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <string.h>
+
+#define VDSO__MAP_NAME "[vdso]"
+
+/*
+ * Include definition of find_vdso_map() also used in util/vdso.c for
+ * building perf.
+ */
+#include "util/find-vdso-map.c"
+
+int main(void)
+{
+	void *start, *end;
+	size_t size, written;
+
+	if (find_vdso_map(&start, &end))
+		return 1;
+
+	size = end - start;
+
+	while (size) {
+		written = fwrite(start, 1, size, stdout);
+		if (!written)
+			return 1;
+		start += written;
+		size -= written;
+	}
+
+	if (fflush(stdout))
+		return 1;
+
+	return 0;
+}
tools/perf/util/find-vdso-map.c new file +30 -0
new file mode 100644
index 0000000..95ef1cf
@@ -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;
+}
tools/perf/util/vdso.c +6 -31
index adca693..f51390a 100644
@@ -15,6 +15,12 @@
 #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 {
@@ -40,37 +46,6 @@ static struct vdso_info *vdso_info__new(void)
 	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;
Signature · 1 line
1.9.1
#48 lore raw

[tip:perf/core] perf tools: Build programs to copy 32-bit compatibility

perf tools: Build programs to copy 32-bit compatibility
Commit-ID:  e477f3f01a89a8fd44031e7f2ba6ffcab037336c
Gitweb:     http://git.kernel.org/tip/e477f3f01a89a8fd44031e7f2ba6ffcab037336c
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 18:16:03 -0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:48 -0200

perf tools: Build programs to copy 32-bit compatibility

perf tools copy VDSO out of memory. However, on 64-bit machines there may be 32-bit compatibility VDOs also. To copy those requires separate 32-bit executables.

This patch adds to the build additional programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32 respectively.

Cc Peter Zijlstra <[email protected]>
Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>,
Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Stephane Eranian <[email protected]>
---
 tools/perf/Makefile.perf                           | 38 +++++++++++++++++++---
 tools/perf/config/Makefile                         | 25 +++++++++++++-
 tools/perf/config/Makefile.arch                    |  8 +++++
 tools/perf/config/feature-checks/Makefile          | 10 +++++-
 .../perf/config/feature-checks/test-compile.c      |  0
 tools/perf/perf-read-vdso.c                        | 34 +++++++++++++++++++
 tools/perf/util/find-vdso-map.c                    | 30 +++++++++++++++++
 tools/perf/util/vdso.c                             | 37 ++++-----------------
 8 files changed, 145 insertions(+), 37 deletions(-)
tools/perf/Makefile.perf +34 -4
index 262916f..9c4ced0 100644
@@ -60,6 +60,12 @@ include config/utilities.mak
 #
 # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
 # for dwarf backtrace post unwind.
+#
+# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32
+# for reading the 32-bit compatibility VDSO in 64-bit mode
+#
+# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32
+# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
 
 ifeq ($(srctree),)
 srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,11 +177,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
 
-#
-# Single 'perf' binary right now:
-#
 PROGRAMS += $(OUTPUT)perf
 
+ifndef NO_PERF_READ_VDSO32
+PROGRAMS += $(OUTPUT)perf-read-vdso32
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+PROGRAMS += $(OUTPUT)perf-read-vdsox32
+endif
+
 # what 'all' will build and 'install' will install, in perfexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
@@ -253,6 +264,7 @@ LIB_H += util/event.h
 LIB_H += util/evsel.h
 LIB_H += util/evlist.h
 LIB_H += util/exec_cmd.h
+LIB_H += util/find-vdso-map.c
 LIB_H += util/levenshtein.h
 LIB_H += util/machine.h
 LIB_H += util/map.h
@@ -732,6 +744,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti
 $(OUTPUT)perf-%: %.o $(PERFLIBS)
 	$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
 
+ifndef NO_PERF_READ_VDSO32
+$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c
+	$(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c
+	$(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
 $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 
@@ -876,6 +898,14 @@ install-bin: all install-gtk
 		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
 		$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
 		$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
+ifndef NO_PERF_READ_VDSO32
+	$(call QUIET_INSTALL, perf-read-vdso32) \
+		$(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
+ifndef NO_PERF_READ_VDSOX32
+	$(call QUIET_INSTALL, perf-read-vdsox32) \
+		$(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
 	$(call QUIET_INSTALL, libexec) \
 		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
 	$(call QUIET_INSTALL, perf-archive) \
@@ -928,7 +958,7 @@ config-clean:
 
 clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
 	$(call QUIET_CLEAN, core-objs)  $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
-	$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
+	$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
 	$(call QUIET_CLEAN, core-gen)   $(RM)  *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
 	$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
 	$(python-clean)
tools/perf/config/Makefile +24 -1
index 58f6091..3ba2382 100644
@@ -230,7 +230,9 @@ VF_FEATURE_TESTS =			\
 	bionic				\
 	liberty				\
 	liberty-z			\
-	cplus-demangle
+	cplus-demangle			\
+	compile-32			\
+	compile-x32
 
 # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features.
 # If in the future we need per-feature checks/flags for features not
@@ -622,6 +624,27 @@ ifdef HAVE_KVM_STAT_SUPPORT
     CFLAGS += -DHAVE_KVM_STAT_SUPPORT
 endif
 
+ifeq (${IS_64_BIT}, 1)
+  ifndef NO_PERF_READ_VDSO32
+    $(call feature_check,compile-32)
+    ifneq ($(feature-compile-32), 1)
+      NO_PERF_READ_VDSO32 := 1
+    endif
+  endif
+  ifneq (${IS_X86_64}, 1)
+    NO_PERF_READ_VDSOX32 := 1
+  endif
+  ifndef NO_PERF_READ_VDSOX32
+    $(call feature_check,compile-x32)
+    ifneq ($(feature-compile-x32), 1)
+      NO_PERF_READ_VDSOX32 := 1
+    endif
+  endif
+else
+  NO_PERF_READ_VDSO32 := 1
+  NO_PERF_READ_VDSOX32 := 1
+endif
+
 # Among the variables below, these:
 #   perfexecdir
 #   template_dir
tools/perf/config/Makefile.arch +8 -0
index 4b06719..851cd01 100644
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64)
     RAW_ARCH := x86_64
   endif
 endif
+
+ifeq (${IS_X86_64}, 1)
+  IS_64_BIT := 1
+else ifeq ($(ARCH),x86)
+  IS_64_BIT := 0
+else
+  IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+endif
tools/perf/config/feature-checks/Makefile +9 -1
index 72ab298..7c68ec7 100644
@@ -27,7 +27,9 @@ FILES=					\
 	test-libunwind-debug-frame.bin	\
 	test-stackprotector-all.bin	\
 	test-timerfd.bin		\
-	test-libdw-dwarf-unwind.bin
+	test-libdw-dwarf-unwind.bin	\
+	test-compile-32.bin		\
+	test-compile-x32.bin
 
 CC := $(CROSS_COMPILE)gcc -MD
 PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -131,6 +133,12 @@ test-libdw-dwarf-unwind.bin:
 test-sync-compare-and-swap.bin:
 	$(BUILD) -Werror
 
+test-compile-32.bin:
+	$(CC) -m32 -o $(OUTPUT)$@ test-compile.c
+
+test-compile-x32.bin:
+	$(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
+
 -include *.d
 
 ###############################
tools/perf/config/feature-checks/test-compile.c renamed from arch/mips/boot/compressed/dummy.c +0 -0
similarity index 100%
copy from arch/mips/boot/compressed/dummy.c
copy to tools/perf/config/feature-checks/test-compile.c
tools/perf/perf-read-vdso.c new file +34 -0
new file mode 100644
index 0000000..764e254
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <string.h>
+
+#define VDSO__MAP_NAME "[vdso]"
+
+/*
+ * Include definition of find_vdso_map() also used in util/vdso.c for
+ * building perf.
+ */
+#include "util/find-vdso-map.c"
+
+int main(void)
+{
+	void *start, *end;
+	size_t size, written;
+
+	if (find_vdso_map(&start, &end))
+		return 1;
+
+	size = end - start;
+
+	while (size) {
+		written = fwrite(start, 1, size, stdout);
+		if (!written)
+			return 1;
+		start += written;
+		size -= written;
+	}
+
+	if (fflush(stdout))
+		return 1;
+
+	return 0;
+}
tools/perf/util/find-vdso-map.c new file +30 -0
new file mode 100644
index 0000000..95ef1cf
@@ -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;
+}
tools/perf/util/vdso.c +6 -31
index adca693..f51390a 100644
@@ -15,6 +15,12 @@
 #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 {
@@ -40,37 +46,6 @@ static struct vdso_info *vdso_info__new(void)
 	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;
author reviewer
#49 lore raw

PATCH 15/16 perf tools: Add support for 32-bit compatibility VDSOs

In reply to Adrian Hunter

'perf record' post-processes the event stream to create a list of build-ids for object files for which sample events have been recorded. That results in those object files being recorded in the build-id cache.

In the case of VDSO, perf tools reads it from memory and copies it into a temporary file, which as decribed above, gets added to the build-id cache.

Then when the perf.data file is processed by other tools, the build-id of VDSO is listed in the perf.data file and the VDSO can be read from the build-id cache. In that case the name of the map, the short name of the DSO, and the entry in the build-id cache are all "[vdso]".

However, in the 64-bit case, there also can be 32-bit compatibility VDSOs.

A previous patch added programs "perf-read-vdso32" and "perf read-vdsox32". This patch uses those programs to read the correct VDSO for a thread and create a temporary file just as for the 64-bit VDSO. The map name and the entry in the build-id cache are still "[vdso]" but the DSO short name becomes "[vdso32]" and "[vdsox32]" respectively.

---
 tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/vdso.h |   4 +-
 2 files changed, 172 insertions(+), 2 deletions(-)
tools/perf/util/vdso.c +169 -1
index f51390a..69daef6 100644
@@ -12,6 +12,7 @@
 #include "util.h"
 #include "symbol.h"
 #include "machine.h"
+#include "thread.h"
 #include "linux/string.h"
 #include "debug.h"
 
@@ -28,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)
@@ -41,6 +47,18 @@ 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));
@@ -92,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);
 }
@@ -110,6 +134,143 @@ 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);
+	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)
 {
@@ -123,6 +284,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;
@@ -139,5 +305,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);
 }
tools/perf/util/vdso.h +3 -1
index af9d692..d97da16 100644
@@ -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)
 {
Signature · 1 line
1.9.1
#50 lore raw

[tip:perf/core] perf tools: Add support for 32-bit compatibility VDSOs

perf tools: Add support for 32-bit compatibility VDSOs
Commit-ID:  f6832e1720f5cc283703cfe9ccbfb46a3fb6f548
Gitweb:     http://git.kernel.org/tip/f6832e1720f5cc283703cfe9ccbfb46a3fb6f548
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:23 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:48 -0200

perf tools: Add support for 32-bit compatibility VDSOs

'perf record' post-processes the event stream to create a list of build-ids for object files for which sample events have been recorded. That results in those object files being recorded in the build-id cache.

In the case of VDSO, perf tools reads it from memory and copies it into a temporary file, which as decribed above, gets added to the build-id cache.

Then when the perf.data file is processed by other tools, the build-id of VDSO is listed in the perf.data file and the VDSO can be read from the build-id cache. In that case the name of the map, the short name of the DSO, and the entry in the build-id cache are all "[vdso]".

However, in the 64-bit case, there also can be 32-bit compatibility VDSOs.

A previous patch added programs "perf-read-vdso32" and "perf read-vdsox32".

This patch uses those programs to read the correct VDSO for a thread and create a temporary file just as for the 64-bit VDSO.

The map name and the entry in the build-id cache are still "[vdso]" but the DSO short name becomes "[vdso32]" and "[vdsox32]" respectively.

---
 tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/vdso.h |   4 +-
 2 files changed, 172 insertions(+), 2 deletions(-)
tools/perf/util/vdso.c +169 -1
index f51390a..69daef6 100644
@@ -12,6 +12,7 @@
 #include "util.h"
 #include "symbol.h"
 #include "machine.h"
+#include "thread.h"
 #include "linux/string.h"
 #include "debug.h"
 
@@ -28,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)
@@ -41,6 +47,18 @@ 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));
@@ -92,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);
 }
@@ -110,6 +134,143 @@ 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);
+	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)
 {
@@ -123,6 +284,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;
@@ -139,5 +305,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);
 }
tools/perf/util/vdso.h +3 -1
index af9d692..d97da16 100644
@@ -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)
 {
author reviewer
#51 lore raw

PATCH 16/16 perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

In reply to Adrian Hunter

popen() causes an error message to print if perf-read-vdso32 does not run. Avoid that by not trying to run it if it was not built. Ditto perf-read-vdsox32.

---
 tools/perf/config/Makefile |  8 ++++++--
 tools/perf/util/vdso.c     | 10 ++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)
tools/perf/config/Makefile +6 -2
index 3ba2382..71264e4 100644
@@ -627,7 +627,9 @@ endif
 ifeq (${IS_64_BIT}, 1)
   ifndef NO_PERF_READ_VDSO32
     $(call feature_check,compile-32)
-    ifneq ($(feature-compile-32), 1)
+    ifeq ($(feature-compile-32), 1)
+      CFLAGS += -DHAVE_PERF_READ_VDSO32
+    else
       NO_PERF_READ_VDSO32 := 1
     endif
   endif
@@ -636,7 +638,9 @@ ifeq (${IS_64_BIT}, 1)
   endif
   ifndef NO_PERF_READ_VDSOX32
     $(call feature_check,compile-x32)
-    ifneq ($(feature-compile-x32), 1)
+    ifeq ($(feature-compile-x32), 1)
+      CFLAGS += -DHAVE_PERF_READ_VDSOX32
+    else
       NO_PERF_READ_VDSOX32 := 1
     endif
   endif
tools/perf/util/vdso.c +10 -0
index 69daef6..5c7dd79 100644
@@ -255,6 +255,16 @@ static int vdso__dso_findnew_compat(struct machine *machine,
 	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);
Signature · 1 line
1.9.1
#52 lore raw

[tip:perf/core] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
Commit-ID:  46b1fa85ff5a2e03423770b3931b97266e8ac6cf
Gitweb:     http://git.kernel.org/tip/46b1fa85ff5a2e03423770b3931b97266e8ac6cf
Author:     Adrian Hunter <[email protected]>
AuthorDate: Thu, 23 Oct 2014 13:45:24 +0300
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 29 Oct 2014 10:32:48 -0200

perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

popen() causes an error message to print if perf-read-vdso32 does not run. Avoid that by not trying to run it if it was not built. Ditto perf-read-vdsox32.

---
 tools/perf/config/Makefile |  8 ++++++--
 tools/perf/util/vdso.c     | 10 ++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)
tools/perf/config/Makefile +6 -2
index 3ba2382..71264e4 100644
@@ -627,7 +627,9 @@ endif
 ifeq (${IS_64_BIT}, 1)
   ifndef NO_PERF_READ_VDSO32
     $(call feature_check,compile-32)
-    ifneq ($(feature-compile-32), 1)
+    ifeq ($(feature-compile-32), 1)
+      CFLAGS += -DHAVE_PERF_READ_VDSO32
+    else
       NO_PERF_READ_VDSO32 := 1
     endif
   endif
@@ -636,7 +638,9 @@ ifeq (${IS_64_BIT}, 1)
   endif
   ifndef NO_PERF_READ_VDSOX32
     $(call feature_check,compile-x32)
-    ifneq ($(feature-compile-x32), 1)
+    ifeq ($(feature-compile-x32), 1)
+      CFLAGS += -DHAVE_PERF_READ_VDSOX32
+    else
       NO_PERF_READ_VDSOX32 := 1
     endif
   endif
tools/perf/util/vdso.c +10 -0
index 69daef6..5c7dd79 100644
@@ -255,6 +255,16 @@ static int vdso__dso_findnew_compat(struct machine *machine,
 	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);
#53 lore raw
In reply to Adrian Hunter

Em Thu, Oct 23, 2014 at 01:45:08PM +0300, Adrian Hunter escreveu:

Quoted 7 lines

Hi

Here are the remaining Intel PT preparation patches. They are all patches that have been posted before, although "perf tools:
Do not attempt to run perf-read-vdso32 if it wasn't built" wasn't sent as a separate email.

Quoted 6 lines

With regard to "32-bit compatibility VDSOs", you may recall that Ingo had a problem getting "sh: perf-read-vdso32: command not found". That message is inevitable when using popen to run perf-read-vdso32 and perf-read-vdso32 cannot be found. At least with the patch "perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built" one reason for the message is eliminated.

The above patches I will try to process, look ok but since they are at the end of the patch kit, it conflicts if I don't process the ones at the start of the patchkit, which I am still not confortable merging.

- Arnaldo

Quoted 70 lines

It would be nice to target 3.19 for Intel PT, but there are still the real patches of which there are about 48.

Adrian Hunter (16):
perf tools: Add id index
perf pmu: Let pmu's with no events show up on perf list
perf session: Add perf_session__deliver_synth_event()
perf tools: Add a thread stack for synthesizing call chains
perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Add branch_type and in_tx to Python export
perf tools: Enhance the thread stack to output call/return data
perf tools: Add call information to the database export API
perf tools: Add call information to Python export
perf tools: Defer export of comms that were not 'set'
perf tools: Build programs to copy 32-bit compatibility VDSOs
perf tools: Add support for 32-bit compatibility VDSOs
perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

 tools/perf/Makefile.perf                           |  42 +-
 tools/perf/builtin-inject.c                        |   1 +
 tools/perf/config/Makefile                         |  29 +-
 tools/perf/config/Makefile.arch                    |   8 +
 tools/perf/config/feature-checks/Makefile          |  10 +-
 tools/perf/config/feature-checks/test-compile.c    |   4 +
 tools/perf/perf-read-vdso.c                        |  34 +
 .../scripts/python/bin/export-to-postgresql-record |   8 +
 .../scripts/python/bin/export-to-postgresql-report |  29 +
 tools/perf/scripts/python/export-to-postgresql.py  | 444 +++++++++++++
 tools/perf/util/comm.h                             |   1 +
 tools/perf/util/db-export.c                        | 426 +++++++++++++
 tools/perf/util/db-export.h                        | 107 ++++
 tools/perf/util/dso.h                              |   1 +
 tools/perf/util/event.c                            |   1 +
 tools/perf/util/event.h                            |  41 ++
 tools/perf/util/evlist.c                           |  26 +-
 tools/perf/util/evsel.h                            |   4 +
 tools/perf/util/find-vdso-map.c                    |  30 +
 tools/perf/util/machine.h                          |   1 +
 tools/perf/util/pmu.c                              |  13 +-
 tools/perf/util/pmu.h                              |   1 +
 .../util/scripting-engines/trace-event-python.c    | 395 +++++++++++-
 tools/perf/util/session.c                          | 136 ++++
 tools/perf/util/session.h                          |  15 +
 tools/perf/util/symbol.h                           |   1 +
 tools/perf/util/thread-stack.c                     | 690 +++++++++++++++++++++
 tools/perf/util/thread-stack.h                     |  79 +++
 tools/perf/util/thread.c                           |   3 +
 tools/perf/util/thread.h                           |   4 +
 tools/perf/util/tool.h                             |   3 +-
 tools/perf/util/vdso.c                             | 217 ++++++-
 tools/perf/util/vdso.h                             |   4 +-
 33 files changed, 2760 insertions(+), 48 deletions(-)
 create mode 100644 tools/perf/config/feature-checks/test-compile.c
 create mode 100644 tools/perf/perf-read-vdso.c
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record
 create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report
 create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
 create mode 100644 tools/perf/util/db-export.c
 create mode 100644 tools/perf/util/db-export.h
 create mode 100644 tools/perf/util/find-vdso-map.c
 create mode 100644 tools/perf/util/thread-stack.c
 create mode 100644 tools/perf/util/thread-stack.h

Regards
Adrian

#54 lore raw
In reply to Adrian Hunter

Em Thu, Oct 23, 2014 at 01:45:08PM +0300, Adrian Hunter escreveu:

Quoted 1 line

Adrian Hunter (16):

Applied these, will test those and put in my next pull req to Ingo:

Quoted 4 lines

perf pmu: Let pmu's with no events show up on perf list perf tools: Build programs to copy 32-bit compatibility VDSOs perf tools: Add support for 32-bit compatibility VDSOs perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built

The next two are related, made questions about the first:

Quoted 2 lines

perf tools: Add id index
perf session: Add perf_session__deliver_synth_event()

Made some questions about these two as well:

Quoted 2 lines

perf tools: Add a thread stack for synthesizing call chains perf tools: Enhance the thread stack to output call/return data

These all are about the db_export, if time permits tomorrow I'll try to do what I suggested and post a patch for you to see, i.e. basically the first one is the one with the questions.

Quoted 8 lines

perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Add branch_type and in_tx to Python export
perf tools: Add call information to the database export API
perf tools: Add call information to Python export
perf tools: Defer export of comms that were not 'set'

- Arnaldo