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