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

perf lock contention: Add -Y/--type-filter option

The -Y/--type-filter option is to filter the result for specific lock
types only. It can accept comma-separated values. Note that it would
accept type names like one in the output. spinlock, mutex, rwsem:R and
so on.

For RW-variant lock types, it converts the name to the both variants.
In other words, "rwsem" is same as "rwsem:R,rwsem:W". Also note that
"mutex" has two different encoding - one for sleeping wait, another for
optimistic spinning. Add "mutex-spin" entry for the lock_type_table so
that we can add it for "mutex" under the table.

$ sudo ./perf lock record -a -- ./perf bench sched messaging

$ sudo ./perf lock con -E 5 -Y spinlock
contended total wait max wait avg wait type caller

802 1.26 ms 11.73 us 1.58 us spinlock __wake_up_common_lock+0x62
13 787.16 us 105.44 us 60.55 us spinlock remove_wait_queue+0x14
12 612.96 us 78.70 us 51.08 us spinlock prepare_to_wait+0x27
114 340.68 us 12.61 us 2.99 us spinlock try_to_wake_up+0x1f5
83 226.38 us 9.15 us 2.73 us spinlock folio_lruvec_lock_irqsave+0x5e

Committer notes:

Make get_type_flag() return UINT_MAX for error instad of -1UL, as that
function returns 'unsigned int' and we store the value on a 'unsigned
int' 'flags' variable which makes clang unhappy:

35 98.23 fedora:37 : FAIL clang version 15.0.6 (Fedora 15.0.6-1.fc37)
builtin-lock.c:2012:14: error: result of comparison of constant 18446744073709551615 with expression of type 'unsigned int' is always true [-Werror,-Wtautological-constant-out-of-range-compare]
if (flags != -1UL) {
~~~~~ ^ ~~~~
builtin-lock.c:2021:14: error: result of comparison of constant 18446744073709551615 with expression of type 'unsigned int' is always true [-Werror,-Wtautological-constant-out-of-range-compare]
if (flags != -1UL) {
~~~~~ ^ ~~~~
builtin-lock.c:2037:14: error: result of comparison of constant 18446744073709551615 with expression of type 'unsigned int' is always true [-Werror,-Wtautological-constant-out-of-range-compare]
if (flags != -1UL) {
~~~~~ ^ ~~~~
3 errors generated.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
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-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
b4a7eff9 59119c09

+136 -8
+17 -6
tools/perf/Documentation/perf-lock.txt
··· 143 143 System-wide collection from all CPUs. 144 144 145 145 -C:: 146 - --cpu:: 146 + --cpu=<value>:: 147 147 Collect samples only on the list of CPUs provided. Multiple CPUs can be 148 148 provided as a comma-separated list with no space: 0,1. Ranges of CPUs 149 149 are specified with -: 0-2. Default is to monitor all CPUs. 150 150 151 151 -p:: 152 - --pid=:: 152 + --pid=<value>:: 153 153 Record events on existing process ID (comma separated list). 154 154 155 - --tid=:: 155 + --tid=<value>:: 156 156 Record events on existing thread ID (comma separated list). 157 157 158 - --map-nr-entries:: 158 + --map-nr-entries=<value>:: 159 159 Maximum number of BPF map entries (default: 10240). 160 160 161 - --max-stack:: 161 + --max-stack=<value>:: 162 162 Maximum stack depth when collecting lock contention (default: 8). 163 163 164 - --stack-skip 164 + --stack-skip=<value>:: 165 165 Number of stack depth to skip when finding a lock caller (default: 3). 166 166 167 167 -E:: ··· 171 171 -l:: 172 172 --lock-addr:: 173 173 Show lock contention stat by address 174 + 175 + -Y:: 176 + --type-filter=<value>:: 177 + Show lock contention only for given lock types (comma separated list). 178 + Available values are: 179 + semaphore, spinlock, rwlock, rwlock:R, rwlock:W, rwsem, rwsem:R, rwsem:W, 180 + rtmutex, rwlock-rt, rwlock-rt:R, rwlock-rt:W, pcpu-sem, pcpu-sem:R, pcpu-sem:W, 181 + mutex 182 + 183 + Note that RW-variant of locks have :R and :W suffix. Names without the 184 + suffix are shortcuts for the both variants. Ex) rwsem = rwsem:R + rwsem:W. 174 185 175 186 176 187 SEE ALSO
+114 -2
tools/perf/builtin-lock.c
··· 63 63 static int stack_skip = CONTENTION_STACK_SKIP; 64 64 static int print_nr_entries = INT_MAX / 2; 65 65 66 + static struct lock_filter filters; 67 + 66 68 static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR; 67 69 68 70 static struct thread_stat *thread_stat_find(u32 tid) ··· 992 990 struct thread_stat *ts; 993 991 struct lock_seq_stat *seq; 994 992 u64 addr = evsel__intval(evsel, sample, "lock_addr"); 993 + unsigned int flags = evsel__intval(evsel, sample, "flags"); 995 994 u64 key; 996 - int ret; 995 + int i, ret; 997 996 998 997 ret = get_key_by_aggr_mode(&key, addr, evsel, sample); 999 998 if (ret < 0) ··· 1004 1001 if (!ls) { 1005 1002 char buf[128]; 1006 1003 const char *name = ""; 1007 - unsigned int flags = evsel__intval(evsel, sample, "flags"); 1008 1004 struct machine *machine = &session->machines.host; 1009 1005 struct map *kmap; 1010 1006 struct symbol *sym; ··· 1036 1034 if (ls->callstack == NULL) 1037 1035 return -ENOMEM; 1038 1036 } 1037 + } 1038 + 1039 + if (filters.nr_types) { 1040 + bool found = false; 1041 + 1042 + for (i = 0; i < filters.nr_types; i++) { 1043 + if (flags == filters.types[i]) { 1044 + found = true; 1045 + break; 1046 + } 1047 + } 1048 + 1049 + if (!found) 1050 + return 0; 1039 1051 } 1040 1052 1041 1053 ts = thread_stat_findnew(sample->tid); ··· 1470 1454 { LCB_F_PERCPU | LCB_F_WRITE, "pcpu-sem:W" }, 1471 1455 { LCB_F_MUTEX, "mutex" }, 1472 1456 { LCB_F_MUTEX | LCB_F_SPIN, "mutex" }, 1457 + /* alias for get_type_flag() */ 1458 + { LCB_F_MUTEX | LCB_F_SPIN, "mutex-spin" }, 1473 1459 }; 1474 1460 1475 1461 static const char *get_type_str(unsigned int flags) ··· 1481 1463 return lock_type_table[i].name; 1482 1464 } 1483 1465 return "unknown"; 1466 + } 1467 + 1468 + static unsigned int get_type_flag(const char *str) 1469 + { 1470 + for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) { 1471 + if (!strcmp(lock_type_table[i].name, str)) 1472 + return lock_type_table[i].flags; 1473 + } 1474 + return UINT_MAX; 1475 + } 1476 + 1477 + static void lock_filter_finish(void) 1478 + { 1479 + zfree(&filters.types); 1480 + filters.nr_types = 0; 1484 1481 } 1485 1482 1486 1483 static void sort_contention_result(void) ··· 1539 1506 total += use_bpf ? st->nr_contended : 1; 1540 1507 if (st->broken) 1541 1508 bad++; 1509 + 1510 + if (!st->wait_time_total) 1511 + continue; 1542 1512 1543 1513 list_for_each_entry(key, &lock_keys, list) { 1544 1514 key->print(key, st); ··· 1789 1753 print_contention_result(&con); 1790 1754 1791 1755 out_delete: 1756 + lock_filter_finish(); 1792 1757 evlist__delete(con.evlist); 1793 1758 lock_contention_finish(); 1794 1759 perf_session__delete(session); ··· 1921 1884 return 0; 1922 1885 } 1923 1886 1887 + static bool add_lock_type(unsigned int flags) 1888 + { 1889 + unsigned int *tmp; 1890 + 1891 + tmp = realloc(filters.types, (filters.nr_types + 1) * sizeof(*filters.types)); 1892 + if (tmp == NULL) 1893 + return false; 1894 + 1895 + tmp[filters.nr_types++] = flags; 1896 + filters.types = tmp; 1897 + return true; 1898 + } 1899 + 1900 + static int parse_lock_type(const struct option *opt __maybe_unused, const char *str, 1901 + int unset __maybe_unused) 1902 + { 1903 + char *s, *tmp, *tok; 1904 + int ret = 0; 1905 + 1906 + s = strdup(str); 1907 + if (s == NULL) 1908 + return -1; 1909 + 1910 + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { 1911 + unsigned int flags = get_type_flag(tok); 1912 + 1913 + if (flags == -1U) { 1914 + char buf[32]; 1915 + 1916 + if (strchr(tok, ':')) 1917 + continue; 1918 + 1919 + /* try :R and :W suffixes for rwlock, rwsem, ... */ 1920 + scnprintf(buf, sizeof(buf), "%s:R", tok); 1921 + flags = get_type_flag(buf); 1922 + if (flags != UINT_MAX) { 1923 + if (!add_lock_type(flags)) { 1924 + ret = -1; 1925 + break; 1926 + } 1927 + } 1928 + 1929 + scnprintf(buf, sizeof(buf), "%s:W", tok); 1930 + flags = get_type_flag(buf); 1931 + if (flags != UINT_MAX) { 1932 + if (!add_lock_type(flags)) { 1933 + ret = -1; 1934 + break; 1935 + } 1936 + } 1937 + continue; 1938 + } 1939 + 1940 + if (!add_lock_type(flags)) { 1941 + ret = -1; 1942 + break; 1943 + } 1944 + 1945 + if (!strcmp(tok, "mutex")) { 1946 + flags = get_type_flag("mutex-spin"); 1947 + if (flags != UINT_MAX) { 1948 + if (!add_lock_type(flags)) { 1949 + ret = -1; 1950 + break; 1951 + } 1952 + } 1953 + } 1954 + } 1955 + 1956 + free(s); 1957 + return ret; 1958 + } 1959 + 1924 1960 int cmd_lock(int argc, const char **argv) 1925 1961 { 1926 1962 const struct option lock_options[] = { ··· 2057 1947 "Default: " __stringify(CONTENTION_STACK_SKIP)), 2058 1948 OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"), 2059 1949 OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"), 1950 + OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS", 1951 + "Filter specific type of locks", parse_lock_type), 2060 1952 OPT_PARENT(lock_options) 2061 1953 }; 2062 1954
+5
tools/perf/util/lock-contention.h
··· 5 5 #include <linux/list.h> 6 6 #include <linux/rbtree.h> 7 7 8 + struct lock_filter { 9 + int nr_types; 10 + unsigned int *types; 11 + }; 12 + 8 13 struct lock_stat { 9 14 struct hlist_node hash_entry; 10 15 struct rb_node rb; /* used for sorting */