diff options
author | Namhyung Kim <namhyung@kernel.org> | 2022-12-19 23:17:30 +0300 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2022-12-21 20:52:39 +0300 |
commit | 511e19b9e2112463c33a744ecb8a798056074408 (patch) | |
tree | bcee7bbdd87e3198fe76094fbbf14e228c4eff68 /tools/perf | |
parent | 529772c4df286159fea453957a6052e20b90e8a8 (diff) | |
download | linux-511e19b9e2112463c33a744ecb8a798056074408.tar.xz |
perf lock contention: Add -L/--lock-filter option
The -L/--lock-filter option is to filter only given locks. The locks
can be specified by address or name (if exists).
$ sudo ./perf lock record -a sleep 1
$ sudo ./perf lock con -l
contended total wait max wait avg wait address symbol
57 1.11 ms 42.83 us 19.54 us ffff9f4140059000
15 280.88 us 23.51 us 18.73 us ffffffff9d007a40 jiffies_lock
1 20.49 us 20.49 us 20.49 us ffffffff9d0d50c0 rcu_state
1 9.02 us 9.02 us 9.02 us ffff9f41759e9ba0
$ sudo ./perf lock con -L jiffies_lock,rcu_state
contended total wait max wait avg wait type caller
15 280.88 us 23.51 us 18.73 us spinlock tick_sched_do_timer+0x93
1 20.49 us 20.49 us 20.49 us spinlock __softirqentry_text_start+0xeb
$ sudo ./perf lock con -L ffff9f4140059000
contended total wait max wait avg wait type caller
38 779.40 us 42.83 us 20.51 us spinlock worker_thread+0x50
11 216.30 us 39.87 us 19.66 us spinlock queue_work_on+0x39
8 118.13 us 20.51 us 14.77 us spinlock kthread+0xe5
Committer testing:
# uname -a
Linux quaco 6.0.12-200.fc36.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 8 17:15:53 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
# perf lock record
^C[ perf record: Woken up 1 times to write data ]
# perf lock con -L jiffies_lock,rcu_state
contended total wait max wait avg wait type caller
# perf lock con
contended total wait max wait avg wait type caller
1 9.06 us 9.06 us 9.06 us spinlock call_timer_fn+0x24
# perf lock con -L call
ignore unknown symbol: call
contended total wait max wait avg wait type caller
1 9.06 us 9.06 us 9.06 us spinlock call_timer_fn+0x24
#
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Blake Jones <blakejones@google.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <song@kernel.org>
Cc: bpf@vger.kernel.org
Link: https://lore.kernel.org/r/20221219201732.460111-5-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-lock.txt | 4 | ||||
-rw-r--r-- | tools/perf/builtin-lock.c | 140 | ||||
-rw-r--r-- | tools/perf/util/lock-contention.h | 4 |
3 files changed, 142 insertions, 6 deletions
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index dea04ad5c28e..0f9f720e599d 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -183,6 +183,10 @@ CONTENTION OPTIONS Note that RW-variant of locks have :R and :W suffix. Names without the suffix are shortcuts for the both variants. Ex) rwsem = rwsem:R + rwsem:W. +-L:: +--lock-filter=<value>:: + Show lock contention only for given lock addresses or names (comma separated list). + SEE ALSO -------- diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 171b0fb5c974..718b82bfcdff 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -32,6 +32,7 @@ #include <semaphore.h> #include <math.h> #include <limits.h> +#include <ctype.h> #include <linux/list.h> #include <linux/hash.h> @@ -995,24 +996,52 @@ static int report_lock_contention_begin_event(struct evsel *evsel, unsigned int flags = evsel__intval(evsel, sample, "flags"); u64 key; int i, ret; + static bool kmap_loaded; + struct machine *machine = &session->machines.host; + struct map *kmap; + struct symbol *sym; ret = get_key_by_aggr_mode(&key, addr, evsel, sample); if (ret < 0) return ret; + if (!kmap_loaded) { + unsigned long *addrs; + + /* make sure it loads the kernel map to find lock symbols */ + map__load(machine__kernel_map(machine)); + kmap_loaded = true; + + /* convert (kernel) symbols to addresses */ + for (i = 0; i < filters.nr_syms; i++) { + sym = machine__find_kernel_symbol_by_name(machine, + filters.syms[i], + &kmap); + if (sym == NULL) { + pr_warning("ignore unknown symbol: %s\n", + filters.syms[i]); + continue; + } + + addrs = realloc(filters.addrs, + (filters.nr_addrs + 1) * sizeof(*addrs)); + if (addrs == NULL) { + pr_warning("memory allocation failure\n"); + return -ENOMEM; + } + + addrs[filters.nr_addrs++] = kmap->unmap_ip(kmap, sym->start); + filters.addrs = addrs; + } + } + ls = lock_stat_find(key); if (!ls) { char buf[128]; const char *name = ""; - struct machine *machine = &session->machines.host; - struct map *kmap; - struct symbol *sym; switch (aggr_mode) { case LOCK_AGGR_ADDR: - /* make sure it loads the kernel map to find lock symbols */ - map__load(machine__kernel_map(machine)); - sym = machine__find_kernel_symbol(machine, key, &kmap); if (sym) name = sym->name; @@ -1052,6 +1081,20 @@ static int report_lock_contention_begin_event(struct evsel *evsel, return 0; } + if (filters.nr_addrs) { + bool found = false; + + for (i = 0; i < filters.nr_addrs; i++) { + if (addr == filters.addrs[i]) { + found = true; + break; + } + } + + if (!found) + return 0; + } + ts = thread_stat_findnew(sample->tid); if (!ts) return -ENOMEM; @@ -1496,6 +1539,15 @@ static void lock_filter_finish(void) { zfree(&filters.types); filters.nr_types = 0; + + zfree(&filters.addrs); + filters.nr_addrs = 0; + + for (int i = 0; i < filters.nr_syms; i++) + free(filters.syms[i]); + + zfree(&filters.syms); + filters.nr_syms = 0; } static void sort_contention_result(void) @@ -1995,6 +2047,80 @@ static int parse_lock_type(const struct option *opt __maybe_unused, const char * return ret; } +static bool add_lock_addr(unsigned long addr) +{ + unsigned long *tmp; + + tmp = realloc(filters.addrs, (filters.nr_addrs + 1) * sizeof(*filters.addrs)); + if (tmp == NULL) { + pr_err("Memory allocation failure\n"); + return false; + } + + tmp[filters.nr_addrs++] = addr; + filters.addrs = tmp; + return true; +} + +static bool add_lock_sym(char *name) +{ + char **tmp; + char *sym = strdup(name); + + if (sym == NULL) { + pr_err("Memory allocation failure\n"); + return false; + } + + tmp = realloc(filters.syms, (filters.nr_syms + 1) * sizeof(*filters.syms)); + if (tmp == NULL) { + pr_err("Memory allocation failure\n"); + free(sym); + return false; + } + + tmp[filters.nr_syms++] = sym; + filters.syms = tmp; + return true; +} + +static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str, + int unset __maybe_unused) +{ + char *s, *tmp, *tok; + int ret = 0; + u64 addr; + + s = strdup(str); + if (s == NULL) + return -1; + + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { + char *end; + + addr = strtoul(tok, &end, 16); + if (*end == '\0') { + if (!add_lock_addr(addr)) { + ret = -1; + break; + } + continue; + } + + /* + * At this moment, we don't have kernel symbols. Save the symbols + * in a separate list and resolve them to addresses later. + */ + if (!add_lock_sym(tok)) { + ret = -1; + break; + } + } + + free(s); + return ret; +} + int cmd_lock(int argc, const char **argv) { const struct option lock_options[] = { @@ -2060,6 +2186,8 @@ int cmd_lock(int argc, const char **argv) OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"), OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS", "Filter specific type of locks", parse_lock_type), + OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES", + "Filter specific address/symbol of locks", parse_lock_addr), OPT_PARENT(lock_options) }; diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-contention.h index dc621386a16b..b99e83fccf5c 100644 --- a/tools/perf/util/lock-contention.h +++ b/tools/perf/util/lock-contention.h @@ -7,7 +7,11 @@ struct lock_filter { int nr_types; + int nr_addrs; + int nr_syms; unsigned int *types; + unsigned long *addrs; + char **syms; }; struct lock_stat { |