From a5563edfa1bd25d052d81f5ad7fe74ba71c3d44e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 31 Jul 2014 09:01:01 +0300 Subject: perf script python: Add helpers for calling Python objects The Python script API repeatedly uses the same lines of code to get and call objects. Make that into helper functions instead. A side-effect is that some reference counting bugs disappear because the new call_object() function always decrements the reference count of 'retval'. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1406786474-9306-19-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../util/scripting-engines/trace-event-python.c | 114 +++++++++------------ 1 file changed, 47 insertions(+), 67 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cbce2545da45..26e5f14239ed 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -73,6 +73,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj Py_DECREF(val); } +static PyObject *get_handler(const char *handler_name) +{ + PyObject *handler; + + handler = PyDict_GetItemString(main_dict, handler_name); + if (handler && !PyCallable_Check(handler)) + return NULL; + return handler; +} + +static void call_object(PyObject *handler, PyObject *args, const char *die_msg) +{ + PyObject *retval; + + retval = PyObject_CallObject(handler, args); + if (retval == NULL) + handler_call_die(die_msg); + Py_DECREF(retval); +} + +static void try_call_object(const char *handler_name, PyObject *args) +{ + PyObject *handler; + + handler = get_handler(handler_name); + if (handler) + call_object(handler, args, handler_name); +} + static void define_value(enum print_arg_type field_type, const char *ev_name, const char *field_name, @@ -80,7 +109,7 @@ static void define_value(enum print_arg_type field_type, const char *field_str) { const char *handler_name = "define_flag_value"; - PyObject *handler, *t, *retval; + PyObject *t; unsigned long long value; unsigned n = 0; @@ -98,13 +127,7 @@ static void define_value(enum print_arg_type field_type, PyTuple_SetItem(t, n++, PyInt_FromLong(value)); PyTuple_SetItem(t, n++, PyString_FromString(field_str)); - handler = PyDict_GetItemString(main_dict, handler_name); - if (handler && PyCallable_Check(handler)) { - retval = PyObject_CallObject(handler, t); - if (retval == NULL) - handler_call_die(handler_name); - Py_DECREF(retval); - } + try_call_object(handler_name, t); Py_DECREF(t); } @@ -127,7 +150,7 @@ static void define_field(enum print_arg_type field_type, const char *delim) { const char *handler_name = "define_flag_field"; - PyObject *handler, *t, *retval; + PyObject *t; unsigned n = 0; if (field_type == PRINT_SYMBOL) @@ -145,13 +168,7 @@ static void define_field(enum print_arg_type field_type, if (field_type == PRINT_FLAGS) PyTuple_SetItem(t, n++, PyString_FromString(delim)); - handler = PyDict_GetItemString(main_dict, handler_name); - if (handler && PyCallable_Check(handler)) { - retval = PyObject_CallObject(handler, t); - if (retval == NULL) - handler_call_die(handler_name); - Py_DECREF(retval); - } + try_call_object(handler_name, t); Py_DECREF(t); } @@ -362,7 +379,7 @@ static void python_process_tracepoint(struct perf_sample *sample, struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *context, *t, *obj, *callchain; + PyObject *handler, *context, *t, *obj, *callchain; PyObject *dict = NULL; static char handler_name[256]; struct format_field *field; @@ -387,9 +404,7 @@ static void python_process_tracepoint(struct perf_sample *sample, sprintf(handler_name, "%s__%s", event->system, event->name); - handler = PyDict_GetItemString(main_dict, handler_name); - if (handler && !PyCallable_Check(handler)) - handler = NULL; + handler = get_handler(handler_name); if (!handler) { dict = PyDict_New(); if (!dict) @@ -450,19 +465,9 @@ static void python_process_tracepoint(struct perf_sample *sample, Py_FatalError("error resizing Python tuple"); if (handler) { - retval = PyObject_CallObject(handler, t); - if (retval == NULL) - handler_call_die(handler_name); - Py_DECREF(retval); + call_object(handler, t, handler_name); } else { - handler = PyDict_GetItemString(main_dict, "trace_unhandled"); - if (handler && PyCallable_Check(handler)) { - - retval = PyObject_CallObject(handler, t); - if (retval == NULL) - handler_call_die("trace_unhandled"); - Py_DECREF(retval); - } + try_call_object("trace_unhandled", t); Py_DECREF(dict); } @@ -474,7 +479,7 @@ static void python_process_general_event(struct perf_sample *sample, struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; + PyObject *handler, *t, *dict, *callchain, *dict_sample; static char handler_name[64]; unsigned n = 0; @@ -496,8 +501,8 @@ static void python_process_general_event(struct perf_sample *sample, snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); - handler = PyDict_GetItemString(main_dict, handler_name); - if (!handler || !PyCallable_Check(handler)) + handler = get_handler(handler_name); + if (!handler) goto exit; pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); @@ -539,10 +544,7 @@ static void python_process_general_event(struct perf_sample *sample, if (_PyTuple_Resize(&t, n) == -1) Py_FatalError("error resizing Python tuple"); - retval = PyObject_CallObject(handler, t); - if (retval == NULL) - handler_call_die(handler_name); - Py_DECREF(retval); + call_object(handler, t, handler_name); exit: Py_DECREF(dict); Py_DECREF(t); @@ -566,36 +568,24 @@ static void python_process_event(union perf_event *event __maybe_unused, static int run_start_sub(void) { - PyObject *handler, *retval; - int err = 0; - main_module = PyImport_AddModule("__main__"); if (main_module == NULL) return -1; Py_INCREF(main_module); main_dict = PyModule_GetDict(main_module); - if (main_dict == NULL) { - err = -1; + if (main_dict == NULL) goto error; - } Py_INCREF(main_dict); - handler = PyDict_GetItemString(main_dict, "trace_begin"); - if (handler == NULL || !PyCallable_Check(handler)) - goto out; + try_call_object("trace_begin", NULL); - retval = PyObject_CallObject(handler, NULL); - if (retval == NULL) - handler_call_die("trace_begin"); + return 0; - Py_DECREF(retval); - return err; error: Py_XDECREF(main_dict); Py_XDECREF(main_module); -out: - return err; + return -1; } /* @@ -654,23 +644,13 @@ error: */ static int python_stop_script(void) { - PyObject *handler, *retval; - int err = 0; + try_call_object("trace_end", NULL); - handler = PyDict_GetItemString(main_dict, "trace_end"); - if (handler == NULL || !PyCallable_Check(handler)) - goto out; - - retval = PyObject_CallObject(handler, NULL); - if (retval == NULL) - handler_call_die("trace_end"); - Py_DECREF(retval); -out: Py_XDECREF(main_dict); Py_XDECREF(main_module); Py_Finalize(); - return err; + return 0; } static int python_generate_script(struct pevent *pevent, const char *outfile) -- cgit From d445dd2a78eed884adf3b3426b078fe69d2516d8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 15 Aug 2014 22:08:37 +0300 Subject: perf scripting: Add 'flush' callback to scripting API In order to defer some output via the scripting API, there needs to be a callback after session processing but before the session is deleted. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1408129739-17368-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 13 +++++++++++++ tools/perf/util/scripting-engines/trace-event-perl.c | 6 ++++++ tools/perf/util/scripting-engines/trace-event-python.c | 6 ++++++ tools/perf/util/trace-event-scripting.c | 7 +++++++ tools/perf/util/trace-event.h | 1 + 5 files changed, 33 insertions(+) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c1b7029884b1..02dce9295e2c 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -485,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused, return 0; } +static int default_flush_script(void) +{ + return 0; +} + static int default_stop_script(void) { return 0; @@ -498,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused, static struct scripting_ops default_scripting_ops = { .start_script = default_start_script, + .flush_script = default_flush_script, .stop_script = default_stop_script, .process_event = process_event, .generate_script = default_generate_script, @@ -513,6 +519,11 @@ static void setup_scripting(void) scripting_ops = &default_scripting_ops; } +static int flush_scripting(void) +{ + return scripting_ops->flush_script(); +} + static int cleanup_scripting(void) { pr_debug("\nperf script stopped\n"); @@ -1813,6 +1824,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) err = __cmd_script(&script); + flush_scripting(); + out_delete: perf_session__delete(session); diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b2dba9c0a3a1..0a01bac4ce02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -432,6 +432,11 @@ error: return err; } +static int perl_flush_script(void) +{ + return 0; +} + /* * Stop trace script */ @@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) struct scripting_ops perl_scripting_ops = { .name = "Perl", .start_script = perl_start_script, + .flush_script = perl_flush_script, .stop_script = perl_stop_script, .process_event = perl_process_event, .generate_script = perl_generate_script, diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 26e5f14239ed..56ba07cce549 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -639,6 +639,11 @@ error: return err; } +static int python_flush_script(void) +{ + return 0; +} + /* * Stop trace script */ @@ -823,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) struct scripting_ops python_scripting_ops = { .name = "Python", .start_script = python_start_script, + .flush_script = python_flush_script, .stop_script = python_stop_script, .process_event = python_process_event, .generate_script = python_generate_script, diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 57aaccc1692e..5c9bdd1591a9 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -30,6 +30,11 @@ struct scripting_context *scripting_context; +static int flush_script_unsupported(void) +{ + return 0; +} + static int stop_script_unsupported(void) { return 0; @@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent struct scripting_ops python_scripting_unsupported_ops = { .name = "Python", .start_script = python_start_script_unsupported, + .flush_script = flush_script_unsupported, .stop_script = stop_script_unsupported, .process_event = process_event_unsupported, .generate_script = python_generate_script_unsupported, @@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent struct scripting_ops perl_scripting_unsupported_ops = { .name = "Perl", .start_script = perl_start_script_unsupported, + .flush_script = flush_script_unsupported, .stop_script = stop_script_unsupported, .process_event = process_event_unsupported, .generate_script = perl_generate_script_unsupported, diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 7b6d68688327..52aaa19e1eb1 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -64,6 +64,7 @@ struct perf_session; struct scripting_ops { const char *name; int (*start_script) (const char *script, int argc, const char **argv); + int (*flush_script) (void); int (*stop_script) (void); void (*process_event) (union perf_event *event, struct perf_sample *sample, -- cgit From 8f651eae186f4dfb1740988623c83ba03dcf3a76 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 9 Oct 2014 16:12:24 -0300 Subject: perf callchain: Move the callchain_param extern to callchain.h It was lost in hist.h, move it to where it belongs, callchain.h, as there are places that gets hist.h by means of evsel.h, and since evsel.h is being untangled from hist.h... Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jean Pihet Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-0rg7ji1jnbm6q6gj35j37jby@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 1 + tools/perf/util/callchain.h | 2 ++ tools/perf/util/evsel.c | 1 + tools/perf/util/hist.h | 2 -- tools/perf/util/scripting-engines/trace-event-python.c | 1 + 5 files changed, 5 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 44c6f3d55ce7..a6b2132c666f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -14,6 +14,7 @@ #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/callchain.h" #include "util/header.h" #include "util/event.h" #include "util/evlist.h" diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2a1f5a46543a..94cfefddf4db 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -65,6 +65,8 @@ struct callchain_param { enum chain_key key; }; +extern struct callchain_param callchain_param; + struct callchain_list { u64 ip; struct map_symbol ms; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0580b13df2e6..a08376427448 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -15,6 +15,7 @@ #include #include #include "asm/bug.h" +#include "callchain.h" #include "evsel.h" #include "evlist.h" #include "util.h" diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a039cd22b23c..ebfc25886cac 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -8,8 +8,6 @@ #include "color.h" #include "ui/progress.h" -extern struct callchain_param callchain_param; - struct hist_entry; struct addr_location; struct symbol; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 56ba07cce549..496f21cadd97 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -28,6 +28,7 @@ #include "../../perf.h" #include "../debug.h" +#include "../callchain.h" #include "../evsel.h" #include "../util.h" #include "../event.h" -- cgit From cc8b7c2bf553151a579a8009020875faa1d43e29 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 23 Oct 2014 15:26:17 -0300 Subject: perf thread: Adopt resolve_callchain method from machine Shortening function signature lenght too, since a thread's machine can be obtained from thread->mg->machine, no need to pass thread, machine. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jean Pihet Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-5wb6css280ty0cel5p0zo2b1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 4 ++-- tools/perf/util/machine.c | 15 +++++++-------- tools/perf/util/machine.h | 13 ++++++------- tools/perf/util/scripting-engines/trace-event-python.c | 6 +++--- tools/perf/util/session.c | 6 +++--- 5 files changed, 21 insertions(+), 23 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index c84d3f8dcb75..00229809a904 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -754,8 +754,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || sort__has_parent) { - return machine__resolve_callchain(al->machine, evsel, al->thread, - sample, parent, al, max_stack); + return thread__resolve_callchain(al->thread, evsel, sample, + parent, al, max_stack); } return 0; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 08e63fdbd14f..fd192e4885cc 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1464,13 +1464,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) entry->map, entry->sym); } -int machine__resolve_callchain(struct machine *machine, - struct perf_evsel *evsel, - struct thread *thread, - struct perf_sample *sample, - struct symbol **parent, - struct addr_location *root_al, - int max_stack) +int thread__resolve_callchain(struct thread *thread, + struct perf_evsel *evsel, + struct perf_sample *sample, + struct symbol **parent, + struct addr_location *root_al, + int max_stack) { int ret = thread__resolve_callchain_sample(thread, sample->callchain, parent, root_al, max_stack); @@ -1487,7 +1486,7 @@ int machine__resolve_callchain(struct machine *machine, (!sample->user_stack.size)) return 0; - return unwind__get_entries(unwind_entry, &callchain_cursor, machine, + return unwind__get_entries(unwind_entry, &callchain_cursor, thread->mg->machine, thread, sample, max_stack); } diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2b651a7f5d0d..88ec74e18cbf 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -122,13 +122,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, struct addr_location *al); struct mem_info *sample__resolve_mem(struct perf_sample *sample, struct addr_location *al); -int machine__resolve_callchain(struct machine *machine, - struct perf_evsel *evsel, - struct thread *thread, - struct perf_sample *sample, - struct symbol **parent, - struct addr_location *root_al, - int max_stack); +int thread__resolve_callchain(struct thread *thread, + struct perf_evsel *evsel, + struct perf_sample *sample, + struct symbol **parent, + struct addr_location *root_al, + int max_stack); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 496f21cadd97..25e5a238f1cb 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -312,9 +312,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample, if (!symbol_conf.use_callchain || !sample->callchain) goto exit; - if (machine__resolve_callchain(al->machine, evsel, al->thread, - sample, NULL, NULL, - PERF_MAX_STACK_DEPTH) != 0) { + if (thread__resolve_callchain(al->thread, evsel, + sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) != 0) { pr_err("Failed to resolve callchain. Skipping\n"); goto exit; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 6702ac28754b..776010844cdc 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1417,9 +1417,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, if (symbol_conf.use_callchain && sample->callchain) { struct addr_location node_al; - if (machine__resolve_callchain(al->machine, evsel, al->thread, - sample, NULL, NULL, - PERF_MAX_STACK_DEPTH) != 0) { + if (thread__resolve_callchain(al->thread, evsel, + sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) != 0) { if (verbose) error("Failed to resolve callchain. Skipping\n"); return; -- cgit From df919b400ad3f9e6aac392ce421d710207abf9be Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 23 Oct 2014 13:45:14 +0300 Subject: perf scripting python: Extend interface to export data in a database-friendly way Use the new db_export facility to export data in a database-friendly way. A Python script selects the db_export mode by setting a global variable 'perf_db_export_mode' to True. The script then optionally implements functions to receive table rows. The functions are: evsel_table machine_table thread_table comm_table dso_table symbol_table sample_table An example script is provided in a subsequent patch. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1414061124-26830-7-git-send-email-adrian.hunter@intel.com [ Reserve space for per symbol db_id space when perf_db_export_mode is on ] Signed-off-by: Arnaldo Carvalho de Melo --- .../util/scripting-engines/trace-event-python.c | 286 ++++++++++++++++++++- 1 file changed, 284 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 25e5a238f1cb..2fd7ee8f18c7 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "../../perf.h" @@ -33,6 +34,9 @@ #include "../util.h" #include "../event.h" #include "../thread.h" +#include "../comm.h" +#include "../machine.h" +#include "../db-export.h" #include "../trace-event.h" #include "../machine.h" @@ -53,6 +57,21 @@ static int zero_flag_atom; static PyObject *main_module, *main_dict; +struct tables { + struct db_export dbe; + PyObject *evsel_handler; + PyObject *machine_handler; + PyObject *thread_handler; + PyObject *comm_handler; + PyObject *comm_thread_handler; + PyObject *dso_handler; + PyObject *symbol_handler; + PyObject *sample_handler; + bool db_export_mode; +}; + +static struct tables tables_global; + static void handler_call_die(const char *handler_name) NORETURN; static void handler_call_die(const char *handler_name) { @@ -475,6 +494,211 @@ static void python_process_tracepoint(struct perf_sample *sample, Py_DECREF(t); } +static PyObject *tuple_new(unsigned int sz) +{ + PyObject *t; + + t = PyTuple_New(sz); + if (!t) + Py_FatalError("couldn't create Python tuple"); + return t; +} + +static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val) +{ +#if BITS_PER_LONG == 64 + return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); +#endif +#if BITS_PER_LONG == 32 + return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val)); +#endif +} + +static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val) +{ + return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); +} + +static int tuple_set_string(PyObject *t, unsigned int pos, const char *s) +{ + return PyTuple_SetItem(t, pos, PyString_FromString(s)); +} + +static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(2); + + tuple_set_u64(t, 0, evsel->db_id); + tuple_set_string(t, 1, perf_evsel__name(evsel)); + + call_object(tables->evsel_handler, t, "evsel_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_machine(struct db_export *dbe, + struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(3); + + tuple_set_u64(t, 0, machine->db_id); + tuple_set_s32(t, 1, machine->pid); + tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : ""); + + call_object(tables->machine_handler, t, "machine_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_thread(struct db_export *dbe, struct thread *thread, + u64 main_thread_db_id, struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(5); + + tuple_set_u64(t, 0, thread->db_id); + tuple_set_u64(t, 1, machine->db_id); + tuple_set_u64(t, 2, main_thread_db_id); + tuple_set_s32(t, 3, thread->pid_); + tuple_set_s32(t, 4, thread->tid); + + call_object(tables->thread_handler, t, "thread_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_comm(struct db_export *dbe, struct comm *comm) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(2); + + tuple_set_u64(t, 0, comm->db_id); + tuple_set_string(t, 1, comm__str(comm)); + + call_object(tables->comm_handler, t, "comm_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_comm_thread(struct db_export *dbe, u64 db_id, + struct comm *comm, struct thread *thread) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(3); + + tuple_set_u64(t, 0, db_id); + tuple_set_u64(t, 1, comm->db_id); + tuple_set_u64(t, 2, thread->db_id); + + call_object(tables->comm_thread_handler, t, "comm_thread_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_dso(struct db_export *dbe, struct dso *dso, + struct machine *machine) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + PyObject *t; + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + + t = tuple_new(5); + + tuple_set_u64(t, 0, dso->db_id); + tuple_set_u64(t, 1, machine->db_id); + tuple_set_string(t, 2, dso->short_name); + tuple_set_string(t, 3, dso->long_name); + tuple_set_string(t, 4, sbuild_id); + + call_object(tables->dso_handler, t, "dso_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_symbol(struct db_export *dbe, struct symbol *sym, + struct dso *dso) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + u64 *sym_db_id = symbol__priv(sym); + PyObject *t; + + t = tuple_new(6); + + tuple_set_u64(t, 0, *sym_db_id); + tuple_set_u64(t, 1, dso->db_id); + tuple_set_u64(t, 2, sym->start); + tuple_set_u64(t, 3, sym->end); + tuple_set_s32(t, 4, sym->binding); + tuple_set_string(t, 5, sym->name); + + call_object(tables->symbol_handler, t, "symbol_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_sample(struct db_export *dbe, + struct export_sample *es) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(19); + + tuple_set_u64(t, 0, es->db_id); + tuple_set_u64(t, 1, es->evsel->db_id); + tuple_set_u64(t, 2, es->al->machine->db_id); + tuple_set_u64(t, 3, es->thread->db_id); + tuple_set_u64(t, 4, es->comm_db_id); + tuple_set_u64(t, 5, es->dso_db_id); + tuple_set_u64(t, 6, es->sym_db_id); + tuple_set_u64(t, 7, es->offset); + tuple_set_u64(t, 8, es->sample->ip); + tuple_set_u64(t, 9, es->sample->time); + tuple_set_s32(t, 10, es->sample->cpu); + tuple_set_u64(t, 11, es->addr_dso_db_id); + tuple_set_u64(t, 12, es->addr_sym_db_id); + tuple_set_u64(t, 13, es->addr_offset); + tuple_set_u64(t, 14, es->sample->addr); + tuple_set_u64(t, 15, es->sample->period); + tuple_set_u64(t, 16, es->sample->weight); + tuple_set_u64(t, 17, es->sample->transaction); + tuple_set_u64(t, 18, es->sample->data_src); + + call_object(tables->sample_handler, t, "sample_table"); + + Py_DECREF(t); + + return 0; +} + static void python_process_general_event(struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread, @@ -551,19 +775,25 @@ exit: Py_DECREF(t); } -static void python_process_event(union perf_event *event __maybe_unused, +static void python_process_event(union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread, struct addr_location *al) { + struct tables *tables = &tables_global; + switch (evsel->attr.type) { case PERF_TYPE_TRACEPOINT: python_process_tracepoint(sample, evsel, thread, al); break; /* Reserve for future process_hw/sw/raw APIs */ default: - python_process_general_event(sample, evsel, thread, al); + if (tables->db_export_mode) + db_export__sample(&tables->dbe, event, sample, evsel, + thread, al); + else + python_process_general_event(sample, evsel, thread, al); } } @@ -589,11 +819,57 @@ error: return -1; } +#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \ + tables->handler_name = get_handler(#table_name); \ + if (tables->handler_name) \ + tables->dbe.export_ ## name = python_export_ ## name; \ +} while (0) + +#define SET_TABLE_HANDLER(name) \ + SET_TABLE_HANDLER_(name, name ## _handler, name ## _table) + +static void set_table_handlers(struct tables *tables) +{ + const char *perf_db_export_mode = "perf_db_export_mode"; + PyObject *db_export_mode; + int ret; + + memset(tables, 0, sizeof(struct tables)); + if (db_export__init(&tables->dbe)) + Py_FatalError("failed to initialize export"); + + db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode); + if (!db_export_mode) + return; + + ret = PyObject_IsTrue(db_export_mode); + if (ret == -1) + handler_call_die(perf_db_export_mode); + if (!ret) + return; + + tables->db_export_mode = true; + /* + * Reserve per symbol space for symbol->db_id via symbol__priv() + */ + symbol_conf.priv_size = sizeof(u64); + + SET_TABLE_HANDLER(evsel); + SET_TABLE_HANDLER(machine); + SET_TABLE_HANDLER(thread); + SET_TABLE_HANDLER(comm); + SET_TABLE_HANDLER(comm_thread); + SET_TABLE_HANDLER(dso); + SET_TABLE_HANDLER(symbol); + SET_TABLE_HANDLER(sample); +} + /* * Start trace script */ static int python_start_script(const char *script, int argc, const char **argv) { + struct tables *tables = &tables_global; const char **command_line; char buf[PATH_MAX]; int i, err = 0; @@ -632,6 +908,8 @@ static int python_start_script(const char *script, int argc, const char **argv) free(command_line); + set_table_handlers(tables); + return err; error: Py_Finalize(); @@ -650,8 +928,12 @@ static int python_flush_script(void) */ static int python_stop_script(void) { + struct tables *tables = &tables_global; + try_call_object("trace_end", NULL); + db_export__exit(&tables->dbe); + Py_XDECREF(main_dict); Py_XDECREF(main_module); Py_Finalize(); -- cgit From c29414f5cfd641d956c5287848fdd8f25bb2afa3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 30 Oct 2014 16:09:44 +0200 Subject: perf tools: Add branch_type and in_tx to Python export Add branch_type and in_tx to Python db export and the export-to-postgresql.py script. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1414678188-14946-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-postgresql.py | 32 ++++++++++++++++++---- .../util/scripting-engines/trace-event-python.c | 30 +++++++++++++++++++- 2 files changed, 55 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index d8f6df0093d6..bb79aecccf58 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py @@ -123,6 +123,10 @@ do_query(query, 'CREATE TABLE symbols (' 'sym_end bigint,' 'binding integer,' 'name varchar(2048))') +do_query(query, 'CREATE TABLE branch_types (' + 'id integer NOT NULL,' + 'name varchar(80))') + if branches: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,' @@ -139,7 +143,9 @@ if branches: 'to_dso_id bigint,' 'to_symbol_id bigint,' 'to_sym_offset bigint,' - 'to_ip bigint)') + 'to_ip bigint,' + 'branch_type integer,' + 'in_tx boolean)') else: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,' @@ -160,7 +166,9 @@ else: 'period bigint,' 'weight bigint,' 'transaction bigint,' - 'data_src bigint)') + 'data_src bigint,' + 'branch_type integer,' + 'in_tx boolean)') do_query(query, 'CREATE VIEW samples_view AS ' 'SELECT ' @@ -178,7 +186,9 @@ do_query(query, 'CREATE VIEW samples_view AS ' 'to_hex(to_ip) AS to_ip_hex,' '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' 'to_sym_offset,' - '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name' + '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' + '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' + 'in_tx' ' FROM samples') @@ -234,6 +244,7 @@ comm_file = open_output_file("comm_table.bin") comm_thread_file = open_output_file("comm_thread_table.bin") dso_file = open_output_file("dso_table.bin") symbol_file = open_output_file("symbol_table.bin") +branch_type_file = open_output_file("branch_type_table.bin") sample_file = open_output_file("sample_table.bin") def trace_begin(): @@ -257,6 +268,7 @@ def trace_end(): copy_output_file(comm_thread_file, "comm_threads") copy_output_file(dso_file, "dsos") copy_output_file(symbol_file, "symbols") + copy_output_file(branch_type_file, "branch_types") copy_output_file(sample_file, "samples") print datetime.datetime.today(), "Removing intermediate files..." @@ -267,6 +279,7 @@ def trace_end(): remove_output_file(comm_thread_file) remove_output_file(dso_file) remove_output_file(symbol_file) + remove_output_file(branch_type_file) remove_output_file(sample_file) os.rmdir(output_dir_name) print datetime.datetime.today(), "Adding primary keys" @@ -277,6 +290,7 @@ def trace_end(): do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') + do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') print datetime.datetime.today(), "Adding foreign keys" @@ -352,9 +366,15 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) symbol_file.write(value) -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, *x): +def branch_type_table(branch_type, name, *x): + n = len(name) + fmt = "!hiii" + str(n) + "s" + value = struct.pack(fmt, 2, 4, branch_type, n, name) + branch_type_file.write(value) + +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): if branches: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 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) + 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) else: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 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) + 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) sample_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 2fd7ee8f18c7..f3ca7798b3d0 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -66,6 +66,7 @@ struct tables { PyObject *comm_thread_handler; PyObject *dso_handler; PyObject *symbol_handler; + PyObject *branch_type_handler; PyObject *sample_handler; bool db_export_mode; }; @@ -664,13 +665,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym, return 0; } +static int python_export_branch_type(struct db_export *dbe, u32 branch_type, + const char *name) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + + t = tuple_new(2); + + tuple_set_s32(t, 0, branch_type); + tuple_set_string(t, 1, name); + + call_object(tables->branch_type_handler, t, "branch_type_table"); + + Py_DECREF(t); + + return 0; +} + static int python_export_sample(struct db_export *dbe, struct export_sample *es) { struct tables *tables = container_of(dbe, struct tables, dbe); PyObject *t; - t = tuple_new(19); + t = tuple_new(21); tuple_set_u64(t, 0, es->db_id); tuple_set_u64(t, 1, es->evsel->db_id); @@ -691,6 +710,8 @@ static int python_export_sample(struct db_export *dbe, tuple_set_u64(t, 16, es->sample->weight); tuple_set_u64(t, 17, es->sample->transaction); tuple_set_u64(t, 18, es->sample->data_src); + tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK); + tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); call_object(tables->sample_handler, t, "sample_table"); @@ -861,6 +882,7 @@ static void set_table_handlers(struct tables *tables) SET_TABLE_HANDLER(comm_thread); SET_TABLE_HANDLER(dso); SET_TABLE_HANDLER(symbol); + SET_TABLE_HANDLER(branch_type); SET_TABLE_HANDLER(sample); } @@ -910,6 +932,12 @@ static int python_start_script(const char *script, int argc, const char **argv) set_table_handlers(tables); + if (tables->db_export_mode) { + err = db_export__branch_types(&tables->dbe); + if (err) + goto error; + } + return err; error: Py_Finalize(); -- cgit From 6a70307ddcd9999598c399d55dc44c07816a575f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 30 Oct 2014 16:09:47 +0200 Subject: 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 Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1414678188-14946-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../scripts/python/bin/export-to-postgresql-report | 15 ++-- tools/perf/scripts/python/export-to-postgresql.py | 66 ++++++++++++++++- .../util/scripting-engines/trace-event-python.c | 84 +++++++++++++++++++++- 3 files changed, 158 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') 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 @@ #!/bin/bash # description: export perf data to a postgresql database -# args: [database name] [columns] +# args: [database name] [columns] [calls] n_args=0 for i in "$@" do @@ -9,11 +9,16 @@ do fi n_args=$(( $n_args + 1 )) done -if [ "$n_args" -gt 2 ] ; then - echo "usage: export-to-postgresql-report [database name] [columns]" +if [ "$n_args" -gt 3 ] ; then + echo "usage: export-to-postgresql-report [database name] [columns] [calls]" exit fi -if [ "$n_args" -gt 1 ] ; then +if [ "$n_args" -gt 2 ] ; then + dbname=$1 + columns=$2 + calls=$3 + shift 3 +elif [ "$n_args" -gt 1 ] ; then dbname=$1 columns=$2 shift 2 @@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then dbname=$1 shift fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns +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'] + \ #from Core import * perf_db_export_mode = True +perf_db_export_calls = False def usage(): - print >> sys.stderr, "Usage is: export-to-postgresql.py []" + print >> sys.stderr, "Usage is: export-to-postgresql.py [] []" print >> sys.stderr, "where: columns 'all' or 'branches'" + print >> sys.stderr, " calls 'calls' => create calls table" raise Exception("Too few arguments") if (len(sys.argv) < 2): @@ -61,6 +63,12 @@ if columns not in ("all", "branches"): branches = (columns == "branches") +if (len(sys.argv) >= 4): + if (sys.argv[3] == "calls"): + perf_db_export_calls = True + else: + usage() + output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" os.mkdir(output_dir_name) @@ -170,6 +178,25 @@ else: 'branch_type integer,' 'in_tx boolean)') +if perf_db_export_calls: + do_query(query, 'CREATE TABLE call_paths (' + 'id bigint NOT NULL,' + 'parent_id bigint,' + 'symbol_id bigint,' + 'ip bigint)') + do_query(query, 'CREATE TABLE calls (' + 'id bigint NOT NULL,' + 'thread_id bigint,' + 'comm_id bigint,' + 'call_path_id bigint,' + 'call_time bigint,' + 'return_time bigint,' + 'branch_count bigint,' + 'call_id bigint,' + 'return_id bigint,' + 'parent_call_path_id bigint,' + 'flags integer)') + do_query(query, 'CREATE VIEW samples_view AS ' 'SELECT ' 'id,' @@ -246,6 +273,9 @@ dso_file = open_output_file("dso_table.bin") symbol_file = open_output_file("symbol_table.bin") branch_type_file = open_output_file("branch_type_table.bin") sample_file = open_output_file("sample_table.bin") +if perf_db_export_calls: + call_path_file = open_output_file("call_path_table.bin") + call_file = open_output_file("call_table.bin") def trace_begin(): print datetime.datetime.today(), "Writing to intermediate files..." @@ -256,6 +286,9 @@ def trace_begin(): comm_table(0, "unknown") dso_table(0, 0, "unknown", "unknown", "") symbol_table(0, 0, 0, 0, 0, "unknown") + sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + if perf_db_export_calls: + call_path_table(0, 0, 0, 0) unhandled_count = 0 @@ -270,6 +303,9 @@ def trace_end(): copy_output_file(symbol_file, "symbols") copy_output_file(branch_type_file, "branch_types") copy_output_file(sample_file, "samples") + if perf_db_export_calls: + copy_output_file(call_path_file, "call_paths") + copy_output_file(call_file, "calls") print datetime.datetime.today(), "Removing intermediate files..." remove_output_file(evsel_file) @@ -281,6 +317,9 @@ def trace_end(): remove_output_file(symbol_file) remove_output_file(branch_type_file) remove_output_file(sample_file) + if perf_db_export_calls: + remove_output_file(call_path_file) + remove_output_file(call_file) os.rmdir(output_dir_name) print datetime.datetime.today(), "Adding primary keys" do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') @@ -292,6 +331,9 @@ def trace_end(): do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') + if perf_db_export_calls: + do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') + do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') print datetime.datetime.today(), "Adding foreign keys" do_query(query, 'ALTER TABLE threads ' @@ -313,6 +355,18 @@ def trace_end(): 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') + if perf_db_export_calls: + do_query(query, 'ALTER TABLE call_paths ' + 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' + 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') + do_query(query, 'ALTER TABLE calls ' + 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' + 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' + 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' + 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' + 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' + 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') + do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') if (unhandled_count): 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 else: 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) sample_file.write(value) + +def call_path_table(cp_id, parent_id, symbol_id, ip, *x): + fmt = "!hiqiqiqiq" + value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) + call_path_file.write(value) + +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): + fmt = "!hiqiqiqiqiqiqiqiqiqiqii" + 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) + 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 @@ #include "../comm.h" #include "../machine.h" #include "../db-export.h" +#include "../thread-stack.h" #include "../trace-event.h" #include "../machine.h" @@ -68,6 +69,8 @@ struct tables { PyObject *symbol_handler; PyObject *branch_type_handler; PyObject *sample_handler; + PyObject *call_path_handler; + PyObject *call_return_handler; bool db_export_mode; }; @@ -720,6 +723,64 @@ static int python_export_sample(struct db_export *dbe, return 0; } +static int python_export_call_path(struct db_export *dbe, struct call_path *cp) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + PyObject *t; + u64 parent_db_id, sym_db_id; + + parent_db_id = cp->parent ? cp->parent->db_id : 0; + sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; + + t = tuple_new(4); + + tuple_set_u64(t, 0, cp->db_id); + tuple_set_u64(t, 1, parent_db_id); + tuple_set_u64(t, 2, sym_db_id); + tuple_set_u64(t, 3, cp->ip); + + call_object(tables->call_path_handler, t, "call_path_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_export_call_return(struct db_export *dbe, + struct call_return *cr) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); + u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; + PyObject *t; + + t = tuple_new(11); + + tuple_set_u64(t, 0, cr->db_id); + tuple_set_u64(t, 1, cr->thread->db_id); + tuple_set_u64(t, 2, comm_db_id); + tuple_set_u64(t, 3, cr->cp->db_id); + tuple_set_u64(t, 4, cr->call_time); + tuple_set_u64(t, 5, cr->return_time); + tuple_set_u64(t, 6, cr->branch_count); + tuple_set_u64(t, 7, cr->call_ref); + tuple_set_u64(t, 8, cr->return_ref); + tuple_set_u64(t, 9, cr->cp->parent->db_id); + tuple_set_s32(t, 10, cr->flags); + + call_object(tables->call_return_handler, t, "call_return_table"); + + Py_DECREF(t); + + return 0; +} + +static int python_process_call_return(struct call_return *cr, void *data) +{ + struct db_export *dbe = data; + + return db_export__call_return(dbe, cr); +} + static void python_process_general_event(struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread, @@ -852,7 +913,9 @@ error: static void set_table_handlers(struct tables *tables) { const char *perf_db_export_mode = "perf_db_export_mode"; - PyObject *db_export_mode; + const char *perf_db_export_calls = "perf_db_export_calls"; + PyObject *db_export_mode, *db_export_calls; + bool export_calls = false; int ret; memset(tables, 0, sizeof(struct tables)); @@ -869,6 +932,23 @@ static void set_table_handlers(struct tables *tables) if (!ret) return; + tables->dbe.crp = NULL; + db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); + if (db_export_calls) { + ret = PyObject_IsTrue(db_export_calls); + if (ret == -1) + handler_call_die(perf_db_export_calls); + export_calls = !!ret; + } + + if (export_calls) { + tables->dbe.crp = + call_return_processor__new(python_process_call_return, + &tables->dbe); + if (!tables->dbe.crp) + Py_FatalError("failed to create calls processor"); + } + tables->db_export_mode = true; /* * Reserve per symbol space for symbol->db_id via symbol__priv() @@ -884,6 +964,8 @@ static void set_table_handlers(struct tables *tables) SET_TABLE_HANDLER(symbol); SET_TABLE_HANDLER(branch_type); SET_TABLE_HANDLER(sample); + SET_TABLE_HANDLER(call_path); + SET_TABLE_HANDLER(call_return); } /* -- cgit From 758008b262f70be41104e4e33ba99181ac03775d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 30 Oct 2014 16:09:48 +0200 Subject: perf tools: Defer export of comms that were not 'set' Tracing for a workload begins before the comm event is seen, which results in the initial comm having a string of the form ":" (e.g. ":12345"). In order to export the correct string, defer the export until the new script 'flush' callback. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1414678188-14946-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/db-export.c | 62 +++++++++++++++++++++- tools/perf/util/db-export.h | 3 ++ .../util/scripting-engines/trace-event-python.c | 4 +- 3 files changed, 67 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 017ecbb0ec05..c81dae399763 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -21,17 +21,74 @@ #include "comm.h" #include "symbol.h" #include "event.h" +#include "util.h" #include "thread-stack.h" #include "db-export.h" +struct deferred_export { + struct list_head node; + struct comm *comm; +}; + +static int db_export__deferred(struct db_export *dbe) +{ + struct deferred_export *de; + int err; + + while (!list_empty(&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); + err = dbe->export_comm(dbe, de->comm); + list_del(&de->node); + free(de); + if (err) + return err; + } + + return 0; +} + +static void db_export__free_deferred(struct db_export *dbe) +{ + struct deferred_export *de; + + while (!list_empty(&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); + list_del(&de->node); + free(de); + } +} + +static int db_export__defer_comm(struct db_export *dbe, struct comm *comm) +{ + struct deferred_export *de; + + de = zalloc(sizeof(struct deferred_export)); + if (!de) + return -ENOMEM; + + de->comm = comm; + list_add_tail(&de->node, &dbe->deferred); + + return 0; +} + int db_export__init(struct db_export *dbe) { memset(dbe, 0, sizeof(struct db_export)); + INIT_LIST_HEAD(&dbe->deferred); return 0; } +int db_export__flush(struct db_export *dbe) +{ + return db_export__deferred(dbe); +} + void db_export__exit(struct db_export *dbe) { + db_export__free_deferred(dbe); call_return_processor__free(dbe->crp); dbe->crp = NULL; } @@ -115,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm, comm->db_id = ++dbe->comm_last_db_id; if (dbe->export_comm) { - err = dbe->export_comm(dbe, comm); + if (main_thread->comm_set) + err = dbe->export_comm(dbe, comm); + else + err = db_export__defer_comm(dbe, comm); if (err) return err; } diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h index dd5ac2ae97d4..adbd22d66798 100644 --- a/tools/perf/util/db-export.h +++ b/tools/perf/util/db-export.h @@ -17,6 +17,7 @@ #define __PERF_DB_EXPORT_H #include +#include struct perf_evsel; struct machine; @@ -74,9 +75,11 @@ struct db_export { u64 sample_last_db_id; u64 call_path_last_db_id; u64 call_return_last_db_id; + struct list_head deferred; }; int db_export__init(struct db_export *dbe); +int db_export__flush(struct db_export *dbe); void db_export__exit(struct db_export *dbe); int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel); int db_export__machine(struct db_export *dbe, struct machine *machine); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cb1d9602f418..118bc62850a8 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1030,7 +1030,9 @@ error: static int python_flush_script(void) { - return 0; + struct tables *tables = &tables_global; + + return db_export__flush(&tables->dbe); } /* -- cgit From adf5bcf39583c4db1bf30069f8957400e61ccb18 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 26 Oct 2014 23:44:05 +0100 Subject: perf script python: Removing event cache as it's no longer needed We don't need to maintain cache of 'struct event_format' objects. Currently the 'struct perf_evsel' holds this reference already. Adding events_defined bitmap to keep track of defined events, which is much cheaper than array of pointers. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1414363445-22370-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- .../util/scripting-engines/trace-event-python.c | 34 ++++------------------ 1 file changed, 6 insertions(+), 28 deletions(-) (limited to 'tools/perf/util/scripting-engines/trace-event-python.c') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 118bc62850a8..d808a328f4dc 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../../perf.h" #include "../debug.h" @@ -46,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void); #define FTRACE_MAX_EVENT \ ((1 << (sizeof(unsigned short) * 8)) - 1) -struct event_format *events[FTRACE_MAX_EVENT]; +static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); #define MAX_FIELDS 64 #define N_COMMON_FIELDS 7 @@ -255,31 +256,6 @@ static void define_event_symbols(struct event_format *event, define_event_symbols(event, ev_name, args->next); } -static inline struct event_format *find_cache_event(struct perf_evsel *evsel) -{ - static char ev_name[256]; - struct event_format *event; - int type = evsel->attr.config; - - /* - * XXX: Do we really need to cache this since now we have evsel->tp_format - * cached already? Need to re-read this "cache" routine that as well calls - * define_event_symbols() :-\ - */ - if (events[type]) - return events[type]; - - events[type] = event = evsel->tp_format; - if (!event) - return NULL; - - sprintf(ev_name, "%s__%s", event->system, event->name); - - define_event_symbols(event, ev_name, event->print_fmt.args); - - return event; -} - static PyObject *get_field_numeric_entry(struct event_format *event, struct format_field *field, void *data) { @@ -403,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample, struct thread *thread, struct addr_location *al) { + struct event_format *event = evsel->tp_format; PyObject *handler, *context, *t, *obj, *callchain; PyObject *dict = NULL; static char handler_name[256]; struct format_field *field; unsigned long s, ns; - struct event_format *event; unsigned n = 0; int pid; int cpu = sample->cpu; @@ -420,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample, if (!t) Py_FatalError("couldn't create Python tuple"); - event = find_cache_event(evsel); if (!event) die("ug! no event found for type %d", (int)evsel->attr.config); @@ -428,6 +403,9 @@ static void python_process_tracepoint(struct perf_sample *sample, sprintf(handler_name, "%s__%s", event->system, event->name); + if (!test_and_set_bit(event->id, events_defined)) + define_event_symbols(event, handler_name, event->print_fmt.args); + handler = get_handler(handler_name); if (!handler) { dict = PyDict_New(); -- cgit