15 #include <subcmd/parse-options.h> 23 #include <sys/types.h> 24 #include <sys/prctl.h> 25 #include <semaphore.h> 30 #include <linux/list.h> 31 #include <linux/hash.h> 32 #include <linux/kernel.h> 37 #define LOCKHASH_BITS 12 38 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) 42 #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) 43 #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) 82 #define SEQ_STATE_UNINITIALIZED 0 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 94 #define MAX_LOCK_DEPTH 48 105 struct list_head list;
117 struct list_head seq_list;
124 struct rb_node *
node;
132 else if (tid < st->tid)
133 node = node->rb_left;
135 node = node->rb_right;
144 struct rb_node *parent = NULL;
152 rb = &(*rb)->rb_left;
153 else if (
new->tid > p->
tid)
154 rb = &(*rb)->rb_right;
156 BUG_ON(
"inserting invalid thread_stat\n");
159 rb_link_node(&
new->rb, parent, rb);
173 pr_err(
"memory allocation failed\n");
187 thread_stat_findnew_first;
195 pr_err(
"memory allocation failed\n");
209 #define SINGLE_KEY(member) \ 210 static int lock_stat_key_ ## member(struct lock_stat *one, \ 211 struct lock_stat *two) \ 213 return one->member > two->member; \ 225 u64 s1 = one->wait_time_min;
226 u64 s2 = two->wait_time_min;
227 if (s1 == ULLONG_MAX)
229 if (s2 == ULLONG_MAX)
250 #define DEF_KEY_LOCK(name, fn_suffix) \ 251 { #name, lock_stat_key_ ## fn_suffix } 269 for (i = 0; keys[i].
name; i++) {
284 struct rb_node **
rb = &
result.rb_node;
285 struct rb_node *parent = NULL;
289 p = container_of(*rb,
struct lock_stat, rb);
293 rb = &(*rb)->rb_left;
295 rb = &(*rb)->rb_right;
298 rb_link_node(&st->
rb, parent, rb);
310 while (node->rb_left)
311 node = node->rb_left;
323 if (ret->
addr == addr)
332 new->name =
zalloc(
sizeof(
char) * strlen(name) + 1);
338 strcpy(
new->name, name);
339 new->wait_time_min = ULLONG_MAX;
341 list_add(&
new->hash_entry, entry);
345 pr_err(
"memory allocation failed\n");
368 if (seq->
addr == addr)
374 pr_err(
"memory allocation failed\n");
410 memcpy(&addr, &tmp,
sizeof(
void *));
426 switch (seq->
state) {
457 list_del(&seq->
list);
461 BUG_ON(
"Unknown state of lock sequence found!\n");
482 memcpy(&addr, &tmp,
sizeof(
void *));
498 switch (seq->
state) {
518 list_del(&seq->
list);
522 BUG_ON(
"Unknown state of lock sequence found!\n");
544 memcpy(&addr, &tmp,
sizeof(
void *));
560 switch (seq->
state) {
573 list_del(&seq->
list);
577 BUG_ON(
"Unknown state of lock sequence found!\n");
599 memcpy(&addr, &tmp,
sizeof(
void *));
615 switch (seq->
state) {
636 BUG_ON(
"Unknown state of lock sequence found!\n");
642 list_del(&seq->
list);
695 const char *
name[4] =
696 {
"acquire",
"acquired",
"contended",
"release" };
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");
703 pr_info(
" %10s: %d\n", name[i], bad_hist[i]);
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)");
733 if (strlen(st->
name) < 16) {
737 strncpy(cut_name, st->
name, 16);
765 struct rb_node *
node;
768 pr_info(
"%10s: comm\n",
"Thread ID");
775 node = rb_next(node);
785 pr_info(
"Address of instance: name of class\n");
803 pr_err(
"Unknown type of information\n");
822 if (thread == NULL) {
823 pr_debug(
"problem processing %d event, skipping it.\n",
830 err =
f(evsel, sample);
866 .ordered_events =
true,
878 pr_err(
"Initializing perf session failed\n");
888 pr_err(
"Initializing perf session tracepoint handlers failed\n");
914 const char *record_args[] = {
915 "record",
"-R",
"-m",
"1024",
"-c",
"1",
917 unsigned int rec_argc, i, j, ret;
918 const char **rec_argv;
920 for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {
922 pr_err(
"tracepoint %s is not enabled. " 923 "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
924 lock_tracepoints[i].name);
929 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
931 rec_argc += 2 * ARRAY_SIZE(lock_tracepoints);
933 rec_argv = calloc(rec_argc + 1,
sizeof(
char *));
937 for (i = 0; i < ARRAY_SIZE(record_args); i++)
938 rec_argv[i] = strdup(record_args[i]);
940 for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) {
941 rec_argv[i++] =
"-e";
942 rec_argv[i++] = strdup(lock_tracepoints[j].
name);
945 for (j = 1; j < (
unsigned int)argc; j++, i++)
946 rec_argv[i] = argv[j];
948 BUG_ON(i != rec_argc);
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"),
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)
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)"),
977 OPT_PARENT(lock_options)
980 const char *
const info_usage[] = {
981 "perf lock info [<options>]",
984 const char *
const lock_subcommands[] = {
"record",
"report",
"script",
986 const char *lock_usage[] = {
990 const char *
const report_usage[] = {
991 "perf lock report [<options>]",
1000 argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
1001 lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1003 usage_with_options(lock_usage, lock_options);
1005 if (!strncmp(argv[0],
"rec", 3)) {
1007 }
else if (!strncmp(argv[0],
"report", 6)) {
1010 argc = parse_options(argc, argv,
1011 report_options, report_usage, 0);
1013 usage_with_options(report_usage, report_options);
1016 }
else if (!strcmp(argv[0],
"script")) {
1019 }
else if (!strcmp(argv[0],
"info")) {
1021 argc = parse_options(argc, argv,
1022 info_options, info_usage, 0);
1024 usage_with_options(info_usage, info_options);
1030 usage_with_options(lock_usage, lock_options);
static struct thread_stat * thread_stat_findnew_first(u32 tid)
static int report_lock_acquired_event(struct perf_evsel *evsel, struct perf_sample *sample)
int(* acquire_event)(struct perf_evsel *evsel, struct perf_sample *sample)
#define SEQ_STATE_UNINITIALIZED
static int report_lock_acquire_event(struct perf_evsel *evsel, struct perf_sample *sample)
int cmd_record(int argc, const char **argv)
static int select_key(void)
#define SEQ_STATE_RELEASED
struct list_head hash_entry
static struct trace_lock_handler report_lock_ops
static int bad_hist[BROKEN_MAX]
struct perf_data_file file
static struct thread_stat *(* thread_stat_findnew)(u32 tid)
static int perf_evsel__process_lock_release(struct perf_evsel *evsel, struct perf_sample *sample)
static struct lock_stat * pop_from_result(void)
static struct thread_stat * thread_stat_find(u32 tid)
int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine)
static struct lock_seq_stat * get_seq(struct thread_stat *ts, void *addr)
static int process_sample_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine)
bool perf_session__has_traces(struct perf_session *session, const char *msg)
int cmd_script(int argc, const char **argv)
static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel, struct perf_sample *sample)
void perf_session__delete(struct perf_session *session)
static void thread_stat_insert(struct thread_stat *new)
static int dump_info(void)
static struct lock_stat * lock_stat_findnew(void *addr, const char *name)
static int lock_stat_key_wait_time_min(struct lock_stat *one, struct lock_stat *two)
#define SEQ_STATE_READ_ACQUIRED
static void insert_to_result(struct lock_stat *st, int(*bigger)(struct lock_stat *, struct lock_stat *))
int(* acquired_event)(struct perf_evsel *evsel, struct perf_sample *sample)
static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel, struct perf_sample *sample)
#define SEQ_STATE_CONTENDED
const char * thread__comm_str(const struct thread *thread)
int(* key)(struct lock_stat *, struct lock_stat *)
unsigned int nr_contended
#define pr_debug(fmt,...)
static struct list_head lockhash_table[LOCKHASH_SIZE]
static struct perf_session * session
#define SEQ_STATE_ACQUIRING
int is_valid_tracepoint(const char *event_string)
static int entry(u64 ip, struct unwind_info *ui)
static char * perf_evsel__strval(struct perf_evsel *evsel, struct perf_sample *sample, const char *name)
static int report_lock_contended_event(struct perf_evsel *evsel, struct perf_sample *sample)
static struct perf_tool tool
static void print_bad_events(int bad, int total)
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, const char *name)
x86 movsq based memcpy() in arch/x86/lib/memcpy_64.S") MEMCPY_FN(memcpy_erms
static void print_result(void)
#define SEQ_STATE_ACQUIRED
int cmd_lock(int argc, const char **argv)
static const char * sort_key
static void sort_result(void)
static struct rb_root thread_stats
struct perf_event_header header
static void dump_map(void)
static int(* compare)(struct lock_stat *, struct lock_stat *)
#define SINGLE_KEY(member)
struct perf_session * perf_session__new(struct perf_data *data, bool repipe, struct perf_tool *tool)
#define lockhashentry(key)
void thread__put(struct thread *thread)
int perf_session__process_events(struct perf_session *session)
static int report_lock_release_event(struct perf_evsel *evsel, struct perf_sample *sample)
static struct trace_lock_handler * trace_handler
static int __cmd_record(int argc, const char **argv)
struct perf_header header
int(* release_event)(struct perf_evsel *evsel, struct perf_sample *sample)
int symbol__init(struct perf_env *env)
struct thread * perf_session__findnew(struct perf_session *session, pid_t pid)
struct list_head seq_list
#define DEF_KEY_LOCK(name, fn_suffix)
int(* tracepoint_handler)(struct perf_evsel *evsel, struct perf_sample *sample)
static int __cmd_report(bool display_info)
#define perf_session__set_tracepoints_handlers(session, array)
int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine)
static void dump_threads(void)
static const struct perf_evsel_str_handler lock_tracepoints[]
int(* contended_event)(struct perf_evsel *evsel, struct perf_sample *sample)
struct thread * machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid)
static int perf_evsel__process_lock_contended(struct perf_evsel *evsel, struct perf_sample *sample)
static struct thread_stat * thread_stat_findnew_after_first(u32 tid)
void static void * zalloc(size_t size)