perf lock contention: Show detail failure reason for BPF
authorNamhyung Kim <namhyung@kernel.org>
Mon, 27 Mar 2023 22:57:11 +0000 (15:57 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 4 Apr 2023 12:39:56 +0000 (09:39 -0300)
It can fail to collect lock stat from BPF for various reasons.  For
example, I've got a report that sometimes time calculation seems wrong
in case of contended spinlocks.  I suspect the time delta went negative
for some reason.

Count them separately and show in the output like below:

$ sudo perf lock contention -abE5 sleep 10
 contended   total wait     max wait     avg wait         type   caller

        13    785.61 us     79.36 us     60.43 us     spinlock   remove_wait_queue+0x14
        10    469.02 us     87.51 us     46.90 us     spinlock   prepare_to_wait+0x27
         9    289.09 us     69.08 us     32.12 us     spinlock   finish_wait+0x36
       114    251.05 us      8.56 us      2.20 us     spinlock   try_to_wake_up+0x1f5
       132    188.63 us      5.01 us      1.43 us     spinlock   __wake_up_common_lock+0x62

=== output for debug ===

bad: 1, total: 279
bad rate: 0.36 %
histogram of failure reasons
       task: 1
      stack: 0
       time: 0

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Hao Luo <haoluo@google.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: bpf@vger.kernel.org
Link: https://lore.kernel.org/r/20230327225711.245738-1-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-lock.c
tools/perf/util/bpf_lock_contention.c
tools/perf/util/bpf_skel/lock_contention.bpf.c
tools/perf/util/lock-contention.h

index 82dd2dfe56921b8e93967b508f441c64ac2a2ed9..32ec58fb80e46ba0cc1c4e7fb464241247eac600 100644 (file)
@@ -1619,6 +1619,24 @@ static void sort_contention_result(void)
        sort_result();
 }
 
+static void print_bpf_events(int total, struct lock_contention_fails *fails)
+{
+       /* Output for debug, this have to be removed */
+       int broken = fails->task + fails->stack + fails->time;
+
+       if (quiet || total == 0 || (broken == 0 && verbose <= 0))
+               return;
+
+       total += broken;
+       pr_info("\n=== output for debug ===\n\n");
+       pr_info("bad: %d, total: %d\n", broken, total);
+       pr_info("bad rate: %.2f %%\n", (double)broken / (double)total * 100);
+
+       pr_info("histogram of failure reasons\n");
+       pr_info(" %10s: %d\n", "task", fails->task);
+       pr_info(" %10s: %d\n", "stack", fails->stack);
+       pr_info(" %10s: %d\n", "time", fails->time);
+}
 static void print_contention_result(struct lock_contention *con)
 {
        struct lock_stat *st;
@@ -1646,8 +1664,6 @@ static void print_contention_result(struct lock_contention *con)
        }
 
        bad = total = printed = 0;
-       if (use_bpf)
-               bad = bad_hist[BROKEN_CONTENDED];
 
        while ((st = pop_from_result())) {
                struct thread *t;
@@ -1704,7 +1720,10 @@ static void print_contention_result(struct lock_contention *con)
                        break;
        }
 
-       print_bad_events(bad, total);
+       if (use_bpf)
+               print_bpf_events(total, &con->fails);
+       else
+               print_bad_events(bad, total);
 }
 
 static bool force;
@@ -1931,9 +1950,6 @@ static int __cmd_contention(int argc, const char **argv)
 
                lock_contention_stop();
                lock_contention_read(&con);
-
-               /* abuse bad hist stats for lost entries */
-               bad_hist[BROKEN_CONTENDED] = con.lost;
        } else {
                err = perf_session__process_events(session);
                if (err)
index 5927bf0bd92bccc81af211bc2faf0cd6b5c78618..b3e8aa9cd011a330e6deada68451a8c3029fbedb 100644 (file)
@@ -259,7 +259,9 @@ int lock_contention_read(struct lock_contention *con)
        fd = bpf_map__fd(skel->maps.lock_stat);
        stack = bpf_map__fd(skel->maps.stacks);
 
-       con->lost = skel->bss->lost;
+       con->fails.task = skel->bss->task_fail;
+       con->fails.stack = skel->bss->stack_fail;
+       con->fails.time = skel->bss->time_fail;
 
        stack_trace = zalloc(stack_size);
        if (stack_trace == NULL)
index ed9160999c3294b7ba1aa05975ea14af12aff374..3f4ee3992e8110dcdf4cb1f6090956daf202ef28 100644 (file)
@@ -124,7 +124,9 @@ int lock_owner;
 int aggr_mode;
 
 /* error stat */
-int lost;
+int task_fail;
+int stack_fail;
+int time_fail;
 
 static inline int can_record(u64 *ctx)
 {
@@ -283,7 +285,7 @@ int contention_begin(u64 *ctx)
                bpf_map_update_elem(&tstamp, &pid, &zero, BPF_ANY);
                pelem = bpf_map_lookup_elem(&tstamp, &pid);
                if (pelem == NULL) {
-                       lost++;
+                       __sync_fetch_and_add(&task_fail, 1);
                        return 0;
                }
        }
@@ -296,7 +298,7 @@ int contention_begin(u64 *ctx)
                pelem->stack_id = bpf_get_stackid(ctx, &stacks,
                                                  BPF_F_FAST_STACK_CMP | stack_skip);
                if (pelem->stack_id < 0)
-                       lost++;
+                       __sync_fetch_and_add(&stack_fail, 1);
        } else if (aggr_mode == LOCK_AGGR_TASK) {
                struct task_struct *task;
 
@@ -340,6 +342,11 @@ int contention_end(u64 *ctx)
                return 0;
 
        duration = bpf_ktime_get_ns() - pelem->timestamp;
+       if ((__s64)duration < 0) {
+               bpf_map_delete_elem(&tstamp, &pid);
+               __sync_fetch_and_add(&time_fail, 1);
+               return 0;
+       }
 
        switch (aggr_mode) {
        case LOCK_AGGR_CALLER:
index 040b618b22156324d3256fa144e2e2c17565879e..10c28302420cecdbfcea4301fbfd4da3bebc58de 100644 (file)
@@ -122,14 +122,20 @@ struct evlist;
 struct machine;
 struct target;
 
+struct lock_contention_fails {
+       int task;
+       int stack;
+       int time;
+};
+
 struct lock_contention {
        struct evlist *evlist;
        struct target *target;
        struct machine *machine;
        struct hlist_head *result;
        struct lock_filter *filters;
+       struct lock_contention_fails fails;
        unsigned long map_nr_entries;
-       int lost;
        int max_stack;
        int stack_skip;
        int aggr_mode;