2721 lines · c
1// SPDX-License-Identifier: GPL-2.02#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_handler8#include "util/evsel.h"9#include "util/symbol.h"10#include "util/thread.h"11#include "util/header.h"12#include "util/target.h"13#include "util/cgroup.h"14#include "util/callchain.h"15#include "util/lock-contention.h"16#include "util/bpf_skel/lock_data.h"17 18#include <subcmd/pager.h>19#include <subcmd/parse-options.h>20#include "util/trace-event.h"21#include "util/tracepoint.h"22 23#include "util/debug.h"24#include "util/session.h"25#include "util/tool.h"26#include "util/data.h"27#include "util/string2.h"28#include "util/map.h"29#include "util/util.h"30 31#include <stdio.h>32#include <sys/types.h>33#include <sys/prctl.h>34#include <semaphore.h>35#include <math.h>36#include <limits.h>37#include <ctype.h>38 39#include <linux/list.h>40#include <linux/hash.h>41#include <linux/kernel.h>42#include <linux/zalloc.h>43#include <linux/err.h>44#include <linux/stringify.h>45 46static struct perf_session *session;47static struct target target;48 49/* based on kernel/lockdep.c */50#define LOCKHASH_BITS 1251#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)52 53static struct hlist_head *lockhash_table;54 55#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)56#define lockhashentry(key) (lockhash_table + __lockhashfn((key)))57 58static struct rb_root thread_stats;59 60static bool combine_locks;61static bool show_thread_stats;62static bool show_lock_addrs;63static bool show_lock_owner;64static bool show_lock_cgroups;65static bool use_bpf;66static unsigned long bpf_map_entries = MAX_ENTRIES;67static int max_stack_depth = CONTENTION_STACK_DEPTH;68static int stack_skip = CONTENTION_STACK_SKIP;69static int print_nr_entries = INT_MAX / 2;70static LIST_HEAD(callstack_filters);71static const char *output_name = NULL;72static FILE *lock_output;73 74struct callstack_filter {75 struct list_head list;76 char name[];77};78 79static struct lock_filter filters;80 81static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR;82 83static bool needs_callstack(void)84{85 return !list_empty(&callstack_filters);86}87 88static struct thread_stat *thread_stat_find(u32 tid)89{90 struct rb_node *node;91 struct thread_stat *st;92 93 node = thread_stats.rb_node;94 while (node) {95 st = container_of(node, struct thread_stat, rb);96 if (st->tid == tid)97 return st;98 else if (tid < st->tid)99 node = node->rb_left;100 else101 node = node->rb_right;102 }103 104 return NULL;105}106 107static void thread_stat_insert(struct thread_stat *new)108{109 struct rb_node **rb = &thread_stats.rb_node;110 struct rb_node *parent = NULL;111 struct thread_stat *p;112 113 while (*rb) {114 p = container_of(*rb, struct thread_stat, rb);115 parent = *rb;116 117 if (new->tid < p->tid)118 rb = &(*rb)->rb_left;119 else if (new->tid > p->tid)120 rb = &(*rb)->rb_right;121 else122 BUG_ON("inserting invalid thread_stat\n");123 }124 125 rb_link_node(&new->rb, parent, rb);126 rb_insert_color(&new->rb, &thread_stats);127}128 129static struct thread_stat *thread_stat_findnew_after_first(u32 tid)130{131 struct thread_stat *st;132 133 st = thread_stat_find(tid);134 if (st)135 return st;136 137 st = zalloc(sizeof(struct thread_stat));138 if (!st) {139 pr_err("memory allocation failed\n");140 return NULL;141 }142 143 st->tid = tid;144 INIT_LIST_HEAD(&st->seq_list);145 146 thread_stat_insert(st);147 148 return st;149}150 151static struct thread_stat *thread_stat_findnew_first(u32 tid);152static struct thread_stat *(*thread_stat_findnew)(u32 tid) =153 thread_stat_findnew_first;154 155static struct thread_stat *thread_stat_findnew_first(u32 tid)156{157 struct thread_stat *st;158 159 st = zalloc(sizeof(struct thread_stat));160 if (!st) {161 pr_err("memory allocation failed\n");162 return NULL;163 }164 st->tid = tid;165 INIT_LIST_HEAD(&st->seq_list);166 167 rb_link_node(&st->rb, NULL, &thread_stats.rb_node);168 rb_insert_color(&st->rb, &thread_stats);169 170 thread_stat_findnew = thread_stat_findnew_after_first;171 return st;172}173 174/* build simple key function one is bigger than two */175#define SINGLE_KEY(member) \176 static int lock_stat_key_ ## member(struct lock_stat *one, \177 struct lock_stat *two) \178 { \179 return one->member > two->member; \180 }181 182SINGLE_KEY(nr_acquired)183SINGLE_KEY(nr_contended)184SINGLE_KEY(avg_wait_time)185SINGLE_KEY(wait_time_total)186SINGLE_KEY(wait_time_max)187 188static int lock_stat_key_wait_time_min(struct lock_stat *one,189 struct lock_stat *two)190{191 u64 s1 = one->wait_time_min;192 u64 s2 = two->wait_time_min;193 if (s1 == ULLONG_MAX)194 s1 = 0;195 if (s2 == ULLONG_MAX)196 s2 = 0;197 return s1 > s2;198}199 200struct lock_key {201 /*202 * name: the value for specify by user203 * this should be simpler than raw name of member204 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total205 */206 const char *name;207 /* header: the string printed on the header line */208 const char *header;209 /* len: the printing width of the field */210 int len;211 /* key: a pointer to function to compare two lock stats for sorting */212 int (*key)(struct lock_stat*, struct lock_stat*);213 /* print: a pointer to function to print a given lock stats */214 void (*print)(struct lock_key*, struct lock_stat*);215 /* list: list entry to link this */216 struct list_head list;217};218 219static void lock_stat_key_print_time(unsigned long long nsec, int len)220{221 static const struct {222 float base;223 const char *unit;224 } table[] = {225 { 1e9 * 3600, "h " },226 { 1e9 * 60, "m " },227 { 1e9, "s " },228 { 1e6, "ms" },229 { 1e3, "us" },230 { 0, NULL },231 };232 233 /* for CSV output */234 if (len == 0) {235 fprintf(lock_output, "%llu", nsec);236 return;237 }238 239 for (int i = 0; table[i].unit; i++) {240 if (nsec < table[i].base)241 continue;242 243 fprintf(lock_output, "%*.2f %s", len - 3, nsec / table[i].base, table[i].unit);244 return;245 }246 247 fprintf(lock_output, "%*llu %s", len - 3, nsec, "ns");248}249 250#define PRINT_KEY(member) \251static void lock_stat_key_print_ ## member(struct lock_key *key, \252 struct lock_stat *ls) \253{ \254 fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\255}256 257#define PRINT_TIME(member) \258static void lock_stat_key_print_ ## member(struct lock_key *key, \259 struct lock_stat *ls) \260{ \261 lock_stat_key_print_time((unsigned long long)ls->member, key->len); \262}263 264PRINT_KEY(nr_acquired)265PRINT_KEY(nr_contended)266PRINT_TIME(avg_wait_time)267PRINT_TIME(wait_time_total)268PRINT_TIME(wait_time_max)269 270static void lock_stat_key_print_wait_time_min(struct lock_key *key,271 struct lock_stat *ls)272{273 u64 wait_time = ls->wait_time_min;274 275 if (wait_time == ULLONG_MAX)276 wait_time = 0;277 278 lock_stat_key_print_time(wait_time, key->len);279}280 281 282static const char *sort_key = "acquired";283 284static int (*compare)(struct lock_stat *, struct lock_stat *);285 286static struct rb_root sorted; /* place to store intermediate data */287static struct rb_root result; /* place to store sorted data */288 289static LIST_HEAD(lock_keys);290static const char *output_fields;291 292#define DEF_KEY_LOCK(name, header, fn_suffix, len) \293 { #name, header, len, lock_stat_key_ ## fn_suffix, lock_stat_key_print_ ## fn_suffix, {} }294static struct lock_key report_keys[] = {295 DEF_KEY_LOCK(acquired, "acquired", nr_acquired, 10),296 DEF_KEY_LOCK(contended, "contended", nr_contended, 10),297 DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),298 DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),299 DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),300 DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),301 302 /* extra comparisons much complicated should be here */303 { }304};305 306static struct lock_key contention_keys[] = {307 DEF_KEY_LOCK(contended, "contended", nr_contended, 10),308 DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),309 DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),310 DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),311 DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),312 313 /* extra comparisons much complicated should be here */314 { }315};316 317static int select_key(bool contention)318{319 int i;320 struct lock_key *keys = report_keys;321 322 if (contention)323 keys = contention_keys;324 325 for (i = 0; keys[i].name; i++) {326 if (!strcmp(keys[i].name, sort_key)) {327 compare = keys[i].key;328 329 /* selected key should be in the output fields */330 if (list_empty(&keys[i].list))331 list_add_tail(&keys[i].list, &lock_keys);332 333 return 0;334 }335 }336 337 pr_err("Unknown compare key: %s\n", sort_key);338 return -1;339}340 341static int add_output_field(bool contention, char *name)342{343 int i;344 struct lock_key *keys = report_keys;345 346 if (contention)347 keys = contention_keys;348 349 for (i = 0; keys[i].name; i++) {350 if (strcmp(keys[i].name, name))351 continue;352 353 /* prevent double link */354 if (list_empty(&keys[i].list))355 list_add_tail(&keys[i].list, &lock_keys);356 357 return 0;358 }359 360 pr_err("Unknown output field: %s\n", name);361 return -1;362}363 364static int setup_output_field(bool contention, const char *str)365{366 char *tok, *tmp, *orig;367 int i, ret = 0;368 struct lock_key *keys = report_keys;369 370 if (contention)371 keys = contention_keys;372 373 /* no output field given: use all of them */374 if (str == NULL) {375 for (i = 0; keys[i].name; i++)376 list_add_tail(&keys[i].list, &lock_keys);377 return 0;378 }379 380 for (i = 0; keys[i].name; i++)381 INIT_LIST_HEAD(&keys[i].list);382 383 orig = tmp = strdup(str);384 if (orig == NULL)385 return -ENOMEM;386 387 while ((tok = strsep(&tmp, ",")) != NULL){388 ret = add_output_field(contention, tok);389 if (ret < 0)390 break;391 }392 free(orig);393 394 return ret;395}396 397static void combine_lock_stats(struct lock_stat *st)398{399 struct rb_node **rb = &sorted.rb_node;400 struct rb_node *parent = NULL;401 struct lock_stat *p;402 int ret;403 404 while (*rb) {405 p = container_of(*rb, struct lock_stat, rb);406 parent = *rb;407 408 if (st->name && p->name)409 ret = strcmp(st->name, p->name);410 else411 ret = !!st->name - !!p->name;412 413 if (ret == 0) {414 p->nr_acquired += st->nr_acquired;415 p->nr_contended += st->nr_contended;416 p->wait_time_total += st->wait_time_total;417 418 if (p->nr_contended)419 p->avg_wait_time = p->wait_time_total / p->nr_contended;420 421 if (p->wait_time_min > st->wait_time_min)422 p->wait_time_min = st->wait_time_min;423 if (p->wait_time_max < st->wait_time_max)424 p->wait_time_max = st->wait_time_max;425 426 p->broken |= st->broken;427 st->combined = 1;428 return;429 }430 431 if (ret < 0)432 rb = &(*rb)->rb_left;433 else434 rb = &(*rb)->rb_right;435 }436 437 rb_link_node(&st->rb, parent, rb);438 rb_insert_color(&st->rb, &sorted);439}440 441static void insert_to_result(struct lock_stat *st,442 int (*bigger)(struct lock_stat *, struct lock_stat *))443{444 struct rb_node **rb = &result.rb_node;445 struct rb_node *parent = NULL;446 struct lock_stat *p;447 448 if (combine_locks && st->combined)449 return;450 451 while (*rb) {452 p = container_of(*rb, struct lock_stat, rb);453 parent = *rb;454 455 if (bigger(st, p))456 rb = &(*rb)->rb_left;457 else458 rb = &(*rb)->rb_right;459 }460 461 rb_link_node(&st->rb, parent, rb);462 rb_insert_color(&st->rb, &result);463}464 465/* returns left most element of result, and erase it */466static struct lock_stat *pop_from_result(void)467{468 struct rb_node *node = result.rb_node;469 470 if (!node)471 return NULL;472 473 while (node->rb_left)474 node = node->rb_left;475 476 rb_erase(node, &result);477 return container_of(node, struct lock_stat, rb);478}479 480struct lock_stat *lock_stat_find(u64 addr)481{482 struct hlist_head *entry = lockhashentry(addr);483 struct lock_stat *ret;484 485 hlist_for_each_entry(ret, entry, hash_entry) {486 if (ret->addr == addr)487 return ret;488 }489 return NULL;490}491 492struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)493{494 struct hlist_head *entry = lockhashentry(addr);495 struct lock_stat *ret, *new;496 497 hlist_for_each_entry(ret, entry, hash_entry) {498 if (ret->addr == addr)499 return ret;500 }501 502 new = zalloc(sizeof(struct lock_stat));503 if (!new)504 goto alloc_failed;505 506 new->addr = addr;507 new->name = strdup(name);508 if (!new->name) {509 free(new);510 goto alloc_failed;511 }512 513 new->flags = flags;514 new->wait_time_min = ULLONG_MAX;515 516 hlist_add_head(&new->hash_entry, entry);517 return new;518 519alloc_failed:520 pr_err("memory allocation failed\n");521 return NULL;522}523 524bool match_callstack_filter(struct machine *machine, u64 *callstack)525{526 struct map *kmap;527 struct symbol *sym;528 u64 ip;529 const char *arch = perf_env__arch(machine->env);530 531 if (list_empty(&callstack_filters))532 return true;533 534 for (int i = 0; i < max_stack_depth; i++) {535 struct callstack_filter *filter;536 537 /*538 * In powerpc, the callchain saved by kernel always includes539 * first three entries as the NIP (next instruction pointer),540 * LR (link register), and the contents of LR save area in the541 * second stack frame. In certain scenarios its possible to have542 * invalid kernel instruction addresses in either LR or the second543 * stack frame's LR. In that case, kernel will store that address as544 * zero.545 *546 * The below check will continue to look into callstack,547 * incase first or second callstack index entry has 0548 * address for powerpc.549 */550 if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") ||551 (i != 1 && i != 2))))552 break;553 554 ip = callstack[i];555 sym = machine__find_kernel_symbol(machine, ip, &kmap);556 if (sym == NULL)557 continue;558 559 list_for_each_entry(filter, &callstack_filters, list) {560 if (strstr(sym->name, filter->name))561 return true;562 }563 }564 return false;565}566 567struct trace_lock_handler {568 /* it's used on CONFIG_LOCKDEP */569 int (*acquire_event)(struct evsel *evsel,570 struct perf_sample *sample);571 572 /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */573 int (*acquired_event)(struct evsel *evsel,574 struct perf_sample *sample);575 576 /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */577 int (*contended_event)(struct evsel *evsel,578 struct perf_sample *sample);579 580 /* it's used on CONFIG_LOCKDEP */581 int (*release_event)(struct evsel *evsel,582 struct perf_sample *sample);583 584 /* it's used when CONFIG_LOCKDEP is off */585 int (*contention_begin_event)(struct evsel *evsel,586 struct perf_sample *sample);587 588 /* it's used when CONFIG_LOCKDEP is off */589 int (*contention_end_event)(struct evsel *evsel,590 struct perf_sample *sample);591};592 593static struct lock_seq_stat *get_seq(struct thread_stat *ts, u64 addr)594{595 struct lock_seq_stat *seq;596 597 list_for_each_entry(seq, &ts->seq_list, list) {598 if (seq->addr == addr)599 return seq;600 }601 602 seq = zalloc(sizeof(struct lock_seq_stat));603 if (!seq) {604 pr_err("memory allocation failed\n");605 return NULL;606 }607 seq->state = SEQ_STATE_UNINITIALIZED;608 seq->addr = addr;609 610 list_add(&seq->list, &ts->seq_list);611 return seq;612}613 614enum broken_state {615 BROKEN_ACQUIRE,616 BROKEN_ACQUIRED,617 BROKEN_CONTENDED,618 BROKEN_RELEASE,619 BROKEN_MAX,620};621 622static int bad_hist[BROKEN_MAX];623 624enum acquire_flags {625 TRY_LOCK = 1,626 READ_LOCK = 2,627};628 629static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid)630{631 switch (aggr_mode) {632 case LOCK_AGGR_ADDR:633 *key = addr;634 break;635 case LOCK_AGGR_TASK:636 *key = tid;637 break;638 case LOCK_AGGR_CALLER:639 case LOCK_AGGR_CGROUP:640 default:641 pr_err("Invalid aggregation mode: %d\n", aggr_mode);642 return -EINVAL;643 }644 return 0;645}646 647static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample);648 649static int get_key_by_aggr_mode(u64 *key, u64 addr, struct evsel *evsel,650 struct perf_sample *sample)651{652 if (aggr_mode == LOCK_AGGR_CALLER) {653 *key = callchain_id(evsel, sample);654 return 0;655 }656 return get_key_by_aggr_mode_simple(key, addr, sample->tid);657}658 659static int report_lock_acquire_event(struct evsel *evsel,660 struct perf_sample *sample)661{662 struct lock_stat *ls;663 struct thread_stat *ts;664 struct lock_seq_stat *seq;665 const char *name = evsel__strval(evsel, sample, "name");666 u64 addr = evsel__intval(evsel, sample, "lockdep_addr");667 int flag = evsel__intval(evsel, sample, "flags");668 u64 key;669 int ret;670 671 ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);672 if (ret < 0)673 return ret;674 675 ls = lock_stat_findnew(key, name, 0);676 if (!ls)677 return -ENOMEM;678 679 ts = thread_stat_findnew(sample->tid);680 if (!ts)681 return -ENOMEM;682 683 seq = get_seq(ts, addr);684 if (!seq)685 return -ENOMEM;686 687 switch (seq->state) {688 case SEQ_STATE_UNINITIALIZED:689 case SEQ_STATE_RELEASED:690 if (!flag) {691 seq->state = SEQ_STATE_ACQUIRING;692 } else {693 if (flag & TRY_LOCK)694 ls->nr_trylock++;695 if (flag & READ_LOCK)696 ls->nr_readlock++;697 seq->state = SEQ_STATE_READ_ACQUIRED;698 seq->read_count = 1;699 ls->nr_acquired++;700 }701 break;702 case SEQ_STATE_READ_ACQUIRED:703 if (flag & READ_LOCK) {704 seq->read_count++;705 ls->nr_acquired++;706 goto end;707 } else {708 goto broken;709 }710 break;711 case SEQ_STATE_ACQUIRED:712 case SEQ_STATE_ACQUIRING:713 case SEQ_STATE_CONTENDED:714broken:715 /* broken lock sequence */716 if (!ls->broken) {717 ls->broken = 1;718 bad_hist[BROKEN_ACQUIRE]++;719 }720 list_del_init(&seq->list);721 free(seq);722 goto end;723 default:724 BUG_ON("Unknown state of lock sequence found!\n");725 break;726 }727 728 ls->nr_acquire++;729 seq->prev_event_time = sample->time;730end:731 return 0;732}733 734static int report_lock_acquired_event(struct evsel *evsel,735 struct perf_sample *sample)736{737 struct lock_stat *ls;738 struct thread_stat *ts;739 struct lock_seq_stat *seq;740 u64 contended_term;741 const char *name = evsel__strval(evsel, sample, "name");742 u64 addr = evsel__intval(evsel, sample, "lockdep_addr");743 u64 key;744 int ret;745 746 ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);747 if (ret < 0)748 return ret;749 750 ls = lock_stat_findnew(key, name, 0);751 if (!ls)752 return -ENOMEM;753 754 ts = thread_stat_findnew(sample->tid);755 if (!ts)756 return -ENOMEM;757 758 seq = get_seq(ts, addr);759 if (!seq)760 return -ENOMEM;761 762 switch (seq->state) {763 case SEQ_STATE_UNINITIALIZED:764 /* orphan event, do nothing */765 return 0;766 case SEQ_STATE_ACQUIRING:767 break;768 case SEQ_STATE_CONTENDED:769 contended_term = sample->time - seq->prev_event_time;770 ls->wait_time_total += contended_term;771 if (contended_term < ls->wait_time_min)772 ls->wait_time_min = contended_term;773 if (ls->wait_time_max < contended_term)774 ls->wait_time_max = contended_term;775 break;776 case SEQ_STATE_RELEASED:777 case SEQ_STATE_ACQUIRED:778 case SEQ_STATE_READ_ACQUIRED:779 /* broken lock sequence */780 if (!ls->broken) {781 ls->broken = 1;782 bad_hist[BROKEN_ACQUIRED]++;783 }784 list_del_init(&seq->list);785 free(seq);786 goto end;787 default:788 BUG_ON("Unknown state of lock sequence found!\n");789 break;790 }791 792 seq->state = SEQ_STATE_ACQUIRED;793 ls->nr_acquired++;794 ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0;795 seq->prev_event_time = sample->time;796end:797 return 0;798}799 800static int report_lock_contended_event(struct evsel *evsel,801 struct perf_sample *sample)802{803 struct lock_stat *ls;804 struct thread_stat *ts;805 struct lock_seq_stat *seq;806 const char *name = evsel__strval(evsel, sample, "name");807 u64 addr = evsel__intval(evsel, sample, "lockdep_addr");808 u64 key;809 int ret;810 811 ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);812 if (ret < 0)813 return ret;814 815 ls = lock_stat_findnew(key, name, 0);816 if (!ls)817 return -ENOMEM;818 819 ts = thread_stat_findnew(sample->tid);820 if (!ts)821 return -ENOMEM;822 823 seq = get_seq(ts, addr);824 if (!seq)825 return -ENOMEM;826 827 switch (seq->state) {828 case SEQ_STATE_UNINITIALIZED:829 /* orphan event, do nothing */830 return 0;831 case SEQ_STATE_ACQUIRING:832 break;833 case SEQ_STATE_RELEASED:834 case SEQ_STATE_ACQUIRED:835 case SEQ_STATE_READ_ACQUIRED:836 case SEQ_STATE_CONTENDED:837 /* broken lock sequence */838 if (!ls->broken) {839 ls->broken = 1;840 bad_hist[BROKEN_CONTENDED]++;841 }842 list_del_init(&seq->list);843 free(seq);844 goto end;845 default:846 BUG_ON("Unknown state of lock sequence found!\n");847 break;848 }849 850 seq->state = SEQ_STATE_CONTENDED;851 ls->nr_contended++;852 ls->avg_wait_time = ls->wait_time_total/ls->nr_contended;853 seq->prev_event_time = sample->time;854end:855 return 0;856}857 858static int report_lock_release_event(struct evsel *evsel,859 struct perf_sample *sample)860{861 struct lock_stat *ls;862 struct thread_stat *ts;863 struct lock_seq_stat *seq;864 const char *name = evsel__strval(evsel, sample, "name");865 u64 addr = evsel__intval(evsel, sample, "lockdep_addr");866 u64 key;867 int ret;868 869 ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);870 if (ret < 0)871 return ret;872 873 ls = lock_stat_findnew(key, name, 0);874 if (!ls)875 return -ENOMEM;876 877 ts = thread_stat_findnew(sample->tid);878 if (!ts)879 return -ENOMEM;880 881 seq = get_seq(ts, addr);882 if (!seq)883 return -ENOMEM;884 885 switch (seq->state) {886 case SEQ_STATE_UNINITIALIZED:887 goto end;888 case SEQ_STATE_ACQUIRED:889 break;890 case SEQ_STATE_READ_ACQUIRED:891 seq->read_count--;892 BUG_ON(seq->read_count < 0);893 if (seq->read_count) {894 ls->nr_release++;895 goto end;896 }897 break;898 case SEQ_STATE_ACQUIRING:899 case SEQ_STATE_CONTENDED:900 case SEQ_STATE_RELEASED:901 /* broken lock sequence */902 if (!ls->broken) {903 ls->broken = 1;904 bad_hist[BROKEN_RELEASE]++;905 }906 goto free_seq;907 default:908 BUG_ON("Unknown state of lock sequence found!\n");909 break;910 }911 912 ls->nr_release++;913free_seq:914 list_del_init(&seq->list);915 free(seq);916end:917 return 0;918}919 920static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,921 char *buf, int size)922{923 u64 offset;924 925 if (map == NULL || sym == NULL) {926 buf[0] = '\0';927 return 0;928 }929 930 offset = map__map_ip(map, ip) - sym->start;931 932 if (offset)933 return scnprintf(buf, size, "%s+%#lx", sym->name, offset);934 else935 return strlcpy(buf, sym->name, size);936}937static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample,938 char *buf, int size)939{940 struct thread *thread;941 struct callchain_cursor *cursor;942 struct machine *machine = &session->machines.host;943 struct symbol *sym;944 int skip = 0;945 int ret;946 947 /* lock names will be replaced to task name later */948 if (show_thread_stats)949 return -1;950 951 thread = machine__findnew_thread(machine, -1, sample->pid);952 if (thread == NULL)953 return -1;954 955 cursor = get_tls_callchain_cursor();956 957 /* use caller function name from the callchain */958 ret = thread__resolve_callchain(thread, cursor, evsel, sample,959 NULL, NULL, max_stack_depth);960 if (ret != 0) {961 thread__put(thread);962 return -1;963 }964 965 callchain_cursor_commit(cursor);966 thread__put(thread);967 968 while (true) {969 struct callchain_cursor_node *node;970 971 node = callchain_cursor_current(cursor);972 if (node == NULL)973 break;974 975 /* skip first few entries - for lock functions */976 if (++skip <= stack_skip)977 goto next;978 979 sym = node->ms.sym;980 if (sym && !machine__is_lock_function(machine, node->ip)) {981 get_symbol_name_offset(node->ms.map, sym, node->ip,982 buf, size);983 return 0;984 }985 986next:987 callchain_cursor_advance(cursor);988 }989 return -1;990}991 992static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)993{994 struct callchain_cursor *cursor;995 struct machine *machine = &session->machines.host;996 struct thread *thread;997 u64 hash = 0;998 int skip = 0;999 int ret;1000 1001 thread = machine__findnew_thread(machine, -1, sample->pid);1002 if (thread == NULL)1003 return -1;1004 1005 cursor = get_tls_callchain_cursor();1006 /* use caller function name from the callchain */1007 ret = thread__resolve_callchain(thread, cursor, evsel, sample,1008 NULL, NULL, max_stack_depth);1009 thread__put(thread);1010 1011 if (ret != 0)1012 return -1;1013 1014 callchain_cursor_commit(cursor);1015 1016 while (true) {1017 struct callchain_cursor_node *node;1018 1019 node = callchain_cursor_current(cursor);1020 if (node == NULL)1021 break;1022 1023 /* skip first few entries - for lock functions */1024 if (++skip <= stack_skip)1025 goto next;1026 1027 if (node->ms.sym && machine__is_lock_function(machine, node->ip))1028 goto next;1029 1030 hash ^= hash_long((unsigned long)node->ip, 64);1031 1032next:1033 callchain_cursor_advance(cursor);1034 }1035 return hash;1036}1037 1038static u64 *get_callstack(struct perf_sample *sample, int max_stack)1039{1040 u64 *callstack;1041 u64 i;1042 int c;1043 1044 callstack = calloc(max_stack, sizeof(*callstack));1045 if (callstack == NULL)1046 return NULL;1047 1048 for (i = 0, c = 0; i < sample->callchain->nr && c < max_stack; i++) {1049 u64 ip = sample->callchain->ips[i];1050 1051 if (ip >= PERF_CONTEXT_MAX)1052 continue;1053 1054 callstack[c++] = ip;1055 }1056 return callstack;1057}1058 1059static int report_lock_contention_begin_event(struct evsel *evsel,1060 struct perf_sample *sample)1061{1062 struct lock_stat *ls;1063 struct thread_stat *ts;1064 struct lock_seq_stat *seq;1065 u64 addr = evsel__intval(evsel, sample, "lock_addr");1066 unsigned int flags = evsel__intval(evsel, sample, "flags");1067 u64 key;1068 int i, ret;1069 static bool kmap_loaded;1070 struct machine *machine = &session->machines.host;1071 struct map *kmap;1072 struct symbol *sym;1073 1074 ret = get_key_by_aggr_mode(&key, addr, evsel, sample);1075 if (ret < 0)1076 return ret;1077 1078 if (!kmap_loaded) {1079 unsigned long *addrs;1080 1081 /* make sure it loads the kernel map to find lock symbols */1082 map__load(machine__kernel_map(machine));1083 kmap_loaded = true;1084 1085 /* convert (kernel) symbols to addresses */1086 for (i = 0; i < filters.nr_syms; i++) {1087 sym = machine__find_kernel_symbol_by_name(machine,1088 filters.syms[i],1089 &kmap);1090 if (sym == NULL) {1091 pr_warning("ignore unknown symbol: %s\n",1092 filters.syms[i]);1093 continue;1094 }1095 1096 addrs = realloc(filters.addrs,1097 (filters.nr_addrs + 1) * sizeof(*addrs));1098 if (addrs == NULL) {1099 pr_warning("memory allocation failure\n");1100 return -ENOMEM;1101 }1102 1103 addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);1104 filters.addrs = addrs;1105 }1106 }1107 1108 ls = lock_stat_find(key);1109 if (!ls) {1110 char buf[128];1111 const char *name = "";1112 1113 switch (aggr_mode) {1114 case LOCK_AGGR_ADDR:1115 sym = machine__find_kernel_symbol(machine, key, &kmap);1116 if (sym)1117 name = sym->name;1118 break;1119 case LOCK_AGGR_CALLER:1120 name = buf;1121 if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)1122 name = "Unknown";1123 break;1124 case LOCK_AGGR_CGROUP:1125 case LOCK_AGGR_TASK:1126 default:1127 break;1128 }1129 1130 ls = lock_stat_findnew(key, name, flags);1131 if (!ls)1132 return -ENOMEM;1133 }1134 1135 if (filters.nr_types) {1136 bool found = false;1137 1138 for (i = 0; i < filters.nr_types; i++) {1139 if (flags == filters.types[i]) {1140 found = true;1141 break;1142 }1143 }1144 1145 if (!found)1146 return 0;1147 }1148 1149 if (filters.nr_addrs) {1150 bool found = false;1151 1152 for (i = 0; i < filters.nr_addrs; i++) {1153 if (addr == filters.addrs[i]) {1154 found = true;1155 break;1156 }1157 }1158 1159 if (!found)1160 return 0;1161 }1162 1163 if (needs_callstack()) {1164 u64 *callstack = get_callstack(sample, max_stack_depth);1165 if (callstack == NULL)1166 return -ENOMEM;1167 1168 if (!match_callstack_filter(machine, callstack)) {1169 free(callstack);1170 return 0;1171 }1172 1173 if (ls->callstack == NULL)1174 ls->callstack = callstack;1175 else1176 free(callstack);1177 }1178 1179 ts = thread_stat_findnew(sample->tid);1180 if (!ts)1181 return -ENOMEM;1182 1183 seq = get_seq(ts, addr);1184 if (!seq)1185 return -ENOMEM;1186 1187 switch (seq->state) {1188 case SEQ_STATE_UNINITIALIZED:1189 case SEQ_STATE_ACQUIRED:1190 break;1191 case SEQ_STATE_CONTENDED:1192 /*1193 * It can have nested contention begin with mutex spinning,1194 * then we would use the original contention begin event and1195 * ignore the second one.1196 */1197 goto end;1198 case SEQ_STATE_ACQUIRING:1199 case SEQ_STATE_READ_ACQUIRED:1200 case SEQ_STATE_RELEASED:1201 /* broken lock sequence */1202 if (!ls->broken) {1203 ls->broken = 1;1204 bad_hist[BROKEN_CONTENDED]++;1205 }1206 list_del_init(&seq->list);1207 free(seq);1208 goto end;1209 default:1210 BUG_ON("Unknown state of lock sequence found!\n");1211 break;1212 }1213 1214 if (seq->state != SEQ_STATE_CONTENDED) {1215 seq->state = SEQ_STATE_CONTENDED;1216 seq->prev_event_time = sample->time;1217 ls->nr_contended++;1218 }1219end:1220 return 0;1221}1222 1223static int report_lock_contention_end_event(struct evsel *evsel,1224 struct perf_sample *sample)1225{1226 struct lock_stat *ls;1227 struct thread_stat *ts;1228 struct lock_seq_stat *seq;1229 u64 contended_term;1230 u64 addr = evsel__intval(evsel, sample, "lock_addr");1231 u64 key;1232 int ret;1233 1234 ret = get_key_by_aggr_mode(&key, addr, evsel, sample);1235 if (ret < 0)1236 return ret;1237 1238 ls = lock_stat_find(key);1239 if (!ls)1240 return 0;1241 1242 ts = thread_stat_find(sample->tid);1243 if (!ts)1244 return 0;1245 1246 seq = get_seq(ts, addr);1247 if (!seq)1248 return -ENOMEM;1249 1250 switch (seq->state) {1251 case SEQ_STATE_UNINITIALIZED:1252 goto end;1253 case SEQ_STATE_CONTENDED:1254 contended_term = sample->time - seq->prev_event_time;1255 ls->wait_time_total += contended_term;1256 if (contended_term < ls->wait_time_min)1257 ls->wait_time_min = contended_term;1258 if (ls->wait_time_max < contended_term)1259 ls->wait_time_max = contended_term;1260 break;1261 case SEQ_STATE_ACQUIRING:1262 case SEQ_STATE_ACQUIRED:1263 case SEQ_STATE_READ_ACQUIRED:1264 case SEQ_STATE_RELEASED:1265 /* broken lock sequence */1266 if (!ls->broken) {1267 ls->broken = 1;1268 bad_hist[BROKEN_ACQUIRED]++;1269 }1270 list_del_init(&seq->list);1271 free(seq);1272 goto end;1273 default:1274 BUG_ON("Unknown state of lock sequence found!\n");1275 break;1276 }1277 1278 seq->state = SEQ_STATE_ACQUIRED;1279 ls->nr_acquired++;1280 ls->avg_wait_time = ls->wait_time_total/ls->nr_acquired;1281end:1282 return 0;1283}1284 1285/* lock oriented handlers */1286/* TODO: handlers for CPU oriented, thread oriented */1287static struct trace_lock_handler report_lock_ops = {1288 .acquire_event = report_lock_acquire_event,1289 .acquired_event = report_lock_acquired_event,1290 .contended_event = report_lock_contended_event,1291 .release_event = report_lock_release_event,1292 .contention_begin_event = report_lock_contention_begin_event,1293 .contention_end_event = report_lock_contention_end_event,1294};1295 1296static struct trace_lock_handler contention_lock_ops = {1297 .contention_begin_event = report_lock_contention_begin_event,1298 .contention_end_event = report_lock_contention_end_event,1299};1300 1301 1302static struct trace_lock_handler *trace_handler;1303 1304static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample)1305{1306 if (trace_handler->acquire_event)1307 return trace_handler->acquire_event(evsel, sample);1308 return 0;1309}1310 1311static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample)1312{1313 if (trace_handler->acquired_event)1314 return trace_handler->acquired_event(evsel, sample);1315 return 0;1316}1317 1318static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample)1319{1320 if (trace_handler->contended_event)1321 return trace_handler->contended_event(evsel, sample);1322 return 0;1323}1324 1325static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample)1326{1327 if (trace_handler->release_event)1328 return trace_handler->release_event(evsel, sample);1329 return 0;1330}1331 1332static int evsel__process_contention_begin(struct evsel *evsel, struct perf_sample *sample)1333{1334 if (trace_handler->contention_begin_event)1335 return trace_handler->contention_begin_event(evsel, sample);1336 return 0;1337}1338 1339static int evsel__process_contention_end(struct evsel *evsel, struct perf_sample *sample)1340{1341 if (trace_handler->contention_end_event)1342 return trace_handler->contention_end_event(evsel, sample);1343 return 0;1344}1345 1346static void print_bad_events(int bad, int total)1347{1348 /* Output for debug, this have to be removed */1349 int i;1350 int broken = 0;1351 const char *name[4] =1352 { "acquire", "acquired", "contended", "release" };1353 1354 for (i = 0; i < BROKEN_MAX; i++)1355 broken += bad_hist[i];1356 1357 if (quiet || total == 0 || (broken == 0 && verbose <= 0))1358 return;1359 1360 fprintf(lock_output, "\n=== output for debug ===\n\n");1361 fprintf(lock_output, "bad: %d, total: %d\n", bad, total);1362 fprintf(lock_output, "bad rate: %.2f %%\n", (double)bad / (double)total * 100);1363 fprintf(lock_output, "histogram of events caused bad sequence\n");1364 for (i = 0; i < BROKEN_MAX; i++)1365 fprintf(lock_output, " %10s: %d\n", name[i], bad_hist[i]);1366}1367 1368/* TODO: various way to print, coloring, nano or milli sec */1369static void print_result(void)1370{1371 struct lock_stat *st;1372 struct lock_key *key;1373 char cut_name[20];1374 int bad, total, printed;1375 1376 if (!quiet) {1377 fprintf(lock_output, "%20s ", "Name");1378 list_for_each_entry(key, &lock_keys, list)1379 fprintf(lock_output, "%*s ", key->len, key->header);1380 fprintf(lock_output, "\n\n");1381 }1382 1383 bad = total = printed = 0;1384 while ((st = pop_from_result())) {1385 total++;1386 if (st->broken)1387 bad++;1388 if (!st->nr_acquired)1389 continue;1390 1391 bzero(cut_name, 20);1392 1393 if (strlen(st->name) < 20) {1394 /* output raw name */1395 const char *name = st->name;1396 1397 if (show_thread_stats) {1398 struct thread *t;1399 1400 /* st->addr contains tid of thread */1401 t = perf_session__findnew(session, st->addr);1402 name = thread__comm_str(t);1403 }1404 1405 fprintf(lock_output, "%20s ", name);1406 } else {1407 strncpy(cut_name, st->name, 16);1408 cut_name[16] = '.';1409 cut_name[17] = '.';1410 cut_name[18] = '.';1411 cut_name[19] = '\0';1412 /* cut off name for saving output style */1413 fprintf(lock_output, "%20s ", cut_name);1414 }1415 1416 list_for_each_entry(key, &lock_keys, list) {1417 key->print(key, st);1418 fprintf(lock_output, " ");1419 }1420 fprintf(lock_output, "\n");1421 1422 if (++printed >= print_nr_entries)1423 break;1424 }1425 1426 print_bad_events(bad, total);1427}1428 1429static bool info_threads, info_map;1430 1431static void dump_threads(void)1432{1433 struct thread_stat *st;1434 struct rb_node *node;1435 struct thread *t;1436 1437 fprintf(lock_output, "%10s: comm\n", "Thread ID");1438 1439 node = rb_first(&thread_stats);1440 while (node) {1441 st = container_of(node, struct thread_stat, rb);1442 t = perf_session__findnew(session, st->tid);1443 fprintf(lock_output, "%10d: %s\n", st->tid, thread__comm_str(t));1444 node = rb_next(node);1445 thread__put(t);1446 }1447}1448 1449static int compare_maps(struct lock_stat *a, struct lock_stat *b)1450{1451 int ret;1452 1453 if (a->name && b->name)1454 ret = strcmp(a->name, b->name);1455 else1456 ret = !!a->name - !!b->name;1457 1458 if (!ret)1459 return a->addr < b->addr;1460 else1461 return ret < 0;1462}1463 1464static void dump_map(void)1465{1466 unsigned int i;1467 struct lock_stat *st;1468 1469 fprintf(lock_output, "Address of instance: name of class\n");1470 for (i = 0; i < LOCKHASH_SIZE; i++) {1471 hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1472 insert_to_result(st, compare_maps);1473 }1474 }1475 1476 while ((st = pop_from_result()))1477 fprintf(lock_output, " %#llx: %s\n", (unsigned long long)st->addr, st->name);1478}1479 1480static void dump_info(void)1481{1482 if (info_threads)1483 dump_threads();1484 1485 if (info_map) {1486 if (info_threads)1487 fputc('\n', lock_output);1488 dump_map();1489 }1490}1491 1492static const struct evsel_str_handler lock_tracepoints[] = {1493 { "lock:lock_acquire", evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */1494 { "lock:lock_acquired", evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */1495 { "lock:lock_contended", evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */1496 { "lock:lock_release", evsel__process_lock_release, }, /* CONFIG_LOCKDEP */1497};1498 1499static const struct evsel_str_handler contention_tracepoints[] = {1500 { "lock:contention_begin", evsel__process_contention_begin, },1501 { "lock:contention_end", evsel__process_contention_end, },1502};1503 1504static int process_event_update(const struct perf_tool *tool,1505 union perf_event *event,1506 struct evlist **pevlist)1507{1508 int ret;1509 1510 ret = perf_event__process_event_update(tool, event, pevlist);1511 if (ret < 0)1512 return ret;1513 1514 /* this can return -EEXIST since we call it for each evsel */1515 perf_session__set_tracepoints_handlers(session, lock_tracepoints);1516 perf_session__set_tracepoints_handlers(session, contention_tracepoints);1517 return 0;1518}1519 1520typedef int (*tracepoint_handler)(struct evsel *evsel,1521 struct perf_sample *sample);1522 1523static int process_sample_event(const struct perf_tool *tool __maybe_unused,1524 union perf_event *event,1525 struct perf_sample *sample,1526 struct evsel *evsel,1527 struct machine *machine)1528{1529 int err = 0;1530 struct thread *thread = machine__findnew_thread(machine, sample->pid,1531 sample->tid);1532 1533 if (thread == NULL) {1534 pr_debug("problem processing %d event, skipping it.\n",1535 event->header.type);1536 return -1;1537 }1538 1539 if (evsel->handler != NULL) {1540 tracepoint_handler f = evsel->handler;1541 err = f(evsel, sample);1542 }1543 1544 thread__put(thread);1545 1546 return err;1547}1548 1549static void combine_result(void)1550{1551 unsigned int i;1552 struct lock_stat *st;1553 1554 if (!combine_locks)1555 return;1556 1557 for (i = 0; i < LOCKHASH_SIZE; i++) {1558 hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1559 combine_lock_stats(st);1560 }1561 }1562}1563 1564static void sort_result(void)1565{1566 unsigned int i;1567 struct lock_stat *st;1568 1569 for (i = 0; i < LOCKHASH_SIZE; i++) {1570 hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {1571 insert_to_result(st, compare);1572 }1573 }1574}1575 1576static const struct {1577 unsigned int flags;1578 const char *str;1579 const char *name;1580} lock_type_table[] = {1581 { 0, "semaphore", "semaphore" },1582 { LCB_F_SPIN, "spinlock", "spinlock" },1583 { LCB_F_SPIN | LCB_F_READ, "rwlock:R", "rwlock" },1584 { LCB_F_SPIN | LCB_F_WRITE, "rwlock:W", "rwlock" },1585 { LCB_F_READ, "rwsem:R", "rwsem" },1586 { LCB_F_WRITE, "rwsem:W", "rwsem" },1587 { LCB_F_RT, "rt-mutex", "rt-mutex" },1588 { LCB_F_RT | LCB_F_READ, "rwlock-rt:R", "rwlock-rt" },1589 { LCB_F_RT | LCB_F_WRITE, "rwlock-rt:W", "rwlock-rt" },1590 { LCB_F_PERCPU | LCB_F_READ, "pcpu-sem:R", "percpu-rwsem" },1591 { LCB_F_PERCPU | LCB_F_WRITE, "pcpu-sem:W", "percpu-rwsem" },1592 { LCB_F_MUTEX, "mutex", "mutex" },1593 { LCB_F_MUTEX | LCB_F_SPIN, "mutex", "mutex" },1594 /* alias for get_type_flag() */1595 { LCB_F_MUTEX | LCB_F_SPIN, "mutex-spin", "mutex" },1596};1597 1598static const char *get_type_str(unsigned int flags)1599{1600 flags &= LCB_F_MAX_FLAGS - 1;1601 1602 for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1603 if (lock_type_table[i].flags == flags)1604 return lock_type_table[i].str;1605 }1606 return "unknown";1607}1608 1609static const char *get_type_name(unsigned int flags)1610{1611 flags &= LCB_F_MAX_FLAGS - 1;1612 1613 for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1614 if (lock_type_table[i].flags == flags)1615 return lock_type_table[i].name;1616 }1617 return "unknown";1618}1619 1620static unsigned int get_type_flag(const char *str)1621{1622 for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1623 if (!strcmp(lock_type_table[i].name, str))1624 return lock_type_table[i].flags;1625 }1626 for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {1627 if (!strcmp(lock_type_table[i].str, str))1628 return lock_type_table[i].flags;1629 }1630 return UINT_MAX;1631}1632 1633static void lock_filter_finish(void)1634{1635 zfree(&filters.types);1636 filters.nr_types = 0;1637 1638 zfree(&filters.addrs);1639 filters.nr_addrs = 0;1640 1641 for (int i = 0; i < filters.nr_syms; i++)1642 free(filters.syms[i]);1643 1644 zfree(&filters.syms);1645 filters.nr_syms = 0;1646 1647 zfree(&filters.cgrps);1648 filters.nr_cgrps = 0;1649}1650 1651static void sort_contention_result(void)1652{1653 sort_result();1654}1655 1656static void print_header_stdio(void)1657{1658 struct lock_key *key;1659 1660 list_for_each_entry(key, &lock_keys, list)1661 fprintf(lock_output, "%*s ", key->len, key->header);1662 1663 switch (aggr_mode) {1664 case LOCK_AGGR_TASK:1665 fprintf(lock_output, " %10s %s\n\n", "pid",1666 show_lock_owner ? "owner" : "comm");1667 break;1668 case LOCK_AGGR_CALLER:1669 fprintf(lock_output, " %10s %s\n\n", "type", "caller");1670 break;1671 case LOCK_AGGR_ADDR:1672 fprintf(lock_output, " %16s %s\n\n", "address", "symbol");1673 break;1674 case LOCK_AGGR_CGROUP:1675 fprintf(lock_output, " %s\n\n", "cgroup");1676 break;1677 default:1678 break;1679 }1680}1681 1682static void print_header_csv(const char *sep)1683{1684 struct lock_key *key;1685 1686 fprintf(lock_output, "# output: ");1687 list_for_each_entry(key, &lock_keys, list)1688 fprintf(lock_output, "%s%s ", key->header, sep);1689 1690 switch (aggr_mode) {1691 case LOCK_AGGR_TASK:1692 fprintf(lock_output, "%s%s %s\n", "pid", sep,1693 show_lock_owner ? "owner" : "comm");1694 break;1695 case LOCK_AGGR_CALLER:1696 fprintf(lock_output, "%s%s %s", "type", sep, "caller");1697 if (verbose > 0)1698 fprintf(lock_output, "%s %s", sep, "stacktrace");1699 fprintf(lock_output, "\n");1700 break;1701 case LOCK_AGGR_ADDR:1702 fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type");1703 break;1704 case LOCK_AGGR_CGROUP:1705 fprintf(lock_output, "%s\n", "cgroup");1706 break;1707 default:1708 break;1709 }1710}1711 1712static void print_header(void)1713{1714 if (!quiet) {1715 if (symbol_conf.field_sep)1716 print_header_csv(symbol_conf.field_sep);1717 else1718 print_header_stdio();1719 }1720}1721 1722static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat *st)1723{1724 struct lock_key *key;1725 struct thread *t;1726 int pid;1727 1728 list_for_each_entry(key, &lock_keys, list) {1729 key->print(key, st);1730 fprintf(lock_output, " ");1731 }1732 1733 switch (aggr_mode) {1734 case LOCK_AGGR_CALLER:1735 fprintf(lock_output, " %10s %s\n", get_type_str(st->flags), st->name);1736 break;1737 case LOCK_AGGR_TASK:1738 pid = st->addr;1739 t = perf_session__findnew(session, pid);1740 fprintf(lock_output, " %10d %s\n",1741 pid, pid == -1 ? "Unknown" : thread__comm_str(t));1742 break;1743 case LOCK_AGGR_ADDR:1744 fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr,1745 st->name, get_type_name(st->flags));1746 break;1747 case LOCK_AGGR_CGROUP:1748 fprintf(lock_output, " %s\n", st->name);1749 break;1750 default:1751 break;1752 }1753 1754 if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {1755 struct map *kmap;1756 struct symbol *sym;1757 char buf[128];1758 u64 ip;1759 1760 for (int i = 0; i < max_stack_depth; i++) {1761 if (!st->callstack || !st->callstack[i])1762 break;1763 1764 ip = st->callstack[i];1765 sym = machine__find_kernel_symbol(con->machine, ip, &kmap);1766 get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));1767 fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf);1768 }1769 }1770}1771 1772static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *st,1773 const char *sep)1774{1775 struct lock_key *key;1776 struct thread *t;1777 int pid;1778 1779 list_for_each_entry(key, &lock_keys, list) {1780 key->print(key, st);1781 fprintf(lock_output, "%s ", sep);1782 }1783 1784 switch (aggr_mode) {1785 case LOCK_AGGR_CALLER:1786 fprintf(lock_output, "%s%s %s", get_type_str(st->flags), sep, st->name);1787 if (verbose <= 0)1788 fprintf(lock_output, "\n");1789 break;1790 case LOCK_AGGR_TASK:1791 pid = st->addr;1792 t = perf_session__findnew(session, pid);1793 fprintf(lock_output, "%d%s %s\n", pid, sep,1794 pid == -1 ? "Unknown" : thread__comm_str(t));1795 break;1796 case LOCK_AGGR_ADDR:1797 fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep,1798 st->name, sep, get_type_name(st->flags));1799 break;1800 case LOCK_AGGR_CGROUP:1801 fprintf(lock_output, "%s\n",st->name);1802 break;1803 default:1804 break;1805 }1806 1807 if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {1808 struct map *kmap;1809 struct symbol *sym;1810 char buf[128];1811 u64 ip;1812 1813 for (int i = 0; i < max_stack_depth; i++) {1814 if (!st->callstack || !st->callstack[i])1815 break;1816 1817 ip = st->callstack[i];1818 sym = machine__find_kernel_symbol(con->machine, ip, &kmap);1819 get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));1820 fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);1821 }1822 fprintf(lock_output, "\n");1823 }1824}1825 1826static void print_lock_stat(struct lock_contention *con, struct lock_stat *st)1827{1828 if (symbol_conf.field_sep)1829 print_lock_stat_csv(con, st, symbol_conf.field_sep);1830 else1831 print_lock_stat_stdio(con, st);1832}1833 1834static void print_footer_stdio(int total, int bad, struct lock_contention_fails *fails)1835{1836 /* Output for debug, this have to be removed */1837 int broken = fails->task + fails->stack + fails->time + fails->data;1838 1839 if (!use_bpf)1840 print_bad_events(bad, total);1841 1842 if (quiet || total == 0 || (broken == 0 && verbose <= 0))1843 return;1844 1845 total += broken;1846 fprintf(lock_output, "\n=== output for debug ===\n\n");1847 fprintf(lock_output, "bad: %d, total: %d\n", broken, total);1848 fprintf(lock_output, "bad rate: %.2f %%\n", 100.0 * broken / total);1849 1850 fprintf(lock_output, "histogram of failure reasons\n");1851 fprintf(lock_output, " %10s: %d\n", "task", fails->task);1852 fprintf(lock_output, " %10s: %d\n", "stack", fails->stack);1853 fprintf(lock_output, " %10s: %d\n", "time", fails->time);1854 fprintf(lock_output, " %10s: %d\n", "data", fails->data);1855}1856 1857static void print_footer_csv(int total, int bad, struct lock_contention_fails *fails,1858 const char *sep)1859{1860 /* Output for debug, this have to be removed */1861 if (use_bpf)1862 bad = fails->task + fails->stack + fails->time + fails->data;1863 1864 if (quiet || total == 0 || (bad == 0 && verbose <= 0))1865 return;1866 1867 total += bad;1868 fprintf(lock_output, "# debug: total=%d%s bad=%d", total, sep, bad);1869 1870 if (use_bpf) {1871 fprintf(lock_output, "%s bad_%s=%d", sep, "task", fails->task);1872 fprintf(lock_output, "%s bad_%s=%d", sep, "stack", fails->stack);1873 fprintf(lock_output, "%s bad_%s=%d", sep, "time", fails->time);1874 fprintf(lock_output, "%s bad_%s=%d", sep, "data", fails->data);1875 } else {1876 int i;1877 const char *name[4] = { "acquire", "acquired", "contended", "release" };1878 1879 for (i = 0; i < BROKEN_MAX; i++)1880 fprintf(lock_output, "%s bad_%s=%d", sep, name[i], bad_hist[i]);1881 }1882 fprintf(lock_output, "\n");1883}1884 1885static void print_footer(int total, int bad, struct lock_contention_fails *fails)1886{1887 if (symbol_conf.field_sep)1888 print_footer_csv(total, bad, fails, symbol_conf.field_sep);1889 else1890 print_footer_stdio(total, bad, fails);1891}1892 1893static void print_contention_result(struct lock_contention *con)1894{1895 struct lock_stat *st;1896 int bad, total, printed;1897 1898 if (!quiet)1899 print_header();1900 1901 bad = total = printed = 0;1902 1903 while ((st = pop_from_result())) {1904 total += use_bpf ? st->nr_contended : 1;1905 if (st->broken)1906 bad++;1907 1908 if (!st->wait_time_total)1909 continue;1910 1911 print_lock_stat(con, st);1912 1913 if (++printed >= print_nr_entries)1914 break;1915 }1916 1917 if (print_nr_entries) {1918 /* update the total/bad stats */1919 while ((st = pop_from_result())) {1920 total += use_bpf ? st->nr_contended : 1;1921 if (st->broken)1922 bad++;1923 }1924 }1925 /* some entries are collected but hidden by the callstack filter */1926 total += con->nr_filtered;1927 1928 print_footer(total, bad, &con->fails);1929}1930 1931static bool force;1932 1933static int __cmd_report(bool display_info)1934{1935 int err = -EINVAL;1936 struct perf_tool eops;1937 struct perf_data data = {1938 .path = input_name,1939 .mode = PERF_DATA_MODE_READ,1940 .force = force,1941 };1942 1943 perf_tool__init(&eops, /*ordered_events=*/true);1944 eops.attr = perf_event__process_attr;1945 eops.event_update = process_event_update;1946 eops.sample = process_sample_event;1947 eops.comm = perf_event__process_comm;1948 eops.mmap = perf_event__process_mmap;1949 eops.namespaces = perf_event__process_namespaces;1950 eops.tracing_data = perf_event__process_tracing_data;1951 session = perf_session__new(&data, &eops);1952 if (IS_ERR(session)) {1953 pr_err("Initializing perf session failed\n");1954 return PTR_ERR(session);1955 }1956 1957 symbol_conf.allow_aliases = true;1958 symbol__init(&session->header.env);1959 1960 if (!data.is_pipe) {1961 if (!perf_session__has_traces(session, "lock record"))1962 goto out_delete;1963 1964 if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {1965 pr_err("Initializing perf session tracepoint handlers failed\n");1966 goto out_delete;1967 }1968 1969 if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) {1970 pr_err("Initializing perf session tracepoint handlers failed\n");1971 goto out_delete;1972 }1973 }1974 1975 if (setup_output_field(false, output_fields))1976 goto out_delete;1977 1978 if (select_key(false))1979 goto out_delete;1980 1981 if (show_thread_stats)1982 aggr_mode = LOCK_AGGR_TASK;1983 1984 err = perf_session__process_events(session);1985 if (err)1986 goto out_delete;1987 1988 setup_pager();1989 if (display_info) /* used for info subcommand */1990 dump_info();1991 else {1992 combine_result();1993 sort_result();1994 print_result();1995 }1996 1997out_delete:1998 perf_session__delete(session);1999 return err;2000}2001 2002static void sighandler(int sig __maybe_unused)2003{2004}2005 2006static int check_lock_contention_options(const struct option *options,2007 const char * const *usage)2008 2009{2010 if (show_thread_stats && show_lock_addrs) {2011 pr_err("Cannot use thread and addr mode together\n");2012 parse_options_usage(usage, options, "threads", 0);2013 parse_options_usage(NULL, options, "lock-addr", 0);2014 return -1;2015 }2016 2017 if (show_lock_owner && !use_bpf) {2018 pr_err("Lock owners are available only with BPF\n");2019 parse_options_usage(usage, options, "lock-owner", 0);2020 parse_options_usage(NULL, options, "use-bpf", 0);2021 return -1;2022 }2023 2024 if (show_lock_owner && show_lock_addrs) {2025 pr_err("Cannot use owner and addr mode together\n");2026 parse_options_usage(usage, options, "lock-owner", 0);2027 parse_options_usage(NULL, options, "lock-addr", 0);2028 return -1;2029 }2030 2031 if (show_lock_cgroups && !use_bpf) {2032 pr_err("Cgroups are available only with BPF\n");2033 parse_options_usage(usage, options, "lock-cgroup", 0);2034 parse_options_usage(NULL, options, "use-bpf", 0);2035 return -1;2036 }2037 2038 if (show_lock_cgroups && show_lock_addrs) {2039 pr_err("Cannot use cgroup and addr mode together\n");2040 parse_options_usage(usage, options, "lock-cgroup", 0);2041 parse_options_usage(NULL, options, "lock-addr", 0);2042 return -1;2043 }2044 2045 if (show_lock_cgroups && show_thread_stats) {2046 pr_err("Cannot use cgroup and thread mode together\n");2047 parse_options_usage(usage, options, "lock-cgroup", 0);2048 parse_options_usage(NULL, options, "threads", 0);2049 return -1;2050 }2051 2052 if (symbol_conf.field_sep) {2053 if (strstr(symbol_conf.field_sep, ":") || /* part of type flags */2054 strstr(symbol_conf.field_sep, "+") || /* part of caller offset */2055 strstr(symbol_conf.field_sep, ".")) { /* can be in a symbol name */2056 pr_err("Cannot use the separator that is already used\n");2057 parse_options_usage(usage, options, "x", 1);2058 return -1;2059 }2060 }2061 2062 if (show_lock_owner)2063 show_thread_stats = true;2064 2065 return 0;2066}2067 2068static int __cmd_contention(int argc, const char **argv)2069{2070 int err = -EINVAL;2071 struct perf_tool eops;2072 struct perf_data data = {2073 .path = input_name,2074 .mode = PERF_DATA_MODE_READ,2075 .force = force,2076 };2077 struct lock_contention con = {2078 .target = &target,2079 .map_nr_entries = bpf_map_entries,2080 .max_stack = max_stack_depth,2081 .stack_skip = stack_skip,2082 .filters = &filters,2083 .save_callstack = needs_callstack(),2084 .owner = show_lock_owner,2085 .cgroups = RB_ROOT,2086 };2087 2088 lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));2089 if (!lockhash_table)2090 return -ENOMEM;2091 2092 con.result = &lockhash_table[0];2093 2094 perf_tool__init(&eops, /*ordered_events=*/true);2095 eops.attr = perf_event__process_attr;2096 eops.event_update = process_event_update;2097 eops.sample = process_sample_event;2098 eops.comm = perf_event__process_comm;2099 eops.mmap = perf_event__process_mmap;2100 eops.tracing_data = perf_event__process_tracing_data;2101 2102 session = perf_session__new(use_bpf ? NULL : &data, &eops);2103 if (IS_ERR(session)) {2104 pr_err("Initializing perf session failed\n");2105 err = PTR_ERR(session);2106 session = NULL;2107 goto out_delete;2108 }2109 2110 con.machine = &session->machines.host;2111 2112 con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :2113 show_lock_addrs ? LOCK_AGGR_ADDR :2114 show_lock_cgroups ? LOCK_AGGR_CGROUP : LOCK_AGGR_CALLER;2115 2116 if (con.aggr_mode == LOCK_AGGR_CALLER)2117 con.save_callstack = true;2118 2119 symbol_conf.allow_aliases = true;2120 symbol__init(&session->header.env);2121 2122 if (use_bpf) {2123 err = target__validate(&target);2124 if (err) {2125 char errbuf[512];2126 2127 target__strerror(&target, err, errbuf, 512);2128 pr_err("%s\n", errbuf);2129 goto out_delete;2130 }2131 2132 signal(SIGINT, sighandler);2133 signal(SIGCHLD, sighandler);2134 signal(SIGTERM, sighandler);2135 2136 con.evlist = evlist__new();2137 if (con.evlist == NULL) {2138 err = -ENOMEM;2139 goto out_delete;2140 }2141 2142 err = evlist__create_maps(con.evlist, &target);2143 if (err < 0)2144 goto out_delete;2145 2146 if (argc) {2147 err = evlist__prepare_workload(con.evlist, &target,2148 argv, false, NULL);2149 if (err < 0)2150 goto out_delete;2151 }2152 2153 if (lock_contention_prepare(&con) < 0) {2154 pr_err("lock contention BPF setup failed\n");2155 goto out_delete;2156 }2157 } else if (!data.is_pipe) {2158 if (!perf_session__has_traces(session, "lock record"))2159 goto out_delete;2160 2161 if (!evlist__find_evsel_by_str(session->evlist,2162 "lock:contention_begin")) {2163 pr_err("lock contention evsel not found\n");2164 goto out_delete;2165 }2166 2167 if (perf_session__set_tracepoints_handlers(session,2168 contention_tracepoints)) {2169 pr_err("Initializing perf session tracepoint handlers failed\n");2170 goto out_delete;2171 }2172 }2173 2174 if (setup_output_field(true, output_fields))2175 goto out_delete;2176 2177 if (select_key(true))2178 goto out_delete;2179 2180 if (symbol_conf.field_sep) {2181 int i;2182 struct lock_key *keys = contention_keys;2183 2184 /* do not align output in CSV format */2185 for (i = 0; keys[i].name; i++)2186 keys[i].len = 0;2187 }2188 2189 if (use_bpf) {2190 lock_contention_start();2191 if (argc)2192 evlist__start_workload(con.evlist);2193 2194 /* wait for signal */2195 pause();2196 2197 lock_contention_stop();2198 lock_contention_read(&con);2199 } else {2200 err = perf_session__process_events(session);2201 if (err)2202 goto out_delete;2203 }2204 2205 setup_pager();2206 2207 sort_contention_result();2208 print_contention_result(&con);2209 2210out_delete:2211 lock_filter_finish();2212 evlist__delete(con.evlist);2213 lock_contention_finish(&con);2214 perf_session__delete(session);2215 zfree(&lockhash_table);2216 return err;2217}2218 2219 2220static int __cmd_record(int argc, const char **argv)2221{2222 const char *record_args[] = {2223 "record", "-R", "-m", "1024", "-c", "1", "--synth", "task",2224 };2225 const char *callgraph_args[] = {2226 "--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH),2227 };2228 unsigned int rec_argc, i, j, ret;2229 unsigned int nr_tracepoints;2230 unsigned int nr_callgraph_args = 0;2231 const char **rec_argv;2232 bool has_lock_stat = true;2233 2234 for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {2235 if (!is_valid_tracepoint(lock_tracepoints[i].name)) {2236 pr_debug("tracepoint %s is not enabled. "2237 "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",2238 lock_tracepoints[i].name);2239 has_lock_stat = false;2240 break;2241 }2242 }2243 2244 if (has_lock_stat)2245 goto setup_args;2246 2247 for (i = 0; i < ARRAY_SIZE(contention_tracepoints); i++) {2248 if (!is_valid_tracepoint(contention_tracepoints[i].name)) {2249 pr_err("tracepoint %s is not enabled.\n",2250 contention_tracepoints[i].name);2251 return 1;2252 }2253 }2254 2255 nr_callgraph_args = ARRAY_SIZE(callgraph_args);2256 2257setup_args:2258 rec_argc = ARRAY_SIZE(record_args) + nr_callgraph_args + argc - 1;2259 2260 if (has_lock_stat)2261 nr_tracepoints = ARRAY_SIZE(lock_tracepoints);2262 else2263 nr_tracepoints = ARRAY_SIZE(contention_tracepoints);2264 2265 /* factor of 2 is for -e in front of each tracepoint */2266 rec_argc += 2 * nr_tracepoints;2267 2268 rec_argv = calloc(rec_argc + 1, sizeof(char *));2269 if (!rec_argv)2270 return -ENOMEM;2271 2272 for (i = 0; i < ARRAY_SIZE(record_args); i++)2273 rec_argv[i] = record_args[i];2274 2275 for (j = 0; j < nr_tracepoints; j++) {2276 rec_argv[i++] = "-e";2277 rec_argv[i++] = has_lock_stat2278 ? lock_tracepoints[j].name2279 : contention_tracepoints[j].name;2280 }2281 2282 for (j = 0; j < nr_callgraph_args; j++, i++)2283 rec_argv[i] = callgraph_args[j];2284 2285 for (j = 1; j < (unsigned int)argc; j++, i++)2286 rec_argv[i] = argv[j];2287 2288 BUG_ON(i != rec_argc);2289 2290 ret = cmd_record(i, rec_argv);2291 free(rec_argv);2292 return ret;2293}2294 2295static int parse_map_entry(const struct option *opt, const char *str,2296 int unset __maybe_unused)2297{2298 unsigned long *len = (unsigned long *)opt->value;2299 unsigned long val;2300 char *endptr;2301 2302 errno = 0;2303 val = strtoul(str, &endptr, 0);2304 if (*endptr != '\0' || errno != 0) {2305 pr_err("invalid BPF map length: %s\n", str);2306 return -1;2307 }2308 2309 *len = val;2310 return 0;2311}2312 2313static int parse_max_stack(const struct option *opt, const char *str,2314 int unset __maybe_unused)2315{2316 unsigned long *len = (unsigned long *)opt->value;2317 long val;2318 char *endptr;2319 2320 errno = 0;2321 val = strtol(str, &endptr, 0);2322 if (*endptr != '\0' || errno != 0) {2323 pr_err("invalid max stack depth: %s\n", str);2324 return -1;2325 }2326 2327 if (val < 0 || val > sysctl__max_stack()) {2328 pr_err("invalid max stack depth: %ld\n", val);2329 return -1;2330 }2331 2332 *len = val;2333 return 0;2334}2335 2336static bool add_lock_type(unsigned int flags)2337{2338 unsigned int *tmp;2339 2340 tmp = realloc(filters.types, (filters.nr_types + 1) * sizeof(*filters.types));2341 if (tmp == NULL)2342 return false;2343 2344 tmp[filters.nr_types++] = flags;2345 filters.types = tmp;2346 return true;2347}2348 2349static int parse_lock_type(const struct option *opt __maybe_unused, const char *str,2350 int unset __maybe_unused)2351{2352 char *s, *tmp, *tok;2353 int ret = 0;2354 2355 s = strdup(str);2356 if (s == NULL)2357 return -1;2358 2359 for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2360 unsigned int flags = get_type_flag(tok);2361 2362 if (flags == -1U) {2363 pr_err("Unknown lock flags: %s\n", tok);2364 ret = -1;2365 break;2366 }2367 2368 if (!add_lock_type(flags)) {2369 ret = -1;2370 break;2371 }2372 }2373 2374 free(s);2375 return ret;2376}2377 2378static bool add_lock_addr(unsigned long addr)2379{2380 unsigned long *tmp;2381 2382 tmp = realloc(filters.addrs, (filters.nr_addrs + 1) * sizeof(*filters.addrs));2383 if (tmp == NULL) {2384 pr_err("Memory allocation failure\n");2385 return false;2386 }2387 2388 tmp[filters.nr_addrs++] = addr;2389 filters.addrs = tmp;2390 return true;2391}2392 2393static bool add_lock_sym(char *name)2394{2395 char **tmp;2396 char *sym = strdup(name);2397 2398 if (sym == NULL) {2399 pr_err("Memory allocation failure\n");2400 return false;2401 }2402 2403 tmp = realloc(filters.syms, (filters.nr_syms + 1) * sizeof(*filters.syms));2404 if (tmp == NULL) {2405 pr_err("Memory allocation failure\n");2406 free(sym);2407 return false;2408 }2409 2410 tmp[filters.nr_syms++] = sym;2411 filters.syms = tmp;2412 return true;2413}2414 2415static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str,2416 int unset __maybe_unused)2417{2418 char *s, *tmp, *tok;2419 int ret = 0;2420 u64 addr;2421 2422 s = strdup(str);2423 if (s == NULL)2424 return -1;2425 2426 for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2427 char *end;2428 2429 addr = strtoul(tok, &end, 16);2430 if (*end == '\0') {2431 if (!add_lock_addr(addr)) {2432 ret = -1;2433 break;2434 }2435 continue;2436 }2437 2438 /*2439 * At this moment, we don't have kernel symbols. Save the symbols2440 * in a separate list and resolve them to addresses later.2441 */2442 if (!add_lock_sym(tok)) {2443 ret = -1;2444 break;2445 }2446 }2447 2448 free(s);2449 return ret;2450}2451 2452static int parse_call_stack(const struct option *opt __maybe_unused, const char *str,2453 int unset __maybe_unused)2454{2455 char *s, *tmp, *tok;2456 int ret = 0;2457 2458 s = strdup(str);2459 if (s == NULL)2460 return -1;2461 2462 for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2463 struct callstack_filter *entry;2464 2465 entry = malloc(sizeof(*entry) + strlen(tok) + 1);2466 if (entry == NULL) {2467 pr_err("Memory allocation failure\n");2468 free(s);2469 return -1;2470 }2471 2472 strcpy(entry->name, tok);2473 list_add_tail(&entry->list, &callstack_filters);2474 }2475 2476 free(s);2477 return ret;2478}2479 2480static int parse_output(const struct option *opt __maybe_unused, const char *str,2481 int unset __maybe_unused)2482{2483 const char **name = (const char **)opt->value;2484 2485 if (str == NULL)2486 return -1;2487 2488 lock_output = fopen(str, "w");2489 if (lock_output == NULL) {2490 pr_err("Cannot open %s\n", str);2491 return -1;2492 }2493 2494 *name = str;2495 return 0;2496}2497 2498static bool add_lock_cgroup(char *name)2499{2500 u64 *tmp;2501 struct cgroup *cgrp;2502 2503 cgrp = cgroup__new(name, /*do_open=*/false);2504 if (cgrp == NULL) {2505 pr_err("Failed to create cgroup: %s\n", name);2506 return false;2507 }2508 2509 if (read_cgroup_id(cgrp) < 0) {2510 pr_err("Failed to read cgroup id for %s\n", name);2511 cgroup__put(cgrp);2512 return false;2513 }2514 2515 tmp = realloc(filters.cgrps, (filters.nr_cgrps + 1) * sizeof(*filters.cgrps));2516 if (tmp == NULL) {2517 pr_err("Memory allocation failure\n");2518 return false;2519 }2520 2521 tmp[filters.nr_cgrps++] = cgrp->id;2522 filters.cgrps = tmp;2523 cgroup__put(cgrp);2524 return true;2525}2526 2527static int parse_cgroup_filter(const struct option *opt __maybe_unused, const char *str,2528 int unset __maybe_unused)2529{2530 char *s, *tmp, *tok;2531 int ret = 0;2532 2533 s = strdup(str);2534 if (s == NULL)2535 return -1;2536 2537 for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {2538 if (!add_lock_cgroup(tok)) {2539 ret = -1;2540 break;2541 }2542 }2543 2544 free(s);2545 return ret;2546}2547 2548int cmd_lock(int argc, const char **argv)2549{2550 const struct option lock_options[] = {2551 OPT_STRING('i', "input", &input_name, "file", "input file name"),2552 OPT_CALLBACK(0, "output", &output_name, "file", "output file name", parse_output),2553 OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),2554 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),2555 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),2556 OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,2557 "file", "vmlinux pathname"),2558 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,2559 "file", "kallsyms pathname"),2560 OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"),2561 OPT_END()2562 };2563 2564 const struct option info_options[] = {2565 OPT_BOOLEAN('t', "threads", &info_threads,2566 "dump the thread list in perf.data"),2567 OPT_BOOLEAN('m', "map", &info_map,2568 "dump the map of lock instances (address:name table)"),2569 OPT_PARENT(lock_options)2570 };2571 2572 const struct option report_options[] = {2573 OPT_STRING('k', "key", &sort_key, "acquired",2574 "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),2575 OPT_STRING('F', "field", &output_fields, NULL,2576 "output fields (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),2577 /* TODO: type */2578 OPT_BOOLEAN('c', "combine-locks", &combine_locks,2579 "combine locks in the same class"),2580 OPT_BOOLEAN('t', "threads", &show_thread_stats,2581 "show per-thread lock stats"),2582 OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),2583 OPT_PARENT(lock_options)2584 };2585 2586 struct option contention_options[] = {2587 OPT_STRING('k', "key", &sort_key, "wait_total",2588 "key for sorting (contended / wait_total / wait_max / wait_min / avg_wait)"),2589 OPT_STRING('F', "field", &output_fields, "contended,wait_total,wait_max,avg_wait",2590 "output fields (contended / wait_total / wait_max / wait_min / avg_wait)"),2591 OPT_BOOLEAN('t', "threads", &show_thread_stats,2592 "show per-thread lock stats"),2593 OPT_BOOLEAN('b', "use-bpf", &use_bpf, "use BPF program to collect lock contention stats"),2594 OPT_BOOLEAN('a', "all-cpus", &target.system_wide,2595 "System-wide collection from all CPUs"),2596 OPT_STRING('C', "cpu", &target.cpu_list, "cpu",2597 "List of cpus to monitor"),2598 OPT_STRING('p', "pid", &target.pid, "pid",2599 "Trace on existing process id"),2600 OPT_STRING(0, "tid", &target.tid, "tid",2601 "Trace on existing thread id (exclusive to --pid)"),2602 OPT_CALLBACK('M', "map-nr-entries", &bpf_map_entries, "num",2603 "Max number of BPF map entries", parse_map_entry),2604 OPT_CALLBACK(0, "max-stack", &max_stack_depth, "num",2605 "Set the maximum stack depth when collecting lock contention, "2606 "Default: " __stringify(CONTENTION_STACK_DEPTH), parse_max_stack),2607 OPT_INTEGER(0, "stack-skip", &stack_skip,2608 "Set the number of stack depth to skip when finding a lock caller, "2609 "Default: " __stringify(CONTENTION_STACK_SKIP)),2610 OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),2611 OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),2612 OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS",2613 "Filter specific type of locks", parse_lock_type),2614 OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES",2615 "Filter specific address/symbol of locks", parse_lock_addr),2616 OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES",2617 "Filter specific function in the callstack", parse_call_stack),2618 OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),2619 OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, "separator",2620 "print result in CSV format with custom separator"),2621 OPT_BOOLEAN(0, "lock-cgroup", &show_lock_cgroups, "show lock stats by cgroup"),2622 OPT_CALLBACK('G', "cgroup-filter", NULL, "CGROUPS",2623 "Filter specific cgroups", parse_cgroup_filter),2624 OPT_PARENT(lock_options)2625 };2626 2627 const char * const info_usage[] = {2628 "perf lock info [<options>]",2629 NULL2630 };2631 const char *const lock_subcommands[] = { "record", "report", "script",2632 "info", "contention", NULL };2633 const char *lock_usage[] = {2634 NULL,2635 NULL2636 };2637 const char * const report_usage[] = {2638 "perf lock report [<options>]",2639 NULL2640 };2641 const char * const contention_usage[] = {2642 "perf lock contention [<options>]",2643 NULL2644 };2645 unsigned int i;2646 int rc = 0;2647 2648 lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));2649 if (!lockhash_table)2650 return -ENOMEM;2651 2652 for (i = 0; i < LOCKHASH_SIZE; i++)2653 INIT_HLIST_HEAD(lockhash_table + i);2654 2655 lock_output = stderr;2656 argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,2657 lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);2658 if (!argc)2659 usage_with_options(lock_usage, lock_options);2660 2661 if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {2662 return __cmd_record(argc, argv);2663 } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {2664 trace_handler = &report_lock_ops;2665 if (argc) {2666 argc = parse_options(argc, argv,2667 report_options, report_usage, 0);2668 if (argc)2669 usage_with_options(report_usage, report_options);2670 }2671 rc = __cmd_report(false);2672 } else if (!strcmp(argv[0], "script")) {2673 /* Aliased to 'perf script' */2674 rc = cmd_script(argc, argv);2675 } else if (!strcmp(argv[0], "info")) {2676 if (argc) {2677 argc = parse_options(argc, argv,2678 info_options, info_usage, 0);2679 if (argc)2680 usage_with_options(info_usage, info_options);2681 }2682 2683 /* If neither threads nor map requested, display both */2684 if (!info_threads && !info_map) {2685 info_threads = true;2686 info_map = true;2687 }2688 2689 /* recycling report_lock_ops */2690 trace_handler = &report_lock_ops;2691 rc = __cmd_report(true);2692 } else if (strlen(argv[0]) > 2 && strstarts("contention", argv[0])) {2693 trace_handler = &contention_lock_ops;2694 sort_key = "wait_total";2695 output_fields = "contended,wait_total,wait_max,avg_wait";2696 2697#ifndef HAVE_BPF_SKEL2698 set_option_nobuild(contention_options, 'b', "use-bpf",2699 "no BUILD_BPF_SKEL=1", false);2700#endif2701 if (argc) {2702 argc = parse_options(argc, argv, contention_options,2703 contention_usage, 0);2704 }2705 2706 if (check_lock_contention_options(contention_options,2707 contention_usage) < 0)2708 return -1;2709 2710 rc = __cmd_contention(argc, argv);2711 } else {2712 usage_with_options(lock_usage, lock_options);2713 }2714 2715 /* free usage string allocated by parse_options_subcommand */2716 free((void *)lock_usage[0]);2717 2718 zfree(&lockhash_table);2719 return rc;2720}2721