perf/core improvements and fixes:

perf stat:
 
   Jin Yao:
 
   - Show percore counts in per CPU output.
 
 perf report:
 
   Jin Yao:
 
   - Allow selecting which block info columns to report and its order.
 
   - Support color ops to print block percents in color.
 
   - Fix wrong block address comparison in block_info__cmp().
 
 perf annotate:
 
   Ravi Bangoria:
 
   - Get rid of annotation->nr_jumps, unused.
 
 expr:
 
   Jiri Olsa:
 
   - Move expr lexer to flex.
 
 llvm:
 
   Arnaldo Carvalho de Melo:
 
   - Add debug hint message about missing kernel-devel package.
 
 core:
 
   Kan Liang:
 
   - Initial patches to support the recently added PERF_SAMPLE_BRANCH_HW_INDEX
     kernel feature.
 
   - Add check for unexpected use of reserved membrs in event attr, so that in
     the future older perf tools will complain instead of silently try to process
     unknown features.
 
 libapi:
 
   Namhyung Kim:
 
   - Adopt cgroupsfs_find_mountpoint() from tools/perf/util/.
 
 libperf:
 
   Michael Petlan:
 
   - Add counting example.
 
 libtraceevent:
 
    Steven Rostedt (VMware):
 
   - Remove extra '\n' in print_event_time().
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCXmd2OgAKCRCyPKLppCJ+
 Jy9FAQDYysNcAnmy6r2XtzGFB7Cprh+YAAt5kVJVUVTE/W4PMQD+N03Z1j7X/O7P
 1WMCmsnUJzvvpOAP7BK4t/RHJtUzXgw=
 =uWa9
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo-5.7-20200310' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

perf stat:

  Jin Yao:

  - Show percore counts in per CPU output.

perf report:

  Jin Yao:

  - Allow selecting which block info columns to report and its order.

  - Support color ops to print block percents in color.

  - Fix wrong block address comparison in block_info__cmp().

perf annotate:

  Ravi Bangoria:

  - Get rid of annotation->nr_jumps, unused.

expr:

  Jiri Olsa:

  - Move expr lexer to flex.

llvm:

  Arnaldo Carvalho de Melo:

  - Add debug hint message about missing kernel-devel package.

core:

  Kan Liang:

  - Initial patches to support the recently added PERF_SAMPLE_BRANCH_HW_INDEX
    kernel feature.

  - Add check for unexpected use of reserved membrs in event attr, so that in
    the future older perf tools will complain instead of silently try to process
    unknown features.

libapi:

  Namhyung Kim:

  - Adopt cgroupsfs_find_mountpoint() from tools/perf/util/.

libperf:

  Michael Petlan:

  - Add counting example.

libtraceevent:

   Steven Rostedt (VMware):

  - Remove extra '\n' in print_event_time().

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2020-03-19 15:01:07 +01:00
commit fdca7c1496
41 changed files with 755 additions and 382 deletions

View File

@ -181,6 +181,8 @@ enum perf_branch_sample_type_shift {
PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */
PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */
PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
};
@ -208,6 +210,8 @@ enum perf_branch_sample_type {
PERF_SAMPLE_BRANCH_TYPE_SAVE =
1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT,
PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT,
PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
};
@ -853,7 +857,9 @@ enum perf_event_type {
* char data[size];}&& PERF_SAMPLE_RAW
*
* { u64 nr;
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
* { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX
* { u64 from, to, flags } lbr[nr];
* } && PERF_SAMPLE_BRANCH_STACK
*
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER

View File

@ -1,2 +1,3 @@
libapi-y += fs.o
libapi-y += tracing_path.o
libapi-y += cgroup.o

67
tools/lib/api/fs/cgroup.c Normal file
View File

@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/stringify.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fs.h"
int cgroupfs_find_mountpoint(char *buf, size_t maxlen, const char *subsys)
{
FILE *fp;
char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
char *token, *saved_ptr = NULL;
fp = fopen("/proc/mounts", "r");
if (!fp)
return -1;
/*
* in order to handle split hierarchy, we need to scan /proc/mounts
* and inspect every cgroupfs mount point to find one that has
* perf_event subsystem
*/
path_v1[0] = '\0';
path_v2[0] = '\0';
while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
__stringify(PATH_MAX)"s %*d %*d\n",
mountpoint, type, tokens) == 3) {
if (!path_v1[0] && !strcmp(type, "cgroup")) {
token = strtok_r(tokens, ",", &saved_ptr);
while (token != NULL) {
if (subsys && !strcmp(token, subsys)) {
strcpy(path_v1, mountpoint);
break;
}
token = strtok_r(NULL, ",", &saved_ptr);
}
}
if (!path_v2[0] && !strcmp(type, "cgroup2"))
strcpy(path_v2, mountpoint);
if (path_v1[0] && path_v2[0])
break;
}
fclose(fp);
if (path_v1[0])
path = path_v1;
else if (path_v2[0])
path = path_v2;
else
return -1;
if (strlen(path) < maxlen) {
strcpy(buf, path);
return 0;
}
return -1;
}

View File

@ -28,6 +28,8 @@ FS(bpf_fs)
#undef FS
int cgroupfs_find_mountpoint(char *buf, size_t maxlen, const char *subsys);
int filename__read_int(const char *filename, int *value);
int filename__read_ull(const char *filename, unsigned long long *value);
int filename__read_xll(const char *filename, unsigned long long *value);

View File

@ -0,0 +1,83 @@
#include <linux/perf_event.h>
#include <perf/evlist.h>
#include <perf/evsel.h>
#include <perf/cpumap.h>
#include <perf/threadmap.h>
#include <perf/mmap.h>
#include <perf/core.h>
#include <perf/event.h>
#include <stdio.h>
#include <unistd.h>
static int libperf_print(enum libperf_print_level level,
const char *fmt, va_list ap)
{
return vfprintf(stderr, fmt, ap);
}
int main(int argc, char **argv)
{
int count = 100000, err = 0;
struct perf_evlist *evlist;
struct perf_evsel *evsel;
struct perf_thread_map *threads;
struct perf_counts_values counts;
struct perf_event_attr attr1 = {
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_CPU_CLOCK,
.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING,
.disabled = 1,
};
struct perf_event_attr attr2 = {
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_TASK_CLOCK,
.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING,
.disabled = 1,
};
libperf_init(libperf_print);
threads = perf_thread_map__new_dummy();
if (!threads) {
fprintf(stderr, "failed to create threads\n");
return -1;
}
perf_thread_map__set_pid(threads, 0, 0);
evlist = perf_evlist__new();
if (!evlist) {
fprintf(stderr, "failed to create evlist\n");
goto out_threads;
}
evsel = perf_evsel__new(&attr1);
if (!evsel) {
fprintf(stderr, "failed to create evsel1\n");
goto out_evlist;
}
perf_evlist__add(evlist, evsel);
evsel = perf_evsel__new(&attr2);
if (!evsel) {
fprintf(stderr, "failed to create evsel2\n");
goto out_evlist;
}
perf_evlist__add(evlist, evsel);
perf_evlist__set_maps(evlist, NULL, threads);
err = perf_evlist__open(evlist);
if (err) {
fprintf(stderr, "failed to open evsel\n");
goto out_evlist;
}
perf_evlist__enable(evlist);
while (count--);
perf_evlist__disable(evlist);
perf_evlist__for_each_evsel(evlist, evsel) {
perf_evsel__read(evsel, 0, 0, &counts);
fprintf(stdout, "count %llu, enabled %llu, run %llu\n",
counts.val, counts.ena, counts.run);
}
perf_evlist__close(evlist);
out_evlist:
perf_evlist__delete(evlist);
out_threads:
perf_thread_map__put(threads);
return err;
}

View File

@ -5541,7 +5541,7 @@ static void print_event_time(struct tep_handle *tep, struct trace_seq *s,
if (p10 > 1 && p10 < time)
trace_seq_printf(s, "%5llu.%0*llu", time / p10, prec, time % p10);
else
trace_seq_printf(s, "%12llu\n", time);
trace_seq_printf(s, "%12llu", time);
}
struct print_event_type {

View File

@ -334,6 +334,15 @@ Configure all used events to run in kernel space.
--all-user::
Configure all used events to run in user space.
--percore-show-thread::
The event modifier "percore" has supported to sum up the event counts
for all hardware threads in a core and show the counts per core.
This option with event modifier "percore" enabled also sums up the event
counts for all hardware threads in a core but show the sum counts per
hardware thread. This is essentially a replacement for the any bit and
convenient for post processing.
EXAMPLES
--------

View File

@ -572,29 +572,12 @@ static void init_block_hist(struct block_hist *bh)
bh->valid = true;
}
static int block_pair_cmp(struct hist_entry *a, struct hist_entry *b)
{
struct block_info *bi_a = a->block_info;
struct block_info *bi_b = b->block_info;
int cmp;
if (!bi_a->sym || !bi_b->sym)
return -1;
cmp = strcmp(bi_a->sym->name, bi_b->sym->name);
if ((!cmp) && (bi_a->start == bi_b->start) && (bi_a->end == bi_b->end))
return 0;
return -1;
}
static struct hist_entry *get_block_pair(struct hist_entry *he,
struct hists *hists_pair)
{
struct rb_root_cached *root = hists_pair->entries_in;
struct rb_node *next = rb_first_cached(root);
int cmp;
int64_t cmp;
while (next != NULL) {
struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
@ -602,7 +585,7 @@ static struct hist_entry *get_block_pair(struct hist_entry *he,
next = rb_next(&he_pair->rb_node_in);
cmp = block_pair_cmp(he_pair, he);
cmp = __block_info__cmp(he_pair, he);
if (!cmp)
return he_pair;
}
@ -1312,7 +1295,8 @@ static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
he->ms.sym);
if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
(strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
start_line, end_line, block_he->diff.cycles);
} else {

View File

@ -104,6 +104,7 @@ struct report {
bool symbol_ipc;
bool total_cycles_mode;
struct block_report *block_reports;
int nr_block_reports;
};
static int report__config(const char *var, const char *value, void *cb)
@ -966,8 +967,19 @@ static int __cmd_report(struct report *rep)
report__output_resort(rep);
if (rep->total_cycles_mode) {
int block_hpps[6] = {
PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT,
PERF_HPP_REPORT__BLOCK_LBR_CYCLES,
PERF_HPP_REPORT__BLOCK_CYCLES_PCT,
PERF_HPP_REPORT__BLOCK_AVG_CYCLES,
PERF_HPP_REPORT__BLOCK_RANGE,
PERF_HPP_REPORT__BLOCK_DSO,
};
rep->block_reports = block_info__create_report(session->evlist,
rep->total_cycles);
rep->total_cycles,
block_hpps, 6,
&rep->nr_block_reports);
if (!rep->block_reports)
return -1;
}
@ -1551,8 +1563,11 @@ error:
zfree(&report.ptime_range);
}
if (report.block_reports)
zfree(&report.block_reports);
if (report.block_reports) {
block_info__free_report(report.block_reports,
report.nr_block_reports);
report.block_reports = NULL;
}
zstd_fini(&(session->zstd_data));
perf_session__delete(session);

View File

@ -735,6 +735,7 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample,
struct perf_event_attr *attr, FILE *fp)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
struct addr_location alf, alt;
u64 i, from, to;
int printed = 0;
@ -743,8 +744,8 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample,
return 0;
for (i = 0; i < br->nr; i++) {
from = br->entries[i].from;
to = br->entries[i].to;
from = entries[i].from;
to = entries[i].to;
if (PRINT_FIELD(DSO)) {
memset(&alf, 0, sizeof(alf));
@ -768,10 +769,10 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample,
}
printed += fprintf(fp, "/%c/%c/%c/%d ",
mispred_str( br->entries + i),
br->entries[i].flags.in_tx? 'X' : '-',
br->entries[i].flags.abort? 'A' : '-',
br->entries[i].flags.cycles);
mispred_str(entries + i),
entries[i].flags.in_tx ? 'X' : '-',
entries[i].flags.abort ? 'A' : '-',
entries[i].flags.cycles);
}
return printed;
@ -782,6 +783,7 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample,
struct perf_event_attr *attr, FILE *fp)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
struct addr_location alf, alt;
u64 i, from, to;
int printed = 0;
@ -793,8 +795,8 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample,
memset(&alf, 0, sizeof(alf));
memset(&alt, 0, sizeof(alt));
from = br->entries[i].from;
to = br->entries[i].to;
from = entries[i].from;
to = entries[i].to;
thread__find_symbol_fb(thread, sample->cpumode, from, &alf);
thread__find_symbol_fb(thread, sample->cpumode, to, &alt);
@ -813,10 +815,10 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample,
printed += fprintf(fp, ")");
}
printed += fprintf(fp, "/%c/%c/%c/%d ",
mispred_str( br->entries + i),
br->entries[i].flags.in_tx? 'X' : '-',
br->entries[i].flags.abort? 'A' : '-',
br->entries[i].flags.cycles);
mispred_str(entries + i),
entries[i].flags.in_tx ? 'X' : '-',
entries[i].flags.abort ? 'A' : '-',
entries[i].flags.cycles);
}
return printed;
@ -827,6 +829,7 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample,
struct perf_event_attr *attr, FILE *fp)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
struct addr_location alf, alt;
u64 i, from, to;
int printed = 0;
@ -838,8 +841,8 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample,
memset(&alf, 0, sizeof(alf));
memset(&alt, 0, sizeof(alt));
from = br->entries[i].from;
to = br->entries[i].to;
from = entries[i].from;
to = entries[i].to;
if (thread__find_map_fb(thread, sample->cpumode, from, &alf) &&
!alf.map->dso->adjust_symbols)
@ -862,10 +865,10 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample,
printed += fprintf(fp, ")");
}
printed += fprintf(fp, "/%c/%c/%c/%d ",
mispred_str(br->entries + i),
br->entries[i].flags.in_tx ? 'X' : '-',
br->entries[i].flags.abort ? 'A' : '-',
br->entries[i].flags.cycles);
mispred_str(entries + i),
entries[i].flags.in_tx ? 'X' : '-',
entries[i].flags.abort ? 'A' : '-',
entries[i].flags.cycles);
}
return printed;
@ -1053,6 +1056,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
struct machine *machine, FILE *fp)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
u64 start, end;
int i, insn, len, nr, ilen, printed = 0;
struct perf_insn x;
@ -1073,31 +1077,31 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
printed += fprintf(fp, "%c", '\n');
/* Handle first from jump, of which we don't know the entry. */
len = grab_bb(buffer, br->entries[nr-1].from,
br->entries[nr-1].from,
len = grab_bb(buffer, entries[nr-1].from,
entries[nr-1].from,
machine, thread, &x.is64bit, &x.cpumode, false);
if (len > 0) {
printed += ip__fprintf_sym(br->entries[nr - 1].from, thread,
printed += ip__fprintf_sym(entries[nr - 1].from, thread,
x.cpumode, x.cpu, &lastsym, attr, fp);
printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1],
printed += ip__fprintf_jump(entries[nr - 1].from, &entries[nr - 1],
&x, buffer, len, 0, fp, &total_cycles);
if (PRINT_FIELD(SRCCODE))
printed += print_srccode(thread, x.cpumode, br->entries[nr - 1].from);
printed += print_srccode(thread, x.cpumode, entries[nr - 1].from);
}
/* Print all blocks */
for (i = nr - 2; i >= 0; i--) {
if (br->entries[i].from || br->entries[i].to)
if (entries[i].from || entries[i].to)
pr_debug("%d: %" PRIx64 "-%" PRIx64 "\n", i,
br->entries[i].from,
br->entries[i].to);
start = br->entries[i + 1].to;
end = br->entries[i].from;
entries[i].from,
entries[i].to);
start = entries[i + 1].to;
end = entries[i].from;
len = grab_bb(buffer, start, end, machine, thread, &x.is64bit, &x.cpumode, false);
/* Patch up missing kernel transfers due to ring filters */
if (len == -ENXIO && i > 0) {
end = br->entries[--i].from;
end = entries[--i].from;
pr_debug("\tpatching up to %" PRIx64 "-%" PRIx64 "\n", start, end);
len = grab_bb(buffer, start, end, machine, thread, &x.is64bit, &x.cpumode, false);
}
@ -1110,7 +1114,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp);
if (ip == end) {
printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, ++insn, fp,
printed += ip__fprintf_jump(ip, &entries[i], &x, buffer + off, len - off, ++insn, fp,
&total_cycles);
if (PRINT_FIELD(SRCCODE))
printed += print_srccode(thread, x.cpumode, ip);
@ -1134,9 +1138,9 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
* Hit the branch? In this case we are already done, and the target
* has not been executed yet.
*/
if (br->entries[0].from == sample->ip)
if (entries[0].from == sample->ip)
goto out;
if (br->entries[0].flags.abort)
if (entries[0].flags.abort)
goto out;
/*
@ -1147,7 +1151,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
* between final branch and sample. When this happens just
* continue walking after the last TO until we hit a branch.
*/
start = br->entries[0].to;
start = entries[0].to;
end = sample->ip;
if (end < start) {
/* Missing jump. Scan 128 bytes for the next branch */

View File

@ -929,6 +929,10 @@ static struct option stat_options[] = {
OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user,
"Configure all used events to run in user space.",
PARSE_OPT_EXCLUSIVE),
OPT_BOOLEAN(0, "percore-show-thread", &stat_config.percore_show_thread,
"Use with 'percore' event qualifier to show the event "
"counts of one hardware thread by sum up total hardware "
"threads of same physical core"),
OPT_END()
};

View File

@ -10,7 +10,7 @@ static int test(struct parse_ctx *ctx, const char *e, double val2)
{
double val;
if (expr__parse(&val, ctx, &e))
if (expr__parse(&val, ctx, e))
TEST_ASSERT_VAL("parse test failed", 0);
TEST_ASSERT_VAL("unexpected value", val == val2);
return 0;
@ -44,12 +44,12 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
return ret;
p = "FOO/0";
ret = expr__parse(&val, &ctx, &p);
TEST_ASSERT_VAL("division by zero", ret == 1);
ret = expr__parse(&val, &ctx, p);
TEST_ASSERT_VAL("division by zero", ret == -1);
p = "BAR/";
ret = expr__parse(&val, &ctx, &p);
TEST_ASSERT_VAL("missing operand", ret == 1);
ret = expr__parse(&val, &ctx, p);
TEST_ASSERT_VAL("missing operand", ret == -1);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO", &other, &num_other) == 0);

View File

@ -99,6 +99,7 @@ static bool samples_same(const struct perf_sample *s1,
if (type & PERF_SAMPLE_BRANCH_STACK) {
COMP(branch_stack->nr);
COMP(branch_stack->hw_idx);
for (i = 0; i < s1->branch_stack->nr; i++)
MCOMP(branch_stack->entries[i]);
}
@ -186,7 +187,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
u64 data[64];
} branch_stack = {
/* 1 branch_entry */
.data = {1, 211, 212, 213},
.data = {1, -1ULL, 211, 212, 213},
};
u64 regs[64];
const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL};
@ -208,6 +209,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
.transaction = 112,
.raw_data = (void *)raw_data,
.callchain = &callchain.callchain,
.no_hw_idx = false,
.branch_stack = &branch_stack.branch_stack,
.user_regs = {
.abi = PERF_SAMPLE_REGS_ABI_64,
@ -244,6 +246,9 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
if (sample_type & PERF_SAMPLE_REGS_INTR)
evsel.core.attr.sample_regs_intr = sample_regs;
if (sample_type & PERF_SAMPLE_BRANCH_STACK)
evsel.core.attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX;
for (i = 0; i < sizeof(regs); i++)
*(i + (u8 *)regs) = i & 0xfe;

View File

@ -121,7 +121,9 @@ perf-y += mem-events.o
perf-y += vsprintf.o
perf-y += units.o
perf-y += time-utils.o
perf-y += expr-flex.o
perf-y += expr-bison.o
perf-y += expr.o
perf-y += branch.o
perf-y += mem2node.o
@ -189,9 +191,13 @@ $(OUTPUT)util/parse-events-bison.c: util/parse-events.y
$(call rule_mkdir)
$(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
$(OUTPUT)util/expr-flex.c: util/expr.l $(OUTPUT)util/expr-bison.c
$(call rule_mkdir)
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/expr-flex.h $(PARSER_DEBUG_FLEX) util/expr.l
$(OUTPUT)util/expr-bison.c: util/expr.y
$(call rule_mkdir)
$(Q)$(call echo-cmd,bison)$(BISON) -v util/expr.y -d $(PARSER_DEBUG_BISON) -o $@ -p expr__
$(Q)$(call echo-cmd,bison)$(BISON) -v util/expr.y -d $(PARSER_DEBUG_BISON) -o $@ -p expr_
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
$(call rule_mkdir)
@ -203,12 +209,14 @@ $(OUTPUT)util/pmu-bison.c: util/pmu.y
CFLAGS_parse-events-flex.o += -w
CFLAGS_pmu-flex.o += -w
CFLAGS_expr-flex.o += -w
CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w
CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
CFLAGS_expr-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
$(OUTPUT)util/expr.o: $(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-bison.c
CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
@ -216,6 +224,7 @@ CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET
CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_parse-events.o += -Wno-redundant-decls
CFLAGS_expr.o += -Wno-redundant-decls
CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
$(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE

View File

@ -2611,8 +2611,6 @@ void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
if (++al->jump_sources > notes->max_jump_sources)
notes->max_jump_sources = al->jump_sources;
++notes->nr_jumps;
}
}

View File

@ -279,7 +279,6 @@ struct annotation {
struct annotation_options *options;
struct annotation_line **offsets;
int nr_events;
int nr_jumps;
int max_jump_sources;
int nr_entries;
int nr_asm_entries;

View File

@ -65,8 +65,7 @@ struct block_info *block_info__new(void)
return bi;
}
int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right)
int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
{
struct block_info *bi_l = left->block_info;
struct block_info *bi_r = right->block_info;
@ -74,30 +73,27 @@ int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
if (!bi_l->sym || !bi_r->sym) {
if (!bi_l->sym && !bi_r->sym)
return 0;
return -1;
else if (!bi_l->sym)
return -1;
else
return 1;
}
if (bi_l->sym == bi_r->sym) {
if (bi_l->start == bi_r->start) {
if (bi_l->end == bi_r->end)
return 0;
else
return (int64_t)(bi_r->end - bi_l->end);
} else
return (int64_t)(bi_r->start - bi_l->start);
} else {
cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
if (cmp)
return cmp;
}
if (bi_l->sym->start != bi_r->sym->start)
return (int64_t)(bi_r->sym->start - bi_l->sym->start);
if (bi_l->start != bi_r->start)
return (int64_t)(bi_r->start - bi_l->start);
return (int64_t)(bi_r->sym->end - bi_l->sym->end);
return (int64_t)(bi_r->end - bi_l->end);
}
int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right)
{
return __block_info__cmp(left, right);
}
static void init_block_info(struct block_info *bi, struct symbol *sym,
@ -185,6 +181,17 @@ static int block_column_width(struct perf_hpp_fmt *fmt,
return block_fmt->width;
}
static int color_pct(struct perf_hpp *hpp, int width, double pct)
{
#ifdef HAVE_SLANG_SUPPORT
if (use_browser) {
return __hpp__slsmg_color_printf(hpp, "%*.2f%%",
width - 1, pct);
}
#endif
return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);
}
static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
struct perf_hpp *hpp,
struct hist_entry *he)
@ -192,14 +199,11 @@ static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
struct block_info *bi = he->block_info;
double ratio = 0.0;
char buf[16];
if (block_fmt->total_cycles)
ratio = (double)bi->cycles / (double)block_fmt->total_cycles;
sprintf(buf, "%.2f%%", 100.0 * ratio);
return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
return color_pct(hpp, block_fmt->width, 100.0 * ratio);
}
static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
@ -252,16 +256,13 @@ static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
struct block_info *bi = he->block_info;
double ratio = 0.0;
u64 avg;
char buf[16];
if (block_fmt->block_cycles && bi->num_aggr) {
avg = bi->cycles_aggr / bi->num_aggr;
ratio = (double)avg / (double)block_fmt->block_cycles;
}
sprintf(buf, "%.2f%%", 100.0 * ratio);
return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
return color_pct(hpp, block_fmt->width, 100.0 * ratio);
}
static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
@ -295,7 +296,8 @@ static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
he->ms.sym);
if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
(strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
scnprintf(buf, sizeof(buf), "[%s -> %s]",
start_line, end_line);
} else {
@ -348,7 +350,7 @@ static void hpp_register(struct block_fmt *block_fmt, int idx,
switch (idx) {
case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
fmt->entry = block_total_cycles_pct_entry;
fmt->color = block_total_cycles_pct_entry;
fmt->cmp = block_info__cmp;
fmt->sort = block_total_cycles_pct_sort;
break;
@ -356,7 +358,7 @@ static void hpp_register(struct block_fmt *block_fmt, int idx,
fmt->entry = block_cycles_lbr_entry;
break;
case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
fmt->entry = block_cycles_pct_entry;
fmt->color = block_cycles_pct_entry;
break;
case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
fmt->entry = block_avg_cycles_entry;
@ -376,33 +378,41 @@ static void hpp_register(struct block_fmt *block_fmt, int idx,
}
static void register_block_columns(struct perf_hpp_list *hpp_list,
struct block_fmt *block_fmts)
struct block_fmt *block_fmts,
int *block_hpps, int nr_hpps)
{
for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; i++)
hpp_register(&block_fmts[i], i, hpp_list);
for (int i = 0; i < nr_hpps; i++)
hpp_register(&block_fmts[i], block_hpps[i], hpp_list);
}
static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts)
static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts,
int *block_hpps, int nr_hpps)
{
__hists__init(&bh->block_hists, &bh->block_list);
perf_hpp_list__init(&bh->block_list);
bh->block_list.nr_header_lines = 1;
register_block_columns(&bh->block_list, block_fmts);
register_block_columns(&bh->block_list, block_fmts,
block_hpps, nr_hpps);
perf_hpp_list__register_sort_field(&bh->block_list,
&block_fmts[PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT].fmt);
/* Sort by the first fmt */
perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt);
}
static void process_block_report(struct hists *hists,
struct block_report *block_report,
u64 total_cycles)
static int process_block_report(struct hists *hists,
struct block_report *block_report,
u64 total_cycles, int *block_hpps,
int nr_hpps)
{
struct rb_node *next = rb_first_cached(&hists->entries);
struct block_hist *bh = &block_report->hist;
struct hist_entry *he;
init_block_hist(bh, block_report->fmts);
if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX)
return -1;
block_report->nr_fmts = nr_hpps;
init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps);
while (next) {
he = rb_entry(next, struct hist_entry, rb_node);
@ -411,16 +421,19 @@ static void process_block_report(struct hists *hists,
next = rb_next(&he->rb_node);
}
for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; i++) {
for (int i = 0; i < nr_hpps; i++) {
block_report->fmts[i].total_cycles = total_cycles;
block_report->fmts[i].block_cycles = block_report->cycles;
}
hists__output_resort(&bh->block_hists, NULL);
return 0;
}
struct block_report *block_info__create_report(struct evlist *evlist,
u64 total_cycles)
u64 total_cycles,
int *block_hpps, int nr_hpps,
int *nr_reps)
{
struct block_report *block_reports;
int nr_hists = evlist->core.nr_entries, i = 0;
@ -433,13 +446,23 @@ struct block_report *block_info__create_report(struct evlist *evlist,
evlist__for_each_entry(evlist, pos) {
struct hists *hists = evsel__hists(pos);
process_block_report(hists, &block_reports[i], total_cycles);
process_block_report(hists, &block_reports[i], total_cycles,
block_hpps, nr_hpps);
i++;
}
*nr_reps = nr_hists;
return block_reports;
}
void block_info__free_report(struct block_report *reps, int nr_reps)
{
for (int i = 0; i < nr_reps; i++)
hists__delete_entries(&reps[i].hist.block_hists);
free(reps);
}
int report__browse_block_hists(struct block_hist *bh, float min_percent,
struct evsel *evsel, struct perf_env *env,
struct annotation_options *annotation_opts)
@ -451,13 +474,11 @@ int report__browse_block_hists(struct block_hist *bh, float min_percent,
symbol_conf.report_individual_block = true;
hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
stdout, true);
hists__delete_entries(&bh->block_hists);
return 0;
case 1:
symbol_conf.report_individual_block = true;
ret = block_hists_tui_browse(bh, evsel, min_percent,
env, annotation_opts);
hists__delete_entries(&bh->block_hists);
return ret;
default:
return -1;

View File

@ -45,6 +45,7 @@ struct block_report {
struct block_hist hist;
u64 cycles;
struct block_fmt fmts[PERF_HPP_REPORT__BLOCK_MAX_INDEX];
int nr_fmts;
};
struct block_hist;
@ -61,6 +62,8 @@ static inline void __block_info__zput(struct block_info **bi)
#define block_info__zput(bi) __block_info__zput(&bi)
int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right);
int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right);
@ -68,7 +71,11 @@ int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
u64 *block_cycles_aggr, u64 total_cycles);
struct block_report *block_info__create_report(struct evlist *evlist,
u64 total_cycles);
u64 total_cycles,
int *block_hpps, int nr_hpps,
int *nr_reps);
void block_info__free_report(struct block_report *reps, int nr_reps);
int report__browse_block_hists(struct block_hist *bh, float min_percent,
struct evsel *evsel, struct perf_env *env,

View File

@ -12,6 +12,7 @@
#include <linux/stddef.h>
#include <linux/perf_event.h>
#include <linux/types.h>
#include "event.h"
struct branch_flags {
u64 mispred:1;
@ -39,9 +40,30 @@ struct branch_entry {
struct branch_stack {
u64 nr;
u64 hw_idx;
struct branch_entry entries[0];
};
/*
* The hw_idx is only available when PERF_SAMPLE_BRANCH_HW_INDEX is applied.
* Otherwise, the output format of a sample with branch stack is
* struct branch_stack {
* u64 nr;
* struct branch_entry entries[0];
* }
* Check whether the hw_idx is available,
* and return the corresponding pointer of entries[0].
*/
static inline struct branch_entry *perf_sample__branch_entries(struct perf_sample *sample)
{
u64 *entry = (u64 *)sample->branch_stack;
entry++;
if (sample->no_hw_idx)
return (struct branch_entry *)entry;
return (struct branch_entry *)(++entry);
}
struct branch_type_stat {
bool branch_to;
u64 counts[PERF_BR_MAX];

View File

@ -3,75 +3,16 @@
#include "evsel.h"
#include "cgroup.h"
#include "evlist.h"
#include <linux/stringify.h>
#include <linux/zalloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <api/fs/fs.h>
int nr_cgroups;
static int
cgroupfs_find_mountpoint(char *buf, size_t maxlen)
{
FILE *fp;
char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path;
char *token, *saved_ptr = NULL;
fp = fopen("/proc/mounts", "r");
if (!fp)
return -1;
/*
* in order to handle split hierarchy, we need to scan /proc/mounts
* and inspect every cgroupfs mount point to find one that has
* perf_event subsystem
*/
path_v1[0] = '\0';
path_v2[0] = '\0';
while (fscanf(fp, "%*s %"__stringify(PATH_MAX)"s %"__stringify(PATH_MAX)"s %"
__stringify(PATH_MAX)"s %*d %*d\n",
mountpoint, type, tokens) == 3) {
if (!path_v1[0] && !strcmp(type, "cgroup")) {
token = strtok_r(tokens, ",", &saved_ptr);
while (token != NULL) {
if (!strcmp(token, "perf_event")) {
strcpy(path_v1, mountpoint);
break;
}
token = strtok_r(NULL, ",", &saved_ptr);
}
}
if (!path_v2[0] && !strcmp(type, "cgroup2"))
strcpy(path_v2, mountpoint);
if (path_v1[0] && path_v2[0])
break;
}
fclose(fp);
if (path_v1[0])
path = path_v1;
else if (path_v2[0])
path = path_v2;
else
return -1;
if (strlen(path) < maxlen) {
strcpy(buf, path);
return 0;
}
return -1;
}
static int open_cgroup(const char *name)
{
char path[PATH_MAX + 1];
@ -79,7 +20,7 @@ static int open_cgroup(const char *name)
int fd;
if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
return -1;
scnprintf(path, PATH_MAX, "%s/%s", mnt, name);

View File

@ -1172,6 +1172,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
union perf_event *event = tidq->event_buf;
struct dummy_branch_stack {
u64 nr;
u64 hw_idx;
struct branch_entry entries;
} dummy_bs;
u64 ip;
@ -1202,6 +1203,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
if (etm->synth_opts.last_branch) {
dummy_bs = (struct dummy_branch_stack){
.nr = 1,
.hw_idx = -1ULL,
.entries = {
.from = sample.ip,
.to = sample.addr,

View File

@ -139,6 +139,7 @@ struct perf_sample {
u16 insn_len;
u8 cpumode;
u16 misc;
bool no_hw_idx; /* No hw_idx collected in branch_stack */
char insn[MAX_INSN];
void *raw_data;
struct ip_callchain *callchain;

View File

@ -712,7 +712,8 @@ static void __perf_evsel__config_callchain(struct evsel *evsel,
attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
PERF_SAMPLE_BRANCH_CALL_STACK |
PERF_SAMPLE_BRANCH_NO_CYCLES |
PERF_SAMPLE_BRANCH_NO_FLAGS;
PERF_SAMPLE_BRANCH_NO_FLAGS |
PERF_SAMPLE_BRANCH_HW_INDEX;
}
} else
pr_warning("Cannot use LBR callstack with branch stack. "
@ -763,7 +764,8 @@ perf_evsel__reset_callgraph(struct evsel *evsel,
if (param->record_mode == CALLCHAIN_LBR) {
perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
attr->branch_sample_type &= ~(PERF_SAMPLE_BRANCH_USER |
PERF_SAMPLE_BRANCH_CALL_STACK);
PERF_SAMPLE_BRANCH_CALL_STACK |
PERF_SAMPLE_BRANCH_HW_INDEX);
}
if (param->record_mode == CALLCHAIN_DWARF) {
perf_evsel__reset_sample_bit(evsel, REGS_USER);
@ -1673,6 +1675,8 @@ fallback_missing_features:
evsel->core.attr.ksymbol = 0;
if (perf_missing_features.bpf)
evsel->core.attr.bpf_event = 0;
if (perf_missing_features.branch_hw_idx)
evsel->core.attr.branch_sample_type &= ~PERF_SAMPLE_BRANCH_HW_INDEX;
retry_sample_id:
if (perf_missing_features.sample_id_all)
evsel->core.attr.sample_id_all = 0;
@ -1784,7 +1788,12 @@ try_fallback:
* Must probe features in the order they were added to the
* perf_event_attr interface.
*/
if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) {
if (!perf_missing_features.branch_hw_idx &&
(evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX)) {
perf_missing_features.branch_hw_idx = true;
pr_debug2("switching off branch HW index support\n");
goto fallback_missing_features;
} else if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) {
perf_missing_features.aux_output = true;
pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");
goto out_close;
@ -2169,7 +2178,12 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
if (data->branch_stack->nr > max_branch_nr)
return -EFAULT;
sz = data->branch_stack->nr * sizeof(struct branch_entry);
if (perf_evsel__has_branch_hw_idx(evsel))
sz += sizeof(u64);
else
data->no_hw_idx = true;
OVERFLOW_CHECK(array, sz, max_size);
array = (void *)array + sz;
}

View File

@ -119,6 +119,7 @@ struct perf_missing_features {
bool ksymbol;
bool bpf;
bool aux_output;
bool branch_hw_idx;
};
extern struct perf_missing_features perf_missing_features;
@ -389,6 +390,11 @@ static inline bool perf_evsel__has_branch_callstack(const struct evsel *evsel)
return evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
}
static inline bool perf_evsel__has_branch_hw_idx(const struct evsel *evsel)
{
return evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX;
}
static inline bool evsel__has_callchain(const struct evsel *evsel)
{
return (evsel->core.attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0;

112
tools/perf/util/expr.c Normal file
View File

@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
#include <assert.h>
#include "expr.h"
#include "expr-bison.h"
#define YY_EXTRA_TYPE int
#include "expr-flex.h"
#ifdef PARSER_DEBUG
extern int expr_debug;
#endif
/* Caller must make sure id is allocated */
void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
{
int idx;
assert(ctx->num_ids < MAX_PARSE_ID);
idx = ctx->num_ids++;
ctx->ids[idx].name = name;
ctx->ids[idx].val = val;
}
void expr__ctx_init(struct parse_ctx *ctx)
{
ctx->num_ids = 0;
}
static int
__expr__parse(double *val, struct parse_ctx *ctx, const char *expr,
int start)
{
YY_BUFFER_STATE buffer;
void *scanner;
int ret;
ret = expr_lex_init_extra(start, &scanner);
if (ret)
return ret;
buffer = expr__scan_string(expr, scanner);
#ifdef PARSER_DEBUG
expr_debug = 1;
#endif
ret = expr_parse(val, ctx, scanner);
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
expr_lex_destroy(scanner);
return ret;
}
int expr__parse(double *final_val, struct parse_ctx *ctx, const char *expr)
{
return __expr__parse(final_val, ctx, expr, EXPR_PARSE) ? -1 : 0;
}
static bool
already_seen(const char *val, const char *one, const char **other,
int num_other)
{
int i;
if (one && !strcasecmp(one, val))
return true;
for (i = 0; i < num_other; i++)
if (!strcasecmp(other[i], val))
return true;
return false;
}
int expr__find_other(const char *expr, const char *one, const char ***other,
int *num_other)
{
int err, i = 0, j = 0;
struct parse_ctx ctx;
expr__ctx_init(&ctx);
err = __expr__parse(NULL, &ctx, expr, EXPR_OTHER);
if (err)
return -1;
*other = malloc((ctx.num_ids + 1) * sizeof(char *));
if (!*other)
return -ENOMEM;
for (i = 0, j = 0; i < ctx.num_ids; i++) {
const char *str = ctx.ids[i].name;
if (already_seen(str, one, *other, j))
continue;
str = strdup(str);
if (!str)
goto out;
(*other)[j++] = str;
}
(*other)[j] = NULL;
out:
if (i != ctx.num_ids) {
while (--j)
free((char *) (*other)[i]);
free(*other);
err = -1;
}
*num_other = j;
return err;
}

View File

@ -2,7 +2,7 @@
#ifndef PARSE_CTX_H
#define PARSE_CTX_H 1
#define EXPR_MAX_OTHER 15
#define EXPR_MAX_OTHER 20
#define MAX_PARSE_ID EXPR_MAX_OTHER
struct parse_id {
@ -17,10 +17,8 @@ struct parse_ctx {
void expr__ctx_init(struct parse_ctx *ctx);
void expr__add_id(struct parse_ctx *ctx, const char *id, double val);
#ifndef IN_EXPR_Y
int expr__parse(double *final_val, struct parse_ctx *ctx, const char **pp);
#endif
int expr__find_other(const char *p, const char *one, const char ***other,
int expr__parse(double *final_val, struct parse_ctx *ctx, const char *expr);
int expr__find_other(const char *expr, const char *one, const char ***other,
int *num_other);
#endif

114
tools/perf/util/expr.l Normal file
View File

@ -0,0 +1,114 @@
%option prefix="expr_"
%option reentrant
%option bison-bridge
%{
#include <linux/compiler.h>
#include "expr.h"
#include "expr-bison.h"
char *expr_get_text(yyscan_t yyscanner);
YYSTYPE *expr_get_lval(yyscan_t yyscanner);
static int __value(YYSTYPE *yylval, char *str, int base, int token)
{
u64 num;
errno = 0;
num = strtoull(str, NULL, base);
if (errno)
return EXPR_ERROR;
yylval->num = num;
return token;
}
static int value(yyscan_t scanner, int base)
{
YYSTYPE *yylval = expr_get_lval(scanner);
char *text = expr_get_text(scanner);
return __value(yylval, text, base, NUMBER);
}
/*
* Allow @ instead of / to be able to specify pmu/event/ without
* conflicts with normal division.
*/
static char *normalize(char *str)
{
char *ret = str;
char *dst = str;
while (*str) {
if (*str == '@')
*dst++ = '/';
else if (*str == '\\')
*dst++ = *++str;
else
*dst++ = *str;
str++;
}
*dst = 0x0;
return ret;
}
static int str(yyscan_t scanner, int token)
{
YYSTYPE *yylval = expr_get_lval(scanner);
char *text = expr_get_text(scanner);
yylval->str = normalize(strdup(text));
if (!yylval->str)
return EXPR_ERROR;
yylval->str = normalize(yylval->str);
return token;
}
%}
number [0-9]+
sch [-,=]
spec \\{sch}
sym [0-9a-zA-Z_\.:@]+
symbol {spec}*{sym}*{spec}*{sym}*
%%
{
int start_token;
start_token = parse_events_get_extra(yyscanner);
if (start_token) {
parse_events_set_extra(NULL, yyscanner);
return start_token;
}
}
max { return MAX; }
min { return MIN; }
if { return IF; }
else { return ELSE; }
#smt_on { return SMT_ON; }
{number} { return value(yyscanner, 10); }
{symbol} { return str(yyscanner, ID); }
"|" { return '|'; }
"^" { return '^'; }
"&" { return '&'; }
"-" { return '-'; }
"+" { return '+'; }
"*" { return '*'; }
"/" { return '/'; }
"%" { return '%'; }
"(" { return '('; }
")" { return ')'; }
"," { return ','; }
. { }
%%
int expr_wrap(void *scanner __maybe_unused)
{
return 1;
}

View File

@ -1,31 +1,32 @@
/* Simple expression parser */
%{
#define YYDEBUG 1
#include <stdio.h>
#include "util.h"
#include "util/debug.h"
#include <stdlib.h> // strtod()
#define IN_EXPR_Y 1
#include "expr.h"
#include "smt.h"
#include <assert.h>
#include <string.h>
#define MAXIDLEN 256
%}
%define api.pure full
%parse-param { double *final_val }
%parse-param { struct parse_ctx *ctx }
%parse-param { const char **pp }
%lex-param { const char **pp }
%parse-param {void *scanner}
%lex-param {void* scanner}
%union {
double num;
char id[MAXIDLEN+1];
double num;
char *str;
}
%token EXPR_PARSE EXPR_OTHER EXPR_ERROR
%token <num> NUMBER
%token <id> ID
%token <str> ID
%token MIN MAX IF ELSE SMT_ON
%left MIN MAX IF
%left '|'
@ -37,11 +38,9 @@
%type <num> expr if_expr
%{
static int expr__lex(YYSTYPE *res, const char **pp);
static void expr__error(double *final_val __maybe_unused,
static void expr_error(double *final_val __maybe_unused,
struct parse_ctx *ctx __maybe_unused,
const char **pp __maybe_unused,
void *scanner,
const char *s)
{
pr_debug("%s\n", s);
@ -63,6 +62,27 @@ static int lookup_id(struct parse_ctx *ctx, char *id, double *val)
%}
%%
start:
EXPR_PARSE all_expr
|
EXPR_OTHER all_other
all_other: all_other other
|
other: ID
{
if (ctx->num_ids + 1 >= EXPR_MAX_OTHER) {
pr_err("failed: way too many variables");
YYABORT;
}
ctx->ids[ctx->num_ids++].name = $1;
}
|
MIN | MAX | IF | ELSE | SMT_ON | NUMBER | '|' | '^' | '&' | '-' | '+' | '*' | '/' | '%' | '(' | ')'
all_expr: if_expr { *final_val = $1; }
;
@ -93,146 +113,3 @@ expr: NUMBER
;
%%
static int expr__symbol(YYSTYPE *res, const char *p, const char **pp)
{
char *dst = res->id;
const char *s = p;
if (*p == '#')
*dst++ = *p++;
while (isalnum(*p) || *p == '_' || *p == '.' || *p == ':' || *p == '@' || *p == '\\') {
if (p - s >= MAXIDLEN)
return -1;
/*
* Allow @ instead of / to be able to specify pmu/event/ without
* conflicts with normal division.
*/
if (*p == '@')
*dst++ = '/';
else if (*p == '\\')
*dst++ = *++p;
else
*dst++ = *p;
p++;
}
*dst = 0;
*pp = p;
dst = res->id;
switch (dst[0]) {
case 'm':
if (!strcmp(dst, "min"))
return MIN;
if (!strcmp(dst, "max"))
return MAX;
break;
case 'i':
if (!strcmp(dst, "if"))
return IF;
break;
case 'e':
if (!strcmp(dst, "else"))
return ELSE;
break;
case '#':
if (!strcasecmp(dst, "#smt_on"))
return SMT_ON;
break;
}
return ID;
}
static int expr__lex(YYSTYPE *res, const char **pp)
{
int tok;
const char *s;
const char *p = *pp;
while (isspace(*p))
p++;
s = p;
switch (*p++) {
case '#':
case 'a' ... 'z':
case 'A' ... 'Z':
return expr__symbol(res, p - 1, pp);
case '0' ... '9': case '.':
res->num = strtod(s, (char **)&p);
tok = NUMBER;
break;
default:
tok = *s;
break;
}
*pp = p;
return tok;
}
/* Caller must make sure id is allocated */
void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
{
int idx;
assert(ctx->num_ids < MAX_PARSE_ID);
idx = ctx->num_ids++;
ctx->ids[idx].name = name;
ctx->ids[idx].val = val;
}
void expr__ctx_init(struct parse_ctx *ctx)
{
ctx->num_ids = 0;
}
static bool already_seen(const char *val, const char *one, const char **other,
int num_other)
{
int i;
if (one && !strcasecmp(one, val))
return true;
for (i = 0; i < num_other; i++)
if (!strcasecmp(other[i], val))
return true;
return false;
}
int expr__find_other(const char *p, const char *one, const char ***other,
int *num_otherp)
{
const char *orig = p;
int err = -1;
int num_other;
*other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *));
if (!*other)
return -1;
num_other = 0;
for (;;) {
YYSTYPE val;
int tok = expr__lex(&val, &p);
if (tok == 0) {
err = 0;
break;
}
if (tok == ID && !already_seen(val.id, one, *other, num_other)) {
if (num_other >= EXPR_MAX_OTHER - 1) {
pr_debug("Too many extra events in %s\n", orig);
break;
}
(*other)[num_other] = strdup(val.id);
if (!(*other)[num_other])
return -1;
num_other++;
}
}
(*other)[num_other] = NULL;
*num_otherp = num_other;
if (err) {
*num_otherp = 0;
free(*other);
*other = NULL;
}
return err;
}

View File

@ -1590,6 +1590,40 @@ static void free_event_desc(struct evsel *events)
free(events);
}
static bool perf_attr_check(struct perf_event_attr *attr)
{
if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) {
pr_warning("Reserved bits are set unexpectedly. "
"Please update perf tool.\n");
return false;
}
if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) {
pr_warning("Unknown sample type (0x%llx) is detected. "
"Please update perf tool.\n",
attr->sample_type);
return false;
}
if (attr->read_format & ~(PERF_FORMAT_MAX-1)) {
pr_warning("Unknown read format (0x%llx) is detected. "
"Please update perf tool.\n",
attr->read_format);
return false;
}
if ((attr->sample_type & PERF_SAMPLE_BRANCH_STACK) &&
(attr->branch_sample_type & ~(PERF_SAMPLE_BRANCH_MAX-1))) {
pr_warning("Unknown branch sample type (0x%llx) is detected. "
"Please update perf tool.\n",
attr->branch_sample_type);
return false;
}
return true;
}
static struct evsel *read_event_desc(struct feat_fd *ff)
{
struct evsel *evsel, *events = NULL;
@ -1634,6 +1668,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff)
memcpy(&evsel->core.attr, buf, msz);
if (!perf_attr_check(&evsel->core.attr))
goto error;
if (do_read_u32(ff, &nr))
goto error;

View File

@ -2584,9 +2584,10 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
u64 *total_cycles)
{
struct branch_info *bi;
struct branch_entry *entries = perf_sample__branch_entries(sample);
/* If we have branch cycles always annotate them. */
if (bs && bs->nr && bs->entries[0].flags.cycles) {
if (bs && bs->nr && entries[0].flags.cycles) {
int i;
bi = sample__resolve_bstack(sample, al);

View File

@ -1295,6 +1295,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
struct perf_sample sample = { .ip = 0, };
struct dummy_branch_stack {
u64 nr;
u64 hw_idx;
struct branch_entry entries;
} dummy_bs;
@ -1316,6 +1317,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
if (pt->synth_opts.last_branch && sort__mode == SORT_MODE__BRANCH) {
dummy_bs = (struct dummy_branch_stack){
.nr = 1,
.hw_idx = -1ULL,
.entries = {
.from = sample.ip,
.to = sample.addr,

View File

@ -265,6 +265,8 @@ static int detect_kbuild_dir(char **kbuild_dir)
return -ENOMEM;
return 0;
}
pr_debug("%s: Couldn't find \"%s\", missing kernel-devel package?.\n",
__func__, autoconf_path);
free(autoconf_path);
return -ENOENT;
}

View File

@ -2081,15 +2081,16 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
{
unsigned int i;
const struct branch_stack *bs = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info));
if (!bi)
return NULL;
for (i = 0; i < bs->nr; i++) {
ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to);
ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags;
ip__resolve_ams(al->thread, &bi[i].to, entries[i].to);
ip__resolve_ams(al->thread, &bi[i].from, entries[i].from);
bi[i].flags = entries[i].flags;
}
return bi;
}
@ -2185,6 +2186,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
/* LBR only affects the user callchain */
if (i != chain_nr) {
struct branch_stack *lbr_stack = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
int lbr_nr = lbr_stack->nr, j, k;
bool branch;
struct branch_flags *flags;
@ -2210,31 +2212,29 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
ip = chain->ips[j];
else if (j > i + 1) {
k = j - i - 2;
ip = lbr_stack->entries[k].from;
ip = entries[k].from;
branch = true;
flags = &lbr_stack->entries[k].flags;
flags = &entries[k].flags;
} else {
ip = lbr_stack->entries[0].to;
ip = entries[0].to;
branch = true;
flags = &lbr_stack->entries[0].flags;
branch_from =
lbr_stack->entries[0].from;
flags = &entries[0].flags;
branch_from = entries[0].from;
}
} else {
if (j < lbr_nr) {
k = lbr_nr - j - 1;
ip = lbr_stack->entries[k].from;
ip = entries[k].from;
branch = true;
flags = &lbr_stack->entries[k].flags;
flags = &entries[k].flags;
}
else if (j > lbr_nr)
ip = chain->ips[i + 1 - (j - lbr_nr)];
else {
ip = lbr_stack->entries[0].to;
ip = entries[0].to;
branch = true;
flags = &lbr_stack->entries[0].flags;
branch_from =
lbr_stack->entries[0].from;
flags = &entries[0].flags;
branch_from = entries[0].from;
}
}
@ -2281,6 +2281,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
int max_stack)
{
struct branch_stack *branch = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
struct ip_callchain *chain = sample->callchain;
int chain_nr = 0;
u8 cpumode = PERF_RECORD_MISC_USER;
@ -2328,7 +2329,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
for (i = 0; i < nr; i++) {
if (callchain_param.order == ORDER_CALLEE) {
be[i] = branch->entries[i];
be[i] = entries[i];
if (chain == NULL)
continue;
@ -2347,7 +2348,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
be[i].from >= chain->ips[first_call] - 8)
first_call++;
} else
be[i] = branch->entries[branch->nr - i - 1];
be[i] = entries[branch->nr - i - 1];
}
memset(iter, 0, sizeof(struct iterations) * nr);

View File

@ -431,7 +431,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
if (map && map->dso) {
char *srcline = map__srcline(map, addr, NULL);
if (srcline != SRCLINE_UNKNOWN)
if (strncmp(srcline, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)
ret = fprintf(fp, "%s%s", prefix, srcline);
free_srcline(srcline);
}

View File

@ -50,6 +50,7 @@ static void __p_branch_sample_type(char *buf, size_t size, u64 value)
bit_name(ABORT_TX), bit_name(IN_TX), bit_name(NO_TX),
bit_name(COND), bit_name(CALL_STACK), bit_name(IND_JUMP),
bit_name(CALL), bit_name(NO_FLAGS), bit_name(NO_CYCLES),
bit_name(HW_INDEX),
{ .name = NULL, }
};
#undef bit_name

View File

@ -464,6 +464,7 @@ static PyObject *python_process_brstack(struct perf_sample *sample,
struct thread *thread)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
PyObject *pylist;
u64 i;
@ -484,28 +485,28 @@ static PyObject *python_process_brstack(struct perf_sample *sample,
Py_FatalError("couldn't create Python dictionary");
pydict_set_item_string_decref(pyelem, "from",
PyLong_FromUnsignedLongLong(br->entries[i].from));
PyLong_FromUnsignedLongLong(entries[i].from));
pydict_set_item_string_decref(pyelem, "to",
PyLong_FromUnsignedLongLong(br->entries[i].to));
PyLong_FromUnsignedLongLong(entries[i].to));
pydict_set_item_string_decref(pyelem, "mispred",
PyBool_FromLong(br->entries[i].flags.mispred));
PyBool_FromLong(entries[i].flags.mispred));
pydict_set_item_string_decref(pyelem, "predicted",
PyBool_FromLong(br->entries[i].flags.predicted));
PyBool_FromLong(entries[i].flags.predicted));
pydict_set_item_string_decref(pyelem, "in_tx",
PyBool_FromLong(br->entries[i].flags.in_tx));
PyBool_FromLong(entries[i].flags.in_tx));
pydict_set_item_string_decref(pyelem, "abort",
PyBool_FromLong(br->entries[i].flags.abort));
PyBool_FromLong(entries[i].flags.abort));
pydict_set_item_string_decref(pyelem, "cycles",
PyLong_FromUnsignedLongLong(br->entries[i].flags.cycles));
PyLong_FromUnsignedLongLong(entries[i].flags.cycles));
thread__find_map_fb(thread, sample->cpumode,
br->entries[i].from, &al);
entries[i].from, &al);
dsoname = get_dsoname(al.map);
pydict_set_item_string_decref(pyelem, "from_dsoname",
_PyUnicode_FromString(dsoname));
thread__find_map_fb(thread, sample->cpumode,
br->entries[i].to, &al);
entries[i].to, &al);
dsoname = get_dsoname(al.map);
pydict_set_item_string_decref(pyelem, "to_dsoname",
_PyUnicode_FromString(dsoname));
@ -561,6 +562,7 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample,
struct thread *thread)
{
struct branch_stack *br = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
PyObject *pylist;
u64 i;
char bf[512];
@ -581,22 +583,22 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample,
Py_FatalError("couldn't create Python dictionary");
thread__find_symbol_fb(thread, sample->cpumode,
br->entries[i].from, &al);
entries[i].from, &al);
get_symoff(al.sym, &al, true, bf, sizeof(bf));
pydict_set_item_string_decref(pyelem, "from",
_PyUnicode_FromString(bf));
thread__find_symbol_fb(thread, sample->cpumode,
br->entries[i].to, &al);
entries[i].to, &al);
get_symoff(al.sym, &al, true, bf, sizeof(bf));
pydict_set_item_string_decref(pyelem, "to",
_PyUnicode_FromString(bf));
get_br_mspred(&br->entries[i].flags, bf, sizeof(bf));
get_br_mspred(&entries[i].flags, bf, sizeof(bf));
pydict_set_item_string_decref(pyelem, "pred",
_PyUnicode_FromString(bf));
if (br->entries[i].flags.in_tx) {
if (entries[i].flags.in_tx) {
pydict_set_item_string_decref(pyelem, "in_tx",
_PyUnicode_FromString("X"));
} else {
@ -604,7 +606,7 @@ static PyObject *python_process_brstacksym(struct perf_sample *sample,
_PyUnicode_FromString("-"));
}
if (br->entries[i].flags.abort) {
if (entries[i].flags.abort) {
pydict_set_item_string_decref(pyelem, "abort",
_PyUnicode_FromString("A"));
} else {

View File

@ -1007,6 +1007,7 @@ static void callchain__lbr_callstack_printf(struct perf_sample *sample)
{
struct ip_callchain *callchain = sample->callchain;
struct branch_stack *lbr_stack = sample->branch_stack;
struct branch_entry *entries = perf_sample__branch_entries(sample);
u64 kernel_callchain_nr = callchain->nr;
unsigned int i;
@ -1043,10 +1044,10 @@ static void callchain__lbr_callstack_printf(struct perf_sample *sample)
i, callchain->ips[i]);
printf("..... %2d: %016" PRIx64 "\n",
(int)(kernel_callchain_nr), lbr_stack->entries[0].to);
(int)(kernel_callchain_nr), entries[0].to);
for (i = 0; i < lbr_stack->nr; i++)
printf("..... %2d: %016" PRIx64 "\n",
(int)(i + kernel_callchain_nr + 1), lbr_stack->entries[i].from);
(int)(i + kernel_callchain_nr + 1), entries[i].from);
}
}
@ -1068,6 +1069,7 @@ static void callchain__printf(struct evsel *evsel,
static void branch_stack__printf(struct perf_sample *sample, bool callstack)
{
struct branch_entry *entries = perf_sample__branch_entries(sample);
uint64_t i;
printf("%s: nr:%" PRIu64 "\n",
@ -1075,7 +1077,7 @@ static void branch_stack__printf(struct perf_sample *sample, bool callstack)
sample->branch_stack->nr);
for (i = 0; i < sample->branch_stack->nr; i++) {
struct branch_entry *e = &sample->branch_stack->entries[i];
struct branch_entry *e = &entries[i];
if (!callstack) {
printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x\n",

View File

@ -110,7 +110,7 @@ static void aggr_printout(struct perf_stat_config *config,
config->csv_sep);
break;
case AGGR_NONE:
if (evsel->percore) {
if (evsel->percore && !config->percore_show_thread) {
fprintf(config->output, "S%d-D%d-C%*d%s",
cpu_map__id_to_socket(id),
cpu_map__id_to_die(id),
@ -628,7 +628,7 @@ static void aggr_cb(struct perf_stat_config *config,
static void print_counter_aggrdata(struct perf_stat_config *config,
struct evsel *counter, int s,
char *prefix, bool metric_only,
bool *first)
bool *first, int cpu)
{
struct aggr_data ad;
FILE *output = config->output;
@ -654,7 +654,7 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
fprintf(output, "%s", prefix);
uval = val * counter->scale;
printout(config, id, nr, counter, uval, prefix,
printout(config, cpu != -1 ? cpu : id, nr, counter, uval, prefix,
run, ena, 1.0, &rt_stat);
if (!metric_only)
fputc('\n', output);
@ -687,7 +687,7 @@ static void print_aggr(struct perf_stat_config *config,
evlist__for_each_entry(evlist, counter) {
print_counter_aggrdata(config, counter, s,
prefix, metric_only,
&first);
&first, -1);
}
if (metric_only)
fputc('\n', output);
@ -1146,6 +1146,26 @@ static void print_footer(struct perf_stat_config *config)
"the same PMU. Try reorganizing the group.\n");
}
static void print_percore_thread(struct perf_stat_config *config,
struct evsel *counter, char *prefix)
{
int s, s2, id;
bool first = true;
for (int i = 0; i < perf_evsel__nr_cpus(counter); i++) {
s2 = config->aggr_get_id(config, evsel__cpus(counter), i);
for (s = 0; s < config->aggr_map->nr; s++) {
id = config->aggr_map->map[s];
if (s2 == id)
break;
}
print_counter_aggrdata(config, counter, s,
prefix, false,
&first, i);
}
}
static void print_percore(struct perf_stat_config *config,
struct evsel *counter, char *prefix)
{
@ -1157,13 +1177,16 @@ static void print_percore(struct perf_stat_config *config,
if (!(config->aggr_map || config->aggr_get_id))
return;
if (config->percore_show_thread)
return print_percore_thread(config, counter, prefix);
for (s = 0; s < config->aggr_map->nr; s++) {
if (prefix && metric_only)
fprintf(output, "%s", prefix);
print_counter_aggrdata(config, counter, s,
prefix, metric_only,
&first);
&first, -1);
}
if (metric_only)

View File

@ -777,9 +777,7 @@ static void generic_metric(struct perf_stat_config *config,
}
if (!metric_events[i]) {
const char *p = metric_expr;
if (expr__parse(&ratio, &pctx, &p) == 0) {
if (expr__parse(&ratio, &pctx, metric_expr) == 0) {
char *unit;
char metric_bf[64];

View File

@ -109,6 +109,7 @@ struct perf_stat_config {
bool walltime_run_table;
bool all_kernel;
bool all_user;
bool percore_show_thread;
FILE *output;
unsigned int interval;
unsigned int timeout;

View File

@ -1183,7 +1183,8 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
if (type & PERF_SAMPLE_BRANCH_STACK) {
sz = sample->branch_stack->nr * sizeof(struct branch_entry);
sz += sizeof(u64);
/* nr, hw_idx */
sz += 2 * sizeof(u64);
result += sz;
}
@ -1344,7 +1345,8 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo
if (type & PERF_SAMPLE_BRANCH_STACK) {
sz = sample->branch_stack->nr * sizeof(struct branch_entry);
sz += sizeof(u64);
/* nr, hw_idx */
sz += 2 * sizeof(u64);
memcpy(array, sample->branch_stack, sz);
array = (void *)array + sz;
}