diff options
Diffstat (limited to 'tools/perf/scripts/python/export-to-postgresql.py')
| -rw-r--r-- | tools/perf/scripts/python/export-to-postgresql.py | 77 |
1 files changed, 54 insertions, 23 deletions
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index 0564dd7377f2..c3eae1d77d36 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 10 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 11 | # more details. | 11 | # more details. |
| 12 | 12 | ||
| 13 | from __future__ import print_function | ||
| 14 | |||
| 13 | import os | 15 | import os |
| 14 | import sys | 16 | import sys |
| 15 | import struct | 17 | import struct |
| @@ -199,6 +201,18 @@ import datetime | |||
| 199 | 201 | ||
| 200 | from PySide.QtSql import * | 202 | from PySide.QtSql import * |
| 201 | 203 | ||
| 204 | if sys.version_info < (3, 0): | ||
| 205 | def toserverstr(str): | ||
| 206 | return str | ||
| 207 | def toclientstr(str): | ||
| 208 | return str | ||
| 209 | else: | ||
| 210 | # Assume UTF-8 server_encoding and client_encoding | ||
| 211 | def toserverstr(str): | ||
| 212 | return bytes(str, "UTF_8") | ||
| 213 | def toclientstr(str): | ||
| 214 | return bytes(str, "UTF_8") | ||
| 215 | |||
| 202 | # Need to access PostgreSQL C library directly to use COPY FROM STDIN | 216 | # Need to access PostgreSQL C library directly to use COPY FROM STDIN |
| 203 | from ctypes import * | 217 | from ctypes import * |
| 204 | libpq = CDLL("libpq.so.5") | 218 | libpq = CDLL("libpq.so.5") |
| @@ -234,12 +248,17 @@ perf_db_export_mode = True | |||
| 234 | perf_db_export_calls = False | 248 | perf_db_export_calls = False |
| 235 | perf_db_export_callchains = False | 249 | perf_db_export_callchains = False |
| 236 | 250 | ||
| 251 | def printerr(*args, **kw_args): | ||
| 252 | print(*args, file=sys.stderr, **kw_args) | ||
| 253 | |||
| 254 | def printdate(*args, **kw_args): | ||
| 255 | print(datetime.datetime.today(), *args, sep=' ', **kw_args) | ||
| 237 | 256 | ||
| 238 | def usage(): | 257 | def usage(): |
| 239 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]" | 258 | printerr("Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]") |
| 240 | print >> sys.stderr, "where: columns 'all' or 'branches'" | 259 | printerr("where: columns 'all' or 'branches'") |
| 241 | print >> sys.stderr, " calls 'calls' => create calls and call_paths table" | 260 | printerr(" calls 'calls' => create calls and call_paths table") |
| 242 | print >> sys.stderr, " callchains 'callchains' => create call_paths table" | 261 | printerr(" callchains 'callchains' => create call_paths table") |
| 243 | raise Exception("Too few arguments") | 262 | raise Exception("Too few arguments") |
| 244 | 263 | ||
| 245 | if (len(sys.argv) < 2): | 264 | if (len(sys.argv) < 2): |
| @@ -273,7 +292,7 @@ def do_query(q, s): | |||
| 273 | return | 292 | return |
| 274 | raise Exception("Query failed: " + q.lastError().text()) | 293 | raise Exception("Query failed: " + q.lastError().text()) |
| 275 | 294 | ||
| 276 | print datetime.datetime.today(), "Creating database..." | 295 | printdate("Creating database...") |
| 277 | 296 | ||
| 278 | db = QSqlDatabase.addDatabase('QPSQL') | 297 | db = QSqlDatabase.addDatabase('QPSQL') |
| 279 | query = QSqlQuery(db) | 298 | query = QSqlQuery(db) |
| @@ -394,7 +413,8 @@ if perf_db_export_calls: | |||
| 394 | 'call_id bigint,' | 413 | 'call_id bigint,' |
| 395 | 'return_id bigint,' | 414 | 'return_id bigint,' |
| 396 | 'parent_call_path_id bigint,' | 415 | 'parent_call_path_id bigint,' |
| 397 | 'flags integer)') | 416 | 'flags integer,' |
| 417 | 'parent_id bigint)') | ||
| 398 | 418 | ||
| 399 | do_query(query, 'CREATE VIEW machines_view AS ' | 419 | do_query(query, 'CREATE VIEW machines_view AS ' |
| 400 | 'SELECT ' | 420 | 'SELECT ' |
| @@ -478,8 +498,9 @@ if perf_db_export_calls: | |||
| 478 | 'branch_count,' | 498 | 'branch_count,' |
| 479 | 'call_id,' | 499 | 'call_id,' |
| 480 | 'return_id,' | 500 | 'return_id,' |
| 481 | 'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,' | 501 | 'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,' |
| 482 | 'parent_call_path_id' | 502 | 'parent_call_path_id,' |
| 503 | 'calls.parent_id' | ||
| 483 | ' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id') | 504 | ' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id') |
| 484 | 505 | ||
| 485 | do_query(query, 'CREATE VIEW samples_view AS ' | 506 | do_query(query, 'CREATE VIEW samples_view AS ' |
| @@ -504,12 +525,12 @@ do_query(query, 'CREATE VIEW samples_view AS ' | |||
| 504 | ' FROM samples') | 525 | ' FROM samples') |
| 505 | 526 | ||
| 506 | 527 | ||
| 507 | file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0) | 528 | file_header = struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0) |
| 508 | file_trailer = "\377\377" | 529 | file_trailer = b"\377\377" |
| 509 | 530 | ||
| 510 | def open_output_file(file_name): | 531 | def open_output_file(file_name): |
| 511 | path_name = output_dir_name + "/" + file_name | 532 | path_name = output_dir_name + "/" + file_name |
| 512 | file = open(path_name, "w+") | 533 | file = open(path_name, "wb+") |
| 513 | file.write(file_header) | 534 | file.write(file_header) |
| 514 | return file | 535 | return file |
| 515 | 536 | ||
| @@ -524,13 +545,13 @@ def copy_output_file_direct(file, table_name): | |||
| 524 | 545 | ||
| 525 | # Use COPY FROM STDIN because security may prevent postgres from accessing the files directly | 546 | # Use COPY FROM STDIN because security may prevent postgres from accessing the files directly |
| 526 | def copy_output_file(file, table_name): | 547 | def copy_output_file(file, table_name): |
| 527 | conn = PQconnectdb("dbname = " + dbname) | 548 | conn = PQconnectdb(toclientstr("dbname = " + dbname)) |
| 528 | if (PQstatus(conn)): | 549 | if (PQstatus(conn)): |
| 529 | raise Exception("COPY FROM STDIN PQconnectdb failed") | 550 | raise Exception("COPY FROM STDIN PQconnectdb failed") |
| 530 | file.write(file_trailer) | 551 | file.write(file_trailer) |
| 531 | file.seek(0) | 552 | file.seek(0) |
| 532 | sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" | 553 | sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" |
| 533 | res = PQexec(conn, sql) | 554 | res = PQexec(conn, toclientstr(sql)) |
| 534 | if (PQresultStatus(res) != 4): | 555 | if (PQresultStatus(res) != 4): |
| 535 | raise Exception("COPY FROM STDIN PQexec failed") | 556 | raise Exception("COPY FROM STDIN PQexec failed") |
| 536 | data = file.read(65536) | 557 | data = file.read(65536) |
| @@ -564,7 +585,7 @@ if perf_db_export_calls: | |||
| 564 | call_file = open_output_file("call_table.bin") | 585 | call_file = open_output_file("call_table.bin") |
| 565 | 586 | ||
| 566 | def trace_begin(): | 587 | def trace_begin(): |
| 567 | print datetime.datetime.today(), "Writing to intermediate files..." | 588 | printdate("Writing to intermediate files...") |
| 568 | # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs | 589 | # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs |
| 569 | evsel_table(0, "unknown") | 590 | evsel_table(0, "unknown") |
| 570 | machine_table(0, 0, "unknown") | 591 | machine_table(0, 0, "unknown") |
| @@ -575,11 +596,12 @@ def trace_begin(): | |||
| 575 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | 596 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |
| 576 | if perf_db_export_calls or perf_db_export_callchains: | 597 | if perf_db_export_calls or perf_db_export_callchains: |
| 577 | call_path_table(0, 0, 0, 0) | 598 | call_path_table(0, 0, 0, 0) |
| 599 | call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
| 578 | 600 | ||
| 579 | unhandled_count = 0 | 601 | unhandled_count = 0 |
| 580 | 602 | ||
| 581 | def trace_end(): | 603 | def trace_end(): |
| 582 | print datetime.datetime.today(), "Copying to database..." | 604 | printdate("Copying to database...") |
| 583 | copy_output_file(evsel_file, "selected_events") | 605 | copy_output_file(evsel_file, "selected_events") |
| 584 | copy_output_file(machine_file, "machines") | 606 | copy_output_file(machine_file, "machines") |
| 585 | copy_output_file(thread_file, "threads") | 607 | copy_output_file(thread_file, "threads") |
| @@ -594,7 +616,7 @@ def trace_end(): | |||
| 594 | if perf_db_export_calls: | 616 | if perf_db_export_calls: |
| 595 | copy_output_file(call_file, "calls") | 617 | copy_output_file(call_file, "calls") |
| 596 | 618 | ||
| 597 | print datetime.datetime.today(), "Removing intermediate files..." | 619 | printdate("Removing intermediate files...") |
| 598 | remove_output_file(evsel_file) | 620 | remove_output_file(evsel_file) |
| 599 | remove_output_file(machine_file) | 621 | remove_output_file(machine_file) |
| 600 | remove_output_file(thread_file) | 622 | remove_output_file(thread_file) |
| @@ -609,7 +631,7 @@ def trace_end(): | |||
| 609 | if perf_db_export_calls: | 631 | if perf_db_export_calls: |
| 610 | remove_output_file(call_file) | 632 | remove_output_file(call_file) |
| 611 | os.rmdir(output_dir_name) | 633 | os.rmdir(output_dir_name) |
| 612 | print datetime.datetime.today(), "Adding primary keys" | 634 | printdate("Adding primary keys") |
| 613 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') | 635 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') |
| 614 | do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') | 636 | do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') |
| 615 | do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') | 637 | do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') |
| @@ -624,7 +646,7 @@ def trace_end(): | |||
| 624 | if perf_db_export_calls: | 646 | if perf_db_export_calls: |
| 625 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') | 647 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') |
| 626 | 648 | ||
| 627 | print datetime.datetime.today(), "Adding foreign keys" | 649 | printdate("Adding foreign keys") |
| 628 | do_query(query, 'ALTER TABLE threads ' | 650 | do_query(query, 'ALTER TABLE threads ' |
| 629 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' | 651 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' |
| 630 | 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') | 652 | 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') |
| @@ -657,10 +679,11 @@ def trace_end(): | |||
| 657 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' | 679 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' |
| 658 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') | 680 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') |
| 659 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') | 681 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') |
| 682 | do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') | ||
| 660 | 683 | ||
| 661 | if (unhandled_count): | 684 | if (unhandled_count): |
| 662 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" | 685 | printdate("Warning: ", unhandled_count, " unhandled events") |
| 663 | print datetime.datetime.today(), "Done" | 686 | printdate("Done") |
| 664 | 687 | ||
| 665 | def trace_unhandled(event_name, context, event_fields_dict): | 688 | def trace_unhandled(event_name, context, event_fields_dict): |
| 666 | global unhandled_count | 689 | global unhandled_count |
| @@ -670,12 +693,14 @@ def sched__sched_switch(*x): | |||
| 670 | pass | 693 | pass |
| 671 | 694 | ||
| 672 | def evsel_table(evsel_id, evsel_name, *x): | 695 | def evsel_table(evsel_id, evsel_name, *x): |
| 696 | evsel_name = toserverstr(evsel_name) | ||
| 673 | n = len(evsel_name) | 697 | n = len(evsel_name) |
| 674 | fmt = "!hiqi" + str(n) + "s" | 698 | fmt = "!hiqi" + str(n) + "s" |
| 675 | value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) | 699 | value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) |
| 676 | evsel_file.write(value) | 700 | evsel_file.write(value) |
| 677 | 701 | ||
| 678 | def machine_table(machine_id, pid, root_dir, *x): | 702 | def machine_table(machine_id, pid, root_dir, *x): |
| 703 | root_dir = toserverstr(root_dir) | ||
| 679 | n = len(root_dir) | 704 | n = len(root_dir) |
| 680 | fmt = "!hiqiii" + str(n) + "s" | 705 | fmt = "!hiqiii" + str(n) + "s" |
| 681 | value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) | 706 | value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) |
| @@ -686,6 +711,7 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x): | |||
| 686 | thread_file.write(value) | 711 | thread_file.write(value) |
| 687 | 712 | ||
| 688 | def comm_table(comm_id, comm_str, *x): | 713 | def comm_table(comm_id, comm_str, *x): |
| 714 | comm_str = toserverstr(comm_str) | ||
| 689 | n = len(comm_str) | 715 | n = len(comm_str) |
| 690 | fmt = "!hiqi" + str(n) + "s" | 716 | fmt = "!hiqi" + str(n) + "s" |
| 691 | value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) | 717 | value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) |
| @@ -697,6 +723,9 @@ def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): | |||
| 697 | comm_thread_file.write(value) | 723 | comm_thread_file.write(value) |
| 698 | 724 | ||
| 699 | def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): | 725 | def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): |
| 726 | short_name = toserverstr(short_name) | ||
| 727 | long_name = toserverstr(long_name) | ||
| 728 | build_id = toserverstr(build_id) | ||
| 700 | n1 = len(short_name) | 729 | n1 = len(short_name) |
| 701 | n2 = len(long_name) | 730 | n2 = len(long_name) |
| 702 | n3 = len(build_id) | 731 | n3 = len(build_id) |
| @@ -705,12 +734,14 @@ def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): | |||
| 705 | dso_file.write(value) | 734 | dso_file.write(value) |
| 706 | 735 | ||
| 707 | def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x): | 736 | def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x): |
| 737 | symbol_name = toserverstr(symbol_name) | ||
| 708 | n = len(symbol_name) | 738 | n = len(symbol_name) |
| 709 | fmt = "!hiqiqiqiqiii" + str(n) + "s" | 739 | fmt = "!hiqiqiqiqiii" + str(n) + "s" |
| 710 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) | 740 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) |
| 711 | symbol_file.write(value) | 741 | symbol_file.write(value) |
| 712 | 742 | ||
| 713 | def branch_type_table(branch_type, name, *x): | 743 | def branch_type_table(branch_type, name, *x): |
| 744 | name = toserverstr(name) | ||
| 714 | n = len(name) | 745 | n = len(name) |
| 715 | fmt = "!hiii" + str(n) + "s" | 746 | fmt = "!hiii" + str(n) + "s" |
| 716 | value = struct.pack(fmt, 2, 4, branch_type, n, name) | 747 | value = struct.pack(fmt, 2, 4, branch_type, n, name) |
| @@ -728,7 +759,7 @@ def call_path_table(cp_id, parent_id, symbol_id, ip, *x): | |||
| 728 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) | 759 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) |
| 729 | call_path_file.write(value) | 760 | call_path_file.write(value) |
| 730 | 761 | ||
| 731 | 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): | 762 | 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, parent_id, *x): |
| 732 | fmt = "!hiqiqiqiqiqiqiqiqiqiqii" | 763 | fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiq" |
| 733 | 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) | 764 | value = struct.pack(fmt, 12, 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, 8, parent_id) |
| 734 | call_file.write(value) | 765 | call_file.write(value) |
