perf tools: Intel PT preparation continued
Subsystems (2)
Adrian Hunter
reviewercover perf tools: Intel PT preparation continued
Hi
Here are the remaining Intel PT preparation patches. They are all patches that have been posted before, although "perf tools:
Do not attempt to run perf-read-vdso32 if it wasn't built" wasn't sent as a separate email.
With regard to "32-bit compatibility VDSOs", you may recall that Ingo had a problem getting "sh: perf-read-vdso32: command not found". That message is inevitable when using popen to run perf-read-vdso32 and perf-read-vdso32 cannot be found. At least with the patch "perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built" one reason for the message is eliminated.
It would be nice to target 3.19 for Intel PT, but there are still the real patches of which there are about 48.
Adrian Hunter (16):
perf tools: Add id index
perf pmu: Let pmu's with no events show up on perf list
perf session: Add perf_session__deliver_synth_event()
perf tools: Add a thread stack for synthesizing call chains
perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Add branch_type and in_tx to Python export
perf tools: Enhance the thread stack to output call/return data
perf tools: Add call information to the database export API
perf tools: Add call information to Python export
perf tools: Defer export of comms that were not 'set'
perf tools: Build programs to copy 32-bit compatibility VDSOs
perf tools: Add support for 32-bit compatibility VDSOs
perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
tools/perf/Makefile.perf | 42 +- tools/perf/builtin-inject.c | 1 + tools/perf/config/Makefile | 29 +- tools/perf/config/Makefile.arch | 8 + tools/perf/config/feature-checks/Makefile | 10 +- tools/perf/config/feature-checks/test-compile.c | 4 + tools/perf/perf-read-vdso.c | 34 + .../scripts/python/bin/export-to-postgresql-record | 8 + .../scripts/python/bin/export-to-postgresql-report | 29 + tools/perf/scripts/python/export-to-postgresql.py | 444 +++++++++++++ tools/perf/util/comm.h | 1 + tools/perf/util/db-export.c | 426 +++++++++++++ tools/perf/util/db-export.h | 107 ++++ tools/perf/util/dso.h | 1 + tools/perf/util/event.c | 1 + tools/perf/util/event.h | 41 ++ tools/perf/util/evlist.c | 26 +- tools/perf/util/evsel.h | 4 + tools/perf/util/find-vdso-map.c | 30 + tools/perf/util/machine.h | 1 + tools/perf/util/pmu.c | 13 +- tools/perf/util/pmu.h | 1 + .../util/scripting-engines/trace-event-python.c | 395 +++++++++++- tools/perf/util/session.c | 136 ++++ tools/perf/util/session.h | 15 + tools/perf/util/symbol.h | 1 + tools/perf/util/thread-stack.c | 690 +++++++++++++++++++++ tools/perf/util/thread-stack.h | 79 +++ tools/perf/util/thread.c | 3 + tools/perf/util/thread.h | 4 + tools/perf/util/tool.h | 3 +- tools/perf/util/vdso.c | 217 ++++++- tools/perf/util/vdso.h | 4 +- 33 files changed, 2760 insertions(+), 48 deletions(-) create mode 100644 tools/perf/config/feature-checks/test-compile.c create mode 100644 tools/perf/perf-read-vdso.c create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report create mode 100644 tools/perf/scripts/python/export-to-postgresql.py create mode 100644 tools/perf/util/db-export.c create mode 100644 tools/perf/util/db-export.h create mode 100644 tools/perf/util/find-vdso-map.c create mode 100644 tools/perf/util/thread-stack.c create mode 100644 tools/perf/util/thread-stack.h
Regards
Adrian
Adrian Hunter
reviewerPATCH 1/16 perf tools: Add id index
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.
--- tools/perf/builtin-inject.c | 1 + tools/perf/util/event.c | 1 + tools/perf/util/event.h | 15 ++++++ tools/perf/util/evlist.c | 26 ++++++++-- tools/perf/util/evsel.h | 3 ++ tools/perf/util/session.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/session.h | 10 ++++ tools/perf/util/tool.h | 3 +- 8 files changed, 177 insertions(+), 4 deletions(-)
tools/perf/builtin-inject.c +1 -0
@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) .tracing_data = perf_event__repipe_op2_synth, .finished_round = perf_event__repipe_op2_synth, .build_id = perf_event__repipe_op2_synth, + .id_index = perf_event__repipe_op2_synth, }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples),
tools/perf/util/event.c +1 -0
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", + [PERF_RECORD_ID_INDEX] = "ID_INDEX", };const char *perf_event__name (unsigned int id)
tools/perf/util/event.h +15 -0
@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */PERF_RECORD_HEADER_TRACING_DATA =66 ,PERF_RECORD_HEADER_BUILD_ID =67 ,PERF_RECORD_FINISHED_ROUND =68 , +PERF_RECORD_ID_INDEX =69 , PERF_RECORD_HEADER_MAX };
@@ -239,6 +240,19 @@ struct tracing_data_event {u32 size; }; +struct id_index_entry { +u64 id; +u64 idx; +u64 cpu; +u64 tid; +}; + +struct id_index_event { +struct perf_event_header header; +u64 nr; +struct id_index_entry entries[0 ]; +}; +union perf_event {struct perf_event_header header;struct mmap_event mmap;
@@ -253,6 +267,7 @@ union perf_event {struct event_type_event event_type;struct tracing_data_event tracing_data;struct build_id_event build_id; +struct id_index_event id_index; };void perf_event__print_totals (void );
tools/perf/util/evlist.c +23 -3
@@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,return 0 ; } +static void perf_evlist__set_sid_idx (struct perf_evlist *evlist, +struct perf_evsel *evsel,int idx,int cpu, +int thread) +{ +struct perf_sample_id *sid =SID (evsel, cpu, thread); + sid->idx = idx; +if (evlist->cpus && cpu >=0 ) + sid->cpu = evlist->cpus->map[cpu]; +else + sid->cpu =-1 ; +if (!evsel->system_wide && evlist->threads && thread >=0 ) + sid->tid = evlist->threads->map[thread]; +else + sid->tid =-1 ; +} +struct perf_sample_id *perf_evlist__id2sid (struct perf_evlist *evlist,u64 id) {struct hlist_head *head;
@@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,return -1 ; } -if ((evsel->attr.read_format &PERF_FORMAT_ID ) && -perf_evlist__id_add_fd (evlist, evsel, cpu, thread, fd) <0 ) -return -1 ; +if (evsel->attr.read_format &PERF_FORMAT_ID ) { +if (perf_evlist__id_add_fd (evlist, evsel, cpu, thread, + fd) <0 ) +return -1 ; +perf_evlist__set_sid_idx (evlist, evsel, idx, cpu, + thread); + } }return 0 ;
tools/perf/util/evsel.h +3 -0
@@ -36,6 +36,9 @@ struct perf_sample_id {struct hlist_node node;u64 id;struct perf_evsel *evsel; +int idx; +int cpu; +pid_t tid;/* Holds total ID period value for PERF_SAMPLE_READ processing. */ u64 period;
tools/perf/util/session.c +122 -0
149 lines not on this page · raw message
tools/perf/util/session.h +10 -0
@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,extern volatile int session_done;#define session_done () ACCESS_ONCE(session_done) + +int perf_event__process_id_index (struct perf_tool *tool, +union perf_event *event, +struct perf_session *session); + +int perf_event__synthesize_id_index (struct perf_tool *tool, +perf_event__handler_t process, +struct perf_evlist *evlist, +struct machine *machine); +#endif /* __PERF_SESSION_H */
tools/perf/util/tool.h +2 -1
@@ -39,7 +39,8 @@ struct perf_tool {event_attr_op attr;event_op2 tracing_data;event_op2 finished_round, - build_id; + build_id, + id_index;bool ordered_events;bool ordering_requires_timestamps; };
Signature · 1 line
1.9.1
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 01/16] perf tools: Add id index
Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
Quoted 5 lines
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.
This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...
Can you try to explain like to a seven year old?
Sigh.
'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.
- Arnaldo
Quoted 327 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/builtin-inject.c | 1 + tools/perf/util/event.c | 1 + tools/perf/util/event.h | 15 ++++++ tools/perf/util/evlist.c | 26 ++++++++-- tools/perf/util/evsel.h | 3 ++ tools/perf/util/session.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/session.h | 10 ++++ tools/perf/util/tool.h | 3 +- 8 files changed, 177 insertions(+), 4 deletions(-)tools/perf/builtin-inject.c +1
-0@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) .tracing_data = perf_event__repipe_op2_synth, .finished_round = perf_event__repipe_op2_synth, .build_id = perf_event__repipe_op2_synth, + .id_index = perf_event__repipe_op2_synth, }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples),tools/perf/util/event.c +1
-0@@ -28,6 +28,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", + [PERF_RECORD_ID_INDEX] = "ID_INDEX", };const char *perf_event__name (unsigned int id)tools/perf/util/event.h +15
-0@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */PERF_RECORD_HEADER_TRACING_DATA =66 ,PERF_RECORD_HEADER_BUILD_ID =67 ,PERF_RECORD_FINISHED_ROUND =68 , +PERF_RECORD_ID_INDEX =69 , PERF_RECORD_HEADER_MAX };@@ -239,6 +240,19 @@ struct tracing_data_event {u32 size; }; +struct id_index_entry { +u64 id; +u64 idx; +u64 cpu; +u64 tid; +}; + +struct id_index_event { +struct perf_event_header header; +u64 nr; +struct id_index_entry entries[0 ]; +}; +union perf_event {struct perf_event_header header;struct mmap_event mmap;@@ -253,6 +267,7 @@ union perf_event {struct event_type_event event_type;struct tracing_data_event tracing_data;struct build_id_event build_id; +struct id_index_event id_index; };void perf_event__print_totals (void );tools/perf/util/evlist.c +23
-3@@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,return 0 ; } +static void perf_evlist__set_sid_idx (struct perf_evlist *evlist, +struct perf_evsel *evsel,int idx,int cpu, +int thread) +{ +struct perf_sample_id *sid =SID (evsel, cpu, thread); + sid->idx = idx; +if (evlist->cpus && cpu >=0 ) + sid->cpu = evlist->cpus->map[cpu]; +else + sid->cpu =-1 ; +if (!evsel->system_wide && evlist->threads && thread >=0 ) + sid->tid = evlist->threads->map[thread]; +else + sid->tid =-1 ; +} +struct perf_sample_id *perf_evlist__id2sid (struct perf_evlist *evlist,u64 id) {struct hlist_head *head;@@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,return -1 ; } -if ((evsel->attr.read_format &PERF_FORMAT_ID ) && -perf_evlist__id_add_fd (evlist, evsel, cpu, thread, fd) <0 ) -return -1 ; +if (evsel->attr.read_format &PERF_FORMAT_ID ) { +if (perf_evlist__id_add_fd (evlist, evsel, cpu, thread, + fd) <0 ) +return -1 ; +perf_evlist__set_sid_idx (evlist, evsel, idx, cpu, + thread); + } }return 0 ;tools/perf/util/evsel.h +3
-0@@ -36,6 +36,9 @@ struct perf_sample_id {struct hlist_node node;u64 id;struct perf_evsel *evsel; +int idx; +int cpu; +pid_t tid;/* Holds total ID period value for PERF_SAMPLE_READ processing. */ u64 period;tools/perf/util/session.c +122
-0@@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,union perf_event *event, struct perf_session *session); +static int process_id_index_stub (struct perf_tool *tool__maybe_unused , +union perf_event *event__maybe_unused , +struct perf_session *perf_session +__maybe_unused ) +{ +dump_printf (": unhandled!\n" ); +return 0 ; +} +void perf_tool__fill_defaults (struct perf_tool *tool) {if (tool->sample ==NULL )@@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)else tool->finished_round = process_finished_round_stub; } +if (tool->id_index ==NULL ) + tool->id_index = process_id_index_stub; }static void swap_sample_id_all (union perf_event *event,void *data)@@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, [PERF_RECORD_HEADER_BUILD_ID] = NULL, + [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, [PERF_RECORD_HEADER_MAX] = NULL, };@@ -888,6 +900,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,return tool->build_id (tool, event, session);case PERF_RECORD_FINISHED_ROUND :return tool->finished_round (tool, event, session); +case PERF_RECORD_ID_INDEX : +return tool->id_index (tool, event, session);default :return -EINVAL ; }@@ -1594,3 +1608,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, out:return err; } + +int perf_event__process_id_index (struct perf_tool *tool__maybe_unused , +union perf_event *event, +struct perf_session *session) +{ +struct perf_evlist *evlist = session->evlist; +struct id_index_event *ie = &event->id_index; +size_t i, nr, max_nr; + + max_nr = (ie->header.size -sizeof (struct id_index_event )) / +sizeof (struct id_index_entry ); + nr = ie->nr; +if (nr > max_nr) +return -EINVAL ; + +if (dump_trace) +fprintf (stdout," nr: %zu\n" , nr); + +for (i =0 ; i < nr; i++) { +struct id_index_entry *e = &ie->entries[i]; +struct perf_sample_id *sid; + +if (dump_trace) { +fprintf (stdout," ... id: %" PRIu64, e->id); +fprintf (stdout," idx: %" PRIu64, e->idx); +fprintf (stdout," cpu: %" PRId64, e->cpu); +fprintf (stdout," tid: %" PRId64"\n" , e->tid); + } + + sid =perf_evlist__id2sid (evlist, e->id); +if (!sid) +return -ENOENT ; + sid->idx = e->idx; + sid->cpu = e->cpu; + sid->tid = e->tid; + } +return 0 ; +} + +int perf_event__synthesize_id_index (struct perf_tool *tool, +perf_event__handler_t process, +struct perf_evlist *evlist, +struct machine *machine) +{ +union perf_event *ev; +struct perf_evsel *evsel; +size_t nr =0 , i =0 , sz, max_nr, n; +int err; + +pr_debug2 ("Synthesizing id index\n" ); + + max_nr = (UINT16_MAX -sizeof (struct id_index_event )) / +sizeof (struct id_index_entry ); + +list_for_each_entry (evsel, &evlist->entries, node) + nr += evsel->ids; + + n = nr > max_nr ? max_nr : nr; + sz =sizeof (struct id_index_event ) + n *sizeof (struct id_index_entry ); + ev =zalloc (sz); +if (!ev) +return -ENOMEM ; + + ev->id_index.header.type =PERF_RECORD_ID_INDEX ; + ev->id_index.header.size = sz; + ev->id_index.nr = n; + +list_for_each_entry (evsel, &evlist->entries, node) { +u32 j; + +for (j =0 ; j < evsel->ids; j++) { +struct id_index_entry *e; +struct perf_sample_id *sid; + +if (i >= n) { + err =process (tool, ev,NULL , machine); +if (err) + goto out_err; + nr -= n; + i =0 ; + } + + e = &ev->id_index.entries[i++]; + + e->id = evsel->id[j]; + + sid =perf_evlist__id2sid (evlist, e->id); +if (!sid) { +free (ev); +return -ENOENT ; + } + + e->idx = sid->idx; + e->cpu = sid->cpu; + e->tid = sid->tid; + } + } + + sz =sizeof (struct id_index_event ) + nr *sizeof (struct id_index_entry ); + ev->id_index.header.size = sz; + ev->id_index.nr = nr; + + err =process (tool, ev,NULL , machine); +out_err: +free (ev); + +return err; +}tools/perf/util/session.h +10
-0@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,extern volatile int session_done;#define session_done () ACCESS_ONCE(session_done) + +int perf_event__process_id_index (struct perf_tool *tool, +union perf_event *event, +struct perf_session *session); + +int perf_event__synthesize_id_index (struct perf_tool *tool, +perf_event__handler_t process, +struct perf_evlist *evlist, +struct machine *machine); +#endif /* __PERF_SESSION_H */ tools/perf/util/tool.h +2
-1@@ -39,7 +39,8 @@ struct perf_tool {event_attr_op attr;event_op2 tracing_data;event_op2 finished_round, - build_id; + build_id, + id_index;bool ordered_events;bool ordering_requires_timestamps; };Signature · 1 line
1.9.1
Namhyung Kim
maintainerRe: [PATCH 01/16] perf tools: Add id index
Quoted 11 lines · Arnaldo Carvalho de Melo
Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
Quoted 5 lines
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...
Quoted 7 lines
Can you try to explain like to a seven year old?
Sigh.
'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.
I also often get confused when I saw an id and/or idx. ;-)
Thanks,
Namhyung
Adrian Hunter
reviewerRe: [PATCH 01/16] perf tools: Add id index
In reply to Arnaldo Carvalho de MeloQuoted 11 lines · Arnaldo Carvalho de Melo
Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
Quoted 5 lines
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...
Quoted 7 lines
Can you try to explain like to a seven year old?
Sigh.
'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.
This is the event id (also called the sample id) which is a unique number allocated by the kernel to the event created by perf_event_open(). Events can include the event id by having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER.
Currently the main use of the event id is to match an event back to the evsel to which it belongs i.e. perf_evlist__id2evsel()
The purpose of this patch is to make it possible to match an event back to the mmap from which it was read. The reason that is useful is because the mmap represents a time-ordered context (either for a cpu or for a thread). Intel PT decodes trace information on that basis. In full-trace mode, that information can be recorded when the Intel PT trace is read, but in sample-mode the Intel PT trace data is embedded in a sample and it is in that case that the "id index" is needed.
So the mmaps are numbered (idx) and the cpu and tid recorded against the id by perf_evlist__set_sid_idx() which is called by perf_evlist__mmap_per_evsel().
That information is recorded on the perf.data file in the new "id index". idx, cpu and tid are added to struct perf_sample_id (which is the node of evlist's hash table to match ids to evsels). The information can be retrieved using perf_evlist__id2sid(). Note however this all depends on having a sample type including PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER, otherwise ids are not recorded.
The "id index" is a synthesized event record which will be created when Intel PT sampling is used by calling perf_event__synthesize_id_index().
Quoted 332 lines
- Arnaldo
Quoted 327 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/builtin-inject.c | 1 + tools/perf/util/event.c | 1 + tools/perf/util/event.h | 15 ++++++ tools/perf/util/evlist.c | 26 ++++++++-- tools/perf/util/evsel.h | 3 ++ tools/perf/util/session.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/session.h | 10 ++++ tools/perf/util/tool.h | 3 +- 8 files changed, 177 insertions(+), 4 deletions(-)tools/perf/builtin-inject.c +1
-0@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) .tracing_data = perf_event__repipe_op2_synth, .finished_round = perf_event__repipe_op2_synth, .build_id = perf_event__repipe_op2_synth, + .id_index = perf_event__repipe_op2_synth, }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples),tools/perf/util/event.c +1
-0@@ -28,6 +28,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", + [PERF_RECORD_ID_INDEX] = "ID_INDEX", };const char *perf_event__name (unsigned int id)tools/perf/util/event.h +15
-0@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */PERF_RECORD_HEADER_TRACING_DATA =66 ,PERF_RECORD_HEADER_BUILD_ID =67 ,PERF_RECORD_FINISHED_ROUND =68 , +PERF_RECORD_ID_INDEX =69 , PERF_RECORD_HEADER_MAX };@@ -239,6 +240,19 @@ struct tracing_data_event {u32 size; }; +struct id_index_entry { +u64 id; +u64 idx; +u64 cpu; +u64 tid; +}; + +struct id_index_event { +struct perf_event_header header; +u64 nr; +struct id_index_entry entries[0 ]; +}; +union perf_event {struct perf_event_header header;struct mmap_event mmap;@@ -253,6 +267,7 @@ union perf_event {struct event_type_event event_type;struct tracing_data_event tracing_data;struct build_id_event build_id; +struct id_index_event id_index; };void perf_event__print_totals (void );tools/perf/util/evlist.c +23
-3@@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,return 0 ; } +static void perf_evlist__set_sid_idx (struct perf_evlist *evlist, +struct perf_evsel *evsel,int idx,int cpu, +int thread) +{ +struct perf_sample_id *sid =SID (evsel, cpu, thread); + sid->idx = idx; +if (evlist->cpus && cpu >=0 ) + sid->cpu = evlist->cpus->map[cpu]; +else + sid->cpu =-1 ; +if (!evsel->system_wide && evlist->threads && thread >=0 ) + sid->tid = evlist->threads->map[thread]; +else + sid->tid =-1 ; +} +struct perf_sample_id *perf_evlist__id2sid (struct perf_evlist *evlist,u64 id) {struct hlist_head *head;@@ -805,9 +821,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,return -1 ; } -if ((evsel->attr.read_format &PERF_FORMAT_ID ) && -perf_evlist__id_add_fd (evlist, evsel, cpu, thread, fd) <0 ) -return -1 ; +if (evsel->attr.read_format &PERF_FORMAT_ID ) { +if (perf_evlist__id_add_fd (evlist, evsel, cpu, thread, + fd) <0 ) +return -1 ; +perf_evlist__set_sid_idx (evlist, evsel, idx, cpu, + thread); + } }return 0 ;tools/perf/util/evsel.h +3
-0@@ -36,6 +36,9 @@ struct perf_sample_id {struct hlist_node node;u64 id;struct perf_evsel *evsel; +int idx; +int cpu; +pid_t tid;/* Holds total ID period value for PERF_SAMPLE_READ processing. */ u64 period;tools/perf/util/session.c +122
-0@@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,union perf_event *event, struct perf_session *session); +static int process_id_index_stub (struct perf_tool *tool__maybe_unused , +union perf_event *event__maybe_unused , +struct perf_session *perf_session +__maybe_unused ) +{ +dump_printf (": unhandled!\n" ); +return 0 ; +} +void perf_tool__fill_defaults (struct perf_tool *tool) {if (tool->sample ==NULL )@@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)else tool->finished_round = process_finished_round_stub; } +if (tool->id_index ==NULL ) + tool->id_index = process_id_index_stub; }static void swap_sample_id_all (union perf_event *event,void *data)@@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, [PERF_RECORD_HEADER_BUILD_ID] = NULL, + [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, [PERF_RECORD_HEADER_MAX] = NULL, };@@ -888,6 +900,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,return tool->build_id (tool, event, session);case PERF_RECORD_FINISHED_ROUND :return tool->finished_round (tool, event, session); +case PERF_RECORD_ID_INDEX : +return tool->id_index (tool, event, session);default :return -EINVAL ; }@@ -1594,3 +1608,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, out:return err; } + +int perf_event__process_id_index (struct perf_tool *tool__maybe_unused , +union perf_event *event, +struct perf_session *session) +{ +struct perf_evlist *evlist = session->evlist; +struct id_index_event *ie = &event->id_index; +size_t i, nr, max_nr; + + max_nr = (ie->header.size -sizeof (struct id_index_event )) / +sizeof (struct id_index_entry ); + nr = ie->nr; +if (nr > max_nr) +return -EINVAL ; + +if (dump_trace) +fprintf (stdout," nr: %zu\n" , nr); + +for (i =0 ; i < nr; i++) { +struct id_index_entry *e = &ie->entries[i]; +struct perf_sample_id *sid; + +if (dump_trace) { +fprintf (stdout," ... id: %" PRIu64, e->id); +fprintf (stdout," idx: %" PRIu64, e->idx); +fprintf (stdout," cpu: %" PRId64, e->cpu); +fprintf (stdout," tid: %" PRId64"\n" , e->tid); + } + + sid =perf_evlist__id2sid (evlist, e->id); +if (!sid) +return -ENOENT ; + sid->idx = e->idx; + sid->cpu = e->cpu; + sid->tid = e->tid; + } +return 0 ; +} + +int perf_event__synthesize_id_index (struct perf_tool *tool, +perf_event__handler_t process, +struct perf_evlist *evlist, +struct machine *machine) +{ +union perf_event *ev; +struct perf_evsel *evsel; +size_t nr =0 , i =0 , sz, max_nr, n; +int err; + +pr_debug2 ("Synthesizing id index\n" ); + + max_nr = (UINT16_MAX -sizeof (struct id_index_event )) / +sizeof (struct id_index_entry ); + +list_for_each_entry (evsel, &evlist->entries, node) + nr += evsel->ids; + + n = nr > max_nr ? max_nr : nr; + sz =sizeof (struct id_index_event ) + n *sizeof (struct id_index_entry ); + ev =zalloc (sz); +if (!ev) +return -ENOMEM ; + + ev->id_index.header.type =PERF_RECORD_ID_INDEX ; + ev->id_index.header.size = sz; + ev->id_index.nr = n; + +list_for_each_entry (evsel, &evlist->entries, node) { +u32 j; + +for (j =0 ; j < evsel->ids; j++) { +struct id_index_entry *e; +struct perf_sample_id *sid; + +if (i >= n) { + err =process (tool, ev,NULL , machine); +if (err) + goto out_err; + nr -= n; + i =0 ; + } + + e = &ev->id_index.entries[i++]; + + e->id = evsel->id[j]; + + sid =perf_evlist__id2sid (evlist, e->id); +if (!sid) { +free (ev); +return -ENOENT ; + } + + e->idx = sid->idx; + e->cpu = sid->cpu; + e->tid = sid->tid; + } + } + + sz =sizeof (struct id_index_event ) + nr *sizeof (struct id_index_entry ); + ev->id_index.header.size = sz; + ev->id_index.nr = nr; + + err =process (tool, ev,NULL , machine); +out_err: +free (ev); + +return err; +}tools/perf/util/session.h +10
-0@@ -126,4 +126,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,extern volatile int session_done;#define session_done () ACCESS_ONCE(session_done) + +int perf_event__process_id_index (struct perf_tool *tool, +union perf_event *event, +struct perf_session *session); + +int perf_event__synthesize_id_index (struct perf_tool *tool, +perf_event__handler_t process, +struct perf_evlist *evlist, +struct machine *machine); +#endif /* __PERF_SESSION_H */ tools/perf/util/tool.h +2
-1@@ -39,7 +39,8 @@ struct perf_tool {event_attr_op attr;event_op2 tracing_data;event_op2 finished_round, - build_id; + build_id, + id_index;bool ordered_events;bool ordering_requires_timestamps; };Signature · 1 line
1.9.1
Jiri Olsa
Acked-byRe: [PATCH 01/16] perf tools: Add id index
Quoted 46 lines · Adrian Hunter
Quoted 11 lines · Arnaldo Carvalho de Melo
Em Thu, Oct 23, 2014 at 01:45:09PM +0300, Adrian Hunter escreveu:
Quoted 5 lines
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.This gets difficult to review, I end up having to look at all the patches together to figure out the use cases, to see if this here makes sense...
Quoted 7 lines
Can you try to explain like to a seven year old?
Sigh.
'id' is somethig super vague, what does this identifies? I want to make progress processing these patches, but with so short explanations like the above one, it gets difficult.
This is the event id (also called the sample id) which is a unique number allocated by the kernel to the event created by
perf_event_open(). Events can include the event id by having a sample type includingPERF_SAMPLE_IDorPERF_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 byperf_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 usingperf_evlist__id2sid(). Note however this all depends on having a sample type includingPERF_SAMPLE_IDorPERF_SAMPLE_IDENTIFIER, otherwise ids are not recorded.
Quoted 2 lines
The "id index" is a synthesized event record which will be created when Intel PT sampling is used by calling
perf_event__synthesize_id_index().
with above text in changelog:
thanks,
jirka
Adrian Hunter
reviewerPATCH 2/16 perf pmu: Let pmu's with no events show up on perf list
In reply to Adrian Hunterperf list only lists PMUs with events. Add a
flag to cause a PMU to be also listed separately.
--- tools/perf/util/pmu.c | 13 +++++++++++-- tools/perf/util/pmu.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-)
tools/perf/util/pmu.c +11 -2
@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only) pmu =NULL ; len =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) len++; +if (pmu->selectable) + len++; + } aliases =malloc (sizeof (char *) * len);if (!aliases)return ; pmu =NULL ; j =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) {char *name =format_alias (buf,sizeof (buf), pmu, alias);bool is_cpu = !strcmp (pmu->name,"cpu" );
@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only) aliases[j] =strdup (aliases[j]); j++; } +if (pmu->selectable) { +scnprintf (buf,sizeof (buf),"%s//" , pmu->name); + aliases[j] =strdup (buf); + j++; + } + } len = j;qsort (aliases, len,sizeof (char *), cmp_string);for (j =0 ; j < len; j++) {
tools/perf/util/pmu.h +1 -0
@@ -18,6 +18,7 @@ struct perf_event_attr;struct perf_pmu {char *name;__u32 type; +bool selectable;struct perf_event_attr *default_config;struct cpu_map *cpus;struct list_head format;/* HEAD struct perf_pmu_format -> list */
Signature · 1 line
1.9.1
Namhyung Kim
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Quoted 35 lines · Adrian Hunter
perf list only lists PMUs with events. Add a
flag to cause a PMU to be also listed separately.Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/util/pmu.c | 13 +++++++++++-- tools/perf/util/pmu.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-)tools/perf/util/pmu.c +5
-2@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only) pmu =NULL ; len =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) len++; +if (pmu->selectable) + len++; + } aliases =malloc (sizeof (char *) * len);if (!aliases)return ; pmu =NULL ; j =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) {char *name =format_alias (buf,sizeof (buf), pmu, alias);bool is_cpu = !strcmp (pmu->name,"cpu" );
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780
Quoted 6 lines
tools/perf/util/pmu.c +3
-0@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only) aliases[j] =strdup (aliases[j]); j++; } +if (pmu->selectable) { +scnprintf (buf,sizeof (buf),"%s//" , pmu->name); + aliases[j] =strdup (buf);
You need to check the return value here (and above too).
Quoted 11 lines
tools/perf/util/pmu.c +3
-0+ j++; + } + } len = j;qsort (aliases, len,sizeof (char *), cmp_string);for (j =0 ; j < len; j++) {tools/perf/util/pmu.h +0
-0
tools/perf/util/pmu.h struct perf_event_attr; · line 21
Quoted 4 lines
tools/perf/util/pmu.h +1
-0@@ -18,6 +18,7 @@ struct perf_event_attr;struct perf_pmu {char *name;__u32 type; +bool selectable;
I think it's a bit confusing. IIUC this 'selectable' means that this PMU doesn't contain any events but wants to be listed. So the normal PMUs which contain events are not selectable, right?
Thanks,
Namhyung
Quoted 3 lines
tools/perf/util/pmu.h +0
-0struct perf_event_attr *default_config;struct cpu_map *cpus;struct list_head format;/* HEAD struct perf_pmu_format -> list */
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780
Quoted 3 lines
tools/perf/util/pmu.c +3
-0+if (pmu->selectable) { +scnprintf (buf,sizeof (buf),"%s//" , pmu->name); + aliases[j] =strdup (buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
- Arnado
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 9 lines
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 780
Quoted 3 lines
tools/perf/util/pmu.c +3
-0+if (pmu->selectable) { +scnprintf (buf,sizeof (buf),"%s//" , pmu->name); + aliases[j] =strdup (buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
Oh well, this print_pmu_events() function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?
Ok, will do the fixes in a separate patch...
- Arnaldo
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 11 lines
Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 9 lines
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (pmu->selectable) { + scnprintf(buf, sizeof(buf), "%s//", pmu->name); + aliases[j] = strdup(buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
Quoted 7 lines
Oh well, this
print_pmu_events()function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?Ok, will do the fixes in a separate patch...
- Arnaldo
The patch below should check everything and warn the user, even maintaining that void return...
Acked-by tags welcome as always :-)
- Arnaldo
tools/perf/util/pmu.c +22 -10
@@ -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)
Adrian Hunter
reviewerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Quoted 33 lines · Arnaldo Carvalho de Melo
Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 11 lines
Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 9 lines
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (pmu->selectable) { + scnprintf(buf, sizeof(buf), "%s//", pmu->name); + aliases[j] = strdup(buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
Quoted 7 lines
Oh well, this
print_pmu_events()function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?Ok, will do the fixes in a separate patch...
- Arnaldo
The patch below should check everything and warn the user, even maintaining that void return...
Acked-by tags welcome as always :-)
- Arnaldo
tools/perf/util/pmu.c +0
-0
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758
Quoted 8 lines
tools/perf/util/pmu.c +2
-2@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)if (pmu->selectable) len++; } - aliases =malloc (sizeof (char *) * len); + aliases =zalloc (sizeof (char *) * len);if (!aliases) -return ; + goto out_enomem;
That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.
Quoted 54 lines
tools/perf/util/pmu.c +20
-8pmu =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)
Adrian Hunter
reviewerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Quoted 29 lines · Adrian Hunter
Quoted 28 lines · Arnaldo Carvalho de Melo
Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 11 lines
Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 9 lines
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (pmu->selectable) { + scnprintf(buf, sizeof(buf), "%s//", pmu->name); + aliases[j] = strdup(buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
Quoted 7 lines
Oh well, this
print_pmu_events()function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?Ok, will do the fixes in a separate patch...
- Arnaldo
The patch below should check everything and warn the user, even maintaining that void return...
Acked-by tags welcome as always :-)
- Arnaldo
Quoted 15 lines
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758
Quoted 13 lines
tools/perf/util/pmu.c +2
-2@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)if (pmu->selectable) len++; } - aliases =malloc (sizeof (char *) * len); + aliases =zalloc (sizeof (char *) * len);if (!aliases) -return ; + goto out_enomem;That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.
Than again, no it doesn't sorry for the noise.
Quoted 55 lines
Quoted 55 lines
tools/perf/util/pmu.c +20
-8pmu =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)
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
In reply to Adrian HunterEm Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:
Quoted 29 lines · Arnaldo Carvalho de Melo
Quoted 28 lines
Em Fri, Oct 24, 2014 at 10:03:20AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 11 lines
Em Fri, Oct 24, 2014 at 09:57:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 9 lines
Em Fri, Oct 24, 2014 at 02:15:52PM +0900, Namhyung Kim escreveu:
Quoted 6 lines · Adrian Hunter
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (pmu->selectable) { + scnprintf(buf, sizeof(buf), "%s//", pmu->name); + aliases[j] = strdup(buf);You need to check the return value here (and above too).
Well spotted, fixing this up.
Quoted 7 lines
Oh well, this
print_pmu_events()function needs some care, it starts by trying to alloc the array, if it fails, it silently returns, does that mean that there are no pmu events? Or that memory allocation failed?Ok, will do the fixes in a separate patch...
- Arnaldo
The patch below should check everything and warn the user, even maintaining that void return...
Acked-by tags welcome as always :-)
- Arnaldo
Quoted 15 lines
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758
Quoted 13 lines
tools/perf/util/pmu.c +2
-2@@ -753,9 +753,9 @@ void print_pmu_events(const char *event_glob, bool name_only)if (pmu->selectable) len++; } - aliases =malloc (sizeof (char *) * len); + aliases =zalloc (sizeof (char *) * len);if (!aliases) -return ; + goto out_enomem;That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.
Oops, yeah, my bad, it should not have the !, fixing...
Quoted 54 lines
Quoted 54 lines
tools/perf/util/pmu.c +20
-8pmu =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)
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 02/16] perf pmu: Let pmu's with no events show up on perf list
Em Fri, Oct 24, 2014 at 11:45:00AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 7 lines
Em Fri, Oct 24, 2014 at 05:36:46PM +0300, Adrian Hunter escreveu:
Quoted 6 lines
tools/perf/util/pmu.c void print_pmu_events(const char *event_glob, bool name_only) · line 758
Quoted 4 lines
tools/perf/util/pmu.c +2
-1+ aliases =zalloc (sizeof (char *) * len);if (!aliases) -return ; + goto out_enomem;That path tries to free aliases[j] but aliases is null. You could set len to 0 in that case.
You got me confused, yeah, it ended up a bit confusing, but the idea was to share the error message, i.e. have just one printf("FATAL:
enomem"...) for both alloc failures, the later will have to free the array elements, and it is ok to call free(NULL), thus the change from malloc to zalloc(aliases), so that all its entries were zeroed, yadda, yadda.
- Arnaldo
Quoted 57 lines
Oops, yeah, my bad, it should not have the !, fixing...
Quoted 54 lines
Quoted 54 lines
tools/perf/util/pmu.c +20
-8pmu =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)
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf pmu: Let pmu' s with no events show up on perf list
In reply to Adrian Hunterperf pmu: Let pmu's with no events show up on perf list
Commit-ID: 42634bc7a02ead59cf2d50e60d8b8f825de8a3b0 Gitweb: http://git.kernel.org/tip/42634bc7a02ead59cf2d50e60d8b8f825de8a3b0 Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:10 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:48 -0200
perf pmu: Let pmu's with no events show up on perf list
perf list only lists PMUs with events. Add a flag to cause a PMU to be also listed separately.
--- tools/perf/util/pmu.c | 13 +++++++++++-- tools/perf/util/pmu.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-)
tools/perf/util/pmu.c +11 -2
@@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only) pmu =NULL ; len =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) len++; +if (pmu->selectable) + len++; + } aliases =malloc (sizeof (char *) * len);if (!aliases)return ; pmu =NULL ; j =0 ; -while ((pmu =perf_pmu__scan (pmu)) !=NULL ) +while ((pmu =perf_pmu__scan (pmu)) !=NULL ) {list_for_each_entry (alias, &pmu->aliases, list) {char *name =format_alias (buf,sizeof (buf), pmu, alias);bool is_cpu = !strcmp (pmu->name,"cpu" );
@@ -772,6 +775,12 @@ void print_pmu_events(const char *event_glob, bool name_only) aliases[j] =strdup (aliases[j]); j++; } +if (pmu->selectable) { +scnprintf (buf,sizeof (buf),"%s//" , pmu->name); + aliases[j] =strdup (buf); + j++; + } + } len = j;qsort (aliases, len,sizeof (char *), cmp_string);for (j =0 ; j < len; j++) {
tools/perf/util/pmu.h +1 -0
@@ -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 */
Adrian Hunter
reviewerPATCH 3/16 perf session: Add perf_session__deliver_synth_event()
In reply to Adrian HunterAdd a function to deliver synthesized events from
within a session.
--- tools/perf/util/session.c | 14 ++++++++++++++ tools/perf/util/session.h | 5 +++++ 2 files changed, 19 insertions(+)
tools/perf/util/session.c +14 -0
@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session, } } +int perf_session__deliver_synth_event (struct perf_session *session, +union perf_event *event, +struct perf_sample *sample, +struct perf_tool *tool) +{ +events_stats__inc (&session->stats, event->header.type); + +if (event->header.type >=PERF_RECORD_USER_TYPE_START ) +return perf_session__process_user_event (session, event, tool, +0 ); + +return perf_session__deliver_event (session, event, sample, tool,0 ); +} +static void event_swap (union perf_event *event,bool sample_id_all) {perf_event__swap_op swap;
tools/perf/util/session.h +5 -0
@@ -127,6 +127,11 @@ extern volatile int session_done;#define session_done () ACCESS_ONCE(session_done) +int perf_session__deliver_synth_event (struct perf_session *session, +union perf_event *event, +struct perf_sample *sample, +struct perf_tool *tool); +int perf_event__process_id_index (struct perf_tool *tool,union perf_event *event,struct perf_session *session);
Signature · 1 line
1.9.1
Namhyung Kim
maintainerRe: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()
Quoted 20 lines · Adrian Hunter
Add a function to deliver synthesized events from
within a session.Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/util/session.c | 14 ++++++++++++++ tools/perf/util/session.h | 5 +++++ 2 files changed, 19 insertions(+)tools/perf/util/session.c +3
-0@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session, } } +int perf_session__deliver_synth_event(struct perf_session *session, + union perf_event *event, + struct perf_sample *sample,
tools/perf/util/session.c static s64 perf_session__process_user_event(struct perf_session *session, · line 922
Quoted 10 lines
tools/perf/util/session.c +10
-0+ struct perf_tool *tool) +{ +events_stats__inc (&session->stats, event->header.type); + +if (event->header.type >=PERF_RECORD_USER_TYPE_START ) +return perf_session__process_user_event (session, event, tool, +0 ); + +return perf_session__deliver_event (session, event, sample, tool,0 ); +}
It seems that it's basically same as what perf_session__process_event() does. So it might deserve a comment or better changelog why it's needed.
Thanks,
Namhyung
Quoted 20 lines
tools/perf/util/session.c +1
-0+static void event_swap (union perf_event *event,bool sample_id_all) {perf_event__swap_op swap;tools/perf/util/session.h +5
-0@@ -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);
Adrian Hunter
reviewerRe: [PATCH 03/16] perf session: Add perf_session__deliver_synth_event()
Quoted 18 lines · Namhyung Kim
Quoted 17 lines · Adrian Hunter
Add a function to deliver synthesized events from
within a session.Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/util/session.c | 14 ++++++++++++++ tools/perf/util/session.h | 5 +++++ 2 files changed, 19 insertions(+)tools/perf/util/session.c +0
-0@@ -907,6 +907,20 @@ static s64 perf_session__process_user_event(struct perf_session *session, } }
Quoted 16 lines
tools/perf/util/session.c static s64 perf_session__process_user_event(struct perf_session *session, · line 922
Quoted 13 lines
tools/perf/util/session.c +13
-0+int perf_session__deliver_synth_event (struct perf_session *session, +union perf_event *event, +struct perf_sample *sample, +struct perf_tool *tool) +{ +events_stats__inc (&session->stats, event->header.type); + +if (event->header.type >=PERF_RECORD_USER_TYPE_START ) +return perf_session__process_user_event (session, event, tool, +0 ); + +return perf_session__deliver_event (session, event, sample, tool,0 ); +}It seems that it's basically same as what
perf_session__process_event()does. So it might deserve a comment or better changelog why it's needed.
Intel PT decoding works by synthesizing events (primarily branch events) that can then be consumed by existing tools. This function will be used to deliver those events.
Quoted 27 lines
Thanks,
NamhyungQuoted 20 lines
tools/perf/util/session.c +1
-0+static void event_swap (union perf_event *event,bool sample_id_all) {perf_event__swap_op swap;tools/perf/util/session.h +5
-0@@ -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);
Adrian Hunter
reviewerPATCH 4/16 perf tools: Add a thread stack for synthesizing call chains
In reply to Adrian HunterAdd a thread stack for synthesizing call chains from call and return events.
--- tools/perf/Makefile.perf | 2 + tools/perf/util/event.h | 26 +++++++ tools/perf/util/thread-stack.c | 151 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/thread-stack.h | 32 +++++++++ tools/perf/util/thread.c | 3 + tools/perf/util/thread.h | 3 + 6 files changed, 217 insertions(+) create mode 100644 tools/perf/util/thread-stack.c create mode 100644 tools/perf/util/thread-stack.h
tools/perf/Makefile.perf +2 -0
@@ -304,6 +304,7 @@ LIB_H += ui/util.h LIB_H += ui/ui.h LIB_H += util/data.h LIB_H += util/kvm-stat.h +LIB_H += util/thread-stack.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o
@@ -380,6 +381,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o LIB_OBJS += $(OUTPUT)util/data.o LIB_OBJS += $(OUTPUT)util/tsc.o LIB_OBJS += $(OUTPUT)util/cloexec.o +LIB_OBJS += $(OUTPUT)util/thread-stack.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/event.h +26 -0
@@ -143,6 +143,32 @@ struct branch_stack {struct branch_entry entries[0 ]; }; +enum { +PERF_FLAG_BRANCH =1ULL <<0 , +PERF_FLAG_CALL =1ULL <<1 , +PERF_FLAG_RETURN =1ULL <<2 , +PERF_FLAG_CONDITIONAL =1ULL <<3 , +PERF_FLAG_SYSCALLRET =1ULL <<4 , +PERF_FLAG_ASYNC =1ULL <<5 , +PERF_FLAG_INTERRUPT =1ULL <<6 , +PERF_FLAG_TX_ABORT =1ULL <<7 , +PERF_FLAG_TRACE_BEGIN =1ULL <<8 , +PERF_FLAG_TRACE_END =1ULL <<9 , +PERF_FLAG_IN_TX =1ULL <<10 , +}; + +#define PERF_BRANCH_MASK (\ + PERF_FLAG_BRANCH |\ + PERF_FLAG_CALL |\ + PERF_FLAG_RETURN |\ + PERF_FLAG_CONDITIONAL |\ + PERF_FLAG_SYSCALLRET |\ + PERF_FLAG_ASYNC |\ + PERF_FLAG_INTERRUPT |\ + PERF_FLAG_TX_ABORT |\ + PERF_FLAG_TRACE_BEGIN |\ + PERF_FLAG_TRACE_END) +struct perf_sample {u64 ip;u32 pid, tid;
tools/perf/util/thread-stack.c new file +151 -0
151 lines not on this page · raw message
tools/perf/util/thread-stack.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +* thread-stack.h: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#ifndef __PERF_THREAD_STACK_H +#define __PERF_THREAD_STACK_H + +#include <sys/types.h> + +#include <linux/types.h> + +struct thread ; +struct ip_callchain ; + +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip); +void thread_stack__free (struct thread *thread); + +#endif
tools/perf/util/thread.c +3 -0
@@ -4,6 +4,7 @@#include <string.h> #include "session.h" #include "thread.h" +#include "thread-stack.h" #include "util.h" #include "debug.h" #include "comm.h"
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) {struct comm *comm, *tmp; +thread_stack__free (thread); +if (thread->mg) {map_groups__put (thread->mg); thread->mg =NULL ;
tools/perf/util/thread.h +3 -0
@@ -8,6 +8,8 @@#include "symbol.h" #include <strlist.h> +struct thread_stack ; + struct thread { union { struct rb_node rb_node;
@@ -25,6 +27,7 @@ struct thread {int comm_len;void *priv; +struct thread_stack *ts; };struct machine ;
Signature · 1 line
1.9.1
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:
Quoted 142 lines
Add a thread stack for synthesizing call chains from call and return events.
Signed-off-by Adrian Hunter <[email protected]>--- 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.htools/perf/Makefile.perf +2
-0@@ -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.otools/perf/util/event.h +26
-0@@ -143,6 +143,32 @@ struct branch_stack {struct branch_entry entries[0 ]; }; +enum { +PERF_FLAG_BRANCH =1ULL <<0 , +PERF_FLAG_CALL =1ULL <<1 , +PERF_FLAG_RETURN =1ULL <<2 , +PERF_FLAG_CONDITIONAL =1ULL <<3 , +PERF_FLAG_SYSCALLRET =1ULL <<4 , +PERF_FLAG_ASYNC =1ULL <<5 , +PERF_FLAG_INTERRUPT =1ULL <<6 , +PERF_FLAG_TX_ABORT =1ULL <<7 , +PERF_FLAG_TRACE_BEGIN =1ULL <<8 , +PERF_FLAG_TRACE_END =1ULL <<9 , +PERF_FLAG_IN_TX =1ULL <<10 , +}; + +#define PERF_BRANCH_MASK (\ + PERF_FLAG_BRANCH |\ + PERF_FLAG_CALL |\ + PERF_FLAG_RETURN |\ + PERF_FLAG_CONDITIONAL |\ + PERF_FLAG_SYSCALLRET |\ + PERF_FLAG_ASYNC |\ + PERF_FLAG_INTERRUPT |\ + PERF_FLAG_TX_ABORT |\ + PERF_FLAG_TRACE_BEGIN |\ + PERF_FLAG_TRACE_END) +struct perf_sample {u64 ip;u32 pid, tid;tools/perf/util/thread-stack.c new file +64
-0@@ -0,0 +1,151 @@ +/* +* thread-stack.c: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#include "thread.h" +#include "event.h" +#include "util.h" +#include "thread-stack.h" + +#define STACK_GROWTH 4096 + +struct thread_stack_entry { +u64 ret_addr; +}; + +struct thread_stack { +struct thread_stack_entry *stack; +size_t cnt; +size_t sz; +u64 trace_nr; +}; + +static void thread_stack__grow (struct thread_stack *ts) +{ +struct thread_stack_entry *new_stack; +size_t sz, new_sz; + + new_sz = ts->sz +STACK_GROWTH ; + sz = new_sz *sizeof (struct thread_stack_entry ); + new_stack =realloc (ts->stack, sz); +if (new_stack) { + ts->stack = new_stack; + ts->sz = new_sz; + } +} + +static struct thread_stack *thread_stack__new (void ) +{ +struct thread_stack *ts; + + ts =zalloc (sizeof (struct thread_stack )); +if (!ts) +return NULL ; + +thread_stack__grow (ts); +if (!ts->stack) { +free (ts); +return NULL ; + } + +return ts; +} +
tools/perf/util/thread-stack.c line 74
Quoted 10 lines
tools/perf/util/thread-stack.c new file +10
-0+static void thread_stack__push (struct thread_stack *ts,u64 ret_addr) +{ +if (ts->cnt == ts->sz) { +thread_stack__grow (ts); +if (ts->cnt == ts->sz) + ts->cnt =0 ; + } + + ts->stack[ts->cnt++].ret_addr = ret_addr; +}
So can you elaborate on the use case, i.e. this silently trows the existing stack contents away if it doesn't grow, looks strange :-\
Merits some explanation about why this is OK, to say the least.
Quoted 159 lines
tools/perf/util/thread-stack.c new file +77
-0+ +static void thread_stack__pop (struct thread_stack *ts,u64 ret_addr) +{ +if (!ts->cnt) +return ; + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { + ts->cnt -=1 ; + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr == ret_addr) { + ts->cnt = i; +return ; + } + } + } +} + +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr) +{ +if (!thread) +return ; + +if (!thread->ts) { + thread->ts =thread_stack__new (); +if (!thread->ts) +return ; + thread->ts->trace_nr = trace_nr; + } + +if (trace_nr != thread->ts->trace_nr) { + thread->ts->trace_nr = trace_nr; + thread->ts->cnt =0 ; + } + +if (flags &PERF_FLAG_CALL ) { +u64 ret_addr; + +if (!to_ip) +return ; + ret_addr = from_ip + insn_len; +if (ret_addr == to_ip) +return ;/* Zero-length calls are excluded */ +thread_stack__push (thread->ts, ret_addr); + }else if (flags &PERF_FLAG_RETURN ) { +if (!from_ip) +return ; +thread_stack__pop (thread->ts, to_ip); + } +} + +void thread_stack__free (struct thread *thread) +{ +if (thread->ts) { +zfree (&thread->ts->stack); +zfree (&thread->ts); + } +} + +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip) +{ +size_t i; + +if (!thread || !thread->ts) + chain->nr =1 ; +else + chain->nr =min (sz, thread->ts->cnt +1 ); + + chain->ips[0 ] = ip; + +for (i =1 ; i < chain->nr; i++) + chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; +}tools/perf/util/thread-stack.h new file +32
-0@@ -0,0 +1,32 @@ +/* +* thread-stack.h: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#ifndef __PERF_THREAD_STACK_H +#define __PERF_THREAD_STACK_H + +#include <sys/types.h> + +#include <linux/types.h> + +struct thread ; +struct ip_callchain ; + +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip); +void thread_stack__free (struct thread *thread); + +#endif tools/perf/util/thread.c +3
-0@@ -4,6 +4,7 @@#include <string.h> #include "session.h" #include "thread.h" +#include "thread-stack.h" #include "util.h" #include "debug.h" #include "comm.h" @@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) {struct comm *comm, *tmp; +thread_stack__free (thread); +if (thread->mg) {map_groups__put (thread->mg); thread->mg =NULL ;tools/perf/util/thread.h +3
-0@@ -8,6 +8,8 @@#include "symbol.h" #include <strlist.h> +struct thread_stack ; + struct thread { union { struct rb_node rb_node;@@ -25,6 +27,7 @@ struct thread {int comm_len;void *priv; +struct thread_stack *ts; };struct machine ;Signature · 1 line
1.9.1
Adrian Hunter
reviewerRe: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
Quoted 157 lines · Arnaldo Carvalho de Melo
Em Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter escreveu:
Quoted 142 lines
Add a thread stack for synthesizing call chains from call and return events.
Signed-off-by Adrian Hunter <[email protected]>--- 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.htools/perf/Makefile.perf +2
-0@@ -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.otools/perf/util/event.h +26
-0@@ -143,6 +143,32 @@ struct branch_stack {struct branch_entry entries[0 ]; }; +enum { +PERF_FLAG_BRANCH =1ULL <<0 , +PERF_FLAG_CALL =1ULL <<1 , +PERF_FLAG_RETURN =1ULL <<2 , +PERF_FLAG_CONDITIONAL =1ULL <<3 , +PERF_FLAG_SYSCALLRET =1ULL <<4 , +PERF_FLAG_ASYNC =1ULL <<5 , +PERF_FLAG_INTERRUPT =1ULL <<6 , +PERF_FLAG_TX_ABORT =1ULL <<7 , +PERF_FLAG_TRACE_BEGIN =1ULL <<8 , +PERF_FLAG_TRACE_END =1ULL <<9 , +PERF_FLAG_IN_TX =1ULL <<10 , +}; + +#define PERF_BRANCH_MASK (\ + PERF_FLAG_BRANCH |\ + PERF_FLAG_CALL |\ + PERF_FLAG_RETURN |\ + PERF_FLAG_CONDITIONAL |\ + PERF_FLAG_SYSCALLRET |\ + PERF_FLAG_ASYNC |\ + PERF_FLAG_INTERRUPT |\ + PERF_FLAG_TX_ABORT |\ + PERF_FLAG_TRACE_BEGIN |\ + PERF_FLAG_TRACE_END) +struct perf_sample {u64 ip;u32 pid, tid;tools/perf/util/thread-stack.c new file +64
-0@@ -0,0 +1,151 @@ +/* +* thread-stack.c: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#include "thread.h" +#include "event.h" +#include "util.h" +#include "thread-stack.h" + +#define STACK_GROWTH 4096 + +struct thread_stack_entry { +u64 ret_addr; +}; + +struct thread_stack { +struct thread_stack_entry *stack; +size_t cnt; +size_t sz; +u64 trace_nr; +}; + +static void thread_stack__grow (struct thread_stack *ts) +{ +struct thread_stack_entry *new_stack; +size_t sz, new_sz; + + new_sz = ts->sz +STACK_GROWTH ; + sz = new_sz *sizeof (struct thread_stack_entry ); + new_stack =realloc (ts->stack, sz); +if (new_stack) { + ts->stack = new_stack; + ts->sz = new_sz; + } +} + +static struct thread_stack *thread_stack__new (void ) +{ +struct thread_stack *ts; + + ts =zalloc (sizeof (struct thread_stack )); +if (!ts) +return NULL ; + +thread_stack__grow (ts); +if (!ts->stack) { +free (ts); +return NULL ; + } + +return ts; +} +tools/perf/util/thread-stack.c line 74
Quoted 10 lines
tools/perf/util/thread-stack.c new file +10
-0+static void thread_stack__push (struct thread_stack *ts,u64 ret_addr) +{ +if (ts->cnt == ts->sz) { +thread_stack__grow (ts); +if (ts->cnt == ts->sz) + ts->cnt =0 ; + } + + ts->stack[ts->cnt++].ret_addr = ret_addr; +}So can you elaborate on the use case, i.e. this silently trows the existing stack contents away if it doesn't grow, looks strange :-\
Quoted 1 line
Merits some explanation about why this is OK, to say the least.
I considered the call stack to be augmenting the information so not worth failing the whole session over. Equally memory allocation failure is likely to be fatal anyway due to oom-killers or other allocations failing.
But it could certainly print a warning, so I will add that.
Quoted 162 lines
Quoted 160 lines
tools/perf/util/thread-stack.c new file +77
-0+ +static void thread_stack__pop (struct thread_stack *ts,u64 ret_addr) +{ +if (!ts->cnt) +return ; + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { + ts->cnt -=1 ; + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr == ret_addr) { + ts->cnt = i; +return ; + } + } + } +} + +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr) +{ +if (!thread) +return ; + +if (!thread->ts) { + thread->ts =thread_stack__new (); +if (!thread->ts) +return ; + thread->ts->trace_nr = trace_nr; + } + +if (trace_nr != thread->ts->trace_nr) { + thread->ts->trace_nr = trace_nr; + thread->ts->cnt =0 ; + } + +if (flags &PERF_FLAG_CALL ) { +u64 ret_addr; + +if (!to_ip) +return ; + ret_addr = from_ip + insn_len; +if (ret_addr == to_ip) +return ;/* Zero-length calls are excluded */ +thread_stack__push (thread->ts, ret_addr); + }else if (flags &PERF_FLAG_RETURN ) { +if (!from_ip) +return ; +thread_stack__pop (thread->ts, to_ip); + } +} + +void thread_stack__free (struct thread *thread) +{ +if (thread->ts) { +zfree (&thread->ts->stack); +zfree (&thread->ts); + } +} + +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip) +{ +size_t i; + +if (!thread || !thread->ts) + chain->nr =1 ; +else + chain->nr =min (sz, thread->ts->cnt +1 ); + + chain->ips[0 ] = ip; + +for (i =1 ; i < chain->nr; i++) + chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; +}tools/perf/util/thread-stack.h new file +32
-0@@ -0,0 +1,32 @@ +/* +* thread-stack.h: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#ifndef __PERF_THREAD_STACK_H +#define __PERF_THREAD_STACK_H + +#include <sys/types.h> + +#include <linux/types.h> + +struct thread ; +struct ip_callchain ; + +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip); +void thread_stack__free (struct thread *thread); + +#endif tools/perf/util/thread.c +3
-0@@ -4,6 +4,7 @@#include <string.h> #include "session.h" #include "thread.h" +#include "thread-stack.h" #include "util.h" #include "debug.h" #include "comm.h" @@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) {struct comm *comm, *tmp; +thread_stack__free (thread); +if (thread->mg) {map_groups__put (thread->mg); thread->mg =NULL ;tools/perf/util/thread.h +3
-0@@ -8,6 +8,8 @@#include "symbol.h" #include <strlist.h> +struct thread_stack ; + struct thread { union { struct rb_node rb_node;@@ -25,6 +27,7 @@ struct thread {int comm_len;void *priv; +struct thread_stack *ts; };struct machine ;Signature · 1 line
1.9.1
Namhyung Kim
maintainerRe: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
In reply to Adrian HunterQuoted 8 lines · Adrian Hunter
tools/perf/util/thread-stack.c new file +8
-0+static void thread_stack__pop (struct thread_stack *ts,u64 ret_addr) +{ +if (!ts->cnt) +return ; + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { + ts->cnt -=1 ; + }else {
tools/perf/util/thread-stack.c line 93
Quoted 10 lines
tools/perf/util/thread-stack.c new file +10
-0+size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr == ret_addr) { + ts->cnt = i; +return ; + } + } + } +}
Why not making it a single loop like:
int i;
for (i = ts->cnt - 1; i >= 0; i--) {
if (ts->stack[i].ret_addr == ret_addr) {
ts->cnt = i;
return;
}
}Thanks,
Namhyung
Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
In reply to Adrian HunterOn Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter wrote:
SNIP
Quoted 9 lines
tools/perf/util/thread-stack.c new file +9
-0+while (i--) { +if (ts->stack[i].ret_addr == ret_addr) { + ts->cnt = i; +return ; + } + } + } +} +
tools/perf/util/thread-stack.c line 104
Quoted 10 lines
tools/perf/util/thread-stack.c new file +10
-0+void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr) +{ +if (!thread) +return ; + +if (!thread->ts) { + thread->ts =thread_stack__new (); +if (!thread->ts) +return ;
should the function return 'int' then..? So the allocation error gets handled properly.
jirka
Re: [PATCH 04/16] perf tools: Add a thread stack for synthesizing call chains
In reply to Adrian HunterOn Thu, Oct 23, 2014 at 01:45:12PM +0300, Adrian Hunter wrote:
SNIP
Quoted 17 lines
};
tools/perf/util/event.h +15
-0+enum { +PERF_FLAG_BRANCH =1ULL <<0 , +PERF_FLAG_CALL =1ULL <<1 , +PERF_FLAG_RETURN =1ULL <<2 , +PERF_FLAG_CONDITIONAL =1ULL <<3 , +PERF_FLAG_SYSCALLRET =1ULL <<4 , +PERF_FLAG_ASYNC =1ULL <<5 , +PERF_FLAG_INTERRUPT =1ULL <<6 , +PERF_FLAG_TX_ABORT =1ULL <<7 , +PERF_FLAG_TRACE_BEGIN =1ULL <<8 , +PERF_FLAG_TRACE_END =1ULL <<9 , +PERF_FLAG_IN_TX =1ULL <<10 , +}; + +#define PERF_BRANCH_MASK (\
tools/perf/util/event.h struct branch_stack { · line 170
Quoted 10 lines
tools/perf/util/event.h +10
-0+PERF_FLAG_BRANCH |\ +PERF_FLAG_CALL |\ +PERF_FLAG_RETURN |\ +PERF_FLAG_CONDITIONAL |\ +PERF_FLAG_SYSCALLRET |\ +PERF_FLAG_ASYNC |\ +PERF_FLAG_INTERRUPT |\ +PERF_FLAG_TX_ABORT |\ +PERF_FLAG_TRACE_BEGIN |\ + PERF_FLAG_TRACE_END)
PERF_FLAG_ prefix seems too generic,
should it be something like PERF_BRANCH_FLAG__* ?
jirka
Adrian Hunter
reviewerPATCH 5/16 perf tools: Add facility to export data in database-friendly way
In reply to Adrian HunterThis patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.
Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.
--- tools/perf/Makefile.perf | 2 + tools/perf/util/comm.h | 1 + tools/perf/util/db-export.c | 268 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/db-export.h | 86 ++++++++++++++ tools/perf/util/dso.h | 1 + tools/perf/util/evsel.h | 1 + tools/perf/util/machine.h | 1 + tools/perf/util/symbol.h | 1 + tools/perf/util/thread.h | 1 + 9 files changed, 362 insertions(+) create mode 100644 tools/perf/util/db-export.c create mode 100644 tools/perf/util/db-export.h
tools/perf/Makefile.perf +2 -0
@@ -305,6 +305,7 @@ LIB_H += ui/ui.h LIB_H += util/data.h LIB_H += util/kvm-stat.h LIB_H += util/thread-stack.h +LIB_H += util/db-export.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o
@@ -382,6 +383,7 @@ LIB_OBJS += $(OUTPUT)util/data.o LIB_OBJS += $(OUTPUT)util/tsc.o LIB_OBJS += $(OUTPUT)util/cloexec.o LIB_OBJS += $(OUTPUT)util/thread-stack.o +LIB_OBJS += $(OUTPUT)util/db-export.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/comm.h +1 -0
@@ -10,6 +10,7 @@ struct comm_str;struct comm {struct comm_str *comm_str;u64 start; +u64 db_id;struct list_head list;bool exec; };
tools/perf/util/db-export.c new file +268 -0
268 lines not on this page · raw message
tools/perf/util/db-export.h new file +86 -0
86 lines not on this page · raw message
tools/perf/util/dso.h +1 -0
@@ -111,6 +111,7 @@ struct dso {enum dso_swap_type needs_swap;enum dso_binary_type symtab_type;enum dso_binary_type binary_type; +u64 db_id; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1;
tools/perf/util/evsel.h +1 -0
@@ -95,6 +95,7 @@ struct perf_evsel {int sample_read;struct perf_evsel *leader;char *group_name; +u64 db_id; };union u64_swap {
tools/perf/util/machine.h +1 -0
@@ -40,6 +40,7 @@ struct machine {u64 kernel_start;symbol_filter_t symbol_filter;pid_t *current_tid; +u64 db_id; }; static inline
tools/perf/util/symbol.h +1 -0
@@ -73,6 +73,7 @@ struct symbol {struct rb_node rb_node;u64 start;u64 end; +u64 db_id;u16 namelen;u8 binding;bool ignore;
tools/perf/util/thread.h +1 -0
@@ -25,6 +25,7 @@ struct thread {bool dead;/* if set thread has exited */ struct list_head comm_list;int comm_len; +u64 db_id;void *priv;struct thread_stack *ts;
Signature · 1 line
1.9.1
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Em Thu, Oct 23, 2014 at 01:45:13PM +0300, Adrian Hunter escreveu:
Quoted 11 lines
This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member '
db_id' is used for that. 'db_id' is just a 64-bit sequence number.
Quoted 3 lines
Exporting centres around the
db_export__sample()function which exports the associated data structures if they have not yet been allocated adb_id.
So this ends up bloating all data structures with 8 extra bytes, can't we use the priv pointer that some of the structures already have for tooling to put tool specific stuff?
In places like this:
tools/perf/util/thread.h struct thread { · line 31
Quoted 8 lines
tools/perf/util/thread.h +1
-0@@ -25,6 +25,7 @@ struct thread {bool dead;/* if set thread has exited */ struct list_head comm_list;int comm_len; +u64 db_id;void *priv;struct thread_stack *ts;
Instead we would have:
union {
void *priv;
u64 db_id;
};Using a unnamed union is all that is needed in struct thread case, I think, the rest of your patch that deals with 'struct thread' doesn't need to change and the end impact for other tools that have no use whatsoever fot this thread->db_id is zero.
Because after all you will end up using some tool to just process samples, etc, i.e. basically a 'perf report' like tool that instead of creating hist_entries, etc will end up just populating the
machines ->
machine -> threads -> map_groups -> map -> dso -> symbols
Hierarchy, right?
Struct symbol even has a mechanism for reserving space for private use, that symbol_conf.priv_size + symbol__priv(), so that we don't incur even in the cost of the priv pointer.
struct perf_evsel already has:
union {
void *priv;
off_t id_offset;
};Just add db_id there and the rest of your patch that deals with perf_evsel will not need to be changed and again the impact for tools that do not use this will be zero.
The other data structures that still don't have a ->priv area can have one added.
I.e. tool specific bloating of core data structures should be avoided.
Fixing up these things will allow a good chunk of this patchkit to be processed.
Thanks,
- Arnaldo
Quoted 475 lines
Signed-off-by Adrian Hunter <[email protected]>--- 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.htools/perf/Makefile.perf +2
-0@@ -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.otools/perf/util/comm.h +1
-0@@ -10,6 +10,7 @@ struct comm_str;struct comm {struct comm_str *comm_str;u64 start; +u64 db_id;struct list_head list;bool exec; };tools/perf/util/db-export.c new file +268
-0@@ -0,0 +1,268 @@ +/* +* db-export.c: Support for exporting data suitable for import to a database +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#include <errno.h> + +#include "evsel.h" +#include "machine.h" +#include "thread.h" +#include "comm.h" +#include "symbol.h" +#include "event.h" +#include "db-export.h" + +int db_export__init (struct db_export *dbe) +{ +memset (dbe,0 ,sizeof (struct db_export )); +return 0 ; +} + +void db_export__exit (struct db_export *dbe__maybe_unused ) +{ +} + +int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel) +{ +if (evsel->db_id) +return 0 ; + + evsel->db_id = ++dbe->evsel_last_db_id; + +if (dbe->export_evsel) +return dbe->export_evsel (dbe, evsel); + +return 0 ; +} + +int db_export__machine (struct db_export *dbe,struct machine *machine) +{ +if (machine->db_id) +return 0 ; + + machine->db_id = ++dbe->machine_last_db_id; + +if (dbe->export_machine) +return dbe->export_machine (dbe, machine); + +return 0 ; +} + +int db_export__thread (struct db_export *dbe,struct thread *thread, +struct machine *machine,struct comm *comm) +{ +u64 main_thread_db_id =0 ; +int err; + +if (thread->db_id) +return 0 ; + + thread->db_id = ++dbe->thread_last_db_id; + +if (thread->pid_ !=-1 ) { +struct thread *main_thread; + +if (thread->pid_ == thread->tid) { + main_thread = thread; + }else { + main_thread =machine__findnew_thread (machine, + thread->pid_, + thread->pid_); +if (!main_thread) +return -ENOMEM ; + err =db_export__thread (dbe, main_thread, machine, + comm); +if (err) +return err; +if (comm) { + err =db_export__comm_thread (dbe, comm, thread); +if (err) +return err; + } + } + main_thread_db_id = main_thread->db_id; + } + +if (dbe->export_thread) +return dbe->export_thread (dbe, thread, main_thread_db_id, + machine); + +return 0 ; +} + +int db_export__comm (struct db_export *dbe,struct comm *comm, +struct thread *main_thread) +{ +int err; + +if (comm->db_id) +return 0 ; + + comm->db_id = ++dbe->comm_last_db_id; + +if (dbe->export_comm) { + err = dbe->export_comm (dbe, comm); +if (err) +return err; + } + +return db_export__comm_thread (dbe, comm, main_thread); +} + +int db_export__comm_thread (struct db_export *dbe,struct comm *comm, +struct thread *thread) +{ +u64 db_id; + + db_id = ++dbe->comm_thread_last_db_id; + +if (dbe->export_comm_thread) +return dbe->export_comm_thread (dbe, db_id, comm, thread); + +return 0 ; +} + +int db_export__dso (struct db_export *dbe,struct dso *dso, +struct machine *machine) +{ +if (dso->db_id) +return 0 ; + + dso->db_id = ++dbe->dso_last_db_id; + +if (dbe->export_dso) +return dbe->export_dso (dbe, dso, machine); + +return 0 ; +} + +int db_export__symbol (struct db_export *dbe,struct symbol *sym, +struct dso *dso) +{ +if (sym->db_id) +return 0 ; + + sym->db_id = ++dbe->symbol_last_db_id; + +if (dbe->export_symbol) +return dbe->export_symbol (dbe, sym, dso); + +return 0 ; +} + +static struct thread *get_main_thread (struct machine *machine, +struct thread *thread) +{ +if (thread->pid_ == thread->tid) +return thread; + +if (thread->pid_ ==-1 ) +return NULL ; + +return machine__find_thread (machine, thread->pid_, thread->pid_); +} + +static int db_ids_from_al (struct db_export *dbe,struct addr_location *al, +u64 *dso_db_id,u64 *sym_db_id,u64 *offset) +{ +int err; + +if (al->map) { +struct dso *dso = al->map->dso; + + err =db_export__dso (dbe, dso, al->machine); +if (err) +return err; + *dso_db_id = dso->db_id; + +if (!al->sym) { + al->sym =symbol__new (al->addr,0 ,0 ,"unknown" ); +if (al->sym) +symbols__insert (&dso->symbols[al->map->type], + al->sym); + } + +if (al->sym) { + err =db_export__symbol (dbe, al->sym, dso); +if (err) +return err; + *sym_db_id = al->sym->db_id; + *offset = al->addr - al->sym->start; + } + } + +return 0 ; +} + +int db_export__sample (struct db_export *dbe,union perf_event *event, +struct perf_sample *sample,struct perf_evsel *evsel, +struct thread *thread,struct addr_location *al) +{ +struct export_sample es = { + .event = event, + .sample = sample, + .evsel = evsel, + .thread = thread, + .al = al, + }; +struct thread *main_thread; +struct comm *comm =NULL ; +int err; + + err =db_export__evsel (dbe, evsel); +if (err) +return err; + + err =db_export__machine (dbe, al->machine); +if (err) +return err; + + main_thread =get_main_thread (al->machine, thread); +if (main_thread) + comm =machine__thread_exec_comm (al->machine, main_thread); + + err =db_export__thread (dbe, thread, al->machine, comm); +if (err) +return err; + +if (comm) { + err =db_export__comm (dbe, comm, main_thread); +if (err) +return err; + es.comm_db_id = comm->db_id; + } + + es.db_id = ++dbe->sample_last_db_id; + + err =db_ids_from_al (dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset); +if (err) +return err; + +if ((evsel->attr.sample_type &PERF_SAMPLE_ADDR ) && +sample_addr_correlates_sym (&evsel->attr)) { +struct addr_location addr_al; + +perf_event__preprocess_sample_addr (event, sample, al->machine, + thread, &addr_al); + err =db_ids_from_al (dbe, &addr_al, &es.addr_dso_db_id, + &es.addr_sym_db_id, &es.addr_offset); +if (err) +return err; + } + +if (dbe->export_sample) +return dbe->export_sample (dbe, &es); + +return 0 ; +}tools/perf/util/db-export.h new file +86
-0@@ -0,0 +1,86 @@ +/* +* db-export.h: Support for exporting data suitable for import to a database +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#ifndef __PERF_DB_EXPORT_H +#define __PERF_DB_EXPORT_H + +#include <linux/types.h> + +struct perf_evsel ; +struct machine ; +struct thread ; +struct comm ; +struct dso ; +struct perf_sample ; +struct addr_location ; + +struct export_sample { +union perf_event *event; +struct perf_sample *sample; +struct perf_evsel *evsel; +struct thread *thread; +struct addr_location *al; +u64 db_id; +u64 comm_db_id; +u64 dso_db_id; +u64 sym_db_id; +u64 offset;/* ip offset from symbol start */ +u64 addr_dso_db_id; +u64 addr_sym_db_id; +u64 addr_offset;/* addr offset from symbol start */ +}; + +struct db_export { +int (*export_evsel)(struct db_export *dbe,struct perf_evsel *evsel); +int (*export_machine)(struct db_export *dbe,struct machine *machine); +int (*export_thread)(struct db_export *dbe,struct thread *thread, +u64 main_thread_db_id,struct machine *machine); +int (*export_comm)(struct db_export *dbe,struct comm *comm); +int (*export_comm_thread)(struct db_export *dbe,u64 db_id, +struct comm *comm,struct thread *thread); +int (*export_dso)(struct db_export *dbe,struct dso *dso, +struct machine *machine); +int (*export_symbol)(struct db_export *dbe,struct symbol *sym, +struct dso *dso); +int (*export_sample)(struct db_export *dbe,struct export_sample *es); +u64 evsel_last_db_id; +u64 machine_last_db_id; +u64 thread_last_db_id; +u64 comm_last_db_id; +u64 comm_thread_last_db_id; +u64 dso_last_db_id; +u64 symbol_last_db_id; +u64 sample_last_db_id; +}; + +int db_export__init (struct db_export *dbe); +void db_export__exit (struct db_export *dbe); +int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel); +int db_export__machine (struct db_export *dbe,struct machine *machine); +int db_export__thread (struct db_export *dbe,struct thread *thread, +struct machine *machine,struct comm *comm); +int db_export__comm (struct db_export *dbe,struct comm *comm, +struct thread *main_thread); +int db_export__comm_thread (struct db_export *dbe,struct comm *comm, +struct thread *thread); +int db_export__dso (struct db_export *dbe,struct dso *dso, +struct machine *machine); +int db_export__symbol (struct db_export *dbe,struct symbol *sym, +struct dso *dso); +int db_export__sample (struct db_export *dbe,union perf_event *event, +struct perf_sample *sample,struct perf_evsel *evsel, +struct thread *thread,struct addr_location *al); + +#endif tools/perf/util/dso.h +1
-0@@ -111,6 +111,7 @@ struct dso {enum dso_swap_type needs_swap;enum dso_binary_type symtab_type;enum dso_binary_type binary_type; +u64 db_id; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1;tools/perf/util/evsel.h +1
-0@@ -95,6 +95,7 @@ struct perf_evsel {int sample_read;struct perf_evsel *leader;char *group_name; +u64 db_id; };union u64_swap {tools/perf/util/machine.h +1
-0@@ -40,6 +40,7 @@ struct machine {u64 kernel_start;symbol_filter_t symbol_filter;pid_t *current_tid; +u64 db_id; }; static inlinetools/perf/util/symbol.h +1
-0@@ -73,6 +73,7 @@ struct symbol {struct rb_node rb_node;u64 start;u64 end; +u64 db_id;u16 namelen;u8 binding;bool ignore;tools/perf/util/thread.h +1
-0@@ -25,6 +25,7 @@ struct thread {bool dead;/* if set thread has exited */ struct list_head comm_list;int comm_len; +u64 db_id;void *priv;struct thread_stack *ts;Signature · 1 line
1.9.1
Namhyung Kim
maintainerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
In reply to Adrian HunterQuoted 10 lines · Adrian Hunter
This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member '
db_id' is used for that. 'db_id' is just a 64-bit sequence number.
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export meta events like task, mmap or some user events to the script optionally and let it process the data?
Thanks,
Namhyung
Quoted 4 lines
Exporting centres around the
db_export__sample()function which exports the associated data structures if they have not yet been allocated adb_id.
Adrian Hunter
reviewerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Quoted 6 lines · Namhyung Kim
Quoted 5 lines · Adrian Hunter
This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
Quoted 8 lines
Quoted 5 lines
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member '
db_id' is used for that. 'db_id' is just a 64-bit sequence number.Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Please explain what you mean by "comfortable".
Quoted 2 lines
meta events like task, mmap or some user events to the script optionally and let it process the data?
Intel PT decoding can generate a lot of data. Many millions of samples at least.
Each sample contains a lot of duplicated information like symbol names, dso names, comms, etc. So it is not optimal to export that for every sample. Even then, each piece of supporting information must be looked up - was it the same as the last sample, no then look it up, was it found, no then add a record. That is very very inefficient.
Exporting each piece of information once, instead of over and over again, is a reasonable thing to do.
Quoted 11 lines
Thanks,
NamhyungQuoted 4 lines
Exporting centres around the
db_export__sample()function which exports the associated data structures if they have not yet been allocated adb_id.
Adrian Hunter
reviewerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Quoted 13 lines · Adrian Hunter
Quoted 12 lines · Namhyung Kim
Quoted 10 lines · Adrian Hunter
This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member '
db_id' is used for that. 'db_id' is just a 64-bit sequence number.
Quoted 4 lines
Quoted 2 lines
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Please explain what you mean by "comfortable".
Or rather: What about it is wrong for core code?
Quoted 30 lines
Quoted 3 lines
meta events like task, mmap or some user events to the script optionally and let it process the data?
Intel PT decoding can generate a lot of data. Many millions of samples at least.
Each sample contains a lot of duplicated information like symbol names, dso names, comms, etc. So it is not optimal to export that for every sample. Even then, each piece of supporting information must be looked up - was it the same as the last sample, no then look it up, was it found, no then add a record. That is very very inefficient.
Exporting each piece of information once, instead of over and over again, is a reasonable thing to do.
Quoted 11 lines
Thanks,
NamhyungQuoted 4 lines
Exporting centres around the
db_export__sample()function which exports the associated data structures if they have not yet been allocated adb_id.
Namhyung Kim
maintainerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Hi Adrian,
Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Please explain what you mean by "comfortable".
Or rather: What about it is wrong for core code?
Well, there's nothing "wrong" about it. :)
But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.
Thanks,
Namhyung
Adrian Hunter
reviewerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Quoted 8 lines · Namhyung Kim
Hi Adrian,
Quoted 5 lines · Adrian Hunter
Quoted 4 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
Please explain what you mean by "comfortable".
Or rather: What about it is wrong for core code?
Well, there's nothing "wrong" about it. :)
But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.
It is pretty self-contained in db-export.[ch] and the scripting engines.
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:
Quoted 13 lines · Namhyung Kim
Quoted 12 lines
Hi Adrian,
Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Please explain what you mean by "comfortable".
Or rather: What about it is wrong for core code?
Quoted 7 lines
Quoted 4 lines
Well, there's nothing "wrong" about it. :)
But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.
It is pretty self-contained in db-export.[ch] and the scripting engines.
So, what I meant was the patch below, on top of yours.
In some data structures where there were no provision for tool specific storage, I added an unnamed union with db_id and a void *priv pointer, where there were, be it a priv pointer, like in perf_evsel, I moved db_id to it, no extra costs for the other tools.
And in struct symbol, I made it use symbol__priv(), that will access the area reserved by setting symbol.priv_size at tool start time, like done for the annotation case in top, report, annotate. I.e. on the tool that will use symbol->db_id you will need to add this just before calling symbol__init():
symbol_conf.priv_size = sizeof(u64);
Since this is all specific to your tool, there should be no problems and I'm now applying this change merged with your original patch + note about that and then moving on to the other patches that make use of it.
- Arnaldo
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 05/16] perf tools: Add facility to export data in database-friendly way
Em Fri, Oct 24, 2014 at 11:40:02AM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 35 lines
Em Fri, Oct 24, 2014 at 04:13:00PM +0300, Adrian Hunter escreveu:
Quoted 13 lines · Namhyung Kim
Quoted 12 lines
Hi Adrian,
Quoted 8 lines · Adrian Hunter
Quoted 5 lines · Adrian Hunter
Quoted 2 lines · Namhyung Kim
Can we do it somewhere in a script not in the core code? I don't feel comfortable to add those bits into the core code. What if we export
Please explain what you mean by "comfortable".
Or rather: What about it is wrong for core code?
Quoted 7 lines
Quoted 4 lines
Well, there's nothing "wrong" about it. :)
But I think if some code is used only by a specific bits, it'd be better hiding it from the rest as much as possible.
It is pretty self-contained in db-export.[ch] and the scripting engines.
So, what I meant was the patch below, on top of yours.
In some data structures where there were no provision for tool specific storage, I added an unnamed union with
db_idand a void *priv pointer, where there were, be it a priv pointer, like inperf_evsel, I moveddb_idto 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 usesymbol->db_idyou will need to add this just before callingsymbol__init():
Quoted 5 lines
symbol_conf.priv_size = sizeof(u64);Since this is all specific to your tool, there should be no problems and I'm now applying this change merged with your original patch + note about that and then moving on to the other patches that make use of it.
Oops, now with the patch:
tools/perf/util/comm.h +4 -1
@@ -10,9 +10,12 @@ struct comm_str;struct comm {struct comm_str *comm_str;u64 start; -u64 db_id;struct list_head list;bool exec; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; };void comm__free (struct comm *comm);
tools/perf/util/db-export.c +9 -7
@@ -150,10 +150,12 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,int db_export__symbol (struct db_export *dbe,struct symbol *sym,struct dso *dso) { -if (sym->db_id) +u64 *sym_db_id =symbol__priv (sym); + +if (*sym_db_id)return 0 ; - sym->db_id = ++dbe->symbol_last_db_id; + *sym_db_id = ++dbe->symbol_last_db_id;if (dbe->export_symbol)return dbe->export_symbol (dbe, sym, dso);
@@ -161,8 +163,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,return 0 ; } -static struct thread *get_main_thread (struct machine *machine, -struct thread *thread) +static struct thread *get_main_thread (struct machine *machine,struct thread *thread) {if (thread->pid_ == thread->tid)return thread;
@@ -194,10 +195,12 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, }if (al->sym) { +u64 *db_id =symbol__priv (al->sym); + err =db_export__symbol (dbe, al->sym, dso);if (err)return err; - *sym_db_id = al->sym->db_id; + *sym_db_id = *db_id; *offset = al->addr - al->sym->start; } }
@@ -253,8 +256,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, sample_addr_correlates_sym(&evsel->attr)) {struct addr_location addr_al; -perf_event__preprocess_sample_addr (event, sample, al->machine, - thread, &addr_al); +perf_event__preprocess_sample_addr (event, sample, thread, &addr_al); err =db_ids_from_al (dbe, &addr_al, &es.addr_dso_db_id, &es.addr_sym_db_id, &es.addr_offset);if (err)
tools/perf/util/dso.h +5 -1
@@ -111,7 +111,6 @@ struct dso {enum dso_swap_type needs_swap;enum dso_binary_type symtab_type;enum dso_binary_type binary_type; -u64 db_id; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1;
@@ -140,6 +139,11 @@ struct dso {struct list_head open_entry; } data; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; +char name[0 ]; };
tools/perf/util/evsel.h +2 -1
@@ -54,6 +54,7 @@ struct cgroup_sel;* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all * is used there is an id sample appended to non-sample events +* @priv: And what is in its containing unnamed union are tool specific */ struct perf_evsel {struct list_head node;
@@ -73,6 +74,7 @@ struct perf_evsel {union {void *priv;off_t id_offset; +u64 db_id; };struct cgroup_sel *cgrp;void *handler;
@@ -92,7 +94,6 @@ struct perf_evsel {int sample_read;struct perf_evsel *leader;char *group_name; -u64 db_id; };union u64_swap {
tools/perf/util/machine.h +4 -1
@@ -40,7 +40,10 @@ struct machine {u64 kernel_start;symbol_filter_t symbol_filter;pid_t *current_tid; -u64 db_id; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; }; static inline
tools/perf/util/symbol.h +0 -1
@@ -73,7 +73,6 @@ struct symbol {struct rb_node rb_node;u64 start;u64 end; -u64 db_id;u16 namelen;u8 binding;bool ignore;
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf tools: Add facility to export data in database-friendly way
In reply to Adrian Hunterperf tools: Add facility to export data in database-friendly way
Commit-ID: 0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92 Gitweb: http://git.kernel.org/tip/0db15b1e84a59e6e1da5fe6e74c35fe52fa29d92 Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:13 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:49 -0200
perf tools: Add facility to export data in database-friendly way
This patch introduces an abstraction for exporting sample data in a database-friendly way. The abstraction does not implement the actual output. A subsequent patch takes this facility into use for extending the script interface.
The abstraction is needed because static data like symbols, dsos, comms etc need to be exported only once. That means allocating them a unique identifier and recording it on each structure. The member 'db_id' is used for that. 'db_id' is just a 64-bit sequence number.
Exporting centres around the db_export__sample() function which exports the associated data structures if they have not yet been allocated a db_id.
--- tools/perf/Makefile.perf | 2 + tools/perf/util/comm.h | 4 + tools/perf/util/db-export.c | 270 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/db-export.h | 86 ++++++++++++++ tools/perf/util/dso.h | 5 + tools/perf/util/evsel.h | 2 + tools/perf/util/machine.h | 4 + tools/perf/util/thread.h | 1 + 8 files changed, 374 insertions(+)
tools/perf/Makefile.perf +2 -0
@@ -258,6 +258,7 @@ LIB_H += util/annotate.h LIB_H += util/cache.h LIB_H += util/callchain.h LIB_H += util/build-id.h +LIB_H += util/db-export.h LIB_H += util/debug.h LIB_H += util/pmu.h LIB_H += util/event.h
@@ -323,6 +324,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o LIB_OBJS += $(OUTPUT)util/build-id.o LIB_OBJS += $(OUTPUT)util/config.o LIB_OBJS += $(OUTPUT)util/ctype.o +LIB_OBJS += $(OUTPUT)util/db-export.o LIB_OBJS += $(OUTPUT)util/pmu.o LIB_OBJS += $(OUTPUT)util/environment.o LIB_OBJS += $(OUTPUT)util/event.o
tools/perf/util/comm.h +4 -0
@@ -12,6 +12,10 @@ struct comm {u64 start;struct list_head list;bool exec; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; };void comm__free (struct comm *comm);
tools/perf/util/db-export.c new file +270 -0
270 lines not on this page · raw message
tools/perf/util/db-export.h new file +86 -0
86 lines not on this page · raw message
tools/perf/util/dso.h +5 -0
@@ -139,6 +139,11 @@ struct dso {struct list_head open_entry; } data; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; +char name[0 ]; };
tools/perf/util/evsel.h +2 -0
@@ -54,6 +54,7 @@ struct cgroup_sel;* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all * is used there is an id sample appended to non-sample events +* @priv: And what is in its containing unnamed union are tool specific */ struct perf_evsel {struct list_head node;
@@ -73,6 +74,7 @@ struct perf_evsel {union {void *priv;off_t id_offset; +u64 db_id; };struct cgroup_sel *cgrp;void *handler;
tools/perf/util/machine.h +4 -0
@@ -40,6 +40,10 @@ struct machine {u64 kernel_start;symbol_filter_t symbol_filter;pid_t *current_tid; +union {/* Tool specific area */ +void *priv; +u64 db_id; + }; }; static inline
tools/perf/util/thread.h +1 -0
@@ -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; };
Adrian Hunter
reviewerPATCH 6/16 perf tools: Extend Python script interface to export data in a database-friendly way
In reply to Adrian HunterUse the new db_export facility to export data in a database-friendly way.
A Python script selects the db_export mode by setting a global variable 'perf_db_export_mode' to True. The script then optionally implements functions to receive table rows. The functions are:
evsel_table
machine_table
thread_table
comm_table
dso_table
symbol_table
sample_table
An example script is provided in a subsequent patch.
--- .../util/scripting-engines/trace-event-python.c | 281 ++++++++++++++++++++- 1 file changed, 279 insertions(+), 2 deletions(-)
tools/perf/util/scripting-engines/trace-event-python.c +279 -2
@@ -24,6 +24,7 @@#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdbool.h> #include <errno.h> #include "../../perf.h"
@@ -33,6 +34,9 @@#include "../util.h" #include "../event.h" #include "../thread.h" +#include "../comm.h" +#include "../machine.h" +#include "../db-export.h" #include "../trace-event.h" #include "../machine.h"
@@ -53,6 +57,21 @@ static int zero_flag_atom;static PyObject *main_module, *main_dict; +struct tables { +struct db_export dbe; +PyObject *evsel_handler; +PyObject *machine_handler; +PyObject *thread_handler; +PyObject *comm_handler; +PyObject *comm_thread_handler; +PyObject *dso_handler; +PyObject *symbol_handler; +PyObject *sample_handler; +bool db_export_mode; +}; + +static struct tables tables_global; + static void handler_call_die(const char *handler_name) NORETURN;static void handler_call_die (const char *handler_name) {
@@ -475,6 +494,210 @@ static void python_process_tracepoint(struct perf_sample *sample,Py_DECREF (t); } +static PyObject *tuple_new (unsigned int sz) +{ +PyObject *t; + + t =PyTuple_New (sz); +if (!t) +Py_FatalError ("couldn't create Python tuple" ); +return t; +} + +static int tuple_set_u64 (PyObject *t,unsigned int pos,u64 val) +{ +#if BITS_PER_LONG ==64 +return PyTuple_SetItem (t, pos,PyInt_FromLong (val)); +#endif +#if BITS_PER_LONG ==32 +return PyTuple_SetItem (t, pos,PyLong_FromLongLong (val)); +#endif +} + +static int tuple_set_s32 (PyObject *t,unsigned int pos,s32 val) +{ +return PyTuple_SetItem (t, pos,PyInt_FromLong (val)); +} + +static int tuple_set_string (PyObject *t,unsigned int pos,const char *s) +{ +return PyTuple_SetItem (t, pos,PyString_FromString (s)); +} + +static int python_export_evsel (struct db_export *dbe,struct perf_evsel *evsel) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (2 ); + +tuple_set_u64 (t,0 , evsel->db_id); +tuple_set_string (t,1 ,perf_evsel__name (evsel)); + +call_object (tables->evsel_handler, t,"evsel_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_machine (struct db_export *dbe, +struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (3 ); + +tuple_set_u64 (t,0 , machine->db_id); +tuple_set_s32 (t,1 , machine->pid); +tuple_set_string (t,2 , machine->root_dir ? machine->root_dir :"" ); + +call_object (tables->machine_handler, t,"machine_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_thread (struct db_export *dbe,struct thread *thread, +u64 main_thread_db_id,struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (5 ); + +tuple_set_u64 (t,0 , thread->db_id); +tuple_set_u64 (t,1 , machine->db_id); +tuple_set_u64 (t,2 , main_thread_db_id); +tuple_set_s32 (t,3 , thread->pid_); +tuple_set_s32 (t,4 , thread->tid); + +call_object (tables->thread_handler, t,"thread_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_comm (struct db_export *dbe,struct comm *comm) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (2 ); + +tuple_set_u64 (t,0 , comm->db_id); +tuple_set_string (t,1 ,comm__str (comm)); + +call_object (tables->comm_handler, t,"comm_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_comm_thread (struct db_export *dbe,u64 db_id, +struct comm *comm,struct thread *thread) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (3 ); + +tuple_set_u64 (t,0 , db_id); +tuple_set_u64 (t,1 , comm->db_id); +tuple_set_u64 (t,2 , thread->db_id); + +call_object (tables->comm_thread_handler, t,"comm_thread_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_dso (struct db_export *dbe,struct dso *dso, +struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +char sbuild_id[BUILD_ID_SIZE *2 +1 ]; +PyObject *t; + +build_id__sprintf (dso->build_id,sizeof (dso->build_id), sbuild_id); + + t =tuple_new (5 ); + +tuple_set_u64 (t,0 , dso->db_id); +tuple_set_u64 (t,1 , machine->db_id); +tuple_set_string (t,2 , dso->short_name); +tuple_set_string (t,3 , dso->long_name); +tuple_set_string (t,4 , sbuild_id); + +call_object (tables->dso_handler, t,"dso_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_symbol (struct db_export *dbe,struct symbol *sym, +struct dso *dso) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (6 ); + +tuple_set_u64 (t,0 , sym->db_id); +tuple_set_u64 (t,1 , dso->db_id); +tuple_set_u64 (t,2 , sym->start); +tuple_set_u64 (t,3 , sym->end); +tuple_set_s32 (t,4 , sym->binding); +tuple_set_string (t,5 , sym->name); + +call_object (tables->symbol_handler, t,"symbol_table" ); + +Py_DECREF (t); + +return 0 ; +} + +static int python_export_sample (struct db_export *dbe, +struct export_sample *es) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (19 ); + +tuple_set_u64 (t,0 , es->db_id); +tuple_set_u64 (t,1 , es->evsel->db_id); +tuple_set_u64 (t,2 , es->al->machine->db_id); +tuple_set_u64 (t,3 , es->thread->db_id); +tuple_set_u64 (t,4 , es->comm_db_id); +tuple_set_u64 (t,5 , es->dso_db_id); +tuple_set_u64 (t,6 , es->sym_db_id); +tuple_set_u64 (t,7 , es->offset); +tuple_set_u64 (t,8 , es->sample->ip); +tuple_set_u64 (t,9 , es->sample->time); +tuple_set_s32 (t,10 , es->sample->cpu); +tuple_set_u64 (t,11 , es->addr_dso_db_id); +tuple_set_u64 (t,12 , es->addr_sym_db_id); +tuple_set_u64 (t,13 , es->addr_offset); +tuple_set_u64 (t,14 , es->sample->addr); +tuple_set_u64 (t,15 , es->sample->period); +tuple_set_u64 (t,16 , es->sample->weight); +tuple_set_u64 (t,17 , es->sample->transaction); +tuple_set_u64 (t,18 , es->sample->data_src); + +call_object (tables->sample_handler, t,"sample_table" ); + +Py_DECREF (t); + +return 0 ; +} + static void python_process_general_event(struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread,
@@ -551,19 +774,25 @@ exit:Py_DECREF (t); } -static void python_process_event (union perf_event *event__maybe_unused , +static void python_process_event (union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al) { +struct tables *tables = &tables_global; +switch (evsel->attr.type) {case PERF_TYPE_TRACEPOINT :python_process_tracepoint (sample, evsel, thread, al);break ;/* Reserve for future process_hw/sw/raw APIs */ default : -python_process_general_event (sample, evsel, thread, al); +if (tables->db_export_mode) +db_export__sample (&tables->dbe, event, sample, evsel, + thread, al); +else +python_process_general_event (sample, evsel, thread, al); } }
@@ -589,11 +818,53 @@ error:return -1 ; } +#define SET_TABLE_HANDLER_ (name, handler_name, table_name) do { \ + tables->handler_name = get_handler(#table_name); \ + if (tables->handler_name) \ + tables->dbe.export_ ## name = python_export_ ## name; \ +} while (0) + +#define SET_TABLE_HANDLER (name) \ + SET_TABLE_HANDLER_(name, name ## _handler, name ## _table) + +static void set_table_handlers (struct tables *tables) +{ +const char *perf_db_export_mode ="perf_db_export_mode" ; +PyObject *db_export_mode; +int ret; + +memset (tables,0 ,sizeof (struct tables )); +if (db_export__init (&tables->dbe)) +Py_FatalError ("failed to initialize export" ); + + db_export_mode =PyDict_GetItemString (main_dict, perf_db_export_mode); +if (!db_export_mode) +return ; + + ret =PyObject_IsTrue (db_export_mode); +if (ret ==-1 ) +handler_call_die (perf_db_export_mode); +if (!ret) +return ; + + tables->db_export_mode = true; + +SET_TABLE_HANDLER (evsel); +SET_TABLE_HANDLER (machine); +SET_TABLE_HANDLER (thread); +SET_TABLE_HANDLER (comm); +SET_TABLE_HANDLER (comm_thread); +SET_TABLE_HANDLER (dso); +SET_TABLE_HANDLER (symbol); +SET_TABLE_HANDLER (sample); +} +/* * Start trace script */ static int python_start_script (const char *script,int argc,const char **argv) { +struct tables *tables = &tables_global;const char **command_line;char buf[PATH_MAX ];int i, err =0 ;
@@ -632,6 +903,8 @@ static int python_start_script(const char *script, int argc, const char **argv)free (command_line); +set_table_handlers (tables); +return err; error:Py_Finalize ();
@@ -650,8 +923,12 @@ static int python_flush_script(void)*/ static int python_stop_script (void ) { +struct tables *tables = &tables_global; +try_call_object ("trace_end" ,NULL ); +db_export__exit (&tables->dbe); +Py_XDECREF (main_dict);Py_XDECREF (main_module);Py_Finalize ();
Signature · 1 line
1.9.1
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf scripting python: Extend interface to export data in a database-friendly way
perf scripting python: Extend interface to export data in a database-friendly way
Commit-ID: df919b400ad3f9e6aac392ce421d710207abf9be Gitweb: http://git.kernel.org/tip/df919b400ad3f9e6aac392ce421d710207abf9be Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:14 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:49 -0200
perf scripting python: Extend interface to export data in a database-friendly way
Use the new db_export facility to export data in a database-friendly way.
A Python script selects the db_export mode by setting a global variable 'perf_db_export_mode' to True. The script then optionally implements functions to receive table rows. The functions are:
evsel_table
machine_table
thread_table
comm_table
dso_table
symbol_table
sample_table
An example script is provided in a subsequent patch.
--- .../util/scripting-engines/trace-event-python.c | 286 ++++++++++++++++++++- 1 file changed, 284 insertions(+), 2 deletions(-)
tools/perf/util/scripting-engines/trace-event-python.c +284 -2
@@ -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 ();
Adrian Hunter
reviewerPATCH 7/16 perf tools: Add Python script to export to postgresql
In reply to Adrian HunterAdd a Python script to export to a postgresql database. The script requires the Python psycopg2 module. The caller of the script must be able to create postgresql databases.
The script takes the database name as a parameter. The database and database tables are created. Data is written to flat files which are then imported using SQL COPY FROM.
Example:
$ perf record ls ... $ perf script report export-to-postgresql lsdb 2014-02-14 10:55:38.631431 Creating database... 2014-02-14 10:55:39.291958 Writing to intermediate files... 2014-02-14 10:55:39.350280 Copying to database... 2014-02-14 10:55:39.358536 Removing intermediate files... 2014-02-14 10:55:39.358665 Adding primary keys 2014-02-14 10:55:39.658697 Adding foreign keys 2014-02-14 10:55:39.667412 Done $ psql lsdb lsdb=# select * from samples_view;
id | time | cpu | pid | tid | command | event | ip_hex | symbol | sym_offset | dso_short_name | to_ip_hex | to_symbol | to_sym_offset | to_dso_short_name
----+-----------------+-----+-------+-------+---------+--------+------------------+------------------------------+------------+-------------------+-----------+-----------+---------------+-------------------
1 | 592112202825015 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown 2 | 592112203258804 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown 3 | 592112203988119 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown
--- .../scripts/python/bin/export-to-postgresql-record | 8 + .../scripts/python/bin/export-to-postgresql-report | 24 ++ tools/perf/scripts/python/export-to-postgresql.py | 360 +++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
tools/perf/scripts/python/bin/export-to-postgresql-record new file +8 -0
@@ -0,0 +1,8 @@ +#!/bin/bash + +# +# export perf data to a postgresql database. Can cover +# perf ip samples (excluding the tracepoints). No special +# record requirements, just record what you want to export. +# +perf record $@
tools/perf/scripts/python/bin/export-to-postgresql-report new file +24 -0
@@ -0,0 +1,24 @@ +#!/bin/bash +# description: export perf data to a postgresql database +# args: [database name] [columns] +n_args=0 +for i in "$@" +do + if expr match "$i" "-" > /dev/null ; then + break + fi + n_args=$(( $n_args + 1 )) +done +if [ "$n_args" -gt 2 ] ; then + echo "usage: export-to-postgresql-report [database name] [columns]" + exit +fi +if [ "$n_args" -gt 1 ] ; then + dbname=$1 + columns=$2 + shift 2 +elif [ "$n_args" -gt 0 ] ; then + dbname=$1 + shift +fi +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
tools/perf/scripts/python/export-to-postgresql.py new file +360 -0
360 lines not on this page · raw message
Signature · 1 line
1.9.1
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf script: Add Python script to export to postgresql
perf script: Add Python script to export to postgresql
Commit-ID: 2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b Gitweb: http://git.kernel.org/tip/2987e32f75dcb40bce0f3ab1d5d48cc1c580fd8b Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:15 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:49 -0200
perf script: Add Python script to export to postgresql
Add a Python script to export to a postgresql database.
The script requires the Python pyside module and the Qt PostgreSQL driver. The packages needed are probably named "python-pyside" and "libqt4-sql-psql"
The caller of the script must be able to create postgresql databases.
The script takes the database name as a parameter. The database and database tables are created. Data is written to flat files which are then imported using SQL COPY FROM.
Example:
$ perf record ls
...
$ perf script report export-to-postgresql lsdb
2014-02-14 10:55:38.631431 Creating database...
2014-02-14 10:55:39.291958 Writing to intermediate files...
2014-02-14 10:55:39.350280 Copying to database...
2014-02-14 10:55:39.358536 Removing intermediate files...
2014-02-14 10:55:39.358665 Adding primary keys
2014-02-14 10:55:39.658697 Adding foreign keys
2014-02-14 10:55:39.667412 Done
$ psql lsdb
lsdb-# \d
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+-------
public | comm_threads | table | acme
public | comms | table | acme
public | dsos | table | acme
public | machines | table | acme
public | samples | table | acme
public | samples_view | view | acme
public | selected_events | table | acme
public | symbols | table | acme
public | threads | table | acme
(9 rows)
lsdb-# \d samples
Table "public.samples"
Column | Type | Modifiers
---------------+---------+-----------
id | bigint | not null
evsel_id | bigint |
machine_id | bigint |
thread_id | bigint |
comm_id | bigint |
dso_id | bigint |
symbol_id | bigint |
sym_offset | bigint |
ip | bigint |
time | bigint |
cpu | integer |
to_dso_id | bigint |
to_symbol_id | bigint |
to_sym_offset | bigint |
to_ip | bigint |
period | bigint |
weight | bigint |
transaction | bigint |
data_src | bigint |
Indexes:
"samples_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"commfk" FOREIGN KEY (comm_id) REFERENCES comms(id)
"dsofk" FOREIGN KEY (dso_id) REFERENCES dsos(id)
"evselfk" FOREIGN KEY (evsel_id) REFERENCES selected_events(id)
"machinefk" FOREIGN KEY (machine_id) REFERENCES machines(id)
"symbolfk" FOREIGN KEY (symbol_id) REFERENCES symbols(id)
"threadfk" FOREIGN KEY (thread_id) REFERENCES threads(id)
"todsofk" FOREIGN KEY (to_dso_id) REFERENCES dsos(id)
"tosymbolfk" FOREIGN KEY (to_symbol_id) REFERENCES symbols(id) lsdb-# \d samples_view
View "public.samples_view"
Column | Type | Modifiers
-------------------+-------------------------+-----------
id | bigint | time | bigint | cpu | integer | pid | integer | tid | integer | command | character varying(16) | event | character varying(80) | ip_hex | text | symbol | character varying(2048) | sym_offset | bigint | dso_short_name | character varying(256) | to_ip_hex | text | to_symbol | character varying(2048) | to_sym_offset | bigint | to_dso_short_name | character varying(256) |
lsdb=# select * from samples_view;
id| time |cpu | pid | tid |command| event | ip_hex | symbol |sym_off| dso_name|to_ip_hex|to_symbol|to_sym_off|to_dso_name
--+------------+----+------+------+-------+--------+---------------+---------------------+-------+---------+---------+---------+----------+----------
1 |12202825015 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe| 10 | [kernel]| 0 | unknown | 0| unknown 2 |12203258804 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe| 10 | [kernel]| 0 | unknown | 0| unknown 3 |12203988119 | -1 | 7339 | 7339 |:17339 | cycles | fffff8104d24a |native_write_msr_safe| 10 | [kernel]| 0 | unknown | 0| unknown
My notes (which may be out-of-date) on setting up postgresql so you can create databases:
fedora:
$ sudo yum install postgresql postgresql-server python-pyside qt-postgresql
$ sudo su - postgres -c initdb
$ sudo service postgresql start
$ sudo su - postgres
$ createuser -s <your username>I used the the unix user name in createuser.
If it fails, try createuser without -s and answer the following question to allow your user to create tables:
Shall the new role be a superuser? (y/n) y
ubuntu:
$ sudo apt-get install postgresql
$ sudo su - postgres
$ createuser <your username>
Shall the new role be a superuser? (y/n) yYou may want to disable automatic startup. One way is to edit /etc/postgresql/9.3/main/start.conf. Another is to disable the init script e.g. sudo update-rc.d postgresql disable
--- .../scripts/python/bin/export-to-postgresql-record | 8 + .../scripts/python/bin/export-to-postgresql-report | 24 ++ tools/perf/scripts/python/export-to-postgresql.py | 360 +++++++++++++++++++++ 3 files changed, 392 insertions(+)
tools/perf/scripts/python/bin/export-to-postgresql-record new file +8 -0
@@ -0,0 +1,8 @@ +#!/bin/bash + +# +# export perf data to a postgresql database. Can cover +# perf ip samples (excluding the tracepoints). No special +# record requirements, just record what you want to export. +# +perf record $@
tools/perf/scripts/python/bin/export-to-postgresql-report new file +24 -0
@@ -0,0 +1,24 @@ +#!/bin/bash +# description: export perf data to a postgresql database +# args: [database name] [columns] +n_args=0 +for i in "$@" +do + if expr match "$i" "-" > /dev/null ; then + break + fi + n_args=$(( $n_args + 1 )) +done +if [ "$n_args" -gt 2 ] ; then + echo "usage: export-to-postgresql-report [database name] [columns]" + exit +fi +if [ "$n_args" -gt 1 ] ; then + dbname=$1 + columns=$2 + shift 2 +elif [ "$n_args" -gt 0 ] ; then + dbname=$1 + shift +fi +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
tools/perf/scripts/python/export-to-postgresql.py new file +360 -0
360 lines not on this page · raw message
Adrian Hunter
reviewerPATCH 8/16 perf tools: Add branch type to db export
In reply to Adrian HunterAdd the ability to export branch types through the database export facility.
--- tools/perf/util/db-export.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/db-export.h | 6 ++++++ 2 files changed, 54 insertions(+)
tools/perf/util/db-export.c +48 -0
@@ -205,6 +205,15 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,return 0 ; } +int db_export__branch_type (struct db_export *dbe,u32 branch_type, +const char *name) +{ +if (dbe->export_branch_type) +return dbe->export_branch_type (dbe, branch_type, name); + +return 0 ; +} +int db_export__sample (struct db_export *dbe,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al)
@@ -266,3 +275,42 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,return 0 ; } + +static struct { +u32 branch_type; +const char *name; +} branch_types[] = { + {0 ,"no branch" }, + {PERF_FLAG_BRANCH |PERF_FLAG_CALL ,"call" }, + {PERF_FLAG_BRANCH |PERF_FLAG_RETURN ,"return" }, + {PERF_FLAG_BRANCH |PERF_FLAG_CONDITIONAL ,"conditional jump" }, + {PERF_FLAG_BRANCH ,"unconditional jump" }, + {PERF_FLAG_BRANCH |PERF_FLAG_CALL |PERF_FLAG_INTERRUPT , +"software interrupt" }, + {PERF_FLAG_BRANCH |PERF_FLAG_RETURN |PERF_FLAG_INTERRUPT , +"return from interrupt" }, + {PERF_FLAG_BRANCH |PERF_FLAG_CALL |PERF_FLAG_SYSCALLRET , +"system call" }, + {PERF_FLAG_BRANCH |PERF_FLAG_RETURN |PERF_FLAG_SYSCALLRET , +"return from system call" }, + {PERF_FLAG_BRANCH |PERF_FLAG_ASYNC ,"asynchronous branch" }, + {PERF_FLAG_BRANCH |PERF_FLAG_CALL |PERF_FLAG_ASYNC | +PERF_FLAG_INTERRUPT ,"hardware interrupt" }, + {PERF_FLAG_BRANCH |PERF_FLAG_TX_ABORT ,"transaction abort" }, + {PERF_FLAG_BRANCH |PERF_FLAG_TRACE_BEGIN ,"trace begin" }, + {PERF_FLAG_BRANCH |PERF_FLAG_TRACE_END ,"trace end" }, + {0 ,NULL } +}; + +int db_export__branch_types (struct db_export *dbe) +{ +int i, err =0 ; + +for (i =0 ; branch_types[i].name ; i++) { + err =db_export__branch_type (dbe, branch_types[i].branch_type, + branch_types[i].name); +if (err) +break ; + } +return err; +}
tools/perf/util/db-export.h +6 -0
@@ -54,6 +54,8 @@ struct db_export { struct machine *machine);int (*export_symbol)(struct db_export *dbe,struct symbol *sym,struct dso *dso); +int (*export_branch_type)(struct db_export *dbe,u32 branch_type, +const char *name);int (*export_sample)(struct db_export *dbe,struct export_sample *es);u64 evsel_last_db_id;u64 machine_last_db_id;
@@ -79,8 +81,12 @@ int db_export__dso(struct db_export *dbe, struct dso *dso, struct machine *machine);int db_export__symbol (struct db_export *dbe,struct symbol *sym,struct dso *dso); +int db_export__branch_type (struct db_export *dbe,u32 branch_type, +const char *name);int db_export__sample (struct db_export *dbe,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al); +int db_export__branch_types (struct db_export *dbe); +#endif
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 9/16 perf tools: Add branch_type and in_tx to Python export
In reply to Adrian HunterAdd branch_type and in_tx to Python db export and
the export-to-postgresql.py script.
--- tools/perf/scripts/python/export-to-postgresql.py | 32 ++++++++++++++++++---- .../util/scripting-engines/trace-event-python.c | 30 +++++++++++++++++++- 2 files changed, 55 insertions(+), 7 deletions(-)
tools/perf/scripts/python/export-to-postgresql.py +26 -6
@@ -123,6 +123,10 @@ do_query(query, 'CREATE TABLE symbols (' 'sym_end bigint,' 'binding integer,' 'name varchar(2048))') +do_query(query, 'CREATE TABLE branch_types (' + 'id integer NOT NULL,' + 'name varchar(80))') + if branches: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,'
@@ -139,7 +143,9 @@ if branches: 'to_dso_id bigint,' 'to_symbol_id bigint,' 'to_sym_offset bigint,' - 'to_ip bigint)') + 'to_ip bigint,' + 'branch_type integer,' + 'in_tx boolean)') else: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,'
@@ -160,7 +166,9 @@ else: 'period bigint,' 'weight bigint,' 'transaction bigint,' - 'data_src bigint)') + 'data_src bigint,' + 'branch_type integer,' + 'in_tx boolean)') do_query(query, 'CREATE VIEW samples_view AS ' 'SELECT '
@@ -178,7 +186,9 @@ do_query(query, 'CREATE VIEW samples_view AS ' 'to_hex(to_ip) AS to_ip_hex,' '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' 'to_sym_offset,' - '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name' + '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' + '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' + 'in_tx' ' FROM samples')
@@ -234,6 +244,7 @@ comm_file = open_output_file("comm_table.bin") comm_thread_file = open_output_file("comm_thread_table.bin") dso_file = open_output_file("dso_table.bin") symbol_file = open_output_file("symbol_table.bin") +branch_type_file = open_output_file("branch_type_table.bin") sample_file = open_output_file("sample_table.bin") def trace_begin():
@@ -257,6 +268,7 @@ def trace_end(): copy_output_file(comm_thread_file, "comm_threads") copy_output_file(dso_file, "dsos") copy_output_file(symbol_file, "symbols") + copy_output_file(branch_type_file, "branch_types") copy_output_file(sample_file, "samples") print datetime.datetime.today(), "Removing intermediate files..."
@@ -267,6 +279,7 @@ def trace_end(): remove_output_file(comm_thread_file) remove_output_file(dso_file) remove_output_file(symbol_file) + remove_output_file(branch_type_file) remove_output_file(sample_file) os.rmdir(output_dir_name) print datetime.datetime.today(), "Adding primary keys"
@@ -277,6 +290,7 @@ def trace_end(): do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') + do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') print datetime.datetime.today(), "Adding foreign keys"
@@ -352,9 +366,15 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) symbol_file.write(value) -def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x): +def branch_type_table(branch_type, name, *x): + n = len(name) + fmt = "!hiii" + str(n) + "s" + value = struct.pack(fmt, 2, 4, branch_type, n, name) + branch_type_file.write(value) + +def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x): if branches: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx) else: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx) sample_file.write(value)
tools/perf/util/scripting-engines/trace-event-python.c +29 -1
@@ -66,6 +66,7 @@ struct tables {PyObject *comm_thread_handler;PyObject *dso_handler;PyObject *symbol_handler; +PyObject *branch_type_handler;PyObject *sample_handler;bool db_export_mode; };
@@ -663,13 +664,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,return 0 ; } +static int python_export_branch_type (struct db_export *dbe,u32 branch_type, +const char *name) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (2 ); + +tuple_set_s32 (t,0 , branch_type); +tuple_set_string (t,1 , name); + +call_object (tables->branch_type_handler, t,"branch_type_table" ); + +Py_DECREF (t); + +return 0 ; +} +static int python_export_sample (struct db_export *dbe,struct export_sample *es) { struct tables *tables = container_of(dbe, struct tables, dbe);PyObject *t; - t =tuple_new (19 ); + t =tuple_new (21 );tuple_set_u64 (t,0 , es->db_id);tuple_set_u64 (t,1 , es->evsel->db_id);
@@ -690,6 +709,8 @@ static int python_export_sample(struct db_export *dbe,tuple_set_u64 (t,16 , es->sample->weight);tuple_set_u64 (t,17 , es->sample->transaction);tuple_set_u64 (t,18 , es->sample->data_src); +tuple_set_s32 (t,19 , es->sample->flags &PERF_BRANCH_MASK ); +tuple_set_s32 (t,20 , !!(es->sample->flags &PERF_FLAG_IN_TX ));call_object (tables->sample_handler, t,"sample_table" );
@@ -856,6 +877,7 @@ static void set_table_handlers(struct tables *tables)SET_TABLE_HANDLER (comm_thread);SET_TABLE_HANDLER (dso);SET_TABLE_HANDLER (symbol); +SET_TABLE_HANDLER (branch_type);SET_TABLE_HANDLER (sample); }
@@ -905,6 +927,12 @@ static int python_start_script(const char *script, int argc, const char **argv)set_table_handlers (tables); +if (tables->db_export_mode) { + err =db_export__branch_types (&tables->dbe); +if (err) + goto error; + } +return err; error:Py_Finalize ();
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 10/16 perf tools: Enhance the thread stack to output call/return data
In reply to Adrian HunterEnhance the thread stack to output detailed information about paired calls and returns.
--- tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/thread-stack.h | 47 ++++ 2 files changed, 590 insertions(+), 4 deletions(-)
tools/perf/util/thread-stack.c +543 -4
@@ -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 2048struct thread_stack_entry {u64 ret_addr; +u64 timestamp; +u64 ref; +u64 branch_count; +struct call_path *cp; +bool no_call; };struct thread_stack {
@@ -29,6 +62,11 @@ struct thread_stack {size_t cnt;size_t sz;u64 trace_nr; +u64 branch_count; +u64 kernel_start; +u64 last_time; +struct call_return_processor *crp; +struct comm *comm; };static void thread_stack__grow (struct thread_stack *ts)
@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts) } } -static struct thread_stack *thread_stack__new (void ) +static struct thread_stack *thread_stack__new (struct thread *thread, +struct call_return_processor *crp) {struct thread_stack *ts;
@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)return NULL ; } +if (thread->mg && thread->mg->machine) + ts->kernel_start =machine__kernel_start (thread->mg->machine); +else + ts->kernel_start =1ULL <<63 ; + ts->crp = crp; +return ts; }
@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) } } +static bool thread_stack__in_kernel (struct thread_stack *ts) +{ +if (!ts->cnt) +return false; + +return ts->stack[ts->cnt -1 ].cp->in_kernel; +} + +static int thread_stack__call_return (struct thread *thread, +struct thread_stack *ts,size_t idx, +u64 timestamp,u64 ref,bool no_return) +{ +struct call_return_processor *crp = ts->crp; +struct thread_stack_entry *tse; +struct call_return cr = { + .thread = thread, + .comm = ts->comm, + .db_id =0 , + }; + + tse = &ts->stack[idx]; + cr.cp = tse->cp; + cr.call_time = tse->timestamp; + cr.return_time = timestamp; + cr.branch_count = ts->branch_count - tse->branch_count; + cr.call_ref = tse->ref; + cr.return_ref = ref; +if (tse->no_call) + cr.flags |=CALL_RETURN_NO_CALL ; +if (no_return) + cr.flags |=CALL_RETURN_NO_RETURN ; + +return crp->process (&cr, crp->data); +} + +static int thread_stack__flush (struct thread *thread,struct thread_stack *ts) +{ +struct call_return_processor *crp = ts->crp; +int err; + +if (!crp) { + ts->cnt =0 ; +return 0 ; + } + +while (ts->cnt) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + ts->last_time,0 , true); +if (err) { +pr_err ("Error flushing thread stack!\n" ); + ts->cnt =0 ; +return err; + } + } + +return 0 ; +} +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr) {
@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,return ;if (!thread->ts) { - thread->ts =thread_stack__new (); + thread->ts =thread_stack__new (thread,NULL );if (!thread->ts)return ; thread->ts->trace_nr = trace_nr; }if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); thread->ts->trace_nr = trace_nr; - thread->ts->cnt =0 ; } +/* Stop here if thread_stack__process() is in use */ +if (thread->ts->crp) +return ; +if (flags &PERF_FLAG_CALL ) {u64 ret_addr;
@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, } } +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr) +{ +if (!thread || !thread->ts) +return ; + +if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); + thread->ts->trace_nr = trace_nr; + } +} +void thread_stack__free (struct thread *thread) {if (thread->ts) { +thread_stack__flush (thread, thread->ts);zfree (&thread->ts->stack);zfree (&thread->ts); }
@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,for (i =1 ; i < chain->nr; i++) chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; } + +static void call_path__init (struct call_path *cp,struct call_path *parent, +struct symbol *sym,u64 ip,bool in_kernel) +{ + cp->parent = parent; + cp->sym = sym; + cp->ip = sym ?0 : ip; + cp->db_id =0 ; + cp->in_kernel = in_kernel; +RB_CLEAR_NODE (&cp->rb_node); + cp->children =RB_ROOT ; +} + +static struct call_path_root *call_path_root__new (void ) +{ +struct call_path_root *cpr; + + cpr =zalloc (sizeof (struct call_path_root )); +if (!cpr) +return NULL ; +call_path__init (&cpr->call_path,NULL ,NULL ,0 , false); +INIT_LIST_HEAD (&cpr->blocks); +return cpr; +} + +static void call_path_root__free (struct call_path_root *cpr) +{ +struct call_path_block *pos, *n; + +list_for_each_entry_safe (pos, n, &cpr->blocks, node) { +list_del (&pos->node); +free (pos); + } +free (cpr); +} + +static struct call_path *call_path__new (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip, +bool in_kernel) +{ +struct call_path_block *cpb; +struct call_path *cp; +size_t n; + +if (cpr->next < cpr->sz) { + cpb = list_last_entry(&cpr->blocks, struct call_path_block, + node); + }else { + cpb =zalloc (sizeof (struct call_path_block )); +if (!cpb) +return NULL ; +list_add_tail (&cpb->node, &cpr->blocks); + cpr->sz +=CALL_PATH_BLOCK_SIZE ; + } + + n = cpr->next++ &CALL_PATH_BLOCK_MASK ; + cp = &cpb->cp[n]; + +call_path__init (cp, parent, sym, ip, in_kernel); + +return cp; +} + +static struct call_path *call_path__findnew (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip,u64 ks) +{ +struct rb_node **p; +struct rb_node *node_parent =NULL ; +struct call_path *cp; +bool in_kernel = ip >= ks; + +if (sym) + ip =0 ; + +if (!parent) +return call_path__new (cpr, parent, sym, ip, in_kernel); + + p = &parent->children.rb_node; +while (*p !=NULL ) { + node_parent = *p; + cp = rb_entry(node_parent, struct call_path, rb_node); + +if (cp->sym == sym && cp->ip == ip) +return cp; + +if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + + cp =call_path__new (cpr, parent, sym, ip, in_kernel); +if (!cp) +return NULL ; + +rb_link_node (&cp->rb_node, node_parent, p); +rb_insert_color (&cp->rb_node, &parent->children); + +return cp; +} + +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data) +{ +struct call_return_processor *crp; + + crp =zalloc (sizeof (struct call_return_processor )); +if (!crp) +return NULL ; + crp->cpr =call_path_root__new (); +if (!crp->cpr) + goto out_free; + crp->process = process; + crp->data = data; +return crp; + +out_free: +free (crp); +return NULL ; +} + +void call_return_processor__free (struct call_return_processor *crp) +{ +if (crp) { +call_path_root__free (crp->cpr); +free (crp); + } +} + +static int thread_stack__push_cp (struct thread_stack *ts,u64 ret_addr, +u64 timestamp,u64 ref,struct call_path *cp, +bool no_call) +{ +struct thread_stack_entry *tse; + +if (ts->cnt == ts->sz) { +thread_stack__grow (ts); +if (ts->cnt == ts->sz) +return -ENOMEM ; + } + + tse = &ts->stack[ts->cnt++]; + tse->ret_addr = ret_addr; + tse->timestamp = timestamp; + tse->ref = ref; + tse->branch_count = ts->branch_count; + tse->cp = cp; + tse->no_call = no_call; + +return 0 ; +} + +static int thread_stack__pop_cp (struct thread *thread,struct thread_stack *ts, +u64 ret_addr,u64 timestamp,u64 ref, +struct symbol *sym) +{ +int err; + +if (!ts->cnt) +return 1 ; + +if (ts->cnt ==1 ) { +struct thread_stack_entry *tse = &ts->stack[0 ]; + +if (tse->cp->sym == sym) +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr != ret_addr) +continue ; + i +=1 ; +while (ts->cnt > i) { + err =thread_stack__call_return (thread, ts, + --ts->cnt, + timestamp, ref, + true); +if (err) +return err; + } +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + } + +return 1 ; +} + +static int thread_stack__bottom (struct thread *thread,struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +struct symbol *sym; +u64 ip; + +if (sample->ip) { + ip = sample->ip; + sym = from_al->sym; + }else if (sample->addr) { + ip = sample->addr; + sym = to_al->sym; + }else { +return 0 ; + } + + cp =call_path__findnew (cpr, &cpr->call_path, sym, ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + +return thread_stack__push_cp (thread->ts, ip, sample->time, ref, cp, + true); +} + +static int thread_stack__no_call_return (struct thread *thread, +struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp, *parent; +u64 ks = ts->kernel_start; +int err; + +if (sample->ip >= ks && sample->addr < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + +/* If the stack is empty, push the userspace address */ +if (!ts->cnt) { + cp =call_path__findnew (cpr, &cpr->call_path, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; +return thread_stack__push_cp (ts,0 , sample->time, ref, + cp, true); + } + }else if (thread_stack__in_kernel (ts) && sample->ip < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + } + +if (ts->cnt) + parent = ts->stack[ts->cnt -1 ].cp; +else + parent = &cpr->call_path; + +/* This 'return' had no 'call', so push and pop top of stack */ + cp =call_path__findnew (cpr, parent, from_al->sym, sample->ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + err =thread_stack__push_cp (ts, sample->addr, sample->time, ref, cp, + true); +if (err) +return err; + +return thread_stack__pop_cp (thread, ts, sample->addr, sample->time, ref, + to_al->sym); +} + +static int thread_stack__trace_begin (struct thread *thread, +struct thread_stack *ts,u64 timestamp, +u64 ref) +{ +struct thread_stack_entry *tse; +int err; + +if (!ts->cnt) +return 0 ; + +/* Pop trace end */ + tse = &ts->stack[ts->cnt -1 ]; +if (tse->cp->sym ==NULL && tse->cp->ip ==0 ) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); +if (err) +return err; + } + +return 0 ; +} + +static int thread_stack__trace_end (struct thread_stack *ts, +struct perf_sample *sample,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +/* No point having 'trace end' on the bottom of the stack */ +if (!ts->cnt || (ts->cnt ==1 && ts->stack[0 ].ref == ref)) +return 0 ; + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp,NULL ,0 , + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + ret_addr = sample->ip + sample->insn_len; + +return thread_stack__push_cp (ts, ret_addr, sample->time, ref, cp, + false); +} + +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp) +{ +struct thread_stack *ts = thread->ts; +int err =0 ; + +if (ts) { +if (!ts->crp) { +/* Supersede thread_stack__event() */ +thread_stack__free (thread); + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + }else { + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + +/* Flush stack on exec */ +if (ts->comm != comm && thread->pid_ == thread->tid) { + err =thread_stack__flush (thread, ts); +if (err) +return err; + ts->comm = comm; + } + +/* If the stack is empty, put the current symbol on the stack */ +if (!ts->cnt) { + err =thread_stack__bottom (thread, ts, sample, from_al, to_al, + ref); +if (err) +return err; + } + + ts->branch_count +=1 ; + ts->last_time = sample->time; + +if (sample->flags &PERF_FLAG_CALL ) { +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +if (!sample->ip || !sample->addr) +return 0 ; + + ret_addr = sample->ip + sample->insn_len; +if (ret_addr == sample->addr) +return 0 ;/* Zero-length calls are excluded */ + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + err =thread_stack__push_cp (ts, ret_addr, sample->time, ref, + cp, false); + }else if (sample->flags &PERF_FLAG_RETURN ) { +if (!sample->ip || !sample->addr) +return 0 ; + + err =thread_stack__pop_cp (thread, ts, sample->addr, + sample->time, ref, from_al->sym); +if (err) { +if (err <0 ) +return err; + err =thread_stack__no_call_return (thread, ts, sample, + from_al, to_al, ref); + } + }else if (sample->flags &PERF_FLAG_TRACE_BEGIN ) { + err =thread_stack__trace_begin (thread, ts, sample->time, ref); + }else if (sample->flags &PERF_FLAG_TRACE_END ) { + err =thread_stack__trace_end (ts, sample, ref); + } + +return err; +}
tools/perf/util/thread-stack.h +47 -0
61 lines not on this page · raw message
Signature · 1 line
1.9.1
Re: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data
Quoted 2 lines · Adrian Hunter
Enhance the thread stack to output detailed information about paired calls and returns.
Any chance we could get more explanation for this one? IMHO it's pretty complex patch to have just 2 lines in changelog..
If you could describe the data structures and their purpose and the way they are/will be captured/hooked in perf.
thanks,
jirka
Quoted 700 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/thread-stack.h | 47 ++++ 2 files changed, 590 insertions(+), 4 deletions(-)tools/perf/util/thread-stack.c +543
-4@@ -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 2048struct thread_stack_entry {u64 ret_addr; +u64 timestamp; +u64 ref; +u64 branch_count; +struct call_path *cp; +bool no_call; };struct thread_stack {@@ -29,6 +62,11 @@ struct thread_stack {size_t cnt;size_t sz;u64 trace_nr; +u64 branch_count; +u64 kernel_start; +u64 last_time; +struct call_return_processor *crp; +struct comm *comm; };static void thread_stack__grow (struct thread_stack *ts)@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts) } } -static struct thread_stack *thread_stack__new (void ) +static struct thread_stack *thread_stack__new (struct thread *thread, +struct call_return_processor *crp) {struct thread_stack *ts;@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)return NULL ; } +if (thread->mg && thread->mg->machine) + ts->kernel_start =machine__kernel_start (thread->mg->machine); +else + ts->kernel_start =1ULL <<63 ; + ts->crp = crp; +return ts; }@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) } } +static bool thread_stack__in_kernel (struct thread_stack *ts) +{ +if (!ts->cnt) +return false; + +return ts->stack[ts->cnt -1 ].cp->in_kernel; +} + +static int thread_stack__call_return (struct thread *thread, +struct thread_stack *ts,size_t idx, +u64 timestamp,u64 ref,bool no_return) +{ +struct call_return_processor *crp = ts->crp; +struct thread_stack_entry *tse; +struct call_return cr = { + .thread = thread, + .comm = ts->comm, + .db_id =0 , + }; + + tse = &ts->stack[idx]; + cr.cp = tse->cp; + cr.call_time = tse->timestamp; + cr.return_time = timestamp; + cr.branch_count = ts->branch_count - tse->branch_count; + cr.call_ref = tse->ref; + cr.return_ref = ref; +if (tse->no_call) + cr.flags |=CALL_RETURN_NO_CALL ; +if (no_return) + cr.flags |=CALL_RETURN_NO_RETURN ; + +return crp->process (&cr, crp->data); +} + +static int thread_stack__flush (struct thread *thread,struct thread_stack *ts) +{ +struct call_return_processor *crp = ts->crp; +int err; + +if (!crp) { + ts->cnt =0 ; +return 0 ; + } + +while (ts->cnt) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + ts->last_time,0 , true); +if (err) { +pr_err ("Error flushing thread stack!\n" ); + ts->cnt =0 ; +return err; + } + } + +return 0 ; +} +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr) {@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,return ;if (!thread->ts) { - thread->ts =thread_stack__new (); + thread->ts =thread_stack__new (thread,NULL );if (!thread->ts)return ; thread->ts->trace_nr = trace_nr; }if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); thread->ts->trace_nr = trace_nr; - thread->ts->cnt =0 ; } +/* Stop here if thread_stack__process() is in use */ +if (thread->ts->crp) +return ; +if (flags &PERF_FLAG_CALL ) {u64 ret_addr;@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, } } +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr) +{ +if (!thread || !thread->ts) +return ; + +if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); + thread->ts->trace_nr = trace_nr; + } +} +void thread_stack__free (struct thread *thread) {if (thread->ts) { +thread_stack__flush (thread, thread->ts);zfree (&thread->ts->stack);zfree (&thread->ts); }@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,for (i =1 ; i < chain->nr; i++) chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; } + +static void call_path__init (struct call_path *cp,struct call_path *parent, +struct symbol *sym,u64 ip,bool in_kernel) +{ + cp->parent = parent; + cp->sym = sym; + cp->ip = sym ?0 : ip; + cp->db_id =0 ; + cp->in_kernel = in_kernel; +RB_CLEAR_NODE (&cp->rb_node); + cp->children =RB_ROOT ; +} + +static struct call_path_root *call_path_root__new (void ) +{ +struct call_path_root *cpr; + + cpr =zalloc (sizeof (struct call_path_root )); +if (!cpr) +return NULL ; +call_path__init (&cpr->call_path,NULL ,NULL ,0 , false); +INIT_LIST_HEAD (&cpr->blocks); +return cpr; +} + +static void call_path_root__free (struct call_path_root *cpr) +{ +struct call_path_block *pos, *n; + +list_for_each_entry_safe (pos, n, &cpr->blocks, node) { +list_del (&pos->node); +free (pos); + } +free (cpr); +} + +static struct call_path *call_path__new (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip, +bool in_kernel) +{ +struct call_path_block *cpb; +struct call_path *cp; +size_t n; + +if (cpr->next < cpr->sz) { + cpb = list_last_entry(&cpr->blocks, struct call_path_block, + node); + }else { + cpb =zalloc (sizeof (struct call_path_block )); +if (!cpb) +return NULL ; +list_add_tail (&cpb->node, &cpr->blocks); + cpr->sz +=CALL_PATH_BLOCK_SIZE ; + } + + n = cpr->next++ &CALL_PATH_BLOCK_MASK ; + cp = &cpb->cp[n]; + +call_path__init (cp, parent, sym, ip, in_kernel); + +return cp; +} + +static struct call_path *call_path__findnew (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip,u64 ks) +{ +struct rb_node **p; +struct rb_node *node_parent =NULL ; +struct call_path *cp; +bool in_kernel = ip >= ks; + +if (sym) + ip =0 ; + +if (!parent) +return call_path__new (cpr, parent, sym, ip, in_kernel); + + p = &parent->children.rb_node; +while (*p !=NULL ) { + node_parent = *p; + cp = rb_entry(node_parent, struct call_path, rb_node); + +if (cp->sym == sym && cp->ip == ip) +return cp; + +if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + + cp =call_path__new (cpr, parent, sym, ip, in_kernel); +if (!cp) +return NULL ; + +rb_link_node (&cp->rb_node, node_parent, p); +rb_insert_color (&cp->rb_node, &parent->children); + +return cp; +} + +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data) +{ +struct call_return_processor *crp; + + crp =zalloc (sizeof (struct call_return_processor )); +if (!crp) +return NULL ; + crp->cpr =call_path_root__new (); +if (!crp->cpr) + goto out_free; + crp->process = process; + crp->data = data; +return crp; + +out_free: +free (crp); +return NULL ; +} + +void call_return_processor__free (struct call_return_processor *crp) +{ +if (crp) { +call_path_root__free (crp->cpr); +free (crp); + } +} + +static int thread_stack__push_cp (struct thread_stack *ts,u64 ret_addr, +u64 timestamp,u64 ref,struct call_path *cp, +bool no_call) +{ +struct thread_stack_entry *tse; + +if (ts->cnt == ts->sz) { +thread_stack__grow (ts); +if (ts->cnt == ts->sz) +return -ENOMEM ; + } + + tse = &ts->stack[ts->cnt++]; + tse->ret_addr = ret_addr; + tse->timestamp = timestamp; + tse->ref = ref; + tse->branch_count = ts->branch_count; + tse->cp = cp; + tse->no_call = no_call; + +return 0 ; +} + +static int thread_stack__pop_cp (struct thread *thread,struct thread_stack *ts, +u64 ret_addr,u64 timestamp,u64 ref, +struct symbol *sym) +{ +int err; + +if (!ts->cnt) +return 1 ; + +if (ts->cnt ==1 ) { +struct thread_stack_entry *tse = &ts->stack[0 ]; + +if (tse->cp->sym == sym) +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr != ret_addr) +continue ; + i +=1 ; +while (ts->cnt > i) { + err =thread_stack__call_return (thread, ts, + --ts->cnt, + timestamp, ref, + true); +if (err) +return err; + } +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + } + +return 1 ; +} + +static int thread_stack__bottom (struct thread *thread,struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +struct symbol *sym; +u64 ip; + +if (sample->ip) { + ip = sample->ip; + sym = from_al->sym; + }else if (sample->addr) { + ip = sample->addr; + sym = to_al->sym; + }else { +return 0 ; + } + + cp =call_path__findnew (cpr, &cpr->call_path, sym, ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + +return thread_stack__push_cp (thread->ts, ip, sample->time, ref, cp, + true); +} + +static int thread_stack__no_call_return (struct thread *thread, +struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp, *parent; +u64 ks = ts->kernel_start; +int err; + +if (sample->ip >= ks && sample->addr < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + +/* If the stack is empty, push the userspace address */ +if (!ts->cnt) { + cp =call_path__findnew (cpr, &cpr->call_path, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; +return thread_stack__push_cp (ts,0 , sample->time, ref, + cp, true); + } + }else if (thread_stack__in_kernel (ts) && sample->ip < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + } + +if (ts->cnt) + parent = ts->stack[ts->cnt -1 ].cp; +else + parent = &cpr->call_path; + +/* This 'return' had no 'call', so push and pop top of stack */ + cp =call_path__findnew (cpr, parent, from_al->sym, sample->ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + err =thread_stack__push_cp (ts, sample->addr, sample->time, ref, cp, + true); +if (err) +return err; + +return thread_stack__pop_cp (thread, ts, sample->addr, sample->time, ref, + to_al->sym); +} + +static int thread_stack__trace_begin (struct thread *thread, +struct thread_stack *ts,u64 timestamp, +u64 ref) +{ +struct thread_stack_entry *tse; +int err; + +if (!ts->cnt) +return 0 ; + +/* Pop trace end */ + tse = &ts->stack[ts->cnt -1 ]; +if (tse->cp->sym ==NULL && tse->cp->ip ==0 ) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); +if (err) +return err; + } + +return 0 ; +} + +static int thread_stack__trace_end (struct thread_stack *ts, +struct perf_sample *sample,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +/* No point having 'trace end' on the bottom of the stack */ +if (!ts->cnt || (ts->cnt ==1 && ts->stack[0 ].ref == ref)) +return 0 ; + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp,NULL ,0 , + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + ret_addr = sample->ip + sample->insn_len; + +return thread_stack__push_cp (ts, ret_addr, sample->time, ref, cp, + false); +} + +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp) +{ +struct thread_stack *ts = thread->ts; +int err =0 ; + +if (ts) { +if (!ts->crp) { +/* Supersede thread_stack__event() */ +thread_stack__free (thread); + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + }else { + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + +/* Flush stack on exec */ +if (ts->comm != comm && thread->pid_ == thread->tid) { + err =thread_stack__flush (thread, ts); +if (err) +return err; + ts->comm = comm; + } + +/* If the stack is empty, put the current symbol on the stack */ +if (!ts->cnt) { + err =thread_stack__bottom (thread, ts, sample, from_al, to_al, + ref); +if (err) +return err; + } + + ts->branch_count +=1 ; + ts->last_time = sample->time; + +if (sample->flags &PERF_FLAG_CALL ) { +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +if (!sample->ip || !sample->addr) +return 0 ; + + ret_addr = sample->ip + sample->insn_len; +if (ret_addr == sample->addr) +return 0 ;/* Zero-length calls are excluded */ + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + err =thread_stack__push_cp (ts, ret_addr, sample->time, ref, + cp, false); + }else if (sample->flags &PERF_FLAG_RETURN ) { +if (!sample->ip || !sample->addr) +return 0 ; + + err =thread_stack__pop_cp (thread, ts, sample->addr, + sample->time, ref, from_al->sym); +if (err) { +if (err <0 ) +return err; + err =thread_stack__no_call_return (thread, ts, sample, + from_al, to_al, ref); + } + }else if (sample->flags &PERF_FLAG_TRACE_BEGIN ) { + err =thread_stack__trace_begin (thread, ts, sample->time, ref); + }else if (sample->flags &PERF_FLAG_TRACE_END ) { + err =thread_stack__trace_end (ts, sample, ref); + } + +return err; +}tools/perf/util/thread-stack.h +47
-0@@ -19,14 +19,61 @@#include <sys/types.h> #include <linux/types.h> +#include <linux/rbtree.h> struct thread ; +struct comm ;struct ip_callchain ; +struct symbol ; +struct dso ; +struct call_return_processor ; +struct comm ; +struct perf_sample ; +struct addr_location ; + +enum { +CALL_RETURN_NO_CALL =1 <<0 , +CALL_RETURN_NO_RETURN =1 <<1 , +}; + +struct call_return { +struct thread *thread; +struct comm *comm; +struct call_path *cp; +u64 call_time; +u64 return_time; +u64 branch_count; +u64 call_ref; +u64 return_ref; +u64 db_id; +u32 flags; +}; + +struct call_path { +struct call_path *parent; +struct symbol *sym; +u64 ip;/* Only if sym is null */ +u64 db_id; +bool in_kernel; +struct rb_node rb_node; +struct rb_root children; +};void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr);void thread_stack__sample (struct thread *thread,struct ip_callchain *chain,size_t sz,u64 ip);void thread_stack__free (struct thread *thread); +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data); +void call_return_processor__free (struct call_return_processor *crp); +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp); +#endif Signature · 1 line
1.9.1
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 10/16] perf tools: Enhance the thread stack to output call/return data
Em Wed, Oct 29, 2014 at 02:23:50PM +0100, Jiri Olsa escreveu:
Quoted 10 lines · Adrian Hunter
Quoted 2 lines
Enhance the thread stack to output detailed information about paired calls and returns.
Any chance we could get more explanation for this one? IMHO it's pretty complex patch to have just 2 lines in changelog..
If you could describe the data structures and their purpose and the way they are/will be captured/hooked in perf.
Right, I thought even to write some example code in 'perf test', so that by writing it I would become familiarized with this code, but never managed to find the time.
Also those functions not returning failure but instead overwriting stuff doesn't look right. They could conceivably be OK for the existing use, but I wonder if this can't be used in more scenarios where proper error handling could be required.
And in general, something new like this should have proper error reporting, if some particular user decides to ignore those errors, well, that is their problem, generic code should have proper error reporting.
- Arnaldo
Quoted 703 lines
thanks,
jirkaQuoted 700 lines
Signed-off-by Adrian Hunter <[email protected]>--- tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/thread-stack.h | 47 ++++ 2 files changed, 590 insertions(+), 4 deletions(-)tools/perf/util/thread-stack.c +543
-4@@ -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 2048struct thread_stack_entry {u64 ret_addr; +u64 timestamp; +u64 ref; +u64 branch_count; +struct call_path *cp; +bool no_call; };struct thread_stack {@@ -29,6 +62,11 @@ struct thread_stack {size_t cnt;size_t sz;u64 trace_nr; +u64 branch_count; +u64 kernel_start; +u64 last_time; +struct call_return_processor *crp; +struct comm *comm; };static void thread_stack__grow (struct thread_stack *ts)@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts) } } -static struct thread_stack *thread_stack__new (void ) +static struct thread_stack *thread_stack__new (struct thread *thread, +struct call_return_processor *crp) {struct thread_stack *ts;@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)return NULL ; } +if (thread->mg && thread->mg->machine) + ts->kernel_start =machine__kernel_start (thread->mg->machine); +else + ts->kernel_start =1ULL <<63 ; + ts->crp = crp; +return ts; }@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) } } +static bool thread_stack__in_kernel (struct thread_stack *ts) +{ +if (!ts->cnt) +return false; + +return ts->stack[ts->cnt -1 ].cp->in_kernel; +} + +static int thread_stack__call_return (struct thread *thread, +struct thread_stack *ts,size_t idx, +u64 timestamp,u64 ref,bool no_return) +{ +struct call_return_processor *crp = ts->crp; +struct thread_stack_entry *tse; +struct call_return cr = { + .thread = thread, + .comm = ts->comm, + .db_id =0 , + }; + + tse = &ts->stack[idx]; + cr.cp = tse->cp; + cr.call_time = tse->timestamp; + cr.return_time = timestamp; + cr.branch_count = ts->branch_count - tse->branch_count; + cr.call_ref = tse->ref; + cr.return_ref = ref; +if (tse->no_call) + cr.flags |=CALL_RETURN_NO_CALL ; +if (no_return) + cr.flags |=CALL_RETURN_NO_RETURN ; + +return crp->process (&cr, crp->data); +} + +static int thread_stack__flush (struct thread *thread,struct thread_stack *ts) +{ +struct call_return_processor *crp = ts->crp; +int err; + +if (!crp) { + ts->cnt =0 ; +return 0 ; + } + +while (ts->cnt) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + ts->last_time,0 , true); +if (err) { +pr_err ("Error flushing thread stack!\n" ); + ts->cnt =0 ; +return err; + } + } + +return 0 ; +} +void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr) {@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,return ;if (!thread->ts) { - thread->ts =thread_stack__new (); + thread->ts =thread_stack__new (thread,NULL );if (!thread->ts)return ; thread->ts->trace_nr = trace_nr; }if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); thread->ts->trace_nr = trace_nr; - thread->ts->cnt =0 ; } +/* Stop here if thread_stack__process() is in use */ +if (thread->ts->crp) +return ; +if (flags &PERF_FLAG_CALL ) {u64 ret_addr;@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, } } +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr) +{ +if (!thread || !thread->ts) +return ; + +if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); + thread->ts->trace_nr = trace_nr; + } +} +void thread_stack__free (struct thread *thread) {if (thread->ts) { +thread_stack__flush (thread, thread->ts);zfree (&thread->ts->stack);zfree (&thread->ts); }@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,for (i =1 ; i < chain->nr; i++) chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; } + +static void call_path__init (struct call_path *cp,struct call_path *parent, +struct symbol *sym,u64 ip,bool in_kernel) +{ + cp->parent = parent; + cp->sym = sym; + cp->ip = sym ?0 : ip; + cp->db_id =0 ; + cp->in_kernel = in_kernel; +RB_CLEAR_NODE (&cp->rb_node); + cp->children =RB_ROOT ; +} + +static struct call_path_root *call_path_root__new (void ) +{ +struct call_path_root *cpr; + + cpr =zalloc (sizeof (struct call_path_root )); +if (!cpr) +return NULL ; +call_path__init (&cpr->call_path,NULL ,NULL ,0 , false); +INIT_LIST_HEAD (&cpr->blocks); +return cpr; +} + +static void call_path_root__free (struct call_path_root *cpr) +{ +struct call_path_block *pos, *n; + +list_for_each_entry_safe (pos, n, &cpr->blocks, node) { +list_del (&pos->node); +free (pos); + } +free (cpr); +} + +static struct call_path *call_path__new (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip, +bool in_kernel) +{ +struct call_path_block *cpb; +struct call_path *cp; +size_t n; + +if (cpr->next < cpr->sz) { + cpb = list_last_entry(&cpr->blocks, struct call_path_block, + node); + }else { + cpb =zalloc (sizeof (struct call_path_block )); +if (!cpb) +return NULL ; +list_add_tail (&cpb->node, &cpr->blocks); + cpr->sz +=CALL_PATH_BLOCK_SIZE ; + } + + n = cpr->next++ &CALL_PATH_BLOCK_MASK ; + cp = &cpb->cp[n]; + +call_path__init (cp, parent, sym, ip, in_kernel); + +return cp; +} + +static struct call_path *call_path__findnew (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip,u64 ks) +{ +struct rb_node **p; +struct rb_node *node_parent =NULL ; +struct call_path *cp; +bool in_kernel = ip >= ks; + +if (sym) + ip =0 ; + +if (!parent) +return call_path__new (cpr, parent, sym, ip, in_kernel); + + p = &parent->children.rb_node; +while (*p !=NULL ) { + node_parent = *p; + cp = rb_entry(node_parent, struct call_path, rb_node); + +if (cp->sym == sym && cp->ip == ip) +return cp; + +if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + + cp =call_path__new (cpr, parent, sym, ip, in_kernel); +if (!cp) +return NULL ; + +rb_link_node (&cp->rb_node, node_parent, p); +rb_insert_color (&cp->rb_node, &parent->children); + +return cp; +} + +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data) +{ +struct call_return_processor *crp; + + crp =zalloc (sizeof (struct call_return_processor )); +if (!crp) +return NULL ; + crp->cpr =call_path_root__new (); +if (!crp->cpr) + goto out_free; + crp->process = process; + crp->data = data; +return crp; + +out_free: +free (crp); +return NULL ; +} + +void call_return_processor__free (struct call_return_processor *crp) +{ +if (crp) { +call_path_root__free (crp->cpr); +free (crp); + } +} + +static int thread_stack__push_cp (struct thread_stack *ts,u64 ret_addr, +u64 timestamp,u64 ref,struct call_path *cp, +bool no_call) +{ +struct thread_stack_entry *tse; + +if (ts->cnt == ts->sz) { +thread_stack__grow (ts); +if (ts->cnt == ts->sz) +return -ENOMEM ; + } + + tse = &ts->stack[ts->cnt++]; + tse->ret_addr = ret_addr; + tse->timestamp = timestamp; + tse->ref = ref; + tse->branch_count = ts->branch_count; + tse->cp = cp; + tse->no_call = no_call; + +return 0 ; +} + +static int thread_stack__pop_cp (struct thread *thread,struct thread_stack *ts, +u64 ret_addr,u64 timestamp,u64 ref, +struct symbol *sym) +{ +int err; + +if (!ts->cnt) +return 1 ; + +if (ts->cnt ==1 ) { +struct thread_stack_entry *tse = &ts->stack[0 ]; + +if (tse->cp->sym == sym) +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr != ret_addr) +continue ; + i +=1 ; +while (ts->cnt > i) { + err =thread_stack__call_return (thread, ts, + --ts->cnt, + timestamp, ref, + true); +if (err) +return err; + } +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + } + +return 1 ; +} + +static int thread_stack__bottom (struct thread *thread,struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +struct symbol *sym; +u64 ip; + +if (sample->ip) { + ip = sample->ip; + sym = from_al->sym; + }else if (sample->addr) { + ip = sample->addr; + sym = to_al->sym; + }else { +return 0 ; + } + + cp =call_path__findnew (cpr, &cpr->call_path, sym, ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + +return thread_stack__push_cp (thread->ts, ip, sample->time, ref, cp, + true); +} + +static int thread_stack__no_call_return (struct thread *thread, +struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp, *parent; +u64 ks = ts->kernel_start; +int err; + +if (sample->ip >= ks && sample->addr < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + +/* If the stack is empty, push the userspace address */ +if (!ts->cnt) { + cp =call_path__findnew (cpr, &cpr->call_path, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; +return thread_stack__push_cp (ts,0 , sample->time, ref, + cp, true); + } + }else if (thread_stack__in_kernel (ts) && sample->ip < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + } + +if (ts->cnt) + parent = ts->stack[ts->cnt -1 ].cp; +else + parent = &cpr->call_path; + +/* This 'return' had no 'call', so push and pop top of stack */ + cp =call_path__findnew (cpr, parent, from_al->sym, sample->ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + err =thread_stack__push_cp (ts, sample->addr, sample->time, ref, cp, + true); +if (err) +return err; + +return thread_stack__pop_cp (thread, ts, sample->addr, sample->time, ref, + to_al->sym); +} + +static int thread_stack__trace_begin (struct thread *thread, +struct thread_stack *ts,u64 timestamp, +u64 ref) +{ +struct thread_stack_entry *tse; +int err; + +if (!ts->cnt) +return 0 ; + +/* Pop trace end */ + tse = &ts->stack[ts->cnt -1 ]; +if (tse->cp->sym ==NULL && tse->cp->ip ==0 ) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); +if (err) +return err; + } + +return 0 ; +} + +static int thread_stack__trace_end (struct thread_stack *ts, +struct perf_sample *sample,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +/* No point having 'trace end' on the bottom of the stack */ +if (!ts->cnt || (ts->cnt ==1 && ts->stack[0 ].ref == ref)) +return 0 ; + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp,NULL ,0 , + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + ret_addr = sample->ip + sample->insn_len; + +return thread_stack__push_cp (ts, ret_addr, sample->time, ref, cp, + false); +} + +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp) +{ +struct thread_stack *ts = thread->ts; +int err =0 ; + +if (ts) { +if (!ts->crp) { +/* Supersede thread_stack__event() */ +thread_stack__free (thread); + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + }else { + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + +/* Flush stack on exec */ +if (ts->comm != comm && thread->pid_ == thread->tid) { + err =thread_stack__flush (thread, ts); +if (err) +return err; + ts->comm = comm; + } + +/* If the stack is empty, put the current symbol on the stack */ +if (!ts->cnt) { + err =thread_stack__bottom (thread, ts, sample, from_al, to_al, + ref); +if (err) +return err; + } + + ts->branch_count +=1 ; + ts->last_time = sample->time; + +if (sample->flags &PERF_FLAG_CALL ) { +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +if (!sample->ip || !sample->addr) +return 0 ; + + ret_addr = sample->ip + sample->insn_len; +if (ret_addr == sample->addr) +return 0 ;/* Zero-length calls are excluded */ + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + err =thread_stack__push_cp (ts, ret_addr, sample->time, ref, + cp, false); + }else if (sample->flags &PERF_FLAG_RETURN ) { +if (!sample->ip || !sample->addr) +return 0 ; + + err =thread_stack__pop_cp (thread, ts, sample->addr, + sample->time, ref, from_al->sym); +if (err) { +if (err <0 ) +return err; + err =thread_stack__no_call_return (thread, ts, sample, + from_al, to_al, ref); + } + }else if (sample->flags &PERF_FLAG_TRACE_BEGIN ) { + err =thread_stack__trace_begin (thread, ts, sample->time, ref); + }else if (sample->flags &PERF_FLAG_TRACE_END ) { + err =thread_stack__trace_end (ts, sample, ref); + } + +return err; +}tools/perf/util/thread-stack.h +47
-0@@ -19,14 +19,61 @@#include <sys/types.h> #include <linux/types.h> +#include <linux/rbtree.h> struct thread ; +struct comm ;struct ip_callchain ; +struct symbol ; +struct dso ; +struct call_return_processor ; +struct comm ; +struct perf_sample ; +struct addr_location ; + +enum { +CALL_RETURN_NO_CALL =1 <<0 , +CALL_RETURN_NO_RETURN =1 <<1 , +}; + +struct call_return { +struct thread *thread; +struct comm *comm; +struct call_path *cp; +u64 call_time; +u64 return_time; +u64 branch_count; +u64 call_ref; +u64 return_ref; +u64 db_id; +u32 flags; +}; + +struct call_path { +struct call_path *parent; +struct symbol *sym; +u64 ip;/* Only if sym is null */ +u64 db_id; +bool in_kernel; +struct rb_node rb_node; +struct rb_root children; +};void thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr);void thread_stack__sample (struct thread *thread,struct ip_callchain *chain,size_t sz,u64 ip);void thread_stack__free (struct thread *thread); +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data); +void call_return_processor__free (struct call_return_processor *crp); +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp); +#endif Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 11/16 perf tools: Add call information to the database export API
In reply to Adrian HunterMake it possible for the database export API to use the enhanced thread stack and export detailed information about paired calls and returns.
--- tools/perf/util/db-export.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/db-export.h | 12 +++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-)
tools/perf/util/db-export.c +51 -1
@@ -21,6 +21,7 @@#include "comm.h" #include "symbol.h" #include "event.h" +#include "thread-stack.h" #include "db-export.h" int db_export__init (struct db_export *dbe)
@@ -29,8 +30,10 @@ int db_export__init(struct db_export *dbe)return 0 ; } -void db_export__exit (struct db_export *dbe__maybe_unused ) +void db_export__exit (struct db_export *dbe) { +call_return_processor__free (dbe->crp); + dbe->crp =NULL ; }int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel)
@@ -268,6 +271,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, &es.addr_sym_db_id, &es.addr_offset);if (err)return err; +if (dbe->crp) { + err =thread_stack__process (thread, comm, sample, al, + &addr_al, es.db_id, + dbe->crp); +if (err) +return err; + } }if (dbe->export_sample)
@@ -314,3 +324,43 @@ int db_export__branch_types(struct db_export *dbe) }return err; } + +int db_export__call_path (struct db_export *dbe,struct call_path *cp) +{ +int err; + +if (cp->db_id) +return 0 ; + +if (cp->parent) { + err =db_export__call_path (dbe, cp->parent); +if (err) +return err; + } + + cp->db_id = ++dbe->call_path_last_db_id; + +if (dbe->export_call_path) +return dbe->export_call_path (dbe, cp); + +return 0 ; +} + +int db_export__call_return (struct db_export *dbe,struct call_return *cr) +{ +int err; + +if (cr->db_id) +return 0 ; + + err =db_export__call_path (dbe, cr->cp); +if (err) +return err; + + cr->db_id = ++dbe->call_return_last_db_id; + +if (dbe->export_call_return) +return dbe->export_call_return (dbe, cr); + +return 0 ; +}
tools/perf/util/db-export.h +12 -0
@@ -25,6 +25,9 @@ struct comm;struct dso ;struct perf_sample ;struct addr_location ; +struct call_return_processor ; +struct call_path ; +struct call_return ;struct export_sample {union perf_event *event;
@@ -57,6 +60,10 @@ struct db_export {int (*export_branch_type)(struct db_export *dbe,u32 branch_type,const char *name);int (*export_sample)(struct db_export *dbe,struct export_sample *es); +int (*export_call_path)(struct db_export *dbe,struct call_path *cp); +int (*export_call_return)(struct db_export *dbe, +struct call_return *cr); +struct call_return_processor *crp;u64 evsel_last_db_id;u64 machine_last_db_id;u64 thread_last_db_id;
@@ -65,6 +72,8 @@ struct db_export {u64 dso_last_db_id;u64 symbol_last_db_id;u64 sample_last_db_id; +u64 call_path_last_db_id; +u64 call_return_last_db_id; };int db_export__init (struct db_export *dbe);
@@ -89,4 +98,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,int db_export__branch_types (struct db_export *dbe); +int db_export__call_path (struct db_export *dbe,struct call_path *cp); +int db_export__call_return (struct db_export *dbe,struct call_return *cr); +#endif
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 12/16 perf tools: Add call information to Python export
In reply to Adrian HunterAdd the ability to export detailed information about paired calls and returns to Python db export and the export-to-postgresql.py script.
--- .../scripts/python/bin/export-to-postgresql-report | 15 ++-- tools/perf/scripts/python/export-to-postgresql.py | 66 ++++++++++++++++- .../util/scripting-engines/trace-event-python.c | 84 +++++++++++++++++++++- 3 files changed, 158 insertions(+), 7 deletions(-)
tools/perf/scripts/python/bin/export-to-postgresql-report +10 -5
@@ -1,6 +1,6 @@ #!/bin/bash # description: export perf data to a postgresql database -# args: [database name] [columns] +# args: [database name] [columns] [calls] n_args=0 for i in "$@" do
@@ -9,11 +9,16 @@ do fi n_args=$(( $n_args + 1 )) done -if [ "$n_args" -gt 2 ] ; then - echo "usage: export-to-postgresql-report [database name] [columns]" +if [ "$n_args" -gt 3 ] ; then + echo "usage: export-to-postgresql-report [database name] [columns] [calls]" exit fi -if [ "$n_args" -gt 1 ] ; then +if [ "$n_args" -gt 2 ] ; then + dbname=$1 + columns=$2 + calls=$3 + shift 3 +elif [ "$n_args" -gt 1 ] ; then dbname=$1 columns=$2 shift 2
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then dbname=$1 shift fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
tools/perf/scripts/python/export-to-postgresql.py +65 -1
126 lines not on this page · raw message
tools/perf/util/scripting-engines/trace-event-python.c +83 -1
120 lines not on this page · raw message
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 13/16 perf tools: Defer export of comms that were not 'set'
In reply to Adrian HunterTracing for a workload begins before the comm event is seen, which results in the initial comm having a string of the form ":<pid>" (e.g. ":12345"). In order to export the correct string, defer the export until the new script 'flush' callback.
--- tools/perf/util/db-export.c | 62 +++++++++++++++++++++- tools/perf/util/db-export.h | 3 ++ .../util/scripting-engines/trace-event-python.c | 4 +- 3 files changed, 67 insertions(+), 2 deletions(-)
tools/perf/util/db-export.c +61 -1
@@ -21,17 +21,74 @@#include "comm.h" #include "symbol.h" #include "event.h" +#include "util.h" #include "thread-stack.h" #include "db-export.h" +struct deferred_export { +struct list_head node; +struct comm *comm; +}; + +static int db_export__deferred (struct db_export *dbe) +{ +struct deferred_export *de; +int err; + +while (!list_empty (&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); + err = dbe->export_comm (dbe, de->comm); +list_del (&de->node); +free (de); +if (err) +return err; + } + +return 0 ; +} + +static void db_export__free_deferred (struct db_export *dbe) +{ +struct deferred_export *de; + +while (!list_empty (&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); +list_del (&de->node); +free (de); + } +} + +static int db_export__defer_comm (struct db_export *dbe,struct comm *comm) +{ +struct deferred_export *de; + + de =zalloc (sizeof (struct deferred_export )); +if (!de) +return -ENOMEM ; + + de->comm = comm; +list_add_tail (&de->node, &dbe->deferred); + +return 0 ; +} +int db_export__init (struct db_export *dbe) {memset (dbe,0 ,sizeof (struct db_export )); +INIT_LIST_HEAD (&dbe->deferred);return 0 ; } +int db_export__flush (struct db_export *dbe) +{ +return db_export__deferred (dbe); +} +void db_export__exit (struct db_export *dbe) { +db_export__free_deferred (dbe);call_return_processor__free (dbe->crp); dbe->crp =NULL ; }
@@ -115,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm, comm->db_id = ++dbe->comm_last_db_id;if (dbe->export_comm) { - err = dbe->export_comm (dbe, comm); +if (main_thread->comm_set) + err = dbe->export_comm (dbe, comm); +else + err =db_export__defer_comm (dbe, comm);if (err)return err; }
tools/perf/util/db-export.h +3 -0
@@ -17,6 +17,7 @@#define __PERF_DB_EXPORT_H#include <linux/types.h> +#include <linux/list.h> struct perf_evsel ;struct machine ;
@@ -74,9 +75,11 @@ struct db_export {u64 sample_last_db_id;u64 call_path_last_db_id;u64 call_return_last_db_id; +struct list_head deferred; };int db_export__init (struct db_export *dbe); +int db_export__flush (struct db_export *dbe);void db_export__exit (struct db_export *dbe);int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel);int db_export__machine (struct db_export *dbe,struct machine *machine);
tools/perf/util/scripting-engines/trace-event-python.c +3 -1
@@ -1025,7 +1025,9 @@ error:static int python_flush_script (void ) { -return 0 ; +struct tables *tables = &tables_global; + +return db_export__flush (&tables->dbe); }/*
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 14/16 perf tools: Build programs to copy 32-bit compatibility VDSOs
In reply to Adrian Hunterperf tools copy VDSO out of memory. However, on 64-bit machines there may be 32-bit compatibility VDOs also. To copy those requires separate 32-bit executables. This patch adds to the build additional programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32 respectively.
--- tools/perf/Makefile.perf | 38 ++++++++++++++++++++++--- tools/perf/config/Makefile | 25 +++++++++++++++- tools/perf/config/Makefile.arch | 8 ++++++ tools/perf/config/feature-checks/Makefile | 10 ++++++- tools/perf/config/feature-checks/test-compile.c | 4 +++ tools/perf/perf-read-vdso.c | 34 ++++++++++++++++++++++ tools/perf/util/find-vdso-map.c | 30 +++++++++++++++++++ tools/perf/util/vdso.c | 37 ++++-------------------- 8 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 tools/perf/config/feature-checks/test-compile.c create mode 100644 tools/perf/perf-read-vdso.c create mode 100644 tools/perf/util/find-vdso-map.c
tools/perf/Makefile.perf +34 -4
@@ -60,6 +60,12 @@ include config/utilities.mak # # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support # for dwarf backtrace post unwind. +# +# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32 +# for reading the 32-bit compatibility VDSO in 64-bit mode +# +# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 +# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,11 +177,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) -# -# Single 'perf' binary right now: -# PROGRAMS += $(OUTPUT)perf +ifndef NO_PERF_READ_VDSO32 +PROGRAMS += $(OUTPUT)perf-read-vdso32 +endif + +ifndef NO_PERF_READ_VDSOX32 +PROGRAMS += $(OUTPUT)perf-read-vdsox32 +endif + # what 'all' will build and 'install' will install, in perfexecdir ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -306,6 +317,7 @@ LIB_H += util/data.h LIB_H += util/kvm-stat.h LIB_H += util/thread-stack.h LIB_H += util/db-export.h +LIB_H += util/find-vdso-map.c LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o
@@ -736,6 +748,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti $(OUTPUT)perf-%: %.o $(PERFLIBS) $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) +ifndef NO_PERF_READ_VDSO32 +$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c + $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c +endif + +ifndef NO_PERF_READ_VDSOX32 +$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c + $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c +endif + $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
@@ -880,6 +902,14 @@ install-bin: all install-gtk $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' +ifndef NO_PERF_READ_VDSO32 + $(call QUIET_INSTALL, perf-read-vdso32) \ + $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)'; +endif +ifndef NO_PERF_READ_VDSOX32 + $(call QUIET_INSTALL, perf-read-vdsox32) \ + $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)'; +endif $(call QUIET_INSTALL, libexec) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(call QUIET_INSTALL, perf-archive) \
@@ -932,7 +962,7 @@ config-clean: clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) - $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf + $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(python-clean)
tools/perf/config/Makefile +24 -1
@@ -230,7 +230,9 @@ VF_FEATURE_TESTS = \ bionic \ liberty \ liberty-z \ - cplus-demangle + cplus-demangle \ + compile-32 \ + compile-x32 # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. # If in the future we need per-feature checks/flags for features not
@@ -622,6 +624,27 @@ ifdef HAVE_KVM_STAT_SUPPORT CFLAGS += -DHAVE_KVM_STAT_SUPPORT endif +ifeq (${IS_64_BIT}, 1) + ifndef NO_PERF_READ_VDSO32 + $(call feature_check,compile-32) + ifneq ($(feature-compile-32), 1) + NO_PERF_READ_VDSO32 := 1 + endif + endif + ifneq (${IS_X86_64}, 1) + NO_PERF_READ_VDSOX32 := 1 + endif + ifndef NO_PERF_READ_VDSOX32 + $(call feature_check,compile-x32) + ifneq ($(feature-compile-x32), 1) + NO_PERF_READ_VDSOX32 := 1 + endif + endif +else + NO_PERF_READ_VDSO32 := 1 + NO_PERF_READ_VDSOX32 := 1 +endif + # Among the variables below, these: # perfexecdir # template_dir
tools/perf/config/Makefile.arch +8 -0
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64) RAW_ARCH := x86_64 endif endif + +ifeq (${IS_X86_64}, 1) + IS_64_BIT := 1 +else ifeq ($(ARCH),x86) + IS_64_BIT := 0 +else + IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) +endif
tools/perf/config/feature-checks/Makefile +9 -1
@@ -27,7 +27,9 @@ FILES= \ test-libunwind-debug-frame.bin \ test-stackprotector-all.bin \ test-timerfd.bin \ - test-libdw-dwarf-unwind.bin + test-libdw-dwarf-unwind.bin \ + test-compile-32.bin \ + test-compile-x32.bin CC := $(CROSS_COMPILE)gcc -MD PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -131,6 +133,12 @@ test-libdw-dwarf-unwind.bin: test-sync-compare-and-swap.bin: $(BUILD) -Werror +test-compile-32.bin: + $(CC) -m32 -o $(OUTPUT)$@ test-compile.c + +test-compile-x32.bin: + $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c + -include *.d ###############################
tools/perf/config/feature-checks/test-compile.c new file +4 -0
@@ -0,0 +1,4 @@ +int main (void ) +{ +return 0 ; +}
tools/perf/perf-read-vdso.c new file +34 -0
@@ -0,0 +1,34 @@ +#include <stdio.h> +#include <string.h> + +#define VDSO__MAP_NAME "[vdso]" + +/* +* Include definition of find_vdso_map() also used in util/vdso.c for +* building perf. +*/ +#include "util/find-vdso-map.c" + +int main (void ) +{ +void *start, *end; +size_t size, written; + +if (find_vdso_map (&start, &end)) +return 1 ; + + size = end - start; + +while (size) { + written =fwrite (start,1 , size, stdout); +if (!written) +return 1 ; + start += written; + size -= written; + } + +if (fflush (stdout)) +return 1 ; + +return 0 ; +}
tools/perf/util/find-vdso-map.c new file +30 -0
@@ -0,0 +1,30 @@ +static int find_vdso_map (void **start,void **end) +{ +FILE *maps; +char line[128 ]; +int found =0 ; + + maps =fopen ("/proc/self/maps" ,"r" ); +if (!maps) { +fprintf (stderr,"vdso: cannot open maps\n" ); +return -1 ; + } + +while (!found &&fgets (line,sizeof (line), maps)) { +int m =-1 ; + +/* We care only about private r-x mappings. */ +if (2 !=sscanf (line,"%p-%p r-xp %*x %*x:%*x %*u %n" , + start, end, &m)) +continue ; +if (m <0 ) +continue ; + +if (!strncmp (&line[m],VDSO__MAP_NAME , +sizeof (VDSO__MAP_NAME ) -1 )) + found =1 ; + } + +fclose (maps); +return !found; +}
tools/perf/util/vdso.c +6 -31
@@ -15,6 +15,12 @@#include "linux/string.h" #include "debug.h" +/* +* Include definition of find_vdso_map() also used in perf-read-vdso.c for +* building perf-read-vdso32 and perf-read-vdsox32. +*/ +#include "find-vdso-map.c" +#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"struct vdso_file {
@@ -40,37 +46,6 @@ static struct vdso_info *vdso_info__new(void)return memdup (&vdso_info_init,sizeof (vdso_info_init)); } -static int find_vdso_map (void **start,void **end) -{ -FILE *maps; -char line[128 ]; -int found =0 ; - - maps =fopen ("/proc/self/maps" ,"r" ); -if (!maps) { -pr_err ("vdso: cannot open maps\n" ); -return -1 ; - } - -while (!found &&fgets (line,sizeof (line), maps)) { -int m =-1 ; - -/* We care only about private r-x mappings. */ -if (2 !=sscanf (line,"%p-%p r-xp %*x %*x:%*x %*u %n" , - start, end, &m)) -continue ; -if (m <0 ) -continue ; - -if (!strncmp (&line[m],VDSO__MAP_NAME , -sizeof (VDSO__MAP_NAME ) -1 )) - found =1 ; - } - -fclose (maps); -return !found; -} -static char *get_file (struct vdso_file *vdso_file) {char *vdso =NULL ;
Signature · 1 line
1.9.1
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf tools: Build programs to copy 32-bit compatibility
perf tools: Build programs to copy 32-bit compatibility
Commit-ID: e477f3f01a89a8fd44031e7f2ba6ffcab037336c Gitweb: http://git.kernel.org/tip/e477f3f01a89a8fd44031e7f2ba6ffcab037336c Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 18:16:03 -0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:48 -0200
perf tools: Build programs to copy 32-bit compatibility
perf tools copy VDSO out of memory. However, on 64-bit machines there may be 32-bit compatibility VDOs also. To copy those requires separate 32-bit executables.
This patch adds to the build additional programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32 respectively.
--- tools/perf/Makefile.perf | 38 +++++++++++++++++++--- tools/perf/config/Makefile | 25 +++++++++++++- tools/perf/config/Makefile.arch | 8 +++++ tools/perf/config/feature-checks/Makefile | 10 +++++- .../perf/config/feature-checks/test-compile.c | 0 tools/perf/perf-read-vdso.c | 34 +++++++++++++++++++ tools/perf/util/find-vdso-map.c | 30 +++++++++++++++++ tools/perf/util/vdso.c | 37 ++++----------------- 8 files changed, 145 insertions(+), 37 deletions(-)
tools/perf/Makefile.perf +34 -4
@@ -60,6 +60,12 @@ include config/utilities.mak # # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support # for dwarf backtrace post unwind. +# +# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32 +# for reading the 32-bit compatibility VDSO in 64-bit mode +# +# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 +# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,11 +177,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) -# -# Single 'perf' binary right now: -# PROGRAMS += $(OUTPUT)perf +ifndef NO_PERF_READ_VDSO32 +PROGRAMS += $(OUTPUT)perf-read-vdso32 +endif + +ifndef NO_PERF_READ_VDSOX32 +PROGRAMS += $(OUTPUT)perf-read-vdsox32 +endif + # what 'all' will build and 'install' will install, in perfexecdir ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -253,6 +264,7 @@ LIB_H += util/event.h LIB_H += util/evsel.h LIB_H += util/evlist.h LIB_H += util/exec_cmd.h +LIB_H += util/find-vdso-map.c LIB_H += util/levenshtein.h LIB_H += util/machine.h LIB_H += util/map.h
@@ -732,6 +744,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti $(OUTPUT)perf-%: %.o $(PERFLIBS) $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) +ifndef NO_PERF_READ_VDSO32 +$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c + $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c +endif + +ifndef NO_PERF_READ_VDSOX32 +$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c + $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c +endif + $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
@@ -876,6 +898,14 @@ install-bin: all install-gtk $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' +ifndef NO_PERF_READ_VDSO32 + $(call QUIET_INSTALL, perf-read-vdso32) \ + $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)'; +endif +ifndef NO_PERF_READ_VDSOX32 + $(call QUIET_INSTALL, perf-read-vdsox32) \ + $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)'; +endif $(call QUIET_INSTALL, libexec) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(call QUIET_INSTALL, perf-archive) \
@@ -928,7 +958,7 @@ config-clean: clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) - $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf + $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(python-clean)
tools/perf/config/Makefile +24 -1
@@ -230,7 +230,9 @@ VF_FEATURE_TESTS = \ bionic \ liberty \ liberty-z \ - cplus-demangle + cplus-demangle \ + compile-32 \ + compile-x32 # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. # If in the future we need per-feature checks/flags for features not
@@ -622,6 +624,27 @@ ifdef HAVE_KVM_STAT_SUPPORT CFLAGS += -DHAVE_KVM_STAT_SUPPORT endif +ifeq (${IS_64_BIT}, 1) + ifndef NO_PERF_READ_VDSO32 + $(call feature_check,compile-32) + ifneq ($(feature-compile-32), 1) + NO_PERF_READ_VDSO32 := 1 + endif + endif + ifneq (${IS_X86_64}, 1) + NO_PERF_READ_VDSOX32 := 1 + endif + ifndef NO_PERF_READ_VDSOX32 + $(call feature_check,compile-x32) + ifneq ($(feature-compile-x32), 1) + NO_PERF_READ_VDSOX32 := 1 + endif + endif +else + NO_PERF_READ_VDSO32 := 1 + NO_PERF_READ_VDSOX32 := 1 +endif + # Among the variables below, these: # perfexecdir # template_dir
tools/perf/config/Makefile.arch +8 -0
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64) RAW_ARCH := x86_64 endif endif + +ifeq (${IS_X86_64}, 1) + IS_64_BIT := 1 +else ifeq ($(ARCH),x86) + IS_64_BIT := 0 +else + IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) +endif
tools/perf/config/feature-checks/Makefile +9 -1
@@ -27,7 +27,9 @@ FILES= \ test-libunwind-debug-frame.bin \ test-stackprotector-all.bin \ test-timerfd.bin \ - test-libdw-dwarf-unwind.bin + test-libdw-dwarf-unwind.bin \ + test-compile-32.bin \ + test-compile-x32.bin CC := $(CROSS_COMPILE)gcc -MD PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -131,6 +133,12 @@ test-libdw-dwarf-unwind.bin: test-sync-compare-and-swap.bin: $(BUILD) -Werror +test-compile-32.bin: + $(CC) -m32 -o $(OUTPUT)$@ test-compile.c + +test-compile-x32.bin: + $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c + -include *.d ###############################
tools/perf/config/feature-checks/test-compile.c renamed from arch/mips/boot/compressed/dummy.c +0 -0
tools/perf/perf-read-vdso.c new file +34 -0
@@ -0,0 +1,34 @@ +#include <stdio.h> +#include <string.h> + +#define VDSO__MAP_NAME "[vdso]" + +/* +* Include definition of find_vdso_map() also used in util/vdso.c for +* building perf. +*/ +#include "util/find-vdso-map.c" + +int main (void ) +{ +void *start, *end; +size_t size, written; + +if (find_vdso_map (&start, &end)) +return 1 ; + + size = end - start; + +while (size) { + written =fwrite (start,1 , size, stdout); +if (!written) +return 1 ; + start += written; + size -= written; + } + +if (fflush (stdout)) +return 1 ; + +return 0 ; +}
tools/perf/util/find-vdso-map.c new file +30 -0
@@ -0,0 +1,30 @@ +static int find_vdso_map (void **start,void **end) +{ +FILE *maps; +char line[128 ]; +int found =0 ; + + maps =fopen ("/proc/self/maps" ,"r" ); +if (!maps) { +fprintf (stderr,"vdso: cannot open maps\n" ); +return -1 ; + } + +while (!found &&fgets (line,sizeof (line), maps)) { +int m =-1 ; + +/* We care only about private r-x mappings. */ +if (2 !=sscanf (line,"%p-%p r-xp %*x %*x:%*x %*u %n" , + start, end, &m)) +continue ; +if (m <0 ) +continue ; + +if (!strncmp (&line[m],VDSO__MAP_NAME , +sizeof (VDSO__MAP_NAME ) -1 )) + found =1 ; + } + +fclose (maps); +return !found; +}
tools/perf/util/vdso.c +6 -31
@@ -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 ;
Adrian Hunter
reviewerPATCH 15/16 perf tools: Add support for 32-bit compatibility VDSOs
In reply to Adrian Hunter'perf record' post-processes the event stream to create a list of build-ids for object files for which sample events have been recorded. That results in those object files being recorded in the build-id cache.
In the case of VDSO, perf tools reads it from memory and copies it into a temporary file, which as decribed above, gets added to the build-id cache.
Then when the perf.data file is processed by other tools, the build-id of VDSO is listed in the perf.data file and the VDSO can be read from the build-id cache. In that case the name of the map, the short name of the DSO, and the entry in the build-id cache are all "[vdso]".
However, in the 64-bit case, there also can be 32-bit compatibility VDSOs.
A previous patch added programs "perf-read-vdso32" and "perf read-vdsox32". This patch uses those programs to read the correct VDSO for a thread and create a temporary file just as for the 64-bit VDSO. The map name and the entry in the build-id cache are still "[vdso]" but the DSO short name becomes "[vdso32]" and "[vdsox32]" respectively.
--- tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/vdso.h | 4 +- 2 files changed, 172 insertions(+), 2 deletions(-)
tools/perf/util/vdso.c +169 -1
@@ -12,6 +12,7 @@#include "util.h" #include "symbol.h" #include "machine.h" +#include "thread.h" #include "linux/string.h" #include "debug.h"
@@ -28,10 +29,15 @@ struct vdso_file {bool error;char temp_file_name[sizeof (VDSO__TEMP_FILE_NAME )];const char *dso_name; +const char *read_prog; };struct vdso_info {struct vdso_file vdso; +#if BITS_PER_LONG ==64 +struct vdso_file vdso32; +struct vdso_file vdsox32; +#endif };static struct vdso_info *vdso_info__new (void )
@@ -41,6 +47,18 @@ static struct vdso_info *vdso_info__new(void) .temp_file_name = VDSO__TEMP_FILE_NAME, .dso_name = DSO__NAME_VDSO, }, +#if BITS_PER_LONG ==64 + .vdso32 = { + .temp_file_name = VDSO__TEMP_FILE_NAME, + .dso_name = DSO__NAME_VDSO32, + .read_prog = "perf-read-vdso32", + }, + .vdsox32 = { + .temp_file_name = VDSO__TEMP_FILE_NAME, + .dso_name = DSO__NAME_VDSOX32, + .read_prog = "perf-read-vdsox32", + }, +#endif };return memdup (&vdso_info_init,sizeof (vdso_info_init));
@@ -92,6 +110,12 @@ void vdso__exit(struct machine *machine)if (vdso_info->vdso.found)unlink (vdso_info->vdso.temp_file_name); +#if BITS_PER_LONG ==64 +if (vdso_info->vdso32.found) +unlink (vdso_info->vdso32.temp_file_name); +if (vdso_info->vdsox32.found) +unlink (vdso_info->vdsox32.temp_file_name); +#endif zfree (&machine->vdso_info); }
@@ -110,6 +134,143 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,return dso; } +#if BITS_PER_LONG ==64 + +static enum dso_type machine__thread_dso_type (struct machine *machine, +struct thread *thread) +{ +enum dso_type dso_type =DSO__TYPE_UNKNOWN ; +struct map *map; +struct dso *dso; + + map =map_groups__first (thread->mg,MAP__FUNCTION ); +for (; map ; map =map_groups__next (map)) { + dso = map->dso; +if (!dso || dso->long_name[0 ] !='/' ) +continue ; + dso_type =dso__type (dso, machine); +if (dso_type !=DSO__TYPE_UNKNOWN ) +break ; + } + +return dso_type; +} + +static int vdso__do_copy_compat (FILE *f,int fd) +{ +char buf[4096 ]; +size_t count; + +while (1 ) { + count =fread (buf,1 ,sizeof (buf), f); +if (ferror (f)) +return -errno; +if (feof (f)) +break ; +if (count &&writen (fd, buf, count) != (ssize_t )count) +return -errno; + } + +return 0 ; +} + +static int vdso__copy_compat (const char *prog,int fd) +{ +FILE *f; +int err; + + f =popen (prog,"r" ); +if (!f) +return -errno; + + err =vdso__do_copy_compat (f, fd); + +if (pclose (f) ==-1 ) +return -errno; + +return err; +} + +static int vdso__create_compat_file (const char *prog,char *temp_name) +{ +int fd, err; + + fd =mkstemp (temp_name); +if (fd <0 ) +return -errno; + + err =vdso__copy_compat (prog, fd); + +if (close (fd) ==-1 ) +return -errno; + +return err; +} + +static const char *vdso__get_compat_file (struct vdso_file *vdso_file) +{ +int err; + +if (vdso_file->found) +return vdso_file->temp_file_name; + +if (vdso_file->error) +return NULL ; + + err =vdso__create_compat_file (vdso_file->read_prog, + vdso_file->temp_file_name); +if (err) { +pr_err ("%s failed, error %d\n" , vdso_file->read_prog, err); + vdso_file->error = true; +return NULL ; + } + + vdso_file->found = true; + +return vdso_file->temp_file_name; +} + +static struct dso *vdso__findnew_compat (struct machine *machine, +struct vdso_file *vdso_file) +{ +const char *file_name; +struct dso *dso; + + dso =dsos__find (&machine->user_dsos, vdso_file->dso_name, true); +if (dso) +return dso; + + file_name =vdso__get_compat_file (vdso_file); +if (!file_name) +return NULL ; + +return vdso__new (machine, vdso_file->dso_name, file_name); +} + +static int vdso__dso_findnew_compat (struct machine *machine, +struct thread *thread, +struct vdso_info *vdso_info, +struct dso **dso) +{ +enum dso_type dso_type; + + dso_type =machine__thread_dso_type (machine, thread); +switch (dso_type) { +case DSO__TYPE_32BIT : + *dso =vdso__findnew_compat (machine, &vdso_info->vdso32); +return 1 ; +case DSO__TYPE_X32BIT : + *dso =vdso__findnew_compat (machine, &vdso_info->vdsox32); +return 1 ; +case DSO__TYPE_UNKNOWN : +case DSO__TYPE_64BIT : +default : +return 0 ; + } +} + +#endif +struct dso *vdso__dso_findnew (struct machine *machine,struct thread *thread__maybe_unused ) {
@@ -123,6 +284,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,if (!vdso_info)return NULL ; +#if BITS_PER_LONG ==64 +if (vdso__dso_findnew_compat (machine, thread, vdso_info, &dso)) +return dso; +#endif + dso =dsos__find (&machine->user_dsos,DSO__NAME_VDSO , true);if (!dso) {char *file;
@@ -139,5 +305,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,bool dso__is_vdso (struct dso *dso) { -return !strcmp (dso->short_name,DSO__NAME_VDSO ); +return !strcmp (dso->short_name,DSO__NAME_VDSO ) || + !strcmp (dso->short_name,DSO__NAME_VDSO32 ) || + !strcmp (dso->short_name,DSO__NAME_VDSOX32 ); }
tools/perf/util/vdso.h +3 -1
@@ -7,7 +7,9 @@#define VDSO__MAP_NAME "[vdso]" -#define DSO__NAME_VDSO "[vdso]" +#define DSO__NAME_VDSO "[vdso]" +#define DSO__NAME_VDSO32 "[vdso32]" +#define DSO__NAME_VDSOX32 "[vdsox32]"static inline bool is_vdso_map (const char *filename) {
Signature · 1 line
1.9.1
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf tools: Add support for 32-bit compatibility VDSOs
perf tools: Add support for 32-bit compatibility VDSOs
Commit-ID: f6832e1720f5cc283703cfe9ccbfb46a3fb6f548 Gitweb: http://git.kernel.org/tip/f6832e1720f5cc283703cfe9ccbfb46a3fb6f548 Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:23 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:48 -0200
perf tools: Add support for 32-bit compatibility VDSOs
'perf record' post-processes the event stream to create a list of build-ids for object files for which sample events have been recorded. That results in those object files being recorded in the build-id cache.
In the case of VDSO, perf tools reads it from memory and copies it into a temporary file, which as decribed above, gets added to the build-id cache.
Then when the perf.data file is processed by other tools, the build-id of VDSO is listed in the perf.data file and the VDSO can be read from the build-id cache. In that case the name of the map, the short name of the DSO, and the entry in the build-id cache are all "[vdso]".
However, in the 64-bit case, there also can be 32-bit compatibility VDSOs.
A previous patch added programs "perf-read-vdso32" and "perf read-vdsox32".
This patch uses those programs to read the correct VDSO for a thread and create a temporary file just as for the 64-bit VDSO.
The map name and the entry in the build-id cache are still "[vdso]" but the DSO short name becomes "[vdso32]" and "[vdsox32]" respectively.
--- tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/vdso.h | 4 +- 2 files changed, 172 insertions(+), 2 deletions(-)
tools/perf/util/vdso.c +169 -1
@@ -12,6 +12,7 @@#include "util.h" #include "symbol.h" #include "machine.h" +#include "thread.h" #include "linux/string.h" #include "debug.h"
@@ -28,10 +29,15 @@ struct vdso_file {bool error;char temp_file_name[sizeof (VDSO__TEMP_FILE_NAME )];const char *dso_name; +const char *read_prog; };struct vdso_info {struct vdso_file vdso; +#if BITS_PER_LONG ==64 +struct vdso_file vdso32; +struct vdso_file vdsox32; +#endif };static struct vdso_info *vdso_info__new (void )
@@ -41,6 +47,18 @@ static struct vdso_info *vdso_info__new(void) .temp_file_name = VDSO__TEMP_FILE_NAME, .dso_name = DSO__NAME_VDSO, }, +#if BITS_PER_LONG ==64 + .vdso32 = { + .temp_file_name = VDSO__TEMP_FILE_NAME, + .dso_name = DSO__NAME_VDSO32, + .read_prog = "perf-read-vdso32", + }, + .vdsox32 = { + .temp_file_name = VDSO__TEMP_FILE_NAME, + .dso_name = DSO__NAME_VDSOX32, + .read_prog = "perf-read-vdsox32", + }, +#endif };return memdup (&vdso_info_init,sizeof (vdso_info_init));
@@ -92,6 +110,12 @@ void vdso__exit(struct machine *machine)if (vdso_info->vdso.found)unlink (vdso_info->vdso.temp_file_name); +#if BITS_PER_LONG ==64 +if (vdso_info->vdso32.found) +unlink (vdso_info->vdso32.temp_file_name); +if (vdso_info->vdsox32.found) +unlink (vdso_info->vdsox32.temp_file_name); +#endif zfree (&machine->vdso_info); }
@@ -110,6 +134,143 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,return dso; } +#if BITS_PER_LONG ==64 + +static enum dso_type machine__thread_dso_type (struct machine *machine, +struct thread *thread) +{ +enum dso_type dso_type =DSO__TYPE_UNKNOWN ; +struct map *map; +struct dso *dso; + + map =map_groups__first (thread->mg,MAP__FUNCTION ); +for (; map ; map =map_groups__next (map)) { + dso = map->dso; +if (!dso || dso->long_name[0 ] !='/' ) +continue ; + dso_type =dso__type (dso, machine); +if (dso_type !=DSO__TYPE_UNKNOWN ) +break ; + } + +return dso_type; +} + +static int vdso__do_copy_compat (FILE *f,int fd) +{ +char buf[4096 ]; +size_t count; + +while (1 ) { + count =fread (buf,1 ,sizeof (buf), f); +if (ferror (f)) +return -errno; +if (feof (f)) +break ; +if (count &&writen (fd, buf, count) != (ssize_t )count) +return -errno; + } + +return 0 ; +} + +static int vdso__copy_compat (const char *prog,int fd) +{ +FILE *f; +int err; + + f =popen (prog,"r" ); +if (!f) +return -errno; + + err =vdso__do_copy_compat (f, fd); + +if (pclose (f) ==-1 ) +return -errno; + +return err; +} + +static int vdso__create_compat_file (const char *prog,char *temp_name) +{ +int fd, err; + + fd =mkstemp (temp_name); +if (fd <0 ) +return -errno; + + err =vdso__copy_compat (prog, fd); + +if (close (fd) ==-1 ) +return -errno; + +return err; +} + +static const char *vdso__get_compat_file (struct vdso_file *vdso_file) +{ +int err; + +if (vdso_file->found) +return vdso_file->temp_file_name; + +if (vdso_file->error) +return NULL ; + + err =vdso__create_compat_file (vdso_file->read_prog, + vdso_file->temp_file_name); +if (err) { +pr_err ("%s failed, error %d\n" , vdso_file->read_prog, err); + vdso_file->error = true; +return NULL ; + } + + vdso_file->found = true; + +return vdso_file->temp_file_name; +} + +static struct dso *vdso__findnew_compat (struct machine *machine, +struct vdso_file *vdso_file) +{ +const char *file_name; +struct dso *dso; + + dso =dsos__find (&machine->user_dsos, vdso_file->dso_name, true); +if (dso) +return dso; + + file_name =vdso__get_compat_file (vdso_file); +if (!file_name) +return NULL ; + +return vdso__new (machine, vdso_file->dso_name, file_name); +} + +static int vdso__dso_findnew_compat (struct machine *machine, +struct thread *thread, +struct vdso_info *vdso_info, +struct dso **dso) +{ +enum dso_type dso_type; + + dso_type =machine__thread_dso_type (machine, thread); +switch (dso_type) { +case DSO__TYPE_32BIT : + *dso =vdso__findnew_compat (machine, &vdso_info->vdso32); +return 1 ; +case DSO__TYPE_X32BIT : + *dso =vdso__findnew_compat (machine, &vdso_info->vdsox32); +return 1 ; +case DSO__TYPE_UNKNOWN : +case DSO__TYPE_64BIT : +default : +return 0 ; + } +} + +#endif +struct dso *vdso__dso_findnew (struct machine *machine,struct thread *thread__maybe_unused ) {
@@ -123,6 +284,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,if (!vdso_info)return NULL ; +#if BITS_PER_LONG ==64 +if (vdso__dso_findnew_compat (machine, thread, vdso_info, &dso)) +return dso; +#endif + dso =dsos__find (&machine->user_dsos,DSO__NAME_VDSO , true);if (!dso) {char *file;
@@ -139,5 +305,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,bool dso__is_vdso (struct dso *dso) { -return !strcmp (dso->short_name,DSO__NAME_VDSO ); +return !strcmp (dso->short_name,DSO__NAME_VDSO ) || + !strcmp (dso->short_name,DSO__NAME_VDSO32 ) || + !strcmp (dso->short_name,DSO__NAME_VDSOX32 ); }
tools/perf/util/vdso.h +3 -1
@@ -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) {
Adrian Hunter
reviewerPATCH 16/16 perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
In reply to Adrian Hunterpopen() causes an error message to print if perf-read-vdso32 does not run. Avoid that by not trying to run it if it was not built. Ditto perf-read-vdsox32.
--- tools/perf/config/Makefile | 8 ++++++-- tools/perf/util/vdso.c | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-)
tools/perf/config/Makefile +6 -2
@@ -627,7 +627,9 @@ endif ifeq (${IS_64_BIT}, 1) ifndef NO_PERF_READ_VDSO32 $(call feature_check,compile-32) - ifneq ($(feature-compile-32), 1) + ifeq ($(feature-compile-32), 1) + CFLAGS += -DHAVE_PERF_READ_VDSO32 + else NO_PERF_READ_VDSO32 := 1 endif endif
@@ -636,7 +638,9 @@ ifeq (${IS_64_BIT}, 1) endif ifndef NO_PERF_READ_VDSOX32 $(call feature_check,compile-x32) - ifneq ($(feature-compile-x32), 1) + ifeq ($(feature-compile-x32), 1) + CFLAGS += -DHAVE_PERF_READ_VDSOX32 + else NO_PERF_READ_VDSOX32 := 1 endif endif
tools/perf/util/vdso.c +10 -0
@@ -255,6 +255,16 @@ static int vdso__dso_findnew_compat(struct machine *machine,enum dso_type dso_type; dso_type =machine__thread_dso_type (machine, thread); + +#ifndef HAVE_PERF_READ_VDSO32 +if (dso_type ==DSO__TYPE_32BIT ) +return 0 ; +#endif +#ifndef HAVE_PERF_READ_VDSOX32 +if (dso_type ==DSO__TYPE_X32BIT ) +return 0 ; +#endif +switch (dso_type) {case DSO__TYPE_32BIT : *dso =vdso__findnew_compat (machine, &vdso_info->vdso32);
Signature · 1 line
1.9.1
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
Commit-ID: 46b1fa85ff5a2e03423770b3931b97266e8ac6cf Gitweb: http://git.kernel.org/tip/46b1fa85ff5a2e03423770b3931b97266e8ac6cf Author: Adrian Hunter <[email protected]> AuthorDate: Thu, 23 Oct 2014 13:45:24 +0300 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 10:32:48 -0200
perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
popen() causes an error message to print if perf-read-vdso32 does not run. Avoid that by not trying to run it if it was not built. Ditto perf-read-vdsox32.
--- tools/perf/config/Makefile | 8 ++++++-- tools/perf/util/vdso.c | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-)
tools/perf/config/Makefile +6 -2
@@ -627,7 +627,9 @@ endif ifeq (${IS_64_BIT}, 1) ifndef NO_PERF_READ_VDSO32 $(call feature_check,compile-32) - ifneq ($(feature-compile-32), 1) + ifeq ($(feature-compile-32), 1) + CFLAGS += -DHAVE_PERF_READ_VDSO32 + else NO_PERF_READ_VDSO32 := 1 endif endif
@@ -636,7 +638,9 @@ ifeq (${IS_64_BIT}, 1) endif ifndef NO_PERF_READ_VDSOX32 $(call feature_check,compile-x32) - ifneq ($(feature-compile-x32), 1) + ifeq ($(feature-compile-x32), 1) + CFLAGS += -DHAVE_PERF_READ_VDSOX32 + else NO_PERF_READ_VDSOX32 := 1 endif endif
tools/perf/util/vdso.c +10 -0
@@ -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);
Arnaldo Carvalho de Melo
maintainerEm Thu, Oct 23, 2014 at 01:45:08PM +0300, Adrian Hunter escreveu:
Quoted 7 lines
Hi
Here are the remaining Intel PT preparation patches. They are all patches that have been posted before, although "perf tools:
Do not attempt to run perf-read-vdso32 if it wasn't built" wasn't sent as a separate email.
Quoted 6 lines
With regard to "32-bit compatibility VDSOs", you may recall that Ingo had a problem getting "sh: perf-read-vdso32: command not found". That message is inevitable when using popen to run perf-read-vdso32 and perf-read-vdso32 cannot be found. At least with the patch "perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built" one reason for the message is eliminated.
The above patches I will try to process, look ok but since they are at the end of the patch kit, it conflicts if I don't process the ones at the start of the patchkit, which I am still not confortable merging.
- Arnaldo
Quoted 70 lines
It would be nice to target 3.19 for Intel PT, but there are still the real patches of which there are about 48.
Adrian Hunter (16):
perf tools: Add id index
perf pmu: Let pmu's with no events show up on perf list
perf session: Addperf_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: Addbranch_typeandin_txto 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 builttools/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.hRegards
Adrian
Arnaldo Carvalho de Melo
maintainerEm Thu, Oct 23, 2014 at 01:45:08PM +0300, Adrian Hunter escreveu:
Quoted 1 line
Adrian Hunter (16):
Applied these, will test those and put in my next pull req to Ingo:
Quoted 4 lines
perf pmu: Let pmu's with no events show up on perf list perf tools: Build programs to copy 32-bit compatibility VDSOs perf tools: Add support for 32-bit compatibility VDSOs perf tools: Do not attempt to run perf-read-vdso32 if it wasn't built
The next two are related, made questions about the first:
Quoted 2 lines
perf tools: Add id index
perf session: Addperf_session__deliver_synth_event()
Made some questions about these two as well:
Quoted 2 lines
perf tools: Add a thread stack for synthesizing call chains perf tools: Enhance the thread stack to output call/return data
These all are about the db_export, if time permits tomorrow I'll try to do what I suggested and post a patch for you to see, i.e. basically the first one is the one with the questions.
Quoted 8 lines
perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Addbranch_typeandin_txto 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