summaryrefslogtreecommitdiff
path: root/tools/perf/util/python.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/python.c')
-rw-r--r--tools/perf/util/python.c1044
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) {