All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/16] perf tools: Intel PT preparation continued
@ 2014-10-23 10:45 Adrian Hunter
  2014-10-23 10:45 ` [PATCH 01/16] perf tools: Add id index Adrian Hunter
                   ` (17 more replies)
  0 siblings, 18 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [PATCH 01/16] perf tools: Add id index
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 21:08   ` Arnaldo Carvalho de Melo
  2014-10-23 10:45 ` [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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(-)

diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index de99ca1..046c719 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -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),
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 4af6b27..bbf6705 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
 	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA",
 	[PERF_RECORD_HEADER_BUILD_ID]		= "BUILD_ID",
 	[PERF_RECORD_FINISHED_ROUND]		= "FINISHED_ROUND",
+	[PERF_RECORD_ID_INDEX]			= "ID_INDEX",
 };
 
 const char *perf_event__name(unsigned int id)
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 5699e7e..c89518e 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */
 	PERF_RECORD_HEADER_TRACING_DATA		= 66,
 	PERF_RECORD_HEADER_BUILD_ID		= 67,
 	PERF_RECORD_FINISHED_ROUND		= 68,
+	PERF_RECORD_ID_INDEX			= 69,
 	PERF_RECORD_HEADER_MAX
 };
 
@@ -239,6 +240,19 @@ struct tracing_data_event {
 	u32 size;
 };
 
+struct id_index_entry {
+	u64 id;
+	u64 idx;
+	u64 cpu;
+	u64 tid;
+};
+
+struct id_index_event {
+	struct perf_event_header header;
+	u64 nr;
+	struct id_index_entry entries[0];
+};
+
 union perf_event {
 	struct perf_event_header	header;
 	struct mmap_event		mmap;
@@ -253,6 +267,7 @@ union perf_event {
 	struct event_type_event		event_type;
 	struct tracing_data_event	tracing_data;
 	struct build_id_event		build_id;
+	struct id_index_event		id_index;
 };
 
 void perf_event__print_totals(void);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 3c9e77d..0babd39 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
 	return 0;
 }
 
+static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
+				     struct perf_evsel *evsel, int idx, int cpu,
+				     int thread)
+{
+	struct perf_sample_id *sid = SID(evsel, cpu, thread);
+	sid->idx = idx;
+	if (evlist->cpus && cpu >= 0)
+		sid->cpu = evlist->cpus->map[cpu];
+	else
+		sid->cpu = -1;
+	if (!evsel->system_wide && evlist->threads && thread >= 0)
+		sid->tid = evlist->threads->map[thread];
+	else
+		sid->tid = -1;
+}
+
 struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
 {
 	struct hlist_head *head;
@@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
 			return -1;
 		}
 
-		if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
-		    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
-			return -1;
+		if (evsel->attr.read_format & PERF_FORMAT_ID) {
+			if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
+						   fd) < 0)
+				return -1;
+			perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
+						 thread);
+		}
 	}
 
 	return 0;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 163c560..4861e8c 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -36,6 +36,9 @@ struct perf_sample_id {
 	struct hlist_node 	node;
 	u64		 	id;
 	struct perf_evsel	*evsel;
+	int			idx;
+	int			cpu;
+	pid_t			tid;
 
 	/* Holds total ID period value for PERF_SAMPLE_READ processing. */
 	u64			period;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 6702ac2..d70e37d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
 				  union perf_event *event,
 				  struct perf_session *session);
 
+static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
+				 union perf_event *event __maybe_unused,
+				 struct perf_session *perf_session
+				 __maybe_unused)
+{
+	dump_printf(": unhandled!\n");
+	return 0;
+}
+
 void perf_tool__fill_defaults(struct perf_tool *tool)
 {
 	if (tool->sample == NULL)
@@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
 		else
 			tool->finished_round = process_finished_round_stub;
 	}
+	if (tool->id_index == NULL)
+		tool->id_index = process_id_index_stub;
 }
  
 static void swap_sample_id_all(union perf_event *event, void *data)
@@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
 	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap,
 	[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
 	[PERF_RECORD_HEADER_BUILD_ID]	  = NULL,
+	[PERF_RECORD_ID_INDEX]		  = perf_event__all64_swap,
 	[PERF_RECORD_HEADER_MAX]	  = NULL,
 };
 
@@ -888,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;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index a4be851..d8521ac 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -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 */
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index f116369..bb2708b 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -39,7 +39,8 @@ struct perf_tool {
 	event_attr_op	attr;
 	event_op2	tracing_data;
 	event_op2	finished_round,
-			build_id;
+			build_id,
+			id_index;
 	bool		ordered_events;
 	bool		ordering_requires_timestamps;
 };
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
  2014-10-23 10:45 ` [PATCH 01/16] perf tools: Add id index Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-24  5:15   ` Namhyung Kim
  2014-10-30  6:45   ` [tip:perf/core] perf pmu: Let pmu' s " tip-bot for Adrian Hunter
  2014-10-23 10:45 ` [PATCH 03/16] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
                   ` (15 subsequent siblings)
  17 siblings, 2 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/pmu.c | 13 +++++++++++--
 tools/perf/util/pmu.h |  1 +
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e243ad9..91dca60 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
 
 	pmu = NULL;
 	len = 0;
-	while ((pmu = perf_pmu__scan(pmu)) != NULL)
+	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
 		list_for_each_entry(alias, &pmu->aliases, list)
 			len++;
+		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++) {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index fe9dfbe..8092de7 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -18,6 +18,7 @@ struct perf_event_attr;
 struct perf_pmu {
 	char *name;
 	__u32 type;
+	bool selectable;
 	struct perf_event_attr *default_config;
 	struct cpu_map *cpus;
 	struct list_head format;  /* HEAD struct perf_pmu_format -> list */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
  2014-10-23 10:45 ` [PATCH 01/16] perf tools: Add id index Adrian Hunter
  2014-10-23 10:45 ` [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-24  5:22   ` Namhyung Kim
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/session.c | 14 ++++++++++++++
 tools/perf/util/session.h |  5 +++++
 2 files changed, 19 insertions(+)

diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index d70e37d..09635d1 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -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;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index d8521ac..dc26ebf 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -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);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (2 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 03/16] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 20:51   ` Arnaldo Carvalho de Melo
                     ` (3 more replies)
  2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
                   ` (13 subsequent siblings)
  17 siblings, 4 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 262916f..5bbe1ff 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index c89518e..e84f929 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -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;
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
new file mode 100644
index 0000000..c1ca2a9
--- /dev/null
+++ b/tools/perf/util/thread-stack.c
@@ -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;
+}
+
+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;
+}
+
+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;
+}
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
new file mode 100644
index 0000000..c0ba4cf
--- /dev/null
+++ b/tools/perf/util/thread-stack.h
@@ -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
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 2b7b2d9..4e3418d 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -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;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 8c75fa7..a057820 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -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;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (3 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 21:42   ` Arnaldo Carvalho de Melo
                     ` (2 more replies)
  2014-10-23 10:45 ` [PATCH 06/16] perf tools: Extend Python script interface to export data in a " Adrian Hunter
                   ` (12 subsequent siblings)
  17 siblings, 3 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 5bbe1ff..9134c93 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index 51c10ab..99e7021 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -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;
 };
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
new file mode 100644
index 0000000..53d0e75
--- /dev/null
+++ b/tools/perf/util/db-export.c
@@ -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;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
new file mode 100644
index 0000000..b3643e8
--- /dev/null
+++ b/tools/perf/util/db-export.h
@@ -0,0 +1,86 @@
+/*
+ * db-export.h: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_DB_EXPORT_H
+#define __PERF_DB_EXPORT_H
+
+#include <linux/types.h>
+
+struct perf_evsel;
+struct machine;
+struct thread;
+struct comm;
+struct dso;
+struct perf_sample;
+struct addr_location;
+
+struct export_sample {
+	union perf_event	*event;
+	struct perf_sample	*sample;
+	struct perf_evsel	*evsel;
+	struct thread		*thread;
+	struct addr_location	*al;
+	u64			db_id;
+	u64			comm_db_id;
+	u64			dso_db_id;
+	u64			sym_db_id;
+	u64			offset; /* ip offset from symbol start */
+	u64			addr_dso_db_id;
+	u64			addr_sym_db_id;
+	u64			addr_offset; /* addr offset from symbol start */
+};
+
+struct db_export {
+	int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+	int (*export_machine)(struct db_export *dbe, struct machine *machine);
+	int (*export_thread)(struct db_export *dbe, struct thread *thread,
+			     u64 main_thread_db_id, struct machine *machine);
+	int (*export_comm)(struct db_export *dbe, struct comm *comm);
+	int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
+				  struct comm *comm, struct thread *thread);
+	int (*export_dso)(struct db_export *dbe, struct dso *dso,
+			  struct machine *machine);
+	int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
+			     struct dso *dso);
+	int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+	u64 evsel_last_db_id;
+	u64 machine_last_db_id;
+	u64 thread_last_db_id;
+	u64 comm_last_db_id;
+	u64 comm_thread_last_db_id;
+	u64 dso_last_db_id;
+	u64 symbol_last_db_id;
+	u64 sample_last_db_id;
+};
+
+int db_export__init(struct db_export *dbe);
+void db_export__exit(struct db_export *dbe);
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__machine(struct db_export *dbe, struct machine *machine);
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+		      struct machine *machine, struct comm *comm);
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+		    struct thread *main_thread);
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+			   struct thread *thread);
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+		   struct machine *machine);
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+		      struct dso *dso);
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+		      struct perf_sample *sample, struct perf_evsel *evsel,
+		      struct thread *thread, struct addr_location *al);
+
+#endif
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index acb651a..8463fc3 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -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;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 4861e8c..4777262 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -95,6 +95,7 @@ struct perf_evsel {
 	int			sample_read;
 	struct perf_evsel	*leader;
 	char			*group_name;
+	u64			db_id;
 };
 
 union u64_swap {
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 2b651a7..4bc57e5 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -40,6 +40,7 @@ struct machine {
 	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
+	u64		  db_id;
 };
 
 static inline
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index eb2c19b..6f54ade 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -73,6 +73,7 @@ struct symbol {
 	struct rb_node	rb_node;
 	u64		start;
 	u64		end;
+	u64		db_id;
 	u16		namelen;
 	u8		binding;
 	bool		ignore;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index a057820..a3ebb21 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -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;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 06/16] perf tools: Extend Python script interface to export data in a database-friendly way
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (4 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-30  6:47   ` [tip:perf/core] perf scripting python: Extend " tip-bot for Adrian Hunter
  2014-10-23 10:45 ` [PATCH 07/16] perf tools: Add Python script to export to postgresql Adrian Hunter
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 .../util/scripting-engines/trace-event-python.c    | 281 ++++++++++++++++++++-
 1 file changed, 279 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 496f21c..539e23d 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 #include <errno.h>
 
 #include "../../perf.h"
@@ -33,6 +34,9 @@
 #include "../util.h"
 #include "../event.h"
 #include "../thread.h"
+#include "../comm.h"
+#include "../machine.h"
+#include "../db-export.h"
 #include "../trace-event.h"
 #include "../machine.h"
 
@@ -53,6 +57,21 @@ static int zero_flag_atom;
 
 static PyObject *main_module, *main_dict;
 
+struct tables {
+	struct db_export	dbe;
+	PyObject		*evsel_handler;
+	PyObject		*machine_handler;
+	PyObject		*thread_handler;
+	PyObject		*comm_handler;
+	PyObject		*comm_thread_handler;
+	PyObject		*dso_handler;
+	PyObject		*symbol_handler;
+	PyObject		*sample_handler;
+	bool			db_export_mode;
+};
+
+static struct tables tables_global;
+
 static void handler_call_die(const char *handler_name) NORETURN;
 static void handler_call_die(const char *handler_name)
 {
@@ -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();
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 07/16] perf tools: Add Python script to export to postgresql
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (5 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 06/16] perf tools: Extend Python script interface to export data in a " Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-30  6:47   ` [tip:perf/core] perf script: " tip-bot for Adrian Hunter
  2014-10-23 10:45 ` [PATCH 08/16] perf tools: Add branch type to db export Adrian Hunter
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 .../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

diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record
new file mode 100644
index 0000000..221d66e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-record
@@ -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 $@
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
new file mode 100644
index 0000000..a8fdd15
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -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
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
new file mode 100644
index 0000000..d8f6df0
--- /dev/null
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -0,0 +1,360 @@
+# export-to-postgresql.py: export perf data to a postgresql 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.
+
+import os
+import sys
+import struct
+import datetime
+
+from PySide.QtSql import *
+
+# Need to access PostgreSQL C library directly to use COPY FROM STDIN
+from ctypes import *
+libpq = CDLL("libpq.so.5")
+PQconnectdb = libpq.PQconnectdb
+PQconnectdb.restype = c_void_p
+PQfinish = libpq.PQfinish
+PQstatus = libpq.PQstatus
+PQexec = libpq.PQexec
+PQexec.restype = c_void_p
+PQresultStatus = libpq.PQresultStatus
+PQputCopyData = libpq.PQputCopyData
+PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ]
+PQputCopyEnd = libpq.PQputCopyEnd
+PQputCopyEnd.argtypes = [ c_void_p, c_void_p ]
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+	'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+# These perf imports are not used at present
+#from perf_trace_context import *
+#from Core import *
+
+perf_db_export_mode = True
+
+def usage():
+	print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]"
+	print >> sys.stderr, "where:	columns		'all' or 'branches'"
+	raise Exception("Too few arguments")
+
+if (len(sys.argv) < 2):
+	usage()
+
+dbname = sys.argv[1]
+
+if (len(sys.argv) >= 3):
+	columns = sys.argv[2]
+else:
+	columns = "all"
+
+if columns not in ("all", "branches"):
+	usage()
+
+branches = (columns == "branches")
+
+output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
+os.mkdir(output_dir_name)
+
+def do_query(q, s):
+	if (q.exec_(s)):
+		return
+	raise Exception("Query failed: " + q.lastError().text())
+
+print datetime.datetime.today(), "Creating database..."
+
+db = QSqlDatabase.addDatabase('QPSQL')
+query = QSqlQuery(db)
+db.setDatabaseName('postgres')
+db.open()
+try:
+	do_query(query, 'CREATE DATABASE ' + dbname)
+except:
+	os.rmdir(output_dir_name)
+	raise
+query.finish()
+query.clear()
+db.close()
+
+db.setDatabaseName(dbname)
+db.open()
+
+query = QSqlQuery(db)
+do_query(query, 'SET client_min_messages TO WARNING')
+
+do_query(query, 'CREATE TABLE selected_events ('
+		'id		bigint		NOT NULL,'
+		'name		varchar(80))')
+do_query(query, 'CREATE TABLE machines ('
+		'id		bigint		NOT NULL,'
+		'pid		integer,'
+		'root_dir 	varchar(4096))')
+do_query(query, 'CREATE TABLE threads ('
+		'id		bigint		NOT NULL,'
+		'machine_id	bigint,'
+		'process_id	bigint,'
+		'pid		integer,'
+		'tid		integer)')
+do_query(query, 'CREATE TABLE comms ('
+		'id		bigint		NOT NULL,'
+		'comm		varchar(16))')
+do_query(query, 'CREATE TABLE comm_threads ('
+		'id		bigint		NOT NULL,'
+		'comm_id	bigint,'
+		'thread_id	bigint)')
+do_query(query, 'CREATE TABLE dsos ('
+		'id		bigint		NOT NULL,'
+		'machine_id	bigint,'
+		'short_name	varchar(256),'
+		'long_name	varchar(4096),'
+		'build_id	varchar(64))')
+do_query(query, 'CREATE TABLE symbols ('
+		'id		bigint		NOT NULL,'
+		'dso_id		bigint,'
+		'sym_start	bigint,'
+		'sym_end	bigint,'
+		'binding	integer,'
+		'name		varchar(2048))')
+if branches:
+	do_query(query, 'CREATE TABLE samples ('
+		'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)')
+else:
+	do_query(query, 'CREATE TABLE samples ('
+		'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)')
+
+do_query(query, 'CREATE VIEW samples_view AS '
+	'SELECT '
+		'id,'
+		'time,'
+		'cpu,'
+		'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+		'(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
+		'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+		'(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
+		'to_hex(ip) AS ip_hex,'
+		'(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
+		'sym_offset,'
+		'(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
+		'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'
+	' FROM samples')
+
+
+file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0)
+file_trailer = "\377\377"
+
+def open_output_file(file_name):
+	path_name = output_dir_name + "/" + file_name
+	file = open(path_name, "w+")
+	file.write(file_header)
+	return file
+
+def close_output_file(file):
+	file.write(file_trailer)
+	file.close()
+
+def copy_output_file_direct(file, table_name):
+	close_output_file(file)
+	sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')"
+	do_query(query, sql)
+
+# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
+def copy_output_file(file, table_name):
+	conn = PQconnectdb("dbname = " + dbname)
+	if (PQstatus(conn)):
+		raise Exception("COPY FROM STDIN PQconnectdb failed")
+	file.write(file_trailer)
+	file.seek(0)
+	sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
+	res = PQexec(conn, sql)
+	if (PQresultStatus(res) != 4):
+		raise Exception("COPY FROM STDIN PQexec failed")
+	data = file.read(65536)
+	while (len(data)):
+		ret = PQputCopyData(conn, data, len(data))
+		if (ret != 1):
+			raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret))
+		data = file.read(65536)
+	ret = PQputCopyEnd(conn, None)
+	if (ret != 1):
+		raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret))
+	PQfinish(conn)
+
+def remove_output_file(file):
+	name = file.name
+	file.close()
+	os.unlink(name)
+
+evsel_file		= open_output_file("evsel_table.bin")
+machine_file		= open_output_file("machine_table.bin")
+thread_file		= open_output_file("thread_table.bin")
+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")
+sample_file		= open_output_file("sample_table.bin")
+
+def trace_begin():
+	print datetime.datetime.today(), "Writing to intermediate files..."
+	# id == 0 means unknown.  It is easier to create records for them than replace the zeroes with NULLs
+	evsel_table(0, "unknown")
+	machine_table(0, 0, "unknown")
+	thread_table(0, 0, 0, -1, -1)
+	comm_table(0, "unknown")
+	dso_table(0, 0, "unknown", "unknown", "")
+	symbol_table(0, 0, 0, 0, 0, "unknown")
+
+unhandled_count = 0
+
+def trace_end():
+	print datetime.datetime.today(), "Copying to database..."
+	copy_output_file(evsel_file,		"selected_events")
+	copy_output_file(machine_file,		"machines")
+	copy_output_file(thread_file,		"threads")
+	copy_output_file(comm_file,		"comms")
+	copy_output_file(comm_thread_file,	"comm_threads")
+	copy_output_file(dso_file,		"dsos")
+	copy_output_file(symbol_file,		"symbols")
+	copy_output_file(sample_file,		"samples")
+
+	print datetime.datetime.today(), "Removing intermediate files..."
+	remove_output_file(evsel_file)
+	remove_output_file(machine_file)
+	remove_output_file(thread_file)
+	remove_output_file(comm_file)
+	remove_output_file(comm_thread_file)
+	remove_output_file(dso_file)
+	remove_output_file(symbol_file)
+	remove_output_file(sample_file)
+	os.rmdir(output_dir_name)
+	print datetime.datetime.today(), "Adding primary keys"
+	do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE machines        ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE threads         ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE comms           ADD PRIMARY KEY (id)')
+	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 samples         ADD PRIMARY KEY (id)')
+
+	print datetime.datetime.today(), "Adding foreign keys"
+	do_query(query, 'ALTER TABLE threads '
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id),'
+					'ADD CONSTRAINT processfk  FOREIGN KEY (process_id)   REFERENCES threads    (id)')
+	do_query(query, 'ALTER TABLE comm_threads '
+					'ADD CONSTRAINT commfk     FOREIGN KEY (comm_id)      REFERENCES comms      (id),'
+					'ADD CONSTRAINT threadfk   FOREIGN KEY (thread_id)    REFERENCES threads    (id)')
+	do_query(query, 'ALTER TABLE dsos '
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id)')
+	do_query(query, 'ALTER TABLE symbols '
+					'ADD CONSTRAINT dsofk      FOREIGN KEY (dso_id)       REFERENCES dsos       (id)')
+	do_query(query, 'ALTER TABLE samples '
+					'ADD CONSTRAINT evselfk    FOREIGN KEY (evsel_id)     REFERENCES selected_events (id),'
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id),'
+					'ADD CONSTRAINT threadfk   FOREIGN KEY (thread_id)    REFERENCES threads    (id),'
+					'ADD CONSTRAINT commfk     FOREIGN KEY (comm_id)      REFERENCES comms      (id),'
+					'ADD CONSTRAINT dsofk      FOREIGN KEY (dso_id)       REFERENCES dsos       (id),'
+					'ADD CONSTRAINT symbolfk   FOREIGN KEY (symbol_id)    REFERENCES symbols    (id),'
+					'ADD CONSTRAINT todsofk    FOREIGN KEY (to_dso_id)    REFERENCES dsos       (id),'
+					'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols    (id)')
+
+	if (unhandled_count):
+		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
+	print datetime.datetime.today(), "Done"
+
+def trace_unhandled(event_name, context, event_fields_dict):
+	global unhandled_count
+	unhandled_count += 1
+
+def sched__sched_switch(*x):
+	pass
+
+def evsel_table(evsel_id, evsel_name, *x):
+	n = len(evsel_name)
+	fmt = "!hiqi" + str(n) + "s"
+	value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
+	evsel_file.write(value)
+
+def machine_table(machine_id, pid, root_dir, *x):
+	n = len(root_dir)
+	fmt = "!hiqiii" + str(n) + "s"
+	value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
+	machine_file.write(value)
+
+def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
+	value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
+	thread_file.write(value)
+
+def comm_table(comm_id, comm_str, *x):
+	n = len(comm_str)
+	fmt = "!hiqi" + str(n) + "s"
+	value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
+	comm_file.write(value)
+
+def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
+	fmt = "!hiqiqiq"
+	value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id)
+	comm_thread_file.write(value)
+
+def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
+	n1 = len(short_name)
+	n2 = len(long_name)
+	n3 = len(build_id)
+	fmt = "!hiqiqi" + str(n1) + "si"  + str(n2) + "si" + str(n3) + "s"
+	value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id)
+	dso_file.write(value)
+
+def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
+	n = len(symbol_name)
+	fmt = "!hiqiqiqiqiii" + str(n) + "s"
+	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):
+	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)
+	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)
+	sample_file.write(value)
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 08/16] perf tools: Add branch type to db export
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (6 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 07/16] perf tools: Add Python script to export to postgresql Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 10:45 ` [PATCH 09/16] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/db-export.c | 48 +++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/db-export.h |  6 ++++++
 2 files changed, 54 insertions(+)

diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 53d0e75..c5b6e02 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -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;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index b3643e8..e4baa45 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -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
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 09/16] perf tools: Add branch_type and in_tx to Python export
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (7 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 08/16] perf tools: Add branch type to db export Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 10:45 ` [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/scripts/python/export-to-postgresql.py  | 32 ++++++++++++++++++----
 .../util/scripting-engines/trace-event-python.c    | 30 +++++++++++++++++++-
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index d8f6df0..bb79aec 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -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)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 539e23d..553aded 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -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();
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (8 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 09/16] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-29 13:23   ` Jiri Olsa
  2014-10-23 10:45 ` [PATCH 11/16] perf tools: Add call information to the database export API Adrian Hunter
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/thread-stack.h |  47 ++++
 2 files changed, 590 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index c1ca2a9..444d132 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -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;
+}
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
index c0ba4cf..5bca14e 100644
--- a/tools/perf/util/thread-stack.h
+++ b/tools/perf/util/thread-stack.h
@@ -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
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 11/16] perf tools: Add call information to the database export API
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (9 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 10:45 ` [PATCH 12/16] perf tools: Add call information to Python export Adrian Hunter
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/db-export.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/db-export.h | 12 +++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index c5b6e02..ca79616 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -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;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index e4baa45..dd5ac2a 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -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
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 12/16] perf tools: Add call information to Python export
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (10 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 11/16] perf tools: Add call information to the database export API Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 10:45 ` [PATCH 13/16] perf tools: Defer export of comms that were not 'set' Adrian Hunter
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 .../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(-)

diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
index a8fdd15..cd335b6 100644
--- a/tools/perf/scripts/python/bin/export-to-postgresql-report
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -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
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index bb79aec..4cdafd8 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -40,10 +40,12 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
 #from Core import *
 
 perf_db_export_mode = True
+perf_db_export_calls = False
 
 def usage():
-	print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]"
+	print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
 	print >> sys.stderr, "where:	columns		'all' or 'branches'"
+	print >> sys.stderr, "		calls		'calls' => create calls table"
 	raise Exception("Too few arguments")
 
 if (len(sys.argv) < 2):
@@ -61,6 +63,12 @@ if columns not in ("all", "branches"):
 
 branches = (columns == "branches")
 
+if (len(sys.argv) >= 4):
+	if (sys.argv[3] == "calls"):
+		perf_db_export_calls = True
+	else:
+		usage()
+
 output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
 os.mkdir(output_dir_name)
 
@@ -170,6 +178,25 @@ else:
 		'branch_type	integer,'
 		'in_tx		boolean)')
 
+if perf_db_export_calls:
+	do_query(query, 'CREATE TABLE call_paths ('
+		'id		bigint		NOT NULL,'
+		'parent_id	bigint,'
+		'symbol_id	bigint,'
+		'ip		bigint)')
+	do_query(query, 'CREATE TABLE calls ('
+		'id		bigint		NOT NULL,'
+		'thread_id	bigint,'
+		'comm_id	bigint,'
+		'call_path_id	bigint,'
+		'call_time	bigint,'
+		'return_time	bigint,'
+		'branch_count	bigint,'
+		'call_id	bigint,'
+		'return_id	bigint,'
+		'parent_call_path_id	bigint,'
+		'flags		integer)')
+
 do_query(query, 'CREATE VIEW samples_view AS '
 	'SELECT '
 		'id,'
@@ -246,6 +273,9 @@ 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")
+if perf_db_export_calls:
+	call_path_file		= open_output_file("call_path_table.bin")
+	call_file		= open_output_file("call_table.bin")
 
 def trace_begin():
 	print datetime.datetime.today(), "Writing to intermediate files..."
@@ -256,6 +286,9 @@ def trace_begin():
 	comm_table(0, "unknown")
 	dso_table(0, 0, "unknown", "unknown", "")
 	symbol_table(0, 0, 0, 0, 0, "unknown")
+	sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+	if perf_db_export_calls:
+		call_path_table(0, 0, 0, 0)
 
 unhandled_count = 0
 
@@ -270,6 +303,9 @@ def trace_end():
 	copy_output_file(symbol_file,		"symbols")
 	copy_output_file(branch_type_file,	"branch_types")
 	copy_output_file(sample_file,		"samples")
+	if perf_db_export_calls:
+		copy_output_file(call_path_file,	"call_paths")
+		copy_output_file(call_file,		"calls")
 
 	print datetime.datetime.today(), "Removing intermediate files..."
 	remove_output_file(evsel_file)
@@ -281,6 +317,9 @@ def trace_end():
 	remove_output_file(symbol_file)
 	remove_output_file(branch_type_file)
 	remove_output_file(sample_file)
+	if perf_db_export_calls:
+		remove_output_file(call_path_file)
+		remove_output_file(call_file)
 	os.rmdir(output_dir_name)
 	print datetime.datetime.today(), "Adding primary keys"
 	do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
@@ -292,6 +331,9 @@ def trace_end():
 	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)')
+	if perf_db_export_calls:
+		do_query(query, 'ALTER TABLE call_paths      ADD PRIMARY KEY (id)')
+		do_query(query, 'ALTER TABLE calls           ADD PRIMARY KEY (id)')
 
 	print datetime.datetime.today(), "Adding foreign keys"
 	do_query(query, 'ALTER TABLE threads '
@@ -313,6 +355,18 @@ def trace_end():
 					'ADD CONSTRAINT symbolfk   FOREIGN KEY (symbol_id)    REFERENCES symbols    (id),'
 					'ADD CONSTRAINT todsofk    FOREIGN KEY (to_dso_id)    REFERENCES dsos       (id),'
 					'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols    (id)')
+	if perf_db_export_calls:
+		do_query(query, 'ALTER TABLE call_paths '
+					'ADD CONSTRAINT parentfk    FOREIGN KEY (parent_id)    REFERENCES call_paths (id),'
+					'ADD CONSTRAINT symbolfk    FOREIGN KEY (symbol_id)    REFERENCES symbols    (id)')
+		do_query(query, 'ALTER TABLE calls '
+					'ADD CONSTRAINT threadfk    FOREIGN KEY (thread_id)    REFERENCES threads    (id),'
+					'ADD CONSTRAINT commfk      FOREIGN KEY (comm_id)      REFERENCES comms      (id),'
+					'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),'
+					'ADD CONSTRAINT callfk      FOREIGN KEY (call_id)      REFERENCES samples    (id),'
+					'ADD CONSTRAINT returnfk    FOREIGN KEY (return_id)    REFERENCES samples    (id),'
+					'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
+		do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
 
 	if (unhandled_count):
 		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
@@ -378,3 +432,13 @@ def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, sy
 	else:
 		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)
+
+def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
+	fmt = "!hiqiqiqiq"
+	value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
+	call_path_file.write(value)
+
+def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x):
+	fmt = "!hiqiqiqiqiqiqiqiqiqiqii"
+	value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags)
+	call_file.write(value)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 553aded..e7792b7 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -37,6 +37,7 @@
 #include "../comm.h"
 #include "../machine.h"
 #include "../db-export.h"
+#include "../thread-stack.h"
 #include "../trace-event.h"
 #include "../machine.h"
 
@@ -68,6 +69,8 @@ struct tables {
 	PyObject		*symbol_handler;
 	PyObject		*branch_type_handler;
 	PyObject		*sample_handler;
+	PyObject		*call_path_handler;
+	PyObject		*call_return_handler;
 	bool			db_export_mode;
 };
 
@@ -719,6 +722,64 @@ static int python_export_sample(struct db_export *dbe,
 	return 0;
 }
 
+static int python_export_call_path(struct db_export *dbe, struct call_path *cp)
+{
+	struct tables *tables = container_of(dbe, struct tables, dbe);
+	PyObject *t;
+	u64 parent_db_id, sym_db_id;
+
+	parent_db_id = cp->parent ? cp->parent->db_id : 0;
+	sym_db_id = cp->sym ? cp->sym->db_id : 0;
+
+	t = tuple_new(4);
+
+	tuple_set_u64(t, 0, cp->db_id);
+	tuple_set_u64(t, 1, parent_db_id);
+	tuple_set_u64(t, 2, sym_db_id);
+	tuple_set_u64(t, 3, cp->ip);
+
+	call_object(tables->call_path_handler, t, "call_path_table");
+
+	Py_DECREF(t);
+
+	return 0;
+}
+
+static int python_export_call_return(struct db_export *dbe,
+				     struct call_return *cr)
+{
+	struct tables *tables = container_of(dbe, struct tables, dbe);
+	u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
+	PyObject *t;
+
+	t = tuple_new(11);
+
+	tuple_set_u64(t, 0, cr->db_id);
+	tuple_set_u64(t, 1, cr->thread->db_id);
+	tuple_set_u64(t, 2, comm_db_id);
+	tuple_set_u64(t, 3, cr->cp->db_id);
+	tuple_set_u64(t, 4, cr->call_time);
+	tuple_set_u64(t, 5, cr->return_time);
+	tuple_set_u64(t, 6, cr->branch_count);
+	tuple_set_u64(t, 7, cr->call_ref);
+	tuple_set_u64(t, 8, cr->return_ref);
+	tuple_set_u64(t, 9, cr->cp->parent->db_id);
+	tuple_set_s32(t, 10, cr->flags);
+
+	call_object(tables->call_return_handler, t, "call_return_table");
+
+	Py_DECREF(t);
+
+	return 0;
+}
+
+static int python_process_call_return(struct call_return *cr, void *data)
+{
+	struct db_export *dbe = data;
+
+	return db_export__call_return(dbe, cr);
+}
+
 static void python_process_general_event(struct perf_sample *sample,
 					 struct perf_evsel *evsel,
 					 struct thread *thread,
@@ -851,7 +912,9 @@ error:
 static void set_table_handlers(struct tables *tables)
 {
 	const char *perf_db_export_mode = "perf_db_export_mode";
-	PyObject *db_export_mode;
+	const char *perf_db_export_calls = "perf_db_export_calls";
+	PyObject *db_export_mode, *db_export_calls;
+	bool export_calls = false;
 	int ret;
 
 	memset(tables, 0, sizeof(struct tables));
@@ -868,6 +931,23 @@ static void set_table_handlers(struct tables *tables)
 	if (!ret)
 		return;
 
+	tables->dbe.crp = NULL;
+	db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls);
+	if (db_export_calls) {
+		ret = PyObject_IsTrue(db_export_calls);
+		if (ret == -1)
+			handler_call_die(perf_db_export_calls);
+		export_calls = !!ret;
+	}
+
+	if (export_calls) {
+		tables->dbe.crp =
+			call_return_processor__new(python_process_call_return,
+						   &tables->dbe);
+		if (!tables->dbe.crp)
+			Py_FatalError("failed to create calls processor");
+	}
+
 	tables->db_export_mode = true;
 
 	SET_TABLE_HANDLER(evsel);
@@ -879,6 +959,8 @@ static void set_table_handlers(struct tables *tables)
 	SET_TABLE_HANDLER(symbol);
 	SET_TABLE_HANDLER(branch_type);
 	SET_TABLE_HANDLER(sample);
+	SET_TABLE_HANDLER(call_path);
+	SET_TABLE_HANDLER(call_return);
 }
 
 /*
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 13/16] perf tools: Defer export of comms that were not 'set'
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (11 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 12/16] perf tools: Add call information to Python export Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-23 10:45 ` [PATCH 14/16] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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(-)

diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index ca79616..ef61cf7 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -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;
 	}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index dd5ac2a..adbd22d 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -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);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index e7792b7..97a70d8 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -1025,7 +1025,9 @@ error:
 
 static int python_flush_script(void)
 {
-	return 0;
+	struct tables *tables = &tables_global;
+
+	return db_export__flush(&tables->dbe);
 }
 
 /*
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 14/16] perf tools: Build programs to copy 32-bit compatibility VDSOs
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (12 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 13/16] perf tools: Defer export of comms that were not 'set' Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-30  6:45   ` [tip:perf/core] perf tools: Build programs to copy 32-bit compatibility tip-bot for Adrian Hunter
  2014-10-23 10:45 ` [PATCH 15/16] perf tools: Add support for 32-bit compatibility VDSOs Adrian Hunter
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 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

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 9134c93..208ff80 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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)
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 58f6091..3ba2382 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -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
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch
index 4b06719..851cd01 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/perf/config/Makefile.arch
@@ -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
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile
index 72ab298..7c68ec7 100644
--- a/tools/perf/config/feature-checks/Makefile
+++ b/tools/perf/config/feature-checks/Makefile
@@ -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
 
 ###############################
diff --git a/tools/perf/config/feature-checks/test-compile.c b/tools/perf/config/feature-checks/test-compile.c
new file mode 100644
index 0000000..31dbf45
--- /dev/null
+++ b/tools/perf/config/feature-checks/test-compile.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return 0;
+}
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c
new file mode 100644
index 0000000..764e254
--- /dev/null
+++ b/tools/perf/perf-read-vdso.c
@@ -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;
+}
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c
new file mode 100644
index 0000000..95ef1cf
--- /dev/null
+++ b/tools/perf/util/find-vdso-map.c
@@ -0,0 +1,30 @@
+static int find_vdso_map(void **start, void **end)
+{
+	FILE *maps;
+	char line[128];
+	int found = 0;
+
+	maps = fopen("/proc/self/maps", "r");
+	if (!maps) {
+		fprintf(stderr, "vdso: cannot open maps\n");
+		return -1;
+	}
+
+	while (!found && fgets(line, sizeof(line), maps)) {
+		int m = -1;
+
+		/* We care only about private r-x mappings. */
+		if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+				start, end, &m))
+			continue;
+		if (m < 0)
+			continue;
+
+		if (!strncmp(&line[m], VDSO__MAP_NAME,
+			     sizeof(VDSO__MAP_NAME) - 1))
+			found = 1;
+	}
+
+	fclose(maps);
+	return !found;
+}
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index adca693..f51390a 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 15/16] perf tools: Add support for 32-bit compatibility VDSOs
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (13 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 14/16] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-30  6:45   ` [tip:perf/core] " tip-bot for Adrian Hunter
  2014-10-23 10:45 ` [PATCH 16/16] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built Adrian Hunter
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

'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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/vdso.h |   4 +-
 2 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index f51390a..69daef6 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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);
 }
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index af9d692..d97da16 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -7,7 +7,9 @@
 
 #define VDSO__MAP_NAME "[vdso]"
 
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO    "[vdso]"
+#define DSO__NAME_VDSO32  "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
 
 static inline bool is_vdso_map(const char *filename)
 {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 16/16] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (14 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 15/16] perf tools: Add support for 32-bit compatibility VDSOs Adrian Hunter
@ 2014-10-23 10:45 ` Adrian Hunter
  2014-10-30  6:46   ` [tip:perf/core] " tip-bot for Adrian Hunter
  2014-10-23 21:11 ` [PATCH 00/16] perf tools: Intel PT preparation continued Arnaldo Carvalho de Melo
  2014-10-23 23:43 ` Arnaldo Carvalho de Melo
  17 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-23 10:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 tools/perf/config/Makefile |  8 ++++++--
 tools/perf/util/vdso.c     | 10 ++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 3ba2382..71264e4 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -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
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 69daef6..5c7dd79 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
@ 2014-10-23 20:51   ` Arnaldo Carvalho de Melo
  2014-10-24  8:47     ` Adrian Hunter
  2014-10-24  5:41   ` Namhyung Kim
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-23 20:51 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:
> Add a thread stack for synthesizing call chains from call
> and return events.
> 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  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
> 
> diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
> index 262916f..5bbe1ff 100644
> --- a/tools/perf/Makefile.perf
> +++ b/tools/perf/Makefile.perf
> @@ -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
> diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
> index c89518e..e84f929 100644
> --- a/tools/perf/util/event.h
> +++ b/tools/perf/util/event.h
> @@ -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;
> diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
> new file mode 100644
> index 0000000..c1ca2a9
> --- /dev/null
> +++ b/tools/perf/util/thread-stack.c
> @@ -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;
> +}
> +
> +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.

> +
> +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;
> +}
> diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
> new file mode 100644
> index 0000000..c0ba4cf
> --- /dev/null
> +++ b/tools/perf/util/thread-stack.h
> @@ -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
> diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
> index 2b7b2d9..4e3418d 100644
> --- a/tools/perf/util/thread.c
> +++ b/tools/perf/util/thread.c
> @@ -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;
> diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
> index 8c75fa7..a057820 100644
> --- a/tools/perf/util/thread.h
> +++ b/tools/perf/util/thread.h
> @@ -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;
> -- 
> 1.9.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 01/16] perf tools: Add id index
  2014-10-23 10:45 ` [PATCH 01/16] perf tools: Add id index Adrian Hunter
@ 2014-10-23 21:08   ` Arnaldo Carvalho de Melo
  2014-10-24  5:10     ` Namhyung Kim
  2014-10-24  7:25     ` Adrian Hunter
  0 siblings, 2 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-23 21:08 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
> 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
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  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(-)
> 
> diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
> index de99ca1..046c719 100644
> --- a/tools/perf/builtin-inject.c
> +++ b/tools/perf/builtin-inject.c
> @@ -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),
> diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
> index 4af6b27..bbf6705 100644
> --- a/tools/perf/util/event.c
> +++ b/tools/perf/util/event.c
> @@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
>  	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA",
>  	[PERF_RECORD_HEADER_BUILD_ID]		= "BUILD_ID",
>  	[PERF_RECORD_FINISHED_ROUND]		= "FINISHED_ROUND",
> +	[PERF_RECORD_ID_INDEX]			= "ID_INDEX",
>  };
>  
>  const char *perf_event__name(unsigned int id)
> diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
> index 5699e7e..c89518e 100644
> --- a/tools/perf/util/event.h
> +++ b/tools/perf/util/event.h
> @@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */
>  	PERF_RECORD_HEADER_TRACING_DATA		= 66,
>  	PERF_RECORD_HEADER_BUILD_ID		= 67,
>  	PERF_RECORD_FINISHED_ROUND		= 68,
> +	PERF_RECORD_ID_INDEX			= 69,
>  	PERF_RECORD_HEADER_MAX
>  };
>  
> @@ -239,6 +240,19 @@ struct tracing_data_event {
>  	u32 size;
>  };
>  
> +struct id_index_entry {
> +	u64 id;
> +	u64 idx;
> +	u64 cpu;
> +	u64 tid;
> +};
> +
> +struct id_index_event {
> +	struct perf_event_header header;
> +	u64 nr;
> +	struct id_index_entry entries[0];
> +};
> +
>  union perf_event {
>  	struct perf_event_header	header;
>  	struct mmap_event		mmap;
> @@ -253,6 +267,7 @@ union perf_event {
>  	struct event_type_event		event_type;
>  	struct tracing_data_event	tracing_data;
>  	struct build_id_event		build_id;
> +	struct id_index_event		id_index;
>  };
>  
>  void perf_event__print_totals(void);
> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> index 3c9e77d..0babd39 100644
> --- a/tools/perf/util/evlist.c
> +++ b/tools/perf/util/evlist.c
> @@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
>  	return 0;
>  }
>  
> +static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
> +				     struct perf_evsel *evsel, int idx, int cpu,
> +				     int thread)
> +{
> +	struct perf_sample_id *sid = SID(evsel, cpu, thread);
> +	sid->idx = idx;
> +	if (evlist->cpus && cpu >= 0)
> +		sid->cpu = evlist->cpus->map[cpu];
> +	else
> +		sid->cpu = -1;
> +	if (!evsel->system_wide && evlist->threads && thread >= 0)
> +		sid->tid = evlist->threads->map[thread];
> +	else
> +		sid->tid = -1;
> +}
> +
>  struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
>  {
>  	struct hlist_head *head;
> @@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
>  			return -1;
>  		}
>  
> -		if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
> -		    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
> -			return -1;
> +		if (evsel->attr.read_format & PERF_FORMAT_ID) {
> +			if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
> +						   fd) < 0)
> +				return -1;
> +			perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
> +						 thread);
> +		}
>  	}
>  
>  	return 0;
> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
> index 163c560..4861e8c 100644
> --- a/tools/perf/util/evsel.h
> +++ b/tools/perf/util/evsel.h
> @@ -36,6 +36,9 @@ struct perf_sample_id {
>  	struct hlist_node 	node;
>  	u64		 	id;
>  	struct perf_evsel	*evsel;
> +	int			idx;
> +	int			cpu;
> +	pid_t			tid;
>  
>  	/* Holds total ID period value for PERF_SAMPLE_READ processing. */
>  	u64			period;
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 6702ac2..d70e37d 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
>  				  union perf_event *event,
>  				  struct perf_session *session);
>  
> +static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
> +				 union perf_event *event __maybe_unused,
> +				 struct perf_session *perf_session
> +				 __maybe_unused)
> +{
> +	dump_printf(": unhandled!\n");
> +	return 0;
> +}
> +
>  void perf_tool__fill_defaults(struct perf_tool *tool)
>  {
>  	if (tool->sample == NULL)
> @@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
>  		else
>  			tool->finished_round = process_finished_round_stub;
>  	}
> +	if (tool->id_index == NULL)
> +		tool->id_index = process_id_index_stub;
>  }
>   
>  static void swap_sample_id_all(union perf_event *event, void *data)
> @@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
>  	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap,
>  	[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
>  	[PERF_RECORD_HEADER_BUILD_ID]	  = NULL,
> +	[PERF_RECORD_ID_INDEX]		  = perf_event__all64_swap,
>  	[PERF_RECORD_HEADER_MAX]	  = NULL,
>  };
>  
> @@ -888,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;
> +}
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index a4be851..d8521ac 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -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 */
> diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
> index f116369..bb2708b 100644
> --- a/tools/perf/util/tool.h
> +++ b/tools/perf/util/tool.h
> @@ -39,7 +39,8 @@ struct perf_tool {
>  	event_attr_op	attr;
>  	event_op2	tracing_data;
>  	event_op2	finished_round,
> -			build_id;
> +			build_id,
> +			id_index;
>  	bool		ordered_events;
>  	bool		ordering_requires_timestamps;
>  };
> -- 
> 1.9.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 00/16] perf tools: Intel PT preparation continued
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (15 preceding siblings ...)
  2014-10-23 10:45 ` [PATCH 16/16] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built Adrian Hunter
@ 2014-10-23 21:11 ` Arnaldo Carvalho de Melo
  2014-10-23 23:43 ` Arnaldo Carvalho de Melo
  17 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-23 21:11 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

Em Thu, Oct 23, 2014 at 01:45:08PM +0300, Adrian Hunter escreveu:
> 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.

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
 
> 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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
@ 2014-10-23 21:42   ` Arnaldo Carvalho de Melo
  2014-10-24  6:02   ` Namhyung Kim
  2014-10-30  6:46   ` [tip:perf/core] " tip-bot for Adrian Hunter
  2 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-23 21:42 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

Em Thu, Oct 23, 2014 at 01:45:13PM +0300, Adrian Hunter escreveu:
> 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.

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:

> @@ -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
 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  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
> 
> diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
> index 5bbe1ff..9134c93 100644
> --- a/tools/perf/Makefile.perf
> +++ b/tools/perf/Makefile.perf
> @@ -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
> diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
> index 51c10ab..99e7021 100644
> --- a/tools/perf/util/comm.h
> +++ b/tools/perf/util/comm.h
> @@ -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;
>  };
> diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
> new file mode 100644
> index 0000000..53d0e75
> --- /dev/null
> +++ b/tools/perf/util/db-export.c
> @@ -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;
> +}
> diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
> new file mode 100644
> index 0000000..b3643e8
> --- /dev/null
> +++ b/tools/perf/util/db-export.h
> @@ -0,0 +1,86 @@
> +/*
> + * db-export.h: Support for exporting data suitable for import to a database
> + * Copyright (c) 2014, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + */
> +
> +#ifndef __PERF_DB_EXPORT_H
> +#define __PERF_DB_EXPORT_H
> +
> +#include <linux/types.h>
> +
> +struct perf_evsel;
> +struct machine;
> +struct thread;
> +struct comm;
> +struct dso;
> +struct perf_sample;
> +struct addr_location;
> +
> +struct export_sample {
> +	union perf_event	*event;
> +	struct perf_sample	*sample;
> +	struct perf_evsel	*evsel;
> +	struct thread		*thread;
> +	struct addr_location	*al;
> +	u64			db_id;
> +	u64			comm_db_id;
> +	u64			dso_db_id;
> +	u64			sym_db_id;
> +	u64			offset; /* ip offset from symbol start */
> +	u64			addr_dso_db_id;
> +	u64			addr_sym_db_id;
> +	u64			addr_offset; /* addr offset from symbol start */
> +};
> +
> +struct db_export {
> +	int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
> +	int (*export_machine)(struct db_export *dbe, struct machine *machine);
> +	int (*export_thread)(struct db_export *dbe, struct thread *thread,
> +			     u64 main_thread_db_id, struct machine *machine);
> +	int (*export_comm)(struct db_export *dbe, struct comm *comm);
> +	int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
> +				  struct comm *comm, struct thread *thread);
> +	int (*export_dso)(struct db_export *dbe, struct dso *dso,
> +			  struct machine *machine);
> +	int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
> +			     struct dso *dso);
> +	int (*export_sample)(struct db_export *dbe, struct export_sample *es);
> +	u64 evsel_last_db_id;
> +	u64 machine_last_db_id;
> +	u64 thread_last_db_id;
> +	u64 comm_last_db_id;
> +	u64 comm_thread_last_db_id;
> +	u64 dso_last_db_id;
> +	u64 symbol_last_db_id;
> +	u64 sample_last_db_id;
> +};
> +
> +int db_export__init(struct db_export *dbe);
> +void db_export__exit(struct db_export *dbe);
> +int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
> +int db_export__machine(struct db_export *dbe, struct machine *machine);
> +int db_export__thread(struct db_export *dbe, struct thread *thread,
> +		      struct machine *machine, struct comm *comm);
> +int db_export__comm(struct db_export *dbe, struct comm *comm,
> +		    struct thread *main_thread);
> +int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
> +			   struct thread *thread);
> +int db_export__dso(struct db_export *dbe, struct dso *dso,
> +		   struct machine *machine);
> +int db_export__symbol(struct db_export *dbe, struct symbol *sym,
> +		      struct dso *dso);
> +int db_export__sample(struct db_export *dbe, union perf_event *event,
> +		      struct perf_sample *sample, struct perf_evsel *evsel,
> +		      struct thread *thread, struct addr_location *al);
> +
> +#endif
> diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
> index acb651a..8463fc3 100644
> --- a/tools/perf/util/dso.h
> +++ b/tools/perf/util/dso.h
> @@ -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;
> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
> index 4861e8c..4777262 100644
> --- a/tools/perf/util/evsel.h
> +++ b/tools/perf/util/evsel.h
> @@ -95,6 +95,7 @@ struct perf_evsel {
>  	int			sample_read;
>  	struct perf_evsel	*leader;
>  	char			*group_name;
> +	u64			db_id;
>  };
>  
>  union u64_swap {
> diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
> index 2b651a7..4bc57e5 100644
> --- a/tools/perf/util/machine.h
> +++ b/tools/perf/util/machine.h
> @@ -40,6 +40,7 @@ struct machine {
>  	u64		  kernel_start;
>  	symbol_filter_t	  symbol_filter;
>  	pid_t		  *current_tid;
> +	u64		  db_id;
>  };
>  
>  static inline
> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> index eb2c19b..6f54ade 100644
> --- a/tools/perf/util/symbol.h
> +++ b/tools/perf/util/symbol.h
> @@ -73,6 +73,7 @@ struct symbol {
>  	struct rb_node	rb_node;
>  	u64		start;
>  	u64		end;
> +	u64		db_id;
>  	u16		namelen;
>  	u8		binding;
>  	bool		ignore;
> diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
> index a057820..a3ebb21 100644
> --- a/tools/perf/util/thread.h
> +++ b/tools/perf/util/thread.h
> @@ -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;
> -- 
> 1.9.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 00/16] perf tools: Intel PT preparation continued
  2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
                   ` (16 preceding siblings ...)
  2014-10-23 21:11 ` [PATCH 00/16] perf tools: Intel PT preparation continued Arnaldo Carvalho de Melo
@ 2014-10-23 23:43 ` Arnaldo Carvalho de Melo
  17 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-23 23:43 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

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

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

>       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:

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

Made some questions about these two as well:

>       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.

>       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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 01/16] perf tools: Add id index
  2014-10-23 21:08   ` Arnaldo Carvalho de Melo
@ 2014-10-24  5:10     ` Namhyung Kim
  2014-10-24  7:25     ` Adrian Hunter
  1 sibling, 0 replies; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24  5:10 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

On Thu, 23 Oct 2014 18:08:17 -0300, Arnaldo Carvalho de Melo wrote:
> Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
>> 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.

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

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-23 10:45 ` [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
@ 2014-10-24  5:15   ` Namhyung Kim
  2014-10-24 12:57     ` Arnaldo Carvalho de Melo
  2014-10-30  6:45   ` [tip:perf/core] perf pmu: Let pmu' s " tip-bot for Adrian Hunter
  1 sibling, 1 reply; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24  5:15 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
> perf list only lists PMUs with events.  Add a
> flag to cause a PMU to be also listed separately.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  tools/perf/util/pmu.c | 13 +++++++++++--
>  tools/perf/util/pmu.h |  1 +
>  2 files changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index e243ad9..91dca60 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
>  
>  	pmu = NULL;
>  	len = 0;
> -	while ((pmu = perf_pmu__scan(pmu)) != NULL)
> +	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
>  		list_for_each_entry(alias, &pmu->aliases, list)
>  			len++;
> +		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);

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


> +			j++;
> +		}
> +	}
>  	len = j;
>  	qsort(aliases, len, sizeof(char *), cmp_string);
>  	for (j = 0; j < len; j++) {
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index fe9dfbe..8092de7 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -18,6 +18,7 @@ struct perf_event_attr;
>  struct perf_pmu {
>  	char *name;
>  	__u32 type;
> +	bool selectable;

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


>  	struct perf_event_attr *default_config;
>  	struct cpu_map *cpus;
>  	struct list_head format;  /* HEAD struct perf_pmu_format -> list */

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()
  2014-10-23 10:45 ` [PATCH 03/16] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
@ 2014-10-24  5:22   ` Namhyung Kim
  2014-10-24 10:41     ` Adrian Hunter
  0 siblings, 1 reply; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24  5:22 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On Thu, 23 Oct 2014 13:45:11 +0300, Adrian Hunter wrote:
> Add a function to deliver synthesized events from
> within a session.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  tools/perf/util/session.c | 14 ++++++++++++++
>  tools/perf/util/session.h |  5 +++++
>  2 files changed, 19 insertions(+)
>
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index d70e37d..09635d1 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -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);
> +}

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


> +
>  static void event_swap(union perf_event *event, bool sample_id_all)
>  {
>  	perf_event__swap_op swap;
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index d8521ac..dc26ebf 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -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);

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
  2014-10-23 20:51   ` Arnaldo Carvalho de Melo
@ 2014-10-24  5:41   ` Namhyung Kim
  2014-10-29  9:03   ` Jiri Olsa
  2014-10-29  9:07   ` Jiri Olsa
  3 siblings, 0 replies; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24  5:41 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On Thu, 23 Oct 2014 13:45:12 +0300, Adrian Hunter wrote:
> +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;
> +			}
> +		}
> +	}
> +}

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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
  2014-10-23 21:42   ` Arnaldo Carvalho de Melo
@ 2014-10-24  6:02   ` Namhyung Kim
  2014-10-24  8:11     ` Adrian Hunter
  2014-10-30  6:46   ` [tip:perf/core] " tip-bot for Adrian Hunter
  2 siblings, 1 reply; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24  6:02 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On Thu, 23 Oct 2014 13:45:13 +0300, Adrian Hunter wrote:
> 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


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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 01/16] perf tools: Add id index
  2014-10-23 21:08   ` Arnaldo Carvalho de Melo
  2014-10-24  5:10     ` Namhyung Kim
@ 2014-10-24  7:25     ` Adrian Hunter
  2014-10-29  8:55       ` Jiri Olsa
  1 sibling, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24  7:25 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

On 24/10/14 00:08, Arnaldo Carvalho de Melo wrote:
> Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
>> 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.

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().

> 
> - Arnaldo
>  
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> ---
>>  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(-)
>>
>> diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
>> index de99ca1..046c719 100644
>> --- a/tools/perf/builtin-inject.c
>> +++ b/tools/perf/builtin-inject.c
>> @@ -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),
>> diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
>> index 4af6b27..bbf6705 100644
>> --- a/tools/perf/util/event.c
>> +++ b/tools/perf/util/event.c
>> @@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
>>  	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA",
>>  	[PERF_RECORD_HEADER_BUILD_ID]		= "BUILD_ID",
>>  	[PERF_RECORD_FINISHED_ROUND]		= "FINISHED_ROUND",
>> +	[PERF_RECORD_ID_INDEX]			= "ID_INDEX",
>>  };
>>  
>>  const char *perf_event__name(unsigned int id)
>> diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
>> index 5699e7e..c89518e 100644
>> --- a/tools/perf/util/event.h
>> +++ b/tools/perf/util/event.h
>> @@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */
>>  	PERF_RECORD_HEADER_TRACING_DATA		= 66,
>>  	PERF_RECORD_HEADER_BUILD_ID		= 67,
>>  	PERF_RECORD_FINISHED_ROUND		= 68,
>> +	PERF_RECORD_ID_INDEX			= 69,
>>  	PERF_RECORD_HEADER_MAX
>>  };
>>  
>> @@ -239,6 +240,19 @@ struct tracing_data_event {
>>  	u32 size;
>>  };
>>  
>> +struct id_index_entry {
>> +	u64 id;
>> +	u64 idx;
>> +	u64 cpu;
>> +	u64 tid;
>> +};
>> +
>> +struct id_index_event {
>> +	struct perf_event_header header;
>> +	u64 nr;
>> +	struct id_index_entry entries[0];
>> +};
>> +
>>  union perf_event {
>>  	struct perf_event_header	header;
>>  	struct mmap_event		mmap;
>> @@ -253,6 +267,7 @@ union perf_event {
>>  	struct event_type_event		event_type;
>>  	struct tracing_data_event	tracing_data;
>>  	struct build_id_event		build_id;
>> +	struct id_index_event		id_index;
>>  };
>>  
>>  void perf_event__print_totals(void);
>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>> index 3c9e77d..0babd39 100644
>> --- a/tools/perf/util/evlist.c
>> +++ b/tools/perf/util/evlist.c
>> @@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
>>  	return 0;
>>  }
>>  
>> +static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
>> +				     struct perf_evsel *evsel, int idx, int cpu,
>> +				     int thread)
>> +{
>> +	struct perf_sample_id *sid = SID(evsel, cpu, thread);
>> +	sid->idx = idx;
>> +	if (evlist->cpus && cpu >= 0)
>> +		sid->cpu = evlist->cpus->map[cpu];
>> +	else
>> +		sid->cpu = -1;
>> +	if (!evsel->system_wide && evlist->threads && thread >= 0)
>> +		sid->tid = evlist->threads->map[thread];
>> +	else
>> +		sid->tid = -1;
>> +}
>> +
>>  struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
>>  {
>>  	struct hlist_head *head;
>> @@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
>>  			return -1;
>>  		}
>>  
>> -		if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
>> -		    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
>> -			return -1;
>> +		if (evsel->attr.read_format & PERF_FORMAT_ID) {
>> +			if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
>> +						   fd) < 0)
>> +				return -1;
>> +			perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
>> +						 thread);
>> +		}
>>  	}
>>  
>>  	return 0;
>> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
>> index 163c560..4861e8c 100644
>> --- a/tools/perf/util/evsel.h
>> +++ b/tools/perf/util/evsel.h
>> @@ -36,6 +36,9 @@ struct perf_sample_id {
>>  	struct hlist_node 	node;
>>  	u64		 	id;
>>  	struct perf_evsel	*evsel;
>> +	int			idx;
>> +	int			cpu;
>> +	pid_t			tid;
>>  
>>  	/* Holds total ID period value for PERF_SAMPLE_READ processing. */
>>  	u64			period;
>> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>> index 6702ac2..d70e37d 100644
>> --- a/tools/perf/util/session.c
>> +++ b/tools/perf/util/session.c
>> @@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
>>  				  union perf_event *event,
>>  				  struct perf_session *session);
>>  
>> +static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
>> +				 union perf_event *event __maybe_unused,
>> +				 struct perf_session *perf_session
>> +				 __maybe_unused)
>> +{
>> +	dump_printf(": unhandled!\n");
>> +	return 0;
>> +}
>> +
>>  void perf_tool__fill_defaults(struct perf_tool *tool)
>>  {
>>  	if (tool->sample == NULL)
>> @@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
>>  		else
>>  			tool->finished_round = process_finished_round_stub;
>>  	}
>> +	if (tool->id_index == NULL)
>> +		tool->id_index = process_id_index_stub;
>>  }
>>   
>>  static void swap_sample_id_all(union perf_event *event, void *data)
>> @@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
>>  	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap,
>>  	[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
>>  	[PERF_RECORD_HEADER_BUILD_ID]	  = NULL,
>> +	[PERF_RECORD_ID_INDEX]		  = perf_event__all64_swap,
>>  	[PERF_RECORD_HEADER_MAX]	  = NULL,
>>  };
>>  
>> @@ -888,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;
>> +}
>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>> index a4be851..d8521ac 100644
>> --- a/tools/perf/util/session.h
>> +++ b/tools/perf/util/session.h
>> @@ -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 */
>> diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
>> index f116369..bb2708b 100644
>> --- a/tools/perf/util/tool.h
>> +++ b/tools/perf/util/tool.h
>> @@ -39,7 +39,8 @@ struct perf_tool {
>>  	event_attr_op	attr;
>>  	event_op2	tracing_data;
>>  	event_op2	finished_round,
>> -			build_id;
>> +			build_id,
>> +			id_index;
>>  	bool		ordered_events;
>>  	bool		ordering_requires_timestamps;
>>  };
>> -- 
>> 1.9.1
> 
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24  6:02   ` Namhyung Kim
@ 2014-10-24  8:11     ` Adrian Hunter
  2014-10-24 10:47       ` Adrian Hunter
  0 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24  8:11 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On 24/10/14 09:02, Namhyung Kim wrote:
> On Thu, 23 Oct 2014 13:45:13 +0300, Adrian Hunter wrote:
>> 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

Please explain what you mean by "comfortable".

> 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.

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


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 20:51   ` Arnaldo Carvalho de Melo
@ 2014-10-24  8:47     ` Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24  8:47 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian

On 23/10/14 23:51, Arnaldo Carvalho de Melo wrote:
> Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:
>> Add a thread stack for synthesizing call chains from call
>> and return events.
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> ---
>>  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
>>
>> diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
>> index 262916f..5bbe1ff 100644
>> --- a/tools/perf/Makefile.perf
>> +++ b/tools/perf/Makefile.perf
>> @@ -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
>> diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
>> index c89518e..e84f929 100644
>> --- a/tools/perf/util/event.h
>> +++ b/tools/perf/util/event.h
>> @@ -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;
>> diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
>> new file mode 100644
>> index 0000000..c1ca2a9
>> --- /dev/null
>> +++ b/tools/perf/util/thread-stack.c
>> @@ -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;
>> +}
>> +
>> +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.

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.

> 
>> +
>> +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;
>> +}
>> diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
>> new file mode 100644
>> index 0000000..c0ba4cf
>> --- /dev/null
>> +++ b/tools/perf/util/thread-stack.h
>> @@ -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
>> diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
>> index 2b7b2d9..4e3418d 100644
>> --- a/tools/perf/util/thread.c
>> +++ b/tools/perf/util/thread.c
>> @@ -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;
>> diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
>> index 8c75fa7..a057820 100644
>> --- a/tools/perf/util/thread.h
>> +++ b/tools/perf/util/thread.h
>> @@ -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;
>> -- 
>> 1.9.1
> 
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()
  2014-10-24  5:22   ` Namhyung Kim
@ 2014-10-24 10:41     ` Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24 10:41 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On 24/10/14 08:22, Namhyung Kim wrote:
> On Thu, 23 Oct 2014 13:45:11 +0300, Adrian Hunter wrote:
>> Add a function to deliver synthesized events from
>> within a session.
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> ---
>>  tools/perf/util/session.c | 14 ++++++++++++++
>>  tools/perf/util/session.h |  5 +++++
>>  2 files changed, 19 insertions(+)
>>
>> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>> index d70e37d..09635d1 100644
>> --- a/tools/perf/util/session.c
>> +++ b/tools/perf/util/session.c
>> @@ -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);
>> +}
> 
> 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.

> 
> Thanks,
> Namhyung
> 
> 
>> +
>>  static void event_swap(union perf_event *event, bool sample_id_all)
>>  {
>>  	perf_event__swap_op swap;
>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>> index d8521ac..dc26ebf 100644
>> --- a/tools/perf/util/session.h
>> +++ b/tools/perf/util/session.h
>> @@ -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);
> 
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24  8:11     ` Adrian Hunter
@ 2014-10-24 10:47       ` Adrian Hunter
  2014-10-24 12:26         ` Namhyung Kim
  0 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24 10:47 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On 24/10/14 11:11, Adrian Hunter wrote:
> On 24/10/14 09:02, Namhyung Kim wrote:
>> On Thu, 23 Oct 2014 13:45:13 +0300, Adrian Hunter wrote:
>>> 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
> 
> Please explain what you mean by "comfortable".

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

> 
>> 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.
> 
>>
>> Thanks,
>> Namhyung
>>
>>
>>>
>>> Exporting centres around the db_export__sample() function
>>> which exports the associated data structures if they have
>>> not yet been allocated a db_id.
>>
>>
> 
> 
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24 10:47       ` Adrian Hunter
@ 2014-10-24 12:26         ` Namhyung Kim
  2014-10-24 13:13           ` Adrian Hunter
  0 siblings, 1 reply; 55+ messages in thread
From: Namhyung Kim @ 2014-10-24 12:26 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

Hi Adrian,

On Fri, Oct 24, 2014 at 7:47 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 24/10/14 11:11, Adrian Hunter wrote:
>> On 24/10/14 09:02, Namhyung Kim wrote:
>>> 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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24  5:15   ` Namhyung Kim
@ 2014-10-24 12:57     ` Arnaldo Carvalho de Melo
  2014-10-24 13:03       ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 12:57 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
> On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
> > +		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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 12:57     ` Arnaldo Carvalho de Melo
@ 2014-10-24 13:03       ` Arnaldo Carvalho de Melo
  2014-10-24 13:21         ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 13:03 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
> > On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
> > > +		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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24 12:26         ` Namhyung Kim
@ 2014-10-24 13:13           ` Adrian Hunter
  2014-10-24 14:40             ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24 13:13 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Jiri Olsa, Paul Mackerras,
	Stephane Eranian

On 24/10/14 15:26, Namhyung Kim wrote:
> Hi Adrian,
> 
> On Fri, Oct 24, 2014 at 7:47 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> On 24/10/14 11:11, Adrian Hunter wrote:
>>> On 24/10/14 09:02, Namhyung Kim wrote:
>>>> 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.

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


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 13:03       ` Arnaldo Carvalho de Melo
@ 2014-10-24 13:21         ` Arnaldo Carvalho de Melo
  2014-10-24 14:36           ` Adrian Hunter
  0 siblings, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 13:21 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
> > Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
> > > On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
> > > > +		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

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

Acked-by tags welcome as always :-)

- Arnaldo


diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 91dca60..881b754 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -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)

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 13:21         ` Arnaldo Carvalho de Melo
@ 2014-10-24 14:36           ` Adrian Hunter
  2014-10-24 14:38             ` Adrian Hunter
  2014-10-24 14:45             ` Arnaldo Carvalho de Melo
  0 siblings, 2 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24 14:36 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Namhyung Kim
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Paul Mackerras, Stephane Eranian

On 24/10/2014 4:21 p.m., Arnaldo Carvalho de Melo wrote:
> Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
>>> Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
>>>> On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
>>>>> +		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
>
> The patch below should check everything and warn the user, even
> maintaining that void return...
>
> Acked-by tags welcome as always :-)
>
> - Arnaldo
>
>
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 91dca60..881b754 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -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.

>   	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)
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 14:36           ` Adrian Hunter
@ 2014-10-24 14:38             ` Adrian Hunter
  2014-10-24 14:45             ` Arnaldo Carvalho de Melo
  1 sibling, 0 replies; 55+ messages in thread
From: Adrian Hunter @ 2014-10-24 14:38 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Namhyung Kim
  Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
	Jiri Olsa, Paul Mackerras, Stephane Eranian

On 24/10/2014 5:36 p.m., Adrian Hunter wrote:
> On 24/10/2014 4:21 p.m., Arnaldo Carvalho de Melo wrote:
>> Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
>>> Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
>>>> Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
>>>>> On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
>>>>>> +        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
>>
>> The patch below should check everything and warn the user, even
>> maintaining that void return...
>>
>> Acked-by tags welcome as always :-)
>>
>> - Arnaldo
>>
>>
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index 91dca60..881b754 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -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.

>
>>       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)
>>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24 13:13           ` Adrian Hunter
@ 2014-10-24 14:40             ` Arnaldo Carvalho de Melo
  2014-10-24 14:41               ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 14:40 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Namhyung Kim, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:
> On 24/10/14 15:26, Namhyung Kim wrote:
> > Hi Adrian,
> > 
> > On Fri, Oct 24, 2014 at 7:47 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
> >> On 24/10/14 11:11, Adrian Hunter wrote:
> >>> On 24/10/14 09:02, Namhyung Kim wrote:
> >>>> 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.
> 
> 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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
  2014-10-24 14:40             ` Arnaldo Carvalho de Melo
@ 2014-10-24 14:41               ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 14:41 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Namhyung Kim, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 11:40:02AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:
> > On 24/10/14 15:26, Namhyung Kim wrote:
> > > Hi Adrian,
> > > 
> > > On Fri, Oct 24, 2014 at 7:47 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
> > >> On 24/10/14 11:11, Adrian Hunter wrote:
> > >>> On 24/10/14 09:02, Namhyung Kim wrote:
> > >>>> 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.
> > 
> > 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.

Oops, now with the patch:

diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index 99e7021..71c9c39 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -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);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 53d0e75..be128b0 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -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)
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 66c99cc..a316e4a 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -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];
 };
 
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 9459899..d3854c4 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -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 {
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 2e150a2..e8b7779 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -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
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 6f54ade..eb2c19b 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -73,7 +73,6 @@ struct symbol {
 	struct rb_node	rb_node;
 	u64		start;
 	u64		end;
-	u64		db_id;
 	u16		namelen;
 	u8		binding;
 	bool		ignore;

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 14:36           ` Adrian Hunter
  2014-10-24 14:38             ` Adrian Hunter
@ 2014-10-24 14:45             ` Arnaldo Carvalho de Melo
  2014-10-24 15:35               ` Arnaldo Carvalho de Melo
  1 sibling, 1 reply; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 14:45 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Namhyung Kim, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:
> On 24/10/2014 4:21 p.m., Arnaldo Carvalho de Melo wrote:
> >Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
> >>Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
> >>>Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
> >>>>On Thu, 23 Oct 2014 13:45:10 +0300, Adrian Hunter wrote:
> >>>>>+		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
> >
> >The patch below should check everything and warn the user, even
> >maintaining that void return...
> >
> >Acked-by tags welcome as always :-)
> >
> >- Arnaldo
> >
> >
> >diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> >index 91dca60..881b754 100644
> >--- a/tools/perf/util/pmu.c
> >+++ b/tools/perf/util/pmu.c
> >@@ -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...
 
> >  	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)
> >

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
  2014-10-24 14:45             ` Arnaldo Carvalho de Melo
@ 2014-10-24 15:35               ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-24 15:35 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Namhyung Kim, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Jiri Olsa, Paul Mackerras, Stephane Eranian

Em Fri, Oct 24, 2014 at 11:45:00AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:
> > >+	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
 
> 
> Oops, yeah, my bad, it should not have the !, fixing...
>  
> > >  	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)
> > >

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 01/16] perf tools: Add id index
  2014-10-24  7:25     ` Adrian Hunter
@ 2014-10-29  8:55       ` Jiri Olsa
  0 siblings, 0 replies; 55+ messages in thread
From: Jiri Olsa @ 2014-10-29  8:55 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
	Stephane Eranian

On Fri, Oct 24, 2014 at 10:25:02AM +0300, Adrian Hunter wrote:
> On 24/10/14 00:08, Arnaldo Carvalho de Melo wrote:
> > Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
> >> 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.
> 
> 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().

with above text in changelog:

Acked-by: Jiri Olsa <jolsa@kernel.org>

thanks,
jirka

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
  2014-10-23 20:51   ` Arnaldo Carvalho de Melo
  2014-10-24  5:41   ` Namhyung Kim
@ 2014-10-29  9:03   ` Jiri Olsa
  2014-10-29  9:07   ` Jiri Olsa
  3 siblings, 0 replies; 55+ messages in thread
From: Jiri Olsa @ 2014-10-29  9:03 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
	Stephane Eranian

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

SNIP

> +		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;

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

jirka

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
  2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
                     ` (2 preceding siblings ...)
  2014-10-29  9:03   ` Jiri Olsa
@ 2014-10-29  9:07   ` Jiri Olsa
  3 siblings, 0 replies; 55+ messages in thread
From: Jiri Olsa @ 2014-10-29  9:07 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
	Stephane Eranian

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

SNIP

>  };
>  
> +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)

PERF_FLAG_ prefix seems too generic,

should it be something like PERF_BRANCH_FLAG__* ?


jirka

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data
  2014-10-23 10:45 ` [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
@ 2014-10-29 13:23   ` Jiri Olsa
  2014-10-29 14:02     ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 55+ messages in thread
From: Jiri Olsa @ 2014-10-29 13:23 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, linux-kernel,
	David Ahern, Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
	Stephane Eranian

On Thu, Oct 23, 2014 at 01:45:18PM +0300, Adrian Hunter wrote:
> 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

> 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
>  tools/perf/util/thread-stack.h |  47 ++++
>  2 files changed, 590 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
> index c1ca2a9..444d132 100644
> --- a/tools/perf/util/thread-stack.c
> +++ b/tools/perf/util/thread-stack.c
> @@ -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;
> +}
> diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
> index c0ba4cf..5bca14e 100644
> --- a/tools/perf/util/thread-stack.h
> +++ b/tools/perf/util/thread-stack.h
> @@ -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
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data
  2014-10-29 13:23   ` Jiri Olsa
@ 2014-10-29 14:02     ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 55+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-10-29 14:02 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
	Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
	Stephane Eranian

Em Wed, Oct 29, 2014 at 02:23:50PM +0100, Jiri Olsa escreveu:
> On Thu, Oct 23, 2014 at 01:45:18PM +0300, Adrian Hunter wrote:
> > 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
 
> thanks,
> jirka
> 
> > 
> > Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> > ---
> >  tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
> >  tools/perf/util/thread-stack.h |  47 ++++
> >  2 files changed, 590 insertions(+), 4 deletions(-)
> > 
> > diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
> > index c1ca2a9..444d132 100644
> > --- a/tools/perf/util/thread-stack.c
> > +++ b/tools/perf/util/thread-stack.c
> > @@ -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;
> > +}
> > diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
> > index c0ba4cf..5bca14e 100644
> > --- a/tools/perf/util/thread-stack.h
> > +++ b/tools/perf/util/thread-stack.h
> > @@ -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
> > -- 
> > 1.9.1
> > 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf pmu: Let pmu' s with no events show up on perf list
  2014-10-23 10:45 ` [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
  2014-10-24  5:15   ` Namhyung Kim
@ 2014-10-30  6:45   ` tip-bot for Adrian Hunter
  1 sibling, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:45 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: namhyung, hpa, adrian.hunter, mingo, paulus, fweisbec, acme,
	linux-kernel, jolsa, dsahern, tglx, eranian, peterz

Commit-ID:  42634bc7a02ead59cf2d50e60d8b8f825de8a3b0
Gitweb:     http://git.kernel.org/tip/42634bc7a02ead59cf2d50e60d8b8f825de8a3b0
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:10 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-3-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/pmu.c | 13 +++++++++++--
 tools/perf/util/pmu.h |  1 +
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e243ad9..91dca60 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
 
 	pmu = NULL;
 	len = 0;
-	while ((pmu = perf_pmu__scan(pmu)) != NULL)
+	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
 		list_for_each_entry(alias, &pmu->aliases, list)
 			len++;
+		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++) {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index fe9dfbe..8092de7 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -18,6 +18,7 @@ struct perf_event_attr;
 struct perf_pmu {
 	char *name;
 	__u32 type;
+	bool selectable;
 	struct perf_event_attr *default_config;
 	struct cpu_map *cpus;
 	struct list_head format;  /* HEAD struct perf_pmu_format -> list */

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf tools: Build programs to copy 32-bit compatibility
  2014-10-23 10:45 ` [PATCH 14/16] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
@ 2014-10-30  6:45   ` tip-bot for Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:45 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, paulus, hpa, namhyung, tglx, adrian.hunter, dsahern,
	peterz, eranian, jolsa, linux-kernel, mingo

Commit-ID:  e477f3f01a89a8fd44031e7f2ba6ffcab037336c
Gitweb:     http://git.kernel.org/tip/e477f3f01a89a8fd44031e7f2ba6ffcab037336c
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 18:16:03 -0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>,
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-15-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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(-)

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 262916f..9c4ced0 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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)
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 58f6091..3ba2382 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -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
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch
index 4b06719..851cd01 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/perf/config/Makefile.arch
@@ -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
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile
index 72ab298..7c68ec7 100644
--- a/tools/perf/config/feature-checks/Makefile
+++ b/tools/perf/config/feature-checks/Makefile
@@ -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
 
 ###############################
diff --git a/arch/mips/boot/compressed/dummy.c b/tools/perf/config/feature-checks/test-compile.c
similarity index 100%
copy from arch/mips/boot/compressed/dummy.c
copy to tools/perf/config/feature-checks/test-compile.c
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c
new file mode 100644
index 0000000..764e254
--- /dev/null
+++ b/tools/perf/perf-read-vdso.c
@@ -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;
+}
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c
new file mode 100644
index 0000000..95ef1cf
--- /dev/null
+++ b/tools/perf/util/find-vdso-map.c
@@ -0,0 +1,30 @@
+static int find_vdso_map(void **start, void **end)
+{
+	FILE *maps;
+	char line[128];
+	int found = 0;
+
+	maps = fopen("/proc/self/maps", "r");
+	if (!maps) {
+		fprintf(stderr, "vdso: cannot open maps\n");
+		return -1;
+	}
+
+	while (!found && fgets(line, sizeof(line), maps)) {
+		int m = -1;
+
+		/* We care only about private r-x mappings. */
+		if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+				start, end, &m))
+			continue;
+		if (m < 0)
+			continue;
+
+		if (!strncmp(&line[m], VDSO__MAP_NAME,
+			     sizeof(VDSO__MAP_NAME) - 1))
+			found = 1;
+	}
+
+	fclose(maps);
+	return !found;
+}
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index adca693..f51390a 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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;

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf tools: Add support for 32-bit compatibility VDSOs
  2014-10-23 10:45 ` [PATCH 15/16] perf tools: Add support for 32-bit compatibility VDSOs Adrian Hunter
@ 2014-10-30  6:45   ` tip-bot for Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:45 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: mingo, hpa, dsahern, jolsa, peterz, fweisbec, linux-kernel,
	paulus, namhyung, adrian.hunter, acme, eranian, tglx

Commit-ID:  f6832e1720f5cc283703cfe9ccbfb46a3fb6f548
Gitweb:     http://git.kernel.org/tip/f6832e1720f5cc283703cfe9ccbfb46a3fb6f548
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:23 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-16-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/vdso.h |   4 +-
 2 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index f51390a..69daef6 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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);
 }
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index af9d692..d97da16 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -7,7 +7,9 @@
 
 #define VDSO__MAP_NAME "[vdso]"
 
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO    "[vdso]"
+#define DSO__NAME_VDSO32  "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
 
 static inline bool is_vdso_map(const char *filename)
 {

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
  2014-10-23 10:45 ` [PATCH 16/16] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built Adrian Hunter
@ 2014-10-30  6:46   ` tip-bot for Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:46 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: peterz, eranian, linux-kernel, jolsa, hpa, fweisbec, mingo,
	paulus, namhyung, tglx, acme, dsahern, adrian.hunter

Commit-ID:  46b1fa85ff5a2e03423770b3931b97266e8ac6cf
Gitweb:     http://git.kernel.org/tip/46b1fa85ff5a2e03423770b3931b97266e8ac6cf
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:24 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-17-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/config/Makefile |  8 ++++++--
 tools/perf/util/vdso.c     | 10 ++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 3ba2382..71264e4 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -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
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 69daef6..5c7dd79 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -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);

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf tools: Add facility to export data in database-friendly way
  2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
  2014-10-23 21:42   ` Arnaldo Carvalho de Melo
  2014-10-24  6:02   ` Namhyung Kim
@ 2014-10-30  6:46   ` tip-bot for Adrian Hunter
  2 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:46 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: fweisbec, adrian.hunter, jolsa, namhyung, paulus, acme,
	linux-kernel, peterz, eranian, dsahern, tglx, hpa, mingo

Commit-ID:  0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92
Gitweb:     http://git.kernel.org/tip/0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:13 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-6-git-send-email-adrian.hunter@intel.com
[ committer note: Stash db_id using symbol_conf.priv_size + symbol__priv() and foo->priv areas ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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(+)

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 9c4ced0..3caf7da 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index 51c10ab..71c9c39 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -12,6 +12,10 @@ struct comm {
 	u64 start;
 	struct list_head list;
 	bool exec;
+	union { /* Tool specific area */
+		void	*priv;
+		u64	db_id;
+	};
 };
 
 void comm__free(struct comm *comm);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
new file mode 100644
index 0000000..be128b0
--- /dev/null
+++ b/tools/perf/util/db-export.c
@@ -0,0 +1,270 @@
+/*
+ * db-export.c: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <errno.h>
+
+#include "evsel.h"
+#include "machine.h"
+#include "thread.h"
+#include "comm.h"
+#include "symbol.h"
+#include "event.h"
+#include "db-export.h"
+
+int db_export__init(struct db_export *dbe)
+{
+	memset(dbe, 0, sizeof(struct db_export));
+	return 0;
+}
+
+void db_export__exit(struct db_export *dbe __maybe_unused)
+{
+}
+
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+	if (evsel->db_id)
+		return 0;
+
+	evsel->db_id = ++dbe->evsel_last_db_id;
+
+	if (dbe->export_evsel)
+		return dbe->export_evsel(dbe, evsel);
+
+	return 0;
+}
+
+int db_export__machine(struct db_export *dbe, struct machine *machine)
+{
+	if (machine->db_id)
+		return 0;
+
+	machine->db_id = ++dbe->machine_last_db_id;
+
+	if (dbe->export_machine)
+		return dbe->export_machine(dbe, machine);
+
+	return 0;
+}
+
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+		      struct machine *machine, struct comm *comm)
+{
+	u64 main_thread_db_id = 0;
+	int err;
+
+	if (thread->db_id)
+		return 0;
+
+	thread->db_id = ++dbe->thread_last_db_id;
+
+	if (thread->pid_ != -1) {
+		struct thread *main_thread;
+
+		if (thread->pid_ == thread->tid) {
+			main_thread = thread;
+		} else {
+			main_thread = machine__findnew_thread(machine,
+							      thread->pid_,
+							      thread->pid_);
+			if (!main_thread)
+				return -ENOMEM;
+			err = db_export__thread(dbe, main_thread, machine,
+						comm);
+			if (err)
+				return err;
+			if (comm) {
+				err = db_export__comm_thread(dbe, comm, thread);
+				if (err)
+					return err;
+			}
+		}
+		main_thread_db_id = main_thread->db_id;
+	}
+
+	if (dbe->export_thread)
+		return dbe->export_thread(dbe, thread, main_thread_db_id,
+					  machine);
+
+	return 0;
+}
+
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+		    struct thread *main_thread)
+{
+	int err;
+
+	if (comm->db_id)
+		return 0;
+
+	comm->db_id = ++dbe->comm_last_db_id;
+
+	if (dbe->export_comm) {
+		err = dbe->export_comm(dbe, comm);
+		if (err)
+			return err;
+	}
+
+	return db_export__comm_thread(dbe, comm, main_thread);
+}
+
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+			   struct thread *thread)
+{
+	u64 db_id;
+
+	db_id = ++dbe->comm_thread_last_db_id;
+
+	if (dbe->export_comm_thread)
+		return dbe->export_comm_thread(dbe, db_id, comm, thread);
+
+	return 0;
+}
+
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+		   struct machine *machine)
+{
+	if (dso->db_id)
+		return 0;
+
+	dso->db_id = ++dbe->dso_last_db_id;
+
+	if (dbe->export_dso)
+		return dbe->export_dso(dbe, dso, machine);
+
+	return 0;
+}
+
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+		      struct dso *dso)
+{
+	u64 *sym_db_id = symbol__priv(sym);
+
+	if (*sym_db_id)
+		return 0;
+
+	*sym_db_id = ++dbe->symbol_last_db_id;
+
+	if (dbe->export_symbol)
+		return dbe->export_symbol(dbe, sym, dso);
+
+	return 0;
+}
+
+static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
+{
+	if (thread->pid_ == thread->tid)
+		return thread;
+
+	if (thread->pid_ == -1)
+		return NULL;
+
+	return machine__find_thread(machine, thread->pid_, thread->pid_);
+}
+
+static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
+			  u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
+{
+	int err;
+
+	if (al->map) {
+		struct dso *dso = al->map->dso;
+
+		err = db_export__dso(dbe, dso, al->machine);
+		if (err)
+			return err;
+		*dso_db_id = dso->db_id;
+
+		if (!al->sym) {
+			al->sym = symbol__new(al->addr, 0, 0, "unknown");
+			if (al->sym)
+				symbols__insert(&dso->symbols[al->map->type],
+						al->sym);
+		}
+
+		if (al->sym) {
+			u64 *db_id = symbol__priv(al->sym);
+
+			err = db_export__symbol(dbe, al->sym, dso);
+			if (err)
+				return err;
+			*sym_db_id = *db_id;
+			*offset = al->addr - al->sym->start;
+		}
+	}
+
+	return 0;
+}
+
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+		      struct perf_sample *sample, struct perf_evsel *evsel,
+		      struct thread *thread, struct addr_location *al)
+{
+	struct export_sample es = {
+		.event = event,
+		.sample = sample,
+		.evsel = evsel,
+		.thread = thread,
+		.al = al,
+	};
+	struct thread *main_thread;
+	struct comm *comm = NULL;
+	int err;
+
+	err = db_export__evsel(dbe, evsel);
+	if (err)
+		return err;
+
+	err = db_export__machine(dbe, al->machine);
+	if (err)
+		return err;
+
+	main_thread = get_main_thread(al->machine, thread);
+	if (main_thread)
+		comm = machine__thread_exec_comm(al->machine, main_thread);
+
+	err = db_export__thread(dbe, thread, al->machine, comm);
+	if (err)
+		return err;
+
+	if (comm) {
+		err = db_export__comm(dbe, comm, main_thread);
+		if (err)
+			return err;
+		es.comm_db_id = comm->db_id;
+	}
+
+	es.db_id = ++dbe->sample_last_db_id;
+
+	err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
+	if (err)
+		return err;
+
+	if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
+	    sample_addr_correlates_sym(&evsel->attr)) {
+		struct addr_location addr_al;
+
+		perf_event__preprocess_sample_addr(event, sample, thread, &addr_al);
+		err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
+				     &es.addr_sym_db_id, &es.addr_offset);
+		if (err)
+			return err;
+	}
+
+	if (dbe->export_sample)
+		return dbe->export_sample(dbe, &es);
+
+	return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
new file mode 100644
index 0000000..b3643e8
--- /dev/null
+++ b/tools/perf/util/db-export.h
@@ -0,0 +1,86 @@
+/*
+ * db-export.h: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_DB_EXPORT_H
+#define __PERF_DB_EXPORT_H
+
+#include <linux/types.h>
+
+struct perf_evsel;
+struct machine;
+struct thread;
+struct comm;
+struct dso;
+struct perf_sample;
+struct addr_location;
+
+struct export_sample {
+	union perf_event	*event;
+	struct perf_sample	*sample;
+	struct perf_evsel	*evsel;
+	struct thread		*thread;
+	struct addr_location	*al;
+	u64			db_id;
+	u64			comm_db_id;
+	u64			dso_db_id;
+	u64			sym_db_id;
+	u64			offset; /* ip offset from symbol start */
+	u64			addr_dso_db_id;
+	u64			addr_sym_db_id;
+	u64			addr_offset; /* addr offset from symbol start */
+};
+
+struct db_export {
+	int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+	int (*export_machine)(struct db_export *dbe, struct machine *machine);
+	int (*export_thread)(struct db_export *dbe, struct thread *thread,
+			     u64 main_thread_db_id, struct machine *machine);
+	int (*export_comm)(struct db_export *dbe, struct comm *comm);
+	int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
+				  struct comm *comm, struct thread *thread);
+	int (*export_dso)(struct db_export *dbe, struct dso *dso,
+			  struct machine *machine);
+	int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
+			     struct dso *dso);
+	int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+	u64 evsel_last_db_id;
+	u64 machine_last_db_id;
+	u64 thread_last_db_id;
+	u64 comm_last_db_id;
+	u64 comm_thread_last_db_id;
+	u64 dso_last_db_id;
+	u64 symbol_last_db_id;
+	u64 sample_last_db_id;
+};
+
+int db_export__init(struct db_export *dbe);
+void db_export__exit(struct db_export *dbe);
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__machine(struct db_export *dbe, struct machine *machine);
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+		      struct machine *machine, struct comm *comm);
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+		    struct thread *main_thread);
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+			   struct thread *thread);
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+		   struct machine *machine);
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+		      struct dso *dso);
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+		      struct perf_sample *sample, struct perf_evsel *evsel,
+		      struct thread *thread, struct addr_location *al);
+
+#endif
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 3c9b391..a316e4a 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -139,6 +139,11 @@ struct dso {
 		struct list_head open_entry;
 	} data;
 
+	union { /* Tool specific area */
+		void	 *priv;
+		u64	 db_id;
+	};
+
 	char		 name[0];
 };
 
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 163c560..d3854c4 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -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;
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 88ec74e..e8b7779 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -40,6 +40,10 @@ struct machine {
 	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
+	union { /* Tool specific area */
+		void	  *priv;
+		u64	  db_id;
+	};
 };
 
 static inline
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 6ef9fe6..d34cf5c 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -23,6 +23,7 @@ struct thread {
 	bool			dead; /* if set thread has exited */
 	struct list_head	comm_list;
 	int			comm_len;
+	u64			db_id;
 
 	void			*priv;
 };

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf scripting python: Extend interface to export data in a database-friendly way
  2014-10-23 10:45 ` [PATCH 06/16] perf tools: Extend Python script interface to export data in a " Adrian Hunter
@ 2014-10-30  6:47   ` tip-bot for Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:47 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: adrian.hunter, namhyung, eranian, peterz, dsahern, jolsa, mingo,
	paulus, linux-kernel, fweisbec, hpa, tglx, acme

Commit-ID:  df919b400ad3f9e6aac392ce421d710207abf9be
Gitweb:     http://git.kernel.org/tip/df919b400ad3f9e6aac392ce421d710207abf9be
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:14 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-7-git-send-email-adrian.hunter@intel.com
[ Reserve space for per symbol db_id space when perf_db_export_mode is on ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 .../util/scripting-engines/trace-event-python.c    | 286 ++++++++++++++++++++-
 1 file changed, 284 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 25e5a23..2fd7ee8 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 #include <errno.h>
 
 #include "../../perf.h"
@@ -33,6 +34,9 @@
 #include "../util.h"
 #include "../event.h"
 #include "../thread.h"
+#include "../comm.h"
+#include "../machine.h"
+#include "../db-export.h"
 #include "../trace-event.h"
 #include "../machine.h"
 
@@ -53,6 +57,21 @@ static int zero_flag_atom;
 
 static PyObject *main_module, *main_dict;
 
+struct tables {
+	struct db_export	dbe;
+	PyObject		*evsel_handler;
+	PyObject		*machine_handler;
+	PyObject		*thread_handler;
+	PyObject		*comm_handler;
+	PyObject		*comm_thread_handler;
+	PyObject		*dso_handler;
+	PyObject		*symbol_handler;
+	PyObject		*sample_handler;
+	bool			db_export_mode;
+};
+
+static struct tables tables_global;
+
 static void handler_call_die(const char *handler_name) NORETURN;
 static void handler_call_die(const char *handler_name)
 {
@@ -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();

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [tip:perf/core] perf script: Add Python script to export to postgresql
  2014-10-23 10:45 ` [PATCH 07/16] perf tools: Add Python script to export to postgresql Adrian Hunter
@ 2014-10-30  6:47   ` tip-bot for Adrian Hunter
  0 siblings, 0 replies; 55+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-10-30  6:47 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, linux-kernel, dsahern, eranian, hpa, paulus, fweisbec,
	mingo, jolsa, adrian.hunter, namhyung, peterz, tglx

Commit-ID:  2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b
Gitweb:     http://git.kernel.org/tip/2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b
Author:     Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Thu, 23 Oct 2014 13:45:15 +0300
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-8-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 .../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(+)

diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record
new file mode 100644
index 0000000..221d66e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-record
@@ -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 $@
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
new file mode 100644
index 0000000..a8fdd15
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -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
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
new file mode 100644
index 0000000..d8f6df0
--- /dev/null
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -0,0 +1,360 @@
+# export-to-postgresql.py: export perf data to a postgresql 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.
+
+import os
+import sys
+import struct
+import datetime
+
+from PySide.QtSql import *
+
+# Need to access PostgreSQL C library directly to use COPY FROM STDIN
+from ctypes import *
+libpq = CDLL("libpq.so.5")
+PQconnectdb = libpq.PQconnectdb
+PQconnectdb.restype = c_void_p
+PQfinish = libpq.PQfinish
+PQstatus = libpq.PQstatus
+PQexec = libpq.PQexec
+PQexec.restype = c_void_p
+PQresultStatus = libpq.PQresultStatus
+PQputCopyData = libpq.PQputCopyData
+PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ]
+PQputCopyEnd = libpq.PQputCopyEnd
+PQputCopyEnd.argtypes = [ c_void_p, c_void_p ]
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+	'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+# These perf imports are not used at present
+#from perf_trace_context import *
+#from Core import *
+
+perf_db_export_mode = True
+
+def usage():
+	print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]"
+	print >> sys.stderr, "where:	columns		'all' or 'branches'"
+	raise Exception("Too few arguments")
+
+if (len(sys.argv) < 2):
+	usage()
+
+dbname = sys.argv[1]
+
+if (len(sys.argv) >= 3):
+	columns = sys.argv[2]
+else:
+	columns = "all"
+
+if columns not in ("all", "branches"):
+	usage()
+
+branches = (columns == "branches")
+
+output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
+os.mkdir(output_dir_name)
+
+def do_query(q, s):
+	if (q.exec_(s)):
+		return
+	raise Exception("Query failed: " + q.lastError().text())
+
+print datetime.datetime.today(), "Creating database..."
+
+db = QSqlDatabase.addDatabase('QPSQL')
+query = QSqlQuery(db)
+db.setDatabaseName('postgres')
+db.open()
+try:
+	do_query(query, 'CREATE DATABASE ' + dbname)
+except:
+	os.rmdir(output_dir_name)
+	raise
+query.finish()
+query.clear()
+db.close()
+
+db.setDatabaseName(dbname)
+db.open()
+
+query = QSqlQuery(db)
+do_query(query, 'SET client_min_messages TO WARNING')
+
+do_query(query, 'CREATE TABLE selected_events ('
+		'id		bigint		NOT NULL,'
+		'name		varchar(80))')
+do_query(query, 'CREATE TABLE machines ('
+		'id		bigint		NOT NULL,'
+		'pid		integer,'
+		'root_dir 	varchar(4096))')
+do_query(query, 'CREATE TABLE threads ('
+		'id		bigint		NOT NULL,'
+		'machine_id	bigint,'
+		'process_id	bigint,'
+		'pid		integer,'
+		'tid		integer)')
+do_query(query, 'CREATE TABLE comms ('
+		'id		bigint		NOT NULL,'
+		'comm		varchar(16))')
+do_query(query, 'CREATE TABLE comm_threads ('
+		'id		bigint		NOT NULL,'
+		'comm_id	bigint,'
+		'thread_id	bigint)')
+do_query(query, 'CREATE TABLE dsos ('
+		'id		bigint		NOT NULL,'
+		'machine_id	bigint,'
+		'short_name	varchar(256),'
+		'long_name	varchar(4096),'
+		'build_id	varchar(64))')
+do_query(query, 'CREATE TABLE symbols ('
+		'id		bigint		NOT NULL,'
+		'dso_id		bigint,'
+		'sym_start	bigint,'
+		'sym_end	bigint,'
+		'binding	integer,'
+		'name		varchar(2048))')
+if branches:
+	do_query(query, 'CREATE TABLE samples ('
+		'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)')
+else:
+	do_query(query, 'CREATE TABLE samples ('
+		'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)')
+
+do_query(query, 'CREATE VIEW samples_view AS '
+	'SELECT '
+		'id,'
+		'time,'
+		'cpu,'
+		'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+		'(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
+		'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+		'(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
+		'to_hex(ip) AS ip_hex,'
+		'(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
+		'sym_offset,'
+		'(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
+		'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'
+	' FROM samples')
+
+
+file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0)
+file_trailer = "\377\377"
+
+def open_output_file(file_name):
+	path_name = output_dir_name + "/" + file_name
+	file = open(path_name, "w+")
+	file.write(file_header)
+	return file
+
+def close_output_file(file):
+	file.write(file_trailer)
+	file.close()
+
+def copy_output_file_direct(file, table_name):
+	close_output_file(file)
+	sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')"
+	do_query(query, sql)
+
+# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
+def copy_output_file(file, table_name):
+	conn = PQconnectdb("dbname = " + dbname)
+	if (PQstatus(conn)):
+		raise Exception("COPY FROM STDIN PQconnectdb failed")
+	file.write(file_trailer)
+	file.seek(0)
+	sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
+	res = PQexec(conn, sql)
+	if (PQresultStatus(res) != 4):
+		raise Exception("COPY FROM STDIN PQexec failed")
+	data = file.read(65536)
+	while (len(data)):
+		ret = PQputCopyData(conn, data, len(data))
+		if (ret != 1):
+			raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret))
+		data = file.read(65536)
+	ret = PQputCopyEnd(conn, None)
+	if (ret != 1):
+		raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret))
+	PQfinish(conn)
+
+def remove_output_file(file):
+	name = file.name
+	file.close()
+	os.unlink(name)
+
+evsel_file		= open_output_file("evsel_table.bin")
+machine_file		= open_output_file("machine_table.bin")
+thread_file		= open_output_file("thread_table.bin")
+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")
+sample_file		= open_output_file("sample_table.bin")
+
+def trace_begin():
+	print datetime.datetime.today(), "Writing to intermediate files..."
+	# id == 0 means unknown.  It is easier to create records for them than replace the zeroes with NULLs
+	evsel_table(0, "unknown")
+	machine_table(0, 0, "unknown")
+	thread_table(0, 0, 0, -1, -1)
+	comm_table(0, "unknown")
+	dso_table(0, 0, "unknown", "unknown", "")
+	symbol_table(0, 0, 0, 0, 0, "unknown")
+
+unhandled_count = 0
+
+def trace_end():
+	print datetime.datetime.today(), "Copying to database..."
+	copy_output_file(evsel_file,		"selected_events")
+	copy_output_file(machine_file,		"machines")
+	copy_output_file(thread_file,		"threads")
+	copy_output_file(comm_file,		"comms")
+	copy_output_file(comm_thread_file,	"comm_threads")
+	copy_output_file(dso_file,		"dsos")
+	copy_output_file(symbol_file,		"symbols")
+	copy_output_file(sample_file,		"samples")
+
+	print datetime.datetime.today(), "Removing intermediate files..."
+	remove_output_file(evsel_file)
+	remove_output_file(machine_file)
+	remove_output_file(thread_file)
+	remove_output_file(comm_file)
+	remove_output_file(comm_thread_file)
+	remove_output_file(dso_file)
+	remove_output_file(symbol_file)
+	remove_output_file(sample_file)
+	os.rmdir(output_dir_name)
+	print datetime.datetime.today(), "Adding primary keys"
+	do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE machines        ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE threads         ADD PRIMARY KEY (id)')
+	do_query(query, 'ALTER TABLE comms           ADD PRIMARY KEY (id)')
+	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 samples         ADD PRIMARY KEY (id)')
+
+	print datetime.datetime.today(), "Adding foreign keys"
+	do_query(query, 'ALTER TABLE threads '
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id),'
+					'ADD CONSTRAINT processfk  FOREIGN KEY (process_id)   REFERENCES threads    (id)')
+	do_query(query, 'ALTER TABLE comm_threads '
+					'ADD CONSTRAINT commfk     FOREIGN KEY (comm_id)      REFERENCES comms      (id),'
+					'ADD CONSTRAINT threadfk   FOREIGN KEY (thread_id)    REFERENCES threads    (id)')
+	do_query(query, 'ALTER TABLE dsos '
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id)')
+	do_query(query, 'ALTER TABLE symbols '
+					'ADD CONSTRAINT dsofk      FOREIGN KEY (dso_id)       REFERENCES dsos       (id)')
+	do_query(query, 'ALTER TABLE samples '
+					'ADD CONSTRAINT evselfk    FOREIGN KEY (evsel_id)     REFERENCES selected_events (id),'
+					'ADD CONSTRAINT machinefk  FOREIGN KEY (machine_id)   REFERENCES machines   (id),'
+					'ADD CONSTRAINT threadfk   FOREIGN KEY (thread_id)    REFERENCES threads    (id),'
+					'ADD CONSTRAINT commfk     FOREIGN KEY (comm_id)      REFERENCES comms      (id),'
+					'ADD CONSTRAINT dsofk      FOREIGN KEY (dso_id)       REFERENCES dsos       (id),'
+					'ADD CONSTRAINT symbolfk   FOREIGN KEY (symbol_id)    REFERENCES symbols    (id),'
+					'ADD CONSTRAINT todsofk    FOREIGN KEY (to_dso_id)    REFERENCES dsos       (id),'
+					'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols    (id)')
+
+	if (unhandled_count):
+		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
+	print datetime.datetime.today(), "Done"
+
+def trace_unhandled(event_name, context, event_fields_dict):
+	global unhandled_count
+	unhandled_count += 1
+
+def sched__sched_switch(*x):
+	pass
+
+def evsel_table(evsel_id, evsel_name, *x):
+	n = len(evsel_name)
+	fmt = "!hiqi" + str(n) + "s"
+	value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
+	evsel_file.write(value)
+
+def machine_table(machine_id, pid, root_dir, *x):
+	n = len(root_dir)
+	fmt = "!hiqiii" + str(n) + "s"
+	value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
+	machine_file.write(value)
+
+def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
+	value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
+	thread_file.write(value)
+
+def comm_table(comm_id, comm_str, *x):
+	n = len(comm_str)
+	fmt = "!hiqi" + str(n) + "s"
+	value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
+	comm_file.write(value)
+
+def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
+	fmt = "!hiqiqiq"
+	value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id)
+	comm_thread_file.write(value)
+
+def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
+	n1 = len(short_name)
+	n2 = len(long_name)
+	n3 = len(build_id)
+	fmt = "!hiqiqi" + str(n1) + "si"  + str(n2) + "si" + str(n3) + "s"
+	value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id)
+	dso_file.write(value)
+
+def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
+	n = len(symbol_name)
+	fmt = "!hiqiqiqiqiii" + str(n) + "s"
+	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):
+	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)
+	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)
+	sample_file.write(value)

^ permalink raw reply related	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2014-10-30  6:48 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-23 10:45 [PATCH 00/16] perf tools: Intel PT preparation continued Adrian Hunter
2014-10-23 10:45 ` [PATCH 01/16] perf tools: Add id index Adrian Hunter
2014-10-23 21:08   ` Arnaldo Carvalho de Melo
2014-10-24  5:10     ` Namhyung Kim
2014-10-24  7:25     ` Adrian Hunter
2014-10-29  8:55       ` Jiri Olsa
2014-10-23 10:45 ` [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
2014-10-24  5:15   ` Namhyung Kim
2014-10-24 12:57     ` Arnaldo Carvalho de Melo
2014-10-24 13:03       ` Arnaldo Carvalho de Melo
2014-10-24 13:21         ` Arnaldo Carvalho de Melo
2014-10-24 14:36           ` Adrian Hunter
2014-10-24 14:38             ` Adrian Hunter
2014-10-24 14:45             ` Arnaldo Carvalho de Melo
2014-10-24 15:35               ` Arnaldo Carvalho de Melo
2014-10-30  6:45   ` [tip:perf/core] perf pmu: Let pmu' s " tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 03/16] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
2014-10-24  5:22   ` Namhyung Kim
2014-10-24 10:41     ` Adrian Hunter
2014-10-23 10:45 ` [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
2014-10-23 20:51   ` Arnaldo Carvalho de Melo
2014-10-24  8:47     ` Adrian Hunter
2014-10-24  5:41   ` Namhyung Kim
2014-10-29  9:03   ` Jiri Olsa
2014-10-29  9:07   ` Jiri Olsa
2014-10-23 10:45 ` [PATCH 05/16] perf tools: Add facility to export data in database-friendly way Adrian Hunter
2014-10-23 21:42   ` Arnaldo Carvalho de Melo
2014-10-24  6:02   ` Namhyung Kim
2014-10-24  8:11     ` Adrian Hunter
2014-10-24 10:47       ` Adrian Hunter
2014-10-24 12:26         ` Namhyung Kim
2014-10-24 13:13           ` Adrian Hunter
2014-10-24 14:40             ` Arnaldo Carvalho de Melo
2014-10-24 14:41               ` Arnaldo Carvalho de Melo
2014-10-30  6:46   ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 06/16] perf tools: Extend Python script interface to export data in a " Adrian Hunter
2014-10-30  6:47   ` [tip:perf/core] perf scripting python: Extend " tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 07/16] perf tools: Add Python script to export to postgresql Adrian Hunter
2014-10-30  6:47   ` [tip:perf/core] perf script: " tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 08/16] perf tools: Add branch type to db export Adrian Hunter
2014-10-23 10:45 ` [PATCH 09/16] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
2014-10-23 10:45 ` [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
2014-10-29 13:23   ` Jiri Olsa
2014-10-29 14:02     ` Arnaldo Carvalho de Melo
2014-10-23 10:45 ` [PATCH 11/16] perf tools: Add call information to the database export API Adrian Hunter
2014-10-23 10:45 ` [PATCH 12/16] perf tools: Add call information to Python export Adrian Hunter
2014-10-23 10:45 ` [PATCH 13/16] perf tools: Defer export of comms that were not 'set' Adrian Hunter
2014-10-23 10:45 ` [PATCH 14/16] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
2014-10-30  6:45   ` [tip:perf/core] perf tools: Build programs to copy 32-bit compatibility tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 15/16] perf tools: Add support for 32-bit compatibility VDSOs Adrian Hunter
2014-10-30  6:45   ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-10-23 10:45 ` [PATCH 16/16] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built Adrian Hunter
2014-10-30  6:46   ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-10-23 21:11 ` [PATCH 00/16] perf tools: Intel PT preparation continued Arnaldo Carvalho de Melo
2014-10-23 23:43 ` Arnaldo Carvalho de Melo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.