builtin-lock.c 23.8 KB
Newer Older
1 2 3
#include "builtin.h"
#include "perf.h"

4
#include "util/evsel.h"
5 6 7 8 9 10 11 12 13 14 15
#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"

#include "util/parse-options.h"
#include "util/trace-event.h"

#include "util/debug.h"
#include "util/session.h"
16
#include "util/tool.h"
17 18 19 20 21 22 23 24 25 26 27

#include <sys/types.h>
#include <sys/prctl.h>
#include <semaphore.h>
#include <pthread.h>
#include <math.h>
#include <limits.h>

#include <linux/list.h>
#include <linux/hash.h>

28 29
static struct perf_session *session;

30 31 32 33 34 35 36 37 38 39
/* based on kernel/lockdep.c */
#define LOCKHASH_BITS		12
#define LOCKHASH_SIZE		(1UL << LOCKHASH_BITS)

static struct list_head lockhash_table[LOCKHASH_SIZE];

#define __lockhashfn(key)	hash_long((unsigned long)key, LOCKHASH_BITS)
#define lockhashentry(key)	(lockhash_table + __lockhashfn((key)))

struct lock_stat {
40 41
	struct list_head	hash_entry;
	struct rb_node		rb;		/* used for sorting */
42

43 44
	/*
	 * FIXME: raw_field_value() returns unsigned long long,
45
	 * so address of lockdep_map should be dealed as 64bit.
46 47 48 49
	 * Is there more better solution?
	 */
	void			*addr;		/* address of lockdep_map, used as ID */
	char			*name;		/* for strcpy(), we cannot use const */
50

51
	unsigned int		nr_acquire;
52
	unsigned int		nr_acquired;
53 54
	unsigned int		nr_contended;
	unsigned int		nr_release;
55

56 57
	unsigned int		nr_readlock;
	unsigned int		nr_trylock;
58
	/* these times are in nano sec. */
59 60 61
	u64			wait_time_total;
	u64			wait_time_min;
	u64			wait_time_max;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

	int			discard; /* flag of blacklist */
};

/*
 * States of lock_seq_stat
 *
 * UNINITIALIZED is required for detecting first event of acquire.
 * As the nature of lock events, there is no guarantee
 * that the first event for the locks are acquire,
 * it can be acquired, contended or release.
 */
#define SEQ_STATE_UNINITIALIZED      0	       /* initial state */
#define SEQ_STATE_RELEASED	1
#define SEQ_STATE_ACQUIRING	2
#define SEQ_STATE_ACQUIRED	3
#define SEQ_STATE_READ_ACQUIRED	4
#define SEQ_STATE_CONTENDED	5

/*
 * MAX_LOCK_DEPTH
 * Imported from include/linux/sched.h.
 * Should this be synchronized?
 */
#define MAX_LOCK_DEPTH 48

/*
 * struct lock_seq_stat:
 * Place to put on state of one lock sequence
 * 1) acquire -> acquired -> release
 * 2) acquire -> contended -> acquired -> release
 * 3) acquire (with read or try) -> release
 * 4) Are there other patterns?
 */
struct lock_seq_stat {
	struct list_head        list;
	int			state;
	u64			prev_event_time;
	void                    *addr;

	int                     read_count;
103 104
};

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
struct thread_stat {
	struct rb_node		rb;

	u32                     tid;
	struct list_head        seq_list;
};

static struct rb_root		thread_stats;

static struct thread_stat *thread_stat_find(u32 tid)
{
	struct rb_node *node;
	struct thread_stat *st;

	node = thread_stats.rb_node;
	while (node) {
		st = container_of(node, struct thread_stat, rb);
		if (st->tid == tid)
			return st;
		else if (tid < st->tid)
			node = node->rb_left;
		else
			node = node->rb_right;
	}

	return NULL;
}

static void thread_stat_insert(struct thread_stat *new)
{
	struct rb_node **rb = &thread_stats.rb_node;
	struct rb_node *parent = NULL;
	struct thread_stat *p;

	while (*rb) {
		p = container_of(*rb, struct thread_stat, rb);
		parent = *rb;

		if (new->tid < p->tid)
			rb = &(*rb)->rb_left;
		else if (new->tid > p->tid)
			rb = &(*rb)->rb_right;
		else
			BUG_ON("inserting invalid thread_stat\n");
	}

	rb_link_node(&new->rb, parent, rb);
	rb_insert_color(&new->rb, &thread_stats);
}

static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
{
	struct thread_stat *st;

	st = thread_stat_find(tid);
	if (st)
		return st;

	st = zalloc(sizeof(struct thread_stat));
164 165 166 167
	if (!st) {
		pr_err("memory allocation failed\n");
		return NULL;
	}
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

	st->tid = tid;
	INIT_LIST_HEAD(&st->seq_list);

	thread_stat_insert(st);

	return st;
}

static struct thread_stat *thread_stat_findnew_first(u32 tid);
static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
	thread_stat_findnew_first;

static struct thread_stat *thread_stat_findnew_first(u32 tid)
{
	struct thread_stat *st;

	st = zalloc(sizeof(struct thread_stat));
186 187 188 189
	if (!st) {
		pr_err("memory allocation failed\n");
		return NULL;
	}
190 191 192 193 194 195 196 197 198 199
	st->tid = tid;
	INIT_LIST_HEAD(&st->seq_list);

	rb_link_node(&st->rb, NULL, &thread_stats.rb_node);
	rb_insert_color(&st->rb, &thread_stats);

	thread_stat_findnew = thread_stat_findnew_after_first;
	return st;
}

200
/* build simple key function one is bigger than two */
201
#define SINGLE_KEY(member)						\
202 203 204 205 206 207 208 209 210 211 212
	static int lock_stat_key_ ## member(struct lock_stat *one,	\
					 struct lock_stat *two)		\
	{								\
		return one->member > two->member;			\
	}

SINGLE_KEY(nr_acquired)
SINGLE_KEY(nr_contended)
SINGLE_KEY(wait_time_total)
SINGLE_KEY(wait_time_max)

213 214 215 216 217 218 219 220 221 222 223 224
static int lock_stat_key_wait_time_min(struct lock_stat *one,
					struct lock_stat *two)
{
	u64 s1 = one->wait_time_min;
	u64 s2 = two->wait_time_min;
	if (s1 == ULLONG_MAX)
		s1 = 0;
	if (s2 == ULLONG_MAX)
		s2 = 0;
	return s1 > s2;
}

225 226 227 228 229 230
struct lock_key {
	/*
	 * name: the value for specify by user
	 * this should be simpler than raw name of member
	 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
	 */
231 232
	const char		*name;
	int			(*key)(struct lock_stat*, struct lock_stat*);
233 234
};

235 236 237 238 239
static const char		*sort_key = "acquired";

static int			(*compare)(struct lock_stat *, struct lock_stat *);

static struct rb_root		result;	/* place to store sorted data */
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

#define DEF_KEY_LOCK(name, fn_suffix)	\
	{ #name, lock_stat_key_ ## fn_suffix }
struct lock_key keys[] = {
	DEF_KEY_LOCK(acquired, nr_acquired),
	DEF_KEY_LOCK(contended, nr_contended),
	DEF_KEY_LOCK(wait_total, wait_time_total),
	DEF_KEY_LOCK(wait_min, wait_time_min),
	DEF_KEY_LOCK(wait_max, wait_time_max),

	/* extra comparisons much complicated should be here */

	{ NULL, NULL }
};

255
static int select_key(void)
256 257 258 259 260 261
{
	int i;

	for (i = 0; keys[i].name; i++) {
		if (!strcmp(keys[i].name, sort_key)) {
			compare = keys[i].key;
262
			return 0;
263 264 265
		}
	}

266 267 268
	pr_err("Unknown compare key: %s\n", sort_key);

	return -1;
269 270 271
}

static void insert_to_result(struct lock_stat *st,
272
			     int (*bigger)(struct lock_stat *, struct lock_stat *))
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
{
	struct rb_node **rb = &result.rb_node;
	struct rb_node *parent = NULL;
	struct lock_stat *p;

	while (*rb) {
		p = container_of(*rb, struct lock_stat, rb);
		parent = *rb;

		if (bigger(st, p))
			rb = &(*rb)->rb_left;
		else
			rb = &(*rb)->rb_right;
	}

	rb_link_node(&st->rb, parent, rb);
	rb_insert_color(&st->rb, &result);
}

/* returns left most element of result, and erase it */
static struct lock_stat *pop_from_result(void)
{
	struct rb_node *node = result.rb_node;

	if (!node)
		return NULL;

	while (node->rb_left)
		node = node->rb_left;

	rb_erase(node, &result);
	return container_of(node, struct lock_stat, rb);
}

307
static struct lock_stat *lock_stat_findnew(void *addr, const char *name)
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
{
	struct list_head *entry = lockhashentry(addr);
	struct lock_stat *ret, *new;

	list_for_each_entry(ret, entry, hash_entry) {
		if (ret->addr == addr)
			return ret;
	}

	new = zalloc(sizeof(struct lock_stat));
	if (!new)
		goto alloc_failed;

	new->addr = addr;
	new->name = zalloc(sizeof(char) * strlen(name) + 1);
	if (!new->name)
		goto alloc_failed;
	strcpy(new->name, name);

	new->wait_time_min = ULLONG_MAX;

	list_add(&new->hash_entry, entry);
	return new;

alloc_failed:
333 334
	pr_err("memory allocation failed\n");
	return NULL;
335 336
}

337
static const char *input_name;
338 339

struct raw_event_sample {
340 341
	u32			size;
	char			data[0];
342 343 344
};

struct trace_acquire_event {
345 346
	void			*addr;
	const char		*name;
347
	int			flag;
348 349 350
};

struct trace_acquired_event {
351 352
	void			*addr;
	const char		*name;
353 354 355
};

struct trace_contended_event {
356 357
	void			*addr;
	const char		*name;
358 359 360
};

struct trace_release_event {
361 362
	void			*addr;
	const char		*name;
363 364 365
};

struct trace_lock_handler {
366
	int (*acquire_event)(struct trace_acquire_event *,
367
			      const struct perf_sample *sample);
368

369
	int (*acquired_event)(struct trace_acquired_event *,
370
			       const struct perf_sample *sample);
371

372
	int (*contended_event)(struct trace_contended_event *,
373
				const struct perf_sample *sample);
374

375
	int (*release_event)(struct trace_release_event *,
376
			      const struct perf_sample *sample);
377 378
};

379 380 381 382 383 384 385 386 387 388
static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
{
	struct lock_seq_stat *seq;

	list_for_each_entry(seq, &ts->seq_list, list) {
		if (seq->addr == addr)
			return seq;
	}

	seq = zalloc(sizeof(struct lock_seq_stat));
389 390 391 392
	if (!seq) {
		pr_err("memory allocation failed\n");
		return NULL;
	}
393 394 395 396 397 398 399
	seq->state = SEQ_STATE_UNINITIALIZED;
	seq->addr = addr;

	list_add(&seq->list, &ts->seq_list);
	return seq;
}

400 401 402 403 404 405 406 407 408
enum broken_state {
	BROKEN_ACQUIRE,
	BROKEN_ACQUIRED,
	BROKEN_CONTENDED,
	BROKEN_RELEASE,
	BROKEN_MAX,
};

static int bad_hist[BROKEN_MAX];
409

410 411 412 413 414
enum acquire_flags {
	TRY_LOCK = 1,
	READ_LOCK = 2,
};

415
static int
416
report_lock_acquire_event(struct trace_acquire_event *acquire_event,
417
			  const struct perf_sample *sample)
418
{
419 420 421 422 423
	struct lock_stat *ls;
	struct thread_stat *ts;
	struct lock_seq_stat *seq;

	ls = lock_stat_findnew(acquire_event->addr, acquire_event->name);
424 425
	if (!ls)
		return -1;
426
	if (ls->discard)
427
		return 0;
428

429
	ts = thread_stat_findnew(sample->tid);
430 431 432
	if (!ts)
		return -1;

433
	seq = get_seq(ts, acquire_event->addr);
434 435
	if (!seq)
		return -1;
436

437 438 439 440 441 442
	switch (seq->state) {
	case SEQ_STATE_UNINITIALIZED:
	case SEQ_STATE_RELEASED:
		if (!acquire_event->flag) {
			seq->state = SEQ_STATE_ACQUIRING;
		} else {
443
			if (acquire_event->flag & TRY_LOCK)
444
				ls->nr_trylock++;
445
			if (acquire_event->flag & READ_LOCK)
446 447 448 449 450 451 452
				ls->nr_readlock++;
			seq->state = SEQ_STATE_READ_ACQUIRED;
			seq->read_count = 1;
			ls->nr_acquired++;
		}
		break;
	case SEQ_STATE_READ_ACQUIRED:
453
		if (acquire_event->flag & READ_LOCK) {
454 455 456 457 458 459
			seq->read_count++;
			ls->nr_acquired++;
			goto end;
		} else {
			goto broken;
		}
460
		break;
461 462 463 464 465 466
	case SEQ_STATE_ACQUIRED:
	case SEQ_STATE_ACQUIRING:
	case SEQ_STATE_CONTENDED:
broken:
		/* broken lock sequence, discard it */
		ls->discard = 1;
467
		bad_hist[BROKEN_ACQUIRE]++;
468 469 470
		list_del(&seq->list);
		free(seq);
		goto end;
471 472
		break;
	default:
473
		BUG_ON("Unknown state of lock sequence found!\n");
474 475 476
		break;
	}

477
	ls->nr_acquire++;
478
	seq->prev_event_time = sample->time;
479
end:
480
	return 0;
481 482
}

483
static int
484
report_lock_acquired_event(struct trace_acquired_event *acquired_event,
485
			   const struct perf_sample *sample)
486
{
487
	u64 timestamp = sample->time;
488 489 490 491
	struct lock_stat *ls;
	struct thread_stat *ts;
	struct lock_seq_stat *seq;
	u64 contended_term;
492

493
	ls = lock_stat_findnew(acquired_event->addr, acquired_event->name);
494 495
	if (!ls)
		return -1;
496
	if (ls->discard)
497
		return 0;
498

499
	ts = thread_stat_findnew(sample->tid);
500 501 502
	if (!ts)
		return -1;

503
	seq = get_seq(ts, acquired_event->addr);
504 505
	if (!seq)
		return -1;
506

507 508 509
	switch (seq->state) {
	case SEQ_STATE_UNINITIALIZED:
		/* orphan event, do nothing */
510
		return 0;
511
	case SEQ_STATE_ACQUIRING:
512
		break;
513 514 515 516 517
	case SEQ_STATE_CONTENDED:
		contended_term = timestamp - seq->prev_event_time;
		ls->wait_time_total += contended_term;
		if (contended_term < ls->wait_time_min)
			ls->wait_time_min = contended_term;
518
		if (ls->wait_time_max < contended_term)
519
			ls->wait_time_max = contended_term;
520
		break;
521 522 523 524 525
	case SEQ_STATE_RELEASED:
	case SEQ_STATE_ACQUIRED:
	case SEQ_STATE_READ_ACQUIRED:
		/* broken lock sequence, discard it */
		ls->discard = 1;
526
		bad_hist[BROKEN_ACQUIRED]++;
527 528 529 530 531
		list_del(&seq->list);
		free(seq);
		goto end;
		break;

532
	default:
533
		BUG_ON("Unknown state of lock sequence found!\n");
534 535 536
		break;
	}

537 538 539 540
	seq->state = SEQ_STATE_ACQUIRED;
	ls->nr_acquired++;
	seq->prev_event_time = timestamp;
end:
541
	return 0;
542 543
}

544
static int
545
report_lock_contended_event(struct trace_contended_event *contended_event,
546
			    const struct perf_sample *sample)
547
{
548 549 550 551 552
	struct lock_stat *ls;
	struct thread_stat *ts;
	struct lock_seq_stat *seq;

	ls = lock_stat_findnew(contended_event->addr, contended_event->name);
553 554
	if (!ls)
		return -1;
555
	if (ls->discard)
556
		return 0;
557

558
	ts = thread_stat_findnew(sample->tid);
559 560 561
	if (!ts)
		return -1;

562
	seq = get_seq(ts, contended_event->addr);
563 564
	if (!seq)
		return -1;
565

566 567 568
	switch (seq->state) {
	case SEQ_STATE_UNINITIALIZED:
		/* orphan event, do nothing */
569
		return 0;
570
	case SEQ_STATE_ACQUIRING:
571
		break;
572 573 574 575 576 577
	case SEQ_STATE_RELEASED:
	case SEQ_STATE_ACQUIRED:
	case SEQ_STATE_READ_ACQUIRED:
	case SEQ_STATE_CONTENDED:
		/* broken lock sequence, discard it */
		ls->discard = 1;
578
		bad_hist[BROKEN_CONTENDED]++;
579 580 581
		list_del(&seq->list);
		free(seq);
		goto end;
582 583
		break;
	default:
584
		BUG_ON("Unknown state of lock sequence found!\n");
585 586 587
		break;
	}

588 589
	seq->state = SEQ_STATE_CONTENDED;
	ls->nr_contended++;
590
	seq->prev_event_time = sample->time;
591
end:
592
	return 0;
593 594
}

595
static int
596
report_lock_release_event(struct trace_release_event *release_event,
597
			  const struct perf_sample *sample)
598
{
599 600 601
	struct lock_stat *ls;
	struct thread_stat *ts;
	struct lock_seq_stat *seq;
602

603
	ls = lock_stat_findnew(release_event->addr, release_event->name);
604 605
	if (!ls)
		return -1;
606
	if (ls->discard)
607
		return 0;
608

609
	ts = thread_stat_findnew(sample->tid);
610 611 612
	if (!ts)
		return -1;

613
	seq = get_seq(ts, release_event->addr);
614 615
	if (!seq)
		return -1;
616

617 618 619 620 621 622 623 624 625 626 627
	switch (seq->state) {
	case SEQ_STATE_UNINITIALIZED:
		goto end;
		break;
	case SEQ_STATE_ACQUIRED:
		break;
	case SEQ_STATE_READ_ACQUIRED:
		seq->read_count--;
		BUG_ON(seq->read_count < 0);
		if (!seq->read_count) {
			ls->nr_release++;
628 629
			goto end;
		}
630 631 632 633 634 635
		break;
	case SEQ_STATE_ACQUIRING:
	case SEQ_STATE_CONTENDED:
	case SEQ_STATE_RELEASED:
		/* broken lock sequence, discard it */
		ls->discard = 1;
636
		bad_hist[BROKEN_RELEASE]++;
637
		goto free_seq;
638 639
		break;
	default:
640
		BUG_ON("Unknown state of lock sequence found!\n");
641 642 643
		break;
	}

644 645 646 647
	ls->nr_release++;
free_seq:
	list_del(&seq->list);
	free(seq);
648
end:
649
	return 0;
650 651 652 653
}

/* lock oriented handlers */
/* TODO: handlers for CPU oriented, thread oriented */
654 655 656 657 658
static struct trace_lock_handler report_lock_ops  = {
	.acquire_event		= report_lock_acquire_event,
	.acquired_event		= report_lock_acquired_event,
	.contended_event	= report_lock_contended_event,
	.release_event		= report_lock_release_event,
659 660 661 662
};

static struct trace_lock_handler *trace_handler;

663
static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel,
664
					     struct perf_sample *sample)
665 666
{
	struct trace_acquire_event acquire_event;
667 668
	struct event_format *event = evsel->tp_format;
	void *data = sample->raw_data;
669
	u64 tmp;		/* this is required for casting... */
670
	int rc = 0;
671 672 673 674

	tmp = raw_field_value(event, "lockdep_addr", data);
	memcpy(&acquire_event.addr, &tmp, sizeof(void *));
	acquire_event.name = (char *)raw_field_ptr(event, "name", data);
675
	acquire_event.flag = (int)raw_field_value(event, "flag", data);
676

677
	if (trace_handler->acquire_event)
678 679 680
		rc = trace_handler->acquire_event(&acquire_event, sample);

	return rc;
681 682
}

683
static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel,
684
					      struct perf_sample *sample)
685 686
{
	struct trace_acquired_event acquired_event;
687 688
	struct event_format *event = evsel->tp_format;
	void *data = sample->raw_data;
689
	u64 tmp;		/* this is required for casting... */
690
	int rc = 0;
691 692 693 694 695

	tmp = raw_field_value(event, "lockdep_addr", data);
	memcpy(&acquired_event.addr, &tmp, sizeof(void *));
	acquired_event.name = (char *)raw_field_ptr(event, "name", data);

696 697 698 699
	if (trace_handler->acquired_event)
		rc = trace_handler->acquired_event(&acquired_event, sample);

	return rc;
700 701
}

702
static int perf_evsel__process_lock_contended(struct perf_evsel *evsel,
703
					       struct perf_sample *sample)
704 705
{
	struct trace_contended_event contended_event;
706 707
	struct event_format *event = evsel->tp_format;
	void *data = sample->raw_data;
708
	u64 tmp;		/* this is required for casting... */
709
	int rc = 0;
710 711 712 713 714

	tmp = raw_field_value(event, "lockdep_addr", data);
	memcpy(&contended_event.addr, &tmp, sizeof(void *));
	contended_event.name = (char *)raw_field_ptr(event, "name", data);

715 716 717 718
	if (trace_handler->contended_event)
		rc = trace_handler->contended_event(&contended_event, sample);

	return rc;
719 720
}

721
static int perf_evsel__process_lock_release(struct perf_evsel *evsel,
722
					     struct perf_sample *sample)
723 724
{
	struct trace_release_event release_event;
725 726
	struct event_format *event = evsel->tp_format;
	void *data = sample->raw_data;
727
	u64 tmp;		/* this is required for casting... */
728
	int rc = 0;
729 730 731 732 733

	tmp = raw_field_value(event, "lockdep_addr", data);
	memcpy(&release_event.addr, &tmp, sizeof(void *));
	release_event.name = (char *)raw_field_ptr(event, "name", data);

734 735 736 737
	if (trace_handler->release_event)
		rc = trace_handler->release_event(&release_event, sample);

	return rc;
738 739
}

740
static int perf_evsel__process_lock_event(struct perf_evsel *evsel,
741
					   struct perf_sample *sample)
742
{
743
	struct event_format *event = evsel->tp_format;
744
	int rc = 0;
745 746

	if (!strcmp(event->name, "lock_acquire"))
747
		rc = perf_evsel__process_lock_acquire(evsel, sample);
748
	if (!strcmp(event->name, "lock_acquired"))
749
		rc = perf_evsel__process_lock_acquired(evsel, sample);
750
	if (!strcmp(event->name, "lock_contended"))
751
		rc = perf_evsel__process_lock_contended(evsel, sample);
752
	if (!strcmp(event->name, "lock_release"))
753 754 755
		rc = perf_evsel__process_lock_release(evsel, sample);

	return rc;
756 757
}

758 759 760 761 762 763 764 765
static void print_bad_events(int bad, int total)
{
	/* Output for debug, this have to be removed */
	int i;
	const char *name[4] =
		{ "acquire", "acquired", "contended", "release" };

	pr_info("\n=== output for debug===\n\n");
766 767
	pr_info("bad: %d, total: %d\n", bad, total);
	pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100);
768 769 770 771 772
	pr_info("histogram of events caused bad sequence\n");
	for (i = 0; i < BROKEN_MAX; i++)
		pr_info(" %10s: %d\n", name[i], bad_hist[i]);
}

773 774 775 776 777
/* TODO: various way to print, coloring, nano or milli sec */
static void print_result(void)
{
	struct lock_stat *st;
	char cut_name[20];
778
	int bad, total;
779

780 781 782
	pr_info("%20s ", "Name");
	pr_info("%10s ", "acquired");
	pr_info("%10s ", "contended");
783

784 785 786
	pr_info("%15s ", "total wait (ns)");
	pr_info("%15s ", "max wait (ns)");
	pr_info("%15s ", "min wait (ns)");
787

788
	pr_info("\n\n");
789

790
	bad = total = 0;
791
	while ((st = pop_from_result())) {
792 793 794 795 796
		total++;
		if (st->discard) {
			bad++;
			continue;
		}
797 798 799 800
		bzero(cut_name, 20);

		if (strlen(st->name) < 16) {
			/* output raw name */
801
			pr_info("%20s ", st->name);
802 803 804 805 806 807 808
		} else {
			strncpy(cut_name, st->name, 16);
			cut_name[16] = '.';
			cut_name[17] = '.';
			cut_name[18] = '.';
			cut_name[19] = '\0';
			/* cut off name for saving output style */
809
			pr_info("%20s ", cut_name);
810 811
		}

812 813
		pr_info("%10u ", st->nr_acquired);
		pr_info("%10u ", st->nr_contended);
814

815 816 817
		pr_info("%15" PRIu64 " ", st->wait_time_total);
		pr_info("%15" PRIu64 " ", st->wait_time_max);
		pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ?
818
		       0 : st->wait_time_min);
819
		pr_info("\n");
820
	}
821

822
	print_bad_events(bad, total);
823 824
}

825
static bool info_threads, info_map;
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843

static void dump_threads(void)
{
	struct thread_stat *st;
	struct rb_node *node;
	struct thread *t;

	pr_info("%10s: comm\n", "Thread ID");

	node = rb_first(&thread_stats);
	while (node) {
		st = container_of(node, struct thread_stat, rb);
		t = perf_session__findnew(session, st->tid);
		pr_info("%10d: %s\n", st->tid, t->comm);
		node = rb_next(node);
	};
}

844 845 846 847 848
static void dump_map(void)
{
	unsigned int i;
	struct lock_stat *st;

849
	pr_info("Address of instance: name of class\n");
850 851
	for (i = 0; i < LOCKHASH_SIZE; i++) {
		list_for_each_entry(st, &lockhash_table[i], hash_entry) {
852
			pr_info(" %p: %s\n", st->addr, st->name);
853 854 855 856
		}
	}
}

857
static int dump_info(void)
858
{
859 860
	int rc = 0;

861 862 863 864
	if (info_threads)
		dump_threads();
	else if (info_map)
		dump_map();
865 866 867 868 869 870
	else {
		rc = -1;
		pr_err("Unknown type of information\n");
	}

	return rc;
871 872
}

873
static int process_sample_event(struct perf_tool *tool __used,
874
				union perf_event *event,
875
				struct perf_sample *sample,
876
				struct perf_evsel *evsel,
877
				struct machine *machine)
878
{
879
	struct thread *thread = machine__findnew_thread(machine, sample->tid);
880 881 882

	if (thread == NULL) {
		pr_debug("problem processing %d event, skipping it.\n",
883
			event->header.type);
884 885 886
		return -1;
	}

887
	return perf_evsel__process_lock_event(evsel, sample);
888 889
}

890
static struct perf_tool eops = {
891
	.sample			= process_sample_event,
892
	.comm			= perf_event__process_comm,
893
	.ordered_samples	= true,
894 895 896 897
};

static int read_events(void)
{
898
	session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
899 900 901 902
	if (!session) {
		pr_err("Initializing perf session failed\n");
		return -1;
	}
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918

	return perf_session__process_events(session, &eops);
}

static void sort_result(void)
{
	unsigned int i;
	struct lock_stat *st;

	for (i = 0; i < LOCKHASH_SIZE; i++) {
		list_for_each_entry(st, &lockhash_table[i], hash_entry) {
			insert_to_result(st, compare);
		}
	}
}

919
static int __cmd_report(void)
920 921
{
	setup_pager();
922 923 924 925 926

	if ((select_key() != 0) ||
	    (read_events() != 0))
		return -1;

927 928
	sort_result();
	print_result();
929 930

	return 0;
931 932
}

933 934
static const char * const report_usage[] = {
	"perf lock report [<options>]",
935 936 937
	NULL
};

938
static const struct option report_options[] = {
939
	OPT_STRING('k', "key", &sort_key, "acquired",
940
		    "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"),
941 942 943 944
	/* TODO: type */
	OPT_END()
};

945 946 947 948 949 950 951 952 953
static const char * const info_usage[] = {
	"perf lock info [<options>]",
	NULL
};

static const struct option info_options[] = {
	OPT_BOOLEAN('t', "threads", &info_threads,
		    "dump thread list in perf.data"),
	OPT_BOOLEAN('m', "map", &info_map,
954
		    "map of lock instances (address:name table)"),
955 956 957
	OPT_END()
};

958
static const char * const lock_usage[] = {
959
	"perf lock [<options>] {record|report|script|info}",
960 961 962 963
	NULL
};

static const struct option lock_options[] = {
964
	OPT_STRING('i', "input", &input_name, "file", "input file name"),
965
	OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
966
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
967 968 969
	OPT_END()
};

970 971 972 973 974 975 976
static const char * const lock_tracepoints[] = {
	"lock:lock_acquire",    /* CONFIG_LOCKDEP */
	"lock:lock_acquired",   /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
	"lock:lock_contended",  /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
	"lock:lock_release",    /* CONFIG_LOCKDEP */
};

977 978 979 980 981 982 983 984 985 986 987 988 989
static const char *record_args[] = {
	"record",
	"-R",
	"-f",
	"-m", "1024",
	"-c", "1",
};

static int __cmd_record(int argc, const char **argv)
{
	unsigned int rec_argc, i, j;
	const char **rec_argv;

990 991 992 993 994 995 996 997 998
	for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {
		if (!is_valid_tracepoint(lock_tracepoints[i])) {
				pr_err("tracepoint %s is not enabled. "
				       "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
				       lock_tracepoints[i]);
				return 1;
		}
	}

999
	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1000 1001
	/* factor of 2 is for -e in front of each tracepoint */
	rec_argc += 2 * ARRAY_SIZE(lock_tracepoints);
1002

1003
	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1004 1005 1006
	if (rec_argv == NULL)
		return -ENOMEM;

1007 1008 1009
	for (i = 0; i < ARRAY_SIZE(record_args); i++)
		rec_argv[i] = strdup(record_args[i]);

1010 1011 1012 1013 1014
	for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) {
		rec_argv[i++] = "-e";
		rec_argv[i++] = strdup(lock_tracepoints[j]);
	}

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	for (j = 1; j < (unsigned int)argc; j++, i++)
		rec_argv[i] = argv[j];

	BUG_ON(i != rec_argc);

	return cmd_record(i, rec_argv, NULL);
}

int cmd_lock(int argc, const char **argv, const char *prefix __used)
{
	unsigned int i;
1026
	int rc = 0;
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038

	symbol__init();
	for (i = 0; i < LOCKHASH_SIZE; i++)
		INIT_LIST_HEAD(lockhash_table + i);

	argc = parse_options(argc, argv, lock_options, lock_usage,
			     PARSE_OPT_STOP_AT_NON_OPTION);
	if (!argc)
		usage_with_options(lock_usage, lock_options);

	if (!strncmp(argv[0], "rec", 3)) {
		return __cmd_record(argc, argv);
1039 1040
	} else if (!strncmp(argv[0], "report", 6)) {
		trace_handler = &report_lock_ops;
1041 1042
		if (argc) {
			argc = parse_options(argc, argv,
1043
					     report_options, report_usage, 0);
1044
			if (argc)
1045
				usage_with_options(report_usage, report_options);
1046
		}
1047
		__cmd_report();
1048 1049 1050
	} else if (!strcmp(argv[0], "script")) {
		/* Aliased to 'perf script' */
		return cmd_script(argc, argv, prefix);
1051 1052 1053 1054 1055 1056 1057
	} else if (!strcmp(argv[0], "info")) {
		if (argc) {
			argc = parse_options(argc, argv,
					     info_options, info_usage, 0);
			if (argc)
				usage_with_options(info_usage, info_options);
		}
1058 1059
		/* recycling report_lock_ops */
		trace_handler = &report_lock_ops;
1060
		setup_pager();
1061 1062 1063 1064
		if (read_events() != 0)
			rc = -1;
		else
			rc = dump_info();
1065 1066 1067 1068
	} else {
		usage_with_options(lock_usage, lock_options);
	}

1069
	return rc;
1070
}