diff options
Diffstat (limited to 'tools/perf/util/python.c')
| -rw-r--r-- | tools/perf/util/python.c | 315 | 
1 files changed, 302 insertions, 13 deletions
| diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index f3c05da25b4a..ea77bea0306f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -10,6 +10,7 @@  #endif  #include <perf/mmap.h>  #include "callchain.h" +#include "counts.h"  #include "evlist.h"  #include "evsel.h"  #include "event.h" @@ -18,6 +19,7 @@  #include "strbuf.h"  #include "thread_map.h"  #include "trace-event.h" +#include "metricgroup.h"  #include "mmap.h"  #include "util/sample.h"  #include <internal/lib.h> @@ -335,7 +337,6 @@ tracepoint_field(const struct pyrf_event *pe, struct tep_format_field *field)  static PyObject*  get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name)  { -	const char *str = _PyUnicode_AsString(PyObject_Str(attr_name));  	struct evsel *evsel = pevent->evsel;  	struct tep_event *tp_format = evsel__tp_format(evsel);  	struct tep_format_field *field; @@ -343,7 +344,18 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name)  	if (IS_ERR_OR_NULL(tp_format))  		return NULL; +	PyObject *obj = PyObject_Str(attr_name); +	if (obj == NULL) +		return NULL; + +	const char *str = PyUnicode_AsUTF8(obj); +	if (str == NULL) { +		Py_DECREF(obj); +		return NULL; +	} +  	field = tep_find_any_field(tp_format, str); +	Py_DECREF(obj);  	return field ? tracepoint_field(pevent, field) : NULL;  }  #endif /* HAVE_LIBTRACEEVENT */ @@ -527,8 +539,10 @@ static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i)  {  	struct pyrf_cpu_map *pcpus = (void *)obj; -	if (i >= perf_cpu_map__nr(pcpus->cpus)) +	if (i >= perf_cpu_map__nr(pcpus->cpus)) { +		PyErr_SetString(PyExc_IndexError, "Index out of range");  		return NULL; +	}  	return Py_BuildValue("i", perf_cpu_map__cpu(pcpus->cpus, i).cpu);  } @@ -566,14 +580,14 @@ struct pyrf_thread_map {  static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,  				 PyObject *args, PyObject *kwargs)  { -	static char *kwlist[] = { "pid", "tid", "uid", NULL }; -	int pid = -1, tid = -1, uid = UINT_MAX; +	static char *kwlist[] = { "pid", "tid", NULL }; +	int pid = -1, tid = -1; -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", -					 kwlist, &pid, &tid, &uid)) +	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", +					 kwlist, &pid, &tid))  		return -1; -	pthreads->threads = thread_map__new(pid, tid, uid); +	pthreads->threads = thread_map__new(pid, tid);  	if (pthreads->threads == NULL)  		return -1;  	return 0; @@ -596,8 +610,10 @@ static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i)  {  	struct pyrf_thread_map *pthreads = (void *)obj; -	if (i >= perf_thread_map__nr(pthreads->threads)) +	if (i >= perf_thread_map__nr(pthreads->threads)) { +		PyErr_SetString(PyExc_IndexError, "Index out of range");  		return NULL; +	}  	return Py_BuildValue("i", perf_thread_map__pid(pthreads->threads, i));  } @@ -626,6 +642,92 @@ static int pyrf_thread_map__setup_types(void)  	return PyType_Ready(&pyrf_thread_map__type);  } +struct pyrf_counts_values { +	PyObject_HEAD + +	struct perf_counts_values values; +}; + +static const char pyrf_counts_values__doc[] = PyDoc_STR("perf counts values object."); + +static void pyrf_counts_values__delete(struct pyrf_counts_values *pcounts_values) +{ +	Py_TYPE(pcounts_values)->tp_free((PyObject *)pcounts_values); +} + +#define counts_values_member_def(member, ptype, help) \ +	{ #member, ptype, \ +	  offsetof(struct pyrf_counts_values, values.member), \ +	  0, help } + +static PyMemberDef pyrf_counts_values_members[] = { +	counts_values_member_def(val, T_ULONG, "Value of event"), +	counts_values_member_def(ena, T_ULONG, "Time for which enabled"), +	counts_values_member_def(run, T_ULONG, "Time for which running"), +	counts_values_member_def(id, T_ULONG, "Unique ID for an event"), +	counts_values_member_def(lost, T_ULONG, "Num of lost samples"), +	{ .name = NULL, }, +}; + +static PyObject *pyrf_counts_values_get_values(struct pyrf_counts_values *self, void *closure) +{ +	PyObject *vals = PyList_New(5); + +	if (!vals) +		return NULL; +	for (int i = 0; i < 5; i++) +		PyList_SetItem(vals, i, PyLong_FromLong(self->values.values[i])); + +	return vals; +} + +static int pyrf_counts_values_set_values(struct pyrf_counts_values *self, PyObject *list, +					 void *closure) +{ +	Py_ssize_t size; +	PyObject *item = NULL; + +	if (!PyList_Check(list)) { +		PyErr_SetString(PyExc_TypeError, "Value assigned must be a list"); +		return -1; +	} + +	size = PyList_Size(list); +	for (Py_ssize_t i = 0; i < size; i++) { +		item = PyList_GetItem(list, i); +		if (!PyLong_Check(item)) { +			PyErr_SetString(PyExc_TypeError, "List members should be numbers"); +			return -1; +		} +		self->values.values[i] = PyLong_AsLong(item); +	} + +	return 0; +} + +static PyGetSetDef pyrf_counts_values_getset[] = { +	{"values", (getter)pyrf_counts_values_get_values, (setter)pyrf_counts_values_set_values, +		"Name field", NULL}, +	{ .name = NULL, }, +}; + +static PyTypeObject pyrf_counts_values__type = { +	PyVarObject_HEAD_INIT(NULL, 0) +	.tp_name	= "perf.counts_values", +	.tp_basicsize	= sizeof(struct pyrf_counts_values), +	.tp_dealloc	= (destructor)pyrf_counts_values__delete, +	.tp_flags	= Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, +	.tp_doc		= pyrf_counts_values__doc, +	.tp_members	= pyrf_counts_values_members, +	.tp_getset	= pyrf_counts_values_getset, +}; + +static int pyrf_counts_values__setup_types(void) +{ +	pyrf_counts_values__type.tp_new = PyType_GenericNew; +	return PyType_Ready(&pyrf_counts_values__type); +} +  struct pyrf_evsel {  	PyObject_HEAD @@ -781,15 +883,104 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,  	return Py_None;  } +static PyObject *pyrf_evsel__cpus(struct pyrf_evsel *pevsel) +{ +	struct pyrf_cpu_map *pcpu_map = PyObject_New(struct pyrf_cpu_map, &pyrf_cpu_map__type); + +	if (pcpu_map) +		pcpu_map->cpus = perf_cpu_map__get(pevsel->evsel.core.cpus); + +	return (PyObject *)pcpu_map; +} + +static PyObject *pyrf_evsel__threads(struct pyrf_evsel *pevsel) +{ +	struct pyrf_thread_map *pthread_map = +		PyObject_New(struct pyrf_thread_map, &pyrf_thread_map__type); + +	if (pthread_map) +		pthread_map->threads = perf_thread_map__get(pevsel->evsel.core.threads); + +	return (PyObject *)pthread_map; +} + +/* + * Ensure evsel's counts and prev_raw_counts are allocated, the latter + * used by tool PMUs to compute the cumulative count as expected by + * stat's process_counter_values. + */ +static int evsel__ensure_counts(struct evsel *evsel) +{ +	int nthreads, ncpus; + +	if (evsel->counts != NULL) +		return 0; + +	nthreads = perf_thread_map__nr(evsel->core.threads); +	ncpus = perf_cpu_map__nr(evsel->core.cpus); + +	evsel->counts = perf_counts__new(ncpus, nthreads); +	if (evsel->counts == NULL) +		return -ENOMEM; + +	evsel->prev_raw_counts = perf_counts__new(ncpus, nthreads); +	if (evsel->prev_raw_counts == NULL) +		return -ENOMEM; + +	return 0; +} + +static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, +				  PyObject *args, PyObject *kwargs) +{ +	struct evsel *evsel = &pevsel->evsel; +	int cpu = 0, cpu_idx, thread = 0, thread_idx; +	struct perf_counts_values *old_count, *new_count; +	struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values, +							       &pyrf_counts_values__type); + +	if (!count_values) +		return NULL; + +	if (!PyArg_ParseTuple(args, "ii", &cpu, &thread)) +		return NULL; + +	cpu_idx = perf_cpu_map__idx(evsel->core.cpus, (struct perf_cpu){.cpu = cpu}); +	if (cpu_idx < 0) { +		PyErr_Format(PyExc_TypeError, "CPU %d is not part of evsel's CPUs", cpu); +		return NULL; +	} +	thread_idx = perf_thread_map__idx(evsel->core.threads, thread); +	if (thread_idx < 0) { +		PyErr_Format(PyExc_TypeError, "Thread %d is not part of evsel's threads", +			     thread); +		return NULL; +	} + +	if (evsel__ensure_counts(evsel)) +		return PyErr_NoMemory(); + +	/* Set up pointers to the old and newly read counter values. */ +	old_count = perf_counts(evsel->prev_raw_counts, cpu_idx, thread_idx); +	new_count = perf_counts(evsel->counts, cpu_idx, thread_idx); +	/* Update the value in evsel->counts. */ +	evsel__read_counter(evsel, cpu_idx, thread_idx); +	/* Copy the value and turn it into the delta from old_count. */ +	count_values->values = *new_count; +	count_values->values.val -= old_count->val; +	count_values->values.ena -= old_count->ena; +	count_values->values.run -= old_count->run; +	/* Save the new count over the old_count for the next read. */ +	*old_count = *new_count; +	return (PyObject *)count_values; +} +  static PyObject *pyrf_evsel__str(PyObject *self)  {  	struct pyrf_evsel *pevsel = (void *)self;  	struct evsel *evsel = &pevsel->evsel; -	if (!evsel->pmu) -		return PyUnicode_FromFormat("evsel(%s)", evsel__name(evsel)); - -	return PyUnicode_FromFormat("evsel(%s/%s/)", evsel->pmu->name, evsel__name(evsel)); +	return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evsel__name(evsel));  }  static PyMethodDef pyrf_evsel__methods[] = { @@ -799,6 +990,24 @@ static PyMethodDef pyrf_evsel__methods[] = {  		.ml_flags = METH_VARARGS | METH_KEYWORDS,  		.ml_doc	  = PyDoc_STR("open the event selector file descriptor table.")  	}, +	{ +		.ml_name  = "cpus", +		.ml_meth  = (PyCFunction)pyrf_evsel__cpus, +		.ml_flags = METH_NOARGS, +		.ml_doc	  = PyDoc_STR("CPUs the event is to be used with.") +	}, +	{ +		.ml_name  = "threads", +		.ml_meth  = (PyCFunction)pyrf_evsel__threads, +		.ml_flags = METH_NOARGS, +		.ml_doc	  = PyDoc_STR("threads the event is to be used with.") +	}, +	{ +		.ml_name  = "read", +		.ml_meth  = (PyCFunction)pyrf_evsel__read, +		.ml_flags = METH_VARARGS | METH_KEYWORDS, +		.ml_doc	  = PyDoc_STR("read counters") +	},  	{ .ml_name = NULL, }  }; @@ -1054,6 +1263,16 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist,  	return Py_None;  } +static PyObject *pyrf_evlist__close(struct pyrf_evlist *pevlist) +{ +	struct evlist *evlist = &pevlist->evlist; + +	evlist__close(evlist); + +	Py_INCREF(Py_None); +	return Py_None; +} +  static PyObject *pyrf_evlist__config(struct pyrf_evlist *pevlist)  {  	struct record_opts opts = { @@ -1113,6 +1332,12 @@ static PyMethodDef pyrf_evlist__methods[] = {  		.ml_doc	  = PyDoc_STR("open the file descriptors.")  	},  	{ +		.ml_name  = "close", +		.ml_meth  = (PyCFunction)pyrf_evlist__close, +		.ml_flags = METH_NOARGS, +		.ml_doc	  = PyDoc_STR("close the file descriptors.") +	}, +	{  		.ml_name  = "poll",  		.ml_meth  = (PyCFunction)pyrf_evlist__poll,  		.ml_flags = METH_VARARGS | METH_KEYWORDS, @@ -1357,10 +1582,37 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel)  	return (PyObject *)pevsel;  } +static int evlist__pos(struct evlist *evlist, struct evsel *evsel) +{ +	struct evsel *pos; +	int idx = 0; + +	evlist__for_each_entry(evlist, pos) { +		if (evsel == pos) +			return idx; +		idx++; +	} +	return -1; +} + +static struct evsel *evlist__at(struct evlist *evlist, int idx) +{ +	struct evsel *pos; +	int idx2 = 0; + +	evlist__for_each_entry(evlist, pos) { +		if (idx == idx2) +			return pos; +		idx2++; +	} +	return NULL; +} +  static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist)  {  	struct pyrf_evlist *pevlist = PyObject_New(struct pyrf_evlist, &pyrf_evlist__type);  	struct evsel *pos; +	struct rb_node *node;  	if (!pevlist)  		return NULL; @@ -1372,6 +1624,39 @@ static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist)  		evlist__add(&pevlist->evlist, &pevsel->evsel);  	} +	evlist__for_each_entry(&pevlist->evlist, pos) { +		struct evsel *leader = evsel__leader(pos); + +		if (pos != leader) { +			int idx = evlist__pos(evlist, leader); + +			if (idx >= 0) +				evsel__set_leader(pos, evlist__at(&pevlist->evlist, idx)); +			else if (leader == NULL) +				evsel__set_leader(pos, pos); +		} +	} +	metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=*/NULL, +					&pevlist->evlist.metric_events, +					&evlist->metric_events); +	for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); node; +	     node = rb_next(node)) { +		struct metric_event *me = container_of(node, struct metric_event, nd); +		struct list_head *mpos; +		int idx = evlist__pos(evlist, me->evsel); + +		if (idx >= 0) +			me->evsel = evlist__at(&pevlist->evlist, idx); +		list_for_each(mpos, &me->head) { +			struct metric_expr *e = container_of(mpos, struct metric_expr, nd); + +			for (int j = 0; e->metric_events[j]; j++) { +				idx = evlist__pos(evlist, e->metric_events[j]); +				if (idx >= 0) +					e->metric_events[j] = evlist__at(&pevlist->evlist, idx); +			} +		} +	}  	return (PyObject *)pevlist;  } @@ -1442,7 +1727,8 @@ PyMODINIT_FUNC PyInit_perf(void)  	    pyrf_evlist__setup_types() < 0 ||  	    pyrf_evsel__setup_types() < 0 ||  	    pyrf_thread_map__setup_types() < 0 || -	    pyrf_cpu_map__setup_types() < 0) +	    pyrf_cpu_map__setup_types() < 0 || +	    pyrf_counts_values__setup_types() < 0)  		return module;  	/* The page_size is placed in util object. */ @@ -1487,6 +1773,9 @@ PyMODINIT_FUNC PyInit_perf(void)  	Py_INCREF(&pyrf_cpu_map__type);  	PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); +	Py_INCREF(&pyrf_counts_values__type); +	PyModule_AddObject(module, "counts_values", (PyObject *)&pyrf_counts_values__type); +  	dict = PyModule_GetDict(module);  	if (dict == NULL)  		goto error; | 
