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>
This commit is contained in:
parent
529772c4df
commit
511e19b9e2
|
@ -183,6 +183,10 @@ CONTENTION OPTIONS
|
||||||
Note that RW-variant of locks have :R and :W suffix. Names without the
|
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.
|
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
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/hash.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");
|
unsigned int flags = evsel__intval(evsel, sample, "flags");
|
||||||
u64 key;
|
u64 key;
|
||||||
int i, ret;
|
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);
|
ret = get_key_by_aggr_mode(&key, addr, evsel, sample);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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);
|
ls = lock_stat_find(key);
|
||||||
if (!ls) {
|
if (!ls) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
const char *name = "";
|
const char *name = "";
|
||||||
struct machine *machine = &session->machines.host;
|
|
||||||
struct map *kmap;
|
|
||||||
struct symbol *sym;
|
|
||||||
|
|
||||||
switch (aggr_mode) {
|
switch (aggr_mode) {
|
||||||
case LOCK_AGGR_ADDR:
|
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);
|
sym = machine__find_kernel_symbol(machine, key, &kmap);
|
||||||
if (sym)
|
if (sym)
|
||||||
name = sym->name;
|
name = sym->name;
|
||||||
|
@ -1052,6 +1081,20 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
|
||||||
return 0;
|
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);
|
ts = thread_stat_findnew(sample->tid);
|
||||||
if (!ts)
|
if (!ts)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1496,6 +1539,15 @@ static void lock_filter_finish(void)
|
||||||
{
|
{
|
||||||
zfree(&filters.types);
|
zfree(&filters.types);
|
||||||
filters.nr_types = 0;
|
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)
|
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;
|
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)
|
int cmd_lock(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const struct option lock_options[] = {
|
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_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),
|
||||||
OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS",
|
OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS",
|
||||||
"Filter specific type of locks", parse_lock_type),
|
"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)
|
OPT_PARENT(lock_options)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
|
|
||||||
struct lock_filter {
|
struct lock_filter {
|
||||||
int nr_types;
|
int nr_types;
|
||||||
|
int nr_addrs;
|
||||||
|
int nr_syms;
|
||||||
unsigned int *types;
|
unsigned int *types;
|
||||||
|
unsigned long *addrs;
|
||||||
|
char **syms;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lock_stat {
|
struct lock_stat {
|
||||||
|
|
Loading…
Reference in New Issue