lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241219060643.2360926-1-jstultz@google.com>
Date: Wed, 18 Dec 2024 22:06:05 -0800
From: John Stultz <jstultz@...gle.com>
To: LKML <linux-kernel@...r.kernel.org>
Cc: John Stultz <jstultz@...gle.com>, Steven Rostedt <rostedt@...dmis.org>, 
	Alexander Potapenko <glider@...gle.com>, Andrey Konovalov <andreyknvl@...il.com>, 
	Andrew Morton <akpm@...ux-foundation.org>, kernel-team@...roid.com
Subject: [PATCH v3 1/2] lib: stacklog_debug: Introduce helper tool for
 collecting and displaying stacktraces

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@...dmis.org>
Cc: Alexander Potapenko <glider@...gle.com>
Cc: Andrey Konovalov <andreyknvl@...il.com>
Cc: Andrew Morton <akpm@...ux-foundation.org>
Cc: kernel-team@...roid.com
Signed-off-by: John Stultz <jstultz@...gle.com>
---
v2:
* Add dependency on STACKTRACE_SUPPORT as reported by
  kernel test robot <lkp@...el.com>
* Expand the Kconfig help to include basic usage
v3:
* Make globals static as Reported-by: kernel test robot <lkp@...el.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


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ