mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
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: Namhyung Kim <namhyung@kernel.org> Acked-by: Jiri 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: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
6d395a5135
commit
f52679b788
@ -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++)
|
||||
MCOMP(read.group.values[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;
|
||||
|
@ -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[];
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -1429,11 +1429,12 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||
result += sizeof(u64);
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
sz = sample->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
result += sz;
|
||||
sz = sample_read_value_size(read_format);
|
||||
result += sz * sample->read.group.nr;
|
||||
} else {
|
||||
result += sizeof(u64);
|
||||
if (read_format & PERF_FORMAT_LOST)
|
||||
result += sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,6 +1519,20 @@ void __weak arch_perf_synthesize_sample_weight(const struct perf_sample *data,
|
||||
*array = data->weight;
|
||||
}
|
||||
|
||||
static __u64 *copy_read_group_values(__u64 *array, __u64 read_format,
|
||||
const struct perf_sample *sample)
|
||||
{
|
||||
size_t sz = sample_read_value_size(read_format);
|
||||
struct sample_read_value *v = sample->read.group.values;
|
||||
|
||||
sample_read_group__for_each(v, sample->read.group.nr, read_format) {
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
memcpy(array, v, sz);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format,
|
||||
const struct perf_sample *sample)
|
||||
{
|
||||
@ -1599,13 +1614,16 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo
|
||||
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
sz = sample->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
memcpy(array, sample->read.group.values, sz);
|
||||
array = (void *)array + sz;
|
||||
array = copy_read_group_values(array, read_format,
|
||||
sample);
|
||||
} else {
|
||||
*array = sample->read.one.id;
|
||||
array++;
|
||||
|
||||
if (read_format & PERF_FORMAT_LOST) {
|
||||
*array = sample->read.one.lost;
|
||||
array++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user