Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ec220e2 authored by Ken Chen's avatar Ken Chen Committed by Alexey Dobriyan
Browse files

proc: add /proc/*/stack



/proc/*/stack adds the ability to query a task's stack trace. It is more
useful than /proc/*/wchan as it provides full stack trace instead of single
depth. Example output:

	$ cat /proc/self/stack
	[<c010a271>] save_stack_trace_tsk+0x17/0x35
	[<c01827b4>] proc_pid_stack+0x4a/0x76
	[<c018312d>] proc_single_show+0x4a/0x5e
	[<c016bdec>] seq_read+0xf3/0x29f
	[<c015a004>] vfs_read+0x6d/0x91
	[<c015a0c1>] sys_read+0x3b/0x60
	[<c0102eda>] syscall_call+0x7/0xb
	[<ffffffff>] 0xffffffff

[add save_stack_trace_tsk() on mips, ACK Ralf --adobriyan]
Signed-off-by: default avatarKen Chen <kenchen@google.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAlexey Dobriyan <adobriyan@gmail.com>
parent 631f9c18
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ Table 1-1: Process specific entries in /proc
 statm		Process memory status information
 status		Process status in human readable form
 wchan		If CONFIG_KALLSYMS is set, a pre-decoded wchan
 stack		Report full stack trace, enable via CONFIG_STACKTRACE
 smaps		Extension based on maps, the rss size for each mapped file
..............................................................................

+18 −6
Original line number Diff line number Diff line
@@ -32,7 +32,8 @@ static void save_raw_context_stack(struct stack_trace *trace,
	}
}

static void save_context_stack(struct stack_trace *trace, struct pt_regs *regs)
static void save_context_stack(struct stack_trace *trace,
	struct task_struct *tsk, struct pt_regs *regs)
{
	unsigned long sp = regs->regs[29];
#ifdef CONFIG_KALLSYMS
@@ -41,7 +42,7 @@ static void save_context_stack(struct stack_trace *trace, struct pt_regs *regs)

	if (raw_show_trace || !__kernel_text_address(pc)) {
		unsigned long stack_page =
			(unsigned long)task_stack_page(current);
			(unsigned long)task_stack_page(tsk);
		if (stack_page && sp >= stack_page &&
		    sp <= stack_page + THREAD_SIZE - 32)
			save_raw_context_stack(trace, sp);
@@ -54,7 +55,7 @@ static void save_context_stack(struct stack_trace *trace, struct pt_regs *regs)
			trace->entries[trace->nr_entries++] = pc;
		if (trace->nr_entries >= trace->max_entries)
			break;
		pc = unwind_stack(current, &sp, pc, &ra);
		pc = unwind_stack(tsk, &sp, pc, &ra);
	} while (pc);
#else
	save_raw_context_stack(trace, sp);
@@ -65,13 +66,24 @@ static void save_context_stack(struct stack_trace *trace, struct pt_regs *regs)
 * Save stack-backtrace addresses into a stack_trace buffer.
 */
void save_stack_trace(struct stack_trace *trace)
{
	save_stack_trace_tsk(current, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);

void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
	struct pt_regs dummyregs;
	struct pt_regs *regs = &dummyregs;

	WARN_ON(trace->nr_entries || !trace->max_entries);

	if (tsk != current) {
		regs->regs[29] = tsk->thread.reg29;
		regs->regs[31] = 0;
		regs->cp0_epc = tsk->thread.reg31;
	} else
		prepare_frametrace(regs);
	save_context_stack(trace, regs);
	save_context_stack(trace, tsk, regs);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
+38 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@
#include <linux/mm.h>
#include <linux/rcupdate.h>
#include <linux/kallsyms.h>
#include <linux/stacktrace.h>
#include <linux/resource.h>
#include <linux/module.h>
#include <linux/mount.h>
@@ -337,6 +338,37 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer)
}
#endif /* CONFIG_KALLSYMS */

#ifdef CONFIG_STACKTRACE

#define MAX_STACK_TRACE_DEPTH	64

static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
			  struct pid *pid, struct task_struct *task)
{
	struct stack_trace trace;
	unsigned long *entries;
	int i;

	entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL);
	if (!entries)
		return -ENOMEM;

	trace.nr_entries	= 0;
	trace.max_entries	= MAX_STACK_TRACE_DEPTH;
	trace.entries		= entries;
	trace.skip		= 0;
	save_stack_trace_tsk(task, &trace);

	for (i = 0; i < trace.nr_entries; i++) {
		seq_printf(m, "[<%p>] %pS\n",
			   (void *)entries[i], (void *)entries[i]);
	}
	kfree(entries);

	return 0;
}
#endif

#ifdef CONFIG_SCHEDSTATS
/*
 * Provides /proc/PID/schedstat
@@ -2500,6 +2532,9 @@ static const struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_KALLSYMS
	INF("wchan",      S_IRUGO, proc_pid_wchan),
#endif
#ifdef CONFIG_STACKTRACE
	ONE("stack",      S_IRUSR, proc_pid_stack),
#endif
#ifdef CONFIG_SCHEDSTATS
	INF("schedstat",  S_IRUGO, proc_pid_schedstat),
#endif
@@ -2835,6 +2870,9 @@ static const struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_KALLSYMS
	INF("wchan",     S_IRUGO, proc_pid_wchan),
#endif
#ifdef CONFIG_STACKTRACE
	ONE("stack",      S_IRUSR, proc_pid_stack),
#endif
#ifdef CONFIG_SCHEDSTATS
	INF("schedstat", S_IRUGO, proc_pid_schedstat),
#endif