Perf: Provide sample information and call-chain to Python script Provide additional sample information on generic events to Python scripts, including pid, tid, and cpu for which the event was recorded. Additionally, provide the call-stack recorded at each event with resolved symbols. At the moment, the pointer to the sample struct is passed to scripts, which seems to be of little use. The patch puts this information in dictionaries for easy access by Python scripts. Signed-off-by: Joseph Schuchart Acked-by: Thomas Ilsche @@ -359,7 +359,7 @@ static void python_process_general_event struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *t, *dict; + PyObject *handler, *retval, *t, *dict, *dict_sample; static char handler_name[64]; unsigned n = 0; @@ -375,6 +375,10 @@ static void python_process_general_event if (!dict) Py_FatalError("couldn't create Python dictionary"); + dict_sample = PyDict_New(); + if (!dict_sample) + Py_FatalError("couldn't create Python dictionary"); + snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); handler = PyDict_GetItemString(main_dict, handler_name); @@ -384,8 +388,76 @@ static void python_process_general_event pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize( (const char *)&evsel->attr, sizeof(evsel->attr))); - pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize( - (const char *)sample, sizeof(*sample))); + + /* PID/TIDs are limited to 2^29, so we can safely use PyInt */ + pydict_set_item_string_decref(dict_sample, "pid", PyInt_FromLong(sample->pid)); + pydict_set_item_string_decref(dict_sample, "tid", PyInt_FromLong(sample->tid)); + pydict_set_item_string_decref(dict_sample, "cpu", PyInt_FromLong(sample->cpu)); + pydict_set_item_string_decref(dict_sample, "time", PyLong_FromUnsignedLongLong(sample->time)); + pydict_set_item_string_decref(dict, "sample", dict_sample); + + /* ip unwinding */ + + if (symbol_conf.use_callchain && sample->callchain) { + PyObject *pylist; + + if (machine__resolve_callchain(machine, evsel, al->thread, + sample, NULL, NULL, PERF_MAX_STACK_DEPTH) != 0) { + pr_err("Failed to resolve callchain. Skipping\n"); + goto exit; + } + callchain_cursor_commit(&callchain_cursor); + + pylist = PyList_New(0); + if (!pylist) + Py_FatalError("couldn't create Python list"); + + while (1) { + PyObject *pyelem; + struct callchain_cursor_node *node; + node = callchain_cursor_current(&callchain_cursor); + if (!node) + break; + + pyelem = PyDict_New(); + if (!pyelem) + Py_FatalError("couldn't create Python dictionary"); + + + pydict_set_item_string_decref(pyelem, "ip", PyInt_FromLong(node->ip)); + + if (node->sym) { + PyObject *pysym = PyDict_New(); + if (!pysym) + Py_FatalError("couldn't create Python dictionary"); + pydict_set_item_string_decref(pysym, "start", PyInt_FromLong(node->sym->start)); + pydict_set_item_string_decref(pysym, "end", PyInt_FromLong(node->sym->end)); + pydict_set_item_string_decref(pysym, "binding", PyInt_FromLong(node->sym->binding)); + pydict_set_item_string_decref(pysym, "name", PyString_FromStringAndSize(node->sym->name, node->sym->namelen)); + pydict_set_item_string_decref(pyelem, "sym", pysym); + Py_DECREF(pysym); + } + + if (node->map) { + struct map *map = node->map; + const char *dsoname = "[unknown]"; + if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (symbol_conf.show_kernel_path && map->dso->long_name) + dsoname = map->dso->long_name; + else if (map->dso->name) + dsoname = map->dso->name; + } + pydict_set_item_string_decref(pyelem, "dso", PyString_FromString(dsoname)); + } + + callchain_cursor_advance(&callchain_cursor); + PyList_Append(pylist, pyelem); + Py_DECREF(pyelem); + } + PyDict_SetItemString(dict, "callstack", pylist); + Py_DECREF(pylist); + } + pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( (const char *)sample->raw_data, sample->raw_size)); pydict_set_item_string_decref(dict, "comm", @@ -407,6 +479,7 @@ static void python_process_general_event if (retval == NULL) handler_call_die(handler_name); exit: + Py_DECREF(dict_sample); Py_DECREF(dict); Py_DECREF(t); }