Commit 4d1792d0 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo

perf lock contention: Add --lock-cgroup option

The --lock-cgroup option shows lock contention stats break down by
cgroups.

Add LOCK_AGGR_CGROUP mode and use it instead of use_cgroup field.

  $ sudo ./perf lock con -ab --lock-cgroup sleep 1
   contended   total wait     max wait     avg wait   cgroup

           8     15.70 us      6.34 us      1.96 us   /
           2      1.48 us       747 ns       738 ns   /user.slice/.../app.slice/app-gnome-google\x2dchrome-6442.scope
           1       848 ns       848 ns       848 ns   /user.slice/.../session.slice/org.gnome.Shell@x11.service
           1       220 ns       220 ns       220 ns   /user.slice/.../session.slice/pipewire-pulse.service

For now, the cgroup mode only works with BPF (-b).

Committer notes:

Remove -g as it is used in the other tools with a clear meaning of
collect/show callchains. As agreed with Namhyung off list.
Reviewed-by: default avatarIan Rogers <irogers@google.com>
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Hao Luo <haoluo@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/20230906174903.346486-4-namhyung@kernel.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent d0c502e4
...@@ -208,6 +208,9 @@ CONTENTION OPTIONS ...@@ -208,6 +208,9 @@ CONTENTION OPTIONS
Show results using a CSV-style output to make it easy to import directly Show results using a CSV-style output to make it easy to import directly
into spreadsheets. Columns are separated by the string specified in SEP. into spreadsheets. Columns are separated by the string specified in SEP.
--lock-cgroup::
Show lock contention stat by cgroup. Requires --use-bpf.
SEE ALSO SEE ALSO
-------- --------
......
...@@ -60,6 +60,7 @@ static bool combine_locks; ...@@ -60,6 +60,7 @@ static bool combine_locks;
static bool show_thread_stats; static bool show_thread_stats;
static bool show_lock_addrs; static bool show_lock_addrs;
static bool show_lock_owner; static bool show_lock_owner;
static bool show_lock_cgroups;
static bool use_bpf; static bool use_bpf;
static unsigned long bpf_map_entries = MAX_ENTRIES; static unsigned long bpf_map_entries = MAX_ENTRIES;
static int max_stack_depth = CONTENTION_STACK_DEPTH; static int max_stack_depth = CONTENTION_STACK_DEPTH;
...@@ -619,6 +620,7 @@ static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid) ...@@ -619,6 +620,7 @@ static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid)
*key = tid; *key = tid;
break; break;
case LOCK_AGGR_CALLER: case LOCK_AGGR_CALLER:
case LOCK_AGGR_CGROUP:
default: default:
pr_err("Invalid aggregation mode: %d\n", aggr_mode); pr_err("Invalid aggregation mode: %d\n", aggr_mode);
return -EINVAL; return -EINVAL;
...@@ -1103,6 +1105,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, ...@@ -1103,6 +1105,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0) if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
name = "Unknown"; name = "Unknown";
break; break;
case LOCK_AGGR_CGROUP:
case LOCK_AGGR_TASK: case LOCK_AGGR_TASK:
default: default:
break; break;
...@@ -1653,6 +1656,9 @@ static void print_header_stdio(void) ...@@ -1653,6 +1656,9 @@ static void print_header_stdio(void)
case LOCK_AGGR_ADDR: case LOCK_AGGR_ADDR:
fprintf(lock_output, " %16s %s\n\n", "address", "symbol"); fprintf(lock_output, " %16s %s\n\n", "address", "symbol");
break; break;
case LOCK_AGGR_CGROUP:
fprintf(lock_output, " %s\n\n", "cgroup");
break;
default: default:
break; break;
} }
...@@ -1680,6 +1686,9 @@ static void print_header_csv(const char *sep) ...@@ -1680,6 +1686,9 @@ static void print_header_csv(const char *sep)
case LOCK_AGGR_ADDR: case LOCK_AGGR_ADDR:
fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type"); fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type");
break; break;
case LOCK_AGGR_CGROUP:
fprintf(lock_output, "%s\n", "cgroup");
break;
default: default:
break; break;
} }
...@@ -1720,6 +1729,9 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat ...@@ -1720,6 +1729,9 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr, fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr,
st->name, get_type_name(st->flags)); st->name, get_type_name(st->flags));
break; break;
case LOCK_AGGR_CGROUP:
fprintf(lock_output, " %s\n", st->name);
break;
default: default:
break; break;
} }
...@@ -1770,6 +1782,9 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s ...@@ -1770,6 +1782,9 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep, fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep,
st->name, sep, get_type_name(st->flags)); st->name, sep, get_type_name(st->flags));
break; break;
case LOCK_AGGR_CGROUP:
fprintf(lock_output, "%s\n",st->name);
break;
default: default:
break; break;
} }
...@@ -1999,6 +2014,27 @@ static int check_lock_contention_options(const struct option *options, ...@@ -1999,6 +2014,27 @@ static int check_lock_contention_options(const struct option *options,
return -1; return -1;
} }
if (show_lock_cgroups && !use_bpf) {
pr_err("Cgroups are available only with BPF\n");
parse_options_usage(usage, options, "lock-cgroup", 0);
parse_options_usage(NULL, options, "use-bpf", 0);
return -1;
}
if (show_lock_cgroups && show_lock_addrs) {
pr_err("Cannot use cgroup and addr mode together\n");
parse_options_usage(usage, options, "lock-cgroup", 0);
parse_options_usage(NULL, options, "lock-addr", 0);
return -1;
}
if (show_lock_cgroups && show_thread_stats) {
pr_err("Cannot use cgroup and thread mode together\n");
parse_options_usage(usage, options, "lock-cgroup", 0);
parse_options_usage(NULL, options, "threads", 0);
return -1;
}
if (symbol_conf.field_sep) { if (symbol_conf.field_sep) {
if (strstr(symbol_conf.field_sep, ":") || /* part of type flags */ if (strstr(symbol_conf.field_sep, ":") || /* part of type flags */
strstr(symbol_conf.field_sep, "+") || /* part of caller offset */ strstr(symbol_conf.field_sep, "+") || /* part of caller offset */
...@@ -2060,7 +2096,8 @@ static int __cmd_contention(int argc, const char **argv) ...@@ -2060,7 +2096,8 @@ static int __cmd_contention(int argc, const char **argv)
con.machine = &session->machines.host; con.machine = &session->machines.host;
con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK : con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :
show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER; show_lock_addrs ? LOCK_AGGR_ADDR :
show_lock_cgroups ? LOCK_AGGR_CGROUP : LOCK_AGGR_CALLER;
if (con.aggr_mode == LOCK_AGGR_CALLER) if (con.aggr_mode == LOCK_AGGR_CALLER)
con.save_callstack = true; con.save_callstack = true;
...@@ -2524,6 +2561,7 @@ int cmd_lock(int argc, const char **argv) ...@@ -2524,6 +2561,7 @@ int cmd_lock(int argc, const char **argv)
OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"), OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),
OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, "separator", OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, "separator",
"print result in CSV format with custom separator"), "print result in CSV format with custom separator"),
OPT_BOOLEAN(0, "lock-cgroup", &show_lock_cgroups, "show lock stats by cgroup"),
OPT_PARENT(lock_options) OPT_PARENT(lock_options)
}; };
......
...@@ -152,7 +152,10 @@ int lock_contention_prepare(struct lock_contention *con) ...@@ -152,7 +152,10 @@ int lock_contention_prepare(struct lock_contention *con)
skel->bss->needs_callstack = con->save_callstack; skel->bss->needs_callstack = con->save_callstack;
skel->bss->lock_owner = con->owner; skel->bss->lock_owner = con->owner;
if (con->use_cgroup) { if (con->aggr_mode == LOCK_AGGR_CGROUP) {
if (cgroup_is_v2("perf_event"))
skel->bss->use_cgroup_v2 = 1;
read_all_cgroups(&con->cgroups); read_all_cgroups(&con->cgroups);
} }
...@@ -214,12 +217,12 @@ static const char *lock_contention_get_name(struct lock_contention *con, ...@@ -214,12 +217,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
return "siglock"; return "siglock";
/* global locks with symbols */ /* global locks with symbols */
sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap); sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
if (sym) if (sym)
return sym->name; return sym->name;
/* try semi-global locks collected separately */ /* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr, &flags)) { if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
if (flags == LOCK_CLASS_RQLOCK) if (flags == LOCK_CLASS_RQLOCK)
return "rq_lock"; return "rq_lock";
} }
...@@ -227,8 +230,8 @@ static const char *lock_contention_get_name(struct lock_contention *con, ...@@ -227,8 +230,8 @@ static const char *lock_contention_get_name(struct lock_contention *con,
return ""; return "";
} }
if (con->use_cgroup) { if (con->aggr_mode == LOCK_AGGR_CGROUP) {
u64 cgrp_id = key->lock_addr; u64 cgrp_id = key->lock_addr_or_cgroup;
struct cgroup *cgrp = __cgroup__find(&con->cgroups, cgrp_id); struct cgroup *cgrp = __cgroup__find(&con->cgroups, cgrp_id);
if (cgrp) if (cgrp)
...@@ -329,7 +332,8 @@ int lock_contention_read(struct lock_contention *con) ...@@ -329,7 +332,8 @@ int lock_contention_read(struct lock_contention *con)
ls_key = key.pid; ls_key = key.pid;
break; break;
case LOCK_AGGR_ADDR: case LOCK_AGGR_ADDR:
ls_key = key.lock_addr; case LOCK_AGGR_CGROUP:
ls_key = key.lock_addr_or_cgroup;
break; break;
default: default:
goto next; goto next;
......
...@@ -118,6 +118,9 @@ int needs_callstack; ...@@ -118,6 +118,9 @@ int needs_callstack;
int stack_skip; int stack_skip;
int lock_owner; int lock_owner;
int use_cgroup_v2;
int perf_subsys_id = -1;
/* determine the key of lock stat */ /* determine the key of lock stat */
int aggr_mode; int aggr_mode;
...@@ -130,6 +133,29 @@ int data_fail; ...@@ -130,6 +133,29 @@ int data_fail;
int task_map_full; int task_map_full;
int data_map_full; int data_map_full;
static inline __u64 get_current_cgroup_id(void)
{
struct task_struct *task;
struct cgroup *cgrp;
if (use_cgroup_v2)
return bpf_get_current_cgroup_id();
task = bpf_get_current_task_btf();
if (perf_subsys_id == -1) {
#if __has_builtin(__builtin_preserve_enum_value)
perf_subsys_id = bpf_core_enum_value(enum cgroup_subsys_id,
perf_event_cgrp_id);
#else
perf_subsys_id = perf_event_cgrp_id;
#endif
}
cgrp = BPF_CORE_READ(task, cgroups, subsys[perf_subsys_id], cgroup);
return BPF_CORE_READ(cgrp, kn, id);
}
static inline int can_record(u64 *ctx) static inline int can_record(u64 *ctx)
{ {
if (has_cpu) { if (has_cpu) {
...@@ -364,10 +390,13 @@ int contention_end(u64 *ctx) ...@@ -364,10 +390,13 @@ int contention_end(u64 *ctx)
key.stack_id = pelem->stack_id; key.stack_id = pelem->stack_id;
break; break;
case LOCK_AGGR_ADDR: case LOCK_AGGR_ADDR:
key.lock_addr = pelem->lock; key.lock_addr_or_cgroup = pelem->lock;
if (needs_callstack) if (needs_callstack)
key.stack_id = pelem->stack_id; key.stack_id = pelem->stack_id;
break; break;
case LOCK_AGGR_CGROUP:
key.lock_addr_or_cgroup = get_current_cgroup_id();
break;
default: default:
/* should not happen */ /* should not happen */
return 0; return 0;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
struct contention_key { struct contention_key {
u32 stack_id; u32 stack_id;
u32 pid; u32 pid;
u64 lock_addr; u64 lock_addr_or_cgroup;
}; };
#define TASK_COMM_LEN 16 #define TASK_COMM_LEN 16
...@@ -39,6 +39,7 @@ enum lock_aggr_mode { ...@@ -39,6 +39,7 @@ enum lock_aggr_mode {
LOCK_AGGR_ADDR = 0, LOCK_AGGR_ADDR = 0,
LOCK_AGGR_TASK, LOCK_AGGR_TASK,
LOCK_AGGR_CALLER, LOCK_AGGR_CALLER,
LOCK_AGGR_CGROUP,
}; };
enum lock_class_sym { enum lock_class_sym {
......
...@@ -144,7 +144,6 @@ struct lock_contention { ...@@ -144,7 +144,6 @@ struct lock_contention {
int owner; int owner;
int nr_filtered; int nr_filtered;
bool save_callstack; bool save_callstack;
bool use_cgroup;
}; };
#ifdef HAVE_BPF_SKEL #ifdef HAVE_BPF_SKEL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment