History

perf script: Update export-to-postgresql to support callchain export

Update the export-to-postgresql.py to support the newly introduced callchain export. callchains are added into the existing call_paths table and can now be associated with samples when the "callpaths" commandline option is used with the script. Ex.: $ perf script -s export-to-postgresql.py example_db all callchains Includes the following changes to enable callchain export via the python export APIs: - Add the "callchains" commandline option, which is used to enable callchain export by setting the perf_db_export_callchains global - Add perf_db_export_callchains checks for call_path table creation and population. - Add call_path_id to samples_table to conform with the new API example usage and output using a small test app: test_app.c: volatile int x = 0; void inc_x_loop() { int i; for(i=0; i<100000000; i++) x++; } void a() { inc_x_loop(); } void b() { inc_x_loop(); } int main() { a(); b(); return 0; } example usage: $ gcc -g -O0 test_app.c $ perf record --call-graph=dwarf ./a.out [ perf record: Woken up 77 times to write data ] [ perf record: Captured and wrote 19.373 MB perf.data (2404 samples) ] $ perf script -s scripts/python/export-to-postgresql.py example_db all callchains $ psql example_db example_db=# SELECT (SELECT name FROM symbols WHERE id = cps.symbol_id) as symbol, (SELECT name FROM symbols WHERE id = (SELECT symbol_id from call_paths where id = cps.parent_id)) as parent_symbol, sum(period) as event_count FROM samples join call_paths as cps on call_path_id = cps.id GROUP BY cps.id,evsel_id ORDER BY event_count DESC LIMIT 5; symbol | parent_symbol | event_count ------------------+--------------------------+------------- inc_x_loop | a | 734250982 inc_x_loop | b | 731028057 unknown | unknown | 1335858 task_tick_fair | scheduler_tick | 1238842 update_wall_time | tick_do_update_jiffies64 | 650373 (5 rows) The above data shows total "self time" in cycles for each call path that was sampled. It is intended to demonstrate how it accounts separately for the two ways to reach the "inc_x_loop" function(via "a" and "b"). Recursive common table expressions can be used as well to get cumulative time spent in a function as well, but that is beyond the scope of this basic example.

Chris Phlipot committed 10 years ago

Commit 3521f3b1 parent 2c15f5e1 file · +30 −17Browse files

Showing the changes to tools/perf/scripts/python (1 of 1 file). Show all

Unified · Split

tools/perf/scripts/python/export-to-postgresql.py+30−17
@@ -223,11 +223,14 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \ · 222 unmodified lines
223223
224224 perf_db_export_mode = True
225225 perf_db_export_calls = False
226+perf_db_export_callchains = False
227+
226228
227229 def usage():
228− print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
230+ print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]"
229231 print >> sys.stderr, "where: columns 'all' or 'branches'"
230− print >> sys.stderr, " calls 'calls' => create calls table"
232+ print >> sys.stderr, " calls 'calls' => create calls and call_paths table"
233+ print >> sys.stderr, " callchains 'callchains' => create call_paths table"
231234 raise Exception("Too few arguments")
232235
233236 if (len(sys.argv) < 2):
@@ -245,9 +248,11 @@ if columns not in ("all", "branches"): · 11 unmodified lines
245248
246249 branches = (columns == "branches")
247250
248−if (len(sys.argv) >= 4):
249− if (sys.argv[3] == "calls"):
251+for i in range(3,len(sys.argv)):
252+ if (sys.argv[i] == "calls"):
250253 perf_db_export_calls = True
254+ elif (sys.argv[i] == "callchains"):
255+ perf_db_export_callchains = True
251256 else:
252257 usage()
253258
@@ -358,14 +363,16 @@ else: · 104 unmodified lines
358363 'transaction bigint,'
359364 'data_src bigint,'
360365 'branch_type integer,'
361− 'in_tx boolean)')
366+ 'in_tx boolean,'
367+ 'call_path_id bigint)')
362368
363−if perf_db_export_calls:
369+if perf_db_export_calls or perf_db_export_callchains:
364370 do_query(query, 'CREATE TABLE call_paths ('
365371 'id bigint NOT NULL,'
366372 'parent_id bigint,'
367373 'symbol_id bigint,'
368374 'ip bigint)')
375+if perf_db_export_calls:
369376 do_query(query, 'CREATE TABLE calls ('
370377 'id bigint NOT NULL,'
371378 'thread_id bigint,'
@@ -427,7 +434,7 @@ do_query(query, 'CREATE VIEW comm_threads_view AS ' · 55 unmodified lines
427434 '(SELECT tid FROM threads WHERE id = thread_id) AS tid'
428435 ' FROM comm_threads')
429436
430−if perf_db_export_calls:
437+if perf_db_export_calls or perf_db_export_callchains:
431438 do_query(query, 'CREATE VIEW call_paths_view AS '
432439 'SELECT '
433440 'c.id,'
@@ -443,6 +450,7 @@ if perf_db_export_calls: · 9 unmodified lines
443450 '(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,'
444451 '(SELECT dso FROM symbols_view WHERE id = p.symbol_id) AS parent_dso_short_name'
445452 ' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id')
453+if perf_db_export_calls:
446454 do_query(query, 'CREATE VIEW calls_view AS '
447455 'SELECT '
448456 'calls.id,'
@@ -540,8 +548,9 @@ dso_file = open_output_file("dso_table.bin") · 91 unmodified lines
540548 symbol_file = open_output_file("symbol_table.bin")
541549 branch_type_file = open_output_file("branch_type_table.bin")
542550 sample_file = open_output_file("sample_table.bin")
543−if perf_db_export_calls:
551+if perf_db_export_calls or perf_db_export_callchains:
544552 call_path_file = open_output_file("call_path_table.bin")
553+if perf_db_export_calls:
545554 call_file = open_output_file("call_table.bin")
546555
547556 def trace_begin():
@@ -553,8 +562,8 @@ def trace_begin(): · 5 unmodified lines
553562 comm_table(0, "unknown")
554563 dso_table(0, 0, "unknown", "unknown", "")
555564 symbol_table(0, 0, 0, 0, 0, "unknown")
556− sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
557− if perf_db_export_calls:
565+ sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
566+ if perf_db_export_calls or perf_db_export_callchains:
558567 call_path_table(0, 0, 0, 0)
559568
560569 unhandled_count = 0
@@ -570,8 +579,9 @@ def trace_end(): · 9 unmodified lines
570579 copy_output_file(symbol_file, "symbols")
571580 copy_output_file(branch_type_file, "branch_types")
572581 copy_output_file(sample_file, "samples")
573− if perf_db_export_calls:
582+ if perf_db_export_calls or perf_db_export_callchains:
574583 copy_output_file(call_path_file, "call_paths")
584+ if perf_db_export_calls:
575585 copy_output_file(call_file, "calls")
576586
577587 print datetime.datetime.today(), "Removing intermediate files..."
@@ -584,8 +594,9 @@ def trace_end(): · 6 unmodified lines
584594 remove_output_file(symbol_file)
585595 remove_output_file(branch_type_file)
586596 remove_output_file(sample_file)
587− if perf_db_export_calls:
597+ if perf_db_export_calls or perf_db_export_callchains:
588598 remove_output_file(call_path_file)
599+ if perf_db_export_calls:
589600 remove_output_file(call_file)
590601 os.rmdir(output_dir_name)
591602 print datetime.datetime.today(), "Adding primary keys"
@@ -598,8 +609,9 @@ def trace_end(): · 6 unmodified lines
598609 do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
599610 do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
600611 do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
601− if perf_db_export_calls:
612+ if perf_db_export_calls or perf_db_export_callchains:
602613 do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
614+ if perf_db_export_calls:
603615 do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
604616
605617 print datetime.datetime.today(), "Adding foreign keys"
@@ -622,10 +634,11 @@ def trace_end(): · 16 unmodified lines
622634 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
623635 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
624636 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
625− if perf_db_export_calls:
637+ if perf_db_export_calls or perf_db_export_callchains:
626638 do_query(query, 'ALTER TABLE call_paths '
627639 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),'
628640 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)')
641+ if perf_db_export_calls:
629642 do_query(query, 'ALTER TABLE calls '
630643 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
631644 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
@@ -693,11 +706,11 @@ def branch_type_table(branch_type, name, *x): · 61 unmodified lines
693706 value = struct.pack(fmt, 2, 4, branch_type, n, name)
694707 branch_type_file.write(value)
695708
696−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):
709+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, call_path_id, *x):
697710 if branches:
698− 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)
711+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiq", 18, 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, 8, call_path_id)
699712 else:
700− 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)
713+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiq", 22, 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, 8, call_path_id)
701714 sample_file.write(value)
702715
703716 def call_path_table(cp_id, parent_id, symbol_id, ip, *x):