[PATCH 1/1] stackdepot: interface to check entries and size of stackdepot.

From: Maninder Singh
Date: Wed Nov 22 2017 - 05:51:52 EST


This patch provides interface to check all the stack enteries
saved in stackdepot so far as well as memory consumed by stackdepot.

1) Take current depot_index and offset to calculate end address for one
iteration of (/sys/kernel/debug/depot_stack/depot_entries).

2) Fill end marker in every slab to point its end, and then use it while
traversing all the slabs of stackdepot.

"debugfs code inspired from page_onwer's way of printing BT"

checked on ARM and x86_64.
$cat /sys/kernel/debug/depot_stack/depot_size
Memory consumed by Stackdepot:208 KB

$ cat /sys/kernel/debug/depot_stack/depot_entries
stack count 1 backtrace
init_page_owner+0x1e/0x210
start_kernel+0x310/0x3cd
secondary_startup_64+0xa5/0xb0
0xffffffffffffffff

Signed-off-by: Vaneet Narang <v.narang@xxxxxxxxxxx>
Signed-off-by: Maninder Singh <maninder1.s@xxxxxxxxxxx>
---
include/linux/stackdepot.h | 13 +++
include/linux/stacktrace.h | 6 ++
lib/stackdepot.c | 183 ++++++++++++++++++++++++++++++++++++++++++++
mm/page_owner.c | 6 --
4 files changed, 202 insertions(+), 6 deletions(-)

diff --git a/include/linux/stackdepot.h b/include/linux/stackdepot.h
index 7978b3e..dd95b11 100644
--- a/include/linux/stackdepot.h
+++ b/include/linux/stackdepot.h
@@ -23,6 +23,19 @@

typedef u32 depot_stack_handle_t;

+/*
+ * structure to store markers which
+ * will be used while printing entries
+ * stored in stackdepot.
+ */
+struct depot_stack_data {
+ int print_offset;
+ int print_counter;
+ int print_index;
+ unsigned long end_marker;
+ void *end_address;
+};
+
struct stack_trace;

depot_stack_handle_t depot_save_stack(struct stack_trace *trace, gfp_t flags);
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index ba29a06..1cfd27d 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -4,6 +4,12 @@

#include <linux/types.h>

+/*
+ * TODO: teach PAGE_OWNER_STACK_DEPTH (__dump_page_owner and save_stack)
+ * to use off stack temporal storage
+ */
+#define PAGE_OWNER_STACK_DEPTH (16)
+
struct task_struct;
struct pt_regs;

diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index f87d138..3067fcb 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -39,6 +39,8 @@
#include <linux/stackdepot.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>

#define DEPOT_STACK_BITS (sizeof(depot_stack_handle_t) * 8)

@@ -111,6 +113,7 @@ static bool init_stack_slab(void **prealloc)
int required_size = offsetof(struct stack_record, entries) +
sizeof(unsigned long) * size;
struct stack_record *stack;
+ void *address;

required_size = ALIGN(required_size, 1 << STACK_ALLOC_ALIGN);

@@ -119,6 +122,17 @@ static bool init_stack_slab(void **prealloc)
WARN_ONCE(1, "Stack depot reached limit capacity");
return NULL;
}
+
+ /*
+ * write POSION_END if any space left in
+ * current slab to represent its end.
+ * later used while printing all the stacks.
+ */
+ if (depot_offset < STACK_ALLOC_SIZE) {
+ address = stack_slabs[depot_index] + depot_offset;
+ memset(address, POISON_END, sizeof(unsigned long));
+ }
+
depot_index++;
depot_offset = 0;
/*
@@ -285,3 +299,172 @@ depot_stack_handle_t depot_save_stack(struct stack_trace *trace,
return retval;
}
EXPORT_SYMBOL_GPL(depot_save_stack);
+
+#define DEPOT_SIZE 64
+
+static ssize_t read_depot_stack_size(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ char kbuf[DEPOT_SIZE];
+ ssize_t ret = 0;
+ unsigned long size = depot_index * (1 << STACK_ALLOC_ORDER) * PAGE_SIZE;
+
+ ret = snprintf(kbuf, count, "Memory consumed by Stackdepot:%lu KB\n", size >> 10);
+ if (ret >= count)
+ return -ENOMEM;
+
+ return simple_read_from_buffer(buf, count, ppos, kbuf, ret);
+}
+
+static ssize_t print_depot_stack(char __user *buf, size_t count, struct stack_trace *trace, loff_t *ppos)
+{
+ char *kbuf;
+ int ret = 0;
+
+ kbuf = kvmalloc(count, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ ret = snprintf(kbuf, count, "stack count %d backtrace\n", (int)*ppos);
+ ret += snprint_stack_trace(kbuf + ret, count - ret, trace, 0);
+ ret += snprintf(kbuf + ret, count - ret, "\n");
+
+ if (ret >= count) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (copy_to_user(buf, kbuf, ret))
+ ret = -EFAULT;
+
+err:
+ kvfree(kbuf);
+ return ret;
+}
+
+/*
+ * read_depot_stack()
+ *
+ * function to print all the entries present
+ * in depot_stack database currently in system.
+ */
+static ssize_t read_depot_stack(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct stack_record *stack;
+ void *address;
+ struct depot_stack_data *debugfs_data;
+
+ debugfs_data = (struct depot_stack_data *)file->private_data;
+
+ if (!debugfs_data)
+ return -EINVAL;
+
+ while (debugfs_data->print_counter <= debugfs_data->print_index) {
+ unsigned long entries[PAGE_OWNER_STACK_DEPTH];
+ struct stack_trace trace = {
+ .nr_entries = 0,
+ .entries = entries,
+ .max_entries = PAGE_OWNER_STACK_DEPTH,
+ .skip = 0
+ };
+
+ address = stack_slabs[debugfs_data->print_counter] + debugfs_data->print_offset;
+ if (address == debugfs_data->end_address)
+ break;
+
+ if (*((unsigned long *)address) == debugfs_data->end_marker) {
+ debugfs_data->print_counter++;
+ debugfs_data->print_offset = 0;
+ continue;
+ }
+
+ stack = address;
+ trace.nr_entries = trace.max_entries = stack->size;
+ trace.entries = stack->entries;
+
+ debugfs_data->print_offset += offsetof(struct stack_record, entries) +
+ (stack->size * sizeof(unsigned long));
+ debugfs_data->print_offset = ALIGN(debugfs_data->print_offset, 1 << STACK_ALLOC_ALIGN);
+ if (debugfs_data->print_offset >= STACK_ALLOC_SIZE) {
+ debugfs_data->print_counter++;
+ debugfs_data->print_offset = 0;
+ }
+
+ *ppos = *ppos + 1; /* one stack found, print it */
+ return print_depot_stack(buf, count, &trace, ppos);
+ }
+
+ return 0;
+}
+
+int read_depot_open(struct inode *inode, struct file *file)
+{
+ struct depot_stack_data *debugfs_data;
+ unsigned long flags;
+
+ debugfs_data = kzalloc(sizeof(struct depot_stack_data), GFP_KERNEL);
+ if (!debugfs_data)
+ return -ENOMEM;
+ /*
+ * First time depot_stack/depot_entries is called.
+ * (/sys/kernel/debug/depot_stack/depot_entries)
+ * initialise print depot_index and stopping address.
+ */
+ memset(&(debugfs_data->end_marker), POISON_END, sizeof(unsigned long));
+
+ spin_lock_irqsave(&depot_lock, flags);
+ debugfs_data->print_index = depot_index;
+ debugfs_data->end_address = stack_slabs[depot_index] + depot_offset;
+ spin_unlock_irqrestore(&depot_lock, flags);
+
+ file->private_data = debugfs_data;
+ return 0;
+}
+
+int read_depot_release(struct inode *inode, struct file *file)
+{
+ void *debugfs_data = file->private_data;
+
+ kfree(debugfs_data);
+ return 0;
+}
+
+static const struct file_operations proc_depot_stack_operations = {
+ .open = read_depot_open,
+ .read = read_depot_stack,
+ .release = read_depot_release,
+};
+
+static const struct file_operations proc_depot_stack_size_operations = {
+ .read = read_depot_stack_size,
+};
+
+static int __init depot_stack_init(void)
+{
+ struct dentry *dentry, *dentry_root;
+
+ dentry_root = debugfs_create_dir("depot_stack", NULL);
+
+ if (!dentry_root) {
+ pr_warn("debugfs 'depot_stack' dir creation failed\n");
+ return -ENOMEM;
+ }
+
+ dentry = debugfs_create_file("depot_entries", 0400, dentry_root,
+ NULL, &proc_depot_stack_operations);
+
+ if (IS_ERR(dentry))
+ goto err;
+
+ dentry = debugfs_create_file("depot_size", 0400, dentry_root,
+ NULL, &proc_depot_stack_size_operations);
+
+ if (IS_ERR(dentry))
+ goto err;
+
+ return 0;
+
+err:
+ debugfs_remove_recursive(dentry_root);
+ return PTR_ERR(dentry);
+}
+late_initcall(depot_stack_init)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 4f44b95..341b326 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -13,12 +13,6 @@

#include "internal.h"

-/*
- * TODO: teach PAGE_OWNER_STACK_DEPTH (__dump_page_owner and save_stack)
- * to use off stack temporal storage
- */
-#define PAGE_OWNER_STACK_DEPTH (16)
-
struct page_owner {
unsigned int order;
gfp_t gfp_mask;
--
1.7.1