unsigned long get_wchan(struct task_struct *p) { unsigned long bp, sp, ip; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)task_stack_page(p); sp = p->thread.sp; if (!stack_page || sp < stack_page || sp > top_esp+stack_page) return 0; /* L4Linux has a different layout in switch_to(), but * the only difference is that we push a return * address after ebp. So we simply adjust the esp to * reflect that. And we leave the different name for * esp to catch direct usage of thread data. */ sp += 4;/* add 4 to remove return address */ /* include/asm-i386/system.h:switch_to() pushes bp last. */ bp = *(unsigned long *) sp; do { if (bp < stack_page || bp > top_ebp+stack_page) return 0; ip = *(unsigned long *) (bp+4); if (!in_sched_functions(ip)) return ip; bp = *(unsigned long *) bp; } while (count++ < 16); return 0; }
static void save_context_stack(struct stack_trace *trace, struct task_struct *tsk, struct pt_regs *regs, int savesched) { unsigned long sp = regs->regs[29]; #ifdef CONFIG_KALLSYMS unsigned long ra = regs->regs[31]; unsigned long pc = regs->cp0_epc; if (raw_show_trace || !__kernel_text_address(pc)) { unsigned long stack_page = (unsigned long)task_stack_page(tsk); if (stack_page && sp >= stack_page && sp <= stack_page + THREAD_SIZE - 32) save_raw_context_stack(trace, sp, savesched); return; } do { if (savesched || !in_sched_functions(pc)) { if (trace->skip > 0) trace->skip--; else trace->entries[trace->nr_entries++] = pc; if (trace->nr_entries >= trace->max_entries) break; } pc = unwind_stack(tsk, &sp, pc, &ra); } while (pc); #else save_raw_context_stack(trace, sp, savesched); #endif }
/* * get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ unsigned long get_wchan(struct task_struct *task) { unsigned long pc = 0; #ifdef CONFIG_KALLSYMS unsigned long sp; unsigned long ra = 0; #endif if (!task || task == current || task->state == TASK_RUNNING) goto out; if (!task_stack_page(task)) goto out; pc = thread_saved_pc(task); #ifdef CONFIG_KALLSYMS sp = task->thread.reg29 + schedule_mfi.frame_size; while (in_sched_functions(pc)) pc = unwind_stack(task, &sp, pc, &ra); #endif out: return pc; }
static int kgdb_get_blocked_state(struct vcpu *p, struct cpu_user_regs *regs, struct unw_frame_info *unw) #endif { unsigned long ip; int count = 0; #ifndef XEN unw_init_from_blocked_task(unw, p); #endif ip = 0UL; do { if (unw_unwind(unw) < 0) return -1; unw_get_ip(unw, &ip); #ifndef XEN if (!in_sched_functions(ip)) break; #else dbg_printk("ip 0x%lx cr_iip 0x%lx\n", ip, regs->cr_iip); if (ip == regs->cr_iip) break; #endif } while (count++ < 16); if (!ip) return -1; else return 0; }
/* * Save stack-backtrace addresses into a stack_trace buffer. */ static void save_context_stack(struct stack_trace *trace, unsigned long sp, struct task_struct *tsk, int savesched) { for (;;) { unsigned long *stack = (unsigned long *) sp; unsigned long newsp, ip; if (!validate_sp(sp, tsk, STACK_FRAME_OVERHEAD)) return; newsp = stack[0]; ip = stack[STACK_FRAME_LR_SAVE]; if (savesched || !in_sched_functions(ip)) { if (!trace->skip) trace->entries[trace->nr_entries++] = ip; else trace->skip--; } if (trace->nr_entries >= trace->max_entries) return; sp = newsp; } }
static bool stack_trace_consume_entry_nosched(void *cookie, unsigned long addr, bool reliable) { if (in_sched_functions(addr)) return true; return stack_trace_consume_entry(cookie, addr, reliable); }
unsigned long get_wchan(struct task_struct *p) { #if 0 /* YURGH. TODO. */ unsigned long ebp, esp, eip; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)p; esp = p->thread.esp; if (!stack_page || esp < stack_page || esp > 8188+stack_page) return 0; /* include/asm-i386/system.h:switch_to() pushes ebp last. */ ebp = *(unsigned long *) esp; do { if (ebp < stack_page || ebp > 8184+stack_page) return 0; eip = *(unsigned long *) (ebp+4); if (!in_sched_functions(eip)) return eip; ebp = *(unsigned long *) ebp; } while (count++ < 16); #endif return 0; }
unsigned long get_wchan(struct task_struct *p) { #if 0 unsigned long ebp, esp, eip; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)p; esp = p->thread.esp; if (!stack_page || esp < stack_page || esp > 8188+stack_page) return 0; ebp = *(unsigned long *) esp; do { if (ebp < stack_page || ebp > 8184+stack_page) return 0; eip = *(unsigned long *) (ebp+4); if (!in_sched_functions(eip)) return eip; ebp = *(unsigned long *) ebp; } while (count++ < 16); #endif return 0; }
unsigned long get_wchan(struct task_struct *p) { unsigned long fp, lr; unsigned long stack_start, stack_end; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; if (IS_ENABLED(CONFIG_FRAME_POINTER)) { stack_start = (unsigned long)end_of_stack(p); stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE; fp = thread_saved_fp(p); do { if (fp < stack_start || fp > stack_end) return 0; lr = ((unsigned long *)fp)[0]; if (!in_sched_functions(lr)) return lr; fp = *(unsigned long *)(fp + 4); } while (count++ < 16); } return 0; }
/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ unsigned long get_wchan(struct task_struct *p) { unsigned long stack_page; unsigned long pc; #ifdef CONFIG_KALLSYMS unsigned long frame; #endif if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)task_stack_page(p); if (!stack_page || !mfinfo_num) return 0; pc = thread_saved_pc(p); #ifdef CONFIG_KALLSYMS if (!in_sched_functions(pc)) return pc; frame = p->thread.reg29 + schedule_frame->frame_size; do { int i; if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) return 0; for (i = mfinfo_num - 1; i >= 0; i--) { if (pc >= (unsigned long) mfinfo[i].func) break; } if (i < 0) break; if (mfinfo[i].pc_offset < 0) break; pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; if (!mfinfo[i].frame_size) break; frame += mfinfo[i].frame_size; } while (in_sched_functions(pc)); #endif return pc; }
static bool save_wchan(unsigned long pc, void *arg) { if (!in_sched_functions(pc)) { unsigned long *p = arg; *p = pc; return true; } return false; }
/* * Return saved PC from a blocked thread */ unsigned long thread_saved_pc(struct task_struct *tsk) { struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp; /* Check whether the thread is blocked in resume() */ if (in_sched_functions(sw->retpc)) return ((unsigned long *)sw->a6)[1]; else return sw->retpc; }
/* * 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; }
/* * Return saved PC of a blocked thread. */ unsigned long thread_saved_pc(struct task_struct *tsk) { struct cpu_context *ctx = &(((struct thread_info *)(tsk->stack))->cpu_context); /* Check whether the thread is blocked in resume() */ if (in_sched_functions(ctx->r15)) return (unsigned long)ctx->r15; else return ctx->r14; }
/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ unsigned long get_wchan(struct task_struct *p) { unsigned long stack_page; unsigned long frame, pc; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)p->thread_info; if (!stack_page || !mips_frame_info_initialized) return 0; pc = thread_saved_pc(p); if (!in_sched_functions(pc)) return pc; frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; do { int i; if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) return 0; for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { if (pc >= (unsigned long) mfinfo[i].func) break; } if (i < 0) break; if (mfinfo[i].omit_fp) break; pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; } while (in_sched_functions(pc)); return pc; }
static bool __save_trace(unsigned long pc, void *arg, bool nosched) { struct stack_trace *trace = arg; if (unlikely(nosched && in_sched_functions(pc))) return false; if (unlikely(trace->skip > 0)) { trace->skip--; return false; } trace->entries[trace->nr_entries++] = pc; return (trace->nr_entries >= trace->max_entries); }
static unsigned long save_context_stack(struct stack_trace *trace, unsigned long sp, unsigned long low, unsigned long high, int savesched) { struct stack_frame *sf; struct pt_regs *regs; unsigned long addr; while(1) { sp &= PSW_ADDR_INSN; if (sp < low || sp > high) return sp; sf = (struct stack_frame *)sp; while(1) { addr = sf->gprs[8] & PSW_ADDR_INSN; if (!trace->skip) trace->entries[trace->nr_entries++] = addr; else trace->skip--; if (trace->nr_entries >= trace->max_entries) return sp; low = sp; sp = sf->back_chain & PSW_ADDR_INSN; if (!sp) break; if (sp <= low || sp > high - sizeof(*sf)) return sp; sf = (struct stack_frame *)sp; } /* Zero backchain detected, check for interrupt frame. */ sp = (unsigned long)(sf + 1); if (sp <= low || sp > high - sizeof(*regs)) return sp; regs = (struct pt_regs *)sp; addr = regs->psw.addr & PSW_ADDR_INSN; if (savesched || !in_sched_functions(addr)) { if (!trace->skip) trace->entries[trace->nr_entries++] = addr; else trace->skip--; } if (trace->nr_entries >= trace->max_entries) return sp; low = sp; sp = regs->gprs[15]; } }
static int __save_address(void *data, unsigned long address, int nosched) { struct stack_trace *trace = data; if (nosched && in_sched_functions(address)) return 0; if (trace->skip > 0) { trace->skip--; return 0; } if (trace->nr_entries < trace->max_entries) { trace->entries[trace->nr_entries++] = address; return 0; } return 1; }
static void __save_stack_address(void *data, unsigned long addr, bool reliable, bool nosched) { struct stack_trace *trace = data; #ifdef CONFIG_FRAME_POINTER if (!reliable) return; #endif if (nosched && in_sched_functions(addr)) return; if (trace->skip > 0) { trace->skip--; return; } if (trace->nr_entries < trace->max_entries) trace->entries[trace->nr_entries++] = addr; }
static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) return 0; if (data->skip) { data->skip--; return 0; } trace->entries[trace->nr_entries++] = addr; return trace->nr_entries >= trace->max_entries; }
static int save_stack_address(struct stack_trace *trace, unsigned long addr, bool nosched) { if (nosched && in_sched_functions(addr)) return 0; if (trace->skip > 0) { trace->skip--; return 0; } if (trace->nr_entries >= trace->max_entries) return -1; trace->entries[trace->nr_entries++] = addr; return 0; }
/* * Save stack-backtrace addresses into a stack_trace buffer: */ static void save_raw_context_stack(struct stack_trace *trace, unsigned long reg29, int savesched) { unsigned long *sp = (unsigned long *)reg29; unsigned long addr; while (!kstack_end(sp)) { addr = *sp++; if (__kernel_text_address(addr) && (savesched || !in_sched_functions(addr))) { if (trace->skip > 0) trace->skip--; else trace->entries[trace->nr_entries++] = addr; if (trace->nr_entries >= trace->max_entries) break; } } }
static void save_stack_address_nosched(void *data, unsigned long addr, int reliable) { struct stack_trace *trace = (struct stack_trace *)data; if (!reliable) return; if (in_sched_functions(addr)) return; if (trace->skip > 0) { trace->skip--; return; } if (trace->nr_entries < trace->max_entries) trace->entries[trace->nr_entries++] = addr; }
unsigned long get_wchan(struct task_struct *p) { unsigned long fp, lr; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = 4096 + (unsigned long)p; fp = thread_saved_fp(p); do { if (fp < stack_page || fp > 4092+stack_page) return 0; lr = pc_pointer (((unsigned long *)fp)[-1]); if (!in_sched_functions(lr)) return lr; fp = *(unsigned long *) (fp - 12); } while (count ++ < 16); return 0; }
unsigned long get_wchan(struct task_struct *p) { struct unwind_frame_info info; unsigned long ip; int count = 0; /* * These bracket the sleeping functions.. */ unwind_frame_init_from_blocked_task(&info, p); do { if (unwind_once(&info) < 0) return 0; ip = info.ip; if (!in_sched_functions(ip)) return ip; } while (count++ < 16); return 0; }
unsigned long get_wchan(struct task_struct *p) { unsigned long fp, pc; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)p; fp = ((struct pt_regs *)p->thread.ksp)->er6; do { if (fp < stack_page+sizeof(struct thread_info) || fp >= 8184+stack_page) return 0; pc = ((unsigned long *)fp)[1]; if (!in_sched_functions(pc)) return pc; fp = *(unsigned long *) fp; } while (count++ < 16); return 0; }
/* * The "wait channel" terminology is archaic, but what we want * is an identification of the point at which the scheduler * was invoked by a blocked thread. */ unsigned long get_wchan(struct task_struct *p) { unsigned long fp, pc; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)task_stack_page(p); fp = ((struct hexagon_switch_stack *)p->thread.switch_sp)->fp; do { if (fp < (stack_page + sizeof(struct thread_info)) || fp >= (THREAD_SIZE - 8 + stack_page)) return 0; pc = ((unsigned long *)fp)[1]; if (!in_sched_functions(pc)) return pc; fp = *(unsigned long *) fp; } while (count++ < 16); return 0; }
unsigned long get_wchan(struct task_struct *p) { unsigned long bp, sp, ip; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long)task_stack_page(p); sp = p->thread.sp; if (!stack_page || sp < stack_page || sp > top_esp+stack_page) return 0; /* include/asm-i386/system.h:switch_to() pushes bp last. */ bp = *(unsigned long *) sp; do { if (bp < stack_page || bp > top_ebp+stack_page) return 0; ip = *(unsigned long *) (bp+4); if (!in_sched_functions(ip)) return ip; bp = *(unsigned long *) bp; } while (count++ < 16); return 0; }
static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; struct pt_regs *regs; unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) return 0; if (data->skip) { data->skip--; return 0; } trace->entries[trace->nr_entries++] = addr; if (trace->nr_entries >= trace->max_entries) return 1; /* * in_exception_text() is designed to test if the PC is one of * the functions which has an exception stack above it, but * unfortunately what is in frame->pc is the return LR value, * not the saved PC value. So, we need to track the previous * frame PC value when doing this. */ addr = data->last_pc; data->last_pc = frame->pc; if (!in_exception_text(addr)) return 0; regs = (struct pt_regs *)frame->sp; trace->entries[trace->nr_entries++] = regs->ARM_pc; return trace->nr_entries >= trace->max_entries; }
unsigned long get_wchan(struct task_struct *p) { unsigned long schedule_frame; unsigned long pc; if (!p || p == current || p->state == TASK_RUNNING) return 0; /* * This one depends on the frame size of schedule(). Do a * "disass schedule" in gdb to find the frame size. Also, the * code assumes that sleep_on() follows immediately after * interruptible_sleep_on() and that add_timer() follows * immediately after interruptible_sleep(). Ugly, isn't it? * Maybe adding a wchan field to task_struct would be better, * after all... */ pc = thread_saved_pc(p); if (in_sched_functions(pc)) { schedule_frame = ((unsigned long *)task_thread_info(p)->pcb.ksp)[6]; return ((unsigned long *)schedule_frame)[12]; } return pc; }