Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.

Signed-off-by: Chris Phlipot <cphlipot0@gmail.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1461831551-12213-7-git-send-email-cphlipot0@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Chris Phlipot and committed by
Arnaldo Carvalho de Melo
3521f3bc 2c15f5eb

+30 -17
+30 -17
tools/perf/scripts/python/export-to-postgresql.py
··· 223 223 224 224 perf_db_export_mode = True 225 225 perf_db_export_calls = False 226 + perf_db_export_callchains = False 227 + 226 228 227 229 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>]" 229 231 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" 231 234 raise Exception("Too few arguments") 232 235 233 236 if (len(sys.argv) < 2): ··· 248 245 249 246 branches = (columns == "branches") 250 247 251 - if (len(sys.argv) >= 4): 252 - if (sys.argv[3] == "calls"): 248 + for i in range(3,len(sys.argv)): 249 + if (sys.argv[i] == "calls"): 253 250 perf_db_export_calls = True 251 + elif (sys.argv[i] == "callchains"): 252 + perf_db_export_callchains = True 254 253 else: 255 254 usage() 256 255 ··· 363 358 'transaction bigint,' 364 359 'data_src bigint,' 365 360 'branch_type integer,' 366 - 'in_tx boolean)') 361 + 'in_tx boolean,' 362 + 'call_path_id bigint)') 367 363 368 - if perf_db_export_calls: 364 + if perf_db_export_calls or perf_db_export_callchains: 369 365 do_query(query, 'CREATE TABLE call_paths (' 370 366 'id bigint NOT NULL,' 371 367 'parent_id bigint,' 372 368 'symbol_id bigint,' 373 369 'ip bigint)') 370 + if perf_db_export_calls: 374 371 do_query(query, 'CREATE TABLE calls (' 375 372 'id bigint NOT NULL,' 376 373 'thread_id bigint,' ··· 434 427 '(SELECT tid FROM threads WHERE id = thread_id) AS tid' 435 428 ' FROM comm_threads') 436 429 437 - if perf_db_export_calls: 430 + if perf_db_export_calls or perf_db_export_callchains: 438 431 do_query(query, 'CREATE VIEW call_paths_view AS ' 439 432 'SELECT ' 440 433 'c.id,' ··· 450 443 '(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,' 451 444 '(SELECT dso FROM symbols_view WHERE id = p.symbol_id) AS parent_dso_short_name' 452 445 ' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id') 446 + if perf_db_export_calls: 453 447 do_query(query, 'CREATE VIEW calls_view AS ' 454 448 'SELECT ' 455 449 'calls.id,' ··· 548 540 symbol_file = open_output_file("symbol_table.bin") 549 541 branch_type_file = open_output_file("branch_type_table.bin") 550 542 sample_file = open_output_file("sample_table.bin") 551 - if perf_db_export_calls: 543 + if perf_db_export_calls or perf_db_export_callchains: 552 544 call_path_file = open_output_file("call_path_table.bin") 545 + if perf_db_export_calls: 553 546 call_file = open_output_file("call_table.bin") 554 547 555 548 def trace_begin(): ··· 562 553 comm_table(0, "unknown") 563 554 dso_table(0, 0, "unknown", "unknown", "") 564 555 symbol_table(0, 0, 0, 0, 0, "unknown") 565 - sample_table(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: 556 + sample_table(0, 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 or perf_db_export_callchains: 567 558 call_path_table(0, 0, 0, 0) 568 559 569 560 unhandled_count = 0 ··· 579 570 copy_output_file(symbol_file, "symbols") 580 571 copy_output_file(branch_type_file, "branch_types") 581 572 copy_output_file(sample_file, "samples") 582 - if perf_db_export_calls: 573 + if perf_db_export_calls or perf_db_export_callchains: 583 574 copy_output_file(call_path_file, "call_paths") 575 + if perf_db_export_calls: 584 576 copy_output_file(call_file, "calls") 585 577 586 578 print datetime.datetime.today(), "Removing intermediate files..." ··· 594 584 remove_output_file(symbol_file) 595 585 remove_output_file(branch_type_file) 596 586 remove_output_file(sample_file) 597 - if perf_db_export_calls: 587 + if perf_db_export_calls or perf_db_export_callchains: 598 588 remove_output_file(call_path_file) 589 + if perf_db_export_calls: 599 590 remove_output_file(call_file) 600 591 os.rmdir(output_dir_name) 601 592 print datetime.datetime.today(), "Adding primary keys" ··· 609 598 do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') 610 599 do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') 611 600 do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') 612 - if perf_db_export_calls: 601 + if perf_db_export_calls or perf_db_export_callchains: 613 602 do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') 603 + if perf_db_export_calls: 614 604 do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') 615 605 616 606 print datetime.datetime.today(), "Adding foreign keys" ··· 634 622 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' 635 623 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' 636 624 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') 637 - if perf_db_export_calls: 625 + if perf_db_export_calls or perf_db_export_callchains: 638 626 do_query(query, 'ALTER TABLE call_paths ' 639 627 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' 640 628 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') 629 + if perf_db_export_calls: 641 630 do_query(query, 'ALTER TABLE calls ' 642 631 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' 643 632 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' ··· 706 693 value = struct.pack(fmt, 2, 4, branch_type, n, name) 707 694 branch_type_file.write(value) 708 695 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, *x): 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, call_path_id, *x): 710 697 if branches: 711 - 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) 698 + 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) 712 699 else: 713 - 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) 700 + 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) 714 701 sample_file.write(value) 715 702 716 703 def call_path_table(cp_id, parent_id, symbol_id, ip, *x):