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.nar...@samsung.com>
Signed-off-by: Maninder Singh <maninder...@samsung.com>
---
 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

Reply via email to