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

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>

authored by

Adrian Hunter and committed by
Arnaldo Carvalho de Melo
6a70307d 88f50d60

+158 -7
+10 -5
tools/perf/scripts/python/bin/export-to-postgresql-report
··· 1 1 #!/bin/bash 2 2 # description: export perf data to a postgresql database 3 - # args: [database name] [columns] 3 + # args: [database name] [columns] [calls] 4 4 n_args=0 5 5 for i in "$@" 6 6 do ··· 9 9 fi 10 10 n_args=$(( $n_args + 1 )) 11 11 done 12 - if [ "$n_args" -gt 2 ] ; then 13 - echo "usage: export-to-postgresql-report [database name] [columns]" 12 + if [ "$n_args" -gt 3 ] ; then 13 + echo "usage: export-to-postgresql-report [database name] [columns] [calls]" 14 14 exit 15 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 22 dbname=$1 18 23 columns=$2 19 24 shift 2 ··· 26 21 dbname=$1 27 22 shift 28 23 fi 29 - perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns 24 + perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
+65 -1
tools/perf/scripts/python/export-to-postgresql.py
··· 40 40 #from Core import * 41 41 42 42 perf_db_export_mode = True 43 + perf_db_export_calls = False 43 44 44 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 47 print >> sys.stderr, "where: columns 'all' or 'branches'" 48 + print >> sys.stderr, " calls 'calls' => create calls table" 47 49 raise Exception("Too few arguments") 48 50 49 51 if (len(sys.argv) < 2): ··· 62 60 usage() 63 61 64 62 branches = (columns == "branches") 63 + 64 + if (len(sys.argv) >= 4): 65 + if (sys.argv[3] == "calls"): 66 + perf_db_export_calls = True 67 + else: 68 + usage() 65 69 66 70 output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" 67 71 os.mkdir(output_dir_name) ··· 178 170 'branch_type integer,' 179 171 'in_tx boolean)') 180 172 173 + if perf_db_export_calls: 174 + do_query(query, 'CREATE TABLE call_paths (' 175 + 'id bigint NOT NULL,' 176 + 'parent_id bigint,' 177 + 'symbol_id bigint,' 178 + 'ip bigint)') 179 + do_query(query, 'CREATE TABLE calls (' 180 + 'id bigint NOT NULL,' 181 + 'thread_id bigint,' 182 + 'comm_id bigint,' 183 + 'call_path_id bigint,' 184 + 'call_time bigint,' 185 + 'return_time bigint,' 186 + 'branch_count bigint,' 187 + 'call_id bigint,' 188 + 'return_id bigint,' 189 + 'parent_call_path_id bigint,' 190 + 'flags integer)') 191 + 181 192 do_query(query, 'CREATE VIEW samples_view AS ' 182 193 'SELECT ' 183 194 'id,' ··· 273 246 symbol_file = open_output_file("symbol_table.bin") 274 247 branch_type_file = open_output_file("branch_type_table.bin") 275 248 sample_file = open_output_file("sample_table.bin") 249 + if perf_db_export_calls: 250 + call_path_file = open_output_file("call_path_table.bin") 251 + call_file = open_output_file("call_table.bin") 276 252 277 253 def trace_begin(): 278 254 print datetime.datetime.today(), "Writing to intermediate files..." ··· 286 256 comm_table(0, "unknown") 287 257 dso_table(0, 0, "unknown", "unknown", "") 288 258 symbol_table(0, 0, 0, 0, 0, "unknown") 259 + sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 260 + if perf_db_export_calls: 261 + call_path_table(0, 0, 0, 0) 289 262 290 263 unhandled_count = 0 291 264 ··· 303 270 copy_output_file(symbol_file, "symbols") 304 271 copy_output_file(branch_type_file, "branch_types") 305 272 copy_output_file(sample_file, "samples") 273 + if perf_db_export_calls: 274 + copy_output_file(call_path_file, "call_paths") 275 + copy_output_file(call_file, "calls") 306 276 307 277 print datetime.datetime.today(), "Removing intermediate files..." 308 278 remove_output_file(evsel_file) ··· 317 281 remove_output_file(symbol_file) 318 282 remove_output_file(branch_type_file) 319 283 remove_output_file(sample_file) 284 + if perf_db_export_calls: 285 + remove_output_file(call_path_file) 286 + remove_output_file(call_file) 320 287 os.rmdir(output_dir_name) 321 288 print datetime.datetime.today(), "Adding primary keys" 322 289 do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') ··· 331 292 do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') 332 293 do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') 333 294 do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') 295 + if perf_db_export_calls: 296 + do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') 297 + do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') 334 298 335 299 print datetime.datetime.today(), "Adding foreign keys" 336 300 do_query(query, 'ALTER TABLE threads ' ··· 355 313 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' 356 314 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' 357 315 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') 316 + if perf_db_export_calls: 317 + do_query(query, 'ALTER TABLE call_paths ' 318 + 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' 319 + 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') 320 + do_query(query, 'ALTER TABLE calls ' 321 + 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' 322 + 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' 323 + 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' 324 + 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' 325 + 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' 326 + 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') 327 + do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') 358 328 359 329 if (unhandled_count): 360 330 print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" ··· 432 378 else: 433 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) 434 380 sample_file.write(value) 381 + 382 + def call_path_table(cp_id, parent_id, symbol_id, ip, *x): 383 + fmt = "!hiqiqiqiq" 384 + value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) 385 + call_path_file.write(value) 386 + 387 + 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): 388 + fmt = "!hiqiqiqiqiqiqiqiqiqiqii" 389 + 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) 390 + call_file.write(value)
+83 -1
tools/perf/util/scripting-engines/trace-event-python.c
··· 37 37 #include "../comm.h" 38 38 #include "../machine.h" 39 39 #include "../db-export.h" 40 + #include "../thread-stack.h" 40 41 #include "../trace-event.h" 41 42 #include "../machine.h" 42 43 ··· 69 68 PyObject *symbol_handler; 70 69 PyObject *branch_type_handler; 71 70 PyObject *sample_handler; 71 + PyObject *call_path_handler; 72 + PyObject *call_return_handler; 72 73 bool db_export_mode; 73 74 }; 74 75 ··· 723 720 return 0; 724 721 } 725 722 723 + static int python_export_call_path(struct db_export *dbe, struct call_path *cp) 724 + { 725 + struct tables *tables = container_of(dbe, struct tables, dbe); 726 + PyObject *t; 727 + u64 parent_db_id, sym_db_id; 728 + 729 + parent_db_id = cp->parent ? cp->parent->db_id : 0; 730 + sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; 731 + 732 + t = tuple_new(4); 733 + 734 + tuple_set_u64(t, 0, cp->db_id); 735 + tuple_set_u64(t, 1, parent_db_id); 736 + tuple_set_u64(t, 2, sym_db_id); 737 + tuple_set_u64(t, 3, cp->ip); 738 + 739 + call_object(tables->call_path_handler, t, "call_path_table"); 740 + 741 + Py_DECREF(t); 742 + 743 + return 0; 744 + } 745 + 746 + static int python_export_call_return(struct db_export *dbe, 747 + struct call_return *cr) 748 + { 749 + struct tables *tables = container_of(dbe, struct tables, dbe); 750 + u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; 751 + PyObject *t; 752 + 753 + t = tuple_new(11); 754 + 755 + tuple_set_u64(t, 0, cr->db_id); 756 + tuple_set_u64(t, 1, cr->thread->db_id); 757 + tuple_set_u64(t, 2, comm_db_id); 758 + tuple_set_u64(t, 3, cr->cp->db_id); 759 + tuple_set_u64(t, 4, cr->call_time); 760 + tuple_set_u64(t, 5, cr->return_time); 761 + tuple_set_u64(t, 6, cr->branch_count); 762 + tuple_set_u64(t, 7, cr->call_ref); 763 + tuple_set_u64(t, 8, cr->return_ref); 764 + tuple_set_u64(t, 9, cr->cp->parent->db_id); 765 + tuple_set_s32(t, 10, cr->flags); 766 + 767 + call_object(tables->call_return_handler, t, "call_return_table"); 768 + 769 + Py_DECREF(t); 770 + 771 + return 0; 772 + } 773 + 774 + static int python_process_call_return(struct call_return *cr, void *data) 775 + { 776 + struct db_export *dbe = data; 777 + 778 + return db_export__call_return(dbe, cr); 779 + } 780 + 726 781 static void python_process_general_event(struct perf_sample *sample, 727 782 struct perf_evsel *evsel, 728 783 struct thread *thread, ··· 913 852 static void set_table_handlers(struct tables *tables) 914 853 { 915 854 const char *perf_db_export_mode = "perf_db_export_mode"; 916 - PyObject *db_export_mode; 855 + const char *perf_db_export_calls = "perf_db_export_calls"; 856 + PyObject *db_export_mode, *db_export_calls; 857 + bool export_calls = false; 917 858 int ret; 918 859 919 860 memset(tables, 0, sizeof(struct tables)); ··· 932 869 if (!ret) 933 870 return; 934 871 872 + tables->dbe.crp = NULL; 873 + db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); 874 + if (db_export_calls) { 875 + ret = PyObject_IsTrue(db_export_calls); 876 + if (ret == -1) 877 + handler_call_die(perf_db_export_calls); 878 + export_calls = !!ret; 879 + } 880 + 881 + if (export_calls) { 882 + tables->dbe.crp = 883 + call_return_processor__new(python_process_call_return, 884 + &tables->dbe); 885 + if (!tables->dbe.crp) 886 + Py_FatalError("failed to create calls processor"); 887 + } 888 + 935 889 tables->db_export_mode = true; 936 890 /* 937 891 * Reserve per symbol space for symbol->db_id via symbol__priv() ··· 964 884 SET_TABLE_HANDLER(symbol); 965 885 SET_TABLE_HANDLER(branch_type); 966 886 SET_TABLE_HANDLER(sample); 887 + SET_TABLE_HANDLER(call_path); 888 + SET_TABLE_HANDLER(call_return); 967 889 } 968 890 969 891 /*