diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2014-10-30 10:09:47 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2014-11-03 16:10:06 -0500 |
commit | 6a70307ddcd9999598c399d55dc44c07816a575f (patch) | |
tree | 210143ded656ebf0ebc3f828caa0ddf7c080a599 | |
parent | 88f50d602f500d206f2f5a9a9751dd45f2d97739 (diff) |
perf tools: Add call information to Python export
Add the ability to export detailed information about paired calls and
returns to Python db export and the export-to-postgresql.py script.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414678188-14946-7-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/scripts/python/bin/export-to-postgresql-report | 15 | ||||
-rw-r--r-- | tools/perf/scripts/python/export-to-postgresql.py | 66 | ||||
-rw-r--r-- | tools/perf/util/scripting-engines/trace-event-python.c | 84 |
3 files changed, 158 insertions, 7 deletions
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report index a8fdd15f85bf..cd335b6e2a01 100644 --- a/tools/perf/scripts/python/bin/export-to-postgresql-report +++ b/tools/perf/scripts/python/bin/export-to-postgresql-report | |||
@@ -1,6 +1,6 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: export perf data to a postgresql database | 2 | # description: export perf data to a postgresql database |
3 | # args: [database name] [columns] | 3 | # args: [database name] [columns] [calls] |
4 | n_args=0 | 4 | n_args=0 |
5 | for i in "$@" | 5 | for i in "$@" |
6 | do | 6 | do |
@@ -9,11 +9,16 @@ do | |||
9 | fi | 9 | fi |
10 | n_args=$(( $n_args + 1 )) | 10 | n_args=$(( $n_args + 1 )) |
11 | done | 11 | done |
12 | if [ "$n_args" -gt 2 ] ; then | 12 | if [ "$n_args" -gt 3 ] ; then |
13 | echo "usage: export-to-postgresql-report [database name] [columns]" | 13 | echo "usage: export-to-postgresql-report [database name] [columns] [calls]" |
14 | exit | 14 | exit |
15 | fi | 15 | fi |
16 | if [ "$n_args" -gt 1 ] ; then | 16 | if [ "$n_args" -gt 2 ] ; then |
17 | dbname=$1 | ||
18 | columns=$2 | ||
19 | calls=$3 | ||
20 | shift 3 | ||
21 | elif [ "$n_args" -gt 1 ] ; then | ||
17 | dbname=$1 | 22 | dbname=$1 |
18 | columns=$2 | 23 | columns=$2 |
19 | shift 2 | 24 | shift 2 |
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then | |||
21 | dbname=$1 | 26 | dbname=$1 |
22 | shift | 27 | shift |
23 | fi | 28 | fi |
24 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns | 29 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls |
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index bb79aecccf58..4cdafd880074 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
@@ -40,10 +40,12 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | |||
40 | #from Core import * | 40 | #from Core import * |
41 | 41 | ||
42 | perf_db_export_mode = True | 42 | perf_db_export_mode = True |
43 | perf_db_export_calls = False | ||
43 | 44 | ||
44 | def usage(): | 45 | def usage(): |
45 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]" | 46 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]" |
46 | print >> sys.stderr, "where: columns 'all' or 'branches'" | 47 | print >> sys.stderr, "where: columns 'all' or 'branches'" |
48 | print >> sys.stderr, " calls 'calls' => create calls table" | ||
47 | raise Exception("Too few arguments") | 49 | raise Exception("Too few arguments") |
48 | 50 | ||
49 | if (len(sys.argv) < 2): | 51 | if (len(sys.argv) < 2): |
@@ -61,6 +63,12 @@ if columns not in ("all", "branches"): | |||
61 | 63 | ||
62 | branches = (columns == "branches") | 64 | branches = (columns == "branches") |
63 | 65 | ||
66 | if (len(sys.argv) >= 4): | ||
67 | if (sys.argv[3] == "calls"): | ||
68 | perf_db_export_calls = True | ||
69 | else: | ||
70 | usage() | ||
71 | |||
64 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" | 72 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" |
65 | os.mkdir(output_dir_name) | 73 | os.mkdir(output_dir_name) |
66 | 74 | ||
@@ -170,6 +178,25 @@ else: | |||
170 | 'branch_type integer,' | 178 | 'branch_type integer,' |
171 | 'in_tx boolean)') | 179 | 'in_tx boolean)') |
172 | 180 | ||
181 | if perf_db_export_calls: | ||
182 | do_query(query, 'CREATE TABLE call_paths (' | ||
183 | 'id bigint NOT NULL,' | ||
184 | 'parent_id bigint,' | ||
185 | 'symbol_id bigint,' | ||
186 | 'ip bigint)') | ||
187 | do_query(query, 'CREATE TABLE calls (' | ||
188 | 'id bigint NOT NULL,' | ||
189 | 'thread_id bigint,' | ||
190 | 'comm_id bigint,' | ||
191 | 'call_path_id bigint,' | ||
192 | 'call_time bigint,' | ||
193 | 'return_time bigint,' | ||
194 | 'branch_count bigint,' | ||
195 | 'call_id bigint,' | ||
196 | 'return_id bigint,' | ||
197 | 'parent_call_path_id bigint,' | ||
198 | 'flags integer)') | ||
199 | |||
173 | do_query(query, 'CREATE VIEW samples_view AS ' | 200 | do_query(query, 'CREATE VIEW samples_view AS ' |
174 | 'SELECT ' | 201 | 'SELECT ' |
175 | 'id,' | 202 | 'id,' |
@@ -246,6 +273,9 @@ dso_file = open_output_file("dso_table.bin") | |||
246 | symbol_file = open_output_file("symbol_table.bin") | 273 | symbol_file = open_output_file("symbol_table.bin") |
247 | branch_type_file = open_output_file("branch_type_table.bin") | 274 | branch_type_file = open_output_file("branch_type_table.bin") |
248 | sample_file = open_output_file("sample_table.bin") | 275 | sample_file = open_output_file("sample_table.bin") |
276 | if perf_db_export_calls: | ||
277 | call_path_file = open_output_file("call_path_table.bin") | ||
278 | call_file = open_output_file("call_table.bin") | ||
249 | 279 | ||
250 | def trace_begin(): | 280 | def trace_begin(): |
251 | print datetime.datetime.today(), "Writing to intermediate files..." | 281 | print datetime.datetime.today(), "Writing to intermediate files..." |
@@ -256,6 +286,9 @@ def trace_begin(): | |||
256 | comm_table(0, "unknown") | 286 | comm_table(0, "unknown") |
257 | dso_table(0, 0, "unknown", "unknown", "") | 287 | dso_table(0, 0, "unknown", "unknown", "") |
258 | symbol_table(0, 0, 0, 0, 0, "unknown") | 288 | symbol_table(0, 0, 0, 0, 0, "unknown") |
289 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
290 | if perf_db_export_calls: | ||
291 | call_path_table(0, 0, 0, 0) | ||
259 | 292 | ||
260 | unhandled_count = 0 | 293 | unhandled_count = 0 |
261 | 294 | ||
@@ -270,6 +303,9 @@ def trace_end(): | |||
270 | copy_output_file(symbol_file, "symbols") | 303 | copy_output_file(symbol_file, "symbols") |
271 | copy_output_file(branch_type_file, "branch_types") | 304 | copy_output_file(branch_type_file, "branch_types") |
272 | copy_output_file(sample_file, "samples") | 305 | copy_output_file(sample_file, "samples") |
306 | if perf_db_export_calls: | ||
307 | copy_output_file(call_path_file, "call_paths") | ||
308 | copy_output_file(call_file, "calls") | ||
273 | 309 | ||
274 | print datetime.datetime.today(), "Removing intermediate files..." | 310 | print datetime.datetime.today(), "Removing intermediate files..." |
275 | remove_output_file(evsel_file) | 311 | remove_output_file(evsel_file) |
@@ -281,6 +317,9 @@ def trace_end(): | |||
281 | remove_output_file(symbol_file) | 317 | remove_output_file(symbol_file) |
282 | remove_output_file(branch_type_file) | 318 | remove_output_file(branch_type_file) |
283 | remove_output_file(sample_file) | 319 | remove_output_file(sample_file) |
320 | if perf_db_export_calls: | ||
321 | remove_output_file(call_path_file) | ||
322 | remove_output_file(call_file) | ||
284 | os.rmdir(output_dir_name) | 323 | os.rmdir(output_dir_name) |
285 | print datetime.datetime.today(), "Adding primary keys" | 324 | print datetime.datetime.today(), "Adding primary keys" |
286 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') | 325 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') |
@@ -292,6 +331,9 @@ def trace_end(): | |||
292 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') | 331 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') |
293 | do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') | 332 | do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') |
294 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') | 333 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') |
334 | if perf_db_export_calls: | ||
335 | do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') | ||
336 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') | ||
295 | 337 | ||
296 | print datetime.datetime.today(), "Adding foreign keys" | 338 | print datetime.datetime.today(), "Adding foreign keys" |
297 | do_query(query, 'ALTER TABLE threads ' | 339 | do_query(query, 'ALTER TABLE threads ' |
@@ -313,6 +355,18 @@ def trace_end(): | |||
313 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' | 355 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' |
314 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' | 356 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' |
315 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') | 357 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') |
358 | if perf_db_export_calls: | ||
359 | do_query(query, 'ALTER TABLE call_paths ' | ||
360 | 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' | ||
361 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') | ||
362 | do_query(query, 'ALTER TABLE calls ' | ||
363 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
364 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
365 | 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' | ||
366 | 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' | ||
367 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' | ||
368 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') | ||
369 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') | ||
316 | 370 | ||
317 | if (unhandled_count): | 371 | if (unhandled_count): |
318 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" | 372 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" |
@@ -378,3 +432,13 @@ def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, sy | |||
378 | else: | 432 | else: |
379 | 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) | 433 | 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) |
380 | sample_file.write(value) | 434 | sample_file.write(value) |
435 | |||
436 | def call_path_table(cp_id, parent_id, symbol_id, ip, *x): | ||
437 | fmt = "!hiqiqiqiq" | ||
438 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) | ||
439 | call_path_file.write(value) | ||
440 | |||
441 | def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x): | ||
442 | fmt = "!hiqiqiqiqiqiqiqiqiqiqii" | ||
443 | value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags) | ||
444 | call_file.write(value) | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index f3ca7798b3d0..cb1d9602f418 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include "../comm.h" | 37 | #include "../comm.h" |
38 | #include "../machine.h" | 38 | #include "../machine.h" |
39 | #include "../db-export.h" | 39 | #include "../db-export.h" |
40 | #include "../thread-stack.h" | ||
40 | #include "../trace-event.h" | 41 | #include "../trace-event.h" |
41 | #include "../machine.h" | 42 | #include "../machine.h" |
42 | 43 | ||
@@ -68,6 +69,8 @@ struct tables { | |||
68 | PyObject *symbol_handler; | 69 | PyObject *symbol_handler; |
69 | PyObject *branch_type_handler; | 70 | PyObject *branch_type_handler; |
70 | PyObject *sample_handler; | 71 | PyObject *sample_handler; |
72 | PyObject *call_path_handler; | ||
73 | PyObject *call_return_handler; | ||
71 | bool db_export_mode; | 74 | bool db_export_mode; |
72 | }; | 75 | }; |
73 | 76 | ||
@@ -720,6 +723,64 @@ static int python_export_sample(struct db_export *dbe, | |||
720 | return 0; | 723 | return 0; |
721 | } | 724 | } |
722 | 725 | ||
726 | static int python_export_call_path(struct db_export *dbe, struct call_path *cp) | ||
727 | { | ||
728 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
729 | PyObject *t; | ||
730 | u64 parent_db_id, sym_db_id; | ||
731 | |||
732 | parent_db_id = cp->parent ? cp->parent->db_id : 0; | ||
733 | sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; | ||
734 | |||
735 | t = tuple_new(4); | ||
736 | |||
737 | tuple_set_u64(t, 0, cp->db_id); | ||
738 | tuple_set_u64(t, 1, parent_db_id); | ||
739 | tuple_set_u64(t, 2, sym_db_id); | ||
740 | tuple_set_u64(t, 3, cp->ip); | ||
741 | |||
742 | call_object(tables->call_path_handler, t, "call_path_table"); | ||
743 | |||
744 | Py_DECREF(t); | ||
745 | |||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | static int python_export_call_return(struct db_export *dbe, | ||
750 | struct call_return *cr) | ||
751 | { | ||
752 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
753 | u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; | ||
754 | PyObject *t; | ||
755 | |||
756 | t = tuple_new(11); | ||
757 | |||
758 | tuple_set_u64(t, 0, cr->db_id); | ||
759 | tuple_set_u64(t, 1, cr->thread->db_id); | ||
760 | tuple_set_u64(t, 2, comm_db_id); | ||
761 | tuple_set_u64(t, 3, cr->cp->db_id); | ||
762 | tuple_set_u64(t, 4, cr->call_time); | ||
763 | tuple_set_u64(t, 5, cr->return_time); | ||
764 | tuple_set_u64(t, 6, cr->branch_count); | ||
765 | tuple_set_u64(t, 7, cr->call_ref); | ||
766 | tuple_set_u64(t, 8, cr->return_ref); | ||
767 | tuple_set_u64(t, 9, cr->cp->parent->db_id); | ||
768 | tuple_set_s32(t, 10, cr->flags); | ||
769 | |||
770 | call_object(tables->call_return_handler, t, "call_return_table"); | ||
771 | |||
772 | Py_DECREF(t); | ||
773 | |||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static int python_process_call_return(struct call_return *cr, void *data) | ||
778 | { | ||
779 | struct db_export *dbe = data; | ||
780 | |||
781 | return db_export__call_return(dbe, cr); | ||
782 | } | ||
783 | |||
723 | static void python_process_general_event(struct perf_sample *sample, | 784 | static void python_process_general_event(struct perf_sample *sample, |
724 | struct perf_evsel *evsel, | 785 | struct perf_evsel *evsel, |
725 | struct thread *thread, | 786 | struct thread *thread, |
@@ -852,7 +913,9 @@ error: | |||
852 | static void set_table_handlers(struct tables *tables) | 913 | static void set_table_handlers(struct tables *tables) |
853 | { | 914 | { |
854 | const char *perf_db_export_mode = "perf_db_export_mode"; | 915 | const char *perf_db_export_mode = "perf_db_export_mode"; |
855 | PyObject *db_export_mode; | 916 | const char *perf_db_export_calls = "perf_db_export_calls"; |
917 | PyObject *db_export_mode, *db_export_calls; | ||
918 | bool export_calls = false; | ||
856 | int ret; | 919 | int ret; |
857 | 920 | ||
858 | memset(tables, 0, sizeof(struct tables)); | 921 | memset(tables, 0, sizeof(struct tables)); |
@@ -869,6 +932,23 @@ static void set_table_handlers(struct tables *tables) | |||
869 | if (!ret) | 932 | if (!ret) |
870 | return; | 933 | return; |
871 | 934 | ||
935 | tables->dbe.crp = NULL; | ||
936 | db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); | ||
937 | if (db_export_calls) { | ||
938 | ret = PyObject_IsTrue(db_export_calls); | ||
939 | if (ret == -1) | ||
940 | handler_call_die(perf_db_export_calls); | ||
941 | export_calls = !!ret; | ||
942 | } | ||
943 | |||
944 | if (export_calls) { | ||
945 | tables->dbe.crp = | ||
946 | call_return_processor__new(python_process_call_return, | ||
947 | &tables->dbe); | ||
948 | if (!tables->dbe.crp) | ||
949 | Py_FatalError("failed to create calls processor"); | ||
950 | } | ||
951 | |||
872 | tables->db_export_mode = true; | 952 | tables->db_export_mode = true; |
873 | /* | 953 | /* |
874 | * Reserve per symbol space for symbol->db_id via symbol__priv() | 954 | * Reserve per symbol space for symbol->db_id via symbol__priv() |
@@ -884,6 +964,8 @@ static void set_table_handlers(struct tables *tables) | |||
884 | SET_TABLE_HANDLER(symbol); | 964 | SET_TABLE_HANDLER(symbol); |
885 | SET_TABLE_HANDLER(branch_type); | 965 | SET_TABLE_HANDLER(branch_type); |
886 | SET_TABLE_HANDLER(sample); | 966 | SET_TABLE_HANDLER(sample); |
967 | SET_TABLE_HANDLER(call_path); | ||
968 | SET_TABLE_HANDLER(call_return); | ||
887 | } | 969 | } |
888 | 970 | ||
889 | /* | 971 | /* |