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:
committed by
Arnaldo Carvalho de Melo
parent
6d395a5135
commit
f52679b788
@@ -86,10 +86,15 @@ static bool samples_same(const struct perf_sample *s1,
|
|||||||
COMP(read.time_running);
|
COMP(read.time_running);
|
||||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||||
if (read_format & PERF_FORMAT_GROUP) {
|
if (read_format & PERF_FORMAT_GROUP) {
|
||||||
for (i = 0; i < s1->read.group.nr; i++)
|
for (i = 0; i < s1->read.group.nr; i++) {
|
||||||
MCOMP(read.group.values[i]);
|
/* FIXME: check values without LOST */
|
||||||
|
if (read_format & PERF_FORMAT_LOST)
|
||||||
|
MCOMP(read.group.values[i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
COMP(read.one.id);
|
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,
|
.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;
|
struct perf_sample sample_out, sample_out_endian;
|
||||||
size_t i, sz, bufsz;
|
size_t i, sz, bufsz;
|
||||||
int err, ret = -1;
|
int err, ret = -1;
|
||||||
@@ -286,6 +291,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
|
|||||||
} else {
|
} else {
|
||||||
sample.read.one.value = 0x08789faeb786aa87ULL;
|
sample.read.one.value = 0x08789faeb786aa87ULL;
|
||||||
sample.read.one.id = 99;
|
sample.read.one.id = 99;
|
||||||
|
sample.read.one.lost = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sz = perf_event__sample_event_size(&sample, sample_type, read_format);
|
sz = perf_event__sample_event_size(&sample, sample_type, read_format);
|
||||||
@@ -370,7 +376,7 @@ out_free:
|
|||||||
*/
|
*/
|
||||||
static int test__sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
|
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_type;
|
||||||
u64 sample_regs;
|
u64 sample_regs;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ struct stack_dump {
|
|||||||
|
|
||||||
struct sample_read_value {
|
struct sample_read_value {
|
||||||
u64 value;
|
u64 value;
|
||||||
u64 id;
|
u64 id; /* only if PERF_FORMAT_ID */
|
||||||
|
u64 lost; /* only if PERF_FORMAT_LOST */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sample_read {
|
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 {
|
struct ip_callchain {
|
||||||
u64 nr;
|
u64 nr;
|
||||||
u64 ips[];
|
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,
|
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;
|
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->val = val;
|
||||||
count->ena = ena;
|
count->ena = ena;
|
||||||
count->run = run;
|
count->run = run;
|
||||||
|
count->lost = lost;
|
||||||
|
|
||||||
perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true);
|
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;
|
u64 read_format = leader->core.attr.read_format;
|
||||||
struct sample_read_value *v;
|
struct sample_read_value *v;
|
||||||
u64 nr, ena = 0, run = 0, i;
|
u64 nr, ena = 0, run = 0, lost = 0;
|
||||||
|
|
||||||
nr = *data++;
|
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)
|
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
|
||||||
run = *data++;
|
run = *data++;
|
||||||
|
|
||||||
v = (struct sample_read_value *) data;
|
v = (void *)data;
|
||||||
|
sample_read_group__for_each(v, nr, read_format) {
|
||||||
evsel__set_count(leader, cpu_map_idx, thread, v[0].value, ena, run);
|
|
||||||
|
|
||||||
for (i = 1; i < nr; i++) {
|
|
||||||
struct evsel *counter;
|
struct evsel *counter;
|
||||||
|
|
||||||
counter = evlist__id2evsel(leader->evlist, v[i].id);
|
counter = evlist__id2evsel(leader->evlist, v->id);
|
||||||
if (!counter)
|
if (!counter)
|
||||||
return -EINVAL;
|
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;
|
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)
|
if (data->read.group.nr > max_group_nr)
|
||||||
return -EFAULT;
|
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);
|
OVERFLOW_CHECK(array, sz, max_size);
|
||||||
data->read.group.values =
|
data->read.group.values =
|
||||||
(struct sample_read_value *)array;
|
(struct sample_read_value *)array;
|
||||||
@@ -2485,6 +2486,12 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
|
|||||||
OVERFLOW_CHECK_u64(array);
|
OVERFLOW_CHECK_u64(array);
|
||||||
data->read.one.id = *array;
|
data->read.one.id = *array;
|
||||||
array++;
|
array++;
|
||||||
|
|
||||||
|
if (read_format & PERF_FORMAT_LOST) {
|
||||||
|
OVERFLOW_CHECK_u64(array);
|
||||||
|
data->read.one.lost = *array;
|
||||||
|
array++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -642,15 +642,19 @@ exit:
|
|||||||
return pylist;
|
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;
|
PyObject *t;
|
||||||
|
|
||||||
t = PyTuple_New(2);
|
t = PyTuple_New(3);
|
||||||
if (!t)
|
if (!t)
|
||||||
Py_FatalError("couldn't create Python tuple");
|
Py_FatalError("couldn't create Python tuple");
|
||||||
PyTuple_SetItem(t, 0, PyLong_FromUnsignedLongLong(value->id));
|
PyTuple_SetItem(t, 0, PyLong_FromUnsignedLongLong(value->id));
|
||||||
PyTuple_SetItem(t, 1, PyLong_FromUnsignedLongLong(value->value));
|
PyTuple_SetItem(t, 1, PyLong_FromUnsignedLongLong(value->value));
|
||||||
|
if (read_format & PERF_FORMAT_LOST)
|
||||||
|
PyTuple_SetItem(t, 2, PyLong_FromUnsignedLongLong(value->lost));
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,12 +685,17 @@ static void set_sample_read_in_dict(PyObject *dict_sample,
|
|||||||
Py_FatalError("couldn't create Python list");
|
Py_FatalError("couldn't create Python list");
|
||||||
|
|
||||||
if (read_format & PERF_FORMAT_GROUP) {
|
if (read_format & PERF_FORMAT_GROUP) {
|
||||||
for (i = 0; i < sample->read.group.nr; i++) {
|
struct sample_read_value *v = sample->read.group.values;
|
||||||
PyObject *t = get_sample_value_as_tuple(&sample->read.group.values[i]);
|
|
||||||
|
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);
|
PyList_SET_ITEM(values, i, t);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
PyList_SET_ITEM(values, 0, t);
|
||||||
}
|
}
|
||||||
pydict_set_item_string_decref(dict_sample, "values", values);
|
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);
|
sample->read.time_running);
|
||||||
|
|
||||||
if (read_format & PERF_FORMAT_GROUP) {
|
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);
|
printf(".... group nr %" PRIu64 "\n", sample->read.group.nr);
|
||||||
|
|
||||||
for (i = 0; i < sample->read.group.nr; i++) {
|
sample_read_group__for_each(value, sample->read.group.nr, read_format) {
|
||||||
struct sample_read_value *value;
|
|
||||||
|
|
||||||
value = &sample->read.group.values[i];
|
|
||||||
printf("..... id %016" PRIx64
|
printf("..... id %016" PRIx64
|
||||||
", value %016" PRIx64 "\n",
|
", value %016" PRIx64,
|
||||||
value->id, value->value);
|
value->id, value->value);
|
||||||
|
if (read_format & PERF_FORMAT_LOST)
|
||||||
|
printf(", lost %" PRIu64, value->lost);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
printf("..... id %016" PRIx64 ", value %016" PRIx64 "\n",
|
printf("..... id %016" PRIx64 ", value %016" PRIx64,
|
||||||
sample->read.one.id, sample->read.one.value);
|
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,
|
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)
|
if (read_format & PERF_FORMAT_ID)
|
||||||
printf("... id : %" PRI_lu64 "\n", read_event->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,
|
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,
|
struct perf_tool *tool,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
struct machine *machine)
|
struct machine *machine,
|
||||||
|
u64 read_format)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
u64 i;
|
struct sample_read_value *v = sample->read.group.values;
|
||||||
|
|
||||||
for (i = 0; i < sample->read.group.nr; i++) {
|
sample_read_group__for_each(v, sample->read.group.nr, read_format) {
|
||||||
ret = deliver_sample_value(evlist, tool, event, sample,
|
ret = deliver_sample_value(evlist, tool, event, sample, v,
|
||||||
&sample->read.group.values[i],
|
|
||||||
machine);
|
machine);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
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. */
|
/* For PERF_SAMPLE_READ we have either single or group mode. */
|
||||||
if (read_format & PERF_FORMAT_GROUP)
|
if (read_format & PERF_FORMAT_GROUP)
|
||||||
return deliver_sample_group(evlist, tool, event, sample,
|
return deliver_sample_group(evlist, tool, event, sample,
|
||||||
machine);
|
machine, read_format);
|
||||||
else
|
else
|
||||||
return deliver_sample_value(evlist, tool, event, sample,
|
return deliver_sample_value(evlist, tool, event, sample,
|
||||||
&sample->read.one, machine);
|
&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);
|
result += sizeof(u64);
|
||||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||||
if (read_format & PERF_FORMAT_GROUP) {
|
if (read_format & PERF_FORMAT_GROUP) {
|
||||||
sz = sample->read.group.nr *
|
sz = sample_read_value_size(read_format);
|
||||||
sizeof(struct sample_read_value);
|
result += sz * sample->read.group.nr;
|
||||||
result += sz;
|
|
||||||
} else {
|
} else {
|
||||||
result += sizeof(u64);
|
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;
|
*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,
|
int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format,
|
||||||
const struct perf_sample *sample)
|
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 */
|
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||||
if (read_format & PERF_FORMAT_GROUP) {
|
if (read_format & PERF_FORMAT_GROUP) {
|
||||||
sz = sample->read.group.nr *
|
array = copy_read_group_values(array, read_format,
|
||||||
sizeof(struct sample_read_value);
|
sample);
|
||||||
memcpy(array, sample->read.group.values, sz);
|
|
||||||
array = (void *)array + sz;
|
|
||||||
} else {
|
} else {
|
||||||
*array = sample->read.one.id;
|
*array = sample->read.one.id;
|
||||||
array++;
|
array++;
|
||||||
|
|
||||||
|
if (read_format & PERF_FORMAT_LOST) {
|
||||||
|
*array = sample->read.one.lost;
|
||||||
|
array++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user