summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorIngo Molnar2017-07-30 11:15:37 +0200
committerIngo Molnar2017-07-30 11:15:37 +0200
commitc3a3800fe46f00ceeeb181cc07cc4fdaed4574f1 (patch)
tree71ce8d6f0b110fd96dcc453494a8771928576696 /tools
parentMerge branch 'perf/urgent' into perf/core, to pick up latest fixes and refres... (diff)
parentperf data: Add doc when no conversion support compiled (diff)
downloadkernel-qcow2-linux-c3a3800fe46f00ceeeb181cc07cc4fdaed4574f1.tar.gz
kernel-qcow2-linux-c3a3800fe46f00ceeeb181cc07cc4fdaed4574f1.tar.xz
kernel-qcow2-linux-c3a3800fe46f00ceeeb181cc07cc4fdaed4574f1.zip
Merge tag 'perf-core-for-mingo-4.14-20170728' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes for 4.14 from Arnaldo Carvalho de Melo: New features: - Add PERF_SAMPLE_CALLCHAIN and PERF_RECORD_MMAP[2] to 'perf data' CTF conversion, allowing CTF trace visualization tools to show callchains and to resolve symbols (Geneviève Bastien) Improvements: - Use group read for event groups in 'perf stat', reducing overhead when groups are defined in the event specification, i.e. when using {} to enclose a list of events, asking them to be read at the same time, e.g.: "perf stat -e '{cycles,instructions}'" (Jiri Olsa) Fixes: - Do not overwrite perf_sample->weight in 'perf annotate' when processing samples, use whatever came from the kernel when perf_event_attr.sample_type has PERF_SAMPLE_WEIGHT set or just handle its default value, 0, when that is not set and "weight" is one of the sort orders chosen (Arnaldo Carvalho de Melo) - 'perf annotate --show-total-period' fixes: - TUI should show period, not nr_samples - Set appropriate column width for period/percent - Fix the column header to show "Period" when when that is what is being asked for (Taeung Song, Arnaldo Carvalho de Melo) - Use default sort if evlist is empty, fixing pipe mode (David Carrillo-Cisneros) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/builtin-annotate.c2
-rw-r--r--tools/perf/builtin-data.c2
-rw-r--r--tools/perf/builtin-stat.c30
-rw-r--r--tools/perf/ui/browsers/annotate.c36
-rw-r--r--tools/perf/util/annotate.c11
-rw-r--r--tools/perf/util/counts.h1
-rw-r--r--tools/perf/util/data-convert-bt.c127
-rw-r--r--tools/perf/util/evlist.h5
-rw-r--r--tools/perf/util/evsel.c139
-rw-r--r--tools/perf/util/evsel.h2
-rw-r--r--tools/perf/util/sort.c2
-rw-r--r--tools/perf/util/stat.c4
-rw-r--r--tools/perf/util/stat.h5
13 files changed, 334 insertions, 32 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 6db782dfce96..658c920d74b9 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -177,8 +177,6 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
*/
process_branch_stack(sample->branch_stack, al, sample);
- sample->weight = 1;
-
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
if (he == NULL)
return -ENOMEM;
diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c
index 0adb5f82335a..46cd8490baf4 100644
--- a/tools/perf/builtin-data.c
+++ b/tools/perf/builtin-data.c
@@ -69,7 +69,7 @@ static int cmd_data_convert(int argc, const char **argv)
};
#ifndef HAVE_LIBBABELTRACE_SUPPORT
- pr_err("No conversion support compiled in.\n");
+ pr_err("No conversion support compiled in. perf should be compiled with environment variables LIBBABELTRACE=1 and LIBBABELTRACE_DIR=/path/to/libbabeltrace/\n");
return -1;
#endif
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 48ac53b199fc..866da7aa54bf 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -213,10 +213,20 @@ static void perf_stat__reset_stats(void)
static int create_perf_stat_counter(struct perf_evsel *evsel)
{
struct perf_event_attr *attr = &evsel->attr;
+ struct perf_evsel *leader = evsel->leader;
- if (stat_config.scale)
+ if (stat_config.scale) {
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
+ }
+
+ /*
+ * The event is part of non trivial group, let's enable
+ * the group read (for leader) and ID retrieval for all
+ * members.
+ */
+ if (leader->nr_members > 1)
+ attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP;
attr->inherit = !no_inherit;
@@ -333,13 +343,21 @@ static int read_counter(struct perf_evsel *counter)
struct perf_counts_values *count;
count = perf_counts(counter->counts, cpu, thread);
- if (perf_evsel__read(counter, cpu, thread, count)) {
+
+ /*
+ * The leader's group read loads data into its group members
+ * (via perf_evsel__read_counter) and sets threir count->loaded.
+ */
+ if (!count->loaded &&
+ perf_evsel__read_counter(counter, cpu, thread)) {
counter->counts->scaled = -1;
perf_counts(counter->counts, cpu, thread)->ena = 0;
perf_counts(counter->counts, cpu, thread)->run = 0;
return -1;
}
+ count->loaded = false;
+
if (STAT_RECORD) {
if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
pr_err("failed to write stat event\n");
@@ -559,6 +577,11 @@ static int store_counter_ids(struct perf_evsel *counter)
return __store_counter_ids(counter, cpus, threads);
}
+static bool perf_evsel__should_store_id(struct perf_evsel *counter)
+{
+ return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID;
+}
+
static int __run_perf_stat(int argc, const char **argv)
{
int interval = stat_config.interval;
@@ -631,7 +654,8 @@ try_again:
if (l > unit_width)
unit_width = l;
- if (STAT_RECORD && store_counter_ids(counter))
+ if (perf_evsel__should_store_id(counter) &&
+ store_counter_ids(counter))
return -1;
}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index dbe4e630b90f..80f38dab9c3a 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -17,8 +17,8 @@
#include <sys/ttydefaults.h>
struct disasm_line_samples {
- double percent;
- u64 nr;
+ double percent;
+ struct sym_hist_entry he;
};
#define IPC_WIDTH 6
@@ -110,11 +110,12 @@ static int annotate_browser__set_jumps_percent_color(struct annotate_browser *br
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
- int w = 7 * ab->nr_events;
+ return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
+}
- if (ab->have_cycles)
- w += IPC_WIDTH + CYCLES_WIDTH;
- return w;
+static int annotate_browser__cycles_width(struct annotate_browser *ab)
+{
+ return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
}
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
@@ -127,7 +128,8 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
(!current_entry || (browser->use_navkeypressed &&
!browser->navkeypressed)));
int width = browser->width, printed;
- int i, pcnt_width = annotate_browser__pcnt_width(ab);
+ int i, pcnt_width = annotate_browser__pcnt_width(ab),
+ cycles_width = annotate_browser__cycles_width(ab);
double percent_max = 0.0;
char bf[256];
bool show_title = false;
@@ -151,8 +153,8 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
bdl->samples[i].percent,
current_entry);
if (annotate_browser__opts.show_total_period) {
- ui_browser__printf(browser, "%6" PRIu64 " ",
- bdl->samples[i].nr);
+ ui_browser__printf(browser, "%11" PRIu64 " ",
+ bdl->samples[i].he.period);
} else {
ui_browser__printf(browser, "%6.2f ",
bdl->samples[i].percent);
@@ -162,9 +164,11 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
ui_browser__set_percent_color(browser, 0, current_entry);
if (!show_title)
- ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
- else
- ui_browser__printf(browser, "%*s", 7, "Percent");
+ ui_browser__write_nstring(browser, " ", pcnt_width);
+ else {
+ ui_browser__printf(browser, "%*s", pcnt_width,
+ annotate_browser__opts.show_total_period ? "Period" : "Percent");
+ }
}
if (ab->have_cycles) {
if (dl->ipc)
@@ -190,7 +194,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
width += 1;
if (!*dl->line)
- ui_browser__write_nstring(browser, " ", width - pcnt_width);
+ ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
else if (dl->offset == -1) {
if (dl->line_nr && annotate_browser__opts.show_linenr)
printed = scnprintf(bf, sizeof(bf), "%-*d ",
@@ -199,7 +203,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
printed = scnprintf(bf, sizeof(bf), "%*s ",
ab->addr_width, " ");
ui_browser__write_nstring(browser, bf, printed);
- ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
+ ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width - cycles_width + 1);
} else {
u64 addr = dl->offset;
int color = -1;
@@ -256,7 +260,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
}
disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
- ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
+ ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
}
if (current_entry)
@@ -457,7 +461,7 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
pos->offset,
next ? next->offset : len,
&path, &sample);
- bpos->samples[i].nr = sample.nr_samples;
+ bpos->samples[i].he = sample;
if (max_percent < bpos->samples[i].percent)
max_percent = bpos->samples[i].percent;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index c2b4b00166ed..2dab0e5a7f2f 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -963,8 +963,9 @@ double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
u64 period = 0;
while (offset < end) {
- hits += h->addr[offset++].nr_samples;
- period += h->addr[offset++].period;
+ hits += h->addr[offset].nr_samples;
+ period += h->addr[offset].period;
+ ++offset;
}
if (h->nr_samples) {
@@ -1142,7 +1143,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
color = get_percent_color(percent);
if (symbol_conf.show_total_period)
- color_fprintf(stdout, color, " %7" PRIu64,
+ color_fprintf(stdout, color, " %11" PRIu64,
sample.period);
else
color_fprintf(stdout, color, " %7.2f", percent);
@@ -1165,7 +1166,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
} else if (max_lines && printed >= max_lines)
return 1;
else {
- int width = 8;
+ int width = symbol_conf.show_total_period ? 12 : 8;
if (queue)
return -1;
@@ -1806,7 +1807,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
int printed = 2, queue_len = 0;
int more = 0;
u64 len;
- int width = 8;
+ int width = symbol_conf.show_total_period ? 12 : 8;
int graph_dotted_len;
filename = strdup(dso->long_name);
diff --git a/tools/perf/util/counts.h b/tools/perf/util/counts.h
index 34d8baaf558a..cb45a6aecf9d 100644
--- a/tools/perf/util/counts.h
+++ b/tools/perf/util/counts.h
@@ -12,6 +12,7 @@ struct perf_counts_values {
};
u64 values[3];
};
+ bool loaded;
};
struct perf_counts {
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 3149b70799fd..2346cecb8ea2 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -76,6 +76,8 @@ struct ctf_writer {
struct bt_ctf_event_class *comm_class;
struct bt_ctf_event_class *exit_class;
struct bt_ctf_event_class *fork_class;
+ struct bt_ctf_event_class *mmap_class;
+ struct bt_ctf_event_class *mmap2_class;
};
struct convert {
@@ -506,6 +508,81 @@ put_len_type:
return ret;
}
+static int
+add_callchain_output_values(struct bt_ctf_event_class *event_class,
+ struct bt_ctf_event *event,
+ struct ip_callchain *callchain)
+{
+ struct bt_ctf_field_type *len_type, *seq_type;
+ struct bt_ctf_field *len_field, *seq_field;
+ unsigned int nr_elements = callchain->nr;
+ unsigned int i;
+ int ret;
+
+ len_type = bt_ctf_event_class_get_field_by_name(
+ event_class, "perf_callchain_size");
+ len_field = bt_ctf_field_create(len_type);
+ if (!len_field) {
+ pr_err("failed to create 'perf_callchain_size' for callchain output event\n");
+ ret = -1;
+ goto put_len_type;
+ }
+
+ ret = bt_ctf_field_unsigned_integer_set_value(len_field, nr_elements);
+ if (ret) {
+ pr_err("failed to set field value for perf_callchain_size\n");
+ goto put_len_field;
+ }
+ ret = bt_ctf_event_set_payload(event, "perf_callchain_size", len_field);
+ if (ret) {
+ pr_err("failed to set payload to perf_callchain_size\n");
+ goto put_len_field;
+ }
+
+ seq_type = bt_ctf_event_class_get_field_by_name(
+ event_class, "perf_callchain");
+ seq_field = bt_ctf_field_create(seq_type);
+ if (!seq_field) {
+ pr_err("failed to create 'perf_callchain' for callchain output event\n");
+ ret = -1;
+ goto put_seq_type;
+ }
+
+ ret = bt_ctf_field_sequence_set_length(seq_field, len_field);
+ if (ret) {
+ pr_err("failed to set length of 'perf_callchain'\n");
+ goto put_seq_field;
+ }
+
+ for (i = 0; i < nr_elements; i++) {
+ struct bt_ctf_field *elem_field =
+ bt_ctf_field_sequence_get_field(seq_field, i);
+
+ ret = bt_ctf_field_unsigned_integer_set_value(elem_field,
+ ((u64 *)(callchain->ips))[i]);
+
+ bt_ctf_field_put(elem_field);
+ if (ret) {
+ pr_err("failed to set callchain[%d]\n", i);
+ goto put_seq_field;
+ }
+ }
+
+ ret = bt_ctf_event_set_payload(event, "perf_callchain", seq_field);
+ if (ret)
+ pr_err("failed to set payload for raw_data\n");
+
+put_seq_field:
+ bt_ctf_field_put(seq_field);
+put_seq_type:
+ bt_ctf_field_type_put(seq_type);
+put_len_field:
+ bt_ctf_field_put(len_field);
+put_len_type:
+ bt_ctf_field_type_put(len_type);
+ return ret;
+}
+
static int add_generic_values(struct ctf_writer *cw,
struct bt_ctf_event *event,
struct perf_evsel *evsel,
@@ -519,7 +596,6 @@ static int add_generic_values(struct ctf_writer *cw,
* PERF_SAMPLE_TIME - not needed as we have it in
* ctf event header
* PERF_SAMPLE_READ - TODO
- * PERF_SAMPLE_CALLCHAIN - TODO
* PERF_SAMPLE_RAW - tracepoint fields are handled separately
* PERF_SAMPLE_BRANCH_STACK - TODO
* PERF_SAMPLE_REGS_USER - TODO
@@ -720,6 +796,7 @@ static int process_sample_event(struct perf_tool *tool,
struct bt_ctf_event_class *event_class;
struct bt_ctf_event *event;
int ret;
+ unsigned long type = evsel->attr.sample_type;
if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
return 0;
@@ -751,6 +828,13 @@ static int process_sample_event(struct perf_tool *tool,
return -1;
}
+ if (type & PERF_SAMPLE_CALLCHAIN) {
+ ret = add_callchain_output_values(event_class,
+ event, sample->callchain);
+ if (ret)
+ return -1;
+ }
+
if (perf_evsel__is_bpf_output(evsel)) {
ret = add_bpf_output_values(event_class, event, sample);
if (ret)
@@ -833,6 +917,18 @@ __FUNC_PROCESS_NON_SAMPLE(exit,
__NON_SAMPLE_SET_FIELD(fork, u32, ptid);
__NON_SAMPLE_SET_FIELD(fork, u64, time);
)
+__FUNC_PROCESS_NON_SAMPLE(mmap,
+ __NON_SAMPLE_SET_FIELD(mmap, u32, pid);
+ __NON_SAMPLE_SET_FIELD(mmap, u32, tid);
+ __NON_SAMPLE_SET_FIELD(mmap, u64_hex, start);
+ __NON_SAMPLE_SET_FIELD(mmap, string, filename);
+)
+__FUNC_PROCESS_NON_SAMPLE(mmap2,
+ __NON_SAMPLE_SET_FIELD(mmap2, u32, pid);
+ __NON_SAMPLE_SET_FIELD(mmap2, u32, tid);
+ __NON_SAMPLE_SET_FIELD(mmap2, u64_hex, start);
+ __NON_SAMPLE_SET_FIELD(mmap2, string, filename);
+)
#undef __NON_SAMPLE_SET_FIELD
#undef __FUNC_PROCESS_NON_SAMPLE
@@ -1043,6 +1139,14 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
if (type & PERF_SAMPLE_TRANSACTION)
ADD_FIELD(event_class, cw->data.u64, "perf_transaction");
+ if (type & PERF_SAMPLE_CALLCHAIN) {
+ ADD_FIELD(event_class, cw->data.u32, "perf_callchain_size");
+ ADD_FIELD(event_class,
+ bt_ctf_field_type_sequence_create(
+ cw->data.u64_hex, "perf_callchain_size"),
+ "perf_callchain");
+ }
+
#undef ADD_FIELD
return 0;
}
@@ -1164,6 +1268,19 @@ __FUNC_ADD_NON_SAMPLE_EVENT_CLASS(exit,
__NON_SAMPLE_ADD_FIELD(u64, time);
)
+__FUNC_ADD_NON_SAMPLE_EVENT_CLASS(mmap,
+ __NON_SAMPLE_ADD_FIELD(u32, pid);
+ __NON_SAMPLE_ADD_FIELD(u32, tid);
+ __NON_SAMPLE_ADD_FIELD(u64_hex, start);
+ __NON_SAMPLE_ADD_FIELD(string, filename);
+)
+
+__FUNC_ADD_NON_SAMPLE_EVENT_CLASS(mmap2,
+ __NON_SAMPLE_ADD_FIELD(u32, pid);
+ __NON_SAMPLE_ADD_FIELD(u32, tid);
+ __NON_SAMPLE_ADD_FIELD(u64_hex, start);
+ __NON_SAMPLE_ADD_FIELD(string, filename);
+)
#undef __NON_SAMPLE_ADD_FIELD
#undef __FUNC_ADD_NON_SAMPLE_EVENT_CLASS
@@ -1181,6 +1298,12 @@ static int setup_non_sample_events(struct ctf_writer *cw,
ret = add_fork_event(cw);
if (ret)
return ret;
+ ret = add_mmap_event(cw);
+ if (ret)
+ return ret;
+ ret = add_mmap2_event(cw);
+ if (ret)
+ return ret;
return 0;
}
@@ -1482,6 +1605,8 @@ int bt_convert__perf2ctf(const char *input, const char *path,
c.tool.comm = process_comm_event;
c.tool.exit = process_exit_event;
c.tool.fork = process_fork_event;
+ c.tool.mmap = process_mmap_event;
+ c.tool.mmap2 = process_mmap2_event;
}
err = perf_config(convert__config, &c);
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 0843746bc389..bf2c4936e35f 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -265,6 +265,11 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist);
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
struct list_head *list);
+static inline bool perf_evlist__empty(struct perf_evlist *evlist)
+{
+ return list_empty(&evlist->entries);
+}
+
static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist)
{
return list_entry(evlist->entries.next, struct perf_evsel, node);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 450b5fadf8cb..3735c9e0080d 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -49,6 +49,7 @@ static struct {
bool clockid_wrong;
bool lbr_flags;
bool write_backward;
+ bool group_read;
} perf_missing_features;
static clockid_t clockid;
@@ -1261,20 +1262,148 @@ void perf_counts_values__scale(struct perf_counts_values *count,
*pscaled = scaled;
}
+static int perf_evsel__read_size(struct perf_evsel *evsel)
+{
+ u64 read_format = evsel->attr.read_format;
+ int entry = sizeof(u64); /* value */
+ int size = 0;
+ int nr = 1;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ size += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ size += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_ID)
+ entry += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_GROUP) {
+ nr = evsel->nr_members;
+ size += sizeof(u64);
+ }
+
+ size += entry * nr;
+ return size;
+}
+
int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
struct perf_counts_values *count)
{
+ size_t size = perf_evsel__read_size(evsel);
+
memset(count, 0, sizeof(*count));
if (FD(evsel, cpu, thread) < 0)
return -EINVAL;
- if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) <= 0)
+ if (readn(FD(evsel, cpu, thread), count->values, size) <= 0)
return -errno;
return 0;
}
+static int
+perf_evsel__read_one(struct perf_evsel *evsel, int cpu, int thread)
+{
+ struct perf_counts_values *count = perf_counts(evsel->counts, cpu, thread);
+
+ return perf_evsel__read(evsel, cpu, thread, count);
+}
+
+static void
+perf_evsel__set_count(struct perf_evsel *counter, int cpu, int thread,
+ u64 val, u64 ena, u64 run)
+{
+ struct perf_counts_values *count;
+
+ count = perf_counts(counter->counts, cpu, thread);
+
+ count->val = val;
+ count->ena = ena;
+ count->run = run;
+ count->loaded = true;
+}
+
+static int
+perf_evsel__process_group_data(struct perf_evsel *leader,
+ int cpu, int thread, u64 *data)
+{
+ u64 read_format = leader->attr.read_format;
+ struct sample_read_value *v;
+ u64 nr, ena = 0, run = 0, i;
+
+ nr = *data++;
+
+ if (nr != (u64) leader->nr_members)
+ return -EINVAL;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ ena = *data++;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ run = *data++;
+
+ v = (struct sample_read_value *) data;
+
+ perf_evsel__set_count(leader, cpu, thread,
+ v[0].value, ena, run);
+
+ for (i = 1; i < nr; i++) {
+ struct perf_evsel *counter;
+
+ counter = perf_evlist__id2evsel(leader->evlist, v[i].id);
+ if (!counter)
+ return -EINVAL;
+
+ perf_evsel__set_count(counter, cpu, thread,
+ v[i].value, ena, run);
+ }
+
+ return 0;
+}
+
+static int
+perf_evsel__read_group(struct perf_evsel *leader, int cpu, int thread)
+{
+ struct perf_stat_evsel *ps = leader->priv;
+ u64 read_format = leader->attr.read_format;
+ int size = perf_evsel__read_size(leader);
+ u64 *data = ps->group_data;
+
+ if (!(read_format & PERF_FORMAT_ID))
+ return -EINVAL;
+
+ if (!perf_evsel__is_group_leader(leader))
+ return -EINVAL;
+
+ if (!data) {
+ data = zalloc(size);
+ if (!data)
+ return -ENOMEM;
+
+ ps->group_data = data;
+ }
+
+ if (FD(leader, cpu, thread) < 0)
+ return -EINVAL;
+
+ if (readn(FD(leader, cpu, thread), data, size) <= 0)
+ return -errno;
+
+ return perf_evsel__process_group_data(leader, cpu, thread, data);
+}
+
+int perf_evsel__read_counter(struct perf_evsel *evsel, int cpu, int thread)
+{
+ u64 read_format = evsel->attr.read_format;
+
+ if (read_format & PERF_FORMAT_GROUP)
+ return perf_evsel__read_group(evsel, cpu, thread);
+ else
+ return perf_evsel__read_one(evsel, cpu, thread);
+}
+
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
int cpu, int thread, bool scale)
{
@@ -1550,6 +1679,8 @@ fallback_missing_features:
if (perf_missing_features.lbr_flags)
evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
PERF_SAMPLE_BRANCH_NO_CYCLES);
+ if (perf_missing_features.group_read && evsel->attr.inherit)
+ evsel->attr.read_format &= ~(PERF_FORMAT_GROUP|PERF_FORMAT_ID);
retry_sample_id:
if (perf_missing_features.sample_id_all)
evsel->attr.sample_id_all = 0;
@@ -1705,6 +1836,12 @@ try_fallback:
perf_missing_features.lbr_flags = true;
pr_debug2("switching off branch sample type no (cycles/flags)\n");
goto fallback_missing_features;
+ } else if (!perf_missing_features.group_read &&
+ evsel->attr.inherit &&
+ (evsel->attr.read_format & PERF_FORMAT_GROUP)) {
+ perf_missing_features.group_read = true;
+ pr_debug2("switching off group read\n");
+ goto fallback_missing_features;
}
out_close:
do {
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index fb40ca3c6519..de03c18daaf0 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -299,6 +299,8 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1,
int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
struct perf_counts_values *count);
+int perf_evsel__read_counter(struct perf_evsel *evsel, int cpu, int thread);
+
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
int cpu, int thread, bool scale);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 8b327c955a4f..12359bd986db 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2563,7 +2563,7 @@ static const char *get_default_sort_order(struct perf_evlist *evlist)
BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
- if (evlist == NULL)
+ if (evlist == NULL || perf_evlist__empty(evlist))
goto out_no_evlist;
evlist__for_each_entry(evlist, evsel) {
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 53b9a994a3dc..35e9848734d6 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -128,6 +128,10 @@ static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
{
+ struct perf_stat_evsel *ps = evsel->priv;
+
+ if (ps)
+ free(ps->group_data);
zfree(&evsel->priv);
}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index 7522bf10b03e..eacaf958e19d 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -28,8 +28,9 @@ enum perf_stat_evsel_id {
};
struct perf_stat_evsel {
- struct stats res_stats[3];
- enum perf_stat_evsel_id id;
+ struct stats res_stats[3];
+ enum perf_stat_evsel_id id;
+ u64 *group_data;
};
enum aggr_mode {