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