|
@@ -387,7 +387,20 @@ static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
|
|
return seq;
|
|
return seq;
|
|
}
|
|
}
|
|
|
|
|
|
-static int bad_hist[4];
|
|
|
|
|
|
+enum broken_state {
|
|
|
|
+ BROKEN_ACQUIRE,
|
|
|
|
+ BROKEN_ACQUIRED,
|
|
|
|
+ BROKEN_CONTENDED,
|
|
|
|
+ BROKEN_RELEASE,
|
|
|
|
+ BROKEN_MAX,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int bad_hist[BROKEN_MAX];
|
|
|
|
+
|
|
|
|
+enum acquire_flags {
|
|
|
|
+ TRY_LOCK = 1,
|
|
|
|
+ READ_LOCK = 2,
|
|
|
|
+};
|
|
|
|
|
|
static void
|
|
static void
|
|
report_lock_acquire_event(struct trace_acquire_event *acquire_event,
|
|
report_lock_acquire_event(struct trace_acquire_event *acquire_event,
|
|
@@ -413,9 +426,9 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event,
|
|
if (!acquire_event->flag) {
|
|
if (!acquire_event->flag) {
|
|
seq->state = SEQ_STATE_ACQUIRING;
|
|
seq->state = SEQ_STATE_ACQUIRING;
|
|
} else {
|
|
} else {
|
|
- if (acquire_event->flag & 1)
|
|
|
|
|
|
+ if (acquire_event->flag & TRY_LOCK)
|
|
ls->nr_trylock++;
|
|
ls->nr_trylock++;
|
|
- if (acquire_event->flag & 2)
|
|
|
|
|
|
+ if (acquire_event->flag & READ_LOCK)
|
|
ls->nr_readlock++;
|
|
ls->nr_readlock++;
|
|
seq->state = SEQ_STATE_READ_ACQUIRED;
|
|
seq->state = SEQ_STATE_READ_ACQUIRED;
|
|
seq->read_count = 1;
|
|
seq->read_count = 1;
|
|
@@ -423,7 +436,7 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event,
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case SEQ_STATE_READ_ACQUIRED:
|
|
case SEQ_STATE_READ_ACQUIRED:
|
|
- if (acquire_event->flag & 2) {
|
|
|
|
|
|
+ if (acquire_event->flag & READ_LOCK) {
|
|
seq->read_count++;
|
|
seq->read_count++;
|
|
ls->nr_acquired++;
|
|
ls->nr_acquired++;
|
|
goto end;
|
|
goto end;
|
|
@@ -437,7 +450,7 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event,
|
|
broken:
|
|
broken:
|
|
/* broken lock sequence, discard it */
|
|
/* broken lock sequence, discard it */
|
|
ls->discard = 1;
|
|
ls->discard = 1;
|
|
- bad_hist[0]++;
|
|
|
|
|
|
+ bad_hist[BROKEN_ACQUIRE]++;
|
|
list_del(&seq->list);
|
|
list_del(&seq->list);
|
|
free(seq);
|
|
free(seq);
|
|
goto end;
|
|
goto end;
|
|
@@ -481,10 +494,9 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event,
|
|
case SEQ_STATE_CONTENDED:
|
|
case SEQ_STATE_CONTENDED:
|
|
contended_term = timestamp - seq->prev_event_time;
|
|
contended_term = timestamp - seq->prev_event_time;
|
|
ls->wait_time_total += contended_term;
|
|
ls->wait_time_total += contended_term;
|
|
-
|
|
|
|
if (contended_term < ls->wait_time_min)
|
|
if (contended_term < ls->wait_time_min)
|
|
ls->wait_time_min = contended_term;
|
|
ls->wait_time_min = contended_term;
|
|
- else if (ls->wait_time_max < contended_term)
|
|
|
|
|
|
+ if (ls->wait_time_max < contended_term)
|
|
ls->wait_time_max = contended_term;
|
|
ls->wait_time_max = contended_term;
|
|
break;
|
|
break;
|
|
case SEQ_STATE_RELEASED:
|
|
case SEQ_STATE_RELEASED:
|
|
@@ -492,7 +504,7 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event,
|
|
case SEQ_STATE_READ_ACQUIRED:
|
|
case SEQ_STATE_READ_ACQUIRED:
|
|
/* broken lock sequence, discard it */
|
|
/* broken lock sequence, discard it */
|
|
ls->discard = 1;
|
|
ls->discard = 1;
|
|
- bad_hist[1]++;
|
|
|
|
|
|
+ bad_hist[BROKEN_ACQUIRED]++;
|
|
list_del(&seq->list);
|
|
list_del(&seq->list);
|
|
free(seq);
|
|
free(seq);
|
|
goto end;
|
|
goto end;
|
|
@@ -540,7 +552,7 @@ report_lock_contended_event(struct trace_contended_event *contended_event,
|
|
case SEQ_STATE_CONTENDED:
|
|
case SEQ_STATE_CONTENDED:
|
|
/* broken lock sequence, discard it */
|
|
/* broken lock sequence, discard it */
|
|
ls->discard = 1;
|
|
ls->discard = 1;
|
|
- bad_hist[2]++;
|
|
|
|
|
|
+ bad_hist[BROKEN_CONTENDED]++;
|
|
list_del(&seq->list);
|
|
list_del(&seq->list);
|
|
free(seq);
|
|
free(seq);
|
|
goto end;
|
|
goto end;
|
|
@@ -594,7 +606,7 @@ report_lock_release_event(struct trace_release_event *release_event,
|
|
case SEQ_STATE_RELEASED:
|
|
case SEQ_STATE_RELEASED:
|
|
/* broken lock sequence, discard it */
|
|
/* broken lock sequence, discard it */
|
|
ls->discard = 1;
|
|
ls->discard = 1;
|
|
- bad_hist[3]++;
|
|
|
|
|
|
+ bad_hist[BROKEN_RELEASE]++;
|
|
goto free_seq;
|
|
goto free_seq;
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
@@ -713,6 +725,21 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
|
|
process_lock_release_event(data, event, cpu, timestamp, thread);
|
|
process_lock_release_event(data, event, cpu, timestamp, thread);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void print_bad_events(int bad, int total)
|
|
|
|
+{
|
|
|
|
+ /* Output for debug, this have to be removed */
|
|
|
|
+ int i;
|
|
|
|
+ const char *name[4] =
|
|
|
|
+ { "acquire", "acquired", "contended", "release" };
|
|
|
|
+
|
|
|
|
+ pr_info("\n=== output for debug===\n\n");
|
|
|
|
+ pr_info("bad: %d, total: %d\n", bad, total);
|
|
|
|
+ pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100);
|
|
|
|
+ pr_info("histogram of events caused bad sequence\n");
|
|
|
|
+ for (i = 0; i < BROKEN_MAX; i++)
|
|
|
|
+ pr_info(" %10s: %d\n", name[i], bad_hist[i]);
|
|
|
|
+}
|
|
|
|
+
|
|
/* TODO: various way to print, coloring, nano or milli sec */
|
|
/* TODO: various way to print, coloring, nano or milli sec */
|
|
static void print_result(void)
|
|
static void print_result(void)
|
|
{
|
|
{
|
|
@@ -720,15 +747,15 @@ static void print_result(void)
|
|
char cut_name[20];
|
|
char cut_name[20];
|
|
int bad, total;
|
|
int bad, total;
|
|
|
|
|
|
- printf("%20s ", "Name");
|
|
|
|
- printf("%10s ", "acquired");
|
|
|
|
- printf("%10s ", "contended");
|
|
|
|
|
|
+ pr_info("%20s ", "Name");
|
|
|
|
+ pr_info("%10s ", "acquired");
|
|
|
|
+ pr_info("%10s ", "contended");
|
|
|
|
|
|
- printf("%15s ", "total wait (ns)");
|
|
|
|
- printf("%15s ", "max wait (ns)");
|
|
|
|
- printf("%15s ", "min wait (ns)");
|
|
|
|
|
|
+ pr_info("%15s ", "total wait (ns)");
|
|
|
|
+ pr_info("%15s ", "max wait (ns)");
|
|
|
|
+ pr_info("%15s ", "min wait (ns)");
|
|
|
|
|
|
- printf("\n\n");
|
|
|
|
|
|
+ pr_info("\n\n");
|
|
|
|
|
|
bad = total = 0;
|
|
bad = total = 0;
|
|
while ((st = pop_from_result())) {
|
|
while ((st = pop_from_result())) {
|
|
@@ -741,7 +768,7 @@ static void print_result(void)
|
|
|
|
|
|
if (strlen(st->name) < 16) {
|
|
if (strlen(st->name) < 16) {
|
|
/* output raw name */
|
|
/* output raw name */
|
|
- printf("%20s ", st->name);
|
|
|
|
|
|
+ pr_info("%20s ", st->name);
|
|
} else {
|
|
} else {
|
|
strncpy(cut_name, st->name, 16);
|
|
strncpy(cut_name, st->name, 16);
|
|
cut_name[16] = '.';
|
|
cut_name[16] = '.';
|
|
@@ -749,33 +776,40 @@ static void print_result(void)
|
|
cut_name[18] = '.';
|
|
cut_name[18] = '.';
|
|
cut_name[19] = '\0';
|
|
cut_name[19] = '\0';
|
|
/* cut off name for saving output style */
|
|
/* cut off name for saving output style */
|
|
- printf("%20s ", cut_name);
|
|
|
|
|
|
+ pr_info("%20s ", cut_name);
|
|
}
|
|
}
|
|
|
|
|
|
- printf("%10u ", st->nr_acquired);
|
|
|
|
- printf("%10u ", st->nr_contended);
|
|
|
|
|
|
+ pr_info("%10u ", st->nr_acquired);
|
|
|
|
+ pr_info("%10u ", st->nr_contended);
|
|
|
|
|
|
- printf("%15llu ", st->wait_time_total);
|
|
|
|
- printf("%15llu ", st->wait_time_max);
|
|
|
|
- printf("%15llu ", st->wait_time_min == ULLONG_MAX ?
|
|
|
|
|
|
+ pr_info("%15llu ", st->wait_time_total);
|
|
|
|
+ pr_info("%15llu ", st->wait_time_max);
|
|
|
|
+ pr_info("%15llu ", st->wait_time_min == ULLONG_MAX ?
|
|
0 : st->wait_time_min);
|
|
0 : st->wait_time_min);
|
|
- printf("\n");
|
|
|
|
|
|
+ pr_info("\n");
|
|
}
|
|
}
|
|
|
|
|
|
- {
|
|
|
|
- /* Output for debug, this have to be removed */
|
|
|
|
- int i;
|
|
|
|
- const char *name[4] =
|
|
|
|
- { "acquire", "acquired", "contended", "release" };
|
|
|
|
|
|
+ print_bad_events(bad, total);
|
|
|
|
+}
|
|
|
|
|
|
- printf("\n=== output for debug===\n\n");
|
|
|
|
- printf("bad:%d, total:%d\n", bad, total);
|
|
|
|
- printf("bad rate:%f\n", (double)(bad / total));
|
|
|
|
|
|
+static int info_threads;
|
|
|
|
+static int info_map;
|
|
|
|
|
|
- printf("histogram of events caused bad sequence\n");
|
|
|
|
- for (i = 0; i < 4; i++)
|
|
|
|
- printf(" %10s: %d\n", name[i], bad_hist[i]);
|
|
|
|
- }
|
|
|
|
|
|
+static void dump_threads(void)
|
|
|
|
+{
|
|
|
|
+ struct thread_stat *st;
|
|
|
|
+ struct rb_node *node;
|
|
|
|
+ struct thread *t;
|
|
|
|
+
|
|
|
|
+ pr_info("%10s: comm\n", "Thread ID");
|
|
|
|
+
|
|
|
|
+ node = rb_first(&thread_stats);
|
|
|
|
+ while (node) {
|
|
|
|
+ st = container_of(node, struct thread_stat, rb);
|
|
|
|
+ t = perf_session__findnew(session, st->tid);
|
|
|
|
+ pr_info("%10d: %s\n", st->tid, t->comm);
|
|
|
|
+ node = rb_next(node);
|
|
|
|
+ };
|
|
}
|
|
}
|
|
|
|
|
|
static void dump_map(void)
|
|
static void dump_map(void)
|
|
@@ -783,13 +817,24 @@ static void dump_map(void)
|
|
unsigned int i;
|
|
unsigned int i;
|
|
struct lock_stat *st;
|
|
struct lock_stat *st;
|
|
|
|
|
|
|
|
+ pr_info("Address of instance: name of class\n");
|
|
for (i = 0; i < LOCKHASH_SIZE; i++) {
|
|
for (i = 0; i < LOCKHASH_SIZE; i++) {
|
|
list_for_each_entry(st, &lockhash_table[i], hash_entry) {
|
|
list_for_each_entry(st, &lockhash_table[i], hash_entry) {
|
|
- printf("%p: %s\n", st->addr, st->name);
|
|
|
|
|
|
+ pr_info(" %p: %s\n", st->addr, st->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void dump_info(void)
|
|
|
|
+{
|
|
|
|
+ if (info_threads)
|
|
|
|
+ dump_threads();
|
|
|
|
+ else if (info_map)
|
|
|
|
+ dump_map();
|
|
|
|
+ else
|
|
|
|
+ die("Unknown type of information\n");
|
|
|
|
+}
|
|
|
|
+
|
|
static int process_sample_event(event_t *self, struct perf_session *s)
|
|
static int process_sample_event(event_t *self, struct perf_session *s)
|
|
{
|
|
{
|
|
struct sample_data data;
|
|
struct sample_data data;
|
|
@@ -858,6 +903,19 @@ static const struct option report_options[] = {
|
|
OPT_END()
|
|
OPT_END()
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static const char * const info_usage[] = {
|
|
|
|
+ "perf lock info [<options>]",
|
|
|
|
+ NULL
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct option info_options[] = {
|
|
|
|
+ OPT_BOOLEAN('t', "threads", &info_threads,
|
|
|
|
+ "dump thread list in perf.data"),
|
|
|
|
+ OPT_BOOLEAN('m', "map", &info_map,
|
|
|
|
+ "map of lock instances (name:address table)"),
|
|
|
|
+ OPT_END()
|
|
|
|
+};
|
|
|
|
+
|
|
static const char * const lock_usage[] = {
|
|
static const char * const lock_usage[] = {
|
|
"perf lock [<options>] {record|trace|report}",
|
|
"perf lock [<options>] {record|trace|report}",
|
|
NULL
|
|
NULL
|
|
@@ -872,7 +930,6 @@ static const struct option lock_options[] = {
|
|
|
|
|
|
static const char *record_args[] = {
|
|
static const char *record_args[] = {
|
|
"record",
|
|
"record",
|
|
- "-a",
|
|
|
|
"-R",
|
|
"-R",
|
|
"-f",
|
|
"-f",
|
|
"-m", "1024",
|
|
"-m", "1024",
|
|
@@ -929,12 +986,18 @@ int cmd_lock(int argc, const char **argv, const char *prefix __used)
|
|
} else if (!strcmp(argv[0], "trace")) {
|
|
} else if (!strcmp(argv[0], "trace")) {
|
|
/* Aliased to 'perf trace' */
|
|
/* Aliased to 'perf trace' */
|
|
return cmd_trace(argc, argv, prefix);
|
|
return cmd_trace(argc, argv, prefix);
|
|
- } else if (!strcmp(argv[0], "map")) {
|
|
|
|
|
|
+ } else if (!strcmp(argv[0], "info")) {
|
|
|
|
+ if (argc) {
|
|
|
|
+ argc = parse_options(argc, argv,
|
|
|
|
+ info_options, info_usage, 0);
|
|
|
|
+ if (argc)
|
|
|
|
+ usage_with_options(info_usage, info_options);
|
|
|
|
+ }
|
|
/* recycling report_lock_ops */
|
|
/* recycling report_lock_ops */
|
|
trace_handler = &report_lock_ops;
|
|
trace_handler = &report_lock_ops;
|
|
setup_pager();
|
|
setup_pager();
|
|
read_events();
|
|
read_events();
|
|
- dump_map();
|
|
|
|
|
|
+ dump_info();
|
|
} else {
|
|
} else {
|
|
usage_with_options(lock_usage, lock_options);
|
|
usage_with_options(lock_usage, lock_options);
|
|
}
|
|
}
|