1 // SPDX-License-Identifier: GPL-2.0 2 #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_handler 8 #include "util/evsel.h" 9 #include "util/symbol.h" 10 #include "util/thread.h" 11 #include "util/header.h" 12 13 #include <subcmd/pager.h> 14 #include <subcmd/parse-options.h> 15 #include "util/trace-event.h" 16 17 #include "util/debug.h" 18 #include "util/session.h" 19 #include "util/tool.h" 20 #include "util/data.h" 21 22 #include <sys/types.h> 23 #include <sys/prctl.h> 24 #include <semaphore.h> 25 #include <pthread.h> 26 #include <math.h> 27 #include <limits.h> 28 29 #include <linux/list.h> 30 #include <linux/hash.h> 31 #include <linux/kernel.h> 32 #include <linux/zalloc.h> 33 #include <linux/err.h> 34 35 static struct perf_session *session; 36 37 /* based on kernel/lockdep.c */ 38 #define LOCKHASH_BITS 12 39 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) 40 41 static struct list_head lockhash_table[LOCKHASH_SIZE]; 42 43 #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) 44 #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) 45 46 struct lock_stat { 47 struct list_head hash_entry; 48 struct rb_node rb; /* used for sorting */ 49 50 /* 51 * FIXME: evsel__intval() returns u64, 52 * so address of lockdep_map should be treated as 64bit. 53 * Is there more better solution? 54 */ 55 void *addr; /* address of lockdep_map, used as ID */ 56 char *name; /* for strcpy(), we cannot use const */ 57 58 unsigned int nr_acquire; 59 unsigned int nr_acquired; 60 unsigned int nr_contended; 61 unsigned int nr_release; 62 63 unsigned int nr_readlock; 64 unsigned int nr_trylock; 65 66 /* these times are in nano sec. */ 67 u64 avg_wait_time; 68 u64 wait_time_total; 69 u64 wait_time_min; 70 u64 wait_time_max; 71 72 int discard; /* flag of blacklist */ 73 }; 74 75 /* 76 * States of lock_seq_stat 77 * 78 * UNINITIALIZED is required for detecting first event of acquire. 79 * As the nature of lock events, there is no guarantee 80 * that the first event for the locks are acquire, 81 * it can be acquired, contended or release. 82 */ 83 #define SEQ_STATE_UNINITIALIZED 0 /* initial state */ 84 #define SEQ_STATE_RELEASED 1 85 #define SEQ_STATE_ACQUIRING 2 86 #define SEQ_STATE_ACQUIRED 3 87 #define SEQ_STATE_READ_ACQUIRED 4 88 #define SEQ_STATE_CONTENDED 5 89 90 /* 91 * MAX_LOCK_DEPTH 92 * Imported from include/linux/sched.h. 93 * Should this be synchronized? 94 */ 95 #define MAX_LOCK_DEPTH 48 96 97 /* 98 * struct lock_seq_stat: 99 * Place to put on state of one lock sequence 100 * 1) acquire -> acquired -> release 101 * 2) acquire -> contended -> acquired -> release 102 * 3) acquire (with read or try) -> release 103 * 4) Are there other patterns? 104 */ 105 struct lock_seq_stat { 106 struct list_head list; 107 int state; 108 u64 prev_event_time; 109 void *addr; 110 111 int read_count; 112 }; 113 114 struct thread_stat { 115 struct rb_node rb; 116 117 u32 tid; 118 struct list_head seq_list; 119 }; 120 121 static struct rb_root thread_stats; 122 123 static struct thread_stat *thread_stat_find(u32 tid) 124 { 125 struct rb_node *node; 126 struct thread_stat *st; 127 128 node = thread_stats.rb_node; 129 while (node) { 130 st = container_of(node, struct thread_stat, rb); 131 if (st->tid == tid) 132 return st; 133 else if (tid < st->tid) 134 node = node->rb_left; 135 else 136 node = node->rb_right; 137 } 138 139 return NULL; 140 } 141 142 static void thread_stat_insert(struct thread_stat *new) 143 { 144 struct rb_node **rb = &thread_stats.rb_node; 145 struct rb_node *parent = NULL; 146 struct thread_stat *p; 147 148 while (*rb) { 149 p = container_of(*rb, struct thread_stat, rb); 150 parent = *rb; 151 152 if (new->tid < p->tid) 153 rb = &(*rb)->rb_left; 154 else if (new->tid > p->tid) 155 rb = &(*rb)->rb_right; 156 else 157 BUG_ON("inserting invalid thread_stat\n"); 158 } 159 160 rb_link_node(&new->rb, parent, rb); 161 rb_insert_color(&new->rb, &thread_stats); 162 } 163 164 static struct thread_stat *thread_stat_findnew_after_first(u32 tid) 165 { 166 struct thread_stat *st; 167 168 st = thread_stat_find(tid); 169 if (st) 170 return st; 171 172 st = zalloc(sizeof(struct thread_stat)); 173 if (!st) { 174 pr_err("memory allocation failed\n"); 175 return NULL; 176 } 177 178 st->tid = tid; 179 INIT_LIST_HEAD(&st->seq_list); 180 181 thread_stat_insert(st); 182 183 return st; 184 } 185 186 static struct thread_stat *thread_stat_findnew_first(u32 tid); 187 static struct thread_stat *(*thread_stat_findnew)(u32 tid) = 188 thread_stat_findnew_first; 189 190 static struct thread_stat *thread_stat_findnew_first(u32 tid) 191 { 192 struct thread_stat *st; 193 194 st = zalloc(sizeof(struct thread_stat)); 195 if (!st) { 196 pr_err("memory allocation failed\n"); 197 return NULL; 198 } 199 st->tid = tid; 200 INIT_LIST_HEAD(&st->seq_list); 201 202 rb_link_node(&st->rb, NULL, &thread_stats.rb_node); 203 rb_insert_color(&st->rb, &thread_stats); 204 205 thread_stat_findnew = thread_stat_findnew_after_first; 206 return st; 207 } 208 209 /* build simple key function one is bigger than two */ 210 #define SINGLE_KEY(member) \ 211 static int lock_stat_key_ ## member(struct lock_stat *one, \ 212 struct lock_stat *two) \ 213 { \ 214 return one->member > two->member; \ 215 } 216 217 SINGLE_KEY(nr_acquired) 218 SINGLE_KEY(nr_contended) 219 SINGLE_KEY(avg_wait_time) 220 SINGLE_KEY(wait_time_total) 221 SINGLE_KEY(wait_time_max) 222 223 static int lock_stat_key_wait_time_min(struct lock_stat *one, 224 struct lock_stat *two) 225 { 226 u64 s1 = one->wait_time_min; 227 u64 s2 = two->wait_time_min; 228 if (s1 == ULLONG_MAX) 229 s1 = 0; 230 if (s2 == ULLONG_MAX) 231 s2 = 0; 232 return s1 > s2; 233 } 234 235 struct lock_key { 236 /* 237 * name: the value for specify by user 238 * this should be simpler than raw name of member 239 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total 240 */ 241 const char *name; 242 int (*key)(struct lock_stat*, struct lock_stat*); 243 }; 244 245 static const char *sort_key = "acquired"; 246 247 static int (*compare)(struct lock_stat *, struct lock_stat *); 248 249 static struct rb_root result; /* place to store sorted data */ 250 251 #define DEF_KEY_LOCK(name, fn_suffix) \ 252 { #name, lock_stat_key_ ## fn_suffix } 253 struct lock_key keys[] = { 254 DEF_KEY_LOCK(acquired, nr_acquired), 255 DEF_KEY_LOCK(contended, nr_contended), 256 DEF_KEY_LOCK(avg_wait, avg_wait_time), 257 DEF_KEY_LOCK(wait_total, wait_time_total), 258 DEF_KEY_LOCK(wait_min, wait_time_min), 259 DEF_KEY_LOCK(wait_max, wait_time_max), 260 261 /* extra comparisons much complicated should be here */ 262 263 { NULL, NULL } 264 }; 265 266 static int select_key(void) 267 { 268 int i; 269 270 for (i = 0; keys[i].name; i++) { 271 if (!strcmp(keys[i].name, sort_key)) { 272 compare = keys[i].key; 273 return 0; 274 } 275 } 276 277 pr_err("Unknown compare key: %s\n", sort_key); 278 279 return -1; 280 } 281 282 static void insert_to_result(struct lock_stat *st, 283 int (*bigger)(struct lock_stat *, struct lock_stat *)) 284 { 285 struct rb_node **rb = &result.rb_node; 286 struct rb_node *parent = NULL; 287 struct lock_stat *p; 288 289 while (*rb) { 290 p = container_of(*rb, struct lock_stat, rb); 291 parent = *rb; 292 293 if (bigger(st, p)) 294 rb = &(*rb)->rb_left; 295 else 296 rb = &(*rb)->rb_right; 297 } 298 299 rb_link_node(&st->rb, parent, rb); 300 rb_insert_color(&st->rb, &result); 301 } 302 303 /* returns left most element of result, and erase it */ 304 static struct lock_stat *pop_from_result(void) 305 { 306 struct rb_node *node = result.rb_node; 307 308 if (!node) 309 return NULL; 310 311 while (node->rb_left) 312 node = node->rb_left; 313 314 rb_erase(node, &result); 315 return container_of(node, struct lock_stat, rb); 316 } 317 318 static struct lock_stat *lock_stat_findnew(void *addr, const char *name) 319 { 320 struct list_head *entry = lockhashentry(addr); 321 struct lock_stat *ret, *new; 322 323 list_for_each_entry(ret, entry, hash_entry) { 324 if (ret->addr == addr) 325 return ret; 326 } 327 328 new = zalloc(sizeof(struct lock_stat)); 329 if (!new) 330 goto alloc_failed; 331 332 new->addr = addr; 333 new->name = zalloc(sizeof(char) * strlen(name) + 1); 334 if (!new->name) { 335 free(new); 336 goto alloc_failed; 337 } 338 339 strcpy(new->name, name); 340 new->wait_time_min = ULLONG_MAX; 341 342 list_add(&new->hash_entry, entry); 343 return new; 344 345 alloc_failed: 346 pr_err("memory allocation failed\n"); 347 return NULL; 348 } 349 350 struct trace_lock_handler { 351 int (*acquire_event)(struct evsel *evsel, 352 struct perf_sample *sample); 353 354 int (*acquired_event)(struct evsel *evsel, 355 struct perf_sample *sample); 356 357 int (*contended_event)(struct evsel *evsel, 358 struct perf_sample *sample); 359 360 int (*release_event)(struct evsel *evsel, 361 struct perf_sample *sample); 362 }; 363 364 static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) 365 { 366 struct lock_seq_stat *seq; 367 368 list_for_each_entry(seq, &ts->seq_list, list) { 369 if (seq->addr == addr) 370 return seq; 371 } 372 373 seq = zalloc(sizeof(struct lock_seq_stat)); 374 if (!seq) { 375 pr_err("memory allocation failed\n"); 376 return NULL; 377 } 378 seq->state = SEQ_STATE_UNINITIALIZED; 379 seq->addr = addr; 380 381 list_add(&seq->list, &ts->seq_list); 382 return seq; 383 } 384 385 enum broken_state { 386 BROKEN_ACQUIRE, 387 BROKEN_ACQUIRED, 388 BROKEN_CONTENDED, 389 BROKEN_RELEASE, 390 BROKEN_MAX, 391 }; 392 393 static int bad_hist[BROKEN_MAX]; 394 395 enum acquire_flags { 396 TRY_LOCK = 1, 397 READ_LOCK = 2, 398 }; 399 400 static int report_lock_acquire_event(struct evsel *evsel, 401 struct perf_sample *sample) 402 { 403 void *addr; 404 struct lock_stat *ls; 405 struct thread_stat *ts; 406 struct lock_seq_stat *seq; 407 const char *name = evsel__strval(evsel, sample, "name"); 408 u64 tmp = evsel__intval(evsel, sample, "lockdep_addr"); 409 int flag = evsel__intval(evsel, sample, "flags"); 410 411 memcpy(&addr, &tmp, sizeof(void *)); 412 413 ls = lock_stat_findnew(addr, name); 414 if (!ls) 415 return -ENOMEM; 416 if (ls->discard) 417 return 0; 418 419 ts = thread_stat_findnew(sample->tid); 420 if (!ts) 421 return -ENOMEM; 422 423 seq = get_seq(ts, addr); 424 if (!seq) 425 return -ENOMEM; 426 427 switch (seq->state) { 428 case SEQ_STATE_UNINITIALIZED: 429 case SEQ_STATE_RELEASED: 430 if (!flag) { 431 seq->state = SEQ_STATE_ACQUIRING; 432 } else { 433 if (flag & TRY_LOCK) 434 ls->nr_trylock++; 435 if (flag & READ_LOCK) 436 ls->nr_readlock++; 437 seq->state = SEQ_STATE_READ_ACQUIRED; 438 seq->read_count = 1; 439 ls->nr_acquired++; 440 } 441 break; 442 case SEQ_STATE_READ_ACQUIRED: 443 if (flag & READ_LOCK) { 444 seq->read_count++; 445 ls->nr_acquired++; 446 goto end; 447 } else { 448 goto broken; 449 } 450 break; 451 case SEQ_STATE_ACQUIRED: 452 case SEQ_STATE_ACQUIRING: 453 case SEQ_STATE_CONTENDED: 454 broken: 455 /* broken lock sequence, discard it */ 456 ls->discard = 1; 457 bad_hist[BROKEN_ACQUIRE]++; 458 list_del_init(&seq->list); 459 free(seq); 460 goto end; 461 default: 462 BUG_ON("Unknown state of lock sequence found!\n"); 463 break; 464 } 465 466 ls->nr_acquire++; 467 seq->prev_event_time = sample->time; 468 end: 469 return 0; 470 } 471 472 static int report_lock_acquired_event(struct evsel *evsel, 473 struct perf_sample *sample) 474 { 475 void *addr; 476 struct lock_stat *ls; 477 struct thread_stat *ts; 478 struct lock_seq_stat *seq; 479 u64 contended_term; 480 const char *name = evsel__strval(evsel, sample, "name"); 481 u64 tmp = evsel__intval(evsel, sample, "lockdep_addr"); 482 483 memcpy(&addr, &tmp, sizeof(void *)); 484 485 ls = lock_stat_findnew(addr, name); 486 if (!ls) 487 return -ENOMEM; 488 if (ls->discard) 489 return 0; 490 491 ts = thread_stat_findnew(sample->tid); 492 if (!ts) 493 return -ENOMEM; 494 495 seq = get_seq(ts, addr); 496 if (!seq) 497 return -ENOMEM; 498 499 switch (seq->state) { 500 case SEQ_STATE_UNINITIALIZED: 501 /* orphan event, do nothing */ 502 return 0; 503 case SEQ_STATE_ACQUIRING: 504 break; 505 case SEQ_STATE_CONTENDED: 506 contended_term = sample->time - seq->prev_event_time; 507 ls->wait_time_total += contended_term; 508 if (contended_term < ls->wait_time_min) 509 ls->wait_time_min = contended_term; 510 if (ls->wait_time_max < contended_term) 511 ls->wait_time_max = contended_term; 512 break; 513 case SEQ_STATE_RELEASED: 514 case SEQ_STATE_ACQUIRED: 515 case SEQ_STATE_READ_ACQUIRED: 516 /* broken lock sequence, discard it */ 517 ls->discard = 1; 518 bad_hist[BROKEN_ACQUIRED]++; 519 list_del_init(&seq->list); 520 free(seq); 521 goto end; 522 default: 523 BUG_ON("Unknown state of lock sequence found!\n"); 524 break; 525 } 526 527 seq->state = SEQ_STATE_ACQUIRED; 528 ls->nr_acquired++; 529 ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0; 530 seq->prev_event_time = sample->time; 531 end: 532 return 0; 533 } 534 535 static int report_lock_contended_event(struct evsel *evsel, 536 struct perf_sample *sample) 537 { 538 void *addr; 539 struct lock_stat *ls; 540 struct thread_stat *ts; 541 struct lock_seq_stat *seq; 542 const char *name = evsel__strval(evsel, sample, "name"); 543 u64 tmp = evsel__intval(evsel, sample, "lockdep_addr"); 544 545 memcpy(&addr, &tmp, sizeof(void *)); 546 547 ls = lock_stat_findnew(addr, name); 548 if (!ls) 549 return -ENOMEM; 550 if (ls->discard) 551 return 0; 552 553 ts = thread_stat_findnew(sample->tid); 554 if (!ts) 555 return -ENOMEM; 556 557 seq = get_seq(ts, addr); 558 if (!seq) 559 return -ENOMEM; 560 561 switch (seq->state) { 562 case SEQ_STATE_UNINITIALIZED: 563 /* orphan event, do nothing */ 564 return 0; 565 case SEQ_STATE_ACQUIRING: 566 break; 567 case SEQ_STATE_RELEASED: 568 case SEQ_STATE_ACQUIRED: 569 case SEQ_STATE_READ_ACQUIRED: 570 case SEQ_STATE_CONTENDED: 571 /* broken lock sequence, discard it */ 572 ls->discard = 1; 573 bad_hist[BROKEN_CONTENDED]++; 574 list_del_init(&seq->list); 575 free(seq); 576 goto end; 577 default: 578 BUG_ON("Unknown state of lock sequence found!\n"); 579 break; 580 } 581 582 seq->state = SEQ_STATE_CONTENDED; 583 ls->nr_contended++; 584 ls->avg_wait_time = ls->wait_time_total/ls->nr_contended; 585 seq->prev_event_time = sample->time; 586 end: 587 return 0; 588 } 589 590 static int report_lock_release_event(struct evsel *evsel, 591 struct perf_sample *sample) 592 { 593 void *addr; 594 struct lock_stat *ls; 595 struct thread_stat *ts; 596 struct lock_seq_stat *seq; 597 const char *name = evsel__strval(evsel, sample, "name"); 598 u64 tmp = evsel__intval(evsel, sample, "lockdep_addr"); 599 600 memcpy(&addr, &tmp, sizeof(void *)); 601 602 ls = lock_stat_findnew(addr, name); 603 if (!ls) 604 return -ENOMEM; 605 if (ls->discard) 606 return 0; 607 608 ts = thread_stat_findnew(sample->tid); 609 if (!ts) 610 return -ENOMEM; 611 612 seq = get_seq(ts, addr); 613 if (!seq) 614 return -ENOMEM; 615 616 switch (seq->state) { 617 case SEQ_STATE_UNINITIALIZED: 618 goto end; 619 case SEQ_STATE_ACQUIRED: 620 break; 621 case SEQ_STATE_READ_ACQUIRED: 622 seq->read_count--; 623 BUG_ON(seq->read_count < 0); 624 if (seq->read_count) { 625 ls->nr_release++; 626 goto end; 627 } 628 break; 629 case SEQ_STATE_ACQUIRING: 630 case SEQ_STATE_CONTENDED: 631 case SEQ_STATE_RELEASED: 632 /* broken lock sequence, discard it */ 633 ls->discard = 1; 634 bad_hist[BROKEN_RELEASE]++; 635 goto free_seq; 636 default: 637 BUG_ON("Unknown state of lock sequence found!\n"); 638 break; 639 } 640 641 ls->nr_release++; 642 free_seq: 643 list_del_init(&seq->list); 644 free(seq); 645 end: 646 return 0; 647 } 648 649 /* lock oriented handlers */ 650 /* TODO: handlers for CPU oriented, thread oriented */ 651 static struct trace_lock_handler report_lock_ops = { 652 .acquire_event = report_lock_acquire_event, 653 .acquired_event = report_lock_acquired_event, 654 .contended_event = report_lock_contended_event, 655 .release_event = report_lock_release_event, 656 }; 657 658 static struct trace_lock_handler *trace_handler; 659 660 static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample) 661 { 662 if (trace_handler->acquire_event) 663 return trace_handler->acquire_event(evsel, sample); 664 return 0; 665 } 666 667 static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample) 668 { 669 if (trace_handler->acquired_event) 670 return trace_handler->acquired_event(evsel, sample); 671 return 0; 672 } 673 674 static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample) 675 { 676 if (trace_handler->contended_event) 677 return trace_handler->contended_event(evsel, sample); 678 return 0; 679 } 680 681 static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample) 682 { 683 if (trace_handler->release_event) 684 return trace_handler->release_event(evsel, sample); 685 return 0; 686 } 687 688 static void print_bad_events(int bad, int total) 689 { 690 /* Output for debug, this have to be removed */ 691 int i; 692 const char *name[4] = 693 { "acquire", "acquired", "contended", "release" }; 694 695 pr_info("\n=== output for debug===\n\n"); 696 pr_info("bad: %d, total: %d\n", bad, total); 697 pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100); 698 pr_info("histogram of events caused bad sequence\n"); 699 for (i = 0; i < BROKEN_MAX; i++) 700 pr_info(" %10s: %d\n", name[i], bad_hist[i]); 701 } 702 703 /* TODO: various way to print, coloring, nano or milli sec */ 704 static void print_result(void) 705 { 706 struct lock_stat *st; 707 char cut_name[20]; 708 int bad, total; 709 710 pr_info("%20s ", "Name"); 711 pr_info("%10s ", "acquired"); 712 pr_info("%10s ", "contended"); 713 714 pr_info("%15s ", "avg wait (ns)"); 715 pr_info("%15s ", "total wait (ns)"); 716 pr_info("%15s ", "max wait (ns)"); 717 pr_info("%15s ", "min wait (ns)"); 718 719 pr_info("\n\n"); 720 721 bad = total = 0; 722 while ((st = pop_from_result())) { 723 total++; 724 if (st->discard) { 725 bad++; 726 continue; 727 } 728 bzero(cut_name, 20); 729 730 if (strlen(st->name) < 16) { 731 /* output raw name */ 732 pr_info("%20s ", st->name); 733 } else { 734 strncpy(cut_name, st->name, 16); 735 cut_name[16] = '.'; 736 cut_name[17] = '.'; 737 cut_name[18] = '.'; 738 cut_name[19] = '\0'; 739 /* cut off name for saving output style */ 740 pr_info("%20s ", cut_name); 741 } 742 743 pr_info("%10u ", st->nr_acquired); 744 pr_info("%10u ", st->nr_contended); 745 746 pr_info("%15" PRIu64 " ", st->avg_wait_time); 747 pr_info("%15" PRIu64 " ", st->wait_time_total); 748 pr_info("%15" PRIu64 " ", st->wait_time_max); 749 pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? 750 0 : st->wait_time_min); 751 pr_info("\n"); 752 } 753 754 print_bad_events(bad, total); 755 } 756 757 static bool info_threads, info_map; 758 759 static void dump_threads(void) 760 { 761 struct thread_stat *st; 762 struct rb_node *node; 763 struct thread *t; 764 765 pr_info("%10s: comm\n", "Thread ID"); 766 767 node = rb_first(&thread_stats); 768 while (node) { 769 st = container_of(node, struct thread_stat, rb); 770 t = perf_session__findnew(session, st->tid); 771 pr_info("%10d: %s\n", st->tid, thread__comm_str(t)); 772 node = rb_next(node); 773 thread__put(t); 774 } 775 } 776 777 static void dump_map(void) 778 { 779 unsigned int i; 780 struct lock_stat *st; 781 782 pr_info("Address of instance: name of class\n"); 783 for (i = 0; i < LOCKHASH_SIZE; i++) { 784 list_for_each_entry(st, &lockhash_table[i], hash_entry) { 785 pr_info(" %p: %s\n", st->addr, st->name); 786 } 787 } 788 } 789 790 static int dump_info(void) 791 { 792 int rc = 0; 793 794 if (info_threads) 795 dump_threads(); 796 else if (info_map) 797 dump_map(); 798 else { 799 rc = -1; 800 pr_err("Unknown type of information\n"); 801 } 802 803 return rc; 804 } 805 806 typedef int (*tracepoint_handler)(struct evsel *evsel, 807 struct perf_sample *sample); 808 809 static int process_sample_event(struct perf_tool *tool __maybe_unused, 810 union perf_event *event, 811 struct perf_sample *sample, 812 struct evsel *evsel, 813 struct machine *machine) 814 { 815 int err = 0; 816 struct thread *thread = machine__findnew_thread(machine, sample->pid, 817 sample->tid); 818 819 if (thread == NULL) { 820 pr_debug("problem processing %d event, skipping it.\n", 821 event->header.type); 822 return -1; 823 } 824 825 if (evsel->handler != NULL) { 826 tracepoint_handler f = evsel->handler; 827 err = f(evsel, sample); 828 } 829 830 thread__put(thread); 831 832 return err; 833 } 834 835 static void sort_result(void) 836 { 837 unsigned int i; 838 struct lock_stat *st; 839 840 for (i = 0; i < LOCKHASH_SIZE; i++) { 841 list_for_each_entry(st, &lockhash_table[i], hash_entry) { 842 insert_to_result(st, compare); 843 } 844 } 845 } 846 847 static const struct evsel_str_handler lock_tracepoints[] = { 848 { "lock:lock_acquire", evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */ 849 { "lock:lock_acquired", evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ 850 { "lock:lock_contended", evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ 851 { "lock:lock_release", evsel__process_lock_release, }, /* CONFIG_LOCKDEP */ 852 }; 853 854 static bool force; 855 856 static int __cmd_report(bool display_info) 857 { 858 int err = -EINVAL; 859 struct perf_tool eops = { 860 .sample = process_sample_event, 861 .comm = perf_event__process_comm, 862 .namespaces = perf_event__process_namespaces, 863 .ordered_events = true, 864 }; 865 struct perf_data data = { 866 .path = input_name, 867 .mode = PERF_DATA_MODE_READ, 868 .force = force, 869 }; 870 871 session = perf_session__new(&data, false, &eops); 872 if (IS_ERR(session)) { 873 pr_err("Initializing perf session failed\n"); 874 return PTR_ERR(session); 875 } 876 877 symbol__init(&session->header.env); 878 879 if (!perf_session__has_traces(session, "lock record")) 880 goto out_delete; 881 882 if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) { 883 pr_err("Initializing perf session tracepoint handlers failed\n"); 884 goto out_delete; 885 } 886 887 if (select_key()) 888 goto out_delete; 889 890 err = perf_session__process_events(session); 891 if (err) 892 goto out_delete; 893 894 setup_pager(); 895 if (display_info) /* used for info subcommand */ 896 err = dump_info(); 897 else { 898 sort_result(); 899 print_result(); 900 } 901 902 out_delete: 903 perf_session__delete(session); 904 return err; 905 } 906 907 static int __cmd_record(int argc, const char **argv) 908 { 909 const char *record_args[] = { 910 "record", "-R", "-m", "1024", "-c", "1", 911 }; 912 unsigned int rec_argc, i, j, ret; 913 const char **rec_argv; 914 915 for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) { 916 if (!is_valid_tracepoint(lock_tracepoints[i].name)) { 917 pr_err("tracepoint %s is not enabled. " 918 "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n", 919 lock_tracepoints[i].name); 920 return 1; 921 } 922 } 923 924 rec_argc = ARRAY_SIZE(record_args) + argc - 1; 925 /* factor of 2 is for -e in front of each tracepoint */ 926 rec_argc += 2 * ARRAY_SIZE(lock_tracepoints); 927 928 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 929 if (!rec_argv) 930 return -ENOMEM; 931 932 for (i = 0; i < ARRAY_SIZE(record_args); i++) 933 rec_argv[i] = strdup(record_args[i]); 934 935 for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) { 936 rec_argv[i++] = "-e"; 937 rec_argv[i++] = strdup(lock_tracepoints[j].name); 938 } 939 940 for (j = 1; j < (unsigned int)argc; j++, i++) 941 rec_argv[i] = argv[j]; 942 943 BUG_ON(i != rec_argc); 944 945 ret = cmd_record(i, rec_argv); 946 free(rec_argv); 947 return ret; 948 } 949 950 int cmd_lock(int argc, const char **argv) 951 { 952 const struct option lock_options[] = { 953 OPT_STRING('i', "input", &input_name, "file", "input file name"), 954 OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), 955 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), 956 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 957 OPT_END() 958 }; 959 960 const struct option info_options[] = { 961 OPT_BOOLEAN('t', "threads", &info_threads, 962 "dump thread list in perf.data"), 963 OPT_BOOLEAN('m', "map", &info_map, 964 "map of lock instances (address:name table)"), 965 OPT_PARENT(lock_options) 966 }; 967 968 const struct option report_options[] = { 969 OPT_STRING('k', "key", &sort_key, "acquired", 970 "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"), 971 /* TODO: type */ 972 OPT_PARENT(lock_options) 973 }; 974 975 const char * const info_usage[] = { 976 "perf lock info [<options>]", 977 NULL 978 }; 979 const char *const lock_subcommands[] = { "record", "report", "script", 980 "info", NULL }; 981 const char *lock_usage[] = { 982 NULL, 983 NULL 984 }; 985 const char * const report_usage[] = { 986 "perf lock report [<options>]", 987 NULL 988 }; 989 unsigned int i; 990 int rc = 0; 991 992 for (i = 0; i < LOCKHASH_SIZE; i++) 993 INIT_LIST_HEAD(lockhash_table + i); 994 995 argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands, 996 lock_usage, PARSE_OPT_STOP_AT_NON_OPTION); 997 if (!argc) 998 usage_with_options(lock_usage, lock_options); 999 1000 if (!strncmp(argv[0], "rec", 3)) { 1001 return __cmd_record(argc, argv); 1002 } else if (!strncmp(argv[0], "report", 6)) { 1003 trace_handler = &report_lock_ops; 1004 if (argc) { 1005 argc = parse_options(argc, argv, 1006 report_options, report_usage, 0); 1007 if (argc) 1008 usage_with_options(report_usage, report_options); 1009 } 1010 rc = __cmd_report(false); 1011 } else if (!strcmp(argv[0], "script")) { 1012 /* Aliased to 'perf script' */ 1013 return cmd_script(argc, argv); 1014 } else if (!strcmp(argv[0], "info")) { 1015 if (argc) { 1016 argc = parse_options(argc, argv, 1017 info_options, info_usage, 0); 1018 if (argc) 1019 usage_with_options(info_usage, info_options); 1020 } 1021 /* recycling report_lock_ops */ 1022 trace_handler = &report_lock_ops; 1023 rc = __cmd_report(true); 1024 } else { 1025 usage_with_options(lock_usage, lock_options); 1026 } 1027 1028 return rc; 1029 } 1030