All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces
@ 2024-12-19  6:06 John Stultz
  2024-12-19  6:06 ` [PATCH v3 2/2] lib: stackdepot: Avoid null pointer if stack_depot_save is called too early John Stultz
  2024-12-19 14:22 ` [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces Steven Rostedt
  0 siblings, 2 replies; 4+ messages in thread
From: John Stultz @ 2024-12-19  6:06 UTC (permalink / raw)
  To: LKML
  Cc: John Stultz, Steven Rostedt, Alexander Potapenko,
	Andrey Konovalov, Andrew Morton, kernel-team

When debugging, its often useful to understand how a function is
called and the different paths taken to get there. Usually
dump_stack() can be used for this purpose. However there are a
number of cases where a function is called very frequently,
making dump_stack far too noisy to be useful.

This is a little debug tool that utilizes stackdepot to capture
unique stack traces and store them in a circular buffer.

In the code, the developer adds: stacklog_debug_save() calls
at points of interest (as they might with stack_dump()).

Then after running the kernel, the developer can dump the unique
stack traces from the buffer via:
   cat /sys/kernel/debug/stacklog_debug

This is pretty trivial, but I've had this hanging around for
awhile and recently hit another case where it was helpful, so I
figured it would be worth sending it out for feedback as to if
others thought it would be useful enough to merge upstream or to
possibly rework into stackdepot itself?

Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: kernel-team@android.com
Signed-off-by: John Stultz <jstultz@google.com>
---
v2:
* Add dependency on STACKTRACE_SUPPORT as reported by
  kernel test robot <lkp@intel.com>
* Expand the Kconfig help to include basic usage
v3:
* Make globals static as Reported-by: kernel test robot <lkp@intel.com>
---
 include/linux/stacklog_debug.h |  13 ++++
 lib/Kconfig                    |  16 +++++
 lib/Makefile                   |   1 +
 lib/stacklog_debug.c           | 110 +++++++++++++++++++++++++++++++++
 4 files changed, 140 insertions(+)
 create mode 100644 include/linux/stacklog_debug.h
 create mode 100644 lib/stacklog_debug.c

diff --git a/include/linux/stacklog_debug.h b/include/linux/stacklog_debug.h
new file mode 100644
index 0000000000000..d88f05d7000a5
--- /dev/null
+++ b/include/linux/stacklog_debug.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_STACKLOG_DEBUG_H
+#define _LINUX_STACKLOG_DEBUG_H
+
+#ifdef CONFIG_STACKLOG_DEBUG
+void stacklog_debug_save(void);
+#else
+static inline void stacklog_debug_save(void)
+{
+}
+#endif
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 5a318f753b2f4..d0320329260b2 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -737,6 +737,22 @@ config REF_TRACKER
 	depends on STACKTRACE_SUPPORT
 	select STACKDEPOT
 
+config STACKLOG_DEBUG
+	bool "Debug tool for logging and later displaying stacktraces"
+	depends on STACKTRACE_SUPPORT
+	select STACKDEPOT
+	select STACKDEPOT_ALWAYS_INIT
+	help
+	  Enables debug infrastructure for logging unique stack traces at
+	  a specific point, which can be later displayed from userland.
+
+	  The developer adds: stacklog_debug_save() calls at points of
+	  interest (as they might with stack_dump()).
+
+	  Then after running the kernel, the developer can dump the unique
+	  stack traces from the buffer via:
+	    cat /sys/kernel/debug/stacklog_debug
+
 config SBITMAP
 	bool
 
diff --git a/lib/Makefile b/lib/Makefile
index a8155c972f028..931347245334e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -268,6 +268,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o
 
 obj-$(CONFIG_POLYNOMIAL) += polynomial.o
 
+obj-$(CONFIG_STACKLOG_DEBUG) += stacklog_debug.o
 # stackdepot.c should not be instrumented or call instrumented functions.
 # Prevent the compiler from calling builtins like memcmp() or bcmp() from this
 # file.
diff --git a/lib/stacklog_debug.c b/lib/stacklog_debug.c
new file mode 100644
index 0000000000000..70d2e203b36c6
--- /dev/null
+++ b/lib/stacklog_debug.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/stackdepot.h>
+#include <linux/debugfs.h>
+#include <linux/stacklog_debug.h>
+
+#define STACKDEPTH 32
+#define BUFSZ 4096
+
+#define LIST_ENTRIES 512
+static DEFINE_SPINLOCK(stack_lock);
+static depot_stack_handle_t stack_list[LIST_ENTRIES];
+static int head, tail;
+
+void stacklog_debug_save(void)
+{
+	unsigned long entries[STACKDEPTH];
+	depot_stack_handle_t stack_hash;
+	unsigned long flags;
+	unsigned int n;
+	int i;
+
+	n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
+	stack_hash = stack_depot_save(entries, n, GFP_NOWAIT);
+	if (!stack_hash)
+		return;
+
+	spin_lock_irqsave(&stack_lock, flags);
+	for (i = head; i < tail; i++)
+		if (stack_list[i % LIST_ENTRIES] == stack_hash)
+			goto out;
+
+	stack_list[(tail++ % LIST_ENTRIES)] = stack_hash;
+
+	if (tail % LIST_ENTRIES == head % LIST_ENTRIES)
+		head++;
+
+	if (tail >= 2 * LIST_ENTRIES) {
+		head %= LIST_ENTRIES;
+		tail %= LIST_ENTRIES;
+		if (tail < head)
+			tail += LIST_ENTRIES;
+	}
+out:
+	spin_unlock_irqrestore(&stack_lock, flags);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int stacklog_stats_show(struct seq_file *s, void *unused)
+{
+	char *buf = kmalloc(BUFSZ, GFP_NOWAIT);
+	unsigned int nr_entries;
+	unsigned long flags;
+	int i, start, stop;
+
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&stack_lock, flags);
+	start = head;
+	stop = tail;
+	spin_unlock_irqrestore(&stack_lock, flags);
+
+	if (start == stop)
+		goto out;
+
+	for (i = start; i < stop; i++) {
+		unsigned long *ent;
+		u32 hash;
+
+		/*
+		 * We avoid holdings the lock over the entire loop
+		 * just to be careful as we don't want to trip a
+		 * call path that calls back into stacklog_debug_save
+		 * which would deadlock, so hold the lock minimally
+		 * (and be ok with the data changing between loop
+		 * iterations).
+		 */
+		spin_lock_irqsave(&stack_lock, flags);
+		hash = stack_list[i % LIST_ENTRIES];
+		spin_unlock_irqrestore(&stack_lock, flags);
+
+		nr_entries = stack_depot_fetch(hash, &ent);
+		stack_trace_snprint(buf, BUFSZ, ent, nr_entries, 0);
+		seq_printf(s, "[idx: %i hash: %ld]====================\n%s\n\n",
+			   i - start, (long)hash, buf);
+	}
+out:
+	kfree(buf);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(stacklog_stats);
+
+static int __init stacklog_debug_init(void)
+{
+	debugfs_create_file("stacklog_debug", 0400, NULL, NULL,
+			    &stacklog_stats_fops);
+	return 0;
+}
+
+late_initcall(stacklog_debug_init);
+#endif
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3 2/2] lib: stackdepot: Avoid null pointer if stack_depot_save is called too early
  2024-12-19  6:06 [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces John Stultz
@ 2024-12-19  6:06 ` John Stultz
  2024-12-19 14:22 ` [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces Steven Rostedt
  1 sibling, 0 replies; 4+ messages in thread
From: John Stultz @ 2024-12-19  6:06 UTC (permalink / raw)
  To: LKML
  Cc: John Stultz, Steven Rostedt, Alexander Potapenko,
	Andrey Konovalov, Andrew Morton, kernel-team

If stack_depot_save is called before stackdepot has initialized
a null reference to the stack_table might be used, crashing the
system.

Instead, check stack_table has been initialized before going
further.

Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: kernel-team@android.com
Signed-off-by: John Stultz <jstultz@google.com>
---
 lib/stackdepot.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index 245d5b4166999..1fe627584535d 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -611,6 +611,9 @@ depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
 	if (unlikely(nr_entries == 0) || stack_depot_disabled)
 		return 0;
 
+	if (!stack_table)
+		return 0;
+
 	hash = hash_stack(entries, nr_entries);
 	bucket = &stack_table[hash & stack_hash_mask];
 
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces
  2024-12-19  6:06 [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces John Stultz
  2024-12-19  6:06 ` [PATCH v3 2/2] lib: stackdepot: Avoid null pointer if stack_depot_save is called too early John Stultz
@ 2024-12-19 14:22 ` Steven Rostedt
  2025-01-02 22:18   ` John Stultz
  1 sibling, 1 reply; 4+ messages in thread
From: Steven Rostedt @ 2024-12-19 14:22 UTC (permalink / raw)
  To: John Stultz
  Cc: LKML, Alexander Potapenko, Andrey Konovalov, Andrew Morton,
	kernel-team

On Wed, 18 Dec 2024 22:06:05 -0800
John Stultz <jstultz@google.com> wrote:

> When debugging, its often useful to understand how a function is
> called and the different paths taken to get there. Usually
> dump_stack() can be used for this purpose. However there are a
> number of cases where a function is called very frequently,
> making dump_stack far too noisy to be useful.
> 
> This is a little debug tool that utilizes stackdepot to capture
> unique stack traces and store them in a circular buffer.
> 
> In the code, the developer adds: stacklog_debug_save() calls
> at points of interest (as they might with stack_dump()).
> 
> Then after running the kernel, the developer can dump the unique
> stack traces from the buffer via:
>    cat /sys/kernel/debug/stacklog_debug
> 
> This is pretty trivial, but I've had this hanging around for
> awhile and recently hit another case where it was helpful, so I
> figured it would be worth sending it out for feedback as to if
> others thought it would be useful enough to merge upstream or to
> possibly rework into stackdepot itself?

Have you looked at using ftrace?

You can even do histograms of stack traces from tracepoints (including
kprobe events).

Let's say we want to see all the calls to kmalloc:

 # echo 'hist:keys=common_stacktrace' > /sys/kernel/tracing/events/kmem/kmalloc/trigger
 # cat /sys/kernel/tracing/events/kmem/kmalloc/hist
# event histogram
#
# trigger info: hist:keys=common_stacktrace:vals=hitcount:sort=hitcount:size=2048 [active]
#

{ common_stacktrace:
         kmalloc_reserve+0xb3/0x100
         __alloc_skb+0xb1/0x160
         alloc_skb_with_frags+0x58/0x1d0
         sock_alloc_send_pskb+0x1db/0x250
         __ip_append_data+0x50d/0xf20
         ip_make_skb+0x142/0x200
         udp_sendmsg+0xaa5/0xc20
         __sock_sendmsg+0x6c/0xb0
         __sys_sendto+0x282/0x2f0
         __x64_sys_sendto+0x26/0x30
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:          1
{ common_stacktrace:
         __kvmalloc_node_noprof+0x45/0xd0
         file_tty_write+0xfb/0x340
         vfs_write+0x2fc/0x350
         ksys_write+0x8c/0x100
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:          1
{ common_stacktrace:
         __kmalloc_node_noprof+0x255/0x370
         __kvmalloc_node_noprof+0x45/0xd0
         seq_read_iter+0x21b/0x440
         seq_read+0x13e/0x170
         vfs_read+0xc2/0x310
         ksys_read+0x8c/0x100
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:          1
{ common_stacktrace:
         virtqueue_add+0x215/0xde0
         virtqueue_add_outbuf+0x3a/0x60
         start_xmit+0x45d/0x7f0
         dev_hard_start_xmit+0xcd/0x260
         sch_direct_xmit+0xdf/0x260
         __dev_queue_xmit+0xc91/0x1100
         ip_finish_output2+0x481/0x4f0
         __ip_queue_xmit+0x664/0x6f0
         __tcp_transmit_skb+0x899/0xa70
         tcp_write_xmit+0x885/0x11a0
         tcp_tsq_handler+0x97/0xc0
         tcp_tasklet_func+0xc9/0x130
         tasklet_action_common+0x136/0x410
         handle_softirqs+0x11e/0x3b0
         __irq_exit_rcu+0x69/0x110
         irq_exit_rcu+0xe/0x20
} hitcount:          1
{ common_stacktrace:
[..]
{ common_stacktrace:
         alloc_pipe_info+0x6a/0x220
         create_pipe_files+0x4a/0x200
         __do_pipe_flags+0x31/0xd0
         do_pipe2+0x4f/0xe0
         __x64_sys_pipe2+0x18/0x20
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:          4
{ common_stacktrace:
         __vmalloc_node_range_noprof+0x2a4/0x710
         vmalloc_noprof+0x45/0x50
         tracing_map_sort_entries+0x2b/0x3f0
         hist_show+0x18d/0x810
         seq_read_iter+0x1a6/0x440
         seq_read+0x13e/0x170
         vfs_read+0xc2/0x310
         ksys_read+0x8c/0x100
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:          4
{ common_stacktrace:
         tracing_map_sort_entries+0xa5/0x3f0
         hist_show+0x18d/0x810
         seq_read_iter+0x1a6/0x440
         seq_read+0x13e/0x170
         vfs_read+0xc2/0x310
         ksys_read+0x8c/0x100
         do_syscall_64+0x8f/0x110
         entry_SYSCALL_64_after_hwframe+0x71/0x79
} hitcount:         83
{ common_stacktrace:
         virtqueue_add+0x215/0xde0
         virtqueue_add_outbuf+0x3a/0x60
         start_xmit+0x45d/0x7f0
         dev_hard_start_xmit+0xcd/0x260
         sch_direct_xmit+0xdf/0x260
         __dev_queue_xmit+0xc91/0x1100
         ip_finish_output2+0x481/0x4f0
         __ip_queue_xmit+0x664/0x6f0
         __tcp_transmit_skb+0x899/0xa70
         tcp_write_xmit+0x885/0x11a0
         __tcp_push_pending_frames+0x32/0xc0
         tcp_sendmsg_locked+0xdbe/0x1040
         tcp_sendmsg+0x2c/0x50
         __sock_sendmsg+0x6c/0xb0
         sock_write_iter+0x110/0x160
         vfs_write+0x2fc/0x350
} hitcount:        392

Totals:
    Hits: 524
    Entries: 22
    Dropped: 0


-- Steve

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces
  2024-12-19 14:22 ` [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces Steven Rostedt
@ 2025-01-02 22:18   ` John Stultz
  0 siblings, 0 replies; 4+ messages in thread
From: John Stultz @ 2025-01-02 22:18 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: LKML, Alexander Potapenko, Andrey Konovalov, Andrew Morton,
	kernel-team

On Thu, Dec 19, 2024 at 6:21 AM Steven Rostedt <rostedt@goodmis.org> wrote:
>
> On Wed, 18 Dec 2024 22:06:05 -0800
> John Stultz <jstultz@google.com> wrote:
>
> > When debugging, its often useful to understand how a function is
> > called and the different paths taken to get there. Usually
> > dump_stack() can be used for this purpose. However there are a
> > number of cases where a function is called very frequently,
> > making dump_stack far too noisy to be useful.
> >
> > This is a little debug tool that utilizes stackdepot to capture
> > unique stack traces and store them in a circular buffer.
> >
> > In the code, the developer adds: stacklog_debug_save() calls
> > at points of interest (as they might with stack_dump()).
> >
> > Then after running the kernel, the developer can dump the unique
> > stack traces from the buffer via:
> >    cat /sys/kernel/debug/stacklog_debug
> >
> > This is pretty trivial, but I've had this hanging around for
> > awhile and recently hit another case where it was helpful, so I
> > figured it would be worth sending it out for feedback as to if
> > others thought it would be useful enough to merge upstream or to
> > possibly rework into stackdepot itself?
>
> Have you looked at using ftrace?
>
> You can even do histograms of stack traces from tracepoints (including
> kprobe events).
>
> Let's say we want to see all the calls to kmalloc:
>
>  # echo 'hist:keys=common_stacktrace' > /sys/kernel/tracing/events/kmem/kmalloc/trigger
>  # cat /sys/kernel/tracing/events/kmem/kmalloc/hist

Cool!  I guess it does depend on the trace-event being there (or added
via kprobe), but it indeed does overlap with my tool, so I'll probably
abandon efforts to upstream mine.

That said, for not-so-bright printk debugging focused developers like
myself, adding tracepoints (explicitly or via kprobes) to the points
we are interested in (along with remembering the advanced ftrace
commands) feels a like a bit more work than adding a dump_stack-like
one-liner, but maybe that's a better thing to try to improve on the
ftrace side instead of adding my new tool.  I'll think about that a
bit.

thanks
-john

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-01-02 22:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-19  6:06 [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces John Stultz
2024-12-19  6:06 ` [PATCH v3 2/2] lib: stackdepot: Avoid null pointer if stack_depot_save is called too early John Stultz
2024-12-19 14:22 ` [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for collecting and displaying stacktraces Steven Rostedt
2025-01-02 22:18   ` John Stultz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.