Commit 688d2e8d authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

perf lock contention: Add -l/--lock-addr option



The -l/--lock-addr option is to implement per-lock-instance contention
stat using LOCK_AGGR_ADDR.  It displays lock address and optionally
symbol name if exists.

  $ sudo ./perf lock con -abl sleep 1
   contended   total wait     max wait     avg wait            address   symbol

           1     36.28 us     36.28 us     36.28 us   ffff92615d6448b8
           9     10.91 us      1.84 us      1.21 us   ffffffffbaed50c0   rcu_state
           1     10.49 us     10.49 us     10.49 us   ffff9262ac4f0c80
           8      4.68 us      1.67 us       585 ns   ffffffffbae07a40   jiffies_lock
           3      3.03 us      1.45 us      1.01 us   ffff9262277861e0
           1       924 ns       924 ns       924 ns   ffff926095ba9d20
           1       436 ns       436 ns       436 ns   ffff9260bfda4f60

Signed-off-by: default avatarNamhyung 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/20221209190727.759804-4-namhyung@kernel.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent eca949b2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -168,6 +168,10 @@ CONTENTION OPTIONS
--entries=<value>::
	Display this many entries.

-l::
--lock-addr::
	Show lock contention stat by address


SEE ALSO
--------
+67 −17
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ static struct rb_root thread_stats;

static bool combine_locks;
static bool show_thread_stats;
static bool show_lock_addrs;
static bool use_bpf;
static unsigned long bpf_map_entries = 10240;
static int max_stack_depth = CONTENTION_STACK_DEPTH;
@@ -999,13 +1000,32 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
	ls = lock_stat_find(key);
	if (!ls) {
		char buf[128];
		const char *caller = buf;
		const char *name = "";
		unsigned int flags = evsel__intval(evsel, sample, "flags");
		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;
			break;
		case LOCK_AGGR_CALLER:
			name = buf;
			if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
			caller = "Unknown";
				name = "Unknown";
			break;
		case LOCK_AGGR_TASK:
		default:
			break;
		}

		ls = lock_stat_findnew(key, caller, flags);
		ls = lock_stat_findnew(key, name, flags);
		if (!ls)
			return -ENOMEM;

@@ -1460,10 +1480,19 @@ static void print_contention_result(struct lock_contention *con)
		list_for_each_entry(key, &lock_keys, list)
			pr_info("%*s ", key->len, key->header);

		if (show_thread_stats)
		switch (aggr_mode) {
		case LOCK_AGGR_TASK:
			pr_info("  %10s   %s\n\n", "pid", "comm");
		else
			break;
		case LOCK_AGGR_CALLER:
			pr_info("  %10s   %s\n\n", "type", "caller");
			break;
		case LOCK_AGGR_ADDR:
			pr_info("  %16s   %s\n\n", "address", "symbol");
			break;
		default:
			break;
		}
	}

	bad = total = printed = 0;
@@ -1471,6 +1500,9 @@ static void print_contention_result(struct lock_contention *con)
		bad = bad_hist[BROKEN_CONTENDED];

	while ((st = pop_from_result())) {
		struct thread *t;
		int pid;

		total += use_bpf ? st->nr_contended : 1;
		if (st->broken)
			bad++;
@@ -1480,18 +1512,24 @@ static void print_contention_result(struct lock_contention *con)
			pr_info(" ");
		}

		if (show_thread_stats) {
			struct thread *t;
			int pid = st->addr;

			/* st->addr contains tid of thread */
		switch (aggr_mode) {
		case LOCK_AGGR_CALLER:
			pr_info("  %10s   %s\n", get_type_str(st), st->name);
			break;
		case LOCK_AGGR_TASK:
			pid = st->addr;
			t = perf_session__findnew(session, pid);
			pr_info("  %10d   %s\n", pid, thread__comm_str(t));
			goto next;
			break;
		case LOCK_AGGR_ADDR:
			pr_info("  %016llx   %s\n", (unsigned long long)st->addr,
				st->name ? : "");
			break;
		default:
			break;
		}

		pr_info("  %10s   %s\n", get_type_str(st), st->name);
		if (verbose) {
		if (aggr_mode == LOCK_AGGR_CALLER && verbose) {
			struct map *kmap;
			struct symbol *sym;
			char buf[128];
@@ -1508,7 +1546,6 @@ static void print_contention_result(struct lock_contention *con)
			}
		}

next:
		if (++printed >= print_nr_entries)
			break;
	}
@@ -1616,7 +1653,6 @@ static int __cmd_contention(int argc, const char **argv)
		.map_nr_entries = bpf_map_entries,
		.max_stack = max_stack_depth,
		.stack_skip = stack_skip,
		.aggr_mode = show_thread_stats ? LOCK_AGGR_TASK : LOCK_AGGR_CALLER,
	};

	session = perf_session__new(use_bpf ? NULL : &data, &eops);
@@ -1627,6 +1663,9 @@ static int __cmd_contention(int argc, const char **argv)

	con.machine = &session->machines.host;

	con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :
		show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER;

	/* for lock function check */
	symbol_conf.sort_by_name = true;
	symbol__init(&session->header.env);
@@ -1907,6 +1946,7 @@ int cmd_lock(int argc, const char **argv)
		    "Set the number of stack depth to skip when finding a lock caller, "
		    "Default: " __stringify(CONTENTION_STACK_SKIP)),
	OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),
	OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),
	OPT_PARENT(lock_options)
	};

@@ -1976,6 +2016,16 @@ int cmd_lock(int argc, const char **argv)
			argc = parse_options(argc, argv, contention_options,
					     contention_usage, 0);
		}

		if (show_thread_stats && show_lock_addrs) {
			pr_err("Cannot use thread and addr mode together\n");
			parse_options_usage(contention_usage, contention_options,
					    "threads", 0);
			parse_options_usage(NULL, contention_options,
					    "lock-addr", 0);
			return -1;
		}

		rc = __cmd_contention(argc, argv);
	} else {
		usage_with_options(lock_usage, lock_options);
+17 −6
Original line number Diff line number Diff line
@@ -137,11 +137,15 @@ int lock_contention_read(struct lock_contention *con)
		thread__set_comm(idle, "swapper", /*timestamp=*/0);
	}

	/* make sure it loads the kernel map */
	map__load(maps__first(machine->kmaps));

	prev_key = NULL;
	while (!bpf_map_get_next_key(fd, prev_key, &key)) {
		struct map *kmap;
		struct symbol *sym;
		int idx = 0;
		s32 stack_id;

		/* to handle errors in the loop body */
		err = -1;
@@ -160,24 +164,31 @@ int lock_contention_read(struct lock_contention *con)
			st->avg_wait_time = data.total_time / data.count;

		st->flags = data.flags;
		st->addr = key.aggr_key;

		if (con->aggr_mode == LOCK_AGGR_TASK) {
			struct contention_task_data task;
			struct thread *t;

			st->addr = key.stack_or_task_id;
			int pid = key.aggr_key;

			/* do not update idle comm which contains CPU number */
			if (st->addr) {
				bpf_map_lookup_elem(task_fd, &key, &task);
				t = __machine__findnew_thread(machine, /*pid=*/-1,
							      key.stack_or_task_id);
				bpf_map_lookup_elem(task_fd, &pid, &task);
				t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
				thread__set_comm(t, task.comm, /*timestamp=*/0);
			}
			goto next;
		}

		bpf_map_lookup_elem(stack, &key, stack_trace);
		if (con->aggr_mode == LOCK_AGGR_ADDR) {
			sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
			if (sym)
				st->name = strdup(sym->name);
			goto next;
		}

		stack_id = key.aggr_key;
		bpf_map_lookup_elem(stack, &stack_id, stack_trace);

		/* skip lock internal functions */
		while (machine__is_lock_function(machine, stack_trace[idx]) &&
+13 −4
Original line number Diff line number Diff line
@@ -168,11 +168,20 @@ int contention_end(u64 *ctx)

	duration = bpf_ktime_get_ns() - pelem->timestamp;

	if (aggr_mode == LOCK_AGGR_CALLER) {
		key.stack_or_task_id = pelem->stack_id;
	} else {
		key.stack_or_task_id = pid;
	switch (aggr_mode) {
	case LOCK_AGGR_CALLER:
		key.aggr_key = pelem->stack_id;
		break;
	case LOCK_AGGR_TASK:
		key.aggr_key = pid;
		update_task_data(pid);
		break;
	case LOCK_AGGR_ADDR:
		key.aggr_key = pelem->lock;
		break;
	default:
		/* should not happen */
		return 0;
	}

	data = bpf_map_lookup_elem(&lock_stat, &key);
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
#define UTIL_BPF_SKEL_LOCK_DATA_H

struct contention_key {
	s32 stack_or_task_id;
	u64 aggr_key;  /* can be stack_id, pid or lock addr */
};

#define TASK_COMM_LEN  16