linux/tools/perf/builtin-lock.c
<<
>>
Prefs
   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
  35static struct perf_session *session;
  36
  37/* based on kernel/lockdep.c */
  38#define LOCKHASH_BITS           12
  39#define LOCKHASH_SIZE           (1UL << LOCKHASH_BITS)
  40
  41static 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
  46struct 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 */
 105struct 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
 114struct thread_stat {
 115        struct rb_node          rb;
 116
 117        u32                     tid;
 118        struct list_head        seq_list;
 119};
 120
 121static struct rb_root           thread_stats;
 122
 123static 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
 142static 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
 164static 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
 186static struct thread_stat *thread_stat_findnew_first(u32 tid);
 187static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
 188        thread_stat_findnew_first;
 189
 190static 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
 217SINGLE_KEY(nr_acquired)
 218SINGLE_KEY(nr_contended)
 219SINGLE_KEY(avg_wait_time)
 220SINGLE_KEY(wait_time_total)
 221SINGLE_KEY(wait_time_max)
 222
 223static 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
 235struct 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
 245static const char               *sort_key = "acquired";
 246
 247static int                      (*compare)(struct lock_stat *, struct lock_stat *);
 248
 249static 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 }
 253struct 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
 266static 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
 282static 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 */
 304static 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
 318static 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
 345alloc_failed:
 346        pr_err("memory allocation failed\n");
 347        return NULL;
 348}
 349
 350struct 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
 364static 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
 385enum broken_state {
 386        BROKEN_ACQUIRE,
 387        BROKEN_ACQUIRED,
 388        BROKEN_CONTENDED,
 389        BROKEN_RELEASE,
 390        BROKEN_MAX,
 391};
 392
 393static int bad_hist[BROKEN_MAX];
 394
 395enum acquire_flags {
 396        TRY_LOCK = 1,
 397        READ_LOCK = 2,
 398};
 399
 400static 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:
 454broken:
 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;
 468end:
 469        return 0;
 470}
 471
 472static 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;
 531end:
 532        return 0;
 533}
 534
 535static 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;
 586end:
 587        return 0;
 588}
 589
 590static 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++;
 642free_seq:
 643        list_del_init(&seq->list);
 644        free(seq);
 645end:
 646        return 0;
 647}
 648
 649/* lock oriented handlers */
 650/* TODO: handlers for CPU oriented, thread oriented */
 651static 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
 658static struct trace_lock_handler *trace_handler;
 659
 660static 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
 667static 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
 674static 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
 681static 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
 688static 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 */
 704static 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
 757static bool info_threads, info_map;
 758
 759static 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
 777static 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
 790static 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
 806typedef int (*tracepoint_handler)(struct evsel *evsel,
 807                                  struct perf_sample *sample);
 808
 809static 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
 835static 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
 847static 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
 854static bool force;
 855
 856static 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, &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
 902out_delete:
 903        perf_session__delete(session);
 904        return err;
 905}
 906
 907static 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
 950int 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