perf tools: Intel PT preparation continued
Adrian Hunter
reviewercover perf tools: Intel PT preparation continued
Hi
Here are the remaining Intel PT preparation patches, re-based on your current tree.
Changes:
perf tools: Add id index
Added more explanation to commit messsage
perf tools: Add a thread stack for synthesizing call chains
Added out-of-memory warnings
Improved thread_stack__pop() loop
Adrian Hunter (9):
perf tools: Add id index
perf session: Add perf_session__deliver_synth_event() perf tools: Add a thread stack for synthesizing call chains 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'
tools/perf/Makefile.perf | 2 + tools/perf/builtin-inject.c | 1 + .../scripts/python/bin/export-to-postgresql-report | 15 +- tools/perf/scripts/python/export-to-postgresql.py | 98 ++- tools/perf/util/db-export.c | 162 ++++- tools/perf/util/db-export.h | 21 + tools/perf/util/event.c | 1 + tools/perf/util/event.h | 41 ++ tools/perf/util/evlist.c | 26 +- tools/perf/util/evsel.h | 3 + .../util/scripting-engines/trace-event-python.c | 118 +++- tools/perf/util/session.c | 136 ++++ tools/perf/util/session.h | 15 + tools/perf/util/thread-stack.c | 696 +++++++++++++++++++++ tools/perf/util/thread-stack.h | 79 +++ tools/perf/util/thread.c | 3 + tools/perf/util/thread.h | 3 + tools/perf/util/tool.h | 3 +- 18 files changed, 1402 insertions(+), 21 deletions(-) create mode 100644 tools/perf/util/thread-stack.c create mode 100644 tools/perf/util/thread-stack.h
Regards
Adrian
Adrian Hunter
reviewerPATCH 1/9 perf tools: Add id index
Add an index of the event identifiers, in preparation for Intel PT.
The event id (also called the sample id) 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().
--- 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
@@ -409,6 +409,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
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf tools: Add id index
perf tools: Add id index
Commit-ID: 3c659eedada2fbf909c5818848753a6647a56426 Gitweb: http://git.kernel.org/tip/3c659eedada2fbf909c5818848753a6647a56426 Author: Adrian Hunter <[email protected]> AuthorDate: Mon, 27 Oct 2014 15:49:22 +0200 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 11:24:47 -0200
perf tools: Add id index
Add an index of the event identifiers, in preparation for Intel PT.
The event id (also called the sample id) 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().
--- 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
@@ -409,6 +409,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; };
Adrian Hunter
reviewerPATCH 2/9 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
tip-bot for Adrian Hunter
merge bot[tip:perf/core] perf session: Add perf_session__deliver_synth_event()
perf session: Add perf_session__deliver_synth_event()
Commit-ID: a293829df788ae96a174b315010d4b56a10e5114 Gitweb: http://git.kernel.org/tip/a293829df788ae96a174b315010d4b56a10e5114 Author: Adrian Hunter <[email protected]> AuthorDate: Mon, 27 Oct 2014 15:49:23 +0200 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 29 Oct 2014 11:36:15 -0200
perf session: Add perf_session__deliver_synth_event()
Add a function to deliver synthesized events from within a session.
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.
--- tools/perf/util/session.c | 13 +++++++++++++ tools/perf/util/session.h | 5 +++++ 2 files changed, 18 insertions(+)
tools/perf/util/session.c +13 -0
@@ -907,6 +907,19 @@ 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);
Adrian Hunter
reviewerPATCH 3/9 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 | 158 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/thread-stack.h | 32 +++++++++ tools/perf/util/thread.c | 3 + tools/perf/util/thread.h | 3 + 6 files changed, 224 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
@@ -317,6 +317,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
@@ -394,6 +395,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 +158 -0
158 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;
@@ -26,6 +28,7 @@ struct thread {u64 db_id;void *priv; +struct thread_stack *ts; };struct machine ;
Signature · 1 line
1.9.1
Adrian Hunter
reviewerPATCH 4/9 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
@@ -208,6 +208,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)
@@ -268,3 +277,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 5/9 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; };
@@ -664,13 +665,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);
@@ -691,6 +710,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" );
@@ -861,6 +882,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); }
@@ -910,6 +932,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 6/9 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 | 546 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/thread-stack.h | 47 ++++ 2 files changed, 589 insertions(+), 4 deletions(-)
tools/perf/util/thread-stack.c +542 -4
@@ -13,16 +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 {
@@ -30,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)
@@ -46,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;
@@ -60,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; }
@@ -97,6 +141,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) {
@@ -104,7 +206,7 @@ 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) {pr_warning ("Out of memory: no thread stack\n" );return ;
@@ -113,10 +215,15 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, }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;
@@ -133,9 +240,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); }
@@ -156,3 +276,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
Adrian Hunter
reviewerPATCH 7/9 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)
@@ -270,6 +273,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)
@@ -316,3 +326,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 8/9 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 9/9 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
@@ -1030,7 +1030,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