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:
commit
fdca7c1496
@ -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
|
||||
|
@ -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
67
tools/lib/api/fs/cgroup.c
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
83
tools/lib/perf/Documentation/examples/counting.c
Normal file
83
tools/lib/perf/Documentation/examples/counting.c
Normal 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;
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
--------
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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()
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
112
tools/perf/util/expr.c
Normal 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;
|
||||
}
|
@ -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
114
tools/perf/util/expr.l
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user