brintos

brintos / linux-shallow public Read only

0
0
Text · 63.8 KiB · 062e2b5 Raw
2721 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <errno.h>3#include <inttypes.h>4#include "builtin.h"5#include "perf.h"6 7#include "util/evlist.h" // for struct evsel_str_handler8#include "util/evsel.h"9#include "util/symbol.h"10#include "util/thread.h"11#include "util/header.h"12#include "util/target.h"13#include "util/cgroup.h"14#include "util/callchain.h"15#include "util/lock-contention.h"16#include "util/bpf_skel/lock_data.h"17 18#include <subcmd/pager.h>19#include <subcmd/parse-options.h>20#include "util/trace-event.h"21#include "util/tracepoint.h"22 23#include "util/debug.h"24#include "util/session.h"25#include "util/tool.h"26#include "util/data.h"27#include "util/string2.h"28#include "util/map.h"29#include "util/util.h"30 31#include <stdio.h>32#include <sys/types.h>33#include <sys/prctl.h>34#include <semaphore.h>35#include <math.h>36#include <limits.h>37#include <ctype.h>38 39#include <linux/list.h>40#include <linux/hash.h>41#include <linux/kernel.h>42#include <linux/zalloc.h>43#include <linux/err.h>44#include <linux/stringify.h>45 46static struct perf_session *session;47static struct target target;48 49/* based on kernel/lockdep.c */50#define LOCKHASH_BITS		1251#define LOCKHASH_SIZE		(1UL << LOCKHASH_BITS)52 53static struct hlist_head *lockhash_table;54 55#define __lockhashfn(key)	hash_long((unsigned long)key, LOCKHASH_BITS)56#define lockhashentry(key)	(lockhash_table + __lockhashfn((key)))57 58static struct rb_root		thread_stats;59 60static bool combine_locks;61static bool show_thread_stats;62static bool show_lock_addrs;63static bool show_lock_owner;64static bool show_lock_cgroups;65static bool use_bpf;66static unsigned long bpf_map_entries = MAX_ENTRIES;67static int max_stack_depth = CONTENTION_STACK_DEPTH;68static int stack_skip = CONTENTION_STACK_SKIP;69static int print_nr_entries = INT_MAX / 2;70static LIST_HEAD(callstack_filters);71static const char *output_name = NULL;72static FILE *lock_output;73 74struct callstack_filter {75	struct list_head list;76	char name[];77};78 79static struct lock_filter filters;80 81static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR;82 83static bool needs_callstack(void)84{85	return !list_empty(&callstack_filters);86}87 88static struct thread_stat *thread_stat_find(u32 tid)89{90	struct rb_node *node;91	struct thread_stat *st;92 93	node = thread_stats.rb_node;94	while (node) {95		st = container_of(node, struct thread_stat, rb);96		if (st->tid == tid)97			return st;98		else if (tid < st->tid)99			node = node->rb_left;100		else101			node = node->rb_right;102	}103 104	return NULL;105}106 107static void thread_stat_insert(struct thread_stat *new)108{109	struct rb_node **rb = &thread_stats.rb_node;110	struct rb_node *parent = NULL;111	struct thread_stat *p;112 113	while (*rb) {114		p = container_of(*rb, struct thread_stat, rb);115		parent = *rb;116 117		if (new->tid < p->tid)118			rb = &(*rb)->rb_left;119		else if (new->tid > p->tid)120			rb = &(*rb)->rb_right;121		else122			BUG_ON("inserting invalid thread_stat\n");123	}124 125	rb_link_node(&new->rb, parent, rb);126	rb_insert_color(&new->rb, &thread_stats);127}128 129static struct thread_stat *thread_stat_findnew_after_first(u32 tid)130{131	struct thread_stat *st;132 133	st = thread_stat_find(tid);134	if (st)135		return st;136 137	st = zalloc(sizeof(struct thread_stat));138	if (!st) {139		pr_err("memory allocation failed\n");140		return NULL;141	}142 143	st->tid = tid;144	INIT_LIST_HEAD(&st->seq_list);145 146	thread_stat_insert(st);147 148	return st;149}150 151static struct thread_stat *thread_stat_findnew_first(u32 tid);152static struct thread_stat *(*thread_stat_findnew)(u32 tid) =153	thread_stat_findnew_first;154 155static struct thread_stat *thread_stat_findnew_first(u32 tid)156{157	struct thread_stat *st;158 159	st = zalloc(sizeof(struct thread_stat));160	if (!st) {161		pr_err("memory allocation failed\n");162		return NULL;163	}164	st->tid = tid;165	INIT_LIST_HEAD(&st->seq_list);166 167	rb_link_node(&st->rb, NULL, &thread_stats.rb_node);168	rb_insert_color(&st->rb, &thread_stats);169 170	thread_stat_findnew = thread_stat_findnew_after_first;171	return st;172}173 174/* build simple key function one is bigger than two */175#define SINGLE_KEY(member)						\176	static int lock_stat_key_ ## member(struct lock_stat *one,	\177					 struct lock_stat *two)		\178	{								\179		return one->member > two->member;			\180	}181 182SINGLE_KEY(nr_acquired)183SINGLE_KEY(nr_contended)184SINGLE_KEY(avg_wait_time)185SINGLE_KEY(wait_time_total)186SINGLE_KEY(wait_time_max)187 188static int lock_stat_key_wait_time_min(struct lock_stat *one,189					struct lock_stat *two)190{191	u64 s1 = one->wait_time_min;192	u64 s2 = two->wait_time_min;193	if (s1 == ULLONG_MAX)194		s1 = 0;195	if (s2 == ULLONG_MAX)196		s2 = 0;197	return s1 > s2;198}199 200struct lock_key {201	/*202	 * name: the value for specify by user203	 * this should be simpler than raw name of member204	 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total205	 */206	const char		*name;207	/* header: the string printed on the header line */208	const char		*header;209	/* len: the printing width of the field */210	int			len;211	/* key: a pointer to function to compare two lock stats for sorting */212	int			(*key)(struct lock_stat*, struct lock_stat*);213	/* print: a pointer to function to print a given lock stats */214	void			(*print)(struct lock_key*, struct lock_stat*);215	/* list: list entry to link this */216	struct list_head	list;217};218 219static void lock_stat_key_print_time(unsigned long long nsec, int len)220{221	static const struct {222		float base;223		const char *unit;224	} table[] = {225		{ 1e9 * 3600, "h " },226		{ 1e9 * 60, "m " },227		{ 1e9, "s " },228		{ 1e6, "ms" },229		{ 1e3, "us" },230		{ 0, NULL },231	};232 233	/* for CSV output */234	if (len == 0) {235		fprintf(lock_output, "%llu", nsec);236		return;237	}238 239	for (int i = 0; table[i].unit; i++) {240		if (nsec < table[i].base)241			continue;242 243		fprintf(lock_output, "%*.2f %s", len - 3, nsec / table[i].base, table[i].unit);244		return;245	}246 247	fprintf(lock_output, "%*llu %s", len - 3, nsec, "ns");248}249 250#define PRINT_KEY(member)						\251static void lock_stat_key_print_ ## member(struct lock_key *key,	\252					   struct lock_stat *ls)	\253{									\254	fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\255}256 257#define PRINT_TIME(member)						\258static void lock_stat_key_print_ ## member(struct lock_key *key,	\259					   struct lock_stat *ls)	\260{									\261	lock_stat_key_print_time((unsigned long long)ls->member, key->len);	\262}263 264PRINT_KEY(nr_acquired)265PRINT_KEY(nr_contended)266PRINT_TIME(avg_wait_time)267PRINT_TIME(wait_time_total)268PRINT_TIME(wait_time_max)269 270static void lock_stat_key_print_wait_time_min(struct lock_key *key,271					      struct lock_stat *ls)272{273	u64 wait_time = ls->wait_time_min;274 275	if (wait_time == ULLONG_MAX)276		wait_time = 0;277 278	lock_stat_key_print_time(wait_time, key->len);279}280 281 282static const char		*sort_key = "acquired";283 284static int			(*compare)(struct lock_stat *, struct lock_stat *);285 286static struct rb_root		sorted; /* place to store intermediate data */287static struct rb_root		result;	/* place to store sorted data */288 289static LIST_HEAD(lock_keys);290static const char		*output_fields;291 292#define DEF_KEY_LOCK(name, header, fn_suffix, len)			\293	{ #name, header, len, lock_stat_key_ ## fn_suffix, lock_stat_key_print_ ## fn_suffix, {} }294static struct lock_key report_keys[] = {295	DEF_KEY_LOCK(acquired, "acquired", nr_acquired, 10),296	DEF_KEY_LOCK(contended, "contended", nr_contended, 10),297	DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),298	DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),299	DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),300	DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),301 302	/* extra comparisons much complicated should be here */303	{ }304};305 306static struct lock_key contention_keys[] = {307	DEF_KEY_LOCK(contended, "contended", nr_contended, 10),308	DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),309	DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),310	DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),311	DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),312 313	/* extra comparisons much complicated should be here */314	{ }315};316 317static int select_key(bool contention)318{319	int i;320	struct lock_key *keys = report_keys;321 322	if (contention)323		keys = contention_keys;324 325	for (i = 0; keys[i].name; i++) {326		if (!strcmp(keys[i].name, sort_key)) {327			compare = keys[i].key;328 329			/* selected key should be in the output fields */330			if (list_empty(&keys[i].list))331				list_add_tail(&keys[i].list, &lock_keys);332 333			return 0;334		}335	}336 337	pr_err("Unknown compare key: %s\n", sort_key);338	return -1;339}340 341static int add_output_field(bool contention, char *name)342{343	int i;344	struct lock_key *keys = report_keys;345 346	if (contention)347		keys = contention_keys;348 349	for (i = 0; keys[i].name; i++) {350		if (strcmp(keys[i].name, name))351			continue;352 353		/* prevent double link */354		if (list_empty(&keys[i].list))355			list_add_tail(&keys[i].list, &lock_keys);356 357		return 0;358	}359 360	pr_err("Unknown output field: %s\n", name);361	return -1;362}363 364static int setup_output_field(bool contention, const char *str)365{366	char *tok, *tmp, *orig;367	int i, ret = 0;368	struct lock_key *keys = report_keys;369 370	if (contention)371		keys = contention_keys;372 373	/* no output field given: use all of them */374	if (str == NULL) {375		for (i = 0; keys[i].name; i++)376			list_add_tail(&keys[i].list, &lock_keys);377		return 0;378	}379 380	for (i = 0; keys[i].name; i++)381		INIT_LIST_HEAD(&keys[i].list);382 383	orig = tmp = strdup(str);384	if (orig == NULL)385		return -ENOMEM;386 387	while ((tok = strsep(&tmp, ",")) != NULL){388		ret = add_output_field(contention, tok);389		if (ret < 0)390			break;391	}392	free(orig);393 394	return ret;395}396 397static void combine_lock_stats(struct lock_stat *st)398{399	struct rb_node **rb = &sorted.rb_node;400	struct rb_node *parent = NULL;401	struct lock_stat *p;402	int ret;403 404	while (*rb) {405		p = container_of(*rb, struct lock_stat, rb);406		parent = *rb;407 408		if (st->name && p->name)409			ret = strcmp(st->name, p->name);410		else411			ret = !!st->name - !!p->name;412 413		if (ret == 0) {414			p->nr_acquired += st->nr_acquired;415			p->nr_contended += st->nr_contended;416			p->wait_time_total += st->wait_time_total;417 418			if (p->nr_contended)419				p->avg_wait_time = p->wait_time_total / p->nr_contended;420 421			if (p->wait_time_min > st->wait_time_min)422				p->wait_time_min = st->wait_time_min;423			if (p->wait_time_max < st->wait_time_max)424				p->wait_time_max = st->wait_time_max;425 426			p->broken |= st->broken;427			st->combined = 1;428			return;429		}430 431		if (ret < 0)432			rb = &(*rb)->rb_left;433		else434			rb = &(*rb)->rb_right;435	}436 437	rb_link_node(&st->rb, parent, rb);438	rb_insert_color(&st->rb, &sorted);439}440 441static void insert_to_result(struct lock_stat *st,442			     int (*bigger)(struct lock_stat *, struct lock_stat *))443{444	struct rb_node **rb = &result.rb_node;445	struct rb_node *parent = NULL;446	struct lock_stat *p;447 448	if (combine_locks && st->combined)449		return;450 451	while (*rb) {452		p = container_of(*rb, struct lock_stat, rb);453		parent = *rb;454 455		if (bigger(st, p))456			rb = &(*rb)->rb_left;457		else458			rb = &(*rb)->rb_right;459	}460 461	rb_link_node(&st->rb, parent, rb);462	rb_insert_color(&st->rb, &result);463}464 465/* returns left most element of result, and erase it */466static struct lock_stat *pop_from_result(void)467{468	struct rb_node *node = result.rb_node;469 470	if (!node)471		return NULL;472 473	while (node->rb_left)474		node = node->rb_left;475 476	rb_erase(node, &result);477	return container_of(node, struct lock_stat, rb);478}479 480struct lock_stat *lock_stat_find(u64 addr)481{482	struct hlist_head *entry = lockhashentry(addr);483	struct lock_stat *ret;484 485	hlist_for_each_entry(ret, entry, hash_entry) {486		if (ret->addr == addr)487			return ret;488	}489	return NULL;490}491 492struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)493{494	struct hlist_head *entry = lockhashentry(addr);495	struct lock_stat *ret, *new;496 497	hlist_for_each_entry(ret, entry, hash_entry) {498		if (ret->addr == addr)499			return ret;500	}501 502	new = zalloc(sizeof(struct lock_stat));503	if (!new)504		goto alloc_failed;505 506	new->addr = addr;507	new->name = strdup(name);508	if (!new->name) {509		free(new);510		goto alloc_failed;511	}512 513	new->flags = flags;514	new->wait_time_min = ULLONG_MAX;515 516	hlist_add_head(&new->hash_entry, entry);517	return new;518 519alloc_failed:520	pr_err("memory allocation failed\n");521	return NULL;522}523 524bool match_callstack_filter(struct machine *machine, u64 *callstack)525{526	struct map *kmap;527	struct symbol *sym;528	u64 ip;529	const char *arch = perf_env__arch(machine->env);530 531	if (list_empty(&callstack_filters))532		return true;533 534	for (int i = 0; i < max_stack_depth; i++) {535		struct callstack_filter *filter;536 537		/*538		 * In powerpc, the callchain saved by kernel always includes539		 * first three entries as the NIP (next instruction pointer),540		 * LR (link register), and the contents of LR save area in the541		 * second stack frame. In certain scenarios its possible to have542		 * invalid kernel instruction addresses in either LR or the second543		 * stack frame's LR. In that case, kernel will store that address as544		 * zero.545		 *546		 * The below check will continue to look into callstack,547		 * incase first or second callstack index entry has 0548		 * address for powerpc.549		 */550		if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") ||551						(i != 1 && i != 2))))552			break;553 554		ip = callstack[i];555		sym = machine__find_kernel_symbol(machine, ip, &kmap);556		if (sym == NULL)557			continue;558 559		list_for_each_entry(filter, &callstack_filters, list) {560			if (strstr(sym->name, filter->name))561				return true;562		}563	}564	return false;565}566 567struct trace_lock_handler {568	/* it's used on CONFIG_LOCKDEP */569	int (*acquire_event)(struct evsel *evsel,570			     struct perf_sample *sample);571 572	/* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */573	int (*acquired_event)(struct evsel *evsel,574			      struct perf_sample *sample);575 576	/* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */577	int (*contended_event)(struct evsel *evsel,578			       struct perf_sample *sample);579 580	/* it's used on CONFIG_LOCKDEP */581	int (*release_event)(struct evsel *evsel,582			     struct perf_sample *sample);583 584	/* it's used when CONFIG_LOCKDEP is off */585	int (*contention_begin_event)(struct evsel *evsel,586				      struct perf_sample *sample);587 588	/* it's used when CONFIG_LOCKDEP is off */589	int (*contention_end_event)(struct evsel *evsel,590				    struct perf_sample *sample);591};592 593static struct lock_seq_stat *get_seq(struct thread_stat *ts, u64 addr)594{595	struct lock_seq_stat *seq;596 597	list_for_each_entry(seq, &ts->seq_list, list) {598		if (seq->addr == addr)599			return seq;600	}601 602	seq = zalloc(sizeof(struct lock_seq_stat));603	if (!seq) {604		pr_err("memory allocation failed\n");605		return NULL;606	}607	seq->state = SEQ_STATE_UNINITIALIZED;608	seq->addr = addr;609 610	list_add(&seq->list, &ts->seq_list);611	return seq;612}613 614enum broken_state {615	BROKEN_ACQUIRE,616	BROKEN_ACQUIRED,617	BROKEN_CONTENDED,618	BROKEN_RELEASE,619	BROKEN_MAX,620};621 622static int bad_hist[BROKEN_MAX];623 624enum acquire_flags {625	TRY_LOCK = 1,626	READ_LOCK = 2,627};628 629static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid)630{631	switch (aggr_mode) {632	case LOCK_AGGR_ADDR:633		*key = addr;634		break;635	case LOCK_AGGR_TASK:636		*key = tid;637		break;638	case LOCK_AGGR_CALLER:639	case LOCK_AGGR_CGROUP:640	default:641		pr_err("Invalid aggregation mode: %d\n", aggr_mode);642		return -EINVAL;643	}644	return 0;645}646 647static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample);648 649static int get_key_by_aggr_mode(u64 *key, u64 addr, struct evsel *evsel,650				 struct perf_sample *sample)651{652	if (aggr_mode == LOCK_AGGR_CALLER) {653		*key = callchain_id(evsel, sample);654		return 0;655	}656	return get_key_by_aggr_mode_simple(key, addr, sample->tid);657}658 659static int report_lock_acquire_event(struct evsel *evsel,660				     struct perf_sample *sample)661{662	struct lock_stat *ls;663	struct thread_stat *ts;664	struct lock_seq_stat *seq;665	const char *name = evsel__strval(evsel, sample, "name");666	u64 addr = evsel__intval(evsel, sample, "lockdep_addr");667	int flag = evsel__intval(evsel, sample, "flags");668	u64 key;669	int ret;670 671	ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);672	if (ret < 0)673		return ret;674 675	ls = lock_stat_findnew(key, name, 0);676	if (!ls)677		return -ENOMEM;678 679	ts = thread_stat_findnew(sample->tid);680	if (!ts)681		return -ENOMEM;682 683	seq = get_seq(ts, addr);684	if (!seq)685		return -ENOMEM;686 687	switch (seq->state) {688	case SEQ_STATE_UNINITIALIZED:689	case SEQ_STATE_RELEASED:690		if (!flag) {691			seq->state = SEQ_STATE_ACQUIRING;692		} else {693			if (flag & TRY_LOCK)694				ls->nr_trylock++;695			if (flag & READ_LOCK)696				ls->nr_readlock++;697			seq->state = SEQ_STATE_READ_ACQUIRED;698			seq->read_count = 1;699			ls->nr_acquired++;700		}701		break;702	case SEQ_STATE_READ_ACQUIRED:703		if (flag & READ_LOCK) {704			seq->read_count++;705			ls->nr_acquired++;706			goto end;707		} else {708			goto broken;709		}710		break;711	case SEQ_STATE_ACQUIRED:712	case SEQ_STATE_ACQUIRING:713	case SEQ_STATE_CONTENDED:714broken:715		/* broken lock sequence */716		if (!ls->broken) {717			ls->broken = 1;718			bad_hist[BROKEN_ACQUIRE]++;719		}720		list_del_init(&seq->list);721		free(seq);722		goto end;723	default:724		BUG_ON("Unknown state of lock sequence found!\n");725		break;726	}727 728	ls->nr_acquire++;729	seq->prev_event_time = sample->time;730end:731	return 0;732}733 734static int report_lock_acquired_event(struct evsel *evsel,735				      struct perf_sample *sample)736{737	struct lock_stat *ls;738	struct thread_stat *ts;739	struct lock_seq_stat *seq;740	u64 contended_term;741	const char *name = evsel__strval(evsel, sample, "name");742	u64 addr = evsel__intval(evsel, sample, "lockdep_addr");743	u64 key;744	int ret;745 746	ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);747	if (ret < 0)748		return ret;749 750	ls = lock_stat_findnew(key, name, 0);751	if (!ls)752		return -ENOMEM;753 754	ts = thread_stat_findnew(sample->tid);755	if (!ts)756		return -ENOMEM;757 758	seq = get_seq(ts, addr);759	if (!seq)760		return -ENOMEM;761 762	switch (seq->state) {763	case SEQ_STATE_UNINITIALIZED:764		/* orphan event, do nothing */765		return 0;766	case SEQ_STATE_ACQUIRING:767		break;768	case SEQ_STATE_CONTENDED:769		contended_term = sample->time - seq->prev_event_time;770		ls->wait_time_total += contended_term;771		if (contended_term < ls->wait_time_min)772			ls->wait_time_min = contended_term;773		if (ls->wait_time_max < contended_term)774			ls->wait_time_max = contended_term;775		break;776	case SEQ_STATE_RELEASED:777	case SEQ_STATE_ACQUIRED:778	case SEQ_STATE_READ_ACQUIRED:779		/* broken lock sequence */780		if (!ls->broken) {781			ls->broken = 1;782			bad_hist[BROKEN_ACQUIRED]++;783		}784		list_del_init(&seq->list);785		free(seq);786		goto end;787	default:788		BUG_ON("Unknown state of lock sequence found!\n");789		break;790	}791 792	seq->state = SEQ_STATE_ACQUIRED;793	ls->nr_acquired++;794	ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0;795	seq->prev_event_time = sample->time;796end:797	return 0;798}799 800static int report_lock_contended_event(struct evsel *evsel,801				       struct perf_sample *sample)802{803	struct lock_stat *ls;804	struct thread_stat *ts;805	struct lock_seq_stat *seq;806	const char *name = evsel__strval(evsel, sample, "name");807	u64 addr = evsel__intval(evsel, sample, "lockdep_addr");808	u64 key;809	int ret;810 811	ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);812	if (ret < 0)813		return ret;814 815	ls = lock_stat_findnew(key, name, 0);816	if (!ls)817		return -ENOMEM;818 819	ts = thread_stat_findnew(sample->tid);820	if (!ts)821		return -ENOMEM;822 823	seq = get_seq(ts, addr);824	if (!seq)825		return -ENOMEM;826 827	switch (seq->state) {828	case SEQ_STATE_UNINITIALIZED:829		/* orphan event, do nothing */830		return 0;831	case SEQ_STATE_ACQUIRING:832		break;833	case SEQ_STATE_RELEASED:834	case SEQ_STATE_ACQUIRED:835	case SEQ_STATE_READ_ACQUIRED:836	case SEQ_STATE_CONTENDED:837		/* broken lock sequence */838		if (!ls->broken) {839			ls->broken = 1;840			bad_hist[BROKEN_CONTENDED]++;841		}842		list_del_init(&seq->list);843		free(seq);844		goto end;845	default:846		BUG_ON("Unknown state of lock sequence found!\n");847		break;848	}849 850	seq->state = SEQ_STATE_CONTENDED;851	ls->nr_contended++;852	ls->avg_wait_time = ls->wait_time_total/ls->nr_contended;853	seq->prev_event_time = sample->time;854end:855	return 0;856}857 858static int report_lock_release_event(struct evsel *evsel,859				     struct perf_sample *sample)860{861	struct lock_stat *ls;862	struct thread_stat *ts;863	struct lock_seq_stat *seq;864	const char *name = evsel__strval(evsel, sample, "name");865	u64 addr = evsel__intval(evsel, sample, "lockdep_addr");866	u64 key;867	int ret;868 869	ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);870	if (ret < 0)871		return ret;872 873	ls = lock_stat_findnew(key, name, 0);874	if (!ls)875		return -ENOMEM;876 877	ts = thread_stat_findnew(sample->tid);878	if (!ts)879		return -ENOMEM;880 881	seq = get_seq(ts, addr);882	if (!seq)883		return -ENOMEM;884 885	switch (seq->state) {886	case SEQ_STATE_UNINITIALIZED:887		goto end;888	case SEQ_STATE_ACQUIRED:889		break;890	case SEQ_STATE_READ_ACQUIRED:891		seq->read_count--;892		BUG_ON(seq->read_count < 0);893		if (seq->read_count) {894			ls->nr_release++;895			goto end;896		}897		break;898	case SEQ_STATE_ACQUIRING:899	case SEQ_STATE_CONTENDED:900	case SEQ_STATE_RELEASED:901		/* broken lock sequence */902		if (!ls->broken) {903			ls->broken = 1;904			bad_hist[BROKEN_RELEASE]++;905		}906		goto free_seq;907	default:908		BUG_ON("Unknown state of lock sequence found!\n");909		break;910	}911 912	ls->nr_release++;913free_seq:914	list_del_init(&seq->list);915	free(seq);916end:917	return 0;918}919 920static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,921				  char *buf, int size)922{923	u64 offset;924 925	if (map == NULL || sym == NULL) {926		buf[0] = '\0';927		return 0;928	}929 930	offset = map__map_ip(map, ip) - sym->start;931 932	if (offset)933		return scnprintf(buf, size, "%s+%#lx", sym->name, offset);934	else935		return strlcpy(buf, sym->name, size);936}937static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample,938				  char *buf, int size)939{940	struct thread *thread;941	struct callchain_cursor *cursor;942	struct machine *machine = &session->machines.host;943	struct symbol *sym;944	int skip = 0;945	int ret;946 947	/* lock names will be replaced to task name later */948	if (show_thread_stats)949		return -1;950 951	thread = machine__findnew_thread(machine, -1, sample->pid);952	if (thread == NULL)953		return -1;954 955	cursor = get_tls_callchain_cursor();956 957	/* use caller function name from the callchain */958	ret = thread__resolve_callchain(thread, cursor, evsel, sample,959					NULL, NULL, max_stack_depth);960	if (ret != 0) {961		thread__put(thread);962		return -1;963	}964 965	callchain_cursor_commit(cursor);966	thread__put(thread);967 968	while (true) {969		struct callchain_cursor_node *node;970 971		node = callchain_cursor_current(cursor);972		if (node == NULL)973			break;974 975		/* skip first few entries - for lock functions */976		if (++skip <= stack_skip)977			goto next;978 979		sym = node->ms.sym;980		if (sym && !machine__is_lock_function(machine, node->ip)) {981			get_symbol_name_offset(node->ms.map, sym, node->ip,982					       buf, size);983			return 0;984		}985 986next:987		callchain_cursor_advance(cursor);988	}989	return -1;990}991 992static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)993{994	struct callchain_cursor *cursor;995	struct machine *machine = &session->machines.host;996	struct thread *thread;997	u64 hash = 0;998	int skip = 0;999	int ret;1000 1001	thread = machine__findnew_thread(machine, -1, sample->pid);1002	if (thread == NULL)1003		return -1;1004 1005	cursor = get_tls_callchain_cursor();1006	/* use caller function name from the callchain */1007	ret = thread__resolve_callchain(thread, cursor, evsel, sample,1008					NULL, NULL, max_stack_depth);1009	thread__put(thread);1010 1011	if (ret != 0)1012		return -1;1013 1014	callchain_cursor_commit(cursor);1015 1016	while (true) {1017		struct callchain_cursor_node *node;1018 1019		node = callchain_cursor_current(cursor);1020		if (node == NULL)1021			break;1022 1023		/* skip first few entries - for lock functions */1024		if (++skip <= stack_skip)1025			goto next;1026 1027		if (node->ms.sym && machine__is_lock_function(machine, node->ip))1028			goto next;1029 1030		hash ^= hash_long((unsigned long)node->ip, 64);1031 1032next:1033		callchain_cursor_advance(cursor);1034	}1035	return hash;1036}1037 1038static u64 *get_callstack(struct perf_sample *sample, int max_stack)1039{1040	u64 *callstack;1041	u64 i;1042	int c;1043 1044	callstack = calloc(max_stack, sizeof(*callstack));1045	if (callstack == NULL)1046		return NULL;1047 1048	for (i = 0, c = 0; i < sample->callchain->nr && c < max_stack; i++) {1049		u64 ip = sample->callchain->ips[i];1050 1051		if (ip >= PERF_CONTEXT_MAX)1052			continue;1053 1054		callstack[c++] = ip;1055	}1056	return callstack;1057}1058 1059static int report_lock_contention_begin_event(struct evsel *evsel,1060					      struct perf_sample *sample)1061{1062	struct lock_stat *ls;1063	struct thread_stat *ts;1064	struct lock_seq_stat *seq;1065	u64 addr = evsel__intval(evsel, sample, "lock_addr");1066	unsigned int flags = evsel__intval(evsel, sample, "flags");1067	u64 key;1068	int i, ret;1069	static bool kmap_loaded;1070	struct machine *machine = &session->machines.host;1071	struct map *kmap;1072	struct symbol *sym;1073 1074	ret = get_key_by_aggr_mode(&key, addr, evsel, sample);1075	if (ret < 0)1076		return ret;1077 1078	if (!kmap_loaded) {1079		unsigned long *addrs;1080 1081		/* make sure it loads the kernel map to find lock symbols */1082		map__load(machine__kernel_map(machine));1083		kmap_loaded = true;1084 1085		/* convert (kernel) symbols to addresses */1086		for (i = 0; i < filters.nr_syms; i++) {1087			sym = machine__find_kernel_symbol_by_name(machine,1088								  filters.syms[i],1089								  &kmap);1090			if (sym == NULL) {1091				pr_warning("ignore unknown symbol: %s\n",1092					   filters.syms[i]);1093				continue;1094			}1095 1096			addrs = realloc(filters.addrs,1097					(filters.nr_addrs + 1) * sizeof(*addrs));1098			if (addrs == NULL) {1099				pr_warning("memory allocation failure\n");1100				return -ENOMEM;1101			}1102 1103			addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);1104			filters.addrs = addrs;1105		}1106	}1107 1108	ls = lock_stat_find(key);1109	if (!ls) {1110		char buf[128];1111		const char *name = "";1112 1113		switch (aggr_mode) {1114		case LOCK_AGGR_ADDR:1115			sym = machine__find_kernel_symbol(machine, key, &kmap);1116			if (sym)1117				name = sym->name;1118			break;1119		case LOCK_AGGR_CALLER:1120			name = buf;1121			if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)1122				name = "Unknown";1123			break;1124		case LOCK_AGGR_CGROUP:1125		case LOCK_AGGR_TASK:1126		default:1127			break;1128		}1129 1130		ls = lock_stat_findnew(key, name, flags);1131		if (!ls)1132			return -ENOMEM;1133	}1134 1135	if (filters.nr_types) {1136		bool found = false;1137 1138		for (i = 0; i < filters.nr_types; i++) {1139			if (flags == filters.types[i]) {1140				found = true;1141				break;1142			}1143		}1144 1145		if (!found)1146			return 0;1147	}1148 1149	if (filters.nr_addrs) {1150		bool found = false;1151 1152		for (i = 0; i < filters.nr_addrs; i++) {1153			if (addr == filters.addrs[i]) {1154				found = true;1155				break;1156			}1157		}1158 1159		if (!found)1160			return 0;1161	}1162 1163	if (needs_callstack()) {1164		u64 *callstack = get_callstack(sample, max_stack_depth);1165		if (callstack == NULL)1166			return -ENOMEM;1167 1168		if (!match_callstack_filter(machine, callstack)) {1169			free(callstack);1170			return 0;1171		}1172 1173		if (ls->callstack == NULL)1174			ls->callstack = callstack;1175		else1176			free(callstack);1177	}1178 1179	ts = thread_stat_findnew(sample->tid);1180	if (!ts)1181		return -ENOMEM;1182 1183	seq = get_seq(ts, addr);1184	if (!seq)1185		return -ENOMEM;1186 1187	switch (seq->state) {1188	case SEQ_STATE_UNINITIALIZED:1189	case SEQ_STATE_ACQUIRED:1190		break;1191	case SEQ_STATE_CONTENDED:1192		/*1193		 * It can have nested contention begin with mutex spinning,1194		 * then we would use the original contention begin event and1195		 * ignore the second one.1196		 */1197		goto end;1198	case SEQ_STATE_ACQUIRING:1199	case SEQ_STATE_READ_ACQUIRED:1200	case SEQ_STATE_RELEASED:1201		/* broken lock sequence */1202		if (!ls->broken) {1203			ls->broken = 1;1204			bad_hist[BROKEN_CONTENDED]++;1205		}1206		list_del_init(&seq->list);1207		free(seq);1208		goto end;1209	default:1210		BUG_ON("Unknown state of lock sequence found!\n");1211		break;1212	}1213 1214	if (seq->state != SEQ_STATE_CONTENDED) {1215		seq->state = SEQ_STATE_CONTENDED;1216		seq->prev_event_time = sample->time;1217		ls->nr_contended++;1218	}1219end:1220	return 0;1221}1222 1223static int report_lock_contention_end_event(struct evsel *evsel,1224					    struct perf_sample *sample)1225{1226	struct lock_stat *ls;1227	struct thread_stat *ts;1228	struct lock_seq_stat *seq;1229	u64 contended_term;1230	u64 addr = evsel__intval(evsel, sample, "lock_addr");1231	u64 key;1232	int ret;1233 1234	ret = get_key_by_aggr_mode(&key, addr, evsel, sample);1235	if (ret < 0)1236		return ret;1237 1238	ls = lock_stat_find(key);1239	if (!ls)1240		return 0;1241 1242	ts = thread_stat_find(sample->tid);1243	if (!ts)1244		return 0;1245 1246	seq = get_seq(ts, addr);1247	if (!seq)1248		return -ENOMEM;1249 1250	switch (seq->state) {1251	case SEQ_STATE_UNINITIALIZED:1252		goto end;1253	case SEQ_STATE_CONTENDED:1254		contended_term = sample->time - seq->prev_event_time;1255		ls->wait_time_total += contended_term;1256		if (contended_term < ls->wait_time_min)1257			ls->wait_time_min = contended_term;1258		if (ls->wait_time_max < contended_term)1259			ls->wait_time_max = contended_term;1260		break;1261	case SEQ_STATE_ACQUIRING:1262	case SEQ_STATE_ACQUIRED:1263	case SEQ_STATE_READ_ACQUIRED:1264	case SEQ_STATE_RELEASED:1265		/* broken lock sequence */1266		if (!ls->broken) {1267			ls->broken = 1;1268			bad_hist[BROKEN_ACQUIRED]++;1269		}1270		list_del_init(&seq->list);1271		free(seq);1272		goto end;1273	default:1274		BUG_ON("Unknown state of lock sequence found!\n");1275		break;1276	}1277 1278	seq->state = SEQ_STATE_ACQUIRED;1279	ls->nr_acquired++;1280	ls->avg_wait_time = ls->wait_time_total/ls->nr_acquired;1281end:1282	return 0;1283}1284 1285/* lock oriented handlers */1286/* TODO: handlers for CPU oriented, thread oriented */1287static struct trace_lock_handler report_lock_ops  = {1288	.acquire_event		= report_lock_acquire_event,1289	.acquired_event		= report_lock_acquired_event,1290	.contended_event	= report_lock_contended_event,1291	.release_event		= report_lock_release_event,1292	.contention_begin_event	= report_lock_contention_begin_event,1293	.contention_end_event	= report_lock_contention_end_event,1294};1295 1296static struct trace_lock_handler contention_lock_ops  = {1297	.contention_begin_event	= report_lock_contention_begin_event,1298	.contention_end_event	= report_lock_contention_end_event,1299};1300 1301 1302static struct trace_lock_handler *trace_handler;1303 1304static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample)1305{1306	if (trace_handler->acquire_event)1307		return trace_handler->acquire_event(evsel, sample);1308	return 0;1309}1310 1311static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample)1312{1313	if (trace_handler->acquired_event)1314		return trace_handler->acquired_event(evsel, sample);1315	return 0;1316}1317 1318static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample)1319{1320	if (trace_handler->contended_event)1321		return trace_handler->contended_event(evsel, sample);1322	return 0;1323}1324 1325static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample)1326{1327	if (trace_handler->release_event)1328		return trace_handler->release_event(evsel, sample);1329	return 0;1330}1331 1332static int evsel__process_contention_begin(struct evsel *evsel, struct perf_sample *sample)1333{1334	if (trace_handler->contention_begin_event)1335		return trace_handler->contention_begin_event(evsel, sample);1336	return 0;1337}1338 1339static int evsel__process_contention_end(struct evsel *evsel, struct perf_sample *sample)1340{1341	if (trace_handler->contention_end_event)1342		return trace_handler->contention_end_event(evsel, sample);1343	return 0;1344}1345 1346static void print_bad_events(int bad, int total)1347{1348	/* Output for debug, this have to be removed */1349	int i;1350	int broken = 0;1351	const char *name[4] =1352		{ "acquire", "acquired", "contended", "release" };1353 1354	for (i = 0; i < BROKEN_MAX; i++)1355		broken += bad_hist[i];1356 1357	if (quiet || total == 0 || (broken == 0 && verbose <= 0))1358		return;1359 1360	fprintf(lock_output, "\n=== output for debug ===\n\n");1361	fprintf(lock_output, "bad: %d, total: %d\n", bad, total);1362	fprintf(lock_output, "bad rate: %.2f %%\n", (double)bad / (double)total * 100);1363	fprintf(lock_output, "histogram of events caused bad sequence\n");1364	for (i = 0; i < BROKEN_MAX; i++)1365		fprintf(lock_output, " %10s: %d\n", name[i], bad_hist[i]);1366}1367 1368/* TODO: various way to print, coloring, nano or milli sec */1369static void print_result(void)1370{1371	struct lock_stat *st;1372	struct lock_key *key;1373	char cut_name[20];1374	int bad, total, printed;1375 1376	if (!quiet) {1377		fprintf(lock_output, "%20s ", "Name");1378		list_for_each_entry(key, &lock_keys, list)1379			fprintf(lock_output, "%*s ", key->len, key->header);1380		fprintf(lock_output, "\n\n");1381	}1382 1383	bad = total = printed = 0;1384	while ((st = pop_from_result())) {1385		total++;1386		if (st->broken)1387			bad++;1388		if (!st->nr_acquired)1389			continue;1390 1391		bzero(cut_name, 20);1392 1393		if (strlen(st->name) < 20) {1394			/* output raw name */1395			const char *name = st->name;1396 1397			if (show_thread_stats) {1398				struct thread *t;1399 1400				/* st->addr contains tid of thread */1401				t = perf_session__findnew(session, st->addr);1402				name = thread__comm_str(t);1403			}1404 1405			fprintf(lock_output, "%20s ", name);1406		} else {1407			strncpy(cut_name, st->name, 16);1408			cut_name[16] = '.';1409			cut_name[17] = '.';1410			cut_name[18] = '.';1411			cut_name[19] = '\0';1412			/* cut off name for saving output style */1413			fprintf(lock_output, "%20s ", cut_name);1414		}1415 1416		list_for_each_entry(key, &lock_keys, list) {1417			key->print(key, st);1418			fprintf(lock_output, " ");1419		}1420		fprintf(lock_output, "\n");1421 1422		if (++printed >= print_nr_entries)1423			break;1424	}1425 1426	print_bad_events(bad, total);1427}1428 1429static bool info_threads, info_map;1430 1431static void dump_threads(void)1432{1433	struct thread_stat *st;1434	struct rb_node *node;1435	struct thread *t;1436 1437	fprintf(lock_output, "%10s: comm\n", "Thread ID");1438 1439	node = rb_first(&thread_stats);1440	while (node) {1441		st = container_of(node, struct thread_stat, rb);1442		t = perf_session__findnew(session, st->tid);1443		fprintf(lock_output, "%10d: %s\n", st->tid, thread__comm_str(t));1444		node = rb_next(node);1445		thread__put(t);1446	}1447}1448 1449static int compare_maps(struct lock_stat *a, struct lock_stat *b)1450{1451	int ret;1452 1453	if (a->name && b->name)1454		ret = strcmp(a->name, b->name);1455	else1456		ret = !!a->name - !!b->name;1457 1458	if (!ret)1459		return a->addr < b->addr;1460	else1461		return ret < 0;1462}1463 1464static void dump_map(void)1465{1466	unsigned int i;1467	struct lock_stat *st;1468 1469	fprintf(lock_output, "Address of instance: name of class\n");1470	for (i = 0; i < LOCKHASH_SIZE; i++) {1471		hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1472			insert_to_result(st, compare_maps);1473		}1474	}1475 1476	while ((st = pop_from_result()))1477		fprintf(lock_output, " %#llx: %s\n", (unsigned long long)st->addr, st->name);1478}1479 1480static void dump_info(void)1481{1482	if (info_threads)1483		dump_threads();1484 1485	if (info_map) {1486		if (info_threads)1487			fputc('\n', lock_output);1488		dump_map();1489	}1490}1491 1492static const struct evsel_str_handler lock_tracepoints[] = {1493	{ "lock:lock_acquire",	 evsel__process_lock_acquire,   }, /* CONFIG_LOCKDEP */1494	{ "lock:lock_acquired",	 evsel__process_lock_acquired,  }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */1495	{ "lock:lock_contended", evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */1496	{ "lock:lock_release",	 evsel__process_lock_release,   }, /* CONFIG_LOCKDEP */1497};1498 1499static const struct evsel_str_handler contention_tracepoints[] = {1500	{ "lock:contention_begin", evsel__process_contention_begin, },1501	{ "lock:contention_end",   evsel__process_contention_end,   },1502};1503 1504static int process_event_update(const struct perf_tool *tool,1505				union perf_event *event,1506				struct evlist **pevlist)1507{1508	int ret;1509 1510	ret = perf_event__process_event_update(tool, event, pevlist);1511	if (ret < 0)1512		return ret;1513 1514	/* this can return -EEXIST since we call it for each evsel */1515	perf_session__set_tracepoints_handlers(session, lock_tracepoints);1516	perf_session__set_tracepoints_handlers(session, contention_tracepoints);1517	return 0;1518}1519 1520typedef int (*tracepoint_handler)(struct evsel *evsel,1521				  struct perf_sample *sample);1522 1523static int process_sample_event(const struct perf_tool *tool __maybe_unused,1524				union perf_event *event,1525				struct perf_sample *sample,1526				struct evsel *evsel,1527				struct machine *machine)1528{1529	int err = 0;1530	struct thread *thread = machine__findnew_thread(machine, sample->pid,1531							sample->tid);1532 1533	if (thread == NULL) {1534		pr_debug("problem processing %d event, skipping it.\n",1535			event->header.type);1536		return -1;1537	}1538 1539	if (evsel->handler != NULL) {1540		tracepoint_handler f = evsel->handler;1541		err = f(evsel, sample);1542	}1543 1544	thread__put(thread);1545 1546	return err;1547}1548 1549static void combine_result(void)1550{1551	unsigned int i;1552	struct lock_stat *st;1553 1554	if (!combine_locks)1555		return;1556 1557	for (i = 0; i < LOCKHASH_SIZE; i++) {1558		hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1559			combine_lock_stats(st);1560		}1561	}1562}1563 1564static void sort_result(void)1565{1566	unsigned int i;1567	struct lock_stat *st;1568 1569	for (i = 0; i < LOCKHASH_SIZE; i++) {1570		hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1571			insert_to_result(st, compare);1572		}1573	}1574}1575 1576static const struct {1577	unsigned int flags;1578	const char *str;1579	const char *name;1580} lock_type_table[] = {1581	{ 0,				"semaphore",	"semaphore" },1582	{ LCB_F_SPIN,			"spinlock",	"spinlock" },1583	{ LCB_F_SPIN | LCB_F_READ,	"rwlock:R",	"rwlock" },1584	{ LCB_F_SPIN | LCB_F_WRITE,	"rwlock:W",	"rwlock" },1585	{ LCB_F_READ,			"rwsem:R",	"rwsem" },1586	{ LCB_F_WRITE,			"rwsem:W",	"rwsem" },1587	{ LCB_F_RT,			"rt-mutex",	"rt-mutex" },1588	{ LCB_F_RT | LCB_F_READ,	"rwlock-rt:R",	"rwlock-rt" },1589	{ LCB_F_RT | LCB_F_WRITE,	"rwlock-rt:W",	"rwlock-rt" },1590	{ LCB_F_PERCPU | LCB_F_READ,	"pcpu-sem:R",	"percpu-rwsem" },1591	{ LCB_F_PERCPU | LCB_F_WRITE,	"pcpu-sem:W",	"percpu-rwsem" },1592	{ LCB_F_MUTEX,			"mutex",	"mutex" },1593	{ LCB_F_MUTEX | LCB_F_SPIN,	"mutex",	"mutex" },1594	/* alias for get_type_flag() */1595	{ LCB_F_MUTEX | LCB_F_SPIN,	"mutex-spin",	"mutex" },1596};1597 1598static const char *get_type_str(unsigned int flags)1599{1600	flags &= LCB_F_MAX_FLAGS - 1;1601 1602	for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1603		if (lock_type_table[i].flags == flags)1604			return lock_type_table[i].str;1605	}1606	return "unknown";1607}1608 1609static const char *get_type_name(unsigned int flags)1610{1611	flags &= LCB_F_MAX_FLAGS - 1;1612 1613	for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1614		if (lock_type_table[i].flags == flags)1615			return lock_type_table[i].name;1616	}1617	return "unknown";1618}1619 1620static unsigned int get_type_flag(const char *str)1621{1622	for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1623		if (!strcmp(lock_type_table[i].name, str))1624			return lock_type_table[i].flags;1625	}1626	for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1627		if (!strcmp(lock_type_table[i].str, str))1628			return lock_type_table[i].flags;1629	}1630	return UINT_MAX;1631}1632 1633static void lock_filter_finish(void)1634{1635	zfree(&filters.types);1636	filters.nr_types = 0;1637 1638	zfree(&filters.addrs);1639	filters.nr_addrs = 0;1640 1641	for (int i = 0; i < filters.nr_syms; i++)1642		free(filters.syms[i]);1643 1644	zfree(&filters.syms);1645	filters.nr_syms = 0;1646 1647	zfree(&filters.cgrps);1648	filters.nr_cgrps = 0;1649}1650 1651static void sort_contention_result(void)1652{1653	sort_result();1654}1655 1656static void print_header_stdio(void)1657{1658	struct lock_key *key;1659 1660	list_for_each_entry(key, &lock_keys, list)1661		fprintf(lock_output, "%*s ", key->len, key->header);1662 1663	switch (aggr_mode) {1664	case LOCK_AGGR_TASK:1665		fprintf(lock_output, "  %10s   %s\n\n", "pid",1666			show_lock_owner ? "owner" : "comm");1667		break;1668	case LOCK_AGGR_CALLER:1669		fprintf(lock_output, "  %10s   %s\n\n", "type", "caller");1670		break;1671	case LOCK_AGGR_ADDR:1672		fprintf(lock_output, "  %16s   %s\n\n", "address", "symbol");1673		break;1674	case LOCK_AGGR_CGROUP:1675		fprintf(lock_output, "  %s\n\n", "cgroup");1676		break;1677	default:1678		break;1679	}1680}1681 1682static void print_header_csv(const char *sep)1683{1684	struct lock_key *key;1685 1686	fprintf(lock_output, "# output: ");1687	list_for_each_entry(key, &lock_keys, list)1688		fprintf(lock_output, "%s%s ", key->header, sep);1689 1690	switch (aggr_mode) {1691	case LOCK_AGGR_TASK:1692		fprintf(lock_output, "%s%s %s\n", "pid", sep,1693			show_lock_owner ? "owner" : "comm");1694		break;1695	case LOCK_AGGR_CALLER:1696		fprintf(lock_output, "%s%s %s", "type", sep, "caller");1697		if (verbose > 0)1698			fprintf(lock_output, "%s %s", sep, "stacktrace");1699		fprintf(lock_output, "\n");1700		break;1701	case LOCK_AGGR_ADDR:1702		fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type");1703		break;1704	case LOCK_AGGR_CGROUP:1705		fprintf(lock_output, "%s\n", "cgroup");1706		break;1707	default:1708		break;1709	}1710}1711 1712static void print_header(void)1713{1714	if (!quiet) {1715		if (symbol_conf.field_sep)1716			print_header_csv(symbol_conf.field_sep);1717		else1718			print_header_stdio();1719	}1720}1721 1722static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat *st)1723{1724	struct lock_key *key;1725	struct thread *t;1726	int pid;1727 1728	list_for_each_entry(key, &lock_keys, list) {1729		key->print(key, st);1730		fprintf(lock_output, " ");1731	}1732 1733	switch (aggr_mode) {1734	case LOCK_AGGR_CALLER:1735		fprintf(lock_output, "  %10s   %s\n", get_type_str(st->flags), st->name);1736		break;1737	case LOCK_AGGR_TASK:1738		pid = st->addr;1739		t = perf_session__findnew(session, pid);1740		fprintf(lock_output, "  %10d   %s\n",1741			pid, pid == -1 ? "Unknown" : thread__comm_str(t));1742		break;1743	case LOCK_AGGR_ADDR:1744		fprintf(lock_output, "  %016llx   %s (%s)\n", (unsigned long long)st->addr,1745			st->name, get_type_name(st->flags));1746		break;1747	case LOCK_AGGR_CGROUP:1748		fprintf(lock_output, "  %s\n", st->name);1749		break;1750	default:1751		break;1752	}1753 1754	if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {1755		struct map *kmap;1756		struct symbol *sym;1757		char buf[128];1758		u64 ip;1759 1760		for (int i = 0; i < max_stack_depth; i++) {1761			if (!st->callstack || !st->callstack[i])1762				break;1763 1764			ip = st->callstack[i];1765			sym = machine__find_kernel_symbol(con->machine, ip, &kmap);1766			get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));1767			fprintf(lock_output, "\t\t\t%#lx  %s\n", (unsigned long)ip, buf);1768		}1769	}1770}1771 1772static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *st,1773				const char *sep)1774{1775	struct lock_key *key;1776	struct thread *t;1777	int pid;1778 1779	list_for_each_entry(key, &lock_keys, list) {1780		key->print(key, st);1781		fprintf(lock_output, "%s ", sep);1782	}1783 1784	switch (aggr_mode) {1785	case LOCK_AGGR_CALLER:1786		fprintf(lock_output, "%s%s %s", get_type_str(st->flags), sep, st->name);1787		if (verbose <= 0)1788			fprintf(lock_output, "\n");1789		break;1790	case LOCK_AGGR_TASK:1791		pid = st->addr;1792		t = perf_session__findnew(session, pid);1793		fprintf(lock_output, "%d%s %s\n", pid, sep,1794			pid == -1 ? "Unknown" : thread__comm_str(t));1795		break;1796	case LOCK_AGGR_ADDR:1797		fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep,1798			st->name, sep, get_type_name(st->flags));1799		break;1800	case LOCK_AGGR_CGROUP:1801		fprintf(lock_output, "%s\n",st->name);1802		break;1803	default:1804		break;1805	}1806 1807	if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {1808		struct map *kmap;1809		struct symbol *sym;1810		char buf[128];1811		u64 ip;1812 1813		for (int i = 0; i < max_stack_depth; i++) {1814			if (!st->callstack || !st->callstack[i])1815				break;1816 1817			ip = st->callstack[i];1818			sym = machine__find_kernel_symbol(con->machine, ip, &kmap);1819			get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));1820			fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);1821		}1822		fprintf(lock_output, "\n");1823	}1824}1825 1826static void print_lock_stat(struct lock_contention *con, struct lock_stat *st)1827{1828	if (symbol_conf.field_sep)1829		print_lock_stat_csv(con, st, symbol_conf.field_sep);1830	else1831		print_lock_stat_stdio(con, st);1832}1833 1834static void print_footer_stdio(int total, int bad, struct lock_contention_fails *fails)1835{1836	/* Output for debug, this have to be removed */1837	int broken = fails->task + fails->stack + fails->time + fails->data;1838 1839	if (!use_bpf)1840		print_bad_events(bad, total);1841 1842	if (quiet || total == 0 || (broken == 0 && verbose <= 0))1843		return;1844 1845	total += broken;1846	fprintf(lock_output, "\n=== output for debug ===\n\n");1847	fprintf(lock_output, "bad: %d, total: %d\n", broken, total);1848	fprintf(lock_output, "bad rate: %.2f %%\n", 100.0 * broken / total);1849 1850	fprintf(lock_output, "histogram of failure reasons\n");1851	fprintf(lock_output, " %10s: %d\n", "task", fails->task);1852	fprintf(lock_output, " %10s: %d\n", "stack", fails->stack);1853	fprintf(lock_output, " %10s: %d\n", "time", fails->time);1854	fprintf(lock_output, " %10s: %d\n", "data", fails->data);1855}1856 1857static void print_footer_csv(int total, int bad, struct lock_contention_fails *fails,1858			     const char *sep)1859{1860	/* Output for debug, this have to be removed */1861	if (use_bpf)1862		bad = fails->task + fails->stack + fails->time + fails->data;1863 1864	if (quiet || total == 0 || (bad == 0 && verbose <= 0))1865		return;1866 1867	total += bad;1868	fprintf(lock_output, "# debug: total=%d%s bad=%d", total, sep, bad);1869 1870	if (use_bpf) {1871		fprintf(lock_output, "%s bad_%s=%d", sep, "task", fails->task);1872		fprintf(lock_output, "%s bad_%s=%d", sep, "stack", fails->stack);1873		fprintf(lock_output, "%s bad_%s=%d", sep, "time", fails->time);1874		fprintf(lock_output, "%s bad_%s=%d", sep, "data", fails->data);1875	} else {1876		int i;1877		const char *name[4] = { "acquire", "acquired", "contended", "release" };1878 1879		for (i = 0; i < BROKEN_MAX; i++)1880			fprintf(lock_output, "%s bad_%s=%d", sep, name[i], bad_hist[i]);1881	}1882	fprintf(lock_output, "\n");1883}1884 1885static void print_footer(int total, int bad, struct lock_contention_fails *fails)1886{1887	if (symbol_conf.field_sep)1888		print_footer_csv(total, bad, fails, symbol_conf.field_sep);1889	else1890		print_footer_stdio(total, bad, fails);1891}1892 1893static void print_contention_result(struct lock_contention *con)1894{1895	struct lock_stat *st;1896	int bad, total, printed;1897 1898	if (!quiet)1899		print_header();1900 1901	bad = total = printed = 0;1902 1903	while ((st = pop_from_result())) {1904		total += use_bpf ? st->nr_contended : 1;1905		if (st->broken)1906			bad++;1907 1908		if (!st->wait_time_total)1909			continue;1910 1911		print_lock_stat(con, st);1912 1913		if (++printed >= print_nr_entries)1914			break;1915	}1916 1917	if (print_nr_entries) {1918		/* update the total/bad stats */1919		while ((st = pop_from_result())) {1920			total += use_bpf ? st->nr_contended : 1;1921			if (st->broken)1922				bad++;1923		}1924	}1925	/* some entries are collected but hidden by the callstack filter */1926	total += con->nr_filtered;1927 1928	print_footer(total, bad, &con->fails);1929}1930 1931static bool force;1932 1933static int __cmd_report(bool display_info)1934{1935	int err = -EINVAL;1936	struct perf_tool eops;1937	struct perf_data data = {1938		.path  = input_name,1939		.mode  = PERF_DATA_MODE_READ,1940		.force = force,1941	};1942 1943	perf_tool__init(&eops, /*ordered_events=*/true);1944	eops.attr		 = perf_event__process_attr;1945	eops.event_update	 = process_event_update;1946	eops.sample		 = process_sample_event;1947	eops.comm		 = perf_event__process_comm;1948	eops.mmap		 = perf_event__process_mmap;1949	eops.namespaces		 = perf_event__process_namespaces;1950	eops.tracing_data	 = perf_event__process_tracing_data;1951	session = perf_session__new(&data, &eops);1952	if (IS_ERR(session)) {1953		pr_err("Initializing perf session failed\n");1954		return PTR_ERR(session);1955	}1956 1957	symbol_conf.allow_aliases = true;1958	symbol__init(&session->header.env);1959 1960	if (!data.is_pipe) {1961		if (!perf_session__has_traces(session, "lock record"))1962			goto out_delete;1963 1964		if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {1965			pr_err("Initializing perf session tracepoint handlers failed\n");1966			goto out_delete;1967		}1968 1969		if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) {1970			pr_err("Initializing perf session tracepoint handlers failed\n");1971			goto out_delete;1972		}1973	}1974 1975	if (setup_output_field(false, output_fields))1976		goto out_delete;1977 1978	if (select_key(false))1979		goto out_delete;1980 1981	if (show_thread_stats)1982		aggr_mode = LOCK_AGGR_TASK;1983 1984	err = perf_session__process_events(session);1985	if (err)1986		goto out_delete;1987 1988	setup_pager();1989	if (display_info) /* used for info subcommand */1990		dump_info();1991	else {1992		combine_result();1993		sort_result();1994		print_result();1995	}1996 1997out_delete:1998	perf_session__delete(session);1999	return err;2000}2001 2002static void sighandler(int sig __maybe_unused)2003{2004}2005 2006static int check_lock_contention_options(const struct option *options,2007					 const char * const *usage)2008 2009{2010	if (show_thread_stats && show_lock_addrs) {2011		pr_err("Cannot use thread and addr mode together\n");2012		parse_options_usage(usage, options, "threads", 0);2013		parse_options_usage(NULL, options, "lock-addr", 0);2014		return -1;2015	}2016 2017	if (show_lock_owner && !use_bpf) {2018		pr_err("Lock owners are available only with BPF\n");2019		parse_options_usage(usage, options, "lock-owner", 0);2020		parse_options_usage(NULL, options, "use-bpf", 0);2021		return -1;2022	}2023 2024	if (show_lock_owner && show_lock_addrs) {2025		pr_err("Cannot use owner and addr mode together\n");2026		parse_options_usage(usage, options, "lock-owner", 0);2027		parse_options_usage(NULL, options, "lock-addr", 0);2028		return -1;2029	}2030 2031	if (show_lock_cgroups && !use_bpf) {2032		pr_err("Cgroups are available only with BPF\n");2033		parse_options_usage(usage, options, "lock-cgroup", 0);2034		parse_options_usage(NULL, options, "use-bpf", 0);2035		return -1;2036	}2037 2038	if (show_lock_cgroups && show_lock_addrs) {2039		pr_err("Cannot use cgroup and addr mode together\n");2040		parse_options_usage(usage, options, "lock-cgroup", 0);2041		parse_options_usage(NULL, options, "lock-addr", 0);2042		return -1;2043	}2044 2045	if (show_lock_cgroups && show_thread_stats) {2046		pr_err("Cannot use cgroup and thread mode together\n");2047		parse_options_usage(usage, options, "lock-cgroup", 0);2048		parse_options_usage(NULL, options, "threads", 0);2049		return -1;2050	}2051 2052	if (symbol_conf.field_sep) {2053		if (strstr(symbol_conf.field_sep, ":") || /* part of type flags */2054		    strstr(symbol_conf.field_sep, "+") || /* part of caller offset */2055		    strstr(symbol_conf.field_sep, ".")) { /* can be in a symbol name */2056			pr_err("Cannot use the separator that is already used\n");2057			parse_options_usage(usage, options, "x", 1);2058			return -1;2059		}2060	}2061 2062	if (show_lock_owner)2063		show_thread_stats = true;2064 2065	return 0;2066}2067 2068static int __cmd_contention(int argc, const char **argv)2069{2070	int err = -EINVAL;2071	struct perf_tool eops;2072	struct perf_data data = {2073		.path  = input_name,2074		.mode  = PERF_DATA_MODE_READ,2075		.force = force,2076	};2077	struct lock_contention con = {2078		.target = &target,2079		.map_nr_entries = bpf_map_entries,2080		.max_stack = max_stack_depth,2081		.stack_skip = stack_skip,2082		.filters = &filters,2083		.save_callstack = needs_callstack(),2084		.owner = show_lock_owner,2085		.cgroups = RB_ROOT,2086	};2087 2088	lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));2089	if (!lockhash_table)2090		return -ENOMEM;2091 2092	con.result = &lockhash_table[0];2093 2094	perf_tool__init(&eops, /*ordered_events=*/true);2095	eops.attr		 = perf_event__process_attr;2096	eops.event_update	 = process_event_update;2097	eops.sample		 = process_sample_event;2098	eops.comm		 = perf_event__process_comm;2099	eops.mmap		 = perf_event__process_mmap;2100	eops.tracing_data	 = perf_event__process_tracing_data;2101 2102	session = perf_session__new(use_bpf ? NULL : &data, &eops);2103	if (IS_ERR(session)) {2104		pr_err("Initializing perf session failed\n");2105		err = PTR_ERR(session);2106		session = NULL;2107		goto out_delete;2108	}2109 2110	con.machine = &session->machines.host;2111 2112	con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :2113		show_lock_addrs ? LOCK_AGGR_ADDR :2114		show_lock_cgroups ? LOCK_AGGR_CGROUP : LOCK_AGGR_CALLER;2115 2116	if (con.aggr_mode == LOCK_AGGR_CALLER)2117		con.save_callstack = true;2118 2119	symbol_conf.allow_aliases = true;2120	symbol__init(&session->header.env);2121 2122	if (use_bpf) {2123		err = target__validate(&target);2124		if (err) {2125			char errbuf[512];2126 2127			target__strerror(&target, err, errbuf, 512);2128			pr_err("%s\n", errbuf);2129			goto out_delete;2130		}2131 2132		signal(SIGINT, sighandler);2133		signal(SIGCHLD, sighandler);2134		signal(SIGTERM, sighandler);2135 2136		con.evlist = evlist__new();2137		if (con.evlist == NULL) {2138			err = -ENOMEM;2139			goto out_delete;2140		}2141 2142		err = evlist__create_maps(con.evlist, &target);2143		if (err < 0)2144			goto out_delete;2145 2146		if (argc) {2147			err = evlist__prepare_workload(con.evlist, &target,2148						       argv, false, NULL);2149			if (err < 0)2150				goto out_delete;2151		}2152 2153		if (lock_contention_prepare(&con) < 0) {2154			pr_err("lock contention BPF setup failed\n");2155			goto out_delete;2156		}2157	} else if (!data.is_pipe) {2158		if (!perf_session__has_traces(session, "lock record"))2159			goto out_delete;2160 2161		if (!evlist__find_evsel_by_str(session->evlist,2162					       "lock:contention_begin")) {2163			pr_err("lock contention evsel not found\n");2164			goto out_delete;2165		}2166 2167		if (perf_session__set_tracepoints_handlers(session,2168						contention_tracepoints)) {2169			pr_err("Initializing perf session tracepoint handlers failed\n");2170			goto out_delete;2171		}2172	}2173 2174	if (setup_output_field(true, output_fields))2175		goto out_delete;2176 2177	if (select_key(true))2178		goto out_delete;2179 2180	if (symbol_conf.field_sep) {2181		int i;2182		struct lock_key *keys = contention_keys;2183 2184		/* do not align output in CSV format */2185		for (i = 0; keys[i].name; i++)2186			keys[i].len = 0;2187	}2188 2189	if (use_bpf) {2190		lock_contention_start();2191		if (argc)2192			evlist__start_workload(con.evlist);2193 2194		/* wait for signal */2195		pause();2196 2197		lock_contention_stop();2198		lock_contention_read(&con);2199	} else {2200		err = perf_session__process_events(session);2201		if (err)2202			goto out_delete;2203	}2204 2205	setup_pager();2206 2207	sort_contention_result();2208	print_contention_result(&con);2209 2210out_delete:2211	lock_filter_finish();2212	evlist__delete(con.evlist);2213	lock_contention_finish(&con);2214	perf_session__delete(session);2215	zfree(&lockhash_table);2216	return err;2217}2218 2219 2220static int __cmd_record(int argc, const char **argv)2221{2222	const char *record_args[] = {2223		"record", "-R", "-m", "1024", "-c", "1", "--synth", "task",2224	};2225	const char *callgraph_args[] = {2226		"--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH),2227	};2228	unsigned int rec_argc, i, j, ret;2229	unsigned int nr_tracepoints;2230	unsigned int nr_callgraph_args = 0;2231	const char **rec_argv;2232	bool has_lock_stat = true;2233 2234	for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {2235		if (!is_valid_tracepoint(lock_tracepoints[i].name)) {2236			pr_debug("tracepoint %s is not enabled. "2237				 "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",2238				 lock_tracepoints[i].name);2239			has_lock_stat = false;2240			break;2241		}2242	}2243 2244	if (has_lock_stat)2245		goto setup_args;2246 2247	for (i = 0; i < ARRAY_SIZE(contention_tracepoints); i++) {2248		if (!is_valid_tracepoint(contention_tracepoints[i].name)) {2249			pr_err("tracepoint %s is not enabled.\n",2250			       contention_tracepoints[i].name);2251			return 1;2252		}2253	}2254 2255	nr_callgraph_args = ARRAY_SIZE(callgraph_args);2256 2257setup_args:2258	rec_argc = ARRAY_SIZE(record_args) + nr_callgraph_args + argc - 1;2259 2260	if (has_lock_stat)2261		nr_tracepoints = ARRAY_SIZE(lock_tracepoints);2262	else2263		nr_tracepoints = ARRAY_SIZE(contention_tracepoints);2264 2265	/* factor of 2 is for -e in front of each tracepoint */2266	rec_argc += 2 * nr_tracepoints;2267 2268	rec_argv = calloc(rec_argc + 1, sizeof(char *));2269	if (!rec_argv)2270		return -ENOMEM;2271 2272	for (i = 0; i < ARRAY_SIZE(record_args); i++)2273		rec_argv[i] = record_args[i];2274 2275	for (j = 0; j < nr_tracepoints; j++) {2276		rec_argv[i++] = "-e";2277		rec_argv[i++] = has_lock_stat2278			? lock_tracepoints[j].name2279			: contention_tracepoints[j].name;2280	}2281 2282	for (j = 0; j < nr_callgraph_args; j++, i++)2283		rec_argv[i] = callgraph_args[j];2284 2285	for (j = 1; j < (unsigned int)argc; j++, i++)2286		rec_argv[i] = argv[j];2287 2288	BUG_ON(i != rec_argc);2289 2290	ret = cmd_record(i, rec_argv);2291	free(rec_argv);2292	return ret;2293}2294 2295static int parse_map_entry(const struct option *opt, const char *str,2296			    int unset __maybe_unused)2297{2298	unsigned long *len = (unsigned long *)opt->value;2299	unsigned long val;2300	char *endptr;2301 2302	errno = 0;2303	val = strtoul(str, &endptr, 0);2304	if (*endptr != '\0' || errno != 0) {2305		pr_err("invalid BPF map length: %s\n", str);2306		return -1;2307	}2308 2309	*len = val;2310	return 0;2311}2312 2313static int parse_max_stack(const struct option *opt, const char *str,2314			   int unset __maybe_unused)2315{2316	unsigned long *len = (unsigned long *)opt->value;2317	long val;2318	char *endptr;2319 2320	errno = 0;2321	val = strtol(str, &endptr, 0);2322	if (*endptr != '\0' || errno != 0) {2323		pr_err("invalid max stack depth: %s\n", str);2324		return -1;2325	}2326 2327	if (val < 0 || val > sysctl__max_stack()) {2328		pr_err("invalid max stack depth: %ld\n", val);2329		return -1;2330	}2331 2332	*len = val;2333	return 0;2334}2335 2336static bool add_lock_type(unsigned int flags)2337{2338	unsigned int *tmp;2339 2340	tmp = realloc(filters.types, (filters.nr_types + 1) * sizeof(*filters.types));2341	if (tmp == NULL)2342		return false;2343 2344	tmp[filters.nr_types++] = flags;2345	filters.types = tmp;2346	return true;2347}2348 2349static int parse_lock_type(const struct option *opt __maybe_unused, const char *str,2350			   int unset __maybe_unused)2351{2352	char *s, *tmp, *tok;2353	int ret = 0;2354 2355	s = strdup(str);2356	if (s == NULL)2357		return -1;2358 2359	for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2360		unsigned int flags = get_type_flag(tok);2361 2362		if (flags == -1U) {2363			pr_err("Unknown lock flags: %s\n", tok);2364			ret = -1;2365			break;2366		}2367 2368		if (!add_lock_type(flags)) {2369			ret = -1;2370			break;2371		}2372	}2373 2374	free(s);2375	return ret;2376}2377 2378static bool add_lock_addr(unsigned long addr)2379{2380	unsigned long *tmp;2381 2382	tmp = realloc(filters.addrs, (filters.nr_addrs + 1) * sizeof(*filters.addrs));2383	if (tmp == NULL) {2384		pr_err("Memory allocation failure\n");2385		return false;2386	}2387 2388	tmp[filters.nr_addrs++] = addr;2389	filters.addrs = tmp;2390	return true;2391}2392 2393static bool add_lock_sym(char *name)2394{2395	char **tmp;2396	char *sym = strdup(name);2397 2398	if (sym == NULL) {2399		pr_err("Memory allocation failure\n");2400		return false;2401	}2402 2403	tmp = realloc(filters.syms, (filters.nr_syms + 1) * sizeof(*filters.syms));2404	if (tmp == NULL) {2405		pr_err("Memory allocation failure\n");2406		free(sym);2407		return false;2408	}2409 2410	tmp[filters.nr_syms++] = sym;2411	filters.syms = tmp;2412	return true;2413}2414 2415static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str,2416			   int unset __maybe_unused)2417{2418	char *s, *tmp, *tok;2419	int ret = 0;2420	u64 addr;2421 2422	s = strdup(str);2423	if (s == NULL)2424		return -1;2425 2426	for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2427		char *end;2428 2429		addr = strtoul(tok, &end, 16);2430		if (*end == '\0') {2431			if (!add_lock_addr(addr)) {2432				ret = -1;2433				break;2434			}2435			continue;2436		}2437 2438		/*2439		 * At this moment, we don't have kernel symbols.  Save the symbols2440		 * in a separate list and resolve them to addresses later.2441		 */2442		if (!add_lock_sym(tok)) {2443			ret = -1;2444			break;2445		}2446	}2447 2448	free(s);2449	return ret;2450}2451 2452static int parse_call_stack(const struct option *opt __maybe_unused, const char *str,2453			   int unset __maybe_unused)2454{2455	char *s, *tmp, *tok;2456	int ret = 0;2457 2458	s = strdup(str);2459	if (s == NULL)2460		return -1;2461 2462	for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2463		struct callstack_filter *entry;2464 2465		entry = malloc(sizeof(*entry) + strlen(tok) + 1);2466		if (entry == NULL) {2467			pr_err("Memory allocation failure\n");2468			free(s);2469			return -1;2470		}2471 2472		strcpy(entry->name, tok);2473		list_add_tail(&entry->list, &callstack_filters);2474	}2475 2476	free(s);2477	return ret;2478}2479 2480static int parse_output(const struct option *opt __maybe_unused, const char *str,2481			int unset __maybe_unused)2482{2483	const char **name = (const char **)opt->value;2484 2485	if (str == NULL)2486		return -1;2487 2488	lock_output = fopen(str, "w");2489	if (lock_output == NULL) {2490		pr_err("Cannot open %s\n", str);2491		return -1;2492	}2493 2494	*name = str;2495	return 0;2496}2497 2498static bool add_lock_cgroup(char *name)2499{2500	u64 *tmp;2501	struct cgroup *cgrp;2502 2503	cgrp = cgroup__new(name, /*do_open=*/false);2504	if (cgrp == NULL) {2505		pr_err("Failed to create cgroup: %s\n", name);2506		return false;2507	}2508 2509	if (read_cgroup_id(cgrp) < 0) {2510		pr_err("Failed to read cgroup id for %s\n", name);2511		cgroup__put(cgrp);2512		return false;2513	}2514 2515	tmp = realloc(filters.cgrps, (filters.nr_cgrps + 1) * sizeof(*filters.cgrps));2516	if (tmp == NULL) {2517		pr_err("Memory allocation failure\n");2518		return false;2519	}2520 2521	tmp[filters.nr_cgrps++] = cgrp->id;2522	filters.cgrps = tmp;2523	cgroup__put(cgrp);2524	return true;2525}2526 2527static int parse_cgroup_filter(const struct option *opt __maybe_unused, const char *str,2528			       int unset __maybe_unused)2529{2530	char *s, *tmp, *tok;2531	int ret = 0;2532 2533	s = strdup(str);2534	if (s == NULL)2535		return -1;2536 2537	for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2538		if (!add_lock_cgroup(tok)) {2539			ret = -1;2540			break;2541		}2542	}2543 2544	free(s);2545	return ret;2546}2547 2548int cmd_lock(int argc, const char **argv)2549{2550	const struct option lock_options[] = {2551	OPT_STRING('i', "input", &input_name, "file", "input file name"),2552	OPT_CALLBACK(0, "output", &output_name, "file", "output file name", parse_output),2553	OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),2554	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),2555	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),2556	OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,2557		   "file", "vmlinux pathname"),2558	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,2559		   "file", "kallsyms pathname"),2560	OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"),2561	OPT_END()2562	};2563 2564	const struct option info_options[] = {2565	OPT_BOOLEAN('t', "threads", &info_threads,2566		    "dump the thread list in perf.data"),2567	OPT_BOOLEAN('m', "map", &info_map,2568		    "dump the map of lock instances (address:name table)"),2569	OPT_PARENT(lock_options)2570	};2571 2572	const struct option report_options[] = {2573	OPT_STRING('k', "key", &sort_key, "acquired",2574		    "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),2575	OPT_STRING('F', "field", &output_fields, NULL,2576		    "output fields (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),2577	/* TODO: type */2578	OPT_BOOLEAN('c', "combine-locks", &combine_locks,2579		    "combine locks in the same class"),2580	OPT_BOOLEAN('t', "threads", &show_thread_stats,2581		    "show per-thread lock stats"),2582	OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),2583	OPT_PARENT(lock_options)2584	};2585 2586	struct option contention_options[] = {2587	OPT_STRING('k', "key", &sort_key, "wait_total",2588		    "key for sorting (contended / wait_total / wait_max / wait_min / avg_wait)"),2589	OPT_STRING('F', "field", &output_fields, "contended,wait_total,wait_max,avg_wait",2590		    "output fields (contended / wait_total / wait_max / wait_min / avg_wait)"),2591	OPT_BOOLEAN('t', "threads", &show_thread_stats,2592		    "show per-thread lock stats"),2593	OPT_BOOLEAN('b', "use-bpf", &use_bpf, "use BPF program to collect lock contention stats"),2594	OPT_BOOLEAN('a', "all-cpus", &target.system_wide,2595		    "System-wide collection from all CPUs"),2596	OPT_STRING('C', "cpu", &target.cpu_list, "cpu",2597		    "List of cpus to monitor"),2598	OPT_STRING('p', "pid", &target.pid, "pid",2599		   "Trace on existing process id"),2600	OPT_STRING(0, "tid", &target.tid, "tid",2601		   "Trace on existing thread id (exclusive to --pid)"),2602	OPT_CALLBACK('M', "map-nr-entries", &bpf_map_entries, "num",2603		     "Max number of BPF map entries", parse_map_entry),2604	OPT_CALLBACK(0, "max-stack", &max_stack_depth, "num",2605		     "Set the maximum stack depth when collecting lock contention, "2606		     "Default: " __stringify(CONTENTION_STACK_DEPTH), parse_max_stack),2607	OPT_INTEGER(0, "stack-skip", &stack_skip,2608		    "Set the number of stack depth to skip when finding a lock caller, "2609		    "Default: " __stringify(CONTENTION_STACK_SKIP)),2610	OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),2611	OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),2612	OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS",2613		     "Filter specific type of locks", parse_lock_type),2614	OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES",2615		     "Filter specific address/symbol of locks", parse_lock_addr),2616	OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES",2617		     "Filter specific function in the callstack", parse_call_stack),2618	OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),2619	OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, "separator",2620		   "print result in CSV format with custom separator"),2621	OPT_BOOLEAN(0, "lock-cgroup", &show_lock_cgroups, "show lock stats by cgroup"),2622	OPT_CALLBACK('G', "cgroup-filter", NULL, "CGROUPS",2623		     "Filter specific cgroups", parse_cgroup_filter),2624	OPT_PARENT(lock_options)2625	};2626 2627	const char * const info_usage[] = {2628		"perf lock info [<options>]",2629		NULL2630	};2631	const char *const lock_subcommands[] = { "record", "report", "script",2632						 "info", "contention", NULL };2633	const char *lock_usage[] = {2634		NULL,2635		NULL2636	};2637	const char * const report_usage[] = {2638		"perf lock report [<options>]",2639		NULL2640	};2641	const char * const contention_usage[] = {2642		"perf lock contention [<options>]",2643		NULL2644	};2645	unsigned int i;2646	int rc = 0;2647 2648	lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));2649	if (!lockhash_table)2650		return -ENOMEM;2651 2652	for (i = 0; i < LOCKHASH_SIZE; i++)2653		INIT_HLIST_HEAD(lockhash_table + i);2654 2655	lock_output = stderr;2656	argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,2657					lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);2658	if (!argc)2659		usage_with_options(lock_usage, lock_options);2660 2661	if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {2662		return __cmd_record(argc, argv);2663	} else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {2664		trace_handler = &report_lock_ops;2665		if (argc) {2666			argc = parse_options(argc, argv,2667					     report_options, report_usage, 0);2668			if (argc)2669				usage_with_options(report_usage, report_options);2670		}2671		rc = __cmd_report(false);2672	} else if (!strcmp(argv[0], "script")) {2673		/* Aliased to 'perf script' */2674		rc = cmd_script(argc, argv);2675	} else if (!strcmp(argv[0], "info")) {2676		if (argc) {2677			argc = parse_options(argc, argv,2678					     info_options, info_usage, 0);2679			if (argc)2680				usage_with_options(info_usage, info_options);2681		}2682 2683		/* If neither threads nor map requested, display both */2684		if (!info_threads && !info_map) {2685			info_threads = true;2686			info_map = true;2687		}2688 2689		/* recycling report_lock_ops */2690		trace_handler = &report_lock_ops;2691		rc = __cmd_report(true);2692	} else if (strlen(argv[0]) > 2 && strstarts("contention", argv[0])) {2693		trace_handler = &contention_lock_ops;2694		sort_key = "wait_total";2695		output_fields = "contended,wait_total,wait_max,avg_wait";2696 2697#ifndef HAVE_BPF_SKEL2698		set_option_nobuild(contention_options, 'b', "use-bpf",2699				   "no BUILD_BPF_SKEL=1", false);2700#endif2701		if (argc) {2702			argc = parse_options(argc, argv, contention_options,2703					     contention_usage, 0);2704		}2705 2706		if (check_lock_contention_options(contention_options,2707						  contention_usage) < 0)2708			return -1;2709 2710		rc = __cmd_contention(argc, argv);2711	} else {2712		usage_with_options(lock_usage, lock_options);2713	}2714 2715	/* free usage string allocated by parse_options_subcommand */2716	free((void *)lock_usage[0]);2717 2718	zfree(&lockhash_table);2719	return rc;2720}2721