Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
511e19b9 529772c4

+142 -6
+4
tools/perf/Documentation/perf-lock.txt
··· 183 183 Note that RW-variant of locks have :R and :W suffix. Names without the 184 184 suffix are shortcuts for the both variants. Ex) rwsem = rwsem:R + rwsem:W. 185 185 186 + -L:: 187 + --lock-filter=<value>:: 188 + Show lock contention only for given lock addresses or names (comma separated list). 189 + 186 190 187 191 SEE ALSO 188 192 --------
+134 -6
tools/perf/builtin-lock.c
··· 32 32 #include <semaphore.h> 33 33 #include <math.h> 34 34 #include <limits.h> 35 + #include <ctype.h> 35 36 36 37 #include <linux/list.h> 37 38 #include <linux/hash.h> ··· 996 995 unsigned int flags = evsel__intval(evsel, sample, "flags"); 997 996 u64 key; 998 997 int i, ret; 998 + static bool kmap_loaded; 999 + struct machine *machine = &session->machines.host; 1000 + struct map *kmap; 1001 + struct symbol *sym; 999 1002 1000 1003 ret = get_key_by_aggr_mode(&key, addr, evsel, sample); 1001 1004 if (ret < 0) 1002 1005 return ret; 1003 1006 1007 + if (!kmap_loaded) { 1008 + unsigned long *addrs; 1009 + 1010 + /* make sure it loads the kernel map to find lock symbols */ 1011 + map__load(machine__kernel_map(machine)); 1012 + kmap_loaded = true; 1013 + 1014 + /* convert (kernel) symbols to addresses */ 1015 + for (i = 0; i < filters.nr_syms; i++) { 1016 + sym = machine__find_kernel_symbol_by_name(machine, 1017 + filters.syms[i], 1018 + &kmap); 1019 + if (sym == NULL) { 1020 + pr_warning("ignore unknown symbol: %s\n", 1021 + filters.syms[i]); 1022 + continue; 1023 + } 1024 + 1025 + addrs = realloc(filters.addrs, 1026 + (filters.nr_addrs + 1) * sizeof(*addrs)); 1027 + if (addrs == NULL) { 1028 + pr_warning("memory allocation failure\n"); 1029 + return -ENOMEM; 1030 + } 1031 + 1032 + addrs[filters.nr_addrs++] = kmap->unmap_ip(kmap, sym->start); 1033 + filters.addrs = addrs; 1034 + } 1035 + } 1036 + 1004 1037 ls = lock_stat_find(key); 1005 1038 if (!ls) { 1006 1039 char buf[128]; 1007 1040 const char *name = ""; 1008 - struct machine *machine = &session->machines.host; 1009 - struct map *kmap; 1010 - struct symbol *sym; 1011 1041 1012 1042 switch (aggr_mode) { 1013 1043 case LOCK_AGGR_ADDR: 1014 - /* make sure it loads the kernel map to find lock symbols */ 1015 - map__load(machine__kernel_map(machine)); 1016 - 1017 1044 sym = machine__find_kernel_symbol(machine, key, &kmap); 1018 1045 if (sym) 1019 1046 name = sym->name; ··· 1072 1043 1073 1044 for (i = 0; i < filters.nr_types; i++) { 1074 1045 if (flags == filters.types[i]) { 1046 + found = true; 1047 + break; 1048 + } 1049 + } 1050 + 1051 + if (!found) 1052 + return 0; 1053 + } 1054 + 1055 + if (filters.nr_addrs) { 1056 + bool found = false; 1057 + 1058 + for (i = 0; i < filters.nr_addrs; i++) { 1059 + if (addr == filters.addrs[i]) { 1075 1060 found = true; 1076 1061 break; 1077 1062 } ··· 1539 1496 { 1540 1497 zfree(&filters.types); 1541 1498 filters.nr_types = 0; 1499 + 1500 + zfree(&filters.addrs); 1501 + filters.nr_addrs = 0; 1502 + 1503 + for (int i = 0; i < filters.nr_syms; i++) 1504 + free(filters.syms[i]); 1505 + 1506 + zfree(&filters.syms); 1507 + filters.nr_syms = 0; 1542 1508 } 1543 1509 1544 1510 static void sort_contention_result(void) ··· 2047 1995 return ret; 2048 1996 } 2049 1997 1998 + static bool add_lock_addr(unsigned long addr) 1999 + { 2000 + unsigned long *tmp; 2001 + 2002 + tmp = realloc(filters.addrs, (filters.nr_addrs + 1) * sizeof(*filters.addrs)); 2003 + if (tmp == NULL) { 2004 + pr_err("Memory allocation failure\n"); 2005 + return false; 2006 + } 2007 + 2008 + tmp[filters.nr_addrs++] = addr; 2009 + filters.addrs = tmp; 2010 + return true; 2011 + } 2012 + 2013 + static bool add_lock_sym(char *name) 2014 + { 2015 + char **tmp; 2016 + char *sym = strdup(name); 2017 + 2018 + if (sym == NULL) { 2019 + pr_err("Memory allocation failure\n"); 2020 + return false; 2021 + } 2022 + 2023 + tmp = realloc(filters.syms, (filters.nr_syms + 1) * sizeof(*filters.syms)); 2024 + if (tmp == NULL) { 2025 + pr_err("Memory allocation failure\n"); 2026 + free(sym); 2027 + return false; 2028 + } 2029 + 2030 + tmp[filters.nr_syms++] = sym; 2031 + filters.syms = tmp; 2032 + return true; 2033 + } 2034 + 2035 + static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str, 2036 + int unset __maybe_unused) 2037 + { 2038 + char *s, *tmp, *tok; 2039 + int ret = 0; 2040 + u64 addr; 2041 + 2042 + s = strdup(str); 2043 + if (s == NULL) 2044 + return -1; 2045 + 2046 + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { 2047 + char *end; 2048 + 2049 + addr = strtoul(tok, &end, 16); 2050 + if (*end == '\0') { 2051 + if (!add_lock_addr(addr)) { 2052 + ret = -1; 2053 + break; 2054 + } 2055 + continue; 2056 + } 2057 + 2058 + /* 2059 + * At this moment, we don't have kernel symbols. Save the symbols 2060 + * in a separate list and resolve them to addresses later. 2061 + */ 2062 + if (!add_lock_sym(tok)) { 2063 + ret = -1; 2064 + break; 2065 + } 2066 + } 2067 + 2068 + free(s); 2069 + return ret; 2070 + } 2071 + 2050 2072 int cmd_lock(int argc, const char **argv) 2051 2073 { 2052 2074 const struct option lock_options[] = { ··· 2186 2060 OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"), 2187 2061 OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS", 2188 2062 "Filter specific type of locks", parse_lock_type), 2063 + OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES", 2064 + "Filter specific address/symbol of locks", parse_lock_addr), 2189 2065 OPT_PARENT(lock_options) 2190 2066 }; 2191 2067
+4
tools/perf/util/lock-contention.h
··· 7 7 8 8 struct lock_filter { 9 9 int nr_types; 10 + int nr_addrs; 11 + int nr_syms; 10 12 unsigned int *types; 13 + unsigned long *addrs; 14 + char **syms; 11 15 }; 12 16 13 17 struct lock_stat {