summaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/Build2
-rw-r--r--tools/perf/util/annotate.c340
-rw-r--r--tools/perf/util/annotate.h28
-rw-r--r--tools/perf/util/auxtrace.c350
-rw-r--r--tools/perf/util/auxtrace.h44
-rw-r--r--tools/perf/util/block-info.c477
-rw-r--r--tools/perf/util/block-info.h79
-rw-r--r--tools/perf/util/callchain.c40
-rw-r--r--tools/perf/util/callchain.h5
-rw-r--r--tools/perf/util/copyfile.c8
-rw-r--r--tools/perf/util/cpumap.c18
-rw-r--r--tools/perf/util/cpumap.h3
-rw-r--r--tools/perf/util/cs-etm.c4
-rw-r--r--tools/perf/util/data.c46
-rw-r--r--tools/perf/util/data.h12
-rw-r--r--tools/perf/util/db-export.c16
-rw-r--r--tools/perf/util/debug.c2
-rw-r--r--tools/perf/util/debug.h9
-rw-r--r--tools/perf/util/dso.c159
-rw-r--r--tools/perf/util/dso.h20
-rw-r--r--tools/perf/util/dsos.c97
-rw-r--r--tools/perf/util/dsos.h14
-rw-r--r--tools/perf/util/dwarf-aux.c142
-rw-r--r--tools/perf/util/dwarf-aux.h3
-rw-r--r--tools/perf/util/env.c56
-rw-r--r--tools/perf/util/env.h7
-rw-r--r--tools/perf/util/event.c6
-rw-r--r--tools/perf/util/event.h6
-rw-r--r--tools/perf/util/evlist.c336
-rw-r--r--tools/perf/util/evlist.h13
-rw-r--r--tools/perf/util/evsel.c79
-rw-r--r--tools/perf/util/evsel_config.h13
-rw-r--r--tools/perf/util/evsel_fprintf.c29
-rw-r--r--tools/perf/util/header.c4
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c73
-rw-r--r--tools/perf/util/hist.h18
-rw-r--r--tools/perf/util/intel-pt.c109
-rw-r--r--tools/perf/util/jitdump.c6
-rw-r--r--tools/perf/util/llvm-utils.c11
-rw-r--r--tools/perf/util/machine.c125
-rw-r--r--tools/perf/util/machine.h4
-rw-r--r--tools/perf/util/map.c181
-rw-r--r--tools/perf/util/map.h17
-rw-r--r--tools/perf/util/map_groups.h31
-rw-r--r--tools/perf/util/map_symbol.h5
-rw-r--r--tools/perf/util/mem-events.c2
-rw-r--r--tools/perf/util/metricgroup.c2
-rw-r--r--tools/perf/util/mmap.c260
-rw-r--r--tools/perf/util/mmap.h28
-rw-r--r--tools/perf/util/parse-events.c308
-rw-r--r--tools/perf/util/parse-events.h10
-rw-r--r--tools/perf/util/parse-events.l1
-rw-r--r--tools/perf/util/parse-events.y391
-rw-r--r--tools/perf/util/parse-regs-options.c8
-rw-r--r--tools/perf/util/perf_event_attr_fprintf.c3
-rw-r--r--tools/perf/util/perf_regs.c4
-rw-r--r--tools/perf/util/perf_regs.h4
-rw-r--r--tools/perf/util/pmu.c59
-rw-r--r--tools/perf/util/pmu.h6
-rw-r--r--tools/perf/util/probe-event.c21
-rw-r--r--tools/perf/util/probe-event.h3
-rw-r--r--tools/perf/util/probe-file.c14
-rw-r--r--tools/perf/util/probe-file.h2
-rw-r--r--tools/perf/util/probe-finder.c193
-rw-r--r--tools/perf/util/probe-finder.h1
-rw-r--r--tools/perf/util/python.c14
-rw-r--r--tools/perf/util/record.c31
-rw-r--r--tools/perf/util/record.h3
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c24
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c27
-rw-r--r--tools/perf/util/session.c119
-rw-r--r--tools/perf/util/session.h11
-rw-r--r--tools/perf/util/sort.c113
-rw-r--r--tools/perf/util/sort.h4
-rw-r--r--tools/perf/util/spark.c34
-rw-r--r--tools/perf/util/spark.h8
-rw-r--r--tools/perf/util/stat-display.c15
-rw-r--r--tools/perf/util/stat.c11
-rw-r--r--tools/perf/util/stat.h3
-rw-r--r--tools/perf/util/string2.h3
-rw-r--r--tools/perf/util/symbol-elf.c2
-rw-r--r--tools/perf/util/symbol.c160
-rw-r--r--tools/perf/util/symbol.h26
-rw-r--r--tools/perf/util/symbol_conf.h1
-rw-r--r--tools/perf/util/synthetic-events.c14
-rw-r--r--tools/perf/util/thread.c2
-rw-r--r--tools/perf/util/time-utils.c27
-rw-r--r--tools/perf/util/time-utils.h5
-rw-r--r--tools/perf/util/trace-event-parse.c31
-rw-r--r--tools/perf/util/trace-event.h2
-rw-r--r--tools/perf/util/unwind-libdw.c7
-rw-r--r--tools/perf/util/unwind-libunwind-local.c7
-rw-r--r--tools/perf/util/unwind.h8
-rw-r--r--tools/perf/util/util.c25
-rw-r--r--tools/perf/util/vdso.c4
96 files changed, 3546 insertions, 1566 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8dcfca1a882f..b8e05a147b2b 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,4 +1,5 @@
perf-y += annotate.o
+perf-y += block-info.o
perf-y += block-range.o
perf-y += build-id.o
perf-y += cacheline.o
@@ -95,6 +96,7 @@ perf-y += cloexec.o
perf-y += call-path.o
perf-y += rwsem.o
perf-y += thread-stack.o
+perf-y += spark.o
perf-$(CONFIG_AUXTRACE) += auxtrace.o
perf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
perf-$(CONFIG_AUXTRACE) += intel-pt.o
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index e830eadfca2a..5ea9a4534848 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -43,6 +43,7 @@
#include <linux/string.h>
#include <bpf/libbpf.h>
#include <subcmd/parse-options.h>
+#include <subcmd/run-command.h>
/* FIXME: For the HE_COLORSET */
#include "ui/browser.h"
@@ -242,7 +243,7 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_s
char *endptr, *tok, *name;
struct map *map = ms->map;
struct addr_map_symbol target = {
- .map = map,
+ .ms = { .map = map, },
};
ops->target.addr = strtoull(ops->raw, &endptr, 16);
@@ -270,9 +271,9 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_s
find_target:
target.addr = map__objdump_2mem(map, ops->target.addr);
- if (map_groups__find_ams(&target) == 0 &&
- map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.sym;
+ if (map_groups__find_ams(ms->mg, &target) == 0 &&
+ map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)
+ ops->target.sym = target.ms.sym;
return 0;
@@ -331,7 +332,7 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
struct map *map = ms->map;
struct symbol *sym = ms->sym;
struct addr_map_symbol target = {
- .map = map,
+ .ms = { .map = map, },
};
const char *c = strchr(ops->raw, ',');
u64 start, end;
@@ -390,9 +391,9 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
* Actual navigation will come next, with further understanding of how
* the symbol searching and disassembly should be done.
*/
- if (map_groups__find_ams(&target) == 0 &&
- map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.sym;
+ if (map_groups__find_ams(ms->mg, &target) == 0 &&
+ map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)
+ ops->target.sym = target.ms.sym;
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
@@ -853,6 +854,10 @@ static int __symbol__account_cycles(struct cyc_hist *ch,
ch[offset].start < start)
return 0;
}
+
+ if (ch[offset].num < NUM_SPARKS)
+ ch[offset].cycles_spark[ch[offset].num] = cycles;
+
ch[offset].have_start = have_start;
ch[offset].start = start;
ch[offset].cycles += cycles;
@@ -860,14 +865,15 @@ static int __symbol__account_cycles(struct cyc_hist *ch,
return 0;
}
-static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
+static int __symbol__inc_addr_samples(struct map_symbol *ms,
struct annotated_source *src, int evidx, u64 addr,
struct perf_sample *sample)
{
+ struct symbol *sym = ms->sym;
unsigned offset;
struct sym_hist *h;
- pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
+ pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, ms->map->unmap_ip(ms->map, addr));
if ((addr < sym->start || addr >= sym->end) &&
(addr != sym->end || sym->start != sym->end)) {
@@ -934,17 +940,17 @@ alloc_histograms:
return notes->src;
}
-static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
+static int symbol__inc_addr_samples(struct map_symbol *ms,
struct evsel *evsel, u64 addr,
struct perf_sample *sample)
{
+ struct symbol *sym = ms->sym;
struct annotated_source *src;
if (sym == NULL)
return 0;
src = symbol__hists(sym, evsel->evlist->core.nr_entries);
- return (src) ? __symbol__inc_addr_samples(sym, map, src, evsel->idx,
- addr, sample) : 0;
+ return src ? __symbol__inc_addr_samples(ms, src, evsel->idx, addr, sample) : 0;
}
static int symbol__account_cycles(u64 addr, u64 start,
@@ -992,17 +998,17 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
* it starts on the function start.
*/
if (start &&
- (start->sym == ams->sym ||
- (ams->sym &&
- start->addr == ams->sym->start + ams->map->start)))
+ (start->ms.sym == ams->ms.sym ||
+ (ams->ms.sym &&
+ start->addr == ams->ms.sym->start + ams->ms.map->start)))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",
ams->addr,
start ? start->addr : 0,
- ams->sym ? ams->sym->start + ams->map->start : 0,
+ ams->ms.sym ? ams->ms.sym->start + ams->ms.map->start : 0,
saddr);
- err = symbol__account_cycles(ams->al_addr, saddr, ams->sym, cycles);
+ err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles);
if (err)
pr_debug2("account_cycles failed %d\n", err);
return err;
@@ -1088,13 +1094,13 @@ void annotation__compute_ipc(struct annotation *notes, size_t size)
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
struct evsel *evsel)
{
- return symbol__inc_addr_samples(ams->sym, ams->map, evsel, ams->al_addr, sample);
+ return symbol__inc_addr_samples(&ams->ms, evsel, ams->al_addr, sample);
}
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
struct evsel *evsel, u64 ip)
{
- return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evsel, ip, sample);
+ return symbol__inc_addr_samples(&he->ms, evsel, ip, sample);
}
static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms)
@@ -1485,44 +1491,26 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
* means that it's not a disassembly line so should be treated differently.
* The ops.raw part will be parsed further according to type of the instruction.
*/
-static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
+static int symbol__parse_objdump_line(struct symbol *sym,
struct annotate_args *args,
- int *line_nr)
+ char *parsed_line, int *line_nr)
{
struct map *map = args->ms.map;
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *dl;
- char *line = NULL, *parsed_line, *tmp, *tmp2;
- size_t line_len;
+ char *tmp;
s64 line_ip, offset = -1;
regmatch_t match[2];
- if (getline(&line, &line_len, file) < 0)
- return -1;
-
- if (!line)
- return -1;
-
- line_ip = -1;
- parsed_line = strim(line);
-
/* /filename:linenr ? Save line number and ignore. */
if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) {
*line_nr = atoi(parsed_line + match[1].rm_so);
return 0;
}
- tmp = skip_spaces(parsed_line);
- if (*tmp) {
- /*
- * Parse hexa addresses followed by ':'
- */
- line_ip = strtoull(tmp, &tmp2, 16);
- if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
- line_ip = -1;
- }
-
- if (line_ip != -1) {
+ /* Process hex address followed by ':'. */
+ line_ip = strtoull(parsed_line, &tmp, 16);
+ if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {
u64 start = map__rip_2objdump(map, sym->start),
end = map__rip_2objdump(map, sym->end);
@@ -1530,7 +1518,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
if ((u64)line_ip < start || (u64)line_ip >= end)
offset = -1;
else
- parsed_line = tmp2 + 1;
+ parsed_line = tmp + 1;
}
args->offset = offset;
@@ -1539,7 +1527,6 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
args->ms.sym = sym;
dl = disasm_line__new(args);
- free(line);
(*line_nr)++;
if (dl == NULL)
@@ -1554,13 +1541,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
/* kcore has no symbols, so add the call target symbol */
if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) {
struct addr_map_symbol target = {
- .map = map,
.addr = dl->ops.target.addr,
+ .ms = { .map = map, },
};
- if (!map_groups__find_ams(&target) &&
- target.sym->start == target.al_addr)
- dl->ops.target.sym = target.sym;
+ if (!map_groups__find_ams(args->ms.mg, &target) &&
+ target.ms.sym->start == target.al_addr)
+ dl->ops.target.sym = target.ms.sym;
}
annotation_line__add(&dl->al, &notes->src->source);
@@ -1597,10 +1584,9 @@ static void delete_last_nop(struct symbol *sym)
}
}
-int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map,
- int errnum, char *buf, size_t buflen)
+int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen)
{
- struct dso *dso = map->dso;
+ struct dso *dso = ms->map->dso;
BUG_ON(buflen == 0);
@@ -1631,6 +1617,19 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *
case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF:
scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation");
break;
+ case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP:
+ scnprintf(buf, buflen, "Problems with arch specific instruction name regular expressions.");
+ break;
+ case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING:
+ scnprintf(buf, buflen, "Problems while parsing the CPUID in the arch specific initialization.");
+ break;
+ case SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE:
+ scnprintf(buf, buflen, "Invalid BPF file: %s.", dso->long_name);
+ break;
+ case SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF:
+ scnprintf(buf, buflen, "The %s BPF file has no BTF section, compile with -g or use pahole -J.",
+ dso->long_name);
+ break;
default:
scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum);
break;
@@ -1662,7 +1661,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
build_id_path = strdup(filename);
if (!build_id_path)
- return -1;
+ return ENOMEM;
/*
* old style build-id cache has name of XX/XXXXXXX.. while
@@ -1713,13 +1712,13 @@ static int symbol__disassemble_bpf(struct symbol *sym,
char tpath[PATH_MAX];
size_t buf_size;
int nr_skip = 0;
- int ret = -1;
char *buf;
bfd *bfdf;
+ int ret;
FILE *s;
if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO)
- return -1;
+ return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE;
pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__,
sym->name, sym->start, sym->end - sym->start);
@@ -1732,8 +1731,10 @@ static int symbol__disassemble_bpf(struct symbol *sym,
assert(bfd_check_format(bfdf, bfd_object));
s = open_memstream(&buf, &buf_size);
- if (!s)
+ if (!s) {
+ ret = errno;
goto out;
+ }
init_disassemble_info(&info, s,
(fprintf_ftype) fprintf);
@@ -1742,8 +1743,10 @@ static int symbol__disassemble_bpf(struct symbol *sym,
info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env,
dso->bpf_prog.id);
- if (!info_node)
+ if (!info_node) {
+ ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
goto out;
+ }
info_linear = info_node->info_linear;
sub_id = dso->bpf_prog.sub_id;
@@ -1840,6 +1843,67 @@ static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused,
}
#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
+/*
+ * Possibly create a new version of line with tabs expanded. Returns the
+ * existing or new line, storage is updated if a new line is allocated. If
+ * allocation fails then NULL is returned.
+ */
+static char *expand_tabs(char *line, char **storage, size_t *storage_len)
+{
+ size_t i, src, dst, len, new_storage_len, num_tabs;
+ char *new_line;
+ size_t line_len = strlen(line);
+
+ for (num_tabs = 0, i = 0; i < line_len; i++)
+ if (line[i] == '\t')
+ num_tabs++;
+
+ if (num_tabs == 0)
+ return line;
+
+ /*
+ * Space for the line and '\0', less the leading and trailing
+ * spaces. Each tab may introduce 7 additional spaces.
+ */
+ new_storage_len = line_len + 1 + (num_tabs * 7);
+
+ new_line = malloc(new_storage_len);
+ if (new_line == NULL) {
+ pr_err("Failure allocating memory for tab expansion\n");
+ return NULL;
+ }
+
+ /*
+ * Copy regions starting at src and expand tabs. If there are two
+ * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces
+ * are inserted.
+ */
+ for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) {
+ if (line[i] == '\t') {
+ len = i - src;
+ memcpy(&new_line[dst], &line[src], len);
+ dst += len;
+ new_line[dst++] = ' ';
+ while (dst % 8 != 0)
+ new_line[dst++] = ' ';
+ src = i + 1;
+ num_tabs--;
+ }
+ }
+
+ /* Expand the last region. */
+ len = line_len - src;
+ memcpy(&new_line[dst], &line[src], len);
+ dst += len;
+ new_line[dst] = '\0';
+
+ free(*storage);
+ *storage = new_line;
+ *storage_len = new_storage_len;
+ return new_line;
+
+}
+
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
struct annotation_options *opts = args->options;
@@ -1851,10 +1915,19 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
struct kcore_extract kce;
bool delete_extract = false;
bool decomp = false;
- int stdout_fd[2];
int lineno = 0;
int nline;
- pid_t pid;
+ char *line;
+ size_t line_len;
+ const char *objdump_argv[] = {
+ "/bin/sh",
+ "-c",
+ NULL, /* Will be the objdump command to run. */
+ "--",
+ NULL, /* Will be the symfs path. */
+ NULL,
+ };
+ struct child_process objdump_process;
int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
if (err)
@@ -1884,7 +1957,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
if (dso__decompress_kmodule_path(dso, symfs_filename,
tmp, sizeof(tmp)) < 0)
- goto out;
+ return -1;
decomp = true;
strcpy(symfs_filename, tmp);
@@ -1893,13 +1966,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
err = asprintf(&command,
"%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64
- " -l -d %s %s -C \"$1\" 2>/dev/null|grep -v \"$1:\"|expand",
+ " -l -d %s %s -C \"$1\"",
opts->objdump_path ?: "objdump",
opts->disassembler_style ? "-M " : "",
opts->disassembler_style ?: "",
map__rip_2objdump(map, sym->start),
map__rip_2objdump(map, sym->end),
- opts->show_asm_raw ? "" : "--no-show-raw",
+ opts->show_asm_raw ? "" : "--no-show-raw-insn",
opts->annotate_src ? "-S" : "");
if (err < 0) {
@@ -1909,55 +1982,73 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
pr_debug("Executing: %s\n", command);
- err = -1;
- if (pipe(stdout_fd) < 0) {
- pr_err("Failure creating the pipe to run %s\n", command);
- goto out_free_command;
- }
-
- pid = fork();
- if (pid < 0) {
- pr_err("Failure forking to run %s\n", command);
- goto out_close_stdout;
- }
+ objdump_argv[2] = command;
+ objdump_argv[4] = symfs_filename;
- if (pid == 0) {
- close(stdout_fd[0]);
- dup2(stdout_fd[1], 1);
- close(stdout_fd[1]);
- execl("/bin/sh", "sh", "-c", command, "--", symfs_filename,
- NULL);
- perror(command);
- exit(-1);
+ /* Create a pipe to read from for stdout */
+ memset(&objdump_process, 0, sizeof(objdump_process));
+ objdump_process.argv = objdump_argv;
+ objdump_process.out = -1;
+ if (start_command(&objdump_process)) {
+ pr_err("Failure starting to run %s\n", command);
+ err = -1;
+ goto out_free_command;
}
- close(stdout_fd[1]);
-
- file = fdopen(stdout_fd[0], "r");
+ file = fdopen(objdump_process.out, "r");
if (!file) {
pr_err("Failure creating FILE stream for %s\n", command);
/*
* If we were using debug info should retry with
* original binary.
*/
- goto out_free_command;
+ err = -1;
+ goto out_close_stdout;
}
+ /* Storage for getline. */
+ line = NULL;
+ line_len = 0;
+
nline = 0;
while (!feof(file)) {
+ const char *match;
+ char *expanded_line;
+
+ if (getline(&line, &line_len, file) < 0 || !line)
+ break;
+
+ /* Skip lines containing "filename:" */
+ match = strstr(line, symfs_filename);
+ if (match && match[strlen(symfs_filename)] == ':')
+ continue;
+
+ expanded_line = strim(line);
+ expanded_line = expand_tabs(expanded_line, &line, &line_len);
+ if (!expanded_line)
+ break;
+
/*
* The source code line number (lineno) needs to be kept in
* across calls to symbol__parse_objdump_line(), so that it
* can associate it with the instructions till the next one.
* See disasm_line__new() and struct disasm_line::line_nr.
*/
- if (symbol__parse_objdump_line(sym, file, args, &lineno) < 0)
+ if (symbol__parse_objdump_line(sym, args, expanded_line,
+ &lineno) < 0)
break;
nline++;
}
+ free(line);
- if (nline == 0)
+ err = finish_command(&objdump_process);
+ if (err)
+ pr_err("Error running %s\n", command);
+
+ if (nline == 0) {
+ err = -1;
pr_err("No output from %s\n", command);
+ }
/*
* kallsyms does not have symbol sizes so there may a nop at the end.
@@ -1967,23 +2058,21 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
delete_last_nop(sym);
fclose(file);
- err = 0;
+
+out_close_stdout:
+ close(objdump_process.out);
+
out_free_command:
free(command);
-out_remove_tmp:
- close(stdout_fd[0]);
+out_remove_tmp:
if (decomp)
unlink(symfs_filename);
if (delete_extract)
kcore_extract__delete(&kce);
-out:
- return err;
-out_close_stdout:
- close(stdout_fd[1]);
- goto out_free_command;
+ return err;
}
static void calc_percent(struct sym_hist *sym_hist,
@@ -2054,11 +2143,10 @@ void symbol__calc_percent(struct symbol *sym, struct evsel *evsel)
annotation__calc_percent(notes, evsel, symbol__size(sym));
}
-int symbol__annotate(struct symbol *sym, struct map *map,
- struct evsel *evsel, size_t privsize,
- struct annotation_options *options,
- struct arch **parch)
+int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, size_t privsize,
+ struct annotation_options *options, struct arch **parch)
{
+ struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct annotate_args args = {
.privsize = privsize,
@@ -2071,11 +2159,11 @@ int symbol__annotate(struct symbol *sym, struct map *map,
int err;
if (!arch_name)
- return -1;
+ return errno;
args.arch = arch = arch__find(arch_name);
if (arch == NULL)
- return -ENOTSUP;
+ return ENOTSUP;
if (parch)
*parch = arch;
@@ -2088,9 +2176,8 @@ int symbol__annotate(struct symbol *sym, struct map *map,
}
}
- args.ms.map = map;
- args.ms.sym = sym;
- notes->start = map__rip_2objdump(map, sym->start);
+ args.ms = *ms;
+ notes->start = map__rip_2objdump(ms->map, sym->start);
return symbol__disassemble(sym, &args);
}
@@ -2246,10 +2333,11 @@ static int annotated_source__addr_fmt_width(struct list_head *lines, u64 start)
return 0;
}
-int symbol__annotate_printf(struct symbol *sym, struct map *map,
- struct evsel *evsel,
+int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
+ struct map *map = ms->map;
+ struct symbol *sym = ms->sym;
struct dso *dso = map->dso;
char *filename;
const char *d_filename;
@@ -2653,30 +2741,29 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
resort_source_line(root, &tmp_root);
}
-static void symbol__calc_lines(struct symbol *sym, struct map *map,
- struct rb_root *root,
+static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root,
struct annotation_options *opts)
{
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes = symbol__annotation(ms->sym);
- annotation__calc_lines(notes, map, root, opts);
+ annotation__calc_lines(notes, ms->map, root, opts);
}
-int symbol__tty_annotate2(struct symbol *sym, struct map *map,
- struct evsel *evsel,
+int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
- struct dso *dso = map->dso;
+ struct dso *dso = ms->map->dso;
+ struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
struct hists *hists = evsel__hists(evsel);
char buf[1024];
- if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0)
+ if (symbol__annotate2(ms, evsel, opts, NULL) < 0)
return -1;
if (opts->print_lines) {
srcline_full_filename = opts->full_path;
- symbol__calc_lines(sym, map, &source_line, opts);
+ symbol__calc_lines(ms, &source_line, opts);
print_summary(&source_line, dso->long_name);
}
@@ -2690,25 +2777,25 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map,
return 0;
}
-int symbol__tty_annotate(struct symbol *sym, struct map *map,
- struct evsel *evsel,
+int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
- struct dso *dso = map->dso;
+ struct dso *dso = ms->map->dso;
+ struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
- if (symbol__annotate(sym, map, evsel, 0, opts, NULL) < 0)
+ if (symbol__annotate(ms, evsel, 0, opts, NULL) < 0)
return -1;
symbol__calc_percent(sym, evsel);
if (opts->print_lines) {
srcline_full_filename = opts->full_path;
- symbol__calc_lines(sym, map, &source_line, opts);
+ symbol__calc_lines(ms, &source_line, opts);
print_summary(&source_line, dso->long_name);
}
- symbol__annotate_printf(sym, map, evsel, opts);
+ symbol__annotate_printf(ms, evsel, opts);
annotated_source__purge(symbol__annotation(sym)->src);
@@ -2962,21 +3049,22 @@ void annotation_line__write(struct annotation_line *al, struct annotation *notes
wops->write_graph);
}
-int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel,
+int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *options, struct arch **parch)
{
+ struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
size_t size = symbol__size(sym);
int nr_pcnt = 1, err;
notes->offsets = zalloc(size * sizeof(struct annotation_line *));
if (notes->offsets == NULL)
- return -1;
+ return ENOMEM;
if (perf_evsel__is_group_event(evsel))
nr_pcnt = evsel->core.nr_members;
- err = symbol__annotate(sym, map, evsel, 0, options, parch);
+ err = symbol__annotate(ms, evsel, 0, options, parch);
if (err)
goto out_free_offsets;
@@ -2997,7 +3085,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel,
out_free_offsets:
zfree(&notes->offsets);
- return -1;
+ return err;
}
#define ANNOTATION__CFG(n) \
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index d94be9140e31..7075d98f69d9 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -11,6 +11,7 @@
#include <pthread.h>
#include <asm/bug.h>
#include "symbol_conf.h"
+#include "spark.h"
struct hist_browser_timer;
struct hist_entry;
@@ -235,6 +236,7 @@ struct cyc_hist {
u64 cycles_aggr;
u64 cycles_max;
u64 cycles_min;
+ s64 cycles_spark[NUM_SPARKS];
u32 num;
u32 num_aggr;
u8 have_start;
@@ -347,11 +349,11 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *samp
struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists);
void symbol__annotate_zero_histograms(struct symbol *sym);
-int symbol__annotate(struct symbol *sym, struct map *map,
+int symbol__annotate(struct map_symbol *ms,
struct evsel *evsel, size_t privsize,
struct annotation_options *options,
struct arch **parch);
-int symbol__annotate2(struct symbol *sym, struct map *map,
+int symbol__annotate2(struct map_symbol *ms,
struct evsel *evsel,
struct annotation_options *options,
struct arch **parch);
@@ -370,15 +372,17 @@ enum symbol_disassemble_errno {
SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START,
SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF,
+ SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING,
+ SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP,
+ SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE,
+ SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF,
__SYMBOL_ANNOTATE_ERRNO__END,
};
-int symbol__strerror_disassemble(struct symbol *sym, struct map *map,
- int errnum, char *buf, size_t buflen);
+int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen);
-int symbol__annotate_printf(struct symbol *sym, struct map *map,
- struct evsel *evsel,
+int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *options);
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
@@ -389,20 +393,16 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
bool ui__has_annotation(void);
-int symbol__tty_annotate(struct symbol *sym, struct map *map,
- struct evsel *evsel, struct annotation_options *opts);
+int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel, struct annotation_options *opts);
-int symbol__tty_annotate2(struct symbol *sym, struct map *map,
- struct evsel *evsel, struct annotation_options *opts);
+int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel, struct annotation_options *opts);
#ifdef HAVE_SLANG_SUPPORT
-int symbol__tui_annotate(struct symbol *sym, struct map *map,
- struct evsel *evsel,
+int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *opts);
#else
-static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
- struct map *map __maybe_unused,
+static inline int symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
struct evsel *evsel __maybe_unused,
struct hist_browser_timer *hbt __maybe_unused,
struct annotation_options *opts __maybe_unused)
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 8470dfe9fe97..eb087e7df6f4 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -31,6 +31,7 @@
#include "map.h"
#include "pmu.h"
#include "evsel.h"
+#include "evsel_config.h"
#include "symbol.h"
#include "util/synthetic-events.h"
#include "thread_map.h"
@@ -57,6 +58,72 @@
#include "symbol/kallsyms.h"
#include <internal/lib.h>
+static struct perf_pmu *perf_evsel__find_pmu(struct evsel *evsel)
+{
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (pmu->type == evsel->core.attr.type)
+ break;
+ }
+
+ return pmu;
+}
+
+static bool perf_evsel__is_aux_event(struct evsel *evsel)
+{
+ struct perf_pmu *pmu = perf_evsel__find_pmu(evsel);
+
+ return pmu && pmu->auxtrace;
+}
+
+/*
+ * Make a group from 'leader' to 'last', requiring that the events were not
+ * already grouped to a different leader.
+ */
+static int perf_evlist__regroup(struct evlist *evlist,
+ struct evsel *leader,
+ struct evsel *last)
+{
+ struct evsel *evsel;
+ bool grp;
+
+ if (!perf_evsel__is_group_leader(leader))
+ return -EINVAL;
+
+ grp = false;
+ evlist__for_each_entry(evlist, evsel) {
+ if (grp) {
+ if (!(evsel->leader == leader ||
+ (evsel->leader == evsel &&
+ evsel->core.nr_members <= 1)))
+ return -EINVAL;
+ } else if (evsel == leader) {
+ grp = true;
+ }
+ if (evsel == last)
+ break;
+ }
+
+ grp = false;
+ evlist__for_each_entry(evlist, evsel) {
+ if (grp) {
+ if (evsel->leader != leader) {
+ evsel->leader = leader;
+ if (leader->core.nr_members < 1)
+ leader->core.nr_members = 1;
+ leader->core.nr_members += 1;
+ }
+ } else if (evsel == leader) {
+ grp = true;
+ }
+ if (evsel == last)
+ break;
+ }
+
+ return 0;
+}
+
static bool auxtrace__dont_decode(struct perf_session *session)
{
return !session->itrace_synth_opts ||
@@ -597,6 +664,132 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
return -EINVAL;
}
+/*
+ * Event record size is 16-bit which results in a maximum size of about 64KiB.
+ * Allow about 4KiB for the rest of the sample record, to give a maximum
+ * AUX area sample size of 60KiB.
+ */
+#define MAX_AUX_SAMPLE_SIZE (60 * 1024)
+
+/* Arbitrary default size if no other default provided */
+#define DEFAULT_AUX_SAMPLE_SIZE (4 * 1024)
+
+static int auxtrace_validate_aux_sample_size(struct evlist *evlist,
+ struct record_opts *opts)
+{
+ struct evsel *evsel;
+ bool has_aux_leader = false;
+ u32 sz;
+
+ evlist__for_each_entry(evlist, evsel) {
+ sz = evsel->core.attr.aux_sample_size;
+ if (perf_evsel__is_group_leader(evsel)) {
+ has_aux_leader = perf_evsel__is_aux_event(evsel);
+ if (sz) {
+ if (has_aux_leader)
+ pr_err("Cannot add AUX area sampling to an AUX area event\n");
+ else
+ pr_err("Cannot add AUX area sampling to a group leader\n");
+ return -EINVAL;
+ }
+ }
+ if (sz > MAX_AUX_SAMPLE_SIZE) {
+ pr_err("AUX area sample size %u too big, max. %d\n",
+ sz, MAX_AUX_SAMPLE_SIZE);
+ return -EINVAL;
+ }
+ if (sz) {
+ if (!has_aux_leader) {
+ pr_err("Cannot add AUX area sampling because group leader is not an AUX area event\n");
+ return -EINVAL;
+ }
+ perf_evsel__set_sample_bit(evsel, AUX);
+ opts->auxtrace_sample_mode = true;
+ } else {
+ perf_evsel__reset_sample_bit(evsel, AUX);
+ }
+ }
+
+ if (!opts->auxtrace_sample_mode) {
+ pr_err("AUX area sampling requires an AUX area event group leader plus other events to which to add samples\n");
+ return -EINVAL;
+ }
+
+ if (!perf_can_aux_sample()) {
+ pr_err("AUX area sampling is not supported by kernel\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int auxtrace_parse_sample_options(struct auxtrace_record *itr,
+ struct evlist *evlist,
+ struct record_opts *opts, const char *str)
+{
+ struct perf_evsel_config_term *term;
+ struct evsel *aux_evsel;
+ bool has_aux_sample_size = false;
+ bool has_aux_leader = false;
+ struct evsel *evsel;
+ char *endptr;
+ unsigned long sz;
+
+ if (!str)
+ goto no_opt;
+
+ if (!itr) {
+ pr_err("No AUX area event to sample\n");
+ return -EINVAL;
+ }
+
+ sz = strtoul(str, &endptr, 0);
+ if (*endptr || sz > UINT_MAX) {
+ pr_err("Bad AUX area sampling option: '%s'\n", str);
+ return -EINVAL;
+ }
+
+ if (!sz)
+ sz = itr->default_aux_sample_size;
+
+ if (!sz)
+ sz = DEFAULT_AUX_SAMPLE_SIZE;
+
+ /* Set aux_sample_size based on --aux-sample option */
+ evlist__for_each_entry(evlist, evsel) {
+ if (perf_evsel__is_group_leader(evsel)) {
+ has_aux_leader = perf_evsel__is_aux_event(evsel);
+ } else if (has_aux_leader) {
+ evsel->core.attr.aux_sample_size = sz;
+ }
+ }
+no_opt:
+ aux_evsel = NULL;
+ /* Override with aux_sample_size from config term */
+ evlist__for_each_entry(evlist, evsel) {
+ if (perf_evsel__is_aux_event(evsel))
+ aux_evsel = evsel;
+ term = perf_evsel__get_config_term(evsel, AUX_SAMPLE_SIZE);
+ if (term) {
+ has_aux_sample_size = true;
+ evsel->core.attr.aux_sample_size = term->val.aux_sample_size;
+ /* If possible, group with the AUX event */
+ if (aux_evsel && evsel->core.attr.aux_sample_size)
+ perf_evlist__regroup(evlist, aux_evsel, evsel);
+ }
+ }
+
+ if (!str && !has_aux_sample_size)
+ return 0;
+
+ if (!itr) {
+ pr_err("No AUX area event to sample\n");
+ return -EINVAL;
+ }
+
+ return auxtrace_validate_aux_sample_size(evlist, opts);
+}
+
struct auxtrace_record *__weak
auxtrace_record__init(struct evlist *evlist __maybe_unused, int *err)
{
@@ -811,6 +1004,113 @@ struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
}
}
+struct auxtrace_queue *auxtrace_queues__sample_queue(struct auxtrace_queues *queues,
+ struct perf_sample *sample,
+ struct perf_session *session)
+{
+ struct perf_sample_id *sid;
+ unsigned int idx;
+ u64 id;
+
+ id = sample->id;
+ if (!id)
+ return NULL;
+
+ sid = perf_evlist__id2sid(session->evlist, id);
+ if (!sid)
+ return NULL;
+
+ idx = sid->idx;
+
+ if (idx >= queues->nr_queues)
+ return NULL;
+
+ return &queues->queue_array[idx];
+}
+
+int auxtrace_queues__add_sample(struct auxtrace_queues *queues,
+ struct perf_session *session,
+ struct perf_sample *sample, u64 data_offset,
+ u64 reference)
+{
+ struct auxtrace_buffer buffer = {
+ .pid = -1,
+ .data_offset = data_offset,
+ .reference = reference,
+ .size = sample->aux_sample.size,
+ };
+ struct perf_sample_id *sid;
+ u64 id = sample->id;
+ unsigned int idx;
+
+ if (!id)
+ return -EINVAL;
+
+ sid = perf_evlist__id2sid(session->evlist, id);
+ if (!sid)
+ return -ENOENT;
+
+ idx = sid->idx;
+ buffer.tid = sid->tid;
+ buffer.cpu = sid->cpu;
+
+ return auxtrace_queues__add_buffer(queues, session, idx, &buffer, NULL);
+}
+
+struct queue_data {
+ bool samples;
+ bool events;
+};
+
+static int auxtrace_queue_data_cb(struct perf_session *session,
+ union perf_event *event, u64 offset,
+ void *data)
+{
+ struct queue_data *qd = data;
+ struct perf_sample sample;
+ int err;
+
+ if (qd->events && event->header.type == PERF_RECORD_AUXTRACE) {
+ if (event->header.size < sizeof(struct perf_record_auxtrace))
+ return -EINVAL;
+ offset += event->header.size;
+ return session->auxtrace->queue_data(session, NULL, event,
+ offset);
+ }
+
+ if (!qd->samples || event->header.type != PERF_RECORD_SAMPLE)
+ return 0;
+
+ err = perf_evlist__parse_sample(session->evlist, event, &sample);
+ if (err)
+ return err;
+
+ if (!sample.aux_sample.size)
+ return 0;
+
+ offset += sample.aux_sample.data - (void *)event;
+
+ return session->auxtrace->queue_data(session, &sample, NULL, offset);
+}
+
+int auxtrace_queue_data(struct perf_session *session, bool samples, bool events)
+{
+ struct queue_data qd = {
+ .samples = samples,
+ .events = events,
+ };
+
+ if (auxtrace__dont_decode(session))
+ return 0;
+
+ if (!session->auxtrace || !session->auxtrace->queue_data)
+ return -EINVAL;
+
+ return perf_session__peek_events(session, session->header.data_offset,
+ session->header.data_size,
+ auxtrace_queue_data_cb, &qd);
+}
+
void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd)
{
size_t adj = buffer->data_offset & (page_size - 1);
@@ -1457,6 +1757,34 @@ int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
return 0;
}
+static struct auxtrace_cache_entry *auxtrace_cache__rm(struct auxtrace_cache *c,
+ u32 key)
+{
+ struct auxtrace_cache_entry *entry;
+ struct hlist_head *hlist;
+ struct hlist_node *n;
+
+ if (!c)
+ return NULL;
+
+ hlist = &c->hashtable[hash_32(key, c->bits)];
+ hlist_for_each_entry_safe(entry, n, hlist, hash) {
+ if (entry->key == key) {
+ hlist_del(&entry->hash);
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+void auxtrace_cache__remove(struct auxtrace_cache *c, u32 key)
+{
+ struct auxtrace_cache_entry *entry = auxtrace_cache__rm(c, key);
+
+ auxtrace_cache__free_entry(c, entry);
+}
+
void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key)
{
struct auxtrace_cache_entry *entry;
@@ -2152,18 +2480,6 @@ out_exit:
return err;
}
-static struct perf_pmu *perf_evsel__find_pmu(struct evsel *evsel)
-{
- struct perf_pmu *pmu = NULL;
-
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (pmu->type == evsel->core.attr.type)
- break;
- }
-
- return pmu;
-}
-
static int perf_evsel__nr_addr_filter(struct evsel *evsel)
{
struct perf_pmu *pmu = perf_evsel__find_pmu(evsel);
@@ -2208,6 +2524,16 @@ int auxtrace__process_event(struct perf_session *session, union perf_event *even
return session->auxtrace->process_event(session, event, sample, tool);
}
+void auxtrace__dump_auxtrace_sample(struct perf_session *session,
+ struct perf_sample *sample)
+{
+ if (!session->auxtrace || !session->auxtrace->dump_auxtrace_sample ||
+ auxtrace__dont_decode(session))
+ return;
+
+ session->auxtrace->dump_auxtrace_sample(session, sample);
+}
+
int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool)
{
if (!session->auxtrace)
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index f201f36bc35f..749d72cd9c7b 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -141,6 +141,9 @@ struct auxtrace_index {
* struct auxtrace - session callbacks to allow AUX area data decoding.
* @process_event: lets the decoder see all session events
* @process_auxtrace_event: process a PERF_RECORD_AUXTRACE event
+ * @queue_data: queue an AUX sample or PERF_RECORD_AUXTRACE event for later
+ * processing
+ * @dump_auxtrace_sample: dump AUX area sample data
* @flush_events: process any remaining data
* @free_events: free resources associated with event processing
* @free: free resources associated with the session
@@ -153,6 +156,11 @@ struct auxtrace {
int (*process_auxtrace_event)(struct perf_session *session,
union perf_event *event,
struct perf_tool *tool);
+ int (*queue_data)(struct perf_session *session,
+ struct perf_sample *sample, union perf_event *event,
+ u64 data_offset);
+ void (*dump_auxtrace_sample)(struct perf_session *session,
+ struct perf_sample *sample);
int (*flush_events)(struct perf_session *session,
struct perf_tool *tool);
void (*free_events)(struct perf_session *session);
@@ -313,6 +321,7 @@ struct auxtrace_mmap_params {
* @reference: provide a 64-bit reference number for auxtrace_event
* @read_finish: called after reading from an auxtrace mmap
* @alignment: alignment (if any) for AUX area data
+ * @default_aux_sample_size: default sample size for --aux sample option
*/
struct auxtrace_record {
int (*recording_options)(struct auxtrace_record *itr,
@@ -336,6 +345,7 @@ struct auxtrace_record {
u64 (*reference)(struct auxtrace_record *itr);
int (*read_finish)(struct auxtrace_record *itr, int idx);
unsigned int alignment;
+ unsigned int default_aux_sample_size;
};
/**
@@ -462,9 +472,19 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues,
struct perf_session *session,
union perf_event *event, off_t data_offset,
struct auxtrace_buffer **buffer_ptr);
+struct auxtrace_queue *
+auxtrace_queues__sample_queue(struct auxtrace_queues *queues,
+ struct perf_sample *sample,
+ struct perf_session *session);
+int auxtrace_queues__add_sample(struct auxtrace_queues *queues,
+ struct perf_session *session,
+ struct perf_sample *sample, u64 data_offset,
+ u64 reference);
void auxtrace_queues__free(struct auxtrace_queues *queues);
int auxtrace_queues__process_index(struct auxtrace_queues *queues,
struct perf_session *session);
+int auxtrace_queue_data(struct perf_session *session, bool samples,
+ bool events);
struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
struct auxtrace_buffer *buffer);
void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd);
@@ -489,6 +509,7 @@ void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c);
void auxtrace_cache__free_entry(struct auxtrace_cache *c, void *entry);
int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
struct auxtrace_cache_entry *entry);
+void auxtrace_cache__remove(struct auxtrace_cache *c, u32 key);
void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key);
struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
@@ -497,6 +518,9 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
struct record_opts *opts,
const char *str);
+int auxtrace_parse_sample_options(struct auxtrace_record *itr,
+ struct evlist *evlist,
+ struct record_opts *opts, const char *str);
int auxtrace_record__options(struct auxtrace_record *itr,
struct evlist *evlist,
struct record_opts *opts);
@@ -549,6 +573,8 @@ int auxtrace_parse_filters(struct evlist *evlist);
int auxtrace__process_event(struct perf_session *session, union perf_event *event,
struct perf_sample *sample, struct perf_tool *tool);
+void auxtrace__dump_auxtrace_sample(struct perf_session *session,
+ struct perf_sample *sample);
int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool);
void auxtrace__free_events(struct perf_session *session);
void auxtrace__free(struct perf_session *session);
@@ -648,6 +674,18 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
}
static inline
+int auxtrace_parse_sample_options(struct auxtrace_record *itr __maybe_unused,
+ struct evlist *evlist __maybe_unused,
+ struct record_opts *opts __maybe_unused,
+ const char *str)
+{
+ if (!str)
+ return 0;
+ pr_err("AUX area tracing not supported\n");
+ return -EINVAL;
+}
+
+static inline
int auxtrace__process_event(struct perf_session *session __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
@@ -657,6 +695,12 @@ int auxtrace__process_event(struct perf_session *session __maybe_unused,
}
static inline
+void auxtrace__dump_auxtrace_sample(struct perf_session *session __maybe_unused,
+ struct perf_sample *sample __maybe_unused)
+{
+}
+
+static inline
int auxtrace__flush_events(struct perf_session *session __maybe_unused,
struct perf_tool *tool __maybe_unused)
{
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
new file mode 100644
index 000000000000..c4b030bf6ec2
--- /dev/null
+++ b/tools/perf/util/block-info.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdlib.h>
+#include <string.h>
+#include <linux/zalloc.h>
+#include "block-info.h"
+#include "sort.h"
+#include "annotate.h"
+#include "symbol.h"
+#include "dso.h"
+#include "map.h"
+#include "srcline.h"
+#include "evlist.h"
+#include "hist.h"
+#include "ui/browsers/hists.h"
+
+static struct block_header_column {
+ const char *name;
+ int width;
+} block_columns[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
+ [PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT] = {
+ .name = "Sampled Cycles%",
+ .width = 15,
+ },
+ [PERF_HPP_REPORT__BLOCK_LBR_CYCLES] = {
+ .name = "Sampled Cycles",
+ .width = 14,
+ },
+ [PERF_HPP_REPORT__BLOCK_CYCLES_PCT] = {
+ .name = "Avg Cycles%",
+ .width = 11,
+ },
+ [PERF_HPP_REPORT__BLOCK_AVG_CYCLES] = {
+ .name = "Avg Cycles",
+ .width = 10,
+ },
+ [PERF_HPP_REPORT__BLOCK_RANGE] = {
+ .name = "[Program Block Range]",
+ .width = 70,
+ },
+ [PERF_HPP_REPORT__BLOCK_DSO] = {
+ .name = "Shared Object",
+ .width = 20,
+ }
+};
+
+struct block_info *block_info__get(struct block_info *bi)
+{
+ if (bi)
+ refcount_inc(&bi->refcnt);
+ return bi;
+}
+
+void block_info__put(struct block_info *bi)
+{
+ if (bi && refcount_dec_and_test(&bi->refcnt))
+ free(bi);
+}
+
+struct block_info *block_info__new(void)
+{
+ struct block_info *bi = zalloc(sizeof(*bi));
+
+ if (bi)
+ refcount_set(&bi->refcnt, 1);
+ return bi;
+}
+
+int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *left, struct hist_entry *right)
+{
+ struct block_info *bi_l = left->block_info;
+ struct block_info *bi_r = right->block_info;
+ int cmp;
+
+ if (!bi_l->sym || !bi_r->sym) {
+ if (!bi_l->sym && !bi_r->sym)
+ return 0;
+ 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);
+ return cmp;
+ }
+
+ if (bi_l->sym->start != bi_r->sym->start)
+ return (int64_t)(bi_r->sym->start - bi_l->sym->start);
+
+ return (int64_t)(bi_r->sym->end - bi_l->sym->end);
+}
+
+static void init_block_info(struct block_info *bi, struct symbol *sym,
+ struct cyc_hist *ch, int offset,
+ u64 total_cycles)
+{
+ bi->sym = sym;
+ bi->start = ch->start;
+ bi->end = offset;
+ bi->cycles = ch->cycles;
+ bi->cycles_aggr = ch->cycles_aggr;
+ bi->num = ch->num;
+ bi->num_aggr = ch->num_aggr;
+ bi->total_cycles = total_cycles;
+
+ memcpy(bi->cycles_spark, ch->cycles_spark,
+ NUM_SPARKS * sizeof(u64));
+}
+
+int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
+ u64 *block_cycles_aggr, u64 total_cycles)
+{
+ struct annotation *notes;
+ struct cyc_hist *ch;
+ static struct addr_location al;
+ u64 cycles = 0;
+
+ if (!he->ms.map || !he->ms.sym)
+ return 0;
+
+ memset(&al, 0, sizeof(al));
+ al.map = he->ms.map;
+ al.sym = he->ms.sym;
+
+ notes = symbol__annotation(he->ms.sym);
+ if (!notes || !notes->src || !notes->src->cycles_hist)
+ return 0;
+ ch = notes->src->cycles_hist;
+ for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
+ if (ch[i].num_aggr) {
+ struct block_info *bi;
+ struct hist_entry *he_block;
+
+ bi = block_info__new();
+ if (!bi)
+ return -1;
+
+ init_block_info(bi, he->ms.sym, &ch[i], i,
+ total_cycles);
+ cycles += bi->cycles_aggr / bi->num_aggr;
+
+ he_block = hists__add_entry_block(&bh->block_hists,
+ &al, bi);
+ if (!he_block) {
+ block_info__put(bi);
+ return -1;
+ }
+ }
+ }
+
+ if (block_cycles_aggr)
+ *block_cycles_aggr += cycles;
+
+ return 0;
+}
+
+static int block_column_header(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp,
+ struct hists *hists __maybe_unused,
+ int line __maybe_unused,
+ int *span __maybe_unused)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
+ block_fmt->header);
+}
+
+static int block_column_width(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp __maybe_unused,
+ struct hists *hists __maybe_unused)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+
+ return block_fmt->width;
+}
+
+static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ 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);
+}
+
+static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
+ struct hist_entry *left,
+ struct hist_entry *right)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+ struct block_info *bi_l = left->block_info;
+ struct block_info *bi_r = right->block_info;
+ double l, r;
+
+ if (block_fmt->total_cycles) {
+ l = ((double)bi_l->cycles /
+ (double)block_fmt->total_cycles) * 100000.0;
+ r = ((double)bi_r->cycles /
+ (double)block_fmt->total_cycles) * 100000.0;
+ return (int64_t)l - (int64_t)r;
+ }
+
+ return 0;
+}
+
+static void cycles_string(u64 cycles, char *buf, int size)
+{
+ if (cycles >= 1000000)
+ scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0);
+ else if (cycles >= 1000)
+ scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0);
+ else
+ scnprintf(buf, size, "%1d", cycles);
+}
+
+static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp, struct hist_entry *he)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+ struct block_info *bi = he->block_info;
+ char cycles_buf[16];
+
+ cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
+ cycles_buf);
+}
+
+static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp, struct hist_entry *he)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_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);
+}
+
+static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+ struct block_info *bi = he->block_info;
+ char cycles_buf[16];
+
+ cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
+ sizeof(cycles_buf));
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
+ cycles_buf);
+}
+
+static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+ struct block_info *bi = he->block_info;
+ char buf[128];
+ char *start_line, *end_line;
+
+ symbol_conf.disable_add2line_warn = true;
+
+ start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ he->ms.sym);
+
+ end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ he->ms.sym);
+
+ if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
+ scnprintf(buf, sizeof(buf), "[%s -> %s]",
+ start_line, end_line);
+ } else {
+ scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
+ bi->start, bi->end);
+ }
+
+ free_srcline(start_line);
+ free_srcline(end_line);
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
+}
+
+static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
+ struct map *map = he->ms.map;
+
+ if (map && map->dso) {
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
+ map->dso->short_name);
+ }
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
+ "[unknown]");
+}
+
+static void init_block_header(struct block_fmt *block_fmt)
+{
+ struct perf_hpp_fmt *fmt = &block_fmt->fmt;
+
+ BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
+
+ block_fmt->header = block_columns[block_fmt->idx].name;
+ block_fmt->width = block_columns[block_fmt->idx].width;
+
+ fmt->header = block_column_header;
+ fmt->width = block_column_width;
+}
+
+static void hpp_register(struct block_fmt *block_fmt, int idx,
+ struct perf_hpp_list *hpp_list)
+{
+ struct perf_hpp_fmt *fmt = &block_fmt->fmt;
+
+ block_fmt->idx = idx;
+ INIT_LIST_HEAD(&fmt->list);
+ INIT_LIST_HEAD(&fmt->sort_list);
+
+ switch (idx) {
+ case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
+ fmt->entry = block_total_cycles_pct_entry;
+ fmt->cmp = block_info__cmp;
+ fmt->sort = block_total_cycles_pct_sort;
+ break;
+ case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
+ fmt->entry = block_cycles_lbr_entry;
+ break;
+ case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
+ fmt->entry = block_cycles_pct_entry;
+ break;
+ case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
+ fmt->entry = block_avg_cycles_entry;
+ break;
+ case PERF_HPP_REPORT__BLOCK_RANGE:
+ fmt->entry = block_range_entry;
+ break;
+ case PERF_HPP_REPORT__BLOCK_DSO:
+ fmt->entry = block_dso_entry;
+ break;
+ default:
+ return;
+ }
+
+ init_block_header(block_fmt);
+ perf_hpp_list__column_register(hpp_list, fmt);
+}
+
+static void register_block_columns(struct perf_hpp_list *hpp_list,
+ struct block_fmt *block_fmts)
+{
+ for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; i++)
+ hpp_register(&block_fmts[i], i, hpp_list);
+}
+
+static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts)
+{
+ __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);
+
+ perf_hpp_list__register_sort_field(&bh->block_list,
+ &block_fmts[PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT].fmt);
+}
+
+static void process_block_report(struct hists *hists,
+ struct block_report *block_report,
+ u64 total_cycles)
+{
+ 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);
+
+ while (next) {
+ he = rb_entry(next, struct hist_entry, rb_node);
+ block_info__process_sym(he, bh, &block_report->cycles,
+ total_cycles);
+ next = rb_next(&he->rb_node);
+ }
+
+ for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; 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);
+}
+
+struct block_report *block_info__create_report(struct evlist *evlist,
+ u64 total_cycles)
+{
+ struct block_report *block_reports;
+ int nr_hists = evlist->core.nr_entries, i = 0;
+ struct evsel *pos;
+
+ block_reports = calloc(nr_hists, sizeof(struct block_report));
+ if (!block_reports)
+ return NULL;
+
+ evlist__for_each_entry(evlist, pos) {
+ struct hists *hists = evsel__hists(pos);
+
+ process_block_report(hists, &block_reports[i], total_cycles);
+ i++;
+ }
+
+ return block_reports;
+}
+
+int report__browse_block_hists(struct block_hist *bh, float min_percent,
+ struct evsel *evsel, struct perf_env *env,
+ struct annotation_options *annotation_opts)
+{
+ int ret;
+
+ switch (use_browser) {
+ case 0:
+ 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;
+ }
+
+ return 0;
+}
+
+float block_info__total_cycles_percent(struct hist_entry *he)
+{
+ struct block_info *bi = he->block_info;
+
+ if (bi->total_cycles)
+ return bi->cycles * 100.0 / bi->total_cycles;
+
+ return 0.0;
+}
diff --git a/tools/perf/util/block-info.h b/tools/perf/util/block-info.h
new file mode 100644
index 000000000000..bef0d75e9819
--- /dev/null
+++ b/tools/perf/util/block-info.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_BLOCK_H
+#define __PERF_BLOCK_H
+
+#include <linux/types.h>
+#include <linux/refcount.h>
+#include "hist.h"
+#include "symbol.h"
+#include "sort.h"
+#include "ui/ui.h"
+
+struct block_info {
+ struct symbol *sym;
+ u64 start;
+ u64 end;
+ u64 cycles;
+ u64 cycles_aggr;
+ s64 cycles_spark[NUM_SPARKS];
+ u64 total_cycles;
+ int num;
+ int num_aggr;
+ refcount_t refcnt;
+};
+
+struct block_fmt {
+ struct perf_hpp_fmt fmt;
+ int idx;
+ int width;
+ const char *header;
+ u64 total_cycles;
+ u64 block_cycles;
+};
+
+enum {
+ 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,
+ PERF_HPP_REPORT__BLOCK_MAX_INDEX
+};
+
+struct block_report {
+ struct block_hist hist;
+ u64 cycles;
+ struct block_fmt fmts[PERF_HPP_REPORT__BLOCK_MAX_INDEX];
+};
+
+struct block_hist;
+
+struct block_info *block_info__new(void);
+struct block_info *block_info__get(struct block_info *bi);
+void block_info__put(struct block_info *bi);
+
+static inline void __block_info__zput(struct block_info **bi)
+{
+ block_info__put(*bi);
+ *bi = NULL;
+}
+
+#define block_info__zput(bi) __block_info__zput(&bi)
+
+int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *left, struct hist_entry *right);
+
+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);
+
+int report__browse_block_hists(struct block_hist *bh, float min_percent,
+ struct evsel *evsel, struct perf_env *env,
+ struct annotation_options *annotation_opts);
+
+float block_info__total_cycles_percent(struct hist_entry *he);
+
+#endif /* __PERF_BLOCK_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 9a9b56ed3f0a..5cefce33b66b 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -582,8 +582,8 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
return -1;
}
call->ip = cursor_node->ip;
- call->ms.sym = cursor_node->sym;
- call->ms.map = map__get(cursor_node->map);
+ call->ms = cursor_node->ms;
+ map__get(call->ms.map);
call->srcline = cursor_node->srcline;
if (cursor_node->branch) {
@@ -720,21 +720,21 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
/* otherwise fall-back to symbol-based comparison below */
__fallthrough;
case CCKEY_FUNCTION:
- if (node->sym && cnode->ms.sym) {
+ if (node->ms.sym && cnode->ms.sym) {
/*
* Compare inlined frames based on their symbol name
* because different inlined frames will have the same
* symbol start. Otherwise do a faster comparison based
* on the symbol start address.
*/
- if (cnode->ms.sym->inlined || node->sym->inlined) {
+ if (cnode->ms.sym->inlined || node->ms.sym->inlined) {
match = match_chain_strings(cnode->ms.sym->name,
- node->sym->name);
+ node->ms.sym->name);
if (match != MATCH_ERROR)
break;
} else {
match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
- node->map, node->sym->start);
+ node->ms.map, node->ms.sym->start);
break;
}
}
@@ -742,7 +742,7 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
__fallthrough;
case CCKEY_ADDRESS:
default:
- match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->map, node->ip);
+ match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip);
break;
}
@@ -1004,8 +1004,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
int err = 0;
list_for_each_entry_safe(list, next_list, &src->val, list) {
- callchain_cursor_append(cursor, list->ip,
- list->ms.map, list->ms.sym,
+ callchain_cursor_append(cursor, list->ip, &list->ms,
false, NULL, 0, 0, 0, list->srcline);
list_del_init(&list->list);
map__zput(list->ms.map);
@@ -1044,7 +1043,7 @@ int callchain_merge(struct callchain_cursor *cursor,
}
int callchain_cursor_append(struct callchain_cursor *cursor,
- u64 ip, struct map *map, struct symbol *sym,
+ u64 ip, struct map_symbol *ms,
bool branch, struct branch_flags *flags,
int nr_loop_iter, u64 iter_cycles, u64 branch_from,
const char *srcline)
@@ -1060,9 +1059,9 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
}
node->ip = ip;
- map__zput(node->map);
- node->map = map__get(map);
- node->sym = sym;
+ map__zput(node->ms.map);
+ node->ms = *ms;
+ map__get(node->ms.map);
node->branch = branch;
node->nr_loop_iter = nr_loop_iter;
node->iter_cycles = iter_cycles;
@@ -1107,8 +1106,9 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
bool hide_unresolved)
{
- al->map = node->map;
- al->sym = node->sym;
+ al->mg = node->ms.mg;
+ al->map = node->ms.map;
+ al->sym = node->ms.sym;
al->srcline = node->srcline;
al->addr = node->ip;
@@ -1119,8 +1119,8 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
goto out;
}
- if (al->map->groups == &al->machine->kmaps) {
- if (machine__is_host(al->machine)) {
+ if (al->mg == &al->mg->machine->kmaps) {
+ if (machine__is_host(al->mg->machine)) {
al->cpumode = PERF_RECORD_MISC_KERNEL;
al->level = 'k';
} else {
@@ -1128,7 +1128,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
al->level = 'g';
}
} else {
- if (machine__is_host(al->machine)) {
+ if (machine__is_host(al->mg->machine)) {
al->cpumode = PERF_RECORD_MISC_USER;
al->level = '.';
} else if (perf_guest) {
@@ -1571,7 +1571,7 @@ int callchain_cursor__copy(struct callchain_cursor *dst,
if (node == NULL)
break;
- rc = callchain_cursor_append(dst, node->ip, node->map, node->sym,
+ rc = callchain_cursor_append(dst, node->ip, &node->ms,
node->branch, &node->branch_flags,
node->nr_loop_iter,
node->iter_cycles,
@@ -1597,5 +1597,5 @@ void callchain_cursor_reset(struct callchain_cursor *cursor)
cursor->last = &cursor->first;
for (node = cursor->first; node != NULL; node = node->next)
- map__zput(node->map);
+ map__zput(node->ms.map);
}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 83398e5bbe4b..706bb7bbe1e1 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -141,8 +141,7 @@ struct callchain_list {
*/
struct callchain_cursor_node {
u64 ip;
- struct map *map;
- struct symbol *sym;
+ struct map_symbol ms;
const char *srcline;
bool branch;
struct branch_flags branch_flags;
@@ -195,7 +194,7 @@ int callchain_merge(struct callchain_cursor *cursor,
void callchain_cursor_reset(struct callchain_cursor *cursor);
int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
- struct map *map, struct symbol *sym,
+ struct map_symbol *ms,
bool branch, struct branch_flags *flags,
int nr_loop_iter, u64 iter_cycles, u64 branch_from,
const char *srcline);
diff --git a/tools/perf/util/copyfile.c b/tools/perf/util/copyfile.c
index 3fa0db136667..47e03de7c235 100644
--- a/tools/perf/util/copyfile.c
+++ b/tools/perf/util/copyfile.c
@@ -101,14 +101,16 @@ static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
if (tofd < 0)
goto out;
- if (fchmod(tofd, mode))
- goto out_close_to;
-
if (st.st_size == 0) { /* /proc? do it slowly... */
err = slow_copyfile(from, tmp, nsi);
+ if (!err && fchmod(tofd, mode))
+ err = -1;
goto out_close_to;
}
+ if (fchmod(tofd, mode))
+ goto out_close_to;
+
nsinfo__mountns_enter(nsi, &nsc);
fromfd = open(from, O_RDONLY);
nsinfo__mountns_exit(&nsc);
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index a22c1114e880..983b7388f22b 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -206,6 +206,11 @@ int cpu_map__get_core_id(int cpu)
return ret ?: value;
}
+int cpu_map__get_node_id(int cpu)
+{
+ return cpu__get_node(cpu);
+}
+
int cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data)
{
int cpu, s_die;
@@ -235,6 +240,14 @@ int cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data)
return (s_die << 16) | (cpu & 0xffff);
}
+int cpu_map__get_node(struct perf_cpu_map *map, int idx, void *data __maybe_unused)
+{
+ if (idx < 0 || idx >= map->nr)
+ return -1;
+
+ return cpu_map__get_node_id(map->map[idx]);
+}
+
int cpu_map__build_socket_map(struct perf_cpu_map *cpus, struct perf_cpu_map **sockp)
{
return cpu_map__build_map(cpus, sockp, cpu_map__get_socket, NULL);
@@ -250,6 +263,11 @@ int cpu_map__build_core_map(struct perf_cpu_map *cpus, struct perf_cpu_map **cor
return cpu_map__build_map(cpus, corep, cpu_map__get_core, NULL);
}
+int cpu_map__build_node_map(struct perf_cpu_map *cpus, struct perf_cpu_map **numap)
+{
+ return cpu_map__build_map(cpus, numap, cpu_map__get_node, NULL);
+}
+
/* setup simple routines to easily access node numbers given a cpu number */
static int get_max_num(char *path, int *max)
{
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 2553bef1279d..57943f3685f8 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -20,9 +20,12 @@ int cpu_map__get_die_id(int cpu);
int cpu_map__get_die(struct perf_cpu_map *map, int idx, void *data);
int cpu_map__get_core_id(int cpu);
int cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data);
+int cpu_map__get_node_id(int cpu);
+int cpu_map__get_node(struct perf_cpu_map *map, int idx, void *data);
int cpu_map__build_socket_map(struct perf_cpu_map *cpus, struct perf_cpu_map **sockp);
int cpu_map__build_die_map(struct perf_cpu_map *cpus, struct perf_cpu_map **diep);
int cpu_map__build_core_map(struct perf_cpu_map *cpus, struct perf_cpu_map **corep);
+int cpu_map__build_node_map(struct perf_cpu_map *cpus, struct perf_cpu_map **nodep);
const struct perf_cpu_map *cpu_map__online(void); /* thread unsafe */
static inline int cpu_map__socket(struct perf_cpu_map *sock, int s)
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 4ba0f871f086..f5f855fff412 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -110,7 +110,7 @@ static int cs_etm__decode_data_block(struct cs_etm_queue *etmq);
* encode the etm queue number as the upper 16 bit and the channel as
* the lower 16 bit.
*/
-#define TO_CS_QUEUE_NR(queue_nr, trace_id_chan) \
+#define TO_CS_QUEUE_NR(queue_nr, trace_chan_id) \
(queue_nr << 16 | trace_chan_id)
#define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16)
#define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff)
@@ -819,7 +819,7 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
* Note that packets decoded above are still in the traceID's packet
* queue and will be processed in cs_etm__process_queues().
*/
- cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_id_chan);
+ cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_chan_id);
ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, timestamp);
out:
return ret;
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
index 88fba2ba549f..c47aa34fdc0a 100644
--- a/tools/perf/util/data.c
+++ b/tools/perf/util/data.c
@@ -76,6 +76,13 @@ int perf_data__open_dir(struct perf_data *data)
DIR *dir;
int nr = 0;
+ /*
+ * Directory containing a single regular perf data file which is already
+ * open, means there is nothing more to do here.
+ */
+ if (perf_data__is_single_file(data))
+ return 0;
+
if (WARN_ON(!data->is_dir))
return -EINVAL;
@@ -96,7 +103,7 @@ int perf_data__open_dir(struct perf_data *data)
if (stat(path, &st))
continue;
- if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data", 4))
+ if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data.", 5))
continue;
ret = -ENOMEM;
@@ -306,7 +313,7 @@ static int open_dir(struct perf_data *data)
* So far we open only the header, so we can read the data version and
* layout.
*/
- if (asprintf(&data->file.path, "%s/header", data->path) < 0)
+ if (asprintf(&data->file.path, "%s/data", data->path) < 0)
return -1;
if (perf_data__is_write(data) &&
@@ -406,7 +413,7 @@ unsigned long perf_data__size(struct perf_data *data)
u64 size = data->file.size;
int i;
- if (!data->is_dir)
+ if (perf_data__is_single_file(data))
return size;
for (i = 0; i < data->dir.nr; i++) {
@@ -417,3 +424,36 @@ unsigned long perf_data__size(struct perf_data *data)
return size;
}
+
+int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz)
+{
+ int ret;
+
+ if (!data->is_dir)
+ return -1;
+
+ ret = snprintf(buf, buf_sz, "%s/kcore_dir", data->path);
+ if (ret < 0 || (size_t)ret >= buf_sz)
+ return -1;
+
+ return mkdir(buf, S_IRWXU);
+}
+
+char *perf_data__kallsyms_name(struct perf_data *data)
+{
+ char *kallsyms_name;
+ struct stat st;
+
+ if (!data->is_dir)
+ return NULL;
+
+ if (asprintf(&kallsyms_name, "%s/kcore_dir/kallsyms", data->path) < 0)
+ return NULL;
+
+ if (stat(kallsyms_name, &st)) {
+ free(kallsyms_name);
+ return NULL;
+ }
+
+ return kallsyms_name;
+}
diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h
index 259868a39019..75947ef6bc17 100644
--- a/tools/perf/util/data.h
+++ b/tools/perf/util/data.h
@@ -9,6 +9,11 @@ enum perf_data_mode {
PERF_DATA_MODE_READ,
};
+enum perf_dir_version {
+ PERF_DIR_SINGLE_FILE = 0,
+ PERF_DIR_VERSION = 1,
+};
+
struct perf_data_file {
char *path;
int fd;
@@ -50,6 +55,11 @@ static inline bool perf_data__is_dir(struct perf_data *data)
return data->is_dir;
}
+static inline bool perf_data__is_single_file(struct perf_data *data)
+{
+ return data->dir.version == PERF_DIR_SINGLE_FILE;
+}
+
static inline int perf_data__fd(struct perf_data *data)
{
return data->file.fd;
@@ -77,4 +87,6 @@ int perf_data__open_dir(struct perf_data *data);
void perf_data__close_dir(struct perf_data *data);
int perf_data__update_dir(struct perf_data *data);
unsigned long perf_data__size(struct perf_data *data);
+int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz);
+char *perf_data__kallsyms_name(struct perf_data *data);
#endif /* __PERF_DATA_H */
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 752227b265e7..d029faf9fc9f 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -181,7 +181,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (al->map) {
struct dso *dso = al->map->dso;
- err = db_export__dso(dbe, dso, al->machine);
+ err = db_export__dso(dbe, dso, al->mg->machine);
if (err)
return err;
*dso_db_id = dso->db_id;
@@ -249,9 +249,9 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
* constructing an addr_location struct and then passing it to
* db_ids_from_al() to perform the export.
*/
- al.sym = node->sym;
- al.map = node->map;
- al.machine = machine;
+ al.sym = node->ms.sym;
+ al.map = node->ms.map;
+ al.mg = thread->mg;
al.addr = node->ip;
if (al.map && !al.sym)
@@ -360,13 +360,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
if (err)
return err;
- err = db_export__machine(dbe, al->machine);
+ err = db_export__machine(dbe, al->mg->machine);
if (err)
return err;
- main_thread = thread__main_thread(al->machine, thread);
+ main_thread = thread__main_thread(al->mg->machine, thread);
- err = db_export__threads(dbe, thread, main_thread, al->machine, &comm);
+ err = db_export__threads(dbe, thread, main_thread, al->mg->machine, &comm);
if (err)
goto out_put;
@@ -380,7 +380,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
goto out_put;
if (dbe->cpr) {
- struct call_path *cp = call_path_from_sample(dbe, al->machine,
+ struct call_path *cp = call_path_from_sample(dbe, al->mg->machine,
thread, sample,
evsel);
if (cp) {
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index e55114f0336f..adb656745ecc 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -24,6 +24,7 @@
#include <linux/ctype.h>
int verbose;
+int debug_peo_args;
bool dump_trace = false, quiet = false;
int debug_ordered_events;
static int redirect_to_stderr;
@@ -180,6 +181,7 @@ static struct debug_variable {
{ .name = "ordered-events", .ptr = &debug_ordered_events},
{ .name = "stderr", .ptr = &redirect_to_stderr},
{ .name = "data-convert", .ptr = &debug_data_convert },
+ { .name = "perf-event-open", .ptr = &debug_peo_args },
{ .name = NULL, }
};
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index d25ae1c4cee9..f1734abd98dd 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -8,6 +8,7 @@
#include <linux/compiler.h>
extern int verbose;
+extern int debug_peo_args;
extern bool quiet, dump_trace;
extern int debug_ordered_events;
extern int debug_data_convert;
@@ -30,6 +31,14 @@ extern int debug_data_convert;
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+/* Special macro to print perf_event_open arguments/return value. */
+#define pr_debug2_peo(fmt, ...) { \
+ if (debug_peo_args) \
+ pr_debugN(0, pr_fmt(fmt), ##__VA_ARGS__); \
+ else \
+ pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__); \
+}
+
#define pr_time_N(n, var, t, fmt, ...) \
eprintf_time(n, var, t, fmt, ##__VA_ARGS__)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index e11ddf86f2b3..91f21239608b 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -768,7 +768,7 @@ dso_cache__free(struct dso *dso)
pthread_mutex_unlock(&dso->lock);
}
-static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset)
+static struct dso_cache *__dso_cache__find(struct dso *dso, u64 offset)
{
const struct rb_root *root = &dso->data.cache;
struct rb_node * const *p = &root->rb_node;
@@ -827,14 +827,16 @@ out:
return cache;
}
-static ssize_t
-dso_cache__memcpy(struct dso_cache *cache, u64 offset,
- u8 *data, u64 size)
+static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data,
+ u64 size, bool out)
{
u64 cache_offset = offset - cache->offset;
u64 cache_size = min(cache->size - cache_offset, size);
- memcpy(data, cache->data + cache_offset, cache_size);
+ if (out)
+ memcpy(data, cache->data + cache_offset, cache_size);
+ else
+ memcpy(cache->data + cache_offset, data, cache_size);
return cache_size;
}
@@ -863,63 +865,73 @@ out:
return ret;
}
-static ssize_t
-dso_cache__read(struct dso *dso, struct machine *machine,
- u64 offset, u8 *data, ssize_t size)
+static struct dso_cache *dso_cache__populate(struct dso *dso,
+ struct machine *machine,
+ u64 offset, ssize_t *ret)
{
u64 cache_offset = offset & DSO__DATA_CACHE_MASK;
struct dso_cache *cache;
struct dso_cache *old;
- ssize_t ret;
cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
- if (!cache)
- return -ENOMEM;
+ if (!cache) {
+ *ret = -ENOMEM;
+ return NULL;
+ }
if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
- ret = bpf_read(dso, cache_offset, cache->data);
+ *ret = bpf_read(dso, cache_offset, cache->data);
else
- ret = file_read(dso, machine, cache_offset, cache->data);
+ *ret = file_read(dso, machine, cache_offset, cache->data);
- if (ret > 0) {
- cache->offset = cache_offset;
- cache->size = ret;
+ if (*ret <= 0) {
+ free(cache);
+ return NULL;
+ }
- old = dso_cache__insert(dso, cache);
- if (old) {
- /* we lose the race */
- free(cache);
- cache = old;
- }
+ cache->offset = cache_offset;
+ cache->size = *ret;
- ret = dso_cache__memcpy(cache, offset, data, size);
+ old = dso_cache__insert(dso, cache);
+ if (old) {
+ /* we lose the race */
+ free(cache);
+ cache = old;
}
- if (ret <= 0)
- free(cache);
+ return cache;
+}
- return ret;
+static struct dso_cache *dso_cache__find(struct dso *dso,
+ struct machine *machine,
+ u64 offset,
+ ssize_t *ret)
+{
+ struct dso_cache *cache = __dso_cache__find(dso, offset);
+
+ return cache ? cache : dso_cache__populate(dso, machine, offset, ret);
}
-static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
- u64 offset, u8 *data, ssize_t size)
+static ssize_t dso_cache_io(struct dso *dso, struct machine *machine,
+ u64 offset, u8 *data, ssize_t size, bool out)
{
struct dso_cache *cache;
+ ssize_t ret = 0;
- cache = dso_cache__find(dso, offset);
- if (cache)
- return dso_cache__memcpy(cache, offset, data, size);
- else
- return dso_cache__read(dso, machine, offset, data, size);
+ cache = dso_cache__find(dso, machine, offset, &ret);
+ if (!cache)
+ return ret;
+
+ return dso_cache__memcpy(cache, offset, data, size, out);
}
/*
* Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks
* in the rb_tree. Any read to already cached data is served
- * by cached data.
+ * by cached data. Writes update the cache only, not the backing file.
*/
-static ssize_t cached_read(struct dso *dso, struct machine *machine,
- u64 offset, u8 *data, ssize_t size)
+static ssize_t cached_io(struct dso *dso, struct machine *machine,
+ u64 offset, u8 *data, ssize_t size, bool out)
{
ssize_t r = 0;
u8 *p = data;
@@ -927,7 +939,7 @@ static ssize_t cached_read(struct dso *dso, struct machine *machine,
do {
ssize_t ret;
- ret = dso_cache_read(dso, machine, offset, p, size);
+ ret = dso_cache_io(dso, machine, offset, p, size, out);
if (ret < 0)
return ret;
@@ -1011,8 +1023,9 @@ off_t dso__data_size(struct dso *dso, struct machine *machine)
return dso->data.file_size;
}
-static ssize_t data_read_offset(struct dso *dso, struct machine *machine,
- u64 offset, u8 *data, ssize_t size)
+static ssize_t data_read_write_offset(struct dso *dso, struct machine *machine,
+ u64 offset, u8 *data, ssize_t size,
+ bool out)
{
if (dso__data_file_size(dso, machine))
return -1;
@@ -1024,7 +1037,7 @@ static ssize_t data_read_offset(struct dso *dso, struct machine *machine,
if (offset + size < offset)
return -1;
- return cached_read(dso, machine, offset, data, size);
+ return cached_io(dso, machine, offset, data, size, out);
}
/**
@@ -1044,7 +1057,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
if (dso->data.status == DSO_DATA_STATUS_ERROR)
return -1;
- return data_read_offset(dso, machine, offset, data, size);
+ return data_read_write_offset(dso, machine, offset, data, size, true);
}
/**
@@ -1065,6 +1078,46 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
return dso__data_read_offset(dso, machine, offset, data, size);
}
+/**
+ * dso__data_write_cache_offs - Write data to dso data cache at file offset
+ * @dso: dso object
+ * @machine: machine object
+ * @offset: file offset
+ * @data: buffer to write
+ * @size: size of the @data buffer
+ *
+ * Write into the dso file data cache, but do not change the file itself.
+ */
+ssize_t dso__data_write_cache_offs(struct dso *dso, struct machine *machine,
+ u64 offset, const u8 *data_in, ssize_t size)
+{
+ u8 *data = (u8 *)data_in; /* cast away const to use same fns for r/w */
+
+ if (dso->data.status == DSO_DATA_STATUS_ERROR)
+ return -1;
+
+ return data_read_write_offset(dso, machine, offset, data, size, false);
+}
+
+/**
+ * dso__data_write_cache_addr - Write data to dso data cache at dso address
+ * @dso: dso object
+ * @machine: machine object
+ * @add: virtual memory address
+ * @data: buffer to write
+ * @size: size of the @data buffer
+ *
+ * External interface to write into the dso file data cache, but do not change
+ * the file itself.
+ */
+ssize_t dso__data_write_cache_addr(struct dso *dso, struct map *map,
+ struct machine *machine, u64 addr,
+ const u8 *data, ssize_t size)
+{
+ u64 offset = map->map_ip(map, addr);
+ return dso__data_write_cache_offs(dso, machine, offset, data, size);
+}
+
struct map *dso__new_map(const char *name)
{
struct map *map = NULL;
@@ -1096,7 +1149,7 @@ struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
return dso;
}
-void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
+static void dso__set_long_name_id(struct dso *dso, const char *name, struct dso_id *id, bool name_allocated)
{
struct rb_root *root = dso->root;
@@ -1109,8 +1162,8 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
if (root) {
rb_erase(&dso->rb_node, root);
/*
- * __dsos__findnew_link_by_longname() isn't guaranteed to add it
- * back, so a clean removal is required here.
+ * __dsos__findnew_link_by_longname_id() isn't guaranteed to
+ * add it back, so a clean removal is required here.
*/
RB_CLEAR_NODE(&dso->rb_node);
dso->root = NULL;
@@ -1121,7 +1174,12 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
dso->long_name_allocated = name_allocated;
if (root)
- __dsos__findnew_link_by_longname(root, dso, NULL);
+ __dsos__findnew_link_by_longname_id(root, dso, NULL, id);
+}
+
+void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
+{
+ dso__set_long_name_id(dso, name, NULL, name_allocated);
}
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)
@@ -1162,13 +1220,15 @@ void dso__set_sorted_by_name(struct dso *dso)
dso->sorted_by_name = true;
}
-struct dso *dso__new(const char *name)
+struct dso *dso__new_id(const char *name, struct dso_id *id)
{
struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1);
if (dso != NULL) {
strcpy(dso->name, name);
- dso__set_long_name(dso, dso->name, false);
+ if (id)
+ dso->id = *id;
+ dso__set_long_name_id(dso, dso->name, id, false);
dso__set_short_name(dso, dso->name, false);
dso->symbols = dso->symbol_names = RB_ROOT_CACHED;
dso->data.cache = RB_ROOT;
@@ -1199,6 +1259,11 @@ struct dso *dso__new(const char *name)
return dso;
}
+struct dso *dso__new(const char *name)
+{
+ return dso__new_id(name, NULL);
+}
+
void dso__delete(struct dso *dso)
{
if (!RB_EMPTY_NODE(&dso->rb_node))
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index e4dddb76770d..2db64b79617a 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -122,6 +122,16 @@ enum dso_load_errno {
#define DSO__DATA_CACHE_SIZE 4096
#define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1)
+/*
+ * Data about backing storage DSO, comes from PERF_RECORD_MMAP2 meta events
+ */
+struct dso_id {
+ u32 maj;
+ u32 min;
+ u64 ino;
+ u64 ino_generation;
+};
+
struct dso_cache {
struct rb_node rb_node;
u64 offset;
@@ -196,6 +206,7 @@ struct dso {
u64 db_id;
};
struct nsinfo *nsinfo;
+ struct dso_id id;
refcount_t refcnt;
char name[0];
};
@@ -214,9 +225,11 @@ static inline void dso__set_loaded(struct dso *dso)
dso->loaded = true;
}
+struct dso *dso__new_id(const char *name, struct dso_id *id);
struct dso *dso__new(const char *name);
void dso__delete(struct dso *dso);
+int dso__cmp_id(struct dso *a, struct dso *b);
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated);
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);
@@ -285,6 +298,8 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
* dso__data_size
* dso__data_read_offset
* dso__data_read_addr
+ * dso__data_write_cache_offs
+ * dso__data_write_cache_addr
*
* Please refer to the dso.c object code for each function and
* arguments documentation. Following text tries to explain the
@@ -332,6 +347,11 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
u8 *data, ssize_t size);
bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by);
+ssize_t dso__data_write_cache_offs(struct dso *dso, struct machine *machine,
+ u64 offset, const u8 *data, ssize_t size);
+ssize_t dso__data_write_cache_addr(struct dso *dso, struct map *map,
+ struct machine *machine, u64 addr,
+ const u8 *data, ssize_t size);
struct map *dso__new_map(const char *name);
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 3ea80d203587..591707c69c39 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -9,6 +9,40 @@
#include <string.h>
#include <symbol.h> // filename__read_build_id
+static int __dso_id__cmp(struct dso_id *a, struct dso_id *b)
+{
+ if (a->maj > b->maj) return -1;
+ if (a->maj < b->maj) return 1;
+
+ if (a->min > b->min) return -1;
+ if (a->min < b->min) return 1;
+
+ if (a->ino > b->ino) return -1;
+ if (a->ino < b->ino) return 1;
+
+ if (a->ino_generation > b->ino_generation) return -1;
+ if (a->ino_generation < b->ino_generation) return 1;
+
+ return 0;
+}
+
+static int dso_id__cmp(struct dso_id *a, struct dso_id *b)
+{
+ /*
+ * The second is always dso->id, so zeroes if not set, assume passing
+ * NULL for a means a zeroed id
+ */
+ if (a == NULL)
+ return 0;
+
+ return __dso_id__cmp(a, b);
+}
+
+int dso__cmp_id(struct dso *a, struct dso *b)
+{
+ return __dso_id__cmp(&a->id, &b->id);
+}
+
bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
{
bool have_build_id = false;
@@ -34,12 +68,30 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
return have_build_id;
}
+static int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b)
+{
+ int rc = strcmp(long_name, b->long_name);
+ return rc ?: dso_id__cmp(id, &b->id);
+}
+
+static int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b)
+{
+ int rc = strcmp(short_name, b->short_name);
+ return rc ?: dso_id__cmp(id, &b->id);
+}
+
+static int dso__cmp_short_name(struct dso *a, struct dso *b)
+{
+ return __dso__cmp_short_name(a->short_name, &a->id, b);
+}
+
/*
* Find a matching entry and/or link current entry to RB tree.
* Either one of the dso or name parameter must be non-NULL or the
* function will not work.
*/
-struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *dso, const char *name)
+struct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso,
+ const char *name, struct dso_id *id)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
@@ -51,7 +103,7 @@ struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *d
*/
while (*p) {
struct dso *this = rb_entry(*p, struct dso, rb_node);
- int rc = strcmp(name, this->long_name);
+ int rc = __dso__cmp_long_name(name, id, this);
parent = *p;
if (rc == 0) {
@@ -67,7 +119,7 @@ struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *d
* In this case, the short name should be different.
* Comparing the short names to differentiate the DSOs.
*/
- rc = strcmp(dso->short_name, this->short_name);
+ rc = dso__cmp_short_name(dso, this);
if (rc == 0) {
pr_err("Duplicated dso name: %s\n", name);
return NULL;
@@ -90,7 +142,7 @@ struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *d
void __dsos__add(struct dsos *dsos, struct dso *dso)
{
list_add_tail(&dso->node, &dsos->head);
- __dsos__findnew_link_by_longname(&dsos->root, dso, NULL);
+ __dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id);
/*
* It is now in the linked list, grab a reference, then garbage collect
* this when needing memory, by looking at LRU dso instances in the
@@ -121,26 +173,27 @@ void dsos__add(struct dsos *dsos, struct dso *dso)
up_write(&dsos->lock);
}
-struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+static struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id)
+{
+ return __dsos__findnew_link_by_longname_id(root, NULL, name, id);
+}
+
+static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short)
{
struct dso *pos;
if (cmp_short) {
list_for_each_entry(pos, &dsos->head, node)
- if (strcmp(pos->short_name, name) == 0)
+ if (__dso__cmp_short_name(name, id, pos) == 0)
return pos;
return NULL;
}
- return __dsos__findnew_by_longname(&dsos->root, name);
+ return __dsos__findnew_by_longname_id(&dsos->root, name, id);
}
-struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
{
- struct dso *dso;
- down_read(&dsos->lock);
- dso = __dsos__find(dsos, name, cmp_short);
- up_read(&dsos->lock);
- return dso;
+ return __dsos__find_id(dsos, name, NULL, cmp_short);
}
static void dso__set_basename(struct dso *dso)
@@ -175,9 +228,9 @@ static void dso__set_basename(struct dso *dso)
dso__set_short_name(dso, base, true);
}
-struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
+static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
{
- struct dso *dso = dso__new(name);
+ struct dso *dso = dso__new_id(name, id);
if (dso != NULL) {
__dsos__add(dsos, dso);
@@ -188,18 +241,22 @@ struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
return dso;
}
-struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
{
- struct dso *dso = __dsos__find(dsos, name, false);
+ return __dsos__addnew_id(dsos, name, NULL);
+}
- return dso ? dso : __dsos__addnew(dsos, name);
+static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
+{
+ struct dso *dso = __dsos__find_id(dsos, name, id, false);
+ return dso ? dso : __dsos__addnew_id(dsos, name, id);
}
-struct dso *dsos__findnew(struct dsos *dsos, const char *name)
+struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
{
struct dso *dso;
down_write(&dsos->lock);
- dso = dso__get(__dsos__findnew(dsos, name));
+ dso = dso__get(__dsos__findnew_id(dsos, name, id));
up_write(&dsos->lock);
return dso;
}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index 32f1fbee0feb..5dbec2bc6966 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -9,6 +9,7 @@
#include "rwsem.h"
struct dso;
+struct dso_id;
/*
* DSOs are put into both a list for fast iteration and rbtree for fast
@@ -24,16 +25,11 @@ void __dsos__add(struct dsos *dsos, struct dso *dso);
void dsos__add(struct dsos *dsos, struct dso *dso);
struct dso *__dsos__addnew(struct dsos *dsos, const char *name);
struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
-struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
-struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
-struct dso *dsos__findnew(struct dsos *dsos, const char *name);
-struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *dso, const char *name);
-
-static inline struct dso *__dsos__findnew_by_longname(struct rb_root *root, const char *name)
-{
- return __dsos__findnew_link_by_longname(root, NULL, name);
-}
+struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id);
+
+struct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso,
+ const char *name, struct dso_id *id);
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index df6cee5c071f..aa898014ad12 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -59,6 +59,51 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die)
return dwarf_formstring(&attr);
}
+/* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */
+static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
+{
+ Dwarf_Addr laddr;
+ Dwarf_Lines *lines;
+ Dwarf_Line *line;
+ size_t nlines, l, u, n;
+ bool flag;
+
+ if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0 ||
+ nlines == 0)
+ return NULL;
+
+ /* Lines are sorted by address, use binary search */
+ l = 0; u = nlines - 1;
+ while (l < u) {
+ n = u - (u - l) / 2;
+ line = dwarf_onesrcline(lines, n);
+ if (!line || dwarf_lineaddr(line, &laddr) != 0)
+ return NULL;
+ if (addr < laddr)
+ u = n - 1;
+ else
+ l = n;
+ }
+ /* Going backward to find the lowest line */
+ do {
+ line = dwarf_onesrcline(lines, --l);
+ if (!line || dwarf_lineaddr(line, &laddr) != 0)
+ return NULL;
+ } while (laddr == addr);
+ l++;
+ /* Going foward to find the statement line */
+ do {
+ line = dwarf_onesrcline(lines, l++);
+ if (!line || dwarf_lineaddr(line, &laddr) != 0 ||
+ dwarf_linebeginstatement(line, &flag) != 0)
+ return NULL;
+ if (laddr > addr)
+ return NULL;
+ } while (!flag);
+
+ return line;
+}
+
/**
* cu_find_lineinfo - Get a line number and file name for given address
* @cu_die: a CU DIE
@@ -72,17 +117,26 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
const char **fname, int *lineno)
{
Dwarf_Line *line;
- Dwarf_Addr laddr;
+ Dwarf_Die die_mem;
+ Dwarf_Addr faddr;
+
+ if (die_find_realfunc(cu_die, (Dwarf_Addr)addr, &die_mem)
+ && die_entrypc(&die_mem, &faddr) == 0 &&
+ faddr == addr) {
+ *fname = dwarf_decl_file(&die_mem);
+ dwarf_decl_line(&die_mem, lineno);
+ goto out;
+ }
- line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr);
- if (line && dwarf_lineaddr(line, &laddr) == 0 &&
- addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
+ line = cu_getsrc_die(cu_die, (Dwarf_Addr)addr);
+ if (line && dwarf_lineno(line, lineno) == 0) {
*fname = dwarf_linesrc(line, NULL, NULL);
if (!*fname)
/* line number is useless without filename */
*lineno = 0;
}
+out:
return *lineno ?: -ENOENT;
}
@@ -308,20 +362,50 @@ bool die_is_func_def(Dwarf_Die *dw_die)
}
/**
+ * die_entrypc - Returns entry PC (the lowest address) of a DIE
+ * @dw_die: a DIE
+ * @addr: where to store entry PC
+ *
+ * Since dwarf_entrypc() does not return entry PC if the DIE has only address
+ * range, we have to use this to retrieve the lowest address from the address
+ * range attribute.
+ */
+int die_entrypc(Dwarf_Die *dw_die, Dwarf_Addr *addr)
+{
+ Dwarf_Addr base, end;
+
+ if (!addr)
+ return -EINVAL;
+
+ if (dwarf_entrypc(dw_die, addr) == 0)
+ return 0;
+
+ return dwarf_ranges(dw_die, 0, &base, addr, &end) < 0 ? -ENOENT : 0;
+}
+
+/**
* die_is_func_instance - Ensure that this DIE is an instance of a subprogram
* @dw_die: a DIE
*
* Ensure that this DIE is an instance (which has an entry address).
- * This returns true if @dw_die is a function instance. If not, you need to
- * call die_walk_instances() to find actual instances.
+ * This returns true if @dw_die is a function instance. If not, the @dw_die
+ * must be a prototype. You can use die_walk_instances() to find actual
+ * instances.
**/
bool die_is_func_instance(Dwarf_Die *dw_die)
{
Dwarf_Addr tmp;
+ Dwarf_Attribute attr_mem;
+ int tag = dwarf_tag(dw_die);
+
+ if (tag != DW_TAG_subprogram &&
+ tag != DW_TAG_inlined_subroutine)
+ return false;
- /* Actually gcc optimizes non-inline as like as inlined */
- return !dwarf_func_inline(dw_die) && dwarf_entrypc(dw_die, &tmp) == 0;
+ return dwarf_entrypc(dw_die, &tmp) == 0 ||
+ dwarf_attr(dw_die, DW_AT_ranges, &attr_mem) != NULL;
}
+
/**
* die_get_data_member_location - Get the data-member offset
* @mb_die: a DIE of a member of a data structure
@@ -598,6 +682,9 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data)
Dwarf_Die *origin;
int tmp;
+ if (!die_is_func_instance(inst))
+ return DIE_FIND_CB_CONTINUE;
+
attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem);
if (attr == NULL)
return DIE_FIND_CB_CONTINUE;
@@ -669,15 +756,14 @@ static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
fname = die_get_call_file(in_die);
lineno = die_get_call_lineno(in_die);
- if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
+ if (fname && lineno > 0 && die_entrypc(in_die, &addr) == 0) {
lw->retval = lw->callback(fname, lineno, addr, lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_END;
}
+ if (!lw->recursive)
+ return DIE_FIND_CB_SIBLING;
}
- if (!lw->recursive)
- /* Don't need to search recursively */
- return DIE_FIND_CB_SIBLING;
if (addr) {
fname = dwarf_decl_file(in_die);
@@ -710,7 +796,7 @@ static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive,
/* Handle function declaration line */
fname = dwarf_decl_file(sp_die);
if (fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
- dwarf_entrypc(sp_die, &addr) == 0) {
+ die_entrypc(sp_die, &addr) == 0) {
lw.retval = callback(fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
@@ -724,6 +810,10 @@ static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
struct __line_walk_param *lw = data;
+ /*
+ * Since inlined function can include another inlined function in
+ * the same file, we need to walk in it recursively.
+ */
lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
@@ -748,11 +838,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
Dwarf_Lines *lines;
Dwarf_Line *line;
Dwarf_Addr addr;
- const char *fname, *decf = NULL;
+ const char *fname, *decf = NULL, *inf = NULL;
int lineno, ret = 0;
int decl = 0, inl;
Dwarf_Die die_mem, *cu_die;
size_t nlines, i;
+ bool flag;
/* Get the CU die */
if (dwarf_tag(rt_die) != DW_TAG_compile_unit) {
@@ -783,6 +874,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
"Possible error in debuginfo.\n");
continue;
}
+ /* Skip end-of-sequence */
+ if (dwarf_lineendsequence(line, &flag) != 0 || flag)
+ continue;
+ /* Skip Non statement line-info */
+ if (dwarf_linebeginstatement(line, &flag) != 0 || !flag)
+ continue;
/* Filter lines based on address */
if (rt_die != cu_die) {
/*
@@ -792,13 +889,21 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
*/
if (!dwarf_haspc(rt_die, addr))
continue;
+
if (die_find_inlinefunc(rt_die, addr, &die_mem)) {
+ /* Call-site check */
+ inf = die_get_call_file(&die_mem);
+ if ((inf && !strcmp(inf, decf)) &&
+ die_get_call_lineno(&die_mem) == lineno)
+ goto found;
+
dwarf_decl_line(&die_mem, &inl);
if (inl != decl ||
decf != dwarf_decl_file(&die_mem))
continue;
}
}
+found:
/* Get source line */
fname = dwarf_linesrc(line, NULL, NULL);
@@ -813,8 +918,9 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
*/
if (rt_die != cu_die)
/*
- * Don't need walk functions recursively, because nested
- * inlined functions don't have lines of the specified DIE.
+ * Don't need walk inlined functions recursively, because
+ * inner inlined functions don't have the lines of the
+ * specified function.
*/
ret = __die_walk_funclines(rt_die, false, callback, data);
else {
@@ -989,7 +1095,7 @@ static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
bool first = true;
const char *name;
- ret = dwarf_entrypc(sp_die, &entry);
+ ret = die_entrypc(sp_die, &entry);
if (ret)
return ret;
@@ -1052,7 +1158,7 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
bool first = true;
const char *name;
- ret = dwarf_entrypc(sp_die, &entry);
+ ret = die_entrypc(sp_die, &entry);
if (ret)
return ret;
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index f204e5892403..506006e0cf66 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -29,6 +29,9 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
/* Get DW_AT_linkage_name (should be NULL for C binary) */
const char *die_get_linkage_name(Dwarf_Die *dw_die);
+/* Get the lowest PC in DIE (including range list) */
+int die_entrypc(Dwarf_Die *dw_die, Dwarf_Addr *addr);
+
/* Ensure that this DIE is a subprogram and definition (not declaration) */
bool die_is_func_def(Dwarf_Die *dw_die);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 3baca06786fb..6242a9215df7 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -2,6 +2,7 @@
#include "cpumap.h"
#include "debug.h"
#include "env.h"
+#include "util/header.h"
#include <linux/ctype.h>
#include <linux/zalloc.h>
#include "bpf-event.h"
@@ -179,6 +180,7 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->sibling_threads);
zfree(&env->pmu_mappings);
zfree(&env->cpu);
+ zfree(&env->numa_map);
for (i = 0; i < env->nr_numa_nodes; i++)
perf_cpu_map__put(env->numa_nodes[i].map);
@@ -256,6 +258,21 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
return 0;
}
+int perf_env__read_cpuid(struct perf_env *env)
+{
+ char cpuid[128];
+ int err = get_cpuid(cpuid, sizeof(cpuid));
+
+ if (err)
+ return err;
+
+ free(env->cpuid);
+ env->cpuid = strdup(cpuid);
+ if (env->cpuid == NULL)
+ return ENOMEM;
+ return 0;
+}
+
static int perf_env__read_arch(struct perf_env *env)
{
struct utsname uts;
@@ -338,3 +355,42 @@ const char *perf_env__arch(struct perf_env *env)
return normalize_arch(arch_name);
}
+
+
+int perf_env__numa_node(struct perf_env *env, int cpu)
+{
+ if (!env->nr_numa_map) {
+ struct numa_node *nn;
+ int i, nr = 0;
+
+ for (i = 0; i < env->nr_numa_nodes; i++) {
+ nn = &env->numa_nodes[i];
+ nr = max(nr, perf_cpu_map__max(nn->map));
+ }
+
+ nr++;
+
+ /*
+ * We initialize the numa_map array to prepare
+ * it for missing cpus, which return node -1
+ */
+ env->numa_map = malloc(nr * sizeof(int));
+ if (!env->numa_map)
+ return -1;
+
+ for (i = 0; i < nr; i++)
+ env->numa_map[i] = -1;
+
+ env->nr_numa_map = nr;
+
+ for (i = 0; i < env->nr_numa_nodes; i++) {
+ int tmp, j;
+
+ nn = &env->numa_nodes[i];
+ perf_cpu_map__for_each_cpu(j, tmp, nn->map)
+ env->numa_map[j] = i;
+ }
+ }
+
+ return cpu >= 0 && cpu < env->nr_numa_map ? env->numa_map[cpu] : -1;
+}
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index db40906e2937..11d05ae3606a 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -87,6 +87,10 @@ struct perf_env {
struct rb_root btfs;
u32 btfs_cnt;
} bpf_progs;
+
+ /* For fast cpu to numa node lookup via perf_env__numa_node */
+ int *numa_map;
+ int nr_numa_map;
};
enum perf_compress_type {
@@ -104,6 +108,7 @@ void perf_env__exit(struct perf_env *env);
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);
+int perf_env__read_cpuid(struct perf_env *env);
int perf_env__read_cpu_topology_map(struct perf_env *env);
void cpu_cache_level__free(struct cpu_cache_level *cache);
@@ -119,4 +124,6 @@ struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
__u32 prog_id);
void perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
+
+int perf_env__numa_node(struct perf_env *env, int cpu);
#endif /* __PERF_ENV_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index fc1e5a991008..0141b26bae47 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -461,7 +461,7 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
struct machine *machine = mg->machine;
bool load_map = false;
- al->machine = machine;
+ al->mg = mg;
al->thread = thread;
al->addr = addr;
al->cpumode = cpumode;
@@ -474,13 +474,13 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
al->level = 'k';
- mg = &machine->kmaps;
+ al->mg = mg = &machine->kmaps;
load_map = true;
} else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
al->level = '.';
} else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
al->level = 'g';
- mg = &machine->kmaps;
+ al->mg = mg = &machine->kmaps;
load_map = true;
} else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) {
al->level = 'u';
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index a0a0c91cde4a..85223159737c 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -114,6 +114,11 @@ enum {
#define MAX_INSN 16
+struct aux_sample {
+ u64 size;
+ void *data;
+};
+
struct perf_sample {
u64 ip;
u32 pid, tid;
@@ -142,6 +147,7 @@ struct perf_sample {
struct regs_dump intr_regs;
struct stack_dump user_stack;
struct sample_read read;
+ struct aux_sample aux_sample;
};
#define PERF_MEM_DATA_SRC_NONE \
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index d277a98e62df..fdce590d2278 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -21,6 +21,7 @@
#include "../perf.h"
#include "asm/bug.h"
#include "bpf-event.h"
+#include "util/string2.h"
#include <signal.h>
#include <unistd.h>
#include <sched.h>
@@ -42,6 +43,7 @@
#include <perf/evlist.h>
#include <perf/evsel.h>
#include <perf/cpumap.h>
+#include <perf/mmap.h>
#include <internal/xyarray.h>
@@ -57,7 +59,6 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
{
perf_evlist__init(&evlist->core);
perf_evlist__set_maps(&evlist->core, cpus, threads);
- fdarray__init(&evlist->core.pollfd, 64);
evlist->workload.pid = -1;
evlist->bkw_mmap_state = BKW_MMAP_NOTREADY;
}
@@ -138,7 +139,7 @@ void evlist__exit(struct evlist *evlist)
{
zfree(&evlist->mmap);
zfree(&evlist->overwrite_mmap);
- fdarray__exit(&evlist->core.pollfd);
+ perf_evlist__exit(&evlist->core);
}
void evlist__delete(struct evlist *evlist)
@@ -148,10 +149,6 @@ void evlist__delete(struct evlist *evlist)
evlist__munmap(evlist);
evlist__close(evlist);
- perf_cpu_map__put(evlist->core.cpus);
- perf_thread_map__put(evlist->core.threads);
- evlist->core.cpus = NULL;
- evlist->core.threads = NULL;
evlist__purge(evlist);
evlist__exit(evlist);
free(evlist);
@@ -186,6 +183,30 @@ void perf_evlist__splice_list_tail(struct evlist *evlist,
}
}
+int __evlist__set_tracepoints_handlers(struct evlist *evlist,
+ const struct evsel_str_handler *assocs, size_t nr_assocs)
+{
+ struct evsel *evsel;
+ size_t i;
+ int err;
+
+ for (i = 0; i < nr_assocs; i++) {
+ // Adding a handler for an event not in this evlist, just ignore it.
+ evsel = perf_evlist__find_tracepoint_by_name(evlist, assocs[i].name);
+ if (evsel == NULL)
+ continue;
+
+ err = -EEXIST;
+ if (evsel->handler != NULL)
+ goto out;
+ evsel->handler = assocs[i].handler;
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
void __perf_evlist__set_leader(struct list_head *list)
{
struct evsel *evsel, *leader;
@@ -403,19 +424,9 @@ int evlist__add_pollfd(struct evlist *evlist, int fd)
return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN);
}
-static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,
- void *arg __maybe_unused)
-{
- struct mmap *map = fda->priv[fd].ptr;
-
- if (map)
- perf_mmap__put(map);
-}
-
int evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask)
{
- return fdarray__filter(&evlist->core.pollfd, revents_and_mask,
- perf_evlist__munmap_filtered, NULL);
+ return perf_evlist__filter_pollfd(&evlist->core, revents_and_mask);
}
int evlist__poll(struct evlist *evlist, int timeout)
@@ -423,22 +434,6 @@ int evlist__poll(struct evlist *evlist, int timeout)
return perf_evlist__poll(&evlist->core, timeout);
}
-static void perf_evlist__set_sid_idx(struct evlist *evlist,
- struct evsel *evsel, int idx, int cpu,
- int thread)
-{
- struct perf_sample_id *sid = SID(evsel, cpu, thread);
- sid->idx = idx;
- if (evlist->core.cpus && cpu >= 0)
- sid->cpu = evlist->core.cpus->map[cpu];
- else
- sid->cpu = -1;
- if (!evsel->core.system_wide && evlist->core.threads && thread >= 0)
- sid->tid = perf_thread_map__pid(evlist->core.threads, thread);
- else
- sid->tid = -1;
-}
-
struct perf_sample_id *perf_evlist__id2sid(struct evlist *evlist, u64 id)
{
struct hlist_head *head;
@@ -577,11 +572,11 @@ static void evlist__munmap_nofree(struct evlist *evlist)
if (evlist->mmap)
for (i = 0; i < evlist->core.nr_mmaps; i++)
- perf_mmap__munmap(&evlist->mmap[i]);
+ perf_mmap__munmap(&evlist->mmap[i].core);
if (evlist->overwrite_mmap)
for (i = 0; i < evlist->core.nr_mmaps; i++)
- perf_mmap__munmap(&evlist->overwrite_mmap[i]);
+ perf_mmap__munmap(&evlist->overwrite_mmap[i].core);
}
void evlist__munmap(struct evlist *evlist)
@@ -591,22 +586,26 @@ void evlist__munmap(struct evlist *evlist)
zfree(&evlist->overwrite_mmap);
}
+static void perf_mmap__unmap_cb(struct perf_mmap *map)
+{
+ struct mmap *m = container_of(map, struct mmap, core);
+
+ mmap__munmap(m);
+}
+
static struct mmap *evlist__alloc_mmap(struct evlist *evlist,
bool overwrite)
{
int i;
struct mmap *map;
- evlist->core.nr_mmaps = perf_cpu_map__nr(evlist->core.cpus);
- if (perf_cpu_map__empty(evlist->core.cpus))
- evlist->core.nr_mmaps = perf_thread_map__nr(evlist->core.threads);
map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap));
if (!map)
return NULL;
for (i = 0; i < evlist->core.nr_mmaps; i++) {
- map[i].core.fd = -1;
- map[i].core.overwrite = overwrite;
+ struct perf_mmap *prev = i ? &map[i - 1].core : NULL;
+
/*
* When the perf_mmap() call is made we grab one refcount, plus
* one extra to let perf_mmap__consume() get the last
@@ -616,151 +615,56 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist,
* Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and
* thus does perf_mmap__get() on it.
*/
- refcount_set(&map[i].core.refcnt, 0);
+ perf_mmap__init(&map[i].core, prev, overwrite, perf_mmap__unmap_cb);
}
+
return map;
}
-static bool
-perf_evlist__should_poll(struct evlist *evlist __maybe_unused,
- struct evsel *evsel)
+static void
+perf_evlist__mmap_cb_idx(struct perf_evlist *_evlist,
+ struct perf_mmap_param *_mp,
+ int idx, bool per_cpu)
{
- if (evsel->core.attr.write_backward)
- return false;
- return true;
+ struct evlist *evlist = container_of(_evlist, struct evlist, core);
+ struct mmap_params *mp = container_of(_mp, struct mmap_params, core);
+
+ auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, idx, per_cpu);
}
-static int evlist__mmap_per_evsel(struct evlist *evlist, int idx,
- struct mmap_params *mp, int cpu_idx,
- int thread, int *_output, int *_output_overwrite)
+static struct perf_mmap*
+perf_evlist__mmap_cb_get(struct perf_evlist *_evlist, bool overwrite, int idx)
{
- struct evsel *evsel;
- int revent;
- int evlist_cpu = cpu_map__cpu(evlist->core.cpus, cpu_idx);
-
- evlist__for_each_entry(evlist, evsel) {
- struct mmap *maps = evlist->mmap;
- int *output = _output;
- int fd;
- int cpu;
-
- mp->prot = PROT_READ | PROT_WRITE;
- if (evsel->core.attr.write_backward) {
- output = _output_overwrite;
- maps = evlist->overwrite_mmap;
-
- if (!maps) {
- maps = evlist__alloc_mmap(evlist, true);
- if (!maps)
- return -1;
- evlist->overwrite_mmap = maps;
- if (evlist->bkw_mmap_state == BKW_MMAP_NOTREADY)
- perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_RUNNING);
- }
- mp->prot &= ~PROT_WRITE;
- }
-
- if (evsel->core.system_wide && thread)
- continue;
+ struct evlist *evlist = container_of(_evlist, struct evlist, core);
+ struct mmap *maps;
- cpu = perf_cpu_map__idx(evsel->core.cpus, evlist_cpu);
- if (cpu == -1)
- continue;
-
- fd = FD(evsel, cpu, thread);
+ maps = overwrite ? evlist->overwrite_mmap : evlist->mmap;
- if (*output == -1) {
- *output = fd;
+ if (!maps) {
+ maps = evlist__alloc_mmap(evlist, overwrite);
+ if (!maps)
+ return NULL;
- if (perf_mmap__mmap(&maps[idx], mp, *output, evlist_cpu) < 0)
- return -1;
+ if (overwrite) {
+ evlist->overwrite_mmap = maps;
+ if (evlist->bkw_mmap_state == BKW_MMAP_NOTREADY)
+ perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_RUNNING);
} else {
- if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
- return -1;
-
- perf_mmap__get(&maps[idx]);
- }
-
- revent = perf_evlist__should_poll(evlist, evsel) ? POLLIN : 0;
-
- /*
- * The system_wide flag causes a selected event to be opened
- * always without a pid. Consequently it will never get a
- * POLLHUP, but it is used for tracking in combination with
- * other events, so it should not need to be polled anyway.
- * Therefore don't add it for polling.
- */
- if (!evsel->core.system_wide &&
- perf_evlist__add_pollfd(&evlist->core, fd, &maps[idx], revent) < 0) {
- perf_mmap__put(&maps[idx]);
- return -1;
- }
-
- if (evsel->core.attr.read_format & PERF_FORMAT_ID) {
- if (perf_evlist__id_add_fd(&evlist->core, &evsel->core, cpu, thread,
- fd) < 0)
- return -1;
- perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
- thread);
+ evlist->mmap = maps;
}
}
- return 0;
+ return &maps[idx].core;
}
-static int evlist__mmap_per_cpu(struct evlist *evlist,
- struct mmap_params *mp)
+static int
+perf_evlist__mmap_cb_mmap(struct perf_mmap *_map, struct perf_mmap_param *_mp,
+ int output, int cpu)
{
- int cpu, thread;
- int nr_cpus = perf_cpu_map__nr(evlist->core.cpus);
- int nr_threads = perf_thread_map__nr(evlist->core.threads);
-
- pr_debug2("perf event ring buffer mmapped per cpu\n");
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- int output = -1;
- int output_overwrite = -1;
-
- auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, cpu,
- true);
+ struct mmap *map = container_of(_map, struct mmap, core);
+ struct mmap_params *mp = container_of(_mp, struct mmap_params, core);
- for (thread = 0; thread < nr_threads; thread++) {
- if (evlist__mmap_per_evsel(evlist, cpu, mp, cpu,
- thread, &output, &output_overwrite))
- goto out_unmap;
- }
- }
-
- return 0;
-
-out_unmap:
- evlist__munmap_nofree(evlist);
- return -1;
-}
-
-static int evlist__mmap_per_thread(struct evlist *evlist,
- struct mmap_params *mp)
-{
- int thread;
- int nr_threads = perf_thread_map__nr(evlist->core.threads);
-
- pr_debug2("perf event ring buffer mmapped per thread\n");
- for (thread = 0; thread < nr_threads; thread++) {
- int output = -1;
- int output_overwrite = -1;
-
- auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, thread,
- false);
-
- if (evlist__mmap_per_evsel(evlist, thread, mp, 0, thread,
- &output, &output_overwrite))
- goto out_unmap;
- }
-
- return 0;
-
-out_unmap:
- evlist__munmap_nofree(evlist);
- return -1;
+ return mmap__mmap(map, mp, output, cpu);
}
unsigned long perf_event_mlock_kb_in_pages(void)
@@ -890,43 +794,30 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned int pages,
bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush,
int comp_level)
{
- struct evsel *evsel;
- const struct perf_cpu_map *cpus = evlist->core.cpus;
- const struct perf_thread_map *threads = evlist->core.threads;
/*
* Delay setting mp.prot: set it before calling perf_mmap__mmap.
* Its value is decided by evsel's write_backward.
* So &mp should not be passed through const pointer.
*/
- struct mmap_params mp = { .nr_cblocks = nr_cblocks, .affinity = affinity, .flush = flush,
- .comp_level = comp_level };
-
- if (!evlist->mmap)
- evlist->mmap = evlist__alloc_mmap(evlist, false);
- if (!evlist->mmap)
- return -ENOMEM;
-
- if (evlist->core.pollfd.entries == NULL && perf_evlist__alloc_pollfd(&evlist->core) < 0)
- return -ENOMEM;
+ struct mmap_params mp = {
+ .nr_cblocks = nr_cblocks,
+ .affinity = affinity,
+ .flush = flush,
+ .comp_level = comp_level
+ };
+ struct perf_evlist_mmap_ops ops = {
+ .idx = perf_evlist__mmap_cb_idx,
+ .get = perf_evlist__mmap_cb_get,
+ .mmap = perf_evlist__mmap_cb_mmap,
+ };
evlist->core.mmap_len = evlist__mmap_size(pages);
pr_debug("mmap size %zuB\n", evlist->core.mmap_len);
- mp.mask = evlist->core.mmap_len - page_size - 1;
auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->core.mmap_len,
auxtrace_pages, auxtrace_overwrite);
- evlist__for_each_entry(evlist, evsel) {
- if ((evsel->core.attr.read_format & PERF_FORMAT_ID) &&
- evsel->core.sample_id == NULL &&
- perf_evsel__alloc_id(&evsel->core, perf_cpu_map__nr(cpus), threads->nr) < 0)
- return -ENOMEM;
- }
-
- if (perf_cpu_map__empty(cpus))
- return evlist__mmap_per_thread(evlist, &mp);
-
- return evlist__mmap_per_cpu(evlist, &mp);
+ return perf_evlist__mmap_ops(&evlist->core, &ops, &mp.core);
}
int evlist__mmap(struct evlist *evlist, unsigned int pages)
@@ -1029,6 +920,9 @@ int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter)
struct evsel *evsel;
int err = 0;
+ if (filter == NULL)
+ return -1;
+
evlist__for_each_entry(evlist, evsel) {
if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
continue;
@@ -1041,16 +935,35 @@ int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter)
return err;
}
-int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids)
+int perf_evlist__append_tp_filter(struct evlist *evlist, const char *filter)
+{
+ struct evsel *evsel;
+ int err = 0;
+
+ if (filter == NULL)
+ return -1;
+
+ evlist__for_each_entry(evlist, evsel) {
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+
+ err = perf_evsel__append_tp_filter(evsel, filter);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+char *asprintf__tp_filter_pids(size_t npids, pid_t *pids)
{
char *filter;
- int ret = -1;
size_t i;
for (i = 0; i < npids; ++i) {
if (i == 0) {
if (asprintf(&filter, "common_pid != %d", pids[i]) < 0)
- return -1;
+ return NULL;
} else {
char *tmp;
@@ -1062,9 +975,18 @@ int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *
}
}
- ret = perf_evlist__set_tp_filter(evlist, filter);
+ return filter;
out_free:
free(filter);
+ return NULL;
+}
+
+int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids)
+{
+ char *filter = asprintf__tp_filter_pids(npids, pids);
+ int ret = perf_evlist__set_tp_filter(evlist, filter);
+
+ free(filter);
return ret;
}
@@ -1073,6 +995,20 @@ int perf_evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid)
return perf_evlist__set_tp_filter_pids(evlist, 1, &pid);
}
+int perf_evlist__append_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids)
+{
+ char *filter = asprintf__tp_filter_pids(npids, pids);
+ int ret = perf_evlist__append_tp_filter(evlist, filter);
+
+ free(filter);
+ return ret;
+}
+
+int perf_evlist__append_tp_filter_pid(struct evlist *evlist, pid_t pid)
+{
+ return perf_evlist__append_tp_filter_pids(evlist, 1, &pid);
+}
+
bool perf_evlist__valid_sample_type(struct evlist *evlist)
{
struct evsel *pos;
@@ -1659,7 +1595,7 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
is_open = false;
if (c2->leader == leader) {
if (is_open)
- perf_evsel__close(&evsel->core);
+ perf_evsel__close(&c2->core);
c2->leader = c2;
c2->core.nr_members = 0;
}
@@ -1729,9 +1665,9 @@ static void *perf_evlist__poll_thread(void *arg)
struct mmap *map = &evlist->mmap[i];
union perf_event *event;
- if (perf_mmap__read_init(map))
+ if (perf_mmap__read_init(&map->core))
continue;
- while ((event = perf_mmap__read_event(map)) != NULL) {
+ while ((event = perf_mmap__read_event(&map->core)) != NULL) {
struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
if (evsel && evsel->side_band.cb)
@@ -1739,10 +1675,10 @@ static void *perf_evlist__poll_thread(void *arg)
else
pr_warning("cannot locate proper evsel for the side band event\n");
- perf_mmap__consume(map);
+ perf_mmap__consume(&map->core);
got_data = true;
}
- perf_mmap__read_done(map);
+ perf_mmap__read_done(&map->core);
}
if (draining && !got_data)
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 7cfe75522ba5..3655b9ebb147 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -118,6 +118,13 @@ void perf_evlist__stop_sb_thread(struct evlist *evlist);
int perf_evlist__add_newtp(struct evlist *evlist,
const char *sys, const char *name, void *handler);
+int __evlist__set_tracepoints_handlers(struct evlist *evlist,
+ const struct evsel_str_handler *assocs,
+ size_t nr_assocs);
+
+#define evlist__set_tracepoints_handlers(evlist, array) \
+ __evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
+
void __perf_evlist__set_sample_bit(struct evlist *evlist,
enum perf_event_sample_format bit);
void __perf_evlist__reset_sample_bit(struct evlist *evlist,
@@ -133,6 +140,11 @@ int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter);
int perf_evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid);
int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids);
+int perf_evlist__append_tp_filter(struct evlist *evlist, const char *filter);
+
+int perf_evlist__append_tp_filter_pid(struct evlist *evlist, pid_t pid);
+int perf_evlist__append_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids);
+
struct evsel *
perf_evlist__find_tracepoint_by_id(struct evlist *evlist, int id);
@@ -164,6 +176,7 @@ void perf_evlist__set_id_pos(struct evlist *evlist);
bool perf_can_sample_identifier(void);
bool perf_can_record_switch_events(void);
bool perf_can_record_cpu_wide(void);
+bool perf_can_aux_sample(void);
void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain);
int record_opts__config(struct record_opts *opts);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 5591af81a070..f4dea055b080 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -30,6 +30,7 @@
#include "counts.h"
#include "event.h"
#include "evsel.h"
+#include "util/env.h"
#include "util/evsel_config.h"
#include "util/evsel_fprintf.h"
#include "evlist.h"
@@ -845,6 +846,11 @@ static void apply_config_terms(struct evsel *evsel,
case PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT:
attr->aux_output = term->val.aux_output ? 1 : 0;
break;
+ case PERF_EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE:
+ /* Already applied by auxtrace */
+ break;
+ case PERF_EVSEL__CONFIG_TERM_CFG_CHG:
+ break;
default:
break;
}
@@ -904,6 +910,19 @@ static bool is_dummy_event(struct evsel *evsel)
(evsel->core.attr.config == PERF_COUNT_SW_DUMMY);
}
+struct perf_evsel_config_term *__perf_evsel__get_config_term(struct evsel *evsel,
+ enum evsel_term_type type)
+{
+ struct perf_evsel_config_term *term, *found_term = NULL;
+
+ list_for_each_entry(term, &evsel->config_terms, list) {
+ if (term->type == type)
+ found_term = term;
+ }
+
+ return found_term;
+}
+
/*
* The enable_on_exec/disabled value strategy:
*
@@ -1523,7 +1542,7 @@ static int __open_attr__fprintf(FILE *fp, const char *name, const char *val,
static void display_attr(struct perf_event_attr *attr)
{
- if (verbose >= 2) {
+ if (verbose >= 2 || debug_peo_args) {
fprintf(stderr, "%.60s\n", graph_dotted_line);
fprintf(stderr, "perf_event_attr:\n");
perf_event_attr__fprintf(stderr, attr, __open_attr__fprintf, NULL);
@@ -1539,7 +1558,7 @@ static int perf_event_open(struct evsel *evsel,
int fd;
while (1) {
- pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",
+ pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",
pid, cpu, group_fd, flags);
fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, group_fd, flags);
@@ -1559,9 +1578,9 @@ static int perf_event_open(struct evsel *evsel,
break;
}
- pr_debug2("\nsys_perf_event_open failed, error %d\n", -ENOTSUP);
+ pr_debug2_peo("\nsys_perf_event_open failed, error %d\n", -ENOTSUP);
evsel->core.attr.precise_ip--;
- pr_debug2("decreasing precise_ip by one (%d)\n", evsel->core.attr.precise_ip);
+ pr_debug2_peo("decreasing precise_ip by one (%d)\n", evsel->core.attr.precise_ip);
display_attr(&evsel->core.attr);
}
@@ -1573,7 +1592,7 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus,
{
int cpu, thread, nthreads;
unsigned long flags = PERF_FLAG_FD_CLOEXEC;
- int pid = -1, err;
+ int pid = -1, err, old_errno;
enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) ||
@@ -1680,12 +1699,12 @@ retry_open:
continue;
}
- pr_debug2("\nsys_perf_event_open failed, error %d\n",
+ pr_debug2_peo("\nsys_perf_event_open failed, error %d\n",
err);
goto try_fallback;
}
- pr_debug2(" = %d\n", fd);
+ pr_debug2_peo(" = %d\n", fd);
if (evsel->bpf_fd >= 0) {
int evt_fd = fd;
@@ -1726,8 +1745,8 @@ try_fallback:
*/
if (err == -EMFILE && set_rlimit < INCREASED_MAX) {
struct rlimit l;
- int old_errno = errno;
+ old_errno = errno;
if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
if (set_rlimit == NO_CHANGE)
l.rlim_cur = l.rlim_max;
@@ -1753,71 +1772,74 @@ try_fallback:
*/
if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) {
perf_missing_features.aux_output = true;
- pr_debug2("Kernel has no attr.aux_output support, bailing out\n");
+ pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");
goto out_close;
} else if (!perf_missing_features.bpf && evsel->core.attr.bpf_event) {
perf_missing_features.bpf = true;
- pr_debug2("switching off bpf_event\n");
+ pr_debug2_peo("switching off bpf_event\n");
goto fallback_missing_features;
} else if (!perf_missing_features.ksymbol && evsel->core.attr.ksymbol) {
perf_missing_features.ksymbol = true;
- pr_debug2("switching off ksymbol\n");
+ pr_debug2_peo("switching off ksymbol\n");
goto fallback_missing_features;
} else if (!perf_missing_features.write_backward && evsel->core.attr.write_backward) {
perf_missing_features.write_backward = true;
- pr_debug2("switching off write_backward\n");
+ pr_debug2_peo("switching off write_backward\n");
goto out_close;
} else if (!perf_missing_features.clockid_wrong && evsel->core.attr.use_clockid) {
perf_missing_features.clockid_wrong = true;
- pr_debug2("switching off clockid\n");
+ pr_debug2_peo("switching off clockid\n");
goto fallback_missing_features;
} else if (!perf_missing_features.clockid && evsel->core.attr.use_clockid) {
perf_missing_features.clockid = true;
- pr_debug2("switching off use_clockid\n");
+ pr_debug2_peo("switching off use_clockid\n");
goto fallback_missing_features;
} else if (!perf_missing_features.cloexec && (flags & PERF_FLAG_FD_CLOEXEC)) {
perf_missing_features.cloexec = true;
- pr_debug2("switching off cloexec flag\n");
+ pr_debug2_peo("switching off cloexec flag\n");
goto fallback_missing_features;
} else if (!perf_missing_features.mmap2 && evsel->core.attr.mmap2) {
perf_missing_features.mmap2 = true;
- pr_debug2("switching off mmap2\n");
+ pr_debug2_peo("switching off mmap2\n");
goto fallback_missing_features;
} else if (!perf_missing_features.exclude_guest &&
(evsel->core.attr.exclude_guest || evsel->core.attr.exclude_host)) {
perf_missing_features.exclude_guest = true;
- pr_debug2("switching off exclude_guest, exclude_host\n");
+ pr_debug2_peo("switching off exclude_guest, exclude_host\n");
goto fallback_missing_features;
} else if (!perf_missing_features.sample_id_all) {
perf_missing_features.sample_id_all = true;
- pr_debug2("switching off sample_id_all\n");
+ pr_debug2_peo("switching off sample_id_all\n");
goto retry_sample_id;
} else if (!perf_missing_features.lbr_flags &&
(evsel->core.attr.branch_sample_type &
(PERF_SAMPLE_BRANCH_NO_CYCLES |
PERF_SAMPLE_BRANCH_NO_FLAGS))) {
perf_missing_features.lbr_flags = true;
- pr_debug2("switching off branch sample type no (cycles/flags)\n");
+ pr_debug2_peo("switching off branch sample type no (cycles/flags)\n");
goto fallback_missing_features;
} else if (!perf_missing_features.group_read &&
evsel->core.attr.inherit &&
(evsel->core.attr.read_format & PERF_FORMAT_GROUP) &&
perf_evsel__is_group_leader(evsel)) {
perf_missing_features.group_read = true;
- pr_debug2("switching off group read\n");
+ pr_debug2_peo("switching off group read\n");
goto fallback_missing_features;
}
out_close:
if (err)
threads->err_thread = thread;
+ old_errno = errno;
do {
while (--thread >= 0) {
- close(FD(evsel, cpu, thread));
+ if (FD(evsel, cpu, thread) >= 0)
+ close(FD(evsel, cpu, thread));
FD(evsel, cpu, thread) = -1;
}
thread = nthreads;
} while (--cpu >= 0);
+ errno = old_errno;
return err;
}
@@ -2205,6 +2227,19 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
array++;
}
+ if (type & PERF_SAMPLE_AUX) {
+ OVERFLOW_CHECK_u64(array);
+ sz = *array++;
+
+ OVERFLOW_CHECK(array, sz, max_size);
+ /* Undo swap of data */
+ if (swapped)
+ mem_bswap_64((char *)array, sz);
+ data->aux_sample.size = sz;
+ data->aux_sample.data = (char *)array;
+ array = (void *)array + sz;
+ }
+
return 0;
}
@@ -2512,7 +2547,7 @@ struct perf_env *perf_evsel__env(struct evsel *evsel)
{
if (evsel && evsel->evlist)
return evsel->evlist->env;
- return NULL;
+ return &perf_env;
}
static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist)
diff --git a/tools/perf/util/evsel_config.h b/tools/perf/util/evsel_config.h
index 8a7648037c18..1f8d2fe0b66e 100644
--- a/tools/perf/util/evsel_config.h
+++ b/tools/perf/util/evsel_config.h
@@ -25,6 +25,8 @@ enum evsel_term_type {
PERF_EVSEL__CONFIG_TERM_BRANCH,
PERF_EVSEL__CONFIG_TERM_PERCORE,
PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT,
+ PERF_EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE,
+ PERF_EVSEL__CONFIG_TERM_CFG_CHG,
};
struct perf_evsel_config_term {
@@ -44,7 +46,18 @@ struct perf_evsel_config_term {
unsigned long max_events;
bool percore;
bool aux_output;
+ u32 aux_sample_size;
+ u64 cfg_chg;
} val;
bool weak;
};
+
+struct evsel;
+
+struct perf_evsel_config_term *__perf_evsel__get_config_term(struct evsel *evsel,
+ enum evsel_term_type type);
+
+#define perf_evsel__get_config_term(evsel, type) \
+ __perf_evsel__get_config_term(evsel, PERF_EVSEL__CONFIG_TERM_ ## type)
+
#endif // __PERF_EVSEL_CONFIG_H
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 028df7afb0dc..3b4842840db0 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -125,13 +125,18 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
callchain_cursor_commit(cursor);
while (1) {
+ struct symbol *sym;
+ struct map *map;
u64 addr = 0;
node = callchain_cursor_current(cursor);
if (!node)
break;
- if (node->sym && node->sym->ignore && print_skip_ignored)
+ sym = node->ms.sym;
+ map = node->ms.map;
+
+ if (sym && sym->ignore && print_skip_ignored)
goto next;
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
@@ -142,42 +147,42 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
if (print_ip)
printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
- if (node->map)
- addr = node->map->map_ip(node->map, node->ip);
+ if (map)
+ addr = map->map_ip(map, node->ip);
if (print_sym) {
printed += fprintf(fp, " ");
node_al.addr = addr;
- node_al.map = node->map;
+ node_al.map = map;
if (print_symoffset) {
- printed += __symbol__fprintf_symname_offs(node->sym, &node_al,
+ printed += __symbol__fprintf_symname_offs(sym, &node_al,
print_unknown_as_addr,
true, fp);
} else {
- printed += __symbol__fprintf_symname(node->sym, &node_al,
+ printed += __symbol__fprintf_symname(sym, &node_al,
print_unknown_as_addr, fp);
}
}
- if (print_dso && (!node->sym || !node->sym->inlined)) {
+ if (print_dso && (!sym || !sym->inlined)) {
printed += fprintf(fp, " (");
- printed += map__fprintf_dsoname(node->map, fp);
+ printed += map__fprintf_dsoname(map, fp);
printed += fprintf(fp, ")");
}
if (print_srcline)
- printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
+ printed += map__fprintf_srcline(map, addr, "\n ", fp);
- if (node->sym && node->sym->inlined)
+ if (sym && sym->inlined)
printed += fprintf(fp, " (inlined)");
if (!print_oneline)
printed += fprintf(fp, "\n");
/* Add srccode here too? */
- if (bt_stop_list && node->sym &&
- strlist__has_entry(bt_stop_list, node->sym->name)) {
+ if (bt_stop_list && sym &&
+ strlist__has_entry(bt_stop_list, sym->name)) {
break;
}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 86d9396cb131..becc2d109423 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1296,8 +1296,10 @@ static int build_mem_topology(struct memory_node *nodes, u64 size, u64 *cntp)
continue;
if (WARN_ONCE(cnt >= size,
- "failed to write MEM_TOPOLOGY, way too many nodes\n"))
+ "failed to write MEM_TOPOLOGY, way too many nodes\n")) {
+ closedir(dir);
return -1;
+ }
ret = memory_node__read(&nodes[cnt++], idx);
}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index ca53a929e9fd..840f95cee349 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -52,10 +52,6 @@ enum perf_header_version {
PERF_HEADER_VERSION_2,
};
-enum perf_dir_version {
- PERF_DIR_VERSION = 1,
-};
-
struct perf_file_section {
u64 offset;
u64 size;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 679a1d75090c..0a8d72ae93ca 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -18,6 +18,7 @@
#include "srcline.h"
#include "symbol.h"
#include "thread.h"
+#include "block-info.h"
#include "ui/progress.h"
#include <errno.h>
#include <math.h>
@@ -80,6 +81,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
int symlen;
u16 len;
+ if (h->block_info)
+ return;
/*
* +4 accounts for '[x] ' priv level info
* +2 accounts for 0x prefix on raw addresses
@@ -109,13 +112,13 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
if (h->branch_info) {
- if (h->branch_info->from.sym) {
- symlen = (int)h->branch_info->from.sym->namelen + 4;
+ if (h->branch_info->from.ms.sym) {
+ symlen = (int)h->branch_info->from.ms.sym->namelen + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
- symlen = dso__name_len(h->branch_info->from.map->dso);
+ symlen = dso__name_len(h->branch_info->from.ms.map->dso);
hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
@@ -123,13 +126,13 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
}
- if (h->branch_info->to.sym) {
- symlen = (int)h->branch_info->to.sym->namelen + 4;
+ if (h->branch_info->to.ms.sym) {
+ symlen = (int)h->branch_info->to.ms.sym->namelen + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
- symlen = dso__name_len(h->branch_info->to.map->dso);
+ symlen = dso__name_len(h->branch_info->to.ms.map->dso);
hists__new_col_len(hists, HISTC_DSO_TO, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
@@ -146,8 +149,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->mem_info) {
- if (h->mem_info->daddr.sym) {
- symlen = (int)h->mem_info->daddr.sym->namelen + 4
+ if (h->mem_info->daddr.ms.sym) {
+ symlen = (int)h->mem_info->daddr.ms.sym->namelen + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen);
@@ -161,8 +164,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
symlen);
}
- if (h->mem_info->iaddr.sym) {
- symlen = (int)h->mem_info->iaddr.sym->namelen + 4
+ if (h->mem_info->iaddr.ms.sym) {
+ symlen = (int)h->mem_info->iaddr.ms.sym->namelen + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
symlen);
@@ -172,8 +175,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
symlen);
}
- if (h->mem_info->daddr.map) {
- symlen = dso__name_len(h->mem_info->daddr.map->dso);
+ if (h->mem_info->daddr.ms.map) {
+ symlen = dso__name_len(h->mem_info->daddr.ms.map->dso);
hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
symlen);
} else {
@@ -440,13 +443,13 @@ static int hist_entry__init(struct hist_entry *he,
memcpy(he->branch_info, template->branch_info,
sizeof(*he->branch_info));
- map__get(he->branch_info->from.map);
- map__get(he->branch_info->to.map);
+ map__get(he->branch_info->from.ms.map);
+ map__get(he->branch_info->to.ms.map);
}
if (he->mem_info) {
- map__get(he->mem_info->iaddr.map);
- map__get(he->mem_info->daddr.map);
+ map__get(he->mem_info->iaddr.ms.map);
+ map__get(he->mem_info->daddr.ms.map);
}
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
@@ -489,13 +492,13 @@ err_rawdata:
err_infos:
if (he->branch_info) {
- map__put(he->branch_info->from.map);
- map__put(he->branch_info->to.map);
+ map__put(he->branch_info->from.ms.map);
+ map__put(he->branch_info->to.ms.map);
zfree(&he->branch_info);
}
if (he->mem_info) {
- map__put(he->mem_info->iaddr.map);
- map__put(he->mem_info->daddr.map);
+ map__put(he->mem_info->iaddr.ms.map);
+ map__put(he->mem_info->daddr.ms.map);
}
err:
map__zput(he->ms.map);
@@ -689,6 +692,7 @@ __hists__add_entry(struct hists *hists,
.ino = ns ? ns->link_info[CGROUP_NS_INDEX].ino : 0,
},
.ms = {
+ .mg = al->mg,
.map = al->map,
.sym = al->sym,
},
@@ -755,6 +759,11 @@ struct hist_entry *hists__add_entry_block(struct hists *hists,
struct hist_entry entry = {
.block_info = block_info,
.hists = hists,
+ .ms = {
+ .mg = al->mg,
+ .map = al->map,
+ .sym = al->sym,
+ },
}, *he = hists__findnew_entry(hists, &entry, al, false);
return he;
@@ -886,8 +895,9 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
if (iter->curr >= iter->total)
return 0;
- al->map = bi[i].to.map;
- al->sym = bi[i].to.sym;
+ al->mg = bi[i].to.ms.mg;
+ al->map = bi[i].to.ms.map;
+ al->sym = bi[i].to.ms.sym;
al->addr = bi[i].to.addr;
return 1;
}
@@ -905,7 +915,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
bi = iter->priv;
- if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
+ if (iter->hide_unresolved && !(bi[i].from.ms.sym && bi[i].to.ms.sym))
goto out;
/*
@@ -1062,6 +1072,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
.comm = thread__comm(al->thread),
.ip = al->addr,
.ms = {
+ .mg = al->mg,
.map = al->map,
.sym = al->sym,
},
@@ -1244,16 +1255,16 @@ void hist_entry__delete(struct hist_entry *he)
map__zput(he->ms.map);
if (he->branch_info) {
- map__zput(he->branch_info->from.map);
- map__zput(he->branch_info->to.map);
+ map__zput(he->branch_info->from.ms.map);
+ map__zput(he->branch_info->to.ms.map);
free_srcline(he->branch_info->srcline_from);
free_srcline(he->branch_info->srcline_to);
zfree(&he->branch_info);
}
if (he->mem_info) {
- map__zput(he->mem_info->iaddr.map);
- map__zput(he->mem_info->daddr.map);
+ map__zput(he->mem_info->iaddr.ms.map);
+ map__zput(he->mem_info->daddr.ms.map);
mem_info__zput(he->mem_info);
}
@@ -1625,7 +1636,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
return 0;
}
-static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
+static int64_t hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
{
struct hists *hists = a->hists;
struct perf_hpp_fmt *fmt;
@@ -2569,7 +2580,8 @@ int hists__unlink(struct hists *hists)
}
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
- struct perf_sample *sample, bool nonany_branch_mode)
+ struct perf_sample *sample, bool nonany_branch_mode,
+ u64 *total_cycles)
{
struct branch_info *bi;
@@ -2596,6 +2608,9 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
nonany_branch_mode ? NULL : prev,
bi[i].flags.cycles);
prev = &bi[i].to;
+
+ if (total_cycles)
+ *total_cycles += bi[i].flags.cycles;
}
free(bi);
}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 6a186b668303..45286900aacb 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -449,6 +449,8 @@ enum rstype {
A_SOURCE
};
+struct block_hist;
+
#ifdef HAVE_SLANG_SUPPORT
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
@@ -474,6 +476,10 @@ void run_script(char *cmd);
int res_sample_browse(struct res_sample *res_samples, int num_res,
struct evsel *evsel, enum rstype rstype);
void res_sample_init(void);
+
+int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
+ float min_percent, struct perf_env *env,
+ struct annotation_options *annotation_opts);
#else
static inline
int perf_evlist__tui_browse_hists(struct evlist *evlist __maybe_unused,
@@ -518,6 +524,15 @@ static inline int res_sample_browse(struct res_sample *res_samples __maybe_unuse
static inline void res_sample_init(void) {}
+static inline int block_hists_tui_browse(struct block_hist *bh __maybe_unused,
+ struct evsel *evsel __maybe_unused,
+ float min_percent __maybe_unused,
+ struct perf_env *env __maybe_unused,
+ struct annotation_options *annotation_opts __maybe_unused)
+{
+ return 0;
+}
+
#define K_LEFT -1000
#define K_RIGHT -2000
#define K_SWITCH_INPUT_DATA -3000
@@ -527,7 +542,8 @@ unsigned int hists__sort_list_width(struct hists *hists);
unsigned int hists__overhead_width(struct hists *hists);
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
- struct perf_sample *sample, bool nonany_branch_mode);
+ struct perf_sample *sample, bool nonany_branch_mode,
+ u64 *total_cycles);
struct option;
int parse_filter_percentage(const struct option *opt, const char *arg, int unset);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index a1c9eb6d4f40..409afc611be9 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -233,6 +233,16 @@ static void intel_pt_log_event(union perf_event *event)
perf_event__fprintf(event, f);
}
+static void intel_pt_dump_sample(struct perf_session *session,
+ struct perf_sample *sample)
+{
+ struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt,
+ auxtrace);
+
+ printf("\n");
+ intel_pt_dump(pt, sample->aux_sample.data, sample->aux_sample.size);
+}
+
static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a,
struct auxtrace_buffer *b)
{
@@ -836,6 +846,18 @@ static bool intel_pt_have_tsc(struct intel_pt *pt)
return have_tsc;
}
+static bool intel_pt_sampling_mode(struct intel_pt *pt)
+{
+ struct evsel *evsel;
+
+ evlist__for_each_entry(pt->session->evlist, evsel) {
+ if ((evsel->core.attr.sample_type & PERF_SAMPLE_AUX) &&
+ evsel->core.attr.aux_sample_size)
+ return true;
+ }
+ return false;
+}
+
static u64 intel_pt_ns_to_ticks(const struct intel_pt *pt, u64 ns)
{
u64 quot, rem;
@@ -2320,6 +2342,56 @@ static int intel_pt_process_timeless_queues(struct intel_pt *pt, pid_t tid,
return 0;
}
+static void intel_pt_sample_set_pid_tid_cpu(struct intel_pt_queue *ptq,
+ struct auxtrace_queue *queue,
+ struct perf_sample *sample)
+{
+ struct machine *m = ptq->pt->machine;
+
+ ptq->pid = sample->pid;
+ ptq->tid = sample->tid;
+ ptq->cpu = queue->cpu;
+
+ intel_pt_log("queue %u cpu %d pid %d tid %d\n",
+ ptq->queue_nr, ptq->cpu, ptq->pid, ptq->tid);
+
+ thread__zput(ptq->thread);
+
+ if (ptq->tid == -1)
+ return;
+
+ if (ptq->pid == -1) {
+ ptq->thread = machine__find_thread(m, -1, ptq->tid);
+ if (ptq->thread)
+ ptq->pid = ptq->thread->pid_;
+ return;
+ }
+
+ ptq->thread = machine__findnew_thread(m, ptq->pid, ptq->tid);
+}
+
+static int intel_pt_process_timeless_sample(struct intel_pt *pt,
+ struct perf_sample *sample)
+{
+ struct auxtrace_queue *queue;
+ struct intel_pt_queue *ptq;
+ u64 ts = 0;
+
+ queue = auxtrace_queues__sample_queue(&pt->queues, sample, pt->session);
+ if (!queue)
+ return -EINVAL;
+
+ ptq = queue->priv;
+ if (!ptq)
+ return 0;
+
+ ptq->stop = false;
+ ptq->time = sample->time;
+ intel_pt_sample_set_pid_tid_cpu(ptq, queue, sample);
+ intel_pt_run_decoder(ptq, &ts);
+ return 0;
+}
+
static int intel_pt_lost(struct intel_pt *pt, struct perf_sample *sample)
{
return intel_pt_synth_error(pt, INTEL_PT_ERR_LOST, sample->cpu,
@@ -2550,7 +2622,11 @@ static int intel_pt_process_event(struct perf_session *session,
}
if (pt->timeless_decoding) {
- if (event->header.type == PERF_RECORD_EXIT) {
+ if (pt->sampling_mode) {
+ if (sample->aux_sample.size)
+ err = intel_pt_process_timeless_sample(pt,
+ sample);
+ } else if (event->header.type == PERF_RECORD_EXIT) {
err = intel_pt_process_timeless_queues(pt,
event->fork.tid,
sample->time);
@@ -2676,6 +2752,28 @@ static int intel_pt_process_auxtrace_event(struct perf_session *session,
return 0;
}
+static int intel_pt_queue_data(struct perf_session *session,
+ struct perf_sample *sample,
+ union perf_event *event, u64 data_offset)
+{
+ struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt,
+ auxtrace);
+ u64 timestamp;
+
+ if (event) {
+ return auxtrace_queues__add_event(&pt->queues, session, event,
+ data_offset, NULL);
+ }
+
+ if (sample->time && sample->time != (u64)-1)
+ timestamp = perf_time_to_tsc(sample->time, &pt->tc);
+ else
+ timestamp = 0;
+
+ return auxtrace_queues__add_sample(&pt->queues, session, sample,
+ data_offset, timestamp);
+}
+
struct intel_pt_synth {
struct perf_tool dummy_tool;
struct perf_session *session;
@@ -3178,7 +3276,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
if (pt->timeless_decoding && !pt->tc.time_mult)
pt->tc.time_mult = 1;
pt->have_tsc = intel_pt_have_tsc(pt);
- pt->sampling_mode = false;
+ pt->sampling_mode = intel_pt_sampling_mode(pt);
pt->est_tsc = !pt->timeless_decoding;
pt->unknown_thread = thread__new(999999999, 999999999);
@@ -3205,6 +3303,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
pt->auxtrace.process_event = intel_pt_process_event;
pt->auxtrace.process_auxtrace_event = intel_pt_process_auxtrace_event;
+ pt->auxtrace.queue_data = intel_pt_queue_data;
+ pt->auxtrace.dump_auxtrace_sample = intel_pt_dump_sample;
pt->auxtrace.flush_events = intel_pt_flush;
pt->auxtrace.free_events = intel_pt_free_events;
pt->auxtrace.free = intel_pt_free;
@@ -3282,7 +3382,10 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
intel_pt_setup_pebs_events(pt);
- err = auxtrace_queues__process_index(&pt->queues, session);
+ if (pt->sampling_mode || list_empty(&session->auxtrace_index))
+ err = auxtrace_queue_data(session, true, true);
+ else
+ err = auxtrace_queues__process_index(&pt->queues, session);
if (err)
goto err_delete_thread;
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 1bdf4c6ea3e5..e3ccb0ce1938 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -395,7 +395,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
size_t size;
u16 idr_size;
const char *sym;
- uint32_t count;
+ uint64_t count;
int ret, csize, usize;
pid_t pid, tid;
struct {
@@ -418,7 +418,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
return -1;
filename = event->mmap2.filename;
- size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u.so",
+ size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so",
jd->dir,
pid,
count);
@@ -529,7 +529,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
return -1;
filename = event->mmap2.filename;
- size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64,
+ size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so",
jd->dir,
pid,
jr->move.code_index);
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 8d04e3d070b1..eae47c2509eb 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -233,14 +233,14 @@ static int detect_kbuild_dir(char **kbuild_dir)
const char *prefix_dir = "";
const char *suffix_dir = "";
+ /* _UTSNAME_LENGTH is 65 */
+ char release[128];
+
char *autoconf_path;
int err;
if (!test_dir) {
- /* _UTSNAME_LENGTH is 65 */
- char release[128];
-
err = fetch_kernel_version(NULL, release,
sizeof(release));
if (err)
@@ -418,10 +418,9 @@ void llvm__dump_obj(const char *path, void *obj_buf, size_t size)
goto out;
}
- pr_info("LLVM: dumping %s\n", obj_path);
+ pr_debug("LLVM: dumping %s\n", obj_path);
if (fwrite(obj_buf, size, 1, fp) != 1)
- pr_warning("WARNING: failed to write to file '%s': %s, skip object dumping\n",
- obj_path, strerror(errno));
+ pr_debug("WARNING: failed to write to file '%s': %s, skip object dumping\n", obj_path, strerror(errno));
fclose(fp);
out:
free(obj_path);
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 70a9f8716a4b..e2a312c649f0 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -42,6 +42,11 @@
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
+static struct dso *machine__kernel_dso(struct machine *machine)
+{
+ return machine->vmlinux_map->dso;
+}
+
static void dsos__init(struct dsos *dsos)
{
INIT_LIST_HEAD(&dsos->head);
@@ -767,45 +772,16 @@ int machine__process_ksymbol(struct machine *machine __maybe_unused,
return machine__process_ksymbol_register(machine, event, sample);
}
-static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename)
-{
- const char *dup_filename;
-
- if (!filename || !dso || !dso->long_name)
- return;
- if (dso->long_name[0] != '[')
- return;
- if (!strchr(filename, '/'))
- return;
-
- dup_filename = strdup(filename);
- if (!dup_filename)
- return;
-
- dso__set_long_name(dso, dup_filename, true);
-}
-
-struct map *machine__findnew_module_map(struct machine *machine, u64 start,
- const char *filename)
+static struct map *machine__addnew_module_map(struct machine *machine, u64 start,
+ const char *filename)
{
struct map *map = NULL;
- struct dso *dso = NULL;
struct kmod_path m;
+ struct dso *dso;
if (kmod_path__parse_name(&m, filename))
return NULL;
- map = map_groups__find_by_name(&machine->kmaps, m.name);
- if (map) {
- /*
- * If the map's dso is an offline module, give dso__load()
- * a chance to find the file path of that module by fixing
- * long_name.
- */
- dso__adjust_kmod_long_name(map->dso, filename);
- goto out;
- }
-
dso = machine__findnew_module_dso(machine, &m, filename);
if (dso == NULL)
goto out;
@@ -861,7 +837,7 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
{
int i;
size_t printed = 0;
- struct dso *kdso = machine__kernel_map(machine)->dso;
+ struct dso *kdso = machine__kernel_dso(machine);
if (kdso->has_build_id) {
char filename[PATH_MAX];
@@ -1057,7 +1033,7 @@ int machine__map_x86_64_entry_trampolines(struct machine *machine,
* In the vmlinux case, pgoff is a virtual address which must now be
* mapped to a vmlinux offset.
*/
- for (map = maps__first(maps); map; map = map__next(map)) {
+ maps__for_each_entry(maps, map) {
struct kmap *kmap = __map__kmap(map);
struct map *dest_map;
@@ -1404,7 +1380,7 @@ static int machine__create_module(void *arg, const char *name, u64 start,
if (arch__fix_module_text_start(&start, &size, name) < 0)
return -1;
- map = machine__findnew_module_map(machine, start, name);
+ map = machine__addnew_module_map(machine, start, name);
if (map == NULL)
return -1;
map->end = start + size;
@@ -1543,8 +1519,7 @@ static bool perf_event__is_extra_kernel_mmap(struct machine *machine,
static int machine__process_extra_kernel_map(struct machine *machine,
union perf_event *event)
{
- struct map *kernel_map = machine__kernel_map(machine);
- struct dso *kernel = kernel_map ? kernel_map->dso : NULL;
+ struct dso *kernel = machine__kernel_dso(machine);
struct extra_kernel_map xm = {
.start = event->mmap.start,
.end = event->mmap.start + event->mmap.len,
@@ -1580,8 +1555,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
strlen(machine->mmap_name) - 1) == 0;
if (event->mmap.filename[0] == '/' ||
(!is_kernel_mmap && event->mmap.filename[0] == '[')) {
- map = machine__findnew_module_map(machine, event->mmap.start,
- event->mmap.filename);
+ map = machine__addnew_module_map(machine, event->mmap.start,
+ event->mmap.filename);
if (map == NULL)
goto out_problem;
@@ -1676,6 +1651,12 @@ int machine__process_mmap2_event(struct machine *machine,
{
struct thread *thread;
struct map *map;
+ struct dso_id dso_id = {
+ .maj = event->mmap2.maj,
+ .min = event->mmap2.min,
+ .ino = event->mmap2.ino,
+ .ino_generation = event->mmap2.ino_generation,
+ };
int ret = 0;
if (dump_trace)
@@ -1696,10 +1677,7 @@ int machine__process_mmap2_event(struct machine *machine,
map = map__new(machine, event->mmap2.start,
event->mmap2.len, event->mmap2.pgoff,
- event->mmap2.maj,
- event->mmap2.min, event->mmap2.ino,
- event->mmap2.ino_generation,
- event->mmap2.prot,
+ &dso_id, event->mmap2.prot,
event->mmap2.flags,
event->mmap2.filename, thread);
@@ -1752,9 +1730,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
map = map__new(machine, event->mmap.start,
event->mmap.len, event->mmap.pgoff,
- 0, 0, 0, 0, prot, 0,
- event->mmap.filename,
- thread);
+ NULL, prot, 0, event->mmap.filename, thread);
if (map == NULL)
goto out_problem_map;
@@ -1964,8 +1940,9 @@ static void ip__resolve_ams(struct thread *thread,
ams->addr = ip;
ams->al_addr = al.addr;
- ams->sym = al.sym;
- ams->map = al.map;
+ ams->ms.mg = al.mg;
+ ams->ms.sym = al.sym;
+ ams->ms.map = al.map;
ams->phys_addr = 0;
}
@@ -1981,8 +1958,9 @@ static void ip__resolve_data(struct thread *thread,
ams->addr = addr;
ams->al_addr = al.addr;
- ams->sym = al.sym;
- ams->map = al.map;
+ ams->ms.mg = al.mg;
+ ams->ms.sym = al.sym;
+ ams->ms.map = al.map;
ams->phys_addr = phys_addr;
}
@@ -2002,8 +1980,9 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
return mi;
}
-static char *callchain_srcline(struct map *map, struct symbol *sym, u64 ip)
+static char *callchain_srcline(struct map_symbol *ms, u64 ip)
{
+ struct map *map = ms->map;
char *srcline = NULL;
if (!map || callchain_param.key == CCKEY_FUNCTION)
@@ -2015,7 +1994,7 @@ static char *callchain_srcline(struct map *map, struct symbol *sym, u64 ip)
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
srcline = get_srcline(map->dso, map__rip_2objdump(map, ip),
- sym, show_sym, show_addr, ip);
+ ms->sym, show_sym, show_addr, ip);
srcline__tree_insert(&map->dso->srclines, ip, srcline);
}
@@ -2038,6 +2017,7 @@ static int add_callchain_ip(struct thread *thread,
struct iterations *iter,
u64 branch_from)
{
+ struct map_symbol ms;
struct addr_location al;
int nr_loop_iter = 0;
u64 iter_cycles = 0;
@@ -2095,8 +2075,11 @@ static int add_callchain_ip(struct thread *thread,
iter_cycles = iter->cycles;
}
- srcline = callchain_srcline(al.map, al.sym, al.addr);
- return callchain_cursor_append(cursor, ip, al.map, al.sym,
+ ms.mg = al.mg;
+ ms.map = al.map;
+ ms.sym = al.sym;
+ srcline = callchain_srcline(&ms, al.addr);
+ return callchain_cursor_append(cursor, ip, &ms,
branch, flags, nr_loop_iter,
iter_cycles, branch_from, srcline);
}
@@ -2403,7 +2386,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
}
check_calls:
- if (callchain_param.order != ORDER_CALLEE) {
+ if (chain && callchain_param.order != ORDER_CALLEE) {
err = find_prev_cpumode(chain, thread, cursor, parent, root_al,
&cpumode, chain->nr - first_call);
if (err)
@@ -2444,9 +2427,10 @@ check_calls:
return 0;
}
-static int append_inlines(struct callchain_cursor *cursor,
- struct map *map, struct symbol *sym, u64 ip)
+static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip)
{
+ struct symbol *sym = ms->sym;
+ struct map *map = ms->map;
struct inline_node *inline_node;
struct inline_list *ilist;
u64 addr;
@@ -2467,8 +2451,11 @@ static int append_inlines(struct callchain_cursor *cursor,
}
list_for_each_entry(ilist, &inline_node->val, list) {
- ret = callchain_cursor_append(cursor, ip, map,
- ilist->symbol, false,
+ struct map_symbol ilist_ms = {
+ .map = map,
+ .sym = ilist->symbol,
+ };
+ ret = callchain_cursor_append(cursor, ip, &ilist_ms, false,
NULL, 0, 0, 0, ilist->srcline);
if (ret != 0)
@@ -2484,22 +2471,21 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
const char *srcline = NULL;
u64 addr = entry->ip;
- if (symbol_conf.hide_unresolved && entry->sym == NULL)
+ if (symbol_conf.hide_unresolved && entry->ms.sym == NULL)
return 0;
- if (append_inlines(cursor, entry->map, entry->sym, entry->ip) == 0)
+ if (append_inlines(cursor, &entry->ms, entry->ip) == 0)
return 0;
/*
* Convert entry->ip from a virtual address to an offset in
* its corresponding binary.
*/
- if (entry->map)
- addr = map__map_ip(entry->map, entry->ip);
+ if (entry->ms.map)
+ addr = map__map_ip(entry->ms.map, entry->ip);
- srcline = callchain_srcline(entry->map, entry->sym, addr);
- return callchain_cursor_append(cursor, entry->ip,
- entry->map, entry->sym,
+ srcline = callchain_srcline(&entry->ms, addr);
+ return callchain_cursor_append(cursor, entry->ip, &entry->ms,
false, NULL, 0, 0, 0, srcline);
}
@@ -2725,9 +2711,14 @@ out:
return addr_cpumode;
}
+struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, struct dso_id *id)
+{
+ return dsos__findnew_id(&machine->dsos, filename, id);
+}
+
struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
{
- return dsos__findnew(&machine->dsos, filename);
+ return machine__findnew_dso_id(machine, filename, NULL);
}
char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp)
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 18e13c0ccd6a..499be204830d 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -11,6 +11,7 @@
struct addr_location;
struct branch_stack;
struct dso;
+struct dso_id;
struct evsel;
struct perf_sample;
struct symbol;
@@ -202,6 +203,7 @@ int machine__nr_cpus_avail(struct machine *machine);
struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid);
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid);
+struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, struct dso_id *id);
struct dso *machine__findnew_dso(struct machine *machine, const char *filename);
size_t machine__fprintf(struct machine *machine, FILE *fp);
@@ -221,8 +223,6 @@ struct symbol *machine__find_kernel_symbol_by_name(struct machine *machine,
return map_groups__find_symbol_by_name(&machine->kmaps, name, mapp);
}
-struct map *machine__findnew_module_map(struct machine *machine, u64 start,
- const char *filename);
int arch__fix_module_text_start(u64 *start, u64 *size, const char *name);
int machine__load_kallsyms(struct machine *machine, const char *filename);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 5b83ed1ebbd6..744bfbaf35cf 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "symbol.h"
+#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
@@ -25,7 +26,6 @@
#include "ui/ui.h"
static void __maps__insert(struct maps *maps, struct map *map);
-static void __maps__insert_name(struct maps *maps, struct map *map);
static inline int is_anon_memory(const char *filename, u32 flags)
{
@@ -139,14 +139,13 @@ void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso)
map->map_ip = map__map_ip;
map->unmap_ip = map__unmap_ip;
RB_CLEAR_NODE(&map->rb_node);
- map->groups = NULL;
map->erange_warned = false;
refcount_set(&map->refcnt, 1);
}
struct map *map__new(struct machine *machine, u64 start, u64 len,
- u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
- u64 ino_gen, u32 prot, u32 flags, char *filename,
+ u64 pgoff, struct dso_id *id,
+ u32 prot, u32 flags, char *filename,
struct thread *thread)
{
struct map *map = malloc(sizeof(*map));
@@ -162,11 +161,6 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
anon = is_anon_memory(filename, flags);
vdso = is_vdso_map(filename);
no_dso = is_no_dso_memory(filename);
-
- map->maj = d_maj;
- map->min = d_min;
- map->ino = ino;
- map->ino_generation = ino_gen;
map->prot = prot;
map->flags = flags;
nsi = nsinfo__get(thread->nsinfo);
@@ -196,7 +190,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
pgoff = 0;
dso = machine__findnew_vdso(machine, thread);
} else
- dso = machine__findnew_dso(machine, filename);
+ dso = machine__findnew_dso_id(machine, filename, id);
if (dso == NULL)
goto out_delete;
@@ -243,18 +237,11 @@ struct map *map__new2(u64 start, struct dso *dso)
return map;
}
-/*
- * Use this and __map__is_kmodule() for map instances that are in
- * machine->kmaps, and thus have map->groups->machine all properly set, to
- * disambiguate between the kernel and modules.
- *
- * When the need arises, introduce map__is_{kernel,kmodule)() that
- * checks (map->groups != NULL && map->groups->machine != NULL &&
- * map->dso->kernel) before calling __map__is_{kernel,kmodule}())
- */
bool __map__is_kernel(const struct map *map)
{
- return machine__kernel_map(map->groups->machine) == map;
+ if (!map->dso->kernel)
+ return false;
+ return machine__kernel_map(map__kmaps((struct map *)map)->machine) == map;
}
bool __map__is_extra_kernel_map(const struct map *map)
@@ -287,7 +274,7 @@ bool map__has_symbols(const struct map *map)
static void map__exit(struct map *map)
{
- BUG_ON(!RB_EMPTY_NODE(&map->rb_node));
+ BUG_ON(refcount_read(&map->refcnt) != 0);
dso__zput(map->dso);
}
@@ -394,7 +381,6 @@ struct map *map__clone(struct map *from)
refcount_set(&map->refcnt, 1);
RB_CLEAR_NODE(&map->rb_node);
dso__get(map->dso);
- map->groups = NULL;
}
return map;
@@ -574,7 +560,6 @@ u64 map__objdump_2mem(struct map *map, u64 ip)
static void maps__init(struct maps *maps)
{
maps->entries = RB_ROOT;
- maps->names = RB_ROOT;
init_rwsem(&maps->lock);
}
@@ -582,39 +567,72 @@ void map_groups__init(struct map_groups *mg, struct machine *machine)
{
maps__init(&mg->maps);
mg->machine = machine;
+ mg->last_search_by_name = NULL;
+ mg->nr_maps = 0;
+ mg->maps_by_name = NULL;
refcount_set(&mg->refcnt, 1);
}
-void map_groups__insert(struct map_groups *mg, struct map *map)
+static void __map_groups__free_maps_by_name(struct map_groups *mg)
{
- maps__insert(&mg->maps, map);
- map->groups = mg;
+ /*
+ * Free everything to try to do it from the rbtree in the next search
+ */
+ zfree(&mg->maps_by_name);
+ mg->nr_maps_allocated = 0;
}
-static void __maps__purge(struct maps *maps)
+void map_groups__insert(struct map_groups *mg, struct map *map)
{
- struct rb_root *root = &maps->entries;
- struct rb_node *next = rb_first(root);
+ struct maps *maps = &mg->maps;
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
+ down_write(&maps->lock);
+ __maps__insert(maps, map);
+ ++mg->nr_maps;
- next = rb_next(&pos->rb_node);
- rb_erase_init(&pos->rb_node, root);
- map__put(pos);
+ /*
+ * If we already performed some search by name, then we need to add the just
+ * inserted map and resort.
+ */
+ if (mg->maps_by_name) {
+ if (mg->nr_maps > mg->nr_maps_allocated) {
+ int nr_allocate = mg->nr_maps * 2;
+ struct map **maps_by_name = realloc(mg->maps_by_name, nr_allocate * sizeof(map));
+
+ if (maps_by_name == NULL) {
+ __map_groups__free_maps_by_name(mg);
+ return;
+ }
+
+ mg->maps_by_name = maps_by_name;
+ mg->nr_maps_allocated = nr_allocate;
+ }
+ mg->maps_by_name[mg->nr_maps - 1] = map;
+ __map_groups__sort_by_name(mg);
}
+ up_write(&maps->lock);
}
-static void __maps__purge_names(struct maps *maps)
+void map_groups__remove(struct map_groups *mg, struct map *map)
{
- struct rb_root *root = &maps->names;
- struct rb_node *next = rb_first(root);
+ struct maps *maps = &mg->maps;
+ down_write(&maps->lock);
+ if (mg->last_search_by_name == map)
+ mg->last_search_by_name = NULL;
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node_name);
+ __maps__remove(maps, map);
+ --mg->nr_maps;
+ if (mg->maps_by_name)
+ __map_groups__free_maps_by_name(mg);
+ up_write(&maps->lock);
+}
- next = rb_next(&pos->rb_node_name);
- rb_erase_init(&pos->rb_node_name, root);
+static void __maps__purge(struct maps *maps)
+{
+ struct map *pos, *next;
+
+ maps__for_each_entry_safe(maps, pos, next) {
+ rb_erase_init(&pos->rb_node, &maps->entries);
map__put(pos);
}
}
@@ -623,7 +641,6 @@ static void maps__exit(struct maps *maps)
{
down_write(&maps->lock);
__maps__purge(maps);
- __maps__purge_names(maps);
up_write(&maps->lock);
}
@@ -686,13 +703,11 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
struct map **mapp)
{
struct symbol *sym;
- struct rb_node *nd;
+ struct map *pos;
down_read(&maps->lock);
- for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
- struct map *pos = rb_entry(nd, struct map, rb_node);
-
+ maps__for_each_entry(maps, pos) {
sym = map__find_symbol_by_name(pos, name);
if (sym == NULL)
@@ -719,31 +734,30 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
return maps__find_symbol_by_name(&mg->maps, name, mapp);
}
-int map_groups__find_ams(struct addr_map_symbol *ams)
+int map_groups__find_ams(struct map_groups *mg, struct addr_map_symbol *ams)
{
- if (ams->addr < ams->map->start || ams->addr >= ams->map->end) {
- if (ams->map->groups == NULL)
+ if (ams->addr < ams->ms.map->start || ams->addr >= ams->ms.map->end) {
+ if (mg == NULL)
return -1;
- ams->map = map_groups__find(ams->map->groups, ams->addr);
- if (ams->map == NULL)
+ ams->ms.map = map_groups__find(mg, ams->addr);
+ if (ams->ms.map == NULL)
return -1;
}
- ams->al_addr = ams->map->map_ip(ams->map, ams->addr);
- ams->sym = map__find_symbol(ams->map, ams->al_addr);
+ ams->al_addr = ams->ms.map->map_ip(ams->ms.map, ams->addr);
+ ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
- return ams->sym ? 0 : -1;
+ return ams->ms.sym ? 0 : -1;
}
static size_t maps__fprintf(struct maps *maps, FILE *fp)
{
size_t printed = 0;
- struct rb_node *nd;
+ struct map *pos;
down_read(&maps->lock);
- for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
- struct map *pos = rb_entry(nd, struct map, rb_node);
+ maps__for_each_entry(maps, pos) {
printed += fprintf(fp, "Map:");
printed += map__fprintf(pos, fp);
if (verbose > 2) {
@@ -765,12 +779,11 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp)
static void __map_groups__insert(struct map_groups *mg, struct map *map)
{
__maps__insert(&mg->maps, map);
- __maps__insert_name(&mg->maps, map);
- map->groups = mg;
}
-static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
+int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp)
{
+ struct maps *maps = &mg->maps;
struct rb_root *root;
struct rb_node *next, *first;
int err = 0;
@@ -835,7 +848,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
}
before->end = map->start;
- __map_groups__insert(pos->groups, before);
+ __map_groups__insert(mg, before);
if (verbose >= 2 && !use_browser)
map__fprintf(before, fp);
map__put(before);
@@ -850,7 +863,9 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
}
after->start = map->end;
- __map_groups__insert(pos->groups, after);
+ after->pgoff += map->end - pos->start;
+ assert(pos->map_ip(pos, map->end) == after->map_ip(after, map->end));
+ __map_groups__insert(mg, after);
if (verbose >= 2 && !use_browser)
map__fprintf(after, fp);
map__put(after);
@@ -868,12 +883,6 @@ out:
return err;
}
-int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
- FILE *fp)
-{
- return maps__fixup_overlappings(&mg->maps, map, fp);
-}
-
/*
* XXX This should not really _copy_ te maps, but refcount them.
*/
@@ -886,7 +895,7 @@ int map_groups__clone(struct thread *thread, struct map_groups *parent)
down_read(&maps->lock);
- for (map = maps__first(maps); map; map = map__next(map)) {
+ maps__for_each_entry(maps, map) {
struct map *new = map__clone(map);
if (new == NULL)
goto out_unlock;
@@ -926,42 +935,17 @@ static void __maps__insert(struct maps *maps, struct map *map)
map__get(map);
}
-static void __maps__insert_name(struct maps *maps, struct map *map)
-{
- struct rb_node **p = &maps->names.rb_node;
- struct rb_node *parent = NULL;
- struct map *m;
- int rc;
-
- while (*p != NULL) {
- parent = *p;
- m = rb_entry(parent, struct map, rb_node_name);
- rc = strcmp(m->dso->short_name, map->dso->short_name);
- if (rc < 0)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&map->rb_node_name, parent, p);
- rb_insert_color(&map->rb_node_name, &maps->names);
- map__get(map);
-}
-
void maps__insert(struct maps *maps, struct map *map)
{
down_write(&maps->lock);
__maps__insert(maps, map);
- __maps__insert_name(maps, map);
up_write(&maps->lock);
}
-static void __maps__remove(struct maps *maps, struct map *map)
+void __maps__remove(struct maps *maps, struct map *map)
{
rb_erase_init(&map->rb_node, &maps->entries);
map__put(map);
-
- rb_erase_init(&map->rb_node_name, &maps->names);
- map__put(map);
}
void maps__remove(struct maps *maps, struct map *map)
@@ -1004,7 +988,7 @@ struct map *maps__first(struct maps *maps)
return NULL;
}
-struct map *map__next(struct map *map)
+static struct map *__map__next(struct map *map)
{
struct rb_node *next = rb_next(&map->rb_node);
@@ -1013,6 +997,11 @@ struct map *map__next(struct map *map)
return NULL;
}
+struct map *map__next(struct map *map)
+{
+ return map ? __map__next(map) : NULL;
+}
+
struct kmap *__map__kmap(struct map *map)
{
if (!map->dso || !map->dso->kernel)
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index c3614195ddc7..5e8899883231 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -23,18 +23,13 @@ struct map {
struct rb_node rb_node;
struct list_head node;
};
- struct rb_node rb_node_name;
u64 start;
u64 end;
- bool erange_warned;
- u32 priv;
+ bool erange_warned:1;
+ bool priv:1;
u32 prot;
- u32 flags;
u64 pgoff;
u64 reloc;
- u32 maj, min; /* only valid for MMAP2 record */
- u64 ino; /* only valid for MMAP2 record */
- u64 ino_generation;/* only valid for MMAP2 record */
/* ip -> dso rip */
u64 (*map_ip)(struct map *, u64);
@@ -42,8 +37,8 @@ struct map {
u64 (*unmap_ip)(struct map *, u64);
struct dso *dso;
- struct map_groups *groups;
refcount_t refcnt;
+ u32 flags;
};
struct kmap;
@@ -110,9 +105,11 @@ struct thread;
void map__init(struct map *map,
u64 start, u64 end, u64 pgoff, struct dso *dso);
+
+struct dso_id;
+
struct map *map__new(struct machine *machine, u64 start, u64 len,
- u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
- u64 ino_gen, u32 prot, u32 flags,
+ u64 pgoff, struct dso_id *id, u32 prot, u32 flags,
char *filename, struct thread *thread);
struct map *map__new2(u64 start, struct dso *dso);
void map__delete(struct map *map);
diff --git a/tools/perf/util/map_groups.h b/tools/perf/util/map_groups.h
index 77252e14008f..63ed211fe241 100644
--- a/tools/perf/util/map_groups.h
+++ b/tools/perf/util/map_groups.h
@@ -16,21 +16,32 @@ struct thread;
struct maps {
struct rb_root entries;
- struct rb_root names;
struct rw_semaphore lock;
};
void maps__insert(struct maps *maps, struct map *map);
void maps__remove(struct maps *maps, struct map *map);
+void __maps__remove(struct maps *maps, struct map *map);
struct map *maps__find(struct maps *maps, u64 addr);
struct map *maps__first(struct maps *maps);
struct map *map__next(struct map *map);
+
+#define maps__for_each_entry(maps, map) \
+ for (map = maps__first(maps); map; map = map__next(map))
+
+#define maps__for_each_entry_safe(maps, map, next) \
+ for (map = maps__first(maps), next = map__next(map); map; map = next, next = map__next(map))
+
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp);
struct map_groups {
struct maps maps;
struct machine *machine;
+ struct map *last_search_by_name;
+ struct map **maps_by_name;
refcount_t refcnt;
+ unsigned int nr_maps;
+ unsigned int nr_maps_allocated;
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
struct unwind_libunwind_ops *unwind_libunwind_ops;
@@ -64,29 +75,25 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
void map_groups__insert(struct map_groups *mg, struct map *map);
-static inline void map_groups__remove(struct map_groups *mg, struct map *map)
-{
- maps__remove(&mg->maps, map);
-}
+void map_groups__remove(struct map_groups *mg, struct map *map);
static inline struct map *map_groups__find(struct map_groups *mg, u64 addr)
{
return maps__find(&mg->maps, addr);
}
-struct map *map_groups__first(struct map_groups *mg);
+#define map_groups__for_each_entry(mg, map) \
+ for (map = maps__first(&mg->maps); map; map = map__next(map))
-static inline struct map *map_groups__next(struct map *map)
-{
- return map__next(map);
-}
+#define map_groups__for_each_entry_safe(mg, map, next) \
+ for (map = maps__first(&mg->maps), next = map__next(map); map; map = next, next = map__next(map))
struct symbol *map_groups__find_symbol(struct map_groups *mg, u64 addr, struct map **mapp);
struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, const char *name, struct map **mapp);
struct addr_map_symbol;
-int map_groups__find_ams(struct addr_map_symbol *ams);
+int map_groups__find_ams(struct map_groups *mg, struct addr_map_symbol *ams);
int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp);
@@ -94,4 +101,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, const char *name);
int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map);
+void __map_groups__sort_by_name(struct map_groups *mg);
+
#endif // __PERF_MAP_GROUPS_H
diff --git a/tools/perf/util/map_symbol.h b/tools/perf/util/map_symbol.h
index 5a1aed9f6bb4..2964d971aeab 100644
--- a/tools/perf/util/map_symbol.h
+++ b/tools/perf/util/map_symbol.h
@@ -4,17 +4,18 @@
#include <linux/types.h>
+struct map_groups;
struct map;
struct symbol;
struct map_symbol {
+ struct map_groups *mg;
struct map *map;
struct symbol *sym;
};
struct addr_map_symbol {
- struct map *map;
- struct symbol *sym;
+ struct map_symbol ms;
u64 addr;
u64 al_addr;
u64 phys_addr;
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index 3d391583f2ae..aa29589f6904 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -410,7 +410,7 @@ do { \
return -1;
}
- if (!mi->daddr.map || !mi->iaddr.map) {
+ if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
stats->nomap++;
return -1;
}
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index a7c0424dbda3..6a4d350d5cdb 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -523,7 +523,7 @@ int metricgroup__parse_groups(const struct option *opt,
if (ret)
return ret;
pr_debug("adding %s\n", extra_events.buf);
- memset(&parse_error, 0, sizeof(struct parse_events_error));
+ bzero(&parse_error, sizeof(parse_error));
ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
if (ret) {
parse_events_print_error(&parse_error, extra_events.buf);
diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c
index a35dc57d5995..063d1b93c53d 100644
--- a/tools/perf/util/mmap.c
+++ b/tools/perf/util/mmap.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // sysconf()
+#include <perf/mmap.h>
#ifdef HAVE_LIBNUMA_SUPPORT
#include <numaif.h>
#endif
@@ -23,116 +24,9 @@
#include "../perf.h"
#include <internal/lib.h> /* page_size */
-size_t perf_mmap__mmap_len(struct mmap *map)
+size_t mmap__mmap_len(struct mmap *map)
{
- return map->core.mask + 1 + page_size;
-}
-
-/* When check_messup is true, 'end' must points to a good entry */
-static union perf_event *perf_mmap__read(struct mmap *map,
- u64 *startp, u64 end)
-{
- unsigned char *data = map->core.base + page_size;
- union perf_event *event = NULL;
- int diff = end - *startp;
-
- if (diff >= (int)sizeof(event->header)) {
- size_t size;
-
- event = (union perf_event *)&data[*startp & map->core.mask];
- size = event->header.size;
-
- if (size < sizeof(event->header) || diff < (int)size)
- return NULL;
-
- /*
- * Event straddles the mmap boundary -- header should always
- * be inside due to u64 alignment of output.
- */
- if ((*startp & map->core.mask) + size != ((*startp + size) & map->core.mask)) {
- unsigned int offset = *startp;
- unsigned int len = min(sizeof(*event), size), cpy;
- void *dst = map->core.event_copy;
-
- do {
- cpy = min(map->core.mask + 1 - (offset & map->core.mask), len);
- memcpy(dst, &data[offset & map->core.mask], cpy);
- offset += cpy;
- dst += cpy;
- len -= cpy;
- } while (len);
-
- event = (union perf_event *)map->core.event_copy;
- }
-
- *startp += size;
- }
-
- return event;
-}
-
-/*
- * Read event from ring buffer one by one.
- * Return one event for each call.
- *
- * Usage:
- * perf_mmap__read_init()
- * while(event = perf_mmap__read_event()) {
- * //process the event
- * perf_mmap__consume()
- * }
- * perf_mmap__read_done()
- */
-union perf_event *perf_mmap__read_event(struct mmap *map)
-{
- union perf_event *event;
-
- /*
- * Check if event was unmapped due to a POLLHUP/POLLERR.
- */
- if (!refcount_read(&map->core.refcnt))
- return NULL;
-
- /* non-overwirte doesn't pause the ringbuffer */
- if (!map->core.overwrite)
- map->core.end = perf_mmap__read_head(map);
-
- event = perf_mmap__read(map, &map->core.start, map->core.end);
-
- if (!map->core.overwrite)
- map->core.prev = map->core.start;
-
- return event;
-}
-
-static bool perf_mmap__empty(struct mmap *map)
-{
- return perf_mmap__read_head(map) == map->core.prev && !map->auxtrace_mmap.base;
-}
-
-void perf_mmap__get(struct mmap *map)
-{
- refcount_inc(&map->core.refcnt);
-}
-
-void perf_mmap__put(struct mmap *map)
-{
- BUG_ON(map->core.base && refcount_read(&map->core.refcnt) == 0);
-
- if (refcount_dec_and_test(&map->core.refcnt))
- perf_mmap__munmap(map);
-}
-
-void perf_mmap__consume(struct mmap *map)
-{
- if (!map->core.overwrite) {
- u64 old = map->core.prev;
-
- perf_mmap__write_tail(map, old);
- }
-
- if (refcount_read(&map->core.refcnt) == 1 && perf_mmap__empty(map))
- perf_mmap__put(map);
+ return perf_mmap__mmap_len(&map->core);
}
int __weak auxtrace_mmap__mmap(struct auxtrace_mmap *mm __maybe_unused,
@@ -170,7 +64,7 @@ static int perf_mmap__aio_enabled(struct mmap *map)
#ifdef HAVE_LIBNUMA_SUPPORT
static int perf_mmap__aio_alloc(struct mmap *map, int idx)
{
- map->aio.data[idx] = mmap(NULL, perf_mmap__mmap_len(map), PROT_READ|PROT_WRITE,
+ map->aio.data[idx] = mmap(NULL, mmap__mmap_len(map), PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (map->aio.data[idx] == MAP_FAILED) {
map->aio.data[idx] = NULL;
@@ -183,7 +77,7 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx)
static void perf_mmap__aio_free(struct mmap *map, int idx)
{
if (map->aio.data[idx]) {
- munmap(map->aio.data[idx], perf_mmap__mmap_len(map));
+ munmap(map->aio.data[idx], mmap__mmap_len(map));
map->aio.data[idx] = NULL;
}
}
@@ -196,7 +90,7 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, int cpu, int affinity)
if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) {
data = map->aio.data[idx];
- mmap_len = perf_mmap__mmap_len(map);
+ mmap_len = mmap__mmap_len(map);
node_mask = 1UL << cpu__get_node(cpu);
if (mbind(data, mmap_len, MPOL_BIND, &node_mask, 1, 0)) {
pr_err("Failed to bind [%p-%p] AIO buffer to node %d: error %m\n",
@@ -210,7 +104,7 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, int cpu, int affinity)
#else /* !HAVE_LIBNUMA_SUPPORT */
static int perf_mmap__aio_alloc(struct mmap *map, int idx)
{
- map->aio.data[idx] = malloc(perf_mmap__mmap_len(map));
+ map->aio.data[idx] = malloc(mmap__mmap_len(map));
if (map->aio.data[idx] == NULL)
return -1;
@@ -311,19 +205,13 @@ static void perf_mmap__aio_munmap(struct mmap *map __maybe_unused)
}
#endif
-void perf_mmap__munmap(struct mmap *map)
+void mmap__munmap(struct mmap *map)
{
perf_mmap__aio_munmap(map);
if (map->data != NULL) {
- munmap(map->data, perf_mmap__mmap_len(map));
+ munmap(map->data, mmap__mmap_len(map));
map->data = NULL;
}
- if (map->core.base != NULL) {
- munmap(map->core.base, perf_mmap__mmap_len(map));
- map->core.base = NULL;
- map->core.fd = -1;
- refcount_set(&map->core.refcnt, 0);
- }
auxtrace_mmap__munmap(&map->auxtrace_mmap);
}
@@ -353,34 +241,13 @@ static void perf_mmap__setup_affinity_mask(struct mmap *map, struct mmap_params
CPU_SET(map->core.cpu, &map->affinity_mask);
}
-int perf_mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu)
+int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu)
{
- /*
- * The last one will be done at perf_mmap__consume(), so that we
- * make sure we don't prevent tools from consuming every last event in
- * the ring buffer.
- *
- * I.e. we can get the POLLHUP meaning that the fd doesn't exist
- * anymore, but the last events for it are still in the ring buffer,
- * waiting to be consumed.
- *
- * Tools can chose to ignore this at their own discretion, but the
- * evlist layer can't just drop it when filtering events in
- * perf_evlist__filter_pollfd().
- */
- refcount_set(&map->core.refcnt, 2);
- map->core.prev = 0;
- map->core.mask = mp->mask;
- map->core.base = mmap(NULL, perf_mmap__mmap_len(map), mp->prot,
- MAP_SHARED, fd, 0);
- if (map->core.base == MAP_FAILED) {
+ if (perf_mmap__mmap(&map->core, &mp->core, fd, cpu)) {
pr_debug2("failed to mmap perf event ring buffer, error %d\n",
errno);
- map->core.base = NULL;
return -1;
}
- map->core.fd = fd;
- map->core.cpu = cpu;
perf_mmap__setup_affinity_mask(map, mp);
@@ -389,7 +256,7 @@ int perf_mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu)
map->comp_level = mp->comp_level;
if (map->comp_level && !perf_mmap__aio_enabled(map)) {
- map->data = mmap(NULL, perf_mmap__mmap_len(map), PROT_READ|PROT_WRITE,
+ map->data = mmap(NULL, mmap__mmap_len(map), PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (map->data == MAP_FAILED) {
pr_debug2("failed to mmap data buffer, error %d\n",
@@ -406,96 +273,16 @@ int perf_mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu)
return perf_mmap__aio_mmap(map, mp);
}
-static int overwrite_rb_find_range(void *buf, int mask, u64 *start, u64 *end)
-{
- struct perf_event_header *pheader;
- u64 evt_head = *start;
- int size = mask + 1;
-
- pr_debug2("%s: buf=%p, start=%"PRIx64"\n", __func__, buf, *start);
- pheader = (struct perf_event_header *)(buf + (*start & mask));
- while (true) {
- if (evt_head - *start >= (unsigned int)size) {
- pr_debug("Finished reading overwrite ring buffer: rewind\n");
- if (evt_head - *start > (unsigned int)size)
- evt_head -= pheader->size;
- *end = evt_head;
- return 0;
- }
-
- pheader = (struct perf_event_header *)(buf + (evt_head & mask));
-
- if (pheader->size == 0) {
- pr_debug("Finished reading overwrite ring buffer: get start\n");
- *end = evt_head;
- return 0;
- }
-
- evt_head += pheader->size;
- pr_debug3("move evt_head: %"PRIx64"\n", evt_head);
- }
- WARN_ONCE(1, "Shouldn't get here\n");
- return -1;
-}
-
-/*
- * Report the start and end of the available data in ringbuffer
- */
-static int __perf_mmap__read_init(struct mmap *md)
-{
- u64 head = perf_mmap__read_head(md);
- u64 old = md->core.prev;
- unsigned char *data = md->core.base + page_size;
- unsigned long size;
-
- md->core.start = md->core.overwrite ? head : old;
- md->core.end = md->core.overwrite ? old : head;
-
- if ((md->core.end - md->core.start) < md->core.flush)
- return -EAGAIN;
-
- size = md->core.end - md->core.start;
- if (size > (unsigned long)(md->core.mask) + 1) {
- if (!md->core.overwrite) {
- WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n");
-
- md->core.prev = head;
- perf_mmap__consume(md);
- return -EAGAIN;
- }
-
- /*
- * Backward ring buffer is full. We still have a chance to read
- * most of data from it.
- */
- if (overwrite_rb_find_range(data, md->core.mask, &md->core.start, &md->core.end))
- return -EINVAL;
- }
-
- return 0;
-}
-
-int perf_mmap__read_init(struct mmap *map)
-{
- /*
- * Check if event was unmapped due to a POLLHUP/POLLERR.
- */
- if (!refcount_read(&map->core.refcnt))
- return -ENOENT;
-
- return __perf_mmap__read_init(map);
-}
-
int perf_mmap__push(struct mmap *md, void *to,
int push(struct mmap *map, void *to, void *buf, size_t size))
{
- u64 head = perf_mmap__read_head(md);
+ u64 head = perf_mmap__read_head(&md->core);
unsigned char *data = md->core.base + page_size;
unsigned long size;
void *buf;
int rc = 0;
- rc = perf_mmap__read_init(md);
+ rc = perf_mmap__read_init(&md->core);
if (rc < 0)
return (rc == -EAGAIN) ? 1 : -1;
@@ -522,24 +309,7 @@ int perf_mmap__push(struct mmap *md, void *to,
}
md->core.prev = head;
- perf_mmap__consume(md);
+ perf_mmap__consume(&md->core);
out:
return rc;
}
-
-/*
- * Mandatory for overwrite mode
- * The direction of overwrite mode is backward.
- * The last perf_mmap__read() will set tail to map->core.prev.
- * Need to correct the map->core.prev to head which is the end of next read.
- */
-void perf_mmap__read_done(struct mmap *map)
-{
- /*
- * Check if event was unmapped due to a POLLHUP/POLLERR.
- */
- if (!refcount_read(&map->core.refcnt))
- return;
-
- map->core.prev = perf_mmap__read_head(map);
-}
diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h
index e567c1c875bd..bee4e83f7109 100644
--- a/tools/perf/util/mmap.h
+++ b/tools/perf/util/mmap.h
@@ -37,37 +37,19 @@ struct mmap {
};
struct mmap_params {
- int prot, mask, nr_cblocks, affinity, flush, comp_level;
+ struct perf_mmap_param core;
+ int nr_cblocks, affinity, flush, comp_level;
struct auxtrace_mmap_params auxtrace_mp;
};
-int perf_mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu);
-void perf_mmap__munmap(struct mmap *map);
-
-void perf_mmap__get(struct mmap *map);
-void perf_mmap__put(struct mmap *map);
-
-void perf_mmap__consume(struct mmap *map);
-
-static inline u64 perf_mmap__read_head(struct mmap *mm)
-{
- return ring_buffer_read_head(mm->core.base);
-}
-
-static inline void perf_mmap__write_tail(struct mmap *md, u64 tail)
-{
- ring_buffer_write_tail(md->core.base, tail);
-}
+int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu);
+void mmap__munmap(struct mmap *map);
union perf_event *perf_mmap__read_forward(struct mmap *map);
-union perf_event *perf_mmap__read_event(struct mmap *map);
-
int perf_mmap__push(struct mmap *md, void *to,
int push(struct mmap *map, void *to, void *buf, size_t size));
-size_t perf_mmap__mmap_len(struct mmap *map);
+size_t mmap__mmap_len(struct mmap *map);
-int perf_mmap__read_init(struct mmap *md);
-void perf_mmap__read_done(struct mmap *map);
#endif /*__PERF_MMAP_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b5e2adef49de..ed7c008b9c8b 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -182,6 +182,37 @@ static int tp_event_has_id(const char *dir_path, struct dirent *evt_dir)
#define MAX_EVENT_LENGTH 512
+void parse_events__handle_error(struct parse_events_error *err, int idx,
+ char *str, char *help)
+{
+ if (WARN(!str, "WARNING: failed to provide error string\n")) {
+ free(help);
+ return;
+ }
+ switch (err->num_errors) {
+ case 0:
+ err->idx = idx;
+ err->str = str;
+ err->help = help;
+ break;
+ case 1:
+ err->first_idx = err->idx;
+ err->idx = idx;
+ err->first_str = err->str;
+ err->str = str;
+ err->first_help = err->help;
+ err->help = help;
+ break;
+ default:
+ WARN_ONCE(1, "WARNING: multiple event parsing errors\n");
+ free(err->str);
+ err->str = str;
+ free(err->help);
+ err->help = help;
+ break;
+ }
+ err->num_errors++;
+}
struct tracepoint_path *tracepoint_id_to_path(u64 config)
{
@@ -480,6 +511,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,
static void tracepoint_error(struct parse_events_error *e, int err,
const char *sys, const char *name)
{
+ const char *str;
char help[BUFSIZ];
if (!e)
@@ -493,18 +525,18 @@ static void tracepoint_error(struct parse_events_error *e, int err,
switch (err) {
case EACCES:
- e->str = strdup("can't access trace events");
+ str = "can't access trace events";
break;
case ENOENT:
- e->str = strdup("unknown tracepoint");
+ str = "unknown tracepoint";
break;
default:
- e->str = strdup("failed to add tracepoint");
+ str = "failed to add tracepoint";
break;
}
tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name);
- e->help = strdup(help);
+ parse_events__handle_error(e, 0, strdup(str), strdup(help));
}
static int add_tracepoint(struct list_head *list, int *idx,
@@ -932,11 +964,11 @@ static int check_type_val(struct parse_events_term *term,
return 0;
if (err) {
- err->idx = term->err_val;
- if (type == PARSE_EVENTS__TERM_TYPE_NUM)
- err->str = strdup("expected numeric value");
- else
- err->str = strdup("expected string value");
+ parse_events__handle_error(err, term->err_val,
+ type == PARSE_EVENTS__TERM_TYPE_NUM
+ ? strdup("expected numeric value")
+ : strdup("expected string value"),
+ NULL);
}
return -EINVAL;
}
@@ -965,6 +997,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
[PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
[PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
+ [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size",
};
static bool config_term_shrinked;
@@ -972,8 +1005,11 @@ static bool config_term_shrinked;
static bool
config_term_avail(int term_type, struct parse_events_error *err)
{
+ char *err_str;
+
if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) {
- err->str = strdup("Invalid term_type");
+ parse_events__handle_error(err, -1,
+ strdup("Invalid term_type"), NULL);
return false;
}
if (!config_term_shrinked)
@@ -992,9 +1028,9 @@ config_term_avail(int term_type, struct parse_events_error *err)
return false;
/* term_type is validated so indexing is safe */
- if (asprintf(&err->str, "'%s' is not usable in 'perf stat'",
- config_term_names[term_type]) < 0)
- err->str = NULL;
+ if (asprintf(&err_str, "'%s' is not usable in 'perf stat'",
+ config_term_names[term_type]) >= 0)
+ parse_events__handle_error(err, -1, err_str, NULL);
return false;
}
}
@@ -1036,17 +1072,20 @@ do { \
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
CHECK_TYPE_VAL(STR);
if (strcmp(term->val.str, "no") &&
- parse_branch_str(term->val.str, &attr->branch_sample_type)) {
- err->str = strdup("invalid branch sample type");
- err->idx = term->err_val;
+ parse_branch_str(term->val.str,
+ &attr->branch_sample_type)) {
+ parse_events__handle_error(err, term->err_val,
+ strdup("invalid branch sample type"),
+ NULL);
return -EINVAL;
}
break;
case PARSE_EVENTS__TERM_TYPE_TIME:
CHECK_TYPE_VAL(NUM);
if (term->val.num > 1) {
- err->str = strdup("expected 0 or 1");
- err->idx = term->err_val;
+ parse_events__handle_error(err, term->err_val,
+ strdup("expected 0 or 1"),
+ NULL);
return -EINVAL;
}
break;
@@ -1080,18 +1119,28 @@ do { \
case PARSE_EVENTS__TERM_TYPE_PERCORE:
CHECK_TYPE_VAL(NUM);
if ((unsigned int)term->val.num > 1) {
- err->str = strdup("expected 0 or 1");
- err->idx = term->err_val;
+ parse_events__handle_error(err, term->err_val,
+ strdup("expected 0 or 1"),
+ NULL);
return -EINVAL;
}
break;
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
CHECK_TYPE_VAL(NUM);
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
+ CHECK_TYPE_VAL(NUM);
+ if (term->val.num > UINT_MAX) {
+ parse_events__handle_error(err, term->err_val,
+ strdup("too big"),
+ NULL);
+ return -EINVAL;
+ }
+ break;
default:
- err->str = strdup("unknown term");
- err->idx = term->err_term;
- err->help = parse_events_formats_error_string(NULL);
+ parse_events__handle_error(err, term->err_term,
+ strdup("unknown term"),
+ parse_events_formats_error_string(NULL));
return -EINVAL;
}
@@ -1139,12 +1188,13 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
return config_term_common(attr, term, err);
default:
if (err) {
- err->idx = term->err_term;
- err->str = strdup("unknown term");
- err->help = strdup("valid terms: call-graph,stack-size\n");
+ parse_events__handle_error(err, term->err_term,
+ strdup("unknown term"),
+ strdup("valid terms: call-graph,stack-size\n"));
}
return -EINVAL;
}
@@ -1234,11 +1284,47 @@ do { \
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
ADD_CONFIG_TERM(AUX_OUTPUT, aux_output, term->val.num ? 1 : 0);
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
+ ADD_CONFIG_TERM(AUX_SAMPLE_SIZE, aux_sample_size, term->val.num);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Add PERF_EVSEL__CONFIG_TERM_CFG_CHG where cfg_chg will have a bit set for
+ * each bit of attr->config that the user has changed.
+ */
+static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config,
+ struct list_head *head_terms)
+{
+ struct parse_events_term *term;
+ u64 bits = 0;
+ int type;
+
+ list_for_each_entry(term, head_config, list) {
+ switch (term->type_term) {
+ case PARSE_EVENTS__TERM_TYPE_USER:
+ type = perf_pmu__format_type(&pmu->format, term->config);
+ if (type != PERF_PMU_FORMAT_VALUE_CONFIG)
+ continue;
+ bits |= perf_pmu__format_bits(&pmu->format, term->config);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ bits = ~(u64)0;
+ break;
default:
break;
}
}
-#undef ADD_EVSEL_CONFIG
+
+ if (bits)
+ ADD_CONFIG_TERM(CFG_CHG, cfg_chg, bits);
+
+#undef ADD_CONFIG_TERM
return 0;
}
@@ -1323,10 +1409,12 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
pmu = perf_pmu__find(name);
if (!pmu) {
- if (asprintf(&err->str,
+ char *err_str;
+
+ if (asprintf(&err_str,
"Cannot find PMU `%s'. Missing kernel support?",
- name) < 0)
- err->str = NULL;
+ name) >= 0)
+ parse_events__handle_error(err, 0, err_str, NULL);
return -EINVAL;
}
@@ -1365,8 +1453,22 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
if (get_config_terms(head_config, &config_terms))
return -ENOMEM;
- if (perf_pmu__config(pmu, &attr, head_config, parse_state->error))
+ /*
+ * When using default config, record which bits of attr->config were
+ * changed by the user.
+ */
+ if (pmu->default_config && get_config_chgs(pmu, head_config, &config_terms))
+ return -ENOMEM;
+
+ if (perf_pmu__config(pmu, &attr, head_config, parse_state->error)) {
+ struct perf_evsel_config_term *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, &config_terms, list) {
+ list_del_init(&pos->list);
+ free(pos);
+ }
return -EINVAL;
+ }
evsel = __add_event(list, &parse_state->idx, &attr,
get_config_name(head_config), pmu,
@@ -1389,7 +1491,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
char *str, struct list_head **listp)
{
- struct list_head *head;
struct parse_events_term *term;
struct list_head *list;
struct perf_pmu *pmu = NULL;
@@ -1406,19 +1507,30 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
list_for_each_entry(alias, &pmu->aliases, list) {
if (!strcasecmp(alias->name, str)) {
+ struct list_head *head;
+ char *config;
+
head = malloc(sizeof(struct list_head));
if (!head)
return -1;
INIT_LIST_HEAD(head);
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- str, 1, false, &str, NULL) < 0)
+ config = strdup(str);
+ if (!config)
return -1;
+ if (parse_events_term__num(&term,
+ PARSE_EVENTS__TERM_TYPE_USER,
+ config, 1, false, &config,
+ NULL) < 0) {
+ free(list);
+ free(config);
+ return -1;
+ }
list_add_tail(&term->list, head);
if (!parse_events_add_pmu(parse_state, list,
pmu->name, head,
true, true)) {
- pr_debug("%s -> %s/%s/\n", str,
+ pr_debug("%s -> %s/%s/\n", config,
pmu->name, alias->str);
ok++;
}
@@ -1427,8 +1539,10 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
}
}
}
- if (!ok)
+ if (!ok) {
+ free(list);
return -1;
+ }
*listp = list;
return 0;
}
@@ -1927,15 +2041,20 @@ int parse_events(struct evlist *evlist, const char *str,
ret = parse_events__scanner(str, &parse_state, PE_START_EVENTS);
perf_pmu__parse_cleanup();
+
+ if (!ret && list_empty(&parse_state.list)) {
+ WARN_ONCE(true, "WARNING: event parser found nothing\n");
+ return -1;
+ }
+
+ /*
+ * Add list to the evlist even with errors to allow callers to clean up.
+ */
+ perf_evlist__splice_list_tail(evlist, &parse_state.list);
+
if (!ret) {
struct evsel *last;
- if (list_empty(&parse_state.list)) {
- WARN_ONCE(true, "WARNING: event parser found nothing\n");
- return -1;
- }
-
- perf_evlist__splice_list_tail(evlist, &parse_state.list);
evlist->nr_groups += parse_state.nr_groups;
last = evlist__last(evlist);
last->cmdline_group_boundary = true;
@@ -1960,15 +2079,14 @@ static int get_term_width(void)
return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col;
}
-void parse_events_print_error(struct parse_events_error *err,
- const char *event)
+static void __parse_events_print_error(int err_idx, const char *err_str,
+ const char *err_help, const char *event)
{
const char *str = "invalid or unsupported event: ";
char _buf[MAX_WIDTH];
char *buf = (char *) event;
int idx = 0;
-
- if (err->str) {
+ if (err_str) {
/* -2 for extra '' in the final fprintf */
int width = get_term_width() - 2;
int len_event = strlen(event);
@@ -1991,8 +2109,8 @@ void parse_events_print_error(struct parse_events_error *err,
buf = _buf;
/* We're cutting from the beginning. */
- if (err->idx > max_err_idx)
- cut = err->idx - max_err_idx;
+ if (err_idx > max_err_idx)
+ cut = err_idx - max_err_idx;
strncpy(buf, event + cut, max_len);
@@ -2005,16 +2123,33 @@ void parse_events_print_error(struct parse_events_error *err,
buf[max_len] = 0;
}
- idx = len_str + err->idx - cut;
+ idx = len_str + err_idx - cut;
}
fprintf(stderr, "%s'%s'\n", str, buf);
if (idx) {
- fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str);
- if (err->help)
- fprintf(stderr, "\n%s\n", err->help);
- zfree(&err->str);
- zfree(&err->help);
+ fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err_str);
+ if (err_help)
+ fprintf(stderr, "\n%s\n", err_help);
+ }
+}
+
+void parse_events_print_error(struct parse_events_error *err,
+ const char *event)
+{
+ if (!err->num_errors)
+ return;
+
+ __parse_events_print_error(err->idx, err->str, err->help, event);
+ zfree(&err->str);
+ zfree(&err->help);
+
+ if (err->num_errors > 1) {
+ fputs("\nInitial error:\n", stderr);
+ __parse_events_print_error(err->first_idx, err->first_str,
+ err->first_help, event);
+ zfree(&err->first_str);
+ zfree(&err->first_help);
}
}
@@ -2024,8 +2159,11 @@ int parse_events_option(const struct option *opt, const char *str,
int unset __maybe_unused)
{
struct evlist *evlist = *(struct evlist **)opt->value;
- struct parse_events_error err = { .idx = 0, };
- int ret = parse_events(evlist, str, &err);
+ struct parse_events_error err;
+ int ret;
+
+ bzero(&err, sizeof(err));
+ ret = parse_events(evlist, str, &err);
if (ret) {
parse_events_print_error(&err, str);
@@ -2600,7 +2738,7 @@ out_enomem:
* Print the help text for the event symbols:
*/
void print_events(const char *event_glob, bool name_only, bool quiet_flag,
- bool long_desc, bool details_flag)
+ bool long_desc, bool details_flag, bool deprecated)
{
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
@@ -2612,7 +2750,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag,
print_hwcache_events(event_glob, name_only);
print_pmu_events(event_glob, name_only, quiet_flag, long_desc,
- details_flag);
+ details_flag, deprecated);
if (event_glob != NULL)
return;
@@ -2718,30 +2856,63 @@ int parse_events_term__sym_hw(struct parse_events_term **term,
char *config, unsigned idx)
{
struct event_symbol *sym;
+ char *str;
struct parse_events_term temp = {
.type_val = PARSE_EVENTS__TERM_TYPE_STR,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
- .config = config ?: (char *) "event",
+ .config = config,
};
+ if (!temp.config) {
+ temp.config = strdup("event");
+ if (!temp.config)
+ return -ENOMEM;
+ }
BUG_ON(idx >= PERF_COUNT_HW_MAX);
sym = &event_symbols_hw[idx];
- return new_term(term, &temp, (char *) sym->symbol, 0);
+ str = strdup(sym->symbol);
+ if (!str)
+ return -ENOMEM;
+ return new_term(term, &temp, str, 0);
}
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term)
{
+ char *str;
struct parse_events_term temp = {
.type_val = term->type_val,
.type_term = term->type_term,
- .config = term->config,
+ .config = NULL,
.err_term = term->err_term,
.err_val = term->err_val,
};
- return new_term(new, &temp, term->val.str, term->val.num);
+ if (term->config) {
+ temp.config = strdup(term->config);
+ if (!temp.config)
+ return -ENOMEM;
+ }
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
+ return new_term(new, &temp, NULL, term->val.num);
+
+ str = strdup(term->val.str);
+ if (!str)
+ return -ENOMEM;
+ return new_term(new, &temp, str, 0);
+}
+
+void parse_events_term__delete(struct parse_events_term *term)
+{
+ if (term->array.nr_ranges)
+ zfree(&term->array.ranges);
+
+ if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM)
+ zfree(&term->val.str);
+
+ zfree(&term->config);
+ free(term);
}
int parse_events_copy_term_list(struct list_head *old,
@@ -2774,10 +2945,8 @@ void parse_events_terms__purge(struct list_head *terms)
struct parse_events_term *term, *h;
list_for_each_entry_safe(term, h, terms, list) {
- if (term->array.nr_ranges)
- zfree(&term->array.ranges);
list_del_init(&term->list);
- free(term);
+ parse_events_term__delete(term);
}
}
@@ -2797,13 +2966,10 @@ void parse_events__clear_array(struct parse_events_array *a)
void parse_events_evlist_error(struct parse_events_state *parse_state,
int idx, const char *str)
{
- struct parse_events_error *err = parse_state->error;
-
- if (!err)
+ if (!parse_state->error)
return;
- err->idx = idx;
- err->str = strdup(str);
- WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
+
+ parse_events__handle_error(parse_state->error, idx, strdup(str), NULL);
}
static void config_terms_list(char *buf, size_t buf_sz)
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 616ca1eda0eb..27596cbd0ba0 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -77,6 +77,7 @@ enum {
PARSE_EVENTS__TERM_TYPE_DRV_CFG,
PARSE_EVENTS__TERM_TYPE_PERCORE,
PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT,
+ PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE,
__PARSE_EVENTS__TERM_TYPE_NR,
};
@@ -110,9 +111,13 @@ struct parse_events_term {
};
struct parse_events_error {
+ int num_errors; /* number of errors encountered */
int idx; /* index in the parsed string */
char *str; /* string to display at the index */
char *help; /* optional help string */
+ int first_idx;/* as above, but for the first encountered error */
+ char *first_str;
+ char *first_help;
};
struct parse_events_state {
@@ -124,6 +129,8 @@ struct parse_events_state {
struct list_head *terms;
};
+void parse_events__handle_error(struct parse_events_error *err, int idx,
+ char *str, char *help);
void parse_events__shrink_config_terms(void);
int parse_events__is_hardcoded_term(struct parse_events_term *term);
int parse_events_term__num(struct parse_events_term **term,
@@ -137,6 +144,7 @@ int parse_events_term__sym_hw(struct parse_events_term **term,
char *config, unsigned idx);
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term);
+void parse_events_term__delete(struct parse_events_term *term);
void parse_events_terms__delete(struct list_head *terms);
void parse_events_terms__purge(struct list_head *terms);
void parse_events__clear_array(struct parse_events_array *a);
@@ -195,7 +203,7 @@ void parse_events_evlist_error(struct parse_events_state *parse_state,
int idx, const char *str);
void print_events(const char *event_glob, bool name_only, bool quiet,
- bool long_desc, bool details_flag);
+ bool long_desc, bool details_flag, bool deprecated);
struct event_symbol {
const char *symbol;
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 7469497cd28e..7b1c8ee537cf 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -285,6 +285,7 @@ overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
+aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 48126ae4cd13..e2eea4e601b4 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "pmu.h"
#include "evsel.h"
#include "parse-events.h"
@@ -25,12 +26,28 @@ do { \
YYABORT; \
} while (0)
-#define ALLOC_LIST(list) \
-do { \
- list = malloc(sizeof(*list)); \
- ABORT_ON(!list); \
- INIT_LIST_HEAD(list); \
-} while (0)
+static struct list_head* alloc_list()
+{
+ struct list_head *list;
+
+ list = malloc(sizeof(*list));
+ if (!list)
+ return NULL;
+
+ INIT_LIST_HEAD(list);
+ return list;
+}
+
+static void free_list_evsel(struct list_head* list_evsel)
+{
+ struct evsel *evsel, *tmp;
+
+ list_for_each_entry_safe(evsel, tmp, list_evsel, core.node) {
+ list_del_init(&evsel->core.node);
+ perf_evsel__delete(evsel);
+ }
+ free(list_evsel);
+}
static void inc_group_count(struct list_head *list,
struct parse_events_state *parse_state)
@@ -61,6 +78,7 @@ static void inc_group_count(struct list_head *list,
%type <num> PE_VALUE_SYM_TOOL
%type <num> PE_RAW
%type <num> PE_TERM
+%type <num> value_sym
%type <str> PE_NAME
%type <str> PE_BPF_OBJECT
%type <str> PE_BPF_SOURCE
@@ -71,37 +89,43 @@ static void inc_group_count(struct list_head *list,
%type <str> PE_EVENT_NAME
%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <str> PE_DRV_CFG_TERM
-%type <num> value_sym
-%type <head> event_config
-%type <head> opt_event_config
-%type <head> opt_pmu_config
+%destructor { free ($$); } <str>
%type <term> event_term
-%type <head> event_pmu
-%type <head> event_legacy_symbol
-%type <head> event_legacy_cache
-%type <head> event_legacy_mem
-%type <head> event_legacy_tracepoint
+%destructor { parse_events_term__delete ($$); } <term>
+%type <list_terms> event_config
+%type <list_terms> opt_event_config
+%type <list_terms> opt_pmu_config
+%destructor { parse_events_terms__delete ($$); } <list_terms>
+%type <list_evsel> event_pmu
+%type <list_evsel> event_legacy_symbol
+%type <list_evsel> event_legacy_cache
+%type <list_evsel> event_legacy_mem
+%type <list_evsel> event_legacy_tracepoint
+%type <list_evsel> event_legacy_numeric
+%type <list_evsel> event_legacy_raw
+%type <list_evsel> event_bpf_file
+%type <list_evsel> event_def
+%type <list_evsel> event_mod
+%type <list_evsel> event_name
+%type <list_evsel> event
+%type <list_evsel> events
+%type <list_evsel> group_def
+%type <list_evsel> group
+%type <list_evsel> groups
+%destructor { free_list_evsel ($$); } <list_evsel>
%type <tracepoint_name> tracepoint_name
-%type <head> event_legacy_numeric
-%type <head> event_legacy_raw
-%type <head> event_bpf_file
-%type <head> event_def
-%type <head> event_mod
-%type <head> event_name
-%type <head> event
-%type <head> events
-%type <head> group_def
-%type <head> group
-%type <head> groups
+%destructor { free ($$.sys); free ($$.event); } <tracepoint_name>
%type <array> array
%type <array> array_term
%type <array> array_terms
+%destructor { free ($$.ranges); } <array>
%union
{
char *str;
u64 num;
- struct list_head *head;
+ struct list_head *list_evsel;
+ struct list_head *list_terms;
struct parse_events_term *term;
struct tracepoint_name {
char *sys;
@@ -120,6 +144,7 @@ start_events: groups
{
struct parse_events_state *parse_state = _parse_state;
+ /* frees $1 */
parse_events_update_lists($1, &parse_state->list);
}
@@ -129,6 +154,7 @@ groups ',' group
struct list_head *list = $1;
struct list_head *group = $3;
+ /* frees $3 */
parse_events_update_lists(group, list);
$$ = list;
}
@@ -138,6 +164,7 @@ groups ',' event
struct list_head *list = $1;
struct list_head *event = $3;
+ /* frees $3 */
parse_events_update_lists(event, list);
$$ = list;
}
@@ -150,8 +177,14 @@ group:
group_def ':' PE_MODIFIER_EVENT
{
struct list_head *list = $1;
+ int err;
- ABORT_ON(parse_events__modifier_group(list, $3));
+ err = parse_events__modifier_group(list, $3);
+ free($3);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -164,6 +197,7 @@ PE_NAME '{' events '}'
inc_group_count(list, _parse_state);
parse_events__set_leader($1, list, _parse_state);
+ free($1);
$$ = list;
}
|
@@ -182,6 +216,7 @@ events ',' event
struct list_head *event = $3;
struct list_head *list = $1;
+ /* frees $3 */
parse_events_update_lists(event, list);
$$ = list;
}
@@ -194,13 +229,19 @@ event_mod:
event_name PE_MODIFIER_EVENT
{
struct list_head *list = $1;
+ int err;
/*
* Apply modifier on all events added by single event definition
* (there could be more events added for multiple tracepoint
* definitions via '*?'.
*/
- ABORT_ON(parse_events__modifier_event(list, $2, false));
+ err = parse_events__modifier_event(list, $2, false);
+ free($2);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -209,8 +250,14 @@ event_name
event_name:
PE_EVENT_NAME event_def
{
- ABORT_ON(parse_events_name($2, $1));
+ int err;
+
+ err = parse_events_name($2, $1);
free($1);
+ if (err) {
+ free_list_evsel($2);
+ YYABORT;
+ }
$$ = $2;
}
|
@@ -230,22 +277,34 @@ PE_NAME opt_pmu_config
{
struct parse_events_state *parse_state = _parse_state;
struct parse_events_error *error = parse_state->error;
- struct list_head *list, *orig_terms, *terms;
+ struct list_head *list = NULL, *orig_terms = NULL, *terms= NULL;
+ char *pattern = NULL;
+
+#define CLEANUP_YYABORT \
+ do { \
+ parse_events_terms__delete($2); \
+ parse_events_terms__delete(orig_terms); \
+ free(list); \
+ free($1); \
+ free(pattern); \
+ YYABORT; \
+ } while(0)
if (parse_events_copy_term_list($2, &orig_terms))
- YYABORT;
+ CLEANUP_YYABORT;
if (error)
error->idx = @1.first_column;
- ALLOC_LIST(list);
+ list = alloc_list();
+ if (!list)
+ CLEANUP_YYABORT;
if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
struct perf_pmu *pmu = NULL;
int ok = 0;
- char *pattern;
if (asprintf(&pattern, "%s*", $1) < 0)
- YYABORT;
+ CLEANUP_YYABORT;
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
char *name = pmu->name;
@@ -254,31 +313,32 @@ PE_NAME opt_pmu_config
strncmp($1, "uncore_", 7))
name += 7;
if (!fnmatch(pattern, name, 0)) {
- if (parse_events_copy_term_list(orig_terms, &terms)) {
- free(pattern);
- YYABORT;
- }
+ if (parse_events_copy_term_list(orig_terms, &terms))
+ CLEANUP_YYABORT;
if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
ok++;
parse_events_terms__delete(terms);
}
}
- free(pattern);
-
if (!ok)
- YYABORT;
+ CLEANUP_YYABORT;
}
parse_events_terms__delete($2);
parse_events_terms__delete(orig_terms);
+ free($1);
$$ = list;
+#undef CLEANUP_YYABORT
}
|
PE_KERNEL_PMU_EVENT sep_dc
{
struct list_head *list;
+ int err;
- if (parse_events_multi_pmu_add(_parse_state, $1, &list) < 0)
+ err = parse_events_multi_pmu_add(_parse_state, $1, &list);
+ free($1);
+ if (err < 0)
YYABORT;
$$ = list;
}
@@ -289,6 +349,8 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
char pmu_name[128];
snprintf(&pmu_name, 128, "%s-%s", $1, $3);
+ free($1);
+ free($3);
if (parse_events_multi_pmu_add(_parse_state, pmu_name, &list) < 0)
YYABORT;
$$ = list;
@@ -305,10 +367,16 @@ value_sym '/' event_config '/'
struct list_head *list;
int type = $1 >> 16;
int config = $1 & 255;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config, $3));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_numeric(_parse_state, list, type, config, $3);
parse_events_terms__delete($3);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -318,7 +386,8 @@ value_sym sep_slash_slash_dc
int type = $1 >> 16;
int config = $1 & 255;
- ALLOC_LIST(list);
+ list = alloc_list();
+ ABORT_ON(!list);
ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config, NULL));
$$ = list;
}
@@ -327,7 +396,8 @@ PE_VALUE_SYM_TOOL sep_slash_slash_dc
{
struct list_head *list;
- ALLOC_LIST(list);
+ list = alloc_list();
+ ABORT_ON(!list);
ABORT_ON(parse_events_add_tool(_parse_state, list, $1));
$$ = list;
}
@@ -338,10 +408,19 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_e
struct parse_events_state *parse_state = _parse_state;
struct parse_events_error *error = parse_state->error;
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_cache(list, &parse_state->idx, $1, $3, $5, error, $6));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_cache(list, &parse_state->idx, $1, $3, $5, error, $6);
parse_events_terms__delete($6);
+ free($1);
+ free($3);
+ free($5);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -350,10 +429,18 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config
struct parse_events_state *parse_state = _parse_state;
struct parse_events_error *error = parse_state->error;
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4);
parse_events_terms__delete($4);
+ free($1);
+ free($3);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -362,10 +449,17 @@ PE_NAME_CACHE_TYPE opt_event_config
struct parse_events_state *parse_state = _parse_state;
struct parse_events_error *error = parse_state->error;
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2);
parse_events_terms__delete($2);
+ free($1);
+ if (err) {
+ free_list_evsel(list);
+ YYABORT;
+ }
$$ = list;
}
@@ -374,10 +468,17 @@ PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc
{
struct parse_events_state *parse_state = _parse_state;
struct list_head *list;
-
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_breakpoint(list, &parse_state->idx,
- (void *) $2, $6, $4));
+ int err;
+
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_breakpoint(list, &parse_state->idx,
+ (void *) $2, $6, $4);
+ free($6);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -386,9 +487,13 @@ PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc
struct parse_events_state *parse_state = _parse_state;
struct list_head *list;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_breakpoint(list, &parse_state->idx,
- (void *) $2, NULL, $4));
+ list = alloc_list();
+ ABORT_ON(!list);
+ if (parse_events_add_breakpoint(list, &parse_state->idx,
+ (void *) $2, NULL, $4)) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -396,10 +501,17 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
{
struct parse_events_state *parse_state = _parse_state;
struct list_head *list;
-
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_breakpoint(list, &parse_state->idx,
- (void *) $2, $4, 0));
+ int err;
+
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_breakpoint(list, &parse_state->idx,
+ (void *) $2, $4, 0);
+ free($4);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
|
@@ -408,9 +520,13 @@ PE_PREFIX_MEM PE_VALUE sep_dc
struct parse_events_state *parse_state = _parse_state;
struct list_head *list;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_breakpoint(list, &parse_state->idx,
- (void *) $2, NULL, 0));
+ list = alloc_list();
+ ABORT_ON(!list);
+ if (parse_events_add_breakpoint(list, &parse_state->idx,
+ (void *) $2, NULL, 0)) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
@@ -420,28 +536,35 @@ tracepoint_name opt_event_config
struct parse_events_state *parse_state = _parse_state;
struct parse_events_error *error = parse_state->error;
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
+ list = alloc_list();
+ ABORT_ON(!list);
if (error)
error->idx = @1.first_column;
- if (parse_events_add_tracepoint(list, &parse_state->idx, $1.sys, $1.event,
- error, $2))
- return -1;
+ err = parse_events_add_tracepoint(list, &parse_state->idx, $1.sys, $1.event,
+ error, $2);
+ parse_events_terms__delete($2);
+ free($1.sys);
+ free($1.event);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
tracepoint_name:
PE_NAME '-' PE_NAME ':' PE_NAME
{
- char sys_name[128];
struct tracepoint_name tracepoint;
- snprintf(&sys_name, 128, "%s-%s", $1, $3);
- tracepoint.sys = &sys_name;
+ ABORT_ON(asprintf(&tracepoint.sys, "%s-%s", $1, $3) < 0);
tracepoint.event = $5;
-
+ free($1);
+ free($3);
$$ = tracepoint;
}
|
@@ -456,10 +579,16 @@ event_legacy_numeric:
PE_VALUE ':' PE_VALUE opt_event_config
{
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_numeric(_parse_state, list, (u32)$1, $3, $4));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_numeric(_parse_state, list, (u32)$1, $3, $4);
parse_events_terms__delete($4);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
@@ -467,10 +596,16 @@ event_legacy_raw:
PE_RAW opt_event_config
{
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, $1, $2));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, $1, $2);
parse_events_terms__delete($2);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
@@ -479,20 +614,33 @@ PE_BPF_OBJECT opt_event_config
{
struct parse_events_state *parse_state = _parse_state;
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(parse_state, list, $1, false, $2));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_load_bpf(parse_state, list, $1, false, $2);
parse_events_terms__delete($2);
+ free($1);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
|
PE_BPF_SOURCE opt_event_config
{
struct list_head *list;
+ int err;
- ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(_parse_state, list, $1, true, $2));
+ list = alloc_list();
+ ABORT_ON(!list);
+ err = parse_events_load_bpf(_parse_state, list, $1, true, $2);
parse_events_terms__delete($2);
+ if (err) {
+ free(list);
+ YYABORT;
+ }
$$ = list;
}
@@ -525,6 +673,10 @@ opt_pmu_config:
start_terms: event_config
{
struct parse_events_state *parse_state = _parse_state;
+ if (parse_state->terms) {
+ parse_events_terms__delete ($1);
+ YYABORT;
+ }
parse_state->terms = $1;
}
@@ -534,7 +686,10 @@ event_config ',' event_term
struct list_head *head = $1;
struct parse_events_term *term = $3;
- ABORT_ON(!head);
+ if (!head) {
+ parse_events_term__delete(term);
+ YYABORT;
+ }
list_add_tail(&term->list, head);
$$ = $1;
}
@@ -555,8 +710,12 @@ PE_NAME '=' PE_NAME
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $3, &@1, &@3));
+ if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $3, &@1, &@3)) {
+ free($1);
+ free($3);
+ YYABORT;
+ }
$$ = term;
}
|
@@ -564,8 +723,11 @@ PE_NAME '=' PE_VALUE
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $3, false, &@1, &@3));
+ if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $3, false, &@1, &@3)) {
+ free($1);
+ YYABORT;
+ }
$$ = term;
}
|
@@ -574,7 +736,10 @@ PE_NAME '=' PE_VALUE_SYM_HW
struct parse_events_term *term;
int config = $3 & 255;
- ABORT_ON(parse_events_term__sym_hw(&term, $1, config));
+ if (parse_events_term__sym_hw(&term, $1, config)) {
+ free($1);
+ YYABORT;
+ }
$$ = term;
}
|
@@ -582,8 +747,11 @@ PE_NAME
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, 1, true, &@1, NULL));
+ if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, 1, true, &@1, NULL)) {
+ free($1);
+ YYABORT;
+ }
$$ = term;
}
|
@@ -600,7 +768,10 @@ PE_TERM '=' PE_NAME
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3));
+ if (parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3)) {
+ free($3);
+ YYABORT;
+ }
$$ = term;
}
|
@@ -624,9 +795,13 @@ PE_NAME array '=' PE_NAME
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $4, &@1, &@4));
-
+ if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $4, &@1, &@4)) {
+ free($1);
+ free($4);
+ free($2.ranges);
+ YYABORT;
+ }
term->array = $2;
$$ = term;
}
@@ -635,8 +810,12 @@ PE_NAME array '=' PE_VALUE
{
struct parse_events_term *term;
- ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $4, false, &@1, &@4));
+ if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $4, false, &@1, &@4)) {
+ free($1);
+ free($2.ranges);
+ YYABORT;
+ }
term->array = $2;
$$ = term;
}
@@ -644,9 +823,15 @@ PE_NAME array '=' PE_VALUE
PE_DRV_CFG_TERM
{
struct parse_events_term *term;
+ char *config = strdup($1);
- ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG,
- $1, $1, &@1, NULL));
+ ABORT_ON(!config);
+ if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG,
+ config, $1, &@1, NULL)) {
+ free($1);
+ free(config);
+ YYABORT;
+ }
$$ = term;
}
@@ -668,14 +853,12 @@ array_terms ',' array_term
struct parse_events_array new_array;
new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges;
- new_array.ranges = malloc(sizeof(new_array.ranges[0]) *
- new_array.nr_ranges);
+ new_array.ranges = realloc($1.ranges,
+ sizeof(new_array.ranges[0]) *
+ new_array.nr_ranges);
ABORT_ON(!new_array.ranges);
- memcpy(&new_array.ranges[0], $1.ranges,
- $1.nr_ranges * sizeof(new_array.ranges[0]));
memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges,
$3.nr_ranges * sizeof(new_array.ranges[0]));
- free($1.ranges);
free($3.ranges);
$$ = new_array;
}
diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c
index ef46c2848808..e687497b3aac 100644
--- a/tools/perf/util/parse-regs-options.c
+++ b/tools/perf/util/parse-regs-options.c
@@ -13,7 +13,7 @@ static int
__parse_regs(const struct option *opt, const char *str, int unset, bool intr)
{
uint64_t *mode = (uint64_t *)opt->value;
- const struct sample_reg *r;
+ const struct sample_reg *r = NULL;
char *s, *os = NULL, *p;
int ret = -1;
uint64_t mask;
@@ -46,19 +46,23 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr)
if (!strcmp(s, "?")) {
fprintf(stderr, "available registers: ");
+#ifdef HAVE_PERF_REGS_SUPPORT
for (r = sample_reg_masks; r->name; r++) {
if (r->mask & mask)
fprintf(stderr, "%s ", r->name);
}
+#endif
fputc('\n', stderr);
/* just printing available regs */
return -1;
}
+#ifdef HAVE_PERF_REGS_SUPPORT
for (r = sample_reg_masks; r->name; r++) {
if ((r->mask & mask) && !strcasecmp(s, r->name))
break;
}
- if (!r->name) {
+#endif
+ if (!r || !r->name) {
ui__warning("Unknown register \"%s\", check man page or run \"perf record %s?\"\n",
s, intr ? "-I" : "--user-regs=");
goto error;
diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
index d4ad3f04923a..651203126c71 100644
--- a/tools/perf/util/perf_event_attr_fprintf.c
+++ b/tools/perf/util/perf_event_attr_fprintf.c
@@ -34,7 +34,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value)
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
bit_name(IDENTIFIER), bit_name(REGS_INTR), bit_name(DATA_SRC),
- bit_name(WEIGHT), bit_name(PHYS_ADDR),
+ bit_name(WEIGHT), bit_name(PHYS_ADDR), bit_name(AUX),
{ .name = NULL, }
};
#undef bit_name
@@ -143,6 +143,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(sample_regs_intr, p_hex);
PRINT_ATTRf(aux_watermark, p_unsigned);
PRINT_ATTRf(sample_max_stack, p_unsigned);
+ PRINT_ATTRf(aux_sample_size, p_unsigned);
return ret;
}
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index 2774cec1f15f..5ee47ae1509c 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -3,10 +3,6 @@
#include "perf_regs.h"
#include "event.h"
-const struct sample_reg __weak sample_reg_masks[] = {
- SMPL_REG_END
-};
-
int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
char **new_op __maybe_unused)
{
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index 47fe34e5f7d5..e014c2c038f4 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -15,8 +15,6 @@ struct sample_reg {
#define SMPL_REG2(n, b) { .name = #n, .mask = 3ULL << (b) }
#define SMPL_REG_END { .name = NULL }
-extern const struct sample_reg sample_reg_masks[];
-
enum {
SDT_ARG_VALID = 0,
SDT_ARG_SKIP,
@@ -27,6 +25,8 @@ uint64_t arch__intr_reg_mask(void);
uint64_t arch__user_reg_mask(void);
#ifdef HAVE_PERF_REGS_SUPPORT
+extern const struct sample_reg sample_reg_masks[];
+
#include <perf_regs.h>
#define DWARF_MINIMAL_REGS ((1ULL << PERF_REG_IP) | (1ULL << PERF_REG_SP))
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 5608da82ad23..e8d348988026 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -308,7 +308,8 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
char *long_desc, char *topic,
char *unit, char *perpkg,
char *metric_expr,
- char *metric_name)
+ char *metric_name,
+ char *deprecated)
{
struct parse_events_term *term;
struct perf_pmu_alias *alias;
@@ -325,6 +326,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
alias->unit[0] = '\0';
alias->per_pkg = false;
alias->snapshot = false;
+ alias->deprecated = false;
ret = parse_events_terms(&alias->terms, val);
if (ret) {
@@ -379,6 +381,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1;
alias->str = strdup(newval);
+ if (deprecated)
+ alias->deprecated = true;
+
if (!perf_pmu_merge_alias(alias, list))
list_add_tail(&alias->list, list);
@@ -400,7 +405,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
strim(buf);
return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
}
static inline bool pmu_alias_info_file(char *name)
@@ -787,7 +792,8 @@ new_alias:
(char *)pe->long_desc, (char *)pe->topic,
(char *)pe->unit, (char *)pe->perpkg,
(char *)pe->metric_expr,
- (char *)pe->metric_name);
+ (char *)pe->metric_name,
+ (char *)pe->deprecated);
}
}
@@ -925,6 +931,16 @@ __u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
return bits;
}
+int perf_pmu__format_type(struct list_head *formats, const char *name)
+{
+ struct perf_pmu_format *format = pmu_find_format(formats, name);
+
+ if (!format)
+ return -1;
+
+ return format->value;
+}
+
/*
* Sets value based on the format definition (format parameter)
* and unformated value (value parameter).
@@ -1044,9 +1060,9 @@ static int pmu_config_term(struct list_head *formats,
if (err) {
char *pmu_term = pmu_formats_string(formats);
- err->idx = term->err_term;
- err->str = strdup("unknown term");
- err->help = parse_events_formats_error_string(pmu_term);
+ parse_events__handle_error(err, term->err_term,
+ strdup("unknown term"),
+ parse_events_formats_error_string(pmu_term));
free(pmu_term);
}
return -EINVAL;
@@ -1074,8 +1090,9 @@ static int pmu_config_term(struct list_head *formats,
if (term->no_value &&
bitmap_weight(format->bits, PERF_PMU_FORMAT_BITS) > 1) {
if (err) {
- err->idx = term->err_val;
- err->str = strdup("no value assigned for term");
+ parse_events__handle_error(err, term->err_val,
+ strdup("no value assigned for term"),
+ NULL);
}
return -EINVAL;
}
@@ -1088,8 +1105,9 @@ static int pmu_config_term(struct list_head *formats,
term->config, term->val.str);
}
if (err) {
- err->idx = term->err_val;
- err->str = strdup("expected numeric value");
+ parse_events__handle_error(err, term->err_val,
+ strdup("expected numeric value"),
+ NULL);
}
return -EINVAL;
}
@@ -1102,11 +1120,15 @@ static int pmu_config_term(struct list_head *formats,
max_val = pmu_format_max_value(format->bits);
if (val > max_val) {
if (err) {
- err->idx = term->err_val;
- if (asprintf(&err->str,
- "value too big for format, maximum is %llu",
- (unsigned long long)max_val) < 0)
- err->str = strdup("value too big for format");
+ char *err_str;
+
+ parse_events__handle_error(err, term->err_val,
+ asprintf(&err_str,
+ "value too big for format, maximum is %llu",
+ (unsigned long long)max_val) < 0
+ ? strdup("value too big for format")
+ : err_str,
+ NULL);
return -EINVAL;
}
/*
@@ -1248,7 +1270,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
info->metric_name = alias->metric_name;
list_del_init(&term->list);
- free(term);
+ parse_events_term__delete(term);
}
/*
@@ -1383,7 +1405,7 @@ static void wordwrap(char *s, int start, int max, int corr)
}
void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
- bool long_desc, bool details_flag)
+ bool long_desc, bool details_flag, bool deprecated)
{
struct perf_pmu *pmu;
struct perf_pmu_alias *alias;
@@ -1414,6 +1436,9 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu");
+ if (alias->deprecated && !deprecated)
+ continue;
+
if (event_glob != NULL &&
!(strglobmatch_nocase(name, event_glob) ||
(!is_cpu && strglobmatch_nocase(alias->name,
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index f36ade6df76d..6737e3d5d568 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -26,6 +26,7 @@ struct perf_pmu {
__u32 type;
bool selectable;
bool is_uncore;
+ bool auxtrace;
int max_precise;
struct perf_event_attr *default_config;
struct perf_cpu_map *cpus;
@@ -57,6 +58,7 @@ struct perf_pmu_alias {
double scale;
bool per_pkg;
bool snapshot;
+ bool deprecated;
char *metric_expr;
char *metric_name;
};
@@ -70,6 +72,7 @@ int perf_pmu__config_terms(struct list_head *formats,
struct list_head *head_terms,
bool zero, struct parse_events_error *error);
__u64 perf_pmu__format_bits(struct list_head *formats, const char *name);
+int perf_pmu__format_type(struct list_head *formats, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
struct perf_pmu_info *info);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
@@ -85,7 +88,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
- bool long_desc, bool details_flag);
+ bool long_desc, bool details_flag,
+ bool deprecated);
bool pmu_have_event(const char *pname, const char *name);
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 91cab5f669d2..52b2d165453a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -46,7 +46,7 @@
#define PERFPROBE_GROUP "probe"
bool probe_event_dry_run; /* Dry run flag */
-struct probe_conf probe_conf;
+struct probe_conf probe_conf = { .magic_num = DEFAULT_PROBE_MAGIC_NUM };
#define semantic_error(msg ...) pr_err("Semantic error :" msg)
@@ -153,7 +153,7 @@ static struct map *kernel_get_module_map(const char *module)
return map__get(pos);
}
- for (pos = maps__first(maps); pos; pos = map__next(pos)) {
+ maps__for_each_entry(maps, pos) {
/* short_name is "[module]" */
if (strncmp(pos->dso->short_name + 1, module,
pos->dso->short_name_len - 2) == 0 &&
@@ -1679,6 +1679,14 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
if (ret < 0)
goto out;
+ /* Generate event name if needed */
+ if (!pev->event && pev->point.function && pev->point.line
+ && !pev->point.lazy_line && !pev->point.offset) {
+ if (asprintf(&pev->event, "%s_L%d", pev->point.function,
+ pev->point.line) < 0)
+ return -ENOMEM;
+ }
+
/* Copy arguments and ensure return probe has no C argument */
pev->nargs = argc - 1;
pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
@@ -2730,8 +2738,13 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
if (tev->event == NULL || tev->group == NULL)
return -ENOMEM;
- /* Add added event name to namelist */
- strlist__add(namelist, event);
+ /*
+ * Add new event name to namelist if multiprobe event is NOT
+ * supported, since we have to use new event name for following
+ * probes in that case.
+ */
+ if (!multiprobe_event_is_supported())
+ strlist__add(namelist, event);
return 0;
}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 96a319cd2378..4f0eb3a20c36 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -16,10 +16,13 @@ struct probe_conf {
bool no_inlines;
bool cache;
int max_probes;
+ unsigned long magic_num;
};
extern struct probe_conf probe_conf;
extern bool probe_event_dry_run;
+#define DEFAULT_PROBE_MAGIC_NUM 0xdeade12d /* u32: 3735937325 */
+
struct symbol;
/* kprobe-tracer and uprobe-tracer tracing point */
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index b659466ea498..5003ba403345 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -1007,6 +1007,8 @@ enum ftrace_readme {
FTRACE_README_KRETPROBE_OFFSET,
FTRACE_README_UPROBE_REF_CTR,
FTRACE_README_USER_ACCESS,
+ FTRACE_README_MULTIPROBE_EVENT,
+ FTRACE_README_IMMEDIATE_VALUE,
FTRACE_README_END,
};
@@ -1020,6 +1022,8 @@ static struct {
DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"),
DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"),
DEFINE_TYPE(FTRACE_README_USER_ACCESS, "*[u]<offset>*"),
+ DEFINE_TYPE(FTRACE_README_MULTIPROBE_EVENT, "*Create/append/*"),
+ DEFINE_TYPE(FTRACE_README_IMMEDIATE_VALUE, "*\\imm-value,*"),
};
static bool scan_ftrace_readme(enum ftrace_readme type)
@@ -1085,3 +1089,13 @@ bool user_access_is_supported(void)
{
return scan_ftrace_readme(FTRACE_README_USER_ACCESS);
}
+
+bool multiprobe_event_is_supported(void)
+{
+ return scan_ftrace_readme(FTRACE_README_MULTIPROBE_EVENT);
+}
+
+bool immediate_value_is_supported(void)
+{
+ return scan_ftrace_readme(FTRACE_README_IMMEDIATE_VALUE);
+}
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index 986c1c94f64f..0dba88c0f5f0 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -71,6 +71,8 @@ bool probe_type_is_available(enum probe_type type);
bool kretprobe_offset_is_supported(void);
bool uprobe_ref_ctr_is_supported(void);
bool user_access_is_supported(void);
+bool multiprobe_event_is_supported(void);
+bool immediate_value_is_supported(void);
#else /* ! HAVE_LIBELF_SUPPORT */
static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
{
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index cd9f95e5044e..c470c49a804f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -177,6 +177,17 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
goto static_var;
+ /* Constant value */
+ if (dwarf_attr(vr_die, DW_AT_const_value, &attr) &&
+ immediate_value_is_supported()) {
+ Dwarf_Sword snum;
+
+ dwarf_formsdata(&attr, &snum);
+ ret = asprintf(&tvar->value, "\\%ld", (long)snum);
+
+ return ret < 0 ? -ENOMEM : 0;
+ }
+
/* TODO: handle more than 1 exprs */
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
return -EINVAL; /* Broken DIE ? */
@@ -525,6 +536,14 @@ next:
return 0;
}
+static void print_var_not_found(const char *varname)
+{
+ pr_err("Failed to find the location of the '%s' variable at this address.\n"
+ " Perhaps it has been optimized out.\n"
+ " Use -V with the --range option to show '%s' location range.\n",
+ varname, varname);
+}
+
/* Show a variables in kprobe event format */
static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
@@ -536,11 +555,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
&pf->sp_die, pf->machine, pf->tvar);
+ if (ret == -ENOENT && pf->skip_empty_arg)
+ /* This can be found in other place. skip it */
+ return 0;
if (ret == -ENOENT || ret == -EINVAL) {
- pr_err("Failed to find the location of the '%s' variable at this address.\n"
- " Perhaps it has been optimized out.\n"
- " Use -V with the --range option to show '%s' location range.\n",
- pf->pvar->var, pf->pvar->var);
+ print_var_not_found(pf->pvar->var);
} else if (ret == -ENOTSUP)
pr_err("Sorry, we don't support this variable location yet.\n");
else if (ret == 0 && pf->pvar->field) {
@@ -587,6 +606,8 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
/* Search again in global variables */
if (!die_find_variable_at(&pf->cu_die, pf->pvar->var,
0, &vr_die)) {
+ if (pf->skip_empty_arg)
+ return 0;
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
ret = -ENOENT;
@@ -604,38 +625,26 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
const char *function,
struct probe_trace_point *tp)
{
- Dwarf_Addr eaddr, highaddr;
+ Dwarf_Addr eaddr;
GElf_Sym sym;
const char *symbol;
/* Verify the address is correct */
- if (dwarf_entrypc(sp_die, &eaddr) != 0) {
- pr_warning("Failed to get entry address of %s\n",
- dwarf_diename(sp_die));
- return -ENOENT;
- }
- if (dwarf_highpc(sp_die, &highaddr) != 0) {
- pr_warning("Failed to get end address of %s\n",
- dwarf_diename(sp_die));
- return -ENOENT;
- }
- if (paddr > highaddr) {
- pr_warning("Offset specified is greater than size of %s\n",
+ if (!dwarf_haspc(sp_die, paddr)) {
+ pr_warning("Specified offset is out of %s\n",
dwarf_diename(sp_die));
return -EINVAL;
}
- symbol = dwarf_diename(sp_die);
+ /* Try to get actual symbol name from symtab */
+ symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
if (!symbol) {
- /* Try to get the symbol name from symtab */
- symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
- if (!symbol) {
- pr_warning("Failed to find symbol at 0x%lx\n",
- (unsigned long)paddr);
- return -ENOENT;
- }
- eaddr = sym.st_value;
+ pr_warning("Failed to find symbol at 0x%lx\n",
+ (unsigned long)paddr);
+ return -ENOENT;
}
+ eaddr = sym.st_value;
+
tp->offset = (unsigned long)(paddr - eaddr);
tp->address = (unsigned long)paddr;
tp->symbol = strdup(symbol);
@@ -756,6 +765,16 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
return 0;
}
+/* Return innermost DIE */
+static int find_inner_scope_cb(Dwarf_Die *fn_die, void *data)
+{
+ struct find_scope_param *fsp = data;
+
+ memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
+ fsp->found = true;
+ return 1;
+}
+
/* Find an appropriate scope fits to given conditions */
static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
{
@@ -767,12 +786,50 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
.die_mem = die_mem,
.found = false,
};
+ int ret;
- cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp);
+ ret = cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb,
+ &fsp);
+ if (!ret && !fsp.found)
+ cu_walk_functions_at(&pf->cu_die, pf->addr,
+ find_inner_scope_cb, &fsp);
return fsp.found ? die_mem : NULL;
}
+static int verify_representive_line(struct probe_finder *pf, const char *fname,
+ int lineno, Dwarf_Addr addr)
+{
+ const char *__fname, *__func = NULL;
+ Dwarf_Die die_mem;
+ int __lineno;
+
+ /* Verify line number and address by reverse search */
+ if (cu_find_lineinfo(&pf->cu_die, addr, &__fname, &__lineno) < 0)
+ return 0;
+
+ pr_debug2("Reversed line: %s:%d\n", __fname, __lineno);
+ if (strcmp(fname, __fname) || lineno == __lineno)
+ return 0;
+
+ pr_warning("This line is sharing the address with other lines.\n");
+
+ if (pf->pev->point.function) {
+ /* Find best match function name and lines */
+ pf->addr = addr;
+ if (find_best_scope(pf, &die_mem)
+ && die_match_name(&die_mem, pf->pev->point.function)
+ && dwarf_decl_line(&die_mem, &lineno) == 0) {
+ __func = dwarf_diename(&die_mem);
+ __lineno -= lineno;
+ }
+ }
+ pr_warning("Please try to probe at %s:%d instead.\n",
+ __func ? : __fname, __lineno);
+
+ return -ENOENT;
+}
+
static int probe_point_line_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
@@ -783,6 +840,9 @@ static int probe_point_line_walker(const char *fname, int lineno,
if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
return 0;
+ if (verify_representive_line(pf, fname, lineno, addr))
+ return -ENOENT;
+
pf->addr = addr;
sc_die = find_best_scope(pf, &die_mem);
if (!sc_die) {
@@ -942,7 +1002,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
ret = find_probe_point_lazy(in_die, pf);
else {
/* Get probe address */
- if (dwarf_entrypc(in_die, &addr) != 0) {
+ if (die_entrypc(in_die, &addr) != 0) {
pr_warning("Failed to get entry address of %s.\n",
dwarf_diename(in_die));
return -ENOENT;
@@ -994,7 +1054,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
param->retval = find_probe_point_by_line(pf);
} else if (die_is_func_instance(sp_die)) {
/* Instances always have the entry address */
- dwarf_entrypc(sp_die, &pf->addr);
+ die_entrypc(sp_die, &pf->addr);
/* But in some case the entry address is 0 */
if (pf->addr == 0) {
pr_debug("%s has no entry PC. Skipped\n",
@@ -1334,6 +1394,44 @@ end:
return ret;
}
+static int fill_empty_trace_arg(struct perf_probe_event *pev,
+ struct probe_trace_event *tevs, int ntevs)
+{
+ char **valp;
+ char *type;
+ int i, j, ret;
+
+ for (i = 0; i < pev->nargs; i++) {
+ type = NULL;
+ for (j = 0; j < ntevs; j++) {
+ if (tevs[j].args[i].value) {
+ type = tevs[j].args[i].type;
+ break;
+ }
+ }
+ if (j == ntevs) {
+ print_var_not_found(pev->args[i].var);
+ return -ENOENT;
+ }
+ for (j = 0; j < ntevs; j++) {
+ valp = &tevs[j].args[i].value;
+ if (*valp)
+ continue;
+
+ ret = asprintf(valp, "\\%lx", probe_conf.magic_num);
+ if (ret < 0)
+ return -ENOMEM;
+ /* Note that type can be NULL */
+ if (type) {
+ tevs[j].args[i].type = strdup(type);
+ if (!tevs[j].args[i].type)
+ return -ENOMEM;
+ }
+ }
+ }
+ return 0;
+}
+
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
int debuginfo__find_trace_events(struct debuginfo *dbg,
struct perf_probe_event *pev,
@@ -1352,7 +1450,13 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
tf.tevs = *tevs;
tf.ntevs = 0;
+ if (pev->nargs != 0 && immediate_value_is_supported())
+ tf.pf.skip_empty_arg = true;
+
ret = debuginfo__find_probes(dbg, &tf.pf);
+ if (ret >= 0 && tf.pf.skip_empty_arg)
+ ret = fill_empty_trace_arg(pev, tf.tevs, tf.ntevs);
+
if (ret < 0) {
for (i = 0; i < tf.ntevs; i++)
clear_probe_trace_event(&tf.tevs[i]);
@@ -1425,6 +1529,18 @@ error:
return DIE_FIND_CB_END;
}
+static bool available_var_finder_overlap(struct available_var_finder *af)
+{
+ int i;
+
+ for (i = 0; i < af->nvls; i++) {
+ if (af->pf.addr == af->vls[i].point.address)
+ return true;
+ }
+ return false;
+
+}
+
/* Add a found vars into available variables list */
static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
{
@@ -1435,6 +1551,14 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
Dwarf_Die die_mem;
int ret;
+ /*
+ * For some reason (e.g. different column assigned to same address),
+ * this callback can be called with the address which already passed.
+ * Ignore it first.
+ */
+ if (available_var_finder_overlap(af))
+ return 0;
+
/* Check number of tevs */
if (af->nvls == af->max_vls) {
pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
@@ -1578,7 +1702,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
/* Get function entry information */
func = basefunc = dwarf_diename(&spdie);
if (!func ||
- dwarf_entrypc(&spdie, &baseaddr) != 0 ||
+ die_entrypc(&spdie, &baseaddr) != 0 ||
dwarf_decl_line(&spdie, &baseline) != 0) {
lineno = 0;
goto post;
@@ -1595,7 +1719,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr,
&indie)) {
/* There is an inline function */
- if (dwarf_entrypc(&indie, &_addr) == 0 &&
+ if (die_entrypc(&indie, &_addr) == 0 &&
_addr == addr) {
/*
* addr is at an inline function entry.
@@ -1675,12 +1799,19 @@ static int line_range_walk_cb(const char *fname, int lineno,
void *data)
{
struct line_finder *lf = data;
+ const char *__fname;
+ int __lineno;
int err;
if ((strtailcmp(fname, lf->fname) != 0) ||
(lf->lno_s > lineno || lf->lno_e < lineno))
return 0;
+ /* Make sure this line can be reversable */
+ if (cu_find_lineinfo(&lf->cu_die, addr, &__fname, &__lineno) > 0
+ && (lineno != __lineno || strcmp(fname, __fname)))
+ return 0;
+
err = line_range_add_line(fname, lineno, lf->lr);
if (err < 0 && err != -EEXIST)
return err;
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 670c477bf8cf..11be10080613 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -87,6 +87,7 @@ struct probe_finder {
unsigned int machine; /* Target machine arch */
struct perf_probe_arg *pvar; /* Current target variable */
struct probe_trace_arg *tvar; /* Current result variable */
+ bool skip_empty_arg; /* Skip non-exist args */
};
struct trace_event_finder {
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 53f31053a27a..83212c65848b 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -6,6 +6,7 @@
#include <linux/err.h>
#include <perf/cpumap.h>
#include <traceevent/event-parse.h>
+#include <perf/mmap.h>
#include "evlist.h"
#include "callchain.h"
#include "evsel.h"
@@ -14,6 +15,7 @@
#include "thread_map.h"
#include "trace-event.h"
#include "mmap.h"
+#include "util/env.h"
#include <internal/lib.h>
#include "../perf-sys.h"
@@ -54,10 +56,16 @@ int parse_callchain_record(const char *arg __maybe_unused,
}
/*
+ * Add this one here not to drag util/env.c
+ */
+struct perf_env perf_env;
+
+/*
* Support debug printing even though util/debug.c is not linked. That means
* implementing 'verbose' and 'eprintf'.
*/
int verbose;
+int debug_peo_args;
int eprintf(int level, int var, const char *fmt, ...);
@@ -1016,10 +1024,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
if (!md)
return NULL;
- if (perf_mmap__read_init(md) < 0)
+ if (perf_mmap__read_init(&md->core) < 0)
goto end;
- event = perf_mmap__read_event(md);
+ event = perf_mmap__read_event(&md->core);
if (event != NULL) {
PyObject *pyevent = pyrf_event__new(event);
struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
@@ -1039,7 +1047,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
err = perf_evsel__parse_sample(evsel, event, &pevent->sample);
/* Consume the even only after we parsed it out. */
- perf_mmap__consume(md);
+ perf_mmap__consume(&md->core);
if (err)
return PyErr_Format(PyExc_OSError,
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index 8579505c29a4..7def66168503 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -136,6 +136,37 @@ bool perf_can_record_cpu_wide(void)
return true;
}
+/*
+ * Architectures are expected to know if AUX area sampling is supported by the
+ * hardware. Here we check for kernel support.
+ */
+bool perf_can_aux_sample(void)
+{
+ struct perf_event_attr attr = {
+ .size = sizeof(struct perf_event_attr),
+ .exclude_kernel = 1,
+ /*
+ * Non-zero value causes the kernel to calculate the effective
+ * attribute size up to that byte.
+ */
+ .aux_sample_size = 1,
+ };
+ int fd;
+
+ fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
+ /*
+ * If the kernel attribute is big enough to contain aux_sample_size
+ * then we assume that it is supported. We are relying on the kernel to
+ * validate the attribute size before anything else that could be wrong.
+ */
+ if (fd < 0 && errno == E2BIG)
+ return false;
+ if (fd >= 0)
+ close(fd);
+
+ return true;
+}
+
void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain)
{
diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h
index 00275afc524d..5421fd2ad383 100644
--- a/tools/perf/util/record.h
+++ b/tools/perf/util/record.h
@@ -32,6 +32,7 @@ struct record_opts {
bool full_auxtrace;
bool auxtrace_snapshot_mode;
bool auxtrace_snapshot_on_exit;
+ bool auxtrace_sample_mode;
bool record_namespaces;
bool record_switch_events;
bool all_kernel;
@@ -44,6 +45,7 @@ struct record_opts {
bool strict_freq;
bool sample_id;
bool no_bpf_event;
+ bool kcore;
unsigned int freq;
unsigned int mmap_pages;
unsigned int auxtrace_mmap_pages;
@@ -55,6 +57,7 @@ struct record_opts {
u64 user_interval;
size_t auxtrace_snapshot_size;
const char *auxtrace_snapshot_opts;
+ const char *auxtrace_sample_opts;
bool sample_transaction;
unsigned initial_delay;
bool use_clockid;
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 15961854ba67..0e608a5ef599 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -294,17 +294,17 @@ static SV *perl_process_callchain(struct perf_sample *sample,
goto exit;
}
- if (node->sym) {
+ if (node->ms.sym) {
HV *sym = newHV();
if (!sym) {
hv_undef(elem);
goto exit;
}
- if (!hv_stores(sym, "start", newSVuv(node->sym->start)) ||
- !hv_stores(sym, "end", newSVuv(node->sym->end)) ||
- !hv_stores(sym, "binding", newSVuv(node->sym->binding)) ||
- !hv_stores(sym, "name", newSVpvn(node->sym->name,
- node->sym->namelen)) ||
+ if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) ||
+ !hv_stores(sym, "end", newSVuv(node->ms.sym->end)) ||
+ !hv_stores(sym, "binding", newSVuv(node->ms.sym->binding)) ||
+ !hv_stores(sym, "name", newSVpvn(node->ms.sym->name,
+ node->ms.sym->namelen)) ||
!hv_stores(elem, "sym", newRV_noinc((SV*)sym))) {
hv_undef(sym);
hv_undef(elem);
@@ -312,8 +312,8 @@ static SV *perl_process_callchain(struct perf_sample *sample,
}
}
- if (node->map) {
- struct map *map = node->map;
+ if (node->ms.map) {
+ struct map *map = node->ms.map;
const char *dsoname = "[unknown]";
if (map && map->dso) {
if (symbol_conf.show_kernel_path && map->dso->long_name)
@@ -539,10 +539,11 @@ static int perl_stop_script(void)
static int perl_generate_script(struct tep_handle *pevent, const char *outfile)
{
+ int i, not_first, count, nr_events;
+ struct tep_event **all_events;
struct tep_event *event = NULL;
struct tep_format_field *f;
char fname[PATH_MAX];
- int not_first, count;
FILE *ofp;
sprintf(fname, "%s.pl", outfile);
@@ -603,8 +604,11 @@ sub print_backtrace\n\
}\n\n\
");
+ nr_events = tep_get_events_count(pevent);
+ all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID);
- while ((event = trace_find_next_event(pevent, event))) {
+ for (i = 0; all_events && i < nr_events; i++) {
+ event = all_events[i];
fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
fprintf(ofp, "\tmy (");
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 5d341efc3237..9581a904af29 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -428,24 +428,24 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
pydict_set_item_string_decref(pyelem, "ip",
PyLong_FromUnsignedLongLong(node->ip));
- if (node->sym) {
+ if (node->ms.sym) {
PyObject *pysym = PyDict_New();
if (!pysym)
Py_FatalError("couldn't create Python dictionary");
pydict_set_item_string_decref(pysym, "start",
- PyLong_FromUnsignedLongLong(node->sym->start));
+ PyLong_FromUnsignedLongLong(node->ms.sym->start));
pydict_set_item_string_decref(pysym, "end",
- PyLong_FromUnsignedLongLong(node->sym->end));
+ PyLong_FromUnsignedLongLong(node->ms.sym->end));
pydict_set_item_string_decref(pysym, "binding",
- _PyLong_FromLong(node->sym->binding));
+ _PyLong_FromLong(node->ms.sym->binding));
pydict_set_item_string_decref(pysym, "name",
- _PyUnicode_FromStringAndSize(node->sym->name,
- node->sym->namelen));
+ _PyUnicode_FromStringAndSize(node->ms.sym->name,
+ node->ms.sym->namelen));
pydict_set_item_string_decref(pyelem, "sym", pysym);
}
- if (node->map) {
- const char *dsoname = get_dsoname(node->map);
+ if (node->ms.map) {
+ const char *dsoname = get_dsoname(node->ms.map);
pydict_set_item_string_decref(pyelem, "dso",
_PyUnicode_FromString(dsoname));
@@ -1127,7 +1127,7 @@ static void python_export_sample_table(struct db_export *dbe,
tuple_set_u64(t, 0, es->db_id);
tuple_set_u64(t, 1, es->evsel->db_id);
- tuple_set_u64(t, 2, es->al->machine->db_id);
+ tuple_set_u64(t, 2, es->al->mg->machine->db_id);
tuple_set_u64(t, 3, es->al->thread->db_id);
tuple_set_u64(t, 4, es->comm_db_id);
tuple_set_u64(t, 5, es->dso_db_id);
@@ -1687,10 +1687,11 @@ static int python_stop_script(void)
static int python_generate_script(struct tep_handle *pevent, const char *outfile)
{
+ int i, not_first, count, nr_events;
+ struct tep_event **all_events;
struct tep_event *event = NULL;
struct tep_format_field *f;
char fname[PATH_MAX];
- int not_first, count;
FILE *ofp;
sprintf(fname, "%s.py", outfile);
@@ -1735,7 +1736,11 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile
fprintf(ofp, "def trace_end():\n");
fprintf(ofp, "\tprint(\"in trace_end\")\n\n");
- while ((event = trace_find_next_event(pevent, event))) {
+ nr_events = tep_get_events_count(pevent);
+ all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID);
+
+ for (i = 0; all_events && i < nr_events; i++) {
+ event = all_events[i];
fprintf(ofp, "def %s__%s(", event->system, event->name);
fprintf(ofp, "event_name, ");
fprintf(ofp, "context, ");
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 061bb4d6a3f5..d0d7d25b23e3 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -227,9 +227,13 @@ struct perf_session *perf_session__new(struct perf_data *data,
/* Open the directory data. */
if (data->is_dir) {
ret = perf_data__open_dir(data);
- if (ret)
- goto out_delete;
+ if (ret)
+ goto out_delete;
}
+
+ if (!symbol_conf.kallsyms_name &&
+ !symbol_conf.vmlinux_name)
+ symbol_conf.kallsyms_name = perf_data__kallsyms_name(data);
}
} else {
session->machines.host.env = &perf_env;
@@ -748,6 +752,7 @@ do { \
bswap_field_32(sample_stack_user);
bswap_field_32(aux_watermark);
bswap_field_16(sample_max_stack);
+ bswap_field_32(aux_sample_size);
/*
* After read_format are bitfields. Check read_format because
@@ -1491,8 +1496,13 @@ static int perf_session__deliver_event(struct perf_session *session,
if (ret > 0)
return 0;
- return machines__deliver_event(&session->machines, session->evlist,
- event, &sample, tool, file_offset);
+ ret = machines__deliver_event(&session->machines, session->evlist,
+ event, &sample, tool, file_offset);
+
+ if (dump_trace && sample.aux_sample.size)
+ auxtrace__dump_auxtrace_sample(session, &sample);
+
+ return ret;
}
static s64 perf_session__process_user_event(struct perf_session *session,
@@ -1649,6 +1659,34 @@ out_parse_sample:
return 0;
}
+int perf_session__peek_events(struct perf_session *session, u64 offset,
+ u64 size, peek_events_cb_t cb, void *data)
+{
+ u64 max_offset = offset + size;
+ char buf[PERF_SAMPLE_MAX_SIZE];
+ union perf_event *event;
+ int err;
+
+ do {
+ err = perf_session__peek_event(session, offset, buf,
+ PERF_SAMPLE_MAX_SIZE, &event,
+ NULL);
+ if (err)
+ return err;
+
+ err = cb(session, event, offset, data);
+ if (err)
+ return err;
+
+ offset += event->header.size;
+ if (event->header.type == PERF_RECORD_AUXTRACE)
+ offset += event->auxtrace.size;
+
+ } while (offset < max_offset);
+
+ return err;
+}
+
static s64 perf_session__process_event(struct perf_session *session,
union perf_event *event, u64 file_offset)
{
@@ -1954,8 +1992,8 @@ out_err:
}
static union perf_event *
-fetch_mmaped_event(struct perf_session *session,
- u64 head, size_t mmap_size, char *buf)
+prefetch_event(char *buf, u64 head, size_t mmap_size,
+ bool needs_swap, union perf_event *error)
{
union perf_event *event;
@@ -1967,20 +2005,32 @@ fetch_mmaped_event(struct perf_session *session,
return NULL;
event = (union perf_event *)(buf + head);
+ if (needs_swap)
+ perf_event_header__bswap(&event->header);
- if (session->header.needs_swap)
+ if (head + event->header.size <= mmap_size)
+ return event;
+
+ /* We're not fetching the event so swap back again */
+ if (needs_swap)
perf_event_header__bswap(&event->header);
- if (head + event->header.size > mmap_size) {
- /* We're not fetching the event so swap back again */
- if (session->header.needs_swap)
- perf_event_header__bswap(&event->header);
- pr_debug("%s: head=%#" PRIx64 " event->header_size=%#x, mmap_size=%#zx: fuzzed perf.data?\n",
- __func__, head, event->header.size, mmap_size);
- return ERR_PTR(-EINVAL);
- }
+ pr_debug("%s: head=%#" PRIx64 " event->header_size=%#x, mmap_size=%#zx:"
+ " fuzzed or compressed perf.data?\n",__func__, head, event->header.size, mmap_size);
+
+ return error;
+}
+
+static union perf_event *
+fetch_mmaped_event(u64 head, size_t mmap_size, char *buf, bool needs_swap)
+{
+ return prefetch_event(buf, head, mmap_size, needs_swap, ERR_PTR(-EINVAL));
+}
- return event;
+static union perf_event *
+fetch_decomp_event(u64 head, size_t mmap_size, char *buf, bool needs_swap)
+{
+ return prefetch_event(buf, head, mmap_size, needs_swap, NULL);
}
static int __perf_session__process_decomp_events(struct perf_session *session)
@@ -1993,10 +2043,8 @@ static int __perf_session__process_decomp_events(struct perf_session *session)
return 0;
while (decomp->head < decomp->size && !session_done()) {
- union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data);
-
- if (IS_ERR(event))
- return PTR_ERR(event);
+ union perf_event *event = fetch_decomp_event(decomp->head, decomp->size, decomp->data,
+ session->header.needs_swap);
if (!event)
break;
@@ -2096,7 +2144,7 @@ remap:
}
more:
- event = fetch_mmaped_event(session, head, mmap_size, buf);
+ event = fetch_mmaped_event(head, mmap_size, buf, session->header.needs_swap);
if (IS_ERR(event))
return PTR_ERR(event);
@@ -2355,35 +2403,6 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
fprintf(fp, "# ========\n#\n");
}
-
-int __perf_session__set_tracepoints_handlers(struct perf_session *session,
- const struct evsel_str_handler *assocs,
- size_t nr_assocs)
-{
- struct evsel *evsel;
- size_t i;
- int err;
-
- for (i = 0; i < nr_assocs; i++) {
- /*
- * Adding a handler for an event not in the session,
- * just ignore it.
- */
- evsel = perf_evlist__find_tracepoint_by_name(session->evlist, assocs[i].name);
- if (evsel == NULL)
- continue;
-
- err = -EEXIST;
- if (evsel->handler != NULL)
- goto out;
- evsel->handler = assocs[i].handler;
- }
-
- err = 0;
-out:
- return err;
-}
-
int perf_event__process_id_index(struct perf_session *session,
union perf_event *event)
{
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index b4c9428c18f0..f76480166d38 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -64,6 +64,11 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset,
void *buf, size_t buf_sz,
union perf_event **event_ptr,
struct perf_sample *sample);
+typedef int (*peek_events_cb_t)(struct perf_session *session,
+ union perf_event *event, u64 offset,
+ void *data);
+int perf_session__peek_events(struct perf_session *session, u64 offset,
+ u64 size, peek_events_cb_t cb, void *data);
int perf_session__process_events(struct perf_session *session);
@@ -120,12 +125,8 @@ void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full);
struct evsel_str_handler;
-int __perf_session__set_tracepoints_handlers(struct perf_session *session,
- const struct evsel_str_handler *assocs,
- size_t nr_assocs);
-
#define perf_session__set_tracepoints_handlers(session, array) \
- __perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array))
+ __evlist__set_tracepoints_handlers(session->evlist, array, ARRAY_SIZE(array))
extern volatile int session_done;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 43d1d410854a..345b5ccc90f6 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -287,10 +287,12 @@ sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
return strcmp(right->ms.sym->name, left->ms.sym->name);
}
-static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
+static int _hist_entry__sym_snprintf(struct map_symbol *ms,
u64 ip, char level, char *bf, size_t size,
unsigned int width)
{
+ struct symbol *sym = ms->sym;
+ struct map *map = ms->map;
size_t ret = 0;
if (verbose > 0) {
@@ -325,7 +327,7 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
+ return _hist_entry__sym_snprintf(&he->ms, he->ip,
he->level, bf, size, width);
}
@@ -386,7 +388,7 @@ struct sort_entry sort_srcline = {
static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
{
- return map__srcline(ams->map, ams->al_addr, ams->sym);
+ return map__srcline(ams->ms.map, ams->al_addr, ams->ms.sym);
}
static int64_t
@@ -769,15 +771,15 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
if (!left->branch_info || !right->branch_info)
return cmp_null(left->branch_info, right->branch_info);
- return _sort__dso_cmp(left->branch_info->from.map,
- right->branch_info->from.map);
+ return _sort__dso_cmp(left->branch_info->from.ms.map,
+ right->branch_info->from.ms.map);
}
static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
if (he->branch_info)
- return _hist_entry__dso_snprintf(he->branch_info->from.map,
+ return _hist_entry__dso_snprintf(he->branch_info->from.ms.map,
bf, size, width);
else
return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
@@ -791,8 +793,8 @@ static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
if (type != HIST_FILTER__DSO)
return -1;
- return dso && (!he->branch_info || !he->branch_info->from.map ||
- he->branch_info->from.map->dso != dso);
+ return dso && (!he->branch_info || !he->branch_info->from.ms.map ||
+ he->branch_info->from.ms.map->dso != dso);
}
static int64_t
@@ -801,15 +803,15 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
if (!left->branch_info || !right->branch_info)
return cmp_null(left->branch_info, right->branch_info);
- return _sort__dso_cmp(left->branch_info->to.map,
- right->branch_info->to.map);
+ return _sort__dso_cmp(left->branch_info->to.ms.map,
+ right->branch_info->to.ms.map);
}
static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
if (he->branch_info)
- return _hist_entry__dso_snprintf(he->branch_info->to.map,
+ return _hist_entry__dso_snprintf(he->branch_info->to.ms.map,
bf, size, width);
else
return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
@@ -823,8 +825,8 @@ static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
if (type != HIST_FILTER__DSO)
return -1;
- return dso && (!he->branch_info || !he->branch_info->to.map ||
- he->branch_info->to.map->dso != dso);
+ return dso && (!he->branch_info || !he->branch_info->to.ms.map ||
+ he->branch_info->to.ms.map->dso != dso);
}
static int64_t
@@ -839,10 +841,10 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
from_l = &left->branch_info->from;
from_r = &right->branch_info->from;
- if (!from_l->sym && !from_r->sym)
+ if (!from_l->ms.sym && !from_r->ms.sym)
return _sort__addr_cmp(from_l->addr, from_r->addr);
- return _sort__sym_cmp(from_l->sym, from_r->sym);
+ return _sort__sym_cmp(from_l->ms.sym, from_r->ms.sym);
}
static int64_t
@@ -856,10 +858,10 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
to_l = &left->branch_info->to;
to_r = &right->branch_info->to;
- if (!to_l->sym && !to_r->sym)
+ if (!to_l->ms.sym && !to_r->ms.sym)
return _sort__addr_cmp(to_l->addr, to_r->addr);
- return _sort__sym_cmp(to_l->sym, to_r->sym);
+ return _sort__sym_cmp(to_l->ms.sym, to_r->ms.sym);
}
static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
@@ -868,8 +870,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
if (he->branch_info) {
struct addr_map_symbol *from = &he->branch_info->from;
- return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
- he->level, bf, size, width);
+ return _hist_entry__sym_snprintf(&from->ms, from->addr, he->level, bf, size, width);
}
return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
@@ -881,8 +882,7 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
if (he->branch_info) {
struct addr_map_symbol *to = &he->branch_info->to;
- return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
- he->level, bf, size, width);
+ return _hist_entry__sym_snprintf(&to->ms, to->addr, he->level, bf, size, width);
}
return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
@@ -896,8 +896,8 @@ static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
if (type != HIST_FILTER__SYMBOL)
return -1;
- return sym && !(he->branch_info && he->branch_info->from.sym &&
- strstr(he->branch_info->from.sym->name, sym));
+ return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
+ strstr(he->branch_info->from.ms.sym->name, sym));
}
static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
@@ -908,8 +908,8 @@ static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
if (type != HIST_FILTER__SYMBOL)
return -1;
- return sym && !(he->branch_info && he->branch_info->to.sym &&
- strstr(he->branch_info->to.sym->name, sym));
+ return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
+ strstr(he->branch_info->to.ms.sym->name, sym));
}
struct sort_entry sort_dso_from = {
@@ -1017,16 +1017,13 @@ static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
uint64_t addr = 0;
- struct map *map = NULL;
- struct symbol *sym = NULL;
+ struct map_symbol *ms = NULL;
if (he->mem_info) {
addr = he->mem_info->daddr.addr;
- map = he->mem_info->daddr.map;
- sym = he->mem_info->daddr.sym;
+ ms = &he->mem_info->daddr.ms;
}
- return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
- width);
+ return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
}
int64_t
@@ -1046,16 +1043,13 @@ static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
uint64_t addr = 0;
- struct map *map = NULL;
- struct symbol *sym = NULL;
+ struct map_symbol *ms = NULL;
if (he->mem_info) {
addr = he->mem_info->iaddr.addr;
- map = he->mem_info->iaddr.map;
- sym = he->mem_info->iaddr.sym;
+ ms = &he->mem_info->iaddr.ms;
}
- return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
- width);
+ return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
}
static int64_t
@@ -1065,9 +1059,9 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
struct map *map_r = NULL;
if (left->mem_info)
- map_l = left->mem_info->daddr.map;
+ map_l = left->mem_info->daddr.ms.map;
if (right->mem_info)
- map_r = right->mem_info->daddr.map;
+ map_r = right->mem_info->daddr.ms.map;
return _sort__dso_cmp(map_l, map_r);
}
@@ -1078,7 +1072,7 @@ static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
struct map *map = NULL;
if (he->mem_info)
- map = he->mem_info->daddr.map;
+ map = he->mem_info->daddr.ms.map;
return _hist_entry__dso_snprintf(map, bf, size, width);
}
@@ -1200,6 +1194,7 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
{
u64 l, r;
struct map *l_map, *r_map;
+ int rc;
if (!left->mem_info) return -1;
if (!right->mem_info) return 1;
@@ -1208,8 +1203,8 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
if (left->cpumode > right->cpumode) return -1;
if (left->cpumode < right->cpumode) return 1;
- l_map = left->mem_info->daddr.map;
- r_map = right->mem_info->daddr.map;
+ l_map = left->mem_info->daddr.ms.map;
+ r_map = right->mem_info->daddr.ms.map;
/* if both are NULL, jump to sort on al_addr instead */
if (!l_map && !r_map)
@@ -1218,18 +1213,9 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
if (!l_map) return -1;
if (!r_map) return 1;
- if (l_map->maj > r_map->maj) return -1;
- if (l_map->maj < r_map->maj) return 1;
-
- if (l_map->min > r_map->min) return -1;
- if (l_map->min < r_map->min) return 1;
-
- if (l_map->ino > r_map->ino) return -1;
- if (l_map->ino < r_map->ino) return 1;
-
- if (l_map->ino_generation > r_map->ino_generation) return -1;
- if (l_map->ino_generation < r_map->ino_generation) return 1;
-
+ rc = dso__cmp_id(l_map->dso, r_map->dso);
+ if (rc)
+ return rc;
/*
* Addresses with no major/minor numbers are assumed to be
* anonymous in userspace. Sort those on pid then address.
@@ -1240,8 +1226,8 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
(!(l_map->flags & MAP_SHARED)) &&
- !l_map->maj && !l_map->min && !l_map->ino &&
- !l_map->ino_generation) {
+ !l_map->dso->id.maj && !l_map->dso->id.min &&
+ !l_map->dso->id.ino && !l_map->dso->id.ino_generation) {
/* userspace anonymous */
if (left->thread->pid_ > right->thread->pid_) return -1;
@@ -1264,27 +1250,26 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
{
uint64_t addr = 0;
- struct map *map = NULL;
- struct symbol *sym = NULL;
+ struct map_symbol *ms = NULL;
char level = he->level;
if (he->mem_info) {
+ struct map *map = he->mem_info->daddr.ms.map;
+
addr = cl_address(he->mem_info->daddr.al_addr);
- map = he->mem_info->daddr.map;
- sym = he->mem_info->daddr.sym;
+ ms = &he->mem_info->daddr.ms;
/* print [s] for shared data mmaps */
if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
map && !(map->prot & PROT_EXEC) &&
(map->flags & MAP_SHARED) &&
- (map->maj || map->min || map->ino ||
- map->ino_generation))
+ (map->dso->id.maj || map->dso->id.min ||
+ map->dso->id.ino || map->dso->id.ino_generation))
level = 's';
else if (!map)
level = 'X';
}
- return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
- width);
+ return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width);
}
struct sort_entry sort_mispredict = {
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 7b93f34ac1f4..5aff9542d9b7 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -10,6 +10,8 @@
#include "callchain.h"
#include "values.h"
#include "hist.h"
+#include "stat.h"
+#include "spark.h"
struct option;
struct thread;
@@ -71,6 +73,8 @@ struct hist_entry_diff {
/* PERF_HPP_DIFF__CYCLES */
s64 cycles;
};
+ struct stats stats;
+ unsigned long svals[NUM_SPARKS];
};
struct hist_entry_ops {
diff --git a/tools/perf/util/spark.c b/tools/perf/util/spark.c
new file mode 100644
index 000000000000..70272a8b81a6
--- /dev/null
+++ b/tools/perf/util/spark.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include "spark.h"
+#include "stat.h"
+
+#define SPARK_SHIFT 8
+
+/* Print spark lines on outf for numval values in val. */
+int print_spark(char *bf, int size, unsigned long *val, int numval)
+{
+ static const char *ticks[NUM_SPARKS] = {
+ "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"
+ };
+ int i, printed = 0;
+ unsigned long min = ULONG_MAX, max = 0, f;
+
+ for (i = 0; i < numval; i++) {
+ if (val[i] < min)
+ min = val[i];
+ if (val[i] > max)
+ max = val[i];
+ }
+ f = ((max - min) << SPARK_SHIFT) / (NUM_SPARKS - 1);
+ if (f < 1)
+ f = 1;
+ for (i = 0; i < numval; i++) {
+ printed += scnprintf(bf + printed, size - printed, "%s",
+ ticks[((val[i] - min) << SPARK_SHIFT) / f]);
+ }
+
+ return printed;
+}
diff --git a/tools/perf/util/spark.h b/tools/perf/util/spark.h
new file mode 100644
index 000000000000..25402d7d7a64
--- /dev/null
+++ b/tools/perf/util/spark.h
@@ -0,0 +1,8 @@
+#ifndef SPARK_H
+#define SPARK_H 1
+
+#define NUM_SPARKS 8
+
+int print_spark(char *bf, int size, unsigned long *val, int numval);
+
+#endif
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index ed3b0ac2f785..bc31fccc0057 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -100,6 +100,15 @@ static void aggr_printout(struct perf_stat_config *config,
nr,
config->csv_sep);
break;
+ case AGGR_NODE:
+ fprintf(config->output, "N%*d%s%*d%s",
+ config->csv_output ? 0 : -5,
+ id,
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ break;
case AGGR_NONE:
if (evsel->percore) {
fprintf(config->output, "S%d-D%d-C%*d%s",
@@ -965,6 +974,11 @@ static void print_interval(struct perf_stat_config *config,
if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) {
switch (config->aggr_mode) {
+ case AGGR_NODE:
+ fprintf(output, "# time node cpus");
+ if (!metric_only)
+ fprintf(output, " counts %*s events\n", unit_width, "unit");
+ break;
case AGGR_SOCKET:
fprintf(output, "# time socket cpus");
if (!metric_only)
@@ -1188,6 +1202,7 @@ perf_evlist__print_counters(struct evlist *evlist,
case AGGR_CORE:
case AGGR_DIE:
case AGGR_SOCKET:
+ case AGGR_NODE:
print_aggr(config, evlist, prefix);
break;
case AGGR_THREAD:
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index ebdd130557fb..332cb730785b 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -299,6 +299,7 @@ process_counter_values(struct perf_stat_config *config, struct evsel *evsel,
case AGGR_CORE:
case AGGR_DIE:
case AGGR_SOCKET:
+ case AGGR_NODE:
case AGGR_NONE:
if (!evsel->snapshot)
perf_evsel__compute_deltas(evsel, cpu, thread, count);
@@ -490,6 +491,16 @@ int create_perf_stat_counter(struct evsel *evsel,
if (config->identifier)
attr->sample_type = PERF_SAMPLE_IDENTIFIER;
+ if (config->all_user) {
+ attr->exclude_kernel = 1;
+ attr->exclude_user = 0;
+ }
+
+ if (config->all_kernel) {
+ attr->exclude_kernel = 0;
+ attr->exclude_user = 1;
+ }
+
/*
* Disabling all counters initially, they will be enabled
* either manually by us or by kernel via enable_on_exec
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index edbeb2f63e8d..bfa9aaf36ce6 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -47,6 +47,7 @@ enum aggr_mode {
AGGR_CORE,
AGGR_THREAD,
AGGR_UNSET,
+ AGGR_NODE,
};
enum {
@@ -106,6 +107,8 @@ struct perf_stat_config {
bool big_num;
bool no_merge;
bool walltime_run_table;
+ bool all_kernel;
+ bool all_user;
FILE *output;
unsigned int interval;
unsigned int timeout;
diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h
index 708805f5573e..73df616ced43 100644
--- a/tools/perf/util/string2.h
+++ b/tools/perf/util/string2.h
@@ -4,6 +4,7 @@
#include <linux/string.h>
#include <linux/types.h>
+#include <sys/types.h> // pid_t
#include <stddef.h>
#include <string.h>
@@ -32,6 +33,8 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int
return asprintf_expr_inout_ints(var, false, nints, ints);
}
+char *asprintf__tp_filter_pids(size_t npids, pid_t *pids);
+
char *strpbrk_esc(char *str, const char *stopset);
char *strdup_esc(const char *str);
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 66f4be1df573..16776d5fbaea 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -934,7 +934,7 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
* we still are sure to have a reference to this DSO via
* *curr_map->dso.
*/
- dsos__add(&map->groups->machine->dsos, curr_dso);
+ dsos__add(&kmaps->machine->dsos, curr_dso);
/* kmaps already got it */
map__put(curr_map);
dso__set_loaded(curr_dso);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a8f80e427674..db9667aacb88 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -242,28 +242,24 @@ void symbols__fixup_end(struct rb_root_cached *symbols)
void map_groups__fixup_end(struct map_groups *mg)
{
struct maps *maps = &mg->maps;
- struct map *next, *curr;
+ struct map *prev = NULL, *curr;
down_write(&maps->lock);
- curr = maps__first(maps);
- if (curr == NULL)
- goto out_unlock;
+ maps__for_each_entry(maps, curr) {
+ if (prev != NULL && !prev->end)
+ prev->end = curr->start;
- for (next = map__next(curr); next; next = map__next(curr)) {
- if (!curr->end)
- curr->end = next->start;
- curr = next;
+ prev = curr;
}
/*
* We still haven't the actual symbols, so guess the
* last map final address.
*/
- if (!curr->end)
+ if (curr && !curr->end)
curr->end = ~0ULL;
-out_unlock:
up_write(&maps->lock);
}
@@ -1053,11 +1049,6 @@ out_delete_from:
return ret;
}
-struct map *map_groups__first(struct map_groups *mg)
-{
- return maps__first(&mg->maps);
-}
-
static int do_validate_kcore_modules(const char *filename,
struct map_groups *kmaps)
{
@@ -1069,13 +1060,10 @@ static int do_validate_kcore_modules(const char *filename,
if (err)
return err;
- old_map = map_groups__first(kmaps);
- while (old_map) {
- struct map *next = map_groups__next(old_map);
+ map_groups__for_each_entry(kmaps, old_map) {
struct module_info *mi;
if (!__map__is_kmodule(old_map)) {
- old_map = next;
continue;
}
@@ -1085,8 +1073,6 @@ static int do_validate_kcore_modules(const char *filename,
err = -EINVAL;
goto out;
}
-
- old_map = next;
}
out:
delete_modules(&modules);
@@ -1189,9 +1175,7 @@ int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map)
struct map *old_map;
LIST_HEAD(merged);
- for (old_map = map_groups__first(kmaps); old_map;
- old_map = map_groups__next(old_map)) {
-
+ map_groups__for_each_entry(kmaps, old_map) {
/* no overload with this one */
if (new_map->end < old_map->start ||
new_map->start >= old_map->end)
@@ -1264,7 +1248,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
{
struct map_groups *kmaps = map__kmaps(map);
struct kcore_mapfn_data md;
- struct map *old_map, *new_map, *replacement_map = NULL;
+ struct map *old_map, *new_map, *replacement_map = NULL, *next;
struct machine *machine;
bool is_64_bit;
int err, fd;
@@ -1311,10 +1295,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
}
/* Remove old maps */
- old_map = map_groups__first(kmaps);
- while (old_map) {
- struct map *next = map_groups__next(old_map);
-
+ map_groups__for_each_entry_safe(kmaps, old_map, next) {
/*
* We need to preserve eBPF maps even if they are
* covered by kcore, because we need to access
@@ -1322,7 +1303,6 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
*/
if (old_map != map && !__map__is_bpf_prog(old_map))
map_groups__remove(kmaps, old_map);
- old_map = next;
}
machine->trampolines_mapped = false;
@@ -1550,7 +1530,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
/*
* kernel modules know their symtab type - it's set when
- * creating a module dso in machine__findnew_module_map().
+ * creating a module dso in machine__addnew_module_map().
*/
return kmod && dso->symtab_type == type;
@@ -1608,7 +1588,7 @@ int dso__load(struct dso *dso, struct map *map)
char *name;
int ret = -1;
u_int i;
- struct machine *machine;
+ struct machine *machine = NULL;
char *root_dir = (char *) "";
int ss_pos = 0;
struct symsrc ss_[2];
@@ -1637,17 +1617,13 @@ int dso__load(struct dso *dso, struct map *map)
goto out;
}
- if (map->groups && map->groups->machine)
- machine = map->groups->machine;
- else
- machine = NULL;
-
if (dso->kernel) {
if (dso->kernel == DSO_TYPE_KERNEL)
ret = dso__load_kernel_sym(dso, map);
else if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
ret = dso__load_guest_kernel_sym(dso, map);
+ machine = map__kmaps(map)->machine;
if (machine__is(machine, "x86_64"))
machine__map_x86_64_entry_trampolines(machine, dso);
goto out;
@@ -1784,28 +1760,82 @@ out:
return ret;
}
+static int map__strcmp(const void *a, const void *b)
+{
+ const struct map *ma = *(const struct map **)a, *mb = *(const struct map **)b;
+ return strcmp(ma->dso->short_name, mb->dso->short_name);
+}
+
+static int map__strcmp_name(const void *name, const void *b)
+{
+ const struct map *map = *(const struct map **)b;
+ return strcmp(name, map->dso->short_name);
+}
+
+void __map_groups__sort_by_name(struct map_groups *mg)
+{
+ qsort(mg->maps_by_name, mg->nr_maps, sizeof(struct map *), map__strcmp);
+}
+
+static int map__groups__sort_by_name_from_rbtree(struct map_groups *mg)
+{
+ struct map *map;
+ struct map **maps_by_name = realloc(mg->maps_by_name, mg->nr_maps * sizeof(map));
+ int i = 0;
+
+ if (maps_by_name == NULL)
+ return -1;
+
+ mg->maps_by_name = maps_by_name;
+ mg->nr_maps_allocated = mg->nr_maps;
+
+ maps__for_each_entry(&mg->maps, map)
+ maps_by_name[i++] = map;
+
+ __map_groups__sort_by_name(mg);
+ return 0;
+}
+
+static struct map *__map_groups__find_by_name(struct map_groups *mg, const char *name)
+{
+ struct map **mapp;
+
+ if (mg->maps_by_name == NULL &&
+ map__groups__sort_by_name_from_rbtree(mg))
+ return NULL;
+
+ mapp = bsearch(name, mg->maps_by_name, mg->nr_maps, sizeof(*mapp), map__strcmp_name);
+ if (mapp)
+ return *mapp;
+ return NULL;
+}
+
struct map *map_groups__find_by_name(struct map_groups *mg, const char *name)
{
struct maps *maps = &mg->maps;
struct map *map;
- struct rb_node *node;
down_read(&maps->lock);
- for (node = maps->names.rb_node; node; ) {
- int rc;
-
- map = rb_entry(node, struct map, rb_node_name);
-
- rc = strcmp(map->dso->short_name, name);
- if (rc < 0)
- node = node->rb_left;
- else if (rc > 0)
- node = node->rb_right;
- else
+ if (mg->last_search_by_name && strcmp(mg->last_search_by_name->dso->short_name, name) == 0) {
+ map = mg->last_search_by_name;
+ goto out_unlock;
+ }
+ /*
+ * If we have mg->maps_by_name, then the name isn't in the rbtree,
+ * as mg->maps_by_name mirrors the rbtree when lookups by name are
+ * made.
+ */
+ map = __map_groups__find_by_name(mg, name);
+ if (map || mg->maps_by_name != NULL)
+ goto out_unlock;
+ /* Fallback to traversing the rbtree... */
+ maps__for_each_entry(maps, map)
+ if (strcmp(map->dso->short_name, name) == 0) {
+ mg->last_search_by_name = map;
goto out_unlock;
- }
+ }
map = NULL;
@@ -2047,15 +2077,9 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map)
{
int err;
const char *kallsyms_filename = NULL;
- struct machine *machine;
+ struct machine *machine = map__kmaps(map)->machine;
char path[PATH_MAX];
- if (!map->groups) {
- pr_debug("Guest kernel map hasn't the point to groups\n");
- return -1;
- }
- machine = map->groups->machine;
-
if (machine__is_default_guest(machine)) {
/*
* if the user specified a vmlinux filename, use it and only
@@ -2371,25 +2395,3 @@ struct mem_info *mem_info__new(void)
refcount_set(&mi->refcnt, 1);
return mi;
}
-
-struct block_info *block_info__get(struct block_info *bi)
-{
- if (bi)
- refcount_inc(&bi->refcnt);
- return bi;
-}
-
-void block_info__put(struct block_info *bi)
-{
- if (bi && refcount_dec_and_test(&bi->refcnt))
- free(bi);
-}
-
-struct block_info *block_info__new(void)
-{
- struct block_info *bi = zalloc(sizeof(*bi));
-
- if (bi)
- refcount_set(&bi->refcnt, 1);
- return bi;
-}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 0b0c6b5b1899..0b718cc9fb28 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -11,6 +11,7 @@
#include <stdio.h>
#include "path.h"
#include "symbol_conf.h"
+#include "spark.h"
#ifdef HAVE_LIBELF_SUPPORT
#include <libelf.h>
@@ -105,20 +106,9 @@ struct ref_reloc_sym {
u64 unrelocated_addr;
};
-struct block_info {
- struct symbol *sym;
- u64 start;
- u64 end;
- u64 cycles;
- u64 cycles_aggr;
- int num;
- int num_aggr;
- refcount_t refcnt;
-};
-
struct addr_location {
- struct machine *machine;
struct thread *thread;
+ struct map_groups *mg;
struct map *map;
struct symbol *sym;
const char *srcline;
@@ -289,16 +279,4 @@ static inline void __mem_info__zput(struct mem_info **mi)
#define mem_info__zput(mi) __mem_info__zput(&mi)
-struct block_info *block_info__new(void);
-struct block_info *block_info__get(struct block_info *bi);
-void block_info__put(struct block_info *bi);
-
-static inline void __block_info__zput(struct block_info **bi)
-{
- block_info__put(*bi);
- *bi = NULL;
-}
-
-#define block_info__zput(bi) __block_info__zput(&bi)
-
#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index e6880789864c..10f1ec3e0349 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -40,6 +40,7 @@ struct symbol_conf {
raw_trace,
report_hierarchy,
report_block,
+ report_individual_block,
inline_name,
disable_add2line_warn;
const char *vmlinux_name,
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 807cbca403a7..48c3f8b9c852 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -438,7 +438,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t
else
event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
- for (pos = maps__first(maps); pos; pos = map__next(pos)) {
+ maps__for_each_entry(maps, pos) {
size_t size;
if (!__map__is_kmodule(pos))
@@ -1228,6 +1228,11 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
if (type & PERF_SAMPLE_PHYS_ADDR)
result += sizeof(u64);
+ if (type & PERF_SAMPLE_AUX) {
+ result += sizeof(u64);
+ result += sample->aux_sample.size;
+ }
+
return result;
}
@@ -1396,6 +1401,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo
array++;
}
+ if (type & PERF_SAMPLE_AUX) {
+ sz = sample->aux_sample.size;
+ *array++ = sz;
+ memcpy(array, sample->aux_sample.data, sz);
+ array = (void *)array + sz;
+ }
+
return 0;
}
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index b64e9e049636..0a277a920970 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -350,7 +350,7 @@ static int __thread__prepare_access(struct thread *thread)
down_read(&maps->lock);
- for (map = maps__first(maps); map; map = map__next(map)) {
+ maps__for_each_entry(maps, map) {
err = unwind__prepare_access(thread->mg, map, &initialized);
if (err || initialized)
break;
diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c
index 9796a2e43f67..302443921681 100644
--- a/tools/perf/util/time-utils.c
+++ b/tools/perf/util/time-utils.c
@@ -458,10 +458,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
return true;
}
-int perf_time__parse_for_ranges(const char *time_str,
+int perf_time__parse_for_ranges_reltime(const char *time_str,
struct perf_session *session,
struct perf_time_interval **ranges,
- int *range_size, int *range_num)
+ int *range_size, int *range_num,
+ bool reltime)
{
bool has_percent = strchr(time_str, '%');
struct perf_time_interval *ptime_range;
@@ -471,7 +472,7 @@ int perf_time__parse_for_ranges(const char *time_str,
if (!ptime_range)
return -ENOMEM;
- if (has_percent) {
+ if (has_percent || reltime) {
if (session->evlist->first_sample_time == 0 &&
session->evlist->last_sample_time == 0) {
pr_err("HINT: no first/last sample time found in perf data.\n"
@@ -479,7 +480,9 @@ int perf_time__parse_for_ranges(const char *time_str,
"(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
goto error;
}
+ }
+ if (has_percent) {
num = perf_time__percent_parse_str(
ptime_range, size,
time_str,
@@ -492,6 +495,15 @@ int perf_time__parse_for_ranges(const char *time_str,
if (num < 0)
goto error_invalid;
+ if (reltime) {
+ int i;
+
+ for (i = 0; i < num; i++) {
+ ptime_range[i].start += session->evlist->first_sample_time;
+ ptime_range[i].end += session->evlist->first_sample_time;
+ }
+ }
+
*range_size = size;
*range_num = num;
*ranges = ptime_range;
@@ -504,6 +516,15 @@ error:
return ret;
}
+int perf_time__parse_for_ranges(const char *time_str,
+ struct perf_session *session,
+ struct perf_time_interval **ranges,
+ int *range_size, int *range_num)
+{
+ return perf_time__parse_for_ranges_reltime(time_str, session, ranges,
+ range_size, range_num, false);
+}
+
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
{
u64 sec = timestamp / NSEC_PER_SEC;
diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h
index 4f42988eb2f7..1142b0bddd5e 100644
--- a/tools/perf/util/time-utils.h
+++ b/tools/perf/util/time-utils.h
@@ -26,6 +26,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
struct perf_session;
+int perf_time__parse_for_ranges_reltime(const char *str, struct perf_session *session,
+ struct perf_time_interval **ranges,
+ int *range_size, int *range_num,
+ bool reltime);
+
int perf_time__parse_for_ranges(const char *str, struct perf_session *session,
struct perf_time_interval **ranges,
int *range_size, int *range_num);
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 5d6bfc70b210..9634f0ae57be 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -173,37 +173,6 @@ int parse_event_file(struct tep_handle *pevent,
return tep_parse_event(pevent, buf, size, sys);
}
-struct tep_event *trace_find_next_event(struct tep_handle *pevent,
- struct tep_event *event)
-{
- static int idx;
- int events_count;
- struct tep_event *all_events;
-
- all_events = tep_get_first_event(pevent);
- events_count = tep_get_events_count(pevent);
- if (!pevent || !all_events || events_count < 1)
- return NULL;
-
- if (!event) {
- idx = 0;
- return all_events;
- }
-
- if (idx < events_count && event == (all_events + idx)) {
- idx++;
- if (idx == events_count)
- return NULL;
- return (all_events + idx);
- }
-
- for (idx = 1; idx < events_count; idx++) {
- if (event == (all_events + (idx - 1)))
- return (all_events + idx);
- }
- return NULL;
-}
-
struct flag {
const char *name;
unsigned long long value;
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 2e158387b3d7..72fdf2a3577c 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -47,8 +47,6 @@ void parse_saved_cmdline(struct tep_handle *pevent, char *file, unsigned int siz
ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe);
-struct tep_event *trace_find_next_event(struct tep_handle *pevent,
- struct tep_event *event);
unsigned long long read_size(struct tep_event *event, void *ptr, int size);
unsigned long long eval_flag(const char *flag);
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 15f6e46d7124..d2a8df01c4a7 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -80,9 +80,10 @@ static int entry(u64 ip, struct unwind_info *ui)
if (__report_module(&al, ip, ui))
return -1;
- e->ip = ip;
- e->map = al.map;
- e->sym = al.sym;
+ e->ip = ip;
+ e->ms.mg = al.mg;
+ e->ms.map = al.map;
+ e->ms.sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''",
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 1800887b2255..6d53347d6744 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -575,9 +575,10 @@ static int entry(u64 ip, struct thread *thread,
struct unwind_entry e;
struct addr_location al;
- e.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
- e.ip = ip;
- e.map = al.map;
+ e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
+ e.ip = ip;
+ e.ms.map = al.map;
+ e.ms.mg = al.mg;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''",
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 3a7d00c20d86..50337c966979 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -4,17 +4,15 @@
#include <linux/compiler.h>
#include <linux/types.h>
+#include "util/map_symbol.h"
-struct map;
struct map_groups;
struct perf_sample;
-struct symbol;
struct thread;
struct unwind_entry {
- struct map *map;
- struct symbol *sym;
- u64 ip;
+ struct map_symbol ms;
+ u64 ip;
};
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 5eda6e19c947..969ae560dad9 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -154,8 +154,10 @@ static int rm_rf_depth_pat(const char *path, int depth, const char **pat)
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
- if (!match_pat(d->d_name, pat))
- return -2;
+ if (!match_pat(d->d_name, pat)) {
+ ret = -2;
+ break;
+ }
scnprintf(namebuf, sizeof(namebuf), "%s/%s",
path, d->d_name);
@@ -180,14 +182,31 @@ static int rm_rf_depth_pat(const char *path, int depth, const char **pat)
return rmdir(path);
}
+static int rm_rf_kcore_dir(const char *path)
+{
+ char kcore_dir_path[PATH_MAX];
+ const char *pat[] = {
+ "kcore",
+ "kallsyms",
+ "modules",
+ NULL,
+ };
+
+ snprintf(kcore_dir_path, sizeof(kcore_dir_path), "%s/kcore_dir", path);
+
+ return rm_rf_depth_pat(kcore_dir_path, 0, pat);
+}
+
int rm_rf_perf_data(const char *path)
{
const char *pat[] = {
- "header",
+ "data",
"data.*",
NULL,
};
+ rm_rf_kcore_dir(path);
+
return rm_rf_depth_pat(path, 0, pat);
}
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index ba4b4395f35d..6e00793c10ee 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -142,9 +142,9 @@ static enum dso_type machine__thread_dso_type(struct machine *machine,
struct thread *thread)
{
enum dso_type dso_type = DSO__TYPE_UNKNOWN;
- struct map *map = map_groups__first(thread->mg);
+ struct map *map;
- for (; map ; map = map_groups__next(map)) {
+ map_groups__for_each_entry(thread->mg, map) {
struct dso *dso = map->dso;
if (!dso || dso->long_name[0] != '/')
continue;