static int collect_syscall(struct task_struct *target, struct syscall_info *info) { struct pt_regs *regs; if (!try_get_task_stack(target)) { /* Task has no stack, so the task isn't in a syscall. */ memset(info, 0, sizeof(*info)); info->data.nr = -1; return 0; } regs = task_pt_regs(target); if (unlikely(!regs)) { put_task_stack(target); return -EAGAIN; } info->sp = user_stack_pointer(regs); info->data.instruction_pointer = instruction_pointer(regs); info->data.nr = syscall_get_nr(target, regs); if (info->data.nr != -1L) syscall_get_arguments(target, regs, (unsigned long *)&info->data.args[0]); put_task_stack(target); return 0; }
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { if (!try_get_task_stack(tsk)) return; __save_stack_trace(trace, tsk, NULL, true); put_task_stack(tsk); }
/* * Called from fs/proc with a reference on @p to find the function * which called into schedule(). This needs to be done carefully * because the task might wake up and we might look at a stack * changing under us. */ unsigned long get_wchan(struct task_struct *p) { unsigned long start, bottom, top, sp, fp, ip, ret = 0; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; if (!try_get_task_stack(p)) return 0; start = (unsigned long)task_stack_page(p); if (!start) goto out; /* * Layout of the stack page: * * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long) * PADDING * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING * stack * ----------- bottom = start * * The tasks stack pointer points at the location where the * framepointer is stored. The data on the stack is: * ... IP FP ... IP FP * * We need to read FP and IP, so we need to adjust the upper * bound by another unsigned long. */ top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; top -= 2 * sizeof(unsigned long); bottom = start; sp = READ_ONCE(p->thread.sp); if (sp < bottom || sp > top) goto out; fp = READ_ONCE_NOCHECK(((struct inactive_task_frame *)sp)->bp); do { if (fp < bottom || fp > top) goto out; ip = READ_ONCE_NOCHECK(*(unsigned long *)(fp + sizeof(unsigned long))); if (!in_sched_functions(ip)) { ret = ip; goto out; } fp = READ_ONCE_NOCHECK(*(unsigned long *)fp); } while (count++ < 16 && p->state != TASK_RUNNING); out: put_task_stack(p); return ret; }
/* * This function returns an error if it detects any unreliable features of the * stack. Otherwise it guarantees that the stack trace is reliable. * * If the task is not 'current', the caller *must* ensure the task is inactive. */ int save_stack_trace_tsk_reliable(struct task_struct *tsk, struct stack_trace *trace) { int ret; if (!try_get_task_stack(tsk)) return -EINVAL; ret = __save_stack_trace_reliable(trace, tsk); put_task_stack(tsk); return ret; }
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { struct stackframe frame; int skip; pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); if (!tsk) tsk = current; if (!try_get_task_stack(tsk)) return; if (tsk == current) { frame.fp = (unsigned long)__builtin_frame_address(0); frame.pc = (unsigned long)dump_backtrace; } else { /* * task blocked in __switch_to */ frame.fp = thread_saved_fp(tsk); frame.pc = thread_saved_pc(tsk); } #ifdef CONFIG_FUNCTION_GRAPH_TRACER frame.graph = 0; #endif skip = !!regs; printk("Call trace:\n"); do { /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(frame.pc); } else if (frame.fp == regs->regs[29]) { skip = 0; /* * Mostly, this is the case where this function is * called in panic/abort. As exception handler's * stack frame does not contain the corresponding pc * at which an exception has taken place, use regs->pc * instead. */ dump_backtrace_entry(regs->pc); } } while (!unwind_frame(tsk, &frame)); put_task_stack(tsk); }
/** * stack_trace_save_tsk_reliable - Save task stack with verification * @tsk: Pointer to the task to examine * @store: Pointer to storage array * @size: Size of the storage array * * Return: An error if it detects any unreliable features of the * stack. Otherwise it guarantees that the stack trace is * reliable and returns the number of entries stored. * * If the task is not 'current', the caller *must* ensure the task is inactive. */ int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store, unsigned int size) { stack_trace_consume_fn consume_entry = stack_trace_consume_entry; struct stacktrace_cookie c = { .store = store, .size = size, }; int ret; /* * If the task doesn't have a stack (e.g., a zombie), the stack is * "reliably" empty. */ if (!try_get_task_stack(tsk)) return 0; ret = arch_stack_walk_reliable(consume_entry, &c, tsk); put_task_stack(tsk); return ret; }
/** * stack_trace_save - Save a stack trace into a storage array * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace * * Return: Number of trace entries stored. */ unsigned int stack_trace_save(unsigned long *store, unsigned int size, unsigned int skipnr) { stack_trace_consume_fn consume_entry = stack_trace_consume_entry; struct stacktrace_cookie c = { .store = store, .size = size, .skip = skipnr + 1, }; arch_stack_walk(consume_entry, &c, current, NULL); return c.len; } EXPORT_SYMBOL_GPL(stack_trace_save); /** * stack_trace_save_tsk - Save a task stack trace into a storage array * @task: The task to examine * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace * * Return: Number of trace entries stored. */ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, unsigned int size, unsigned int skipnr) { stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; struct stacktrace_cookie c = { .store = store, .size = size, .skip = skipnr + 1, }; if (!try_get_task_stack(tsk)) return 0; arch_stack_walk(consume_entry, &c, tsk, NULL); put_task_stack(tsk); return c.len; } /** * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array * @regs: Pointer to pt_regs to examine * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace * * Return: Number of trace entries stored. */ unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, unsigned int size, unsigned int skipnr) { stack_trace_consume_fn consume_entry = stack_trace_consume_entry; struct stacktrace_cookie c = { .store = store, .size = size, .skip = skipnr, }; arch_stack_walk(consume_entry, &c, current, regs); return c.len; }
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { struct stackframe frame; unsigned long irq_stack_ptr; int skip; pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); if (!tsk) tsk = current; if (!try_get_task_stack(tsk)) return; /* * Switching between stacks is valid when tracing current and in * non-preemptible context. */ if (tsk == current && !preemptible()) irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id()); else irq_stack_ptr = 0; if (tsk == current) { frame.fp = (unsigned long)__builtin_frame_address(0); frame.sp = current_stack_pointer; frame.pc = (unsigned long)dump_backtrace; } else { /* * task blocked in __switch_to */ frame.fp = thread_saved_fp(tsk); frame.sp = thread_saved_sp(tsk); frame.pc = thread_saved_pc(tsk); } #ifdef CONFIG_FUNCTION_GRAPH_TRACER frame.graph = tsk->curr_ret_stack; #endif skip = !!regs; printk("Call trace:\n"); while (1) { unsigned long where = frame.pc; unsigned long stack; int ret; /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(where); } else if (frame.fp == regs->regs[29]) { skip = 0; /* * Mostly, this is the case where this function is * called in panic/abort. As exception handler's * stack frame does not contain the corresponding pc * at which an exception has taken place, use regs->pc * instead. */ dump_backtrace_entry(regs->pc); } ret = unwind_frame(tsk, &frame); if (ret < 0) break; stack = frame.sp; if (in_exception_text(where)) { /* * If we switched to the irq_stack before calling this * exception handler, then the pt_regs will be on the * task stack. The easiest way to tell is if the large * pt_regs would overlap with the end of the irq_stack. */ if (stack < irq_stack_ptr && (stack + sizeof(struct pt_regs)) > irq_stack_ptr) stack = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr); dump_mem("", "Exception stack", stack, stack + sizeof(struct pt_regs)); } } put_task_stack(tsk); }