Commit f52679b7 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

perf tools: Support reading PERF_FORMAT_LOST



The recent kernel added lost count can be read from either read(2) or
ring buffer data with PERF_SAMPLE_READ.  As it's a variable length data
we need to access it according to the format info.

But for perf tools use cases, PERF_FORMAT_ID is always set.  So we can
only check PERF_FORMAT_LOST bit to determine the data format.

Add sample_read_value_size() and next_sample_read_value() helpers to
make it a bit easier to access.  Use them in all places where it reads
the struct sample_read_value.

Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220819003644.508916-5-namhyung@kernel.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 6d395a51
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -86,10 +86,15 @@ static bool samples_same(const struct perf_sample *s1,
			COMP(read.time_running);
		/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
		if (read_format & PERF_FORMAT_GROUP) {
			for (i = 0; i < s1->read.group.nr; i++)
			for (i = 0; i < s1->read.group.nr; i++) {
				/* FIXME: check values without LOST */
				if (read_format & PERF_FORMAT_LOST)
					MCOMP(read.group.values[i]);
			}
		} else {
			COMP(read.one.id);
			if (read_format & PERF_FORMAT_LOST)
				COMP(read.one.lost);
		}
	}

@@ -263,7 +268,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
			.data	= (void *)aux_data,
		},
	};
	struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},};
	struct sample_read_value values[] = {{1, 5, 0}, {9, 3, 0}, {2, 7, 0}, {6, 4, 1},};
	struct perf_sample sample_out, sample_out_endian;
	size_t i, sz, bufsz;
	int err, ret = -1;
@@ -286,6 +291,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
	} else {
		sample.read.one.value = 0x08789faeb786aa87ULL;
		sample.read.one.id    = 99;
		sample.read.one.lost  = 1;
	}

	sz = perf_event__sample_event_size(&sample, sample_type, read_format);
@@ -370,7 +376,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
 */
static int test__sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
	const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15};
	const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 28, 29, 30, 31};
	u64 sample_type;
	u64 sample_regs;
	size_t i;
+20 −1
Original line number Diff line number Diff line
@@ -65,7 +65,8 @@ struct stack_dump {

struct sample_read_value {
	u64 value;
	u64 id;
	u64 id;   /* only if PERF_FORMAT_ID */
	u64 lost; /* only if PERF_FORMAT_LOST */
};

struct sample_read {
@@ -80,6 +81,24 @@ struct sample_read {
	};
};

static inline size_t sample_read_value_size(u64 read_format)
{
	/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
	if (read_format & PERF_FORMAT_LOST)
		return sizeof(struct sample_read_value);
	else
		return offsetof(struct sample_read_value, lost);
}

static inline struct sample_read_value *
next_sample_read_value(struct sample_read_value *v, u64 read_format)
{
	return (void *)v + sample_read_value_size(read_format);
}

#define sample_read_group__for_each(v, nr, rf)		\
	for (int __i = 0; __i < (int)nr; v = next_sample_read_value(v, rf), __i++)

struct ip_callchain {
	u64 nr;
	u64 ips[];
+18 −11
Original line number Diff line number Diff line
@@ -1541,7 +1541,7 @@ static int evsel__read_one(struct evsel *evsel, int cpu_map_idx, int thread)
}

static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread,
			     u64 val, u64 ena, u64 run)
			     u64 val, u64 ena, u64 run, u64 lost)
{
	struct perf_counts_values *count;

@@ -1550,6 +1550,7 @@ static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread,
	count->val    = val;
	count->ena    = ena;
	count->run    = run;
	count->lost   = lost;

	perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true);
}
@@ -1558,7 +1559,7 @@ static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx, int
{
	u64 read_format = leader->core.attr.read_format;
	struct sample_read_value *v;
	u64 nr, ena = 0, run = 0, i;
	u64 nr, ena = 0, run = 0, lost = 0;

	nr = *data++;

@@ -1571,18 +1572,18 @@ static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx, int
	if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
		run = *data++;

	v = (struct sample_read_value *) data;

	evsel__set_count(leader, cpu_map_idx, thread, v[0].value, ena, run);

	for (i = 1; i < nr; i++) {
	v = (void *)data;
	sample_read_group__for_each(v, nr, read_format) {
		struct evsel *counter;

		counter = evlist__id2evsel(leader->evlist, v[i].id);
		counter = evlist__id2evsel(leader->evlist, v->id);
		if (!counter)
			return -EINVAL;

		evsel__set_count(counter, cpu_map_idx, thread, v[i].value, ena, run);
		if (read_format & PERF_FORMAT_LOST)
			lost = v->lost;

		evsel__set_count(counter, cpu_map_idx, thread, v->value, ena, run, lost);
	}

	return 0;
@@ -2475,8 +2476,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,

			if (data->read.group.nr > max_group_nr)
				return -EFAULT;
			sz = data->read.group.nr *
			     sizeof(struct sample_read_value);

			sz = data->read.group.nr * sample_read_value_size(read_format);
			OVERFLOW_CHECK(array, sz, max_size);
			data->read.group.values =
					(struct sample_read_value *)array;
@@ -2485,6 +2486,12 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
			OVERFLOW_CHECK_u64(array);
			data->read.one.id = *array;
			array++;

			if (read_format & PERF_FORMAT_LOST) {
				OVERFLOW_CHECK_u64(array);
				data->read.one.lost = *array;
				array++;
			}
		}
	}

+14 −5
Original line number Diff line number Diff line
@@ -642,15 +642,19 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample,
	return pylist;
}

static PyObject *get_sample_value_as_tuple(struct sample_read_value *value)
static PyObject *get_sample_value_as_tuple(struct sample_read_value *value,
					   u64 read_format)
{
	PyObject *t;

	t = PyTuple_New(2);
	t = PyTuple_New(3);
	if (!t)
		Py_FatalError("couldn't create Python tuple");
	PyTuple_SetItem(t, 0, PyLong_FromUnsignedLongLong(value->id));
	PyTuple_SetItem(t, 1, PyLong_FromUnsignedLongLong(value->value));
	if (read_format & PERF_FORMAT_LOST)
		PyTuple_SetItem(t, 2, PyLong_FromUnsignedLongLong(value->lost));

	return t;
}

@@ -681,12 +685,17 @@ static void set_sample_read_in_dict(PyObject *dict_sample,
		Py_FatalError("couldn't create Python list");

	if (read_format & PERF_FORMAT_GROUP) {
		for (i = 0; i < sample->read.group.nr; i++) {
			PyObject *t = get_sample_value_as_tuple(&sample->read.group.values[i]);
		struct sample_read_value *v = sample->read.group.values;

		i = 0;
		sample_read_group__for_each(v, sample->read.group.nr, read_format) {
			PyObject *t = get_sample_value_as_tuple(v, read_format);
			PyList_SET_ITEM(values, i, t);
			i++;
		}
	} else {
		PyObject *t = get_sample_value_as_tuple(&sample->read.one);
		PyObject *t = get_sample_value_as_tuple(&sample->read.one,
							read_format);
		PyList_SET_ITEM(values, 0, t);
	}
	pydict_set_item_string_decref(dict_sample, "values", values);
+21 −14
Original line number Diff line number Diff line
@@ -1283,21 +1283,25 @@ static void sample_read__printf(struct perf_sample *sample, u64 read_format)
		       sample->read.time_running);

	if (read_format & PERF_FORMAT_GROUP) {
		u64 i;
		struct sample_read_value *value = sample->read.group.values;

		printf(".... group nr %" PRIu64 "\n", sample->read.group.nr);

		for (i = 0; i < sample->read.group.nr; i++) {
			struct sample_read_value *value;

			value = &sample->read.group.values[i];
		sample_read_group__for_each(value, sample->read.group.nr, read_format) {
			printf("..... id %016" PRIx64
			       ", value %016" PRIx64 "\n",
			       ", value %016" PRIx64,
			       value->id, value->value);
			if (read_format & PERF_FORMAT_LOST)
				printf(", lost %" PRIu64, value->lost);
			printf("\n");
		}
	} else
		printf("..... id %016" PRIx64 ", value %016" PRIx64 "\n",
	} else {
		printf("..... id %016" PRIx64 ", value %016" PRIx64,
			sample->read.one.id, sample->read.one.value);
		if (read_format & PERF_FORMAT_LOST)
			printf(", lost %" PRIu64, sample->read.one.lost);
		printf("\n");
	}
}

static void dump_event(struct evlist *evlist, union perf_event *event,
@@ -1411,6 +1415,9 @@ static void dump_read(struct evsel *evsel, union perf_event *event)

	if (read_format & PERF_FORMAT_ID)
		printf("... id           : %" PRI_lu64 "\n", read_event->id);

	if (read_format & PERF_FORMAT_LOST)
		printf("... lost         : %" PRI_lu64 "\n", read_event->lost);
}

static struct machine *machines__find_for_cpumode(struct machines *machines,
@@ -1479,14 +1486,14 @@ static int deliver_sample_group(struct evlist *evlist,
				struct perf_tool *tool,
				union  perf_event *event,
				struct perf_sample *sample,
				struct machine *machine)
				struct machine *machine,
				u64 read_format)
{
	int ret = -EINVAL;
	u64 i;
	struct sample_read_value *v = sample->read.group.values;

	for (i = 0; i < sample->read.group.nr; i++) {
		ret = deliver_sample_value(evlist, tool, event, sample,
					   &sample->read.group.values[i],
	sample_read_group__for_each(v, sample->read.group.nr, read_format) {
		ret = deliver_sample_value(evlist, tool, event, sample, v,
					   machine);
		if (ret)
			break;
@@ -1510,7 +1517,7 @@ static int evlist__deliver_sample(struct evlist *evlist, struct perf_tool *tool,
	/* For PERF_SAMPLE_READ we have either single or group mode. */
	if (read_format & PERF_FORMAT_GROUP)
		return deliver_sample_group(evlist, tool, event, sample,
					    machine);
					    machine, read_format);
	else
		return deliver_sample_value(evlist, tool, event, sample,
					    &sample->read.one, machine);
Loading