diff options
| author | Ingo Molnar <mingo@kernel.org> | 2019-11-12 12:06:08 +0100 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2019-11-12 12:06:08 +0100 |
| commit | 56b2147f34d057b0898c53a3eb2e9e70756ab89f (patch) | |
| tree | ae1c1d7036738b8d81ecab5353be3216a8e5d815 /tools/perf/util | |
| parent | 8f05c1ff8bfb8cbae0898e5dc6791927d1e5c503 (diff) | |
| parent | 7fa46cbf20d327d78114b1c8c7e69fabe7c57794 (diff) | |
| download | blackbird-op-linux-56b2147f34d057b0898c53a3eb2e9e70756ab89f.tar.gz blackbird-op-linux-56b2147f34d057b0898c53a3eb2e9e70756ab89f.zip | |
Merge tag 'perf-core-for-mingo-5.5-20191107' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
perf report:
Jin Yao:
- Introduce --total-cycles, for basic block profiling, further using data
obtained from LBR, an example should suffice:
# perf record -b
^C[ perf record: Woken up 595 times to write data ]
[ perf record: Captured and wrote 156.672 MB perf.data (196873 samples) ]
# perf evlist -v
cycles: size: 112, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CPU|PERIOD|BRANCH_STACK, read_format: ID, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1, branch_sample_type: ANY
# perf report --total-cycles --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
# Total Lost Samples: 0
#
# Samples: 6M of event 'cycles'
# Event count (approx.): 6299936
#
# Sampled Sampled Avg Avg
# Cycles% Cycles Cycles% Cycles [Program Block Range] Shared Object
# ....... ...... ....... ..... .................................... ................
#
2.17% 1.7M 0.08% 607 [compiler.h:199 -> common.c:221] [kernel.vmlinux]
0.72% 544.5K 0.03% 230 [entry_64.S:657 -> entry_64.S:662] [kernel.vmlinux]
0.56% 541.8K 0.09% 672 [compiler.h:199 -> common.c:300] [kernel.vmlinux]
0.39% 293.2K 0.01% 104 [list_debug.c:43 -> list_debug.c:61] [kernel.vmlinux]
0.36% 278.6K 0.03% 272 [entry_64.S:1289 -> entry_64.S:1308] [kernel.vmlinux]
perf record:
Adrian Hunter:
- Allow storing perf.data in a directory together with a copy of /proc/kcore.
Jiwei Sun:
- Add support for limit perf output file size, i.e.:
# perf record --all-cpus -F 10000 --max-size=4M sleep 10h
[ perf record: perf size limit reached (4097 KB), stopping session ]
[ perf record: Woken up 6 times to write data ]
[ perf record: Captured and wrote 4.048 MB perf.data (54094 samples) ]
Terminated
# ls -lah perf.data
-rw-------. 1 root root 4.1M Nov 7 15:27 perf.data
#
perf stat:
Jiri Olsa:
- Add --per-node agregation support:
In live mode:
# perf stat -a -I 1000 -e cycles --per-node
# time node cpus counts unit events
1.000542550 N0 20 6,202,097 cycles
1.000542550 N1 20 639,559 cycles
2.002040063 N0 20 7,412,495 cycles
2.002040063 N1 20 2,185,577 cycles
3.003451699 N0 20 6,508,917 cycles
3.003451699 N1 20 765,607 cycles
...
Or in the record/report stat session:
# perf stat record -a -I 1000 -e cycles
# time counts unit events
1.000536937 10,008,468 cycles
2.002090152 9,578,539 cycles
3.003625233 7,647,869 cycles
4.005135036 7,032,086 cycles
^C 4.340902364 3,923,893 cycles
# perf stat report --per-node
# time node cpus counts unit events
1.000536937 N0 20 9,355,086 cycles
1.000536937 N1 20 653,382 cycles
2.002090152 N0 20 7,712,838 cycles
2.002090152 N1 20 1,865,701 cycles
...
perf probe:
Masami Hiramatsu:
Various fixes related to recent additions to the DWARF format:
- Fix to find range-only function instance
- Walk function lines in lexical blocks
- Fix to show function entry line as probe-able
- Fix wrong address verification
- Fix to probe a function which has no entry pc
- Fix to probe an inline function which has no entry pc
- Fix to list probe event with correct line number
- Fix to show inlined function callsite without entry_pc
- Fix to show ranges of variables in functions without entry_pc
- Return a better scope DIE if there is no best scope
- Skip end-of-sequence and non statement lines
- Filter out instances except for inlined subroutine and subprogram
- Fix to show calling lines of inlined functions
- Skip overlapped location on searching variables
perf inject:
Adrian Hunter:
- Do not strip evsels with --strip, as they are needed for create_gcov
(see the autofdo example in tools/perf/Documentation/intel-pt.txt).
Intel PT:
Adrian Hunter:
- Intel PT uses an auxtrace_cache to store the results of code-walking, to avoid
repeated decoding. Add an auxtrace_cache__remove to handle text poke events.
core:
Andi Kleen:
- Always preserve errno while cleaning up perf_event_open failures.
llvm:
Arnaldo Carvalho de Melo:
- No need to tell that the request for saving a .o file for BPF events, as
expressed in ~/.perfconfig was satisfied, make that a debug message.
perf vendor events:
Intel:
Haiyan Song:
- Update CascadelakeX events to v1.05.
- Update all the Intel JSON metrics from TMAM 3.6.
Treewide:
Ian Rogers:
- Improve error paths, plugging leaks found using LLVM tools
such as libFuzzer.
jevents:
Yunfeng Ye:
- Fix resource leak in process_mapfile() and main()
perf kvm:
Igor Lubashev:
- Use evlist layer api when possible.
libsubcmd:
James Clark:
- Move EXTRA_FLAGS to the end to allow overriding existing flags.
- Use -O0 with DEBUG=1
perf diff:
Jin Yao:
- Don't use hack to skip column length calculation
CoreSight ETM:
Leo yan:
- Fix definition of macro TO_CS_QUEUE_NR
ARM64:
John Garry:
- Do not try to include libelf header files when its feature detection
failed, fixing the cross build for ARM64.
perf tests:
Leo Yan:
- Fix out of bounds memory access in the backward ring buffer test.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util')
43 files changed, 1573 insertions, 385 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 39814b1806a6..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 diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index ef1866a902c4..bee0fee122f8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1892,7 +1892,7 @@ static char *expand_tabs(char *line, char **storage, size_t *storage_len) } /* Expand the last region. */ - len = line_len + 1 - src; + len = line_len - src; memcpy(&new_line[dst], &line[src], len); dst += len; new_line[dst] = '\0'; diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 8470dfe9fe97..c555c3ccd79d 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1457,6 +1457,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; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index f201f36bc35f..3f4aa5427d76 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -489,6 +489,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, diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c new file mode 100644 index 000000000000..9abc201ebe63 --- /dev/null +++ b/tools/perf/util/block-info.c @@ -0,0 +1,538 @@ +// 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 "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; +} + +#ifdef HAVE_SLANG_SUPPORT +static int block_hists_browser__title(struct hist_browser *browser, char *bf, + size_t size) +{ + struct hists *hists = evsel__hists(browser->block_evsel); + const char *evname = perf_evsel__name(browser->block_evsel); + unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; + int ret; + + ret = scnprintf(bf, size, "# Samples: %lu", nr_samples); + if (evname) + scnprintf(bf + ret, size - ret, " of event '%s'", evname); + + return 0; +} + +static int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, + float min_percent) +{ + struct hists *hists = &bh->block_hists; + struct hist_browser *browser; + int key = -1; + static const char help[] = + " q Quit \n"; + + browser = hist_browser__new(hists); + if (!browser) + return -1; + + browser->block_evsel = evsel; + browser->title = block_hists_browser__title; + browser->min_pcnt = min_percent; + + /* reset abort key so that it can get Ctrl-C as a key */ + SLang_reset_tty(); + SLang_init_tty(0, 0, 0); + + while (1) { + key = hist_browser__run(browser, "? - help", true); + + switch (key) { + case 'q': + goto out; + case '?': + ui_browser__help_window(&browser->b, help); + break; + default: + break; + } + } + +out: + hist_browser__delete(browser); + return 0; +} +#else +static int block_hists_tui_browse(struct block_hist *bh __maybe_unused, + struct evsel *evsel __maybe_unused, + float min_percent __maybe_unused) +{ + return 0; +} +#endif + +int report__browse_block_hists(struct block_hist *bh, float min_percent, + struct evsel *evsel) +{ + 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); + 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..e4d20bccd9b6 --- /dev/null +++ b/tools/perf/util/block-info.h @@ -0,0 +1,78 @@ +/* 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); + +float block_info__total_cycles_percent(struct hist_entry *he); + +#endif /* __PERF_BLOCK_H */ 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/dso.c b/tools/perf/util/dso.c index e11ddf86f2b3..0f1b77275a86 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; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index e4dddb76770d..2f1fcbc6fead 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -285,6 +285,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 +334,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/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index df6cee5c071f..5544bfbd0f6c 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -308,20 +308,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); - /* Actually gcc optimizes non-inline as like as inlined */ - return !dwarf_func_inline(dw_die) && dwarf_entrypc(dw_die, &tmp) == 0; + if (tag != DW_TAG_subprogram && + tag != DW_TAG_inlined_subroutine) + return false; + + 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 +628,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 +702,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 +742,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 +756,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 +784,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 +820,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 +835,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 +864,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 +1041,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 +1104,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 2a91a10ccfcc..6242a9215df7 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -180,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); @@ -354,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 a3059dc1abe5..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 { @@ -120,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/evsel.c b/tools/perf/util/evsel.c index abc7fda4a0fe..d4451846af93 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1574,7 +1574,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) || @@ -1727,8 +1727,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; @@ -1812,13 +1812,16 @@ 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; } 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 7b6eaf5e0bda..78749535dbf3 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 @@ -755,6 +758,10 @@ struct hist_entry *hists__add_entry_block(struct hists *hists, struct hist_entry entry = { .block_info = block_info, .hists = hists, + .ms = { + .map = al->map, + .sym = al->sym, + }, }, *he = hists__findnew_entry(hists, &entry, al, false); return he; @@ -2569,7 +2576,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 +2604,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..4d87c7b4c1b2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -527,7 +527,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/llvm-utils.c b/tools/perf/util/llvm-utils.c index 8b14e4a7f1dc..eae47c2509eb 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -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..e768ef24633f 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); @@ -861,7 +866,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 +1062,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; @@ -1543,8 +1548,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, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index eec9b282c047..466c9b035e19 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -288,7 +288,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); } @@ -594,28 +594,20 @@ void map_groups__insert(struct map_groups *mg, struct map *map) static void __maps__purge(struct maps *maps) { - struct rb_root *root = &maps->entries; - struct rb_node *next = rb_first(root); + struct map *pos, *next; - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - - next = rb_next(&pos->rb_node); - rb_erase_init(&pos->rb_node, root); + maps__for_each_entry_safe(maps, pos, next) { + rb_erase_init(&pos->rb_node, &maps->entries); map__put(pos); } } static void __maps__purge_names(struct maps *maps) { - struct rb_root *root = &maps->names; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node_name); + struct map *pos, *next; - next = rb_next(&pos->rb_node_name); - rb_erase_init(&pos->rb_node_name, root); + maps__for_each_entry_by_name_safe(maps, pos, next) { + rb_erase_init(&pos->rb_node_name, &maps->names); map__put(pos); } } @@ -687,13 +679,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) @@ -739,12 +729,11 @@ int map_groups__find_ams(struct addr_map_symbol *ams) 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) { @@ -889,7 +878,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; @@ -1007,7 +996,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); @@ -1016,6 +1005,34 @@ struct map *map__next(struct map *map) return NULL; } +struct map *map__next(struct map *map) +{ + return map ? __map__next(map) : NULL; +} + +struct map *maps__first_by_name(struct maps *maps) +{ + struct rb_node *first = rb_first(&maps->names); + + if (first) + return rb_entry(first, struct map, rb_node_name); + return NULL; +} + +static struct map *__map__next_by_name(struct map *map) +{ + struct rb_node *next = rb_next(&map->rb_node_name); + + if (next) + return rb_entry(next, struct map, rb_node_name); + return NULL; +} + +struct map *map__next_by_name(struct map *map) +{ + return map ? __map__next_by_name(map) : NULL; +} + struct kmap *__map__kmap(struct map *map) { if (!map->dso || !map->dso->kernel) diff --git a/tools/perf/util/map_groups.h b/tools/perf/util/map_groups.h index 77252e14008f..bfbdbf5a443a 100644 --- a/tools/perf/util/map_groups.h +++ b/tools/perf/util/map_groups.h @@ -25,7 +25,22 @@ 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 *maps__first_by_name(struct maps *maps); +struct map *map__next_by_name(struct map *map); + +#define maps__for_each_entry_by_name(maps, map) \ + for (map = maps__first_by_name(maps); map; map = map__next_by_name(map)) + +#define maps__for_each_entry_by_name_safe(maps, map, next) \ + for (map = maps__first_by_name(maps), next = map__next_by_name(map); map; map = next, next = map__next_by_name(map)) struct map_groups { struct maps maps; @@ -74,12 +89,11 @@ 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); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index db882f630f7e..6d18ff9bce49 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -182,6 +182,20 @@ 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; + } + WARN_ONCE(err->str, "WARNING: multiple event parsing errors\n"); + err->idx = idx; + free(err->str); + err->str = str; + free(err->help); + err->help = help; +} struct tracepoint_path *tracepoint_id_to_path(u64 config) { @@ -932,11 +946,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; } @@ -972,8 +986,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 +1009,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 +1053,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,8 +1100,9 @@ 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; @@ -1089,9 +1110,9 @@ do { \ CHECK_TYPE_VAL(NUM); 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; } @@ -1142,9 +1163,9 @@ static int config_term_tracepoint(struct perf_event_attr *attr, 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; } @@ -1323,10 +1344,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, -1, err_str, NULL); return -EINVAL; } @@ -1365,8 +1388,15 @@ 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)) + 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 +1419,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 +1435,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 +1467,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 +1969,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; @@ -2718,30 +2765,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 +2854,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 +2875,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 769e07cddaa2..5ee8ac93840c 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -124,6 +124,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 +139,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); diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 48126ae4cd13..4cac830015be 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,33 @@ 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($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 +312,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 +348,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 +366,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 +385,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 +395,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 +407,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 +428,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 +448,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 +467,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 +486,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 +500,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 +519,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 +535,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); + return -1; + } $$ = 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 +578,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 +595,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 +613,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 +672,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 +685,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 +709,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 +722,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 +735,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 +746,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 +767,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 +794,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 +809,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 +822,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 +852,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/pmu.c b/tools/perf/util/pmu.c index adbe97e941dd..db1e57113f4b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1050,9 +1050,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; @@ -1080,8 +1080,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; } @@ -1094,8 +1095,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; } @@ -1108,11 +1110,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; } /* @@ -1254,7 +1260,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); } /* diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 91cab5f669d2..e29948b8fcab 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -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 && diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index cd9f95e5044e..9ecea45da4ca 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -604,38 +604,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 +744,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,8 +765,13 @@ 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; } @@ -942,7 +945,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 +997,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", @@ -1425,6 +1428,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 +1450,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 +1601,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 +1618,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. diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h index 00275afc524d..948bbcf9aef3 100644 --- a/tools/perf/util/record.h +++ b/tools/perf/util/record.h @@ -44,6 +44,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; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 6cc32f5ec043..f07b8ecb91bc 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; 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 6822e4ffe224..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); diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 081c4a5113c6..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 { diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a8f80e427674..2764863212b1 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; @@ -1637,7 +1617,7 @@ int dso__load(struct dso *dso, struct map *map) goto out; } - if (map->groups && map->groups->machine) + if (map->groups) machine = map->groups->machine; else machine = NULL; @@ -2371,25 +2351,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 cc2a89b99d3d..c3bd16d75d5d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -106,18 +106,6 @@ struct ref_reloc_sym { u64 unrelocated_addr; }; -struct block_info { - struct symbol *sym; - u64 start; - u64 end; - u64 cycles; - u64 cycles_aggr; - s64 cycles_spark[NUM_SPARKS]; - int num; - int num_aggr; - refcount_t refcnt; -}; - struct addr_location { struct machine *machine; struct thread *thread; @@ -291,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..cfa3c9f67141 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)) 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/util.c b/tools/perf/util/util.c index ae56c766eda1..969ae560dad9 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -182,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; |

