diff options
Diffstat (limited to 'tools/perf/util/python.c')
| -rw-r--r-- | tools/perf/util/python.c | 1044 |
1 files changed, 979 insertions, 65 deletions
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index d8a842621cda..95140dfaef9c 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -2,11 +2,18 @@ #define PY_SSIZE_T_CLEAN #include <Python.h> +#include <elf.h> +#include <errno.h> #include <inttypes.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> #include <string.h> +#include <stdlib.h> #include <linux/err.h> #include <poll.h> +#include <sys/stat.h> #include <unistd.h> #include <internal/lib.h> @@ -22,12 +29,17 @@ #include "counts.h" #include "data.h" #include "debug.h" +#include "call-path.h" +#include "db-export.h" +#include "thread-stack.h" #include "dso.h" #include "dwarf-regs.h" #include "event.h" +#include "env.h" #include "branch.h" #include "evlist.h" #include "evsel.h" +#include "symbol.h" #include "expr.h" #include "map.h" #include "metricgroup.h" @@ -88,6 +100,8 @@ struct pyrf_event { PyObject_HEAD /** @sample: The parsed sample from the event. */ struct perf_sample sample; + /** @machine: The machine associated with the sample/event. */ + struct machine *machine; /** @al: The address location from machine__resolve, lazily computed. */ struct addr_location al; /** @al_resolved: True when machine__resolve been called. */ @@ -106,6 +120,8 @@ struct pyrf_event { sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ + sample_member_def(machine_pid, machine_pid, T_UINT, "event machine pid"), \ + sample_member_def(vcpu, vcpu, T_UINT, "event vcpu"), \ sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), @@ -581,6 +597,7 @@ static PyMemberDef pyrf_sample_event__members[] = { sample_member_def(sample_data_src, data_src, T_ULONGLONG, "event data source"), sample_member_def(sample_insn_count, insn_cnt, T_ULONGLONG, "event instruction count"), sample_member_def(sample_cyc_count, cyc_cnt, T_ULONGLONG, "event cycle count"), + sample_member_def(flags, flags, T_UINT, "event flags"), member_def(perf_event_header, type, T_UINT, "event type"), { .name = NULL, }, }; @@ -679,6 +696,7 @@ static int pyrf_sample_event__resolve_al(struct pyrf_event *pevent) struct evsel *evsel = pevent->sample.evsel; struct evlist *evlist = evsel ? evsel->evlist : NULL; struct perf_session *session = evlist ? evlist__session(evlist) : NULL; + struct machine *machine; if (pevent->al_resolved) return 0; @@ -686,8 +704,17 @@ static int pyrf_sample_event__resolve_al(struct pyrf_event *pevent) if (!session) return -1; + /* + * Use pevent->machine, which pyrf_event__new() initializes either from + * the machine resolved by perf_session (machines__find_for_cpumode(), + * preserving DEFAULT_GUEST_KERNEL_ID == 0 for default guests) or via + * sample.machine_pid when pyrf_event__new() is called with a NULL + * machine. + */ + machine = pevent->machine ? pevent->machine : &session->machines.host; + addr_location__init(&pevent->al); - if (machine__resolve(&session->machines.host, &pevent->al, &pevent->sample) < 0) { + if (machine__resolve(machine, &pevent->al, &pevent->sample) < 0) { addr_location__exit(&pevent->al); return -1; } @@ -771,6 +798,15 @@ static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent, return PyLong_FromUnsignedLongLong(pevent->al.sym->start); } +static PyObject *pyrf_sample_event__get_sym_offset(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym) + Py_RETURN_NONE; + + return PyLong_FromUnsignedLongLong(pevent->al.addr - pevent->al.sym->start); +} + static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent, void *closure __maybe_unused) { @@ -801,6 +837,12 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args) PyObject *result; struct addr_location al; + /* + * addr defaults to pevent->sample.ip when omitted, and when + * addr != pevent->sample.ip a local addr_location is resolved so + * callers can inspect callchain/branch addresses without mutating + * the sample's cached pevent->al. + */ if (!PyArg_ParseTuple(args, "|K", &addr)) return NULL; @@ -829,6 +871,12 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args) addr_location__exit(&al); if (srcfile) { + /* + * PyUnicode_FromStringAndSize(line, len) (via Py_BuildValue "s#") + * must copy the source line before free_srccode() frees the + * buffer, and because the line returned by find_sourceline() is + * bounded by len rather than NUL-terminated. + */ srccode = find_sourceline(srcfile, line, &len); result = Py_BuildValue("(sIs#)", srcfile, line, srccode, (Py_ssize_t)len); free(srcfile); @@ -872,11 +920,13 @@ struct pyrf_callchain_node { u64 ip; struct map *map; struct symbol *sym; + char *sym_name; }; static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode) { map__put(pnode->map); + free(pnode->sym_name); Py_TYPE(pnode)->tp_free((PyObject *)pnode); } @@ -889,6 +939,8 @@ static PyObject *pyrf_callchain_node__get_ip(struct pyrf_callchain_node *pnode, static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pnode, void *closure __maybe_unused) { + if (pnode->sym_name) + return PyUnicode_FromString(pnode->sym_name); if (pnode->sym) return PyUnicode_FromString(pnode->sym->name); return PyUnicode_FromString("[unknown]"); @@ -912,10 +964,23 @@ static PyObject *pyrf_callchain_node__get_dso(struct pyrf_callchain_node *pnode, return PyUnicode_FromString(dsoname); } +static PyObject *pyrf_callchain_node__get_sym_offset(struct pyrf_callchain_node *pnode, + void *closure __maybe_unused) +{ + u64 addr; + + if (!pnode->sym) + Py_RETURN_NONE; + + addr = pnode->map ? map__map_ip(pnode->map, pnode->ip) : pnode->ip; + return PyLong_FromUnsignedLongLong(addr - pnode->sym->start); +} + static PyGetSetDef pyrf_callchain_node__getset[] = { - { .name = "ip", .get = (getter)pyrf_callchain_node__get_ip, }, - { .name = "symbol", .get = (getter)pyrf_callchain_node__get_symbol, }, - { .name = "dso", .get = (getter)pyrf_callchain_node__get_dso, }, + { .name = "ip", .get = (getter)pyrf_callchain_node__get_ip, }, + { .name = "symbol", .get = (getter)pyrf_callchain_node__get_symbol, }, + { .name = "dso", .get = (getter)pyrf_callchain_node__get_dso, }, + { .name = "sym_offset", .get = (getter)pyrf_callchain_node__get_sym_offset, }, { .name = NULL, }, }; @@ -933,6 +998,7 @@ struct pyrf_callchain_frame { u64 ip; struct map *map; struct symbol *sym; + char *sym_name; }; struct pyrf_callchain { @@ -944,8 +1010,10 @@ struct pyrf_callchain { static void pyrf_callchain__delete(struct pyrf_callchain *pchain) { if (pchain->frames) { - for (u64 i = 0; i < pchain->nr_frames; i++) + for (u64 i = 0; i < pchain->nr_frames; i++) { map__put(pchain->frames[i].map); + free(pchain->frames[i].sym_name); + } free(pchain->frames); } Py_TYPE(pchain)->tp_free((PyObject *)pchain); @@ -975,6 +1043,7 @@ static PyObject *pyrf_callchain__item(PyObject *obj, Py_ssize_t i) pnode->ip = pchain->frames[i].ip; pnode->map = map__get(pchain->frames[i].map); pnode->sym = pchain->frames[i].sym; + pnode->sym_name = pchain->frames[i].sym_name ? strdup(pchain->frames[i].sym_name) : NULL; return (PyObject *)pnode; } @@ -1166,7 +1235,114 @@ pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name) return obj ?: PyObject_GenericGetAttr((PyObject *) pevent, attr_name); } +static int pyrf_sample_event__resolve_addr_al(struct pyrf_event *pevent, + struct addr_location *addr_al) +{ + addr_location__init(addr_al); + if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.thread) + return -1; + + thread__find_symbol_fb(pevent->al.thread, pevent->sample.cpumode, + pevent->sample.addr, addr_al); + return 0; +} + +static PyObject *pyrf_sample_event__get_addr_dso(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + struct addr_location addr_al; + PyObject *ret = Py_None; + + if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.map) + ret = PyUnicode_FromString(dso__name(map__dso(addr_al.map))); + else + Py_INCREF(Py_None); + + addr_location__exit(&addr_al); + return ret; +} + +static PyObject *pyrf_sample_event__get_addr_symbol(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + struct addr_location addr_al; + PyObject *ret = Py_None; + + if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym) + ret = PyUnicode_FromString(addr_al.sym->name); + else + Py_INCREF(Py_None); + + addr_location__exit(&addr_al); + return ret; +} + +static PyObject *pyrf_sample_event__get_addr_sym_offset(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + struct addr_location addr_al; + PyObject *ret = Py_None; + + if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym) + ret = PyLong_FromUnsignedLongLong(addr_al.addr - addr_al.sym->start); + else + Py_INCREF(Py_None); + + addr_location__exit(&addr_al); + return ret; +} + +static PyObject *pyrf_sample_event__get_branch_type(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLong(pevent->sample.flags & PERF_BRANCH_MASK); +} + +static PyObject *pyrf_sample_event__get_in_tx(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLong(!!(pevent->sample.flags & PERF_IP_FLAG_IN_TX)); +} + +static PyObject *pyrf_sample_event__get_transaction(struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pevent->sample.transaction); +} + static PyGetSetDef pyrf_sample_event__getset[] = { + + { + .name = "addr_dso", + .get = (getter)pyrf_sample_event__get_addr_dso, + .doc = "event destination dso.", + }, + { + .name = "addr_symbol", + .get = (getter)pyrf_sample_event__get_addr_symbol, + .doc = "event destination symbol.", + }, + { + .name = "addr_sym_offset", + .get = (getter)pyrf_sample_event__get_addr_sym_offset, + .doc = "event destination symbol offset.", + }, + { + .name = "branch_type", + .get = (getter)pyrf_sample_event__get_branch_type, + .doc = "branch type.", + }, + { + .name = "in_tx", + .get = (getter)pyrf_sample_event__get_in_tx, + .doc = "in transaction flag.", + }, + { + .name = "transaction", + .get = (getter)pyrf_sample_event__get_transaction, + .doc = "transaction execution.", + }, + { .name = "callchain", .get = pyrf_sample_event__get_callchain, @@ -1228,6 +1404,12 @@ static PyGetSetDef pyrf_sample_event__getset[] = { .doc = "event map page offset.", }, { + .name = "sym_offset", + .get = (getter)pyrf_sample_event__get_sym_offset, + .set = NULL, + .doc = "event symbol offset.", + }, + { .name = "symbol", .get = (getter)pyrf_sample_event__get_symbol, .set = NULL, @@ -1283,8 +1465,45 @@ static const char pyrf_context_switch_event__doc[] = PyDoc_STR("perf context_swi static PyMemberDef pyrf_context_switch_event__members[] = { sample_members member_def(perf_event_header, type, T_UINT, "event type"), - member_def(perf_record_switch, next_prev_pid, T_UINT, "next/prev pid"), - member_def(perf_record_switch, next_prev_tid, T_UINT, "next/prev tid"), + member_def(perf_event_header, misc, T_USHORT, "event misc"), + { .name = NULL, }, +}; + +static PyObject *pyrf_context_switch_event__get_next_prev_pid(const struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE) + return PyLong_FromUnsignedLong(pevent->event.context_switch.next_prev_pid); + Py_RETURN_NONE; +} + +static PyObject *pyrf_context_switch_event__get_next_prev_tid(const struct pyrf_event *pevent, + void *closure __maybe_unused) +{ + if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE) + return PyLong_FromUnsignedLong(pevent->event.context_switch.next_prev_tid); + Py_RETURN_NONE; +} + +static PyGetSetDef pyrf_context_switch_event__getset[] = { + { + .name = "evsel", + .get = pyrf_event__get_evsel, + .set = NULL, + .doc = "tracking event.", + }, + { + .name = "next_prev_pid", + .get = (getter)pyrf_context_switch_event__get_next_prev_pid, + .set = NULL, + .doc = "next/prev pid for CPU-wide switch, or None.", + }, + { + .name = "next_prev_tid", + .get = (getter)pyrf_context_switch_event__get_next_prev_tid, + .set = NULL, + .doc = "next/prev tid for CPU-wide switch, or None.", + }, { .name = NULL, }, }; @@ -1292,11 +1511,19 @@ static PyObject *pyrf_context_switch_event__repr(const struct pyrf_event *pevent { PyObject *ret; char *s; - - if (asprintf(&s, "{ type: context_switch, next_prev_pid: %u, next_prev_tid: %u, switch_out: %u }", - pevent->event.context_switch.next_prev_pid, - pevent->event.context_switch.next_prev_tid, - !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT)) < 0) { + int res; + + if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE) { + res = asprintf(&s, + "{ type: context_switch, next_prev_pid: %u, next_prev_tid: %u, switch_out: %u }", + pevent->event.context_switch.next_prev_pid, + pevent->event.context_switch.next_prev_tid, + !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT)); + } else { + res = asprintf(&s, "{ type: context_switch, switch_out: %u }", + !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT)); + } + if (res < 0) { ret = PyErr_NoMemory(); } else { ret = PyUnicode_FromString(s); @@ -1313,7 +1540,7 @@ static PyTypeObject pyrf_context_switch_event__type = { .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc = pyrf_context_switch_event__doc, .tp_members = pyrf_context_switch_event__members, - .tp_getset = pyrf_event__getset, + .tp_getset = pyrf_context_switch_event__getset, .tp_repr = (reprfunc)pyrf_context_switch_event__repr, }; @@ -1388,8 +1615,8 @@ static PyTypeObject *pyrf_event__type[] = { }; static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *evsel, - struct perf_session *session, - struct machine *machine) + struct perf_session *session, struct machine *machine, + struct perf_sample *sample_arg) { struct pyrf_event *pevent; struct perf_sample *sample; @@ -1397,9 +1624,6 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev u32 min_size; bool needs_swap; - if (!machine) - machine = session ? &session->machines.host : NULL; - if (event->header.type >= ARRAY_SIZE(pyrf_event__type) || pyrf_event__type[event->header.type] == NULL) { return PyErr_Format(PyExc_TypeError, "Unexpected header type %u", @@ -1431,13 +1655,17 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev pevent->event.mmap2.filename[sizeof(pevent->event.mmap2.filename) - 1] = '\0'; perf_sample__init(&pevent->sample, /*all=*/true); + pevent->machine = machine; pevent->callchain = NULL; pevent->brstack = NULL; pevent->al_resolved = false; addr_location__init(&pevent->al); - if (!evsel) + if (!evsel) { + if (!pevent->machine && session) + pevent->machine = &session->machines.host; return (PyObject *)pevent; + } /* Parse the sample again so that pointers are within the copied event. */ needs_swap = evsel->needs_swap; @@ -1446,10 +1674,45 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev err = evsel__parse_sample(evsel, &pevent->event, &pevent->sample); evsel->needs_swap = needs_swap; if (err < 0) { - Py_DECREF(pevent); - return PyErr_Format(PyExc_OSError, - "perf: can't parse sample, err=%d", err); + /* + * Synthesized events (e.g. Intel PT itrace) may not have raw sample + * buffers for evsel__parse_sample(); use the pre-parsed sample_arg. + */ + if (sample_arg) { + perf_sample__exit(&pevent->sample); + pevent->sample = *sample_arg; + if (pevent->sample.evsel) + pevent->sample.evsel = evsel__get(pevent->sample.evsel); + pevent->sample.merged_callchain = false; + + pevent->sample.user_regs = NULL; + pevent->sample.intr_regs = NULL; + pevent->sample.raw_data = NULL; + pevent->sample.raw_size = 0; + } else { + Py_DECREF(pevent); + return PyErr_Format(PyExc_OSError, + "perf: can't parse sample, err=%d", err); + } + } + if (session && session->evlist && perf_guest && pevent->sample.id) { + struct perf_sample_id *sid = evlist__id2sid(session->evlist, pevent->sample.id); + + if (sid) { + pevent->sample.machine_pid = sid->machine_pid; + pevent->sample.vcpu = sid->vcpu.cpu; + } + } + if (!machine && session) { + machine = pevent->sample.machine_pid ? + machines__find(&session->machines, pevent->sample.machine_pid) : + &session->machines.host; + if (!machine) + machine = &session->machines.host; } + pevent->machine = machine; + if (machine && machine->pid > 0 && !pevent->sample.machine_pid) + pevent->sample.machine_pid = machine->pid; sample = &pevent->sample; if (machine && sample->callchain) { struct addr_location al; @@ -1795,7 +2058,6 @@ static int pyrf_pmu__setup_types(void) return PyType_Ready(&pyrf_pmu__type); } - /** A python iterator for pmus that has no equivalent in the C code. */ struct pyrf_pmu_iterator { PyObject_HEAD @@ -2206,12 +2468,13 @@ static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, { struct evsel *evsel = pevsel->evsel; int cpu = 0, cpu_idx, thread = 0, thread_idx; + static char *kwlist[] = { "cpu", "thread", NULL }; struct perf_counts_values *old_count, *new_count; struct pyrf_counts_values *count_values; CHECK_INITIALIZED(evsel, "evsel"); - if (!PyArg_ParseTuple(args, "ii", &cpu, &thread)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &cpu, &thread)) return NULL; cpu_idx = perf_cpu_map__idx(evsel->core.cpus, (struct perf_cpu){.cpu = cpu}); @@ -2736,10 +2999,6 @@ static int prepare_metric(const struct metric_expr *mexp, double val, ena, run; int ret, source_count = 0; struct perf_counts_values *old_count, *new_count; - char *n = strdup(evsel__metric_id(cur)); - - if (!n) - return -ENOMEM; /* * If there are multiple uncore PMUs and we're not reading the @@ -2781,9 +3040,15 @@ static int prepare_metric(const struct metric_expr *mexp, if (ena != run && run != 0) val = val * ena / run; + char *n = strdup(evsel__metric_id(cur)); + + if (!n) + return -ENOMEM; ret = expr__add_id_val_source_count(pctx, n, val, source_count); - if (ret) + if (ret) { + free(n); return ret; + } } for (int i = 0; metric_refs && metric_refs[i].metric_name; i++) { @@ -2801,6 +3066,7 @@ static PyObject *pyrf_evlist__compute_metric(struct pyrf_evlist *pevlist, { int ret, cpu = 0, cpu_idx = 0, thread = 0, thread_idx = 0; const char *metric; + static char *kwlist[] = { "metric", "cpu", "thread", NULL }; struct rb_node *node; struct metric_expr *mexp = NULL; struct expr_parse_ctx *pctx; @@ -2809,7 +3075,7 @@ static PyObject *pyrf_evlist__compute_metric(struct pyrf_evlist *pevlist, CHECK_INITIALIZED(pevlist->evlist, "evlist"); - if (!PyArg_ParseTuple(args, "sii", &metric, &cpu, &thread)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sii", kwlist, &metric, &cpu, &thread)) return NULL; for (node = rb_first_cached(&evlist__metric_events(pevlist->evlist)->entries); @@ -2951,10 +3217,7 @@ free_list: return PyErr_NoMemory(); } - -static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, - PyObject *args, - PyObject *kwargs __maybe_unused) +static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, PyObject *args) { struct evlist *evlist; PyObject *pevsel; @@ -3032,7 +3295,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, perf_mmap__consume(&md->core); Py_RETURN_NONE; } - pyevent = pyrf_event__new(event, evsel, evlist__session(evlist), /*machine=*/NULL); + pyevent = pyrf_event__new(event, evsel, evlist__session(evlist), /*machine=*/NULL, NULL); perf_mmap__consume(&md->core); if (pyevent == NULL) return PyErr_Occurred() ? NULL : PyErr_NoMemory(); @@ -3166,7 +3429,7 @@ static PyMethodDef pyrf_evlist__methods[] = { { .ml_name = "add", .ml_meth = (PyCFunction)pyrf_evlist__add, - .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_flags = METH_VARARGS, .ml_doc = PyDoc_STR("adds an event selector to the list.") }, { @@ -3400,6 +3663,11 @@ static const struct perf_constant perf__constants[] = { PERF_CONST(RECORD_STAT_ROUND), PERF_CONST(RECORD_MISC_SWITCH_OUT), + + /* Values for the flags of a perf.call_return, see thread-stack.h. */ + { "CALL_RETURN_NO_CALL", CALL_RETURN_NO_CALL }, + { "CALL_RETURN_NO_RETURN", CALL_RETURN_NO_RETURN }, + { "CALL_RETURN_NON_CALL", CALL_RETURN_NON_CALL }, { .name = NULL, }, }; @@ -3410,7 +3678,7 @@ static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel, char *sys = NULL; char *name = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ss", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &sys, &name)) return NULL; @@ -3678,6 +3946,10 @@ static int pyrf_data__init(struct pyrf_data *pdata, PyObject *args, PyObject *kw pdata->data.mode = PERF_DATA_MODE_READ; pdata->data.file.fd = fd; if (perf_data__open(&pdata->data) < 0) { + if (fd >= 0) { + close(fd); + pdata->data.file.fd = -1; + } PyErr_Format(PyExc_IOError, "Failed to open perf data: %s", pdata->data.path ? pdata->data.path : "perf.data"); return -1; @@ -3834,24 +4106,448 @@ struct pyrf_session { struct perf_session *session; struct perf_tool tool; + struct itrace_synth_opts itrace_opts; struct pyrf_data *pdata; PyObject *sample; PyObject *stat; + PyObject *context_switch; + PyObject *call_return; + struct call_return_processor *crp; + /** + * @call_return_last_db_id: Counter used to give each perf.call_return a + * unique db_id. A call's db_id may be allocated before the call + * returns, so the identifiers cannot be generated by the Python script. + */ + u64 call_return_last_db_id; + u64 sample_last_db_id; + char *vmlinux_name; + char *kallsyms_name; + char *symfs; +}; + +struct pyrf_call_return { + PyObject_HEAD + struct call_return cr; + /** + * @call_path: perf.callchain of the call path, ordered from the + * outermost caller to the callee. Resolved eagerly as the underlying + * struct call_path is owned by the call return processor. + */ + PyObject *call_path; + char *comm; + pid_t machine_pid; + pid_t pid; + pid_t tid; +}; + +static PyObject *pyrf_call_return__repr(struct pyrf_call_return *pcr) +{ + return PyUnicode_FromString("{ type: call_return }"); +} + +static PyObject *pyrf_call_return__get_comm(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + if (pcr->comm) + return PyUnicode_FromString(pcr->comm); + Py_RETURN_NONE; +} + +static PyObject *pyrf_call_return__get_machine_pid(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + return PyLong_FromLong(pcr->machine_pid); +} + +static PyObject *pyrf_call_return__get_pid(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + if (pcr->pid != -1) + return PyLong_FromLong(pcr->pid); + Py_RETURN_NONE; +} + +static PyObject *pyrf_call_return__get_tid(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + if (pcr->tid != -1) + return PyLong_FromLong(pcr->tid); + Py_RETURN_NONE; +} + +static PyObject *pyrf_call_return__get_call_time(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromUnsignedLongLong(cr->call_time); +} + +static PyObject *pyrf_call_return__get_return_time(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromUnsignedLongLong(cr->return_time); +} + +static PyObject *pyrf_call_return__get_branch_count(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromUnsignedLongLong(cr->branch_count); +} + +static PyObject *pyrf_call_return__get_call_ref(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromUnsignedLongLong(cr->call_ref); +} + +static PyObject *pyrf_call_return__get_return_ref(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromUnsignedLongLong(cr->return_ref); +} + +static PyObject *pyrf_call_return__get_flags(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + struct call_return *cr = &pcr->cr; + + return PyLong_FromLong(cr->flags); +} + +static PyObject *pyrf_call_return__get_call_path(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + PyObject *call_path = pcr->call_path ?: Py_None; + + Py_INCREF(call_path); + return call_path; +} + +static PyObject *pyrf_call_return__get_parent_id(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pcr->cr.parent_db_id); +} + +static PyObject *pyrf_call_return__get_insn_count(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pcr->cr.insn_count); +} + +static PyObject *pyrf_call_return__get_cyc_count(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pcr->cr.cyc_count); +} + +static PyObject *pyrf_call_return__get_db_id(struct pyrf_call_return *pcr, + void *closure __maybe_unused) +{ + return PyLong_FromUnsignedLongLong(pcr->cr.db_id); +} + +static PyGetSetDef pyrf_call_return__getset[] = { + { + .name = "db_id", + .get = (getter)pyrf_call_return__get_db_id, + .doc = "Unique identifier of this call return.", + }, + { + .name = "parent_id", + .get = (getter)pyrf_call_return__get_parent_id, + .doc = "db_id of the calling call return, 0 if there is none.", + }, + { + .name = "insn_count", + .get = (getter)pyrf_call_return__get_insn_count, + .doc = "Instructions executed between the call and the return.", + }, + { + .name = "cyc_count", + .get = (getter)pyrf_call_return__get_cyc_count, + .doc = "Cycles elapsed between the call and the return.", + }, + { + .name = "comm", + .get = (getter)pyrf_call_return__get_comm, + .doc = "Thread comm at the time of the call/return, or None.", + }, + { + .name = "machine_pid", + .get = (getter)pyrf_call_return__get_machine_pid, + .doc = "Machine PID (0 for host).", + }, + { + .name = "pid", + .get = (getter)pyrf_call_return__get_pid, + .doc = "Process ID of the thread, or None.", + }, + { + .name = "tid", + .get = (getter)pyrf_call_return__get_tid, + .doc = "Thread identifier, None if unknown.", + }, + { + .name = "call_time", + .get = (getter)pyrf_call_return__get_call_time, + .doc = "Timestamp of the call.", + }, + { + .name = "return_time", + .get = (getter)pyrf_call_return__get_return_time, + .doc = "Timestamp of the return.", + }, + { + .name = "branch_count", + .get = (getter)pyrf_call_return__get_branch_count, + .doc = "Branches taken between the call and the return.", + }, + { + .name = "call_ref", + .get = (getter)pyrf_call_return__get_call_ref, + .doc = "Reference to the sample of the call.", + }, + { + .name = "return_ref", + .get = (getter)pyrf_call_return__get_return_ref, + .doc = "Reference to the sample of the return.", + }, + { + .name = "flags", + .get = (getter)pyrf_call_return__get_flags, + .doc = "Bitmask of perf.CALL_RETURN_* values.", + }, + { + .name = "call_path", + .get = (getter)pyrf_call_return__get_call_path, + .doc = "perf.callchain from outermost caller to callee.", + }, + { .name = NULL, }, +}; + +static void pyrf_call_return__delete(struct pyrf_call_return *pevent) +{ + Py_XDECREF(pevent->call_path); + free(pevent->comm); + Py_TYPE(pevent)->tp_free((PyObject *)pevent); +} + +static PyTypeObject pyrf_call_return__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.call_return", + .tp_basicsize = sizeof(struct pyrf_call_return), + .tp_dealloc = (destructor)pyrf_call_return__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = "perf call_return object.", + .tp_getset = pyrf_call_return__getset, + .tp_repr = (reprfunc)pyrf_call_return__repr, }; +/* + * Turn a struct call_path, that is a leaf with parent pointers up to the + * outermost caller, into a perf.callchain ordered from the outermost caller to + * the leaf. The struct call_path is owned by the call return processor and so + * must be resolved before returning to Python. + */ +static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp, + struct thread *thread) +{ + struct pyrf_callchain *pchain; + const struct call_path *pos; + u64 nr_frames = 0; + + /* The root call path is a sentinel with no symbol, skip it. */ + for (pos = cp; pos && pos->parent; pos = pos->parent) + nr_frames++; + + pchain = PyObject_New(struct pyrf_callchain, &pyrf_callchain__type); + if (!pchain) + return NULL; + + pchain->nr_frames = nr_frames; + pchain->frames = nr_frames ? calloc(nr_frames, sizeof(*pchain->frames)) : NULL; + if (nr_frames && !pchain->frames) { + pchain->nr_frames = 0; + Py_DECREF(pchain); + return PyErr_NoMemory(); + } + /* Fill in reverse so that the outermost caller is first. */ + for (pos = cp; nr_frames > 0; pos = pos->parent) { + struct pyrf_callchain_frame *frame = &pchain->frames[--nr_frames]; + + frame->ip = pos->ip; + frame->sym = NULL; + frame->sym_name = pos->sym ? strdup(pos->sym->name) : NULL; + frame->map = NULL; + if (thread) { + struct addr_location al; + u8 cpumode = pos->in_kernel ? PERF_RECORD_MISC_KERNEL + : PERF_RECORD_MISC_USER; + + addr_location__init(&al); + thread__find_map(thread, cpumode, pos->ip, &al); + frame->map = map__get(al.map); + if (frame->map) + frame->sym = pos->sym; + addr_location__exit(&al); + } + } + return (PyObject *)pchain; +} + +static PyObject *pyrf_call_return__new(struct call_return *cr) +{ + struct pyrf_call_return *pevent; + + pevent = PyObject_New(struct pyrf_call_return, &pyrf_call_return__type); + if (!pevent) + return NULL; + + pevent->cr = *cr; + /* Avoid lifetime issues with thread by caching tid/pid/machine_pid */ + if (cr->thread) { + struct maps *maps = thread__maps(cr->thread); + + pevent->machine_pid = maps && maps__machine(maps) ? maps__machine(maps)->pid : 0; + pevent->pid = thread__pid(cr->thread); + pevent->tid = thread__tid(cr->thread); + } else { + pevent->machine_pid = 0; + pevent->pid = -1; + pevent->tid = -1; + } + pevent->cr.thread = NULL; + pevent->comm = cr->comm ? strdup(comm__str(cr->comm)) : NULL; + pevent->call_path = NULL; + if (cr->cp) { + pevent->call_path = pyrf_call_path__to_callchain(cr->cp, cr->thread); + if (!pevent->call_path) { + Py_DECREF(pevent); + return NULL; + } + } + return (PyObject *)pevent; +} + +static int pyrf_session__call_return_process(struct call_return *cr, + u64 *parent_db_id, + void *data) +{ + struct pyrf_session *psession = data; + PyObject *pyevent, *ret; + + if (!psession->call_return) + return 0; + + if (!cr->db_id) + cr->db_id = ++psession->call_return_last_db_id; + + if (parent_db_id) { + if (!*parent_db_id) + *parent_db_id = ++psession->call_return_last_db_id; + cr->parent_db_id = *parent_db_id; + } + + pyevent = pyrf_call_return__new(cr); + if (!pyevent) + return -1; + + ret = PyObject_CallFunctionObjArgs(psession->call_return, pyevent, NULL); + if (!ret) { + Py_DECREF(pyevent); + return -1; + } + Py_DECREF(ret); + Py_DECREF(pyevent); + return 0; +} + static int pyrf_session_tool__sample(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) { struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool); - PyObject *pyevent = pyrf_event__new(event, sample->evsel, psession->session, machine); + u64 sample_db_id = ++psession->sample_last_db_id; + + if (psession->crp) { + struct addr_location al, addr_al; + struct thread *thread = NULL; + + addr_location__init(&al); + addr_location__init(&addr_al); + + if (machine__resolve(machine, &al, sample) >= 0) { + thread = al.thread; + if (sample->flags & (PERF_IP_FLAG_CALL | + PERF_IP_FLAG_TRACE_BEGIN | + PERF_IP_FLAG_RETURN | + PERF_IP_FLAG_BRANCH | + PERF_IP_FLAG_TRACE_END)) { + thread__resolve(thread, &addr_al, sample); + } + + int err; + + err = thread_stack__process(thread, thread__comm(thread), sample, + &al, &addr_al, sample_db_id, psession->crp); + if (err) { + addr_location__exit(&addr_al); + addr_location__exit(&al); + return err; + } + } + + addr_location__exit(&addr_al); + addr_location__exit(&al); + } + + if (psession->sample) { + PyObject *pyevent = pyrf_event__new(event, sample->evsel, + psession->session, + machine, sample); + PyObject *ret; + + if (pyevent == NULL) + return -ENOMEM; + + ret = PyObject_CallFunction(psession->sample, "O", pyevent); + Py_DECREF(pyevent); + if (!ret) + return -1; + Py_DECREF(ret); + } + return 0; +} + +static int pyrf_session_tool__context_switch(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool); + PyObject *pyevent = pyrf_event__new(event, sample->evsel, psession->session, machine, NULL); PyObject *ret; + if (perf_event__process_switch(tool, event, sample, machine) < 0) { + Py_XDECREF(pyevent); + return -1; + } + if (pyevent == NULL) return -ENOMEM; - ret = PyObject_CallFunction(psession->sample, "O", pyevent); + ret = PyObject_CallFunction(psession->context_switch, "O", pyevent); if (!ret) { Py_DECREF(pyevent); return -1; @@ -3868,7 +4564,7 @@ static int pyrf_session_tool__stat(const struct perf_tool *tool, struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool); struct evsel *evsel = evlist__id2evsel(session->evlist, event->stat.id); PyObject *pyevent = pyrf_event__new(event, /*evsel=*/NULL, psession->session, - /*machine=*/NULL); + /*machine=*/NULL, NULL); const char *name = evsel ? evsel__name(evsel) : "unknown"; PyObject *ret; @@ -3891,7 +4587,7 @@ static int pyrf_session_tool__stat_round(const struct perf_tool *tool, { struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool); PyObject *pyevent = pyrf_event__new(event, /*evsel=*/NULL, psession->session, - /*machine=*/NULL); + /*machine=*/NULL, NULL); PyObject *ret; if (pyevent == NULL) @@ -3912,26 +4608,40 @@ static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObje struct machine *machine; struct thread *thread = NULL; PyObject *result; - int pid; + int pid, tid = -1; CHECK_INITIALIZED(psession->session, "session"); - if (!PyArg_ParseTuple(args, "i", &pid)) + if (!PyArg_ParseTuple(args, "i|i", &pid, &tid)) return NULL; + if (tid == -1) { + tid = pid; + pid = -1; + } + + /* + * When host lookup fails, guest machines (default_guest and the guests + * rb_root) are searched. + */ machine = &psession->session->machines.host; - thread = machine__find_thread(machine, pid, pid); + thread = machine__find_thread(machine, pid, tid); if (!thread) { - machine = perf_session__find_machine(psession->session, pid); + machine = perf_session__find_machine(psession->session, pid != -1 ? pid : tid); if (machine) - thread = machine__find_thread(machine, pid, pid); + thread = machine__find_thread(machine, pid, tid); + if (!thread) { + machine = machines__find(&psession->session->machines, + DEFAULT_GUEST_KERNEL_ID); + if (machine) + thread = machine__find_thread(machine, pid, tid); + } } - if (!thread) { - PyErr_Format(PyExc_TypeError, "Failed to find thread %d", pid); - return NULL; - } + /* Return None rather than raising TypeError when a PID/TID is not known. */ + if (!thread) + Py_RETURN_NONE; result = pyrf_thread__from_thread(thread); thread__put(thread); return result; @@ -3940,23 +4650,41 @@ static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObje static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { struct pyrf_data *pdata; - PyObject *sample = NULL, *stat = NULL; - static char *kwlist[] = { "data", "sample", "stat", NULL }; + PyObject *sample = NULL, *stat = NULL, *context_switch = NULL; + PyObject *call_return = NULL; + char *itrace_str = NULL; + char *vmlinux_str = NULL, *kallsyms_str = NULL, *symfs_str = NULL; + static char *kwlist[] = { "data", "sample", "stat", "context_switch", + "call_return", "itrace", "vmlinux", "kallsyms", + "symfs", NULL }; struct pyrf_session *psession; struct perf_session *session; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &pyrf_data__type, &pdata, - &sample, &stat)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OOOOzzzz", kwlist, + &pyrf_data__type, &pdata, + &sample, &stat, &context_switch, + &call_return, &itrace_str, + &vmlinux_str, &kallsyms_str, &symfs_str)) return NULL; psession = PyObject_New(struct pyrf_session, type); if (!psession) return NULL; + /* PyObject_New doesn't zero the memory, initialize every member. */ psession->session = NULL; psession->sample = NULL; psession->stat = NULL; + psession->context_switch = NULL; + psession->call_return = NULL; + psession->crp = NULL; + psession->call_return_last_db_id = 0; + psession->sample_last_db_id = 0; + psession->vmlinux_name = NULL; + psession->kallsyms_name = NULL; + psession->symfs = NULL; psession->pdata = NULL; + memset(&psession->itrace_opts, 0, sizeof(psession->itrace_opts)); Py_INCREF(pdata); psession->pdata = pdata; @@ -3966,7 +4694,7 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject #define ADD_TOOL(name) \ do { \ - if (name) { \ + if (name && name != Py_None) { \ if (!PyCallable_Check(name)) { \ PyErr_SetString(PyExc_TypeError, #name " must be callable"); \ goto err_out; \ @@ -3978,14 +4706,38 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject } while (0) ADD_TOOL(sample); + if (call_return && call_return != Py_None && (!sample || sample == Py_None)) + psession->tool.sample = pyrf_session_tool__sample; ADD_TOOL(stat); + ADD_TOOL(context_switch); + + if (call_return && call_return != Py_None) { + if (!PyCallable_Check(call_return)) { + PyErr_SetString(PyExc_TypeError, "call_return must be callable"); + Py_DECREF(psession); + return NULL; + } + Py_INCREF(call_return); + psession->call_return = call_return; + psession->crp = call_return_processor__new(pyrf_session__call_return_process, + psession); + if (!psession->crp) { + PyErr_SetString(PyExc_MemoryError, + "Failed to allocate call_return_processor"); + Py_DECREF(psession); + return NULL; + } + } #undef ADD_TOOL - if (stat) + if (stat && stat != Py_None) psession->tool.stat_round = pyrf_session_tool__stat_round; - psession->tool.comm = perf_event__process_comm; + psession->tool.auxtrace_info = perf_event__process_auxtrace_info; + psession->tool.auxtrace = perf_event__process_auxtrace; + psession->tool.auxtrace_error = perf_event__process_auxtrace_error; + psession->tool.id_index = perf_event__process_id_index; psession->tool.mmap = perf_event__process_mmap; psession->tool.mmap2 = perf_event__process_mmap2; psession->tool.namespaces = perf_event__process_namespaces; @@ -3996,7 +4748,44 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject psession->tool.text_poke = perf_event__process_text_poke; psession->tool.build_id = perf_event__process_build_id; psession->tool.attr = perf_event__process_attr; + psession->tool.event_update = perf_event__process_event_update; psession->tool.feature = perf_event__process_feature; +#ifdef HAVE_LIBTRACEEVENT + psession->tool.tracing_data = perf_event__process_tracing_data; +#endif + + if (!vmlinux_str) + vmlinux_str = getenv("PERF_SYMBOL_VMLINUX"); + if (vmlinux_str && vmlinux_str[0]) { + psession->vmlinux_name = strdup(vmlinux_str); + if (!psession->vmlinux_name) { + PyErr_NoMemory(); + goto err_out; + } + symbol_conf.vmlinux_name = psession->vmlinux_name; + } + + if (!kallsyms_str) + kallsyms_str = getenv("PERF_SYMBOL_KALLSYMS"); + if (kallsyms_str && kallsyms_str[0]) { + psession->kallsyms_name = strdup(kallsyms_str); + if (!psession->kallsyms_name) { + PyErr_NoMemory(); + goto err_out; + } + symbol_conf.kallsyms_name = psession->kallsyms_name; + } + + if (!symfs_str) + symfs_str = getenv("PERF_SYMBOL_SYMFS"); + if (symfs_str && symfs_str[0]) { + psession->symfs = strdup(symfs_str); + if (!psession->symfs) { + PyErr_NoMemory(); + goto err_out; + } + symbol_conf.symfs = psession->symfs; + } session = perf_session__new(&pdata->data, &psession->tool); if (IS_ERR(session)) { @@ -4004,6 +4793,18 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject goto err_out; } psession->session = session; + psession->session->itrace_synth_opts = &psession->itrace_opts; + + if (!itrace_str) + itrace_str = getenv("PERF_ITRACE"); + if (itrace_str) { + memset(&psession->itrace_opts, 0, sizeof(psession->itrace_opts)); + if (itrace_do_parse_synth_opts(&psession->itrace_opts, itrace_str, 0) < 0) { + PyErr_SetString(PyExc_ValueError, "Failed to parse itrace options"); + Py_DECREF(psession); + return NULL; + } + } symbol_conf.use_callchain = true; symbol_conf.show_kernel_path = true; @@ -4013,8 +4814,6 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject goto err_out; } - - return (PyObject *)psession; err_out: Py_DECREF(psession); @@ -4024,9 +4823,25 @@ err_out: static void pyrf_session__delete(struct pyrf_session *psession) { perf_session__delete(psession->session); + if (symbol_conf.vmlinux_name == psession->vmlinux_name) + symbol_conf.vmlinux_name = NULL; + free(psession->vmlinux_name); + if (symbol_conf.kallsyms_name == psession->kallsyms_name) + symbol_conf.kallsyms_name = NULL; + free(psession->kallsyms_name); + if (symbol_conf.symfs == psession->symfs) + symbol_conf.symfs = ""; + free(psession->symfs); + free(psession->itrace_opts.vm_tm_corr_args); + free(psession->itrace_opts.cpu_bitmap); + free(psession->itrace_opts.ptime_range); Py_XDECREF(psession->pdata); Py_XDECREF(psession->sample); Py_XDECREF(psession->stat); + Py_XDECREF(psession->context_switch); + Py_XDECREF(psession->call_return); + if (psession->crp) + call_return_processor__free(psession->crp); Py_TYPE(psession)->tp_free((PyObject *)psession); } @@ -4069,10 +4884,54 @@ static const char pyrf_session__doc[] = PyDoc_STR("perf session object."); static PyObject *pyrf_session__getattro(struct pyrf_session *psession, PyObject *attr_name) { + const char *name_str = PyUnicode_AsUTF8(attr_name); + + if (!name_str) + return NULL; if (!psession->session) { PyErr_SetString(PyExc_ValueError, "session not initialized"); return NULL; } + if (!strcmp(name_str, "e_machine")) + return PyLong_FromLong(perf_session__e_machine(psession->session, + /*e_flags=*/NULL)); + + if (!strcmp(name_str, "is_64_bit")) { + /* + * Prefer the machine of the recorded threads over the perf_env, + * as in pipe mode the perf_env may not be populated yet and + * perf_env__kernel_is_64_bit would cache the host's bitness. + */ + switch (perf_session__e_machine(psession->session, /*e_flags=*/NULL)) { + case EM_AARCH64: + case EM_ALPHA: + case EM_IA_64: + case EM_LOONGARCH: + case EM_PPC64: + case EM_SPARCV9: + case EM_X86_64: + Py_RETURN_TRUE; + case EM_386: + case EM_ARM: + case EM_PPC: + case EM_SPARC: + Py_RETURN_FALSE; + default: + /* + * Machines like EM_MIPS, EM_PARISC, EM_RISCV and + * EM_S390 are used for both word sizes, fall back on + * the recorded arch string if available. + */ + break; + } + { + struct perf_env *env = perf_session__env(psession->session); + + if (env && env->arch) + return PyBool_FromLong(perf_env__kernel_is_64_bit(env) == 1); + return PyBool_FromLong(sizeof(void *) == 8); + } + } return PyObject_GenericGetAttr((PyObject *) psession, attr_name); } @@ -4104,14 +4963,35 @@ static int pyrf_session__setup_types(void) return PyType_Ready(&pyrf_session__type); } +static int pyrf__parse_elf_machine(PyObject *obj, int *elf_machine) +{ + long val; + + if (!obj || obj == Py_None) { + *elf_machine = EM_HOST; + return 0; + } + val = PyLong_AsLong(obj); + if (val == -1 && PyErr_Occurred()) + return -1; + if (val <= 0 || val > UINT16_MAX) + *elf_machine = EM_HOST; + else + *elf_machine = (int)val; + return 0; +} + static PyObject *pyrf__syscall_name(PyObject *self, PyObject *args, PyObject *kwargs) { const char *name; int id; int elf_machine = EM_HOST; + PyObject *elf_machine_obj = NULL; static char *kwlist[] = { "id", "elf_machine", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|$i", kwlist, &id, &elf_machine)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O", kwlist, &id, &elf_machine_obj)) + return NULL; + if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0) return NULL; name = syscalltbl__name(elf_machine, id); @@ -4125,9 +5005,12 @@ static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject *kwar const char *name; int id; int elf_machine = EM_HOST; + PyObject *elf_machine_obj = NULL; static char *kwlist[] = { "name", "elf_machine", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|$i", kwlist, &name, &elf_machine)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &name, &elf_machine_obj)) + return NULL; + if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0) return NULL; id = syscalltbl__id(elf_machine, name); @@ -4138,6 +5021,30 @@ static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject *kwar return PyLong_FromLong(id); } +static PyObject *pyrf__arch_strerrno(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *name; + int err; + int elf_machine = EM_HOST; + PyObject *elf_machine_obj = NULL; + static char *kwlist[] = { "err", "elf_machine", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O", kwlist, &err, &elf_machine_obj)) + return NULL; + if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0) + return NULL; + + if (err == INT_MIN) + Py_RETURN_NONE; + if (err < 0) + err = -err; + + name = perf_env__arch_strerrno((uint16_t)elf_machine, err); + if (!name || !strcmp(name, "(unknown)")) + Py_RETURN_NONE; + return PyUnicode_FromString(name); +} + static PyObject *pyrf__config_get(PyObject *self, PyObject *args) { const char *config_name, *val; @@ -4202,6 +5109,12 @@ static PyMethodDef perf__methods[] = { .ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_doc = PyDoc_STR("Turns a syscall name to a number.") }, + { + .ml_name = "arch_strerrno", + .ml_meth = (PyCFunction) pyrf__arch_strerrno, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("Turns an errno number to a string.") + }, { .ml_name = NULL, } }; @@ -4226,7 +5139,8 @@ PyMODINIT_FUNC PyInit_perf(void) if (module == NULL) return NULL; - if (pyrf_event__setup_types() < 0 || + if (PyType_Ready(&pyrf_call_return__type) < 0 || + pyrf_event__setup_types() < 0 || pyrf_evlist__setup_types() < 0 || pyrf_evsel__setup_types() < 0 || pyrf_thread_map__setup_types() < 0 || @@ -4243,6 +5157,7 @@ PyMODINIT_FUNC PyInit_perf(void) /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); + perf_debug_setup(); Py_INCREF(&pyrf_evlist__type); PyModule_AddObject(module, "evlist", (PyObject *)&pyrf_evlist__type); @@ -4277,9 +5192,6 @@ PyMODINIT_FUNC PyInit_perf(void) Py_INCREF(&pyrf_throttle_event__type); PyModule_AddObject(module, "throttle_event", (PyObject *)&pyrf_throttle_event__type); - Py_INCREF(&pyrf_task_event__type); - PyModule_AddObject(module, "task_event", (PyObject *)&pyrf_task_event__type); - Py_INCREF(&pyrf_read_event__type); PyModule_AddObject(module, "read_event", (PyObject *)&pyrf_read_event__type); @@ -4309,6 +5221,8 @@ PyMODINIT_FUNC PyInit_perf(void) Py_INCREF(&pyrf_session__type); PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type); + Py_INCREF(&pyrf_call_return__type); + PyModule_AddObject(module, "call_return", (PyObject *)&pyrf_call_return__type); Py_INCREF(&pyrf_branch_entry__type); if (PyModule_AddObject(module, "branch_entry", (PyObject *)&pyrf_branch_entry__type) < 0) { |
