for (i = 0; i < stack_trace_nr_entries; i++) { if (i + 1 == stack_trace_nr_entries)
size = stack_trace_index[i]; else
size = stack_trace_index[i] - stack_trace_index[i+1];
/* * The stack tracer looks for a maximum stack at each call from a function. It * registers a callback from ftrace, and in that callback it examines the stack * size. It determines the stack size from the variable passed in, which is the * address of a local variable in the stack_trace_call() callback function. * The stack size is calculated by the address of the local variable to the top * of the current stack. If that size is smaller than the currently saved max * stack size, nothing more is done. * * If the size of the stack is greater than the maximum recorded size, then the * following algorithm takes place. * * For architectures (like x86) that store the function's return address before * saving the function's local variables, the stack will look something like * this: * * [ top of stack ] * 0: sys call entry frame * 10: return addr to entry code * 11: start of sys_foo frame * 20: return addr to sys_foo * 21: start of kernel_func_bar frame * 30: return addr to kernel_func_bar * 31: [ do trace stack here ] * * The save_stack_trace() is called returning all the functions it finds in the * current stack. Which would be (from the bottom of the stack to the top): * * return addr to kernel_func_bar * return addr to sys_foo * return addr to entry code * * Now to figure out how much each of these functions' local variable size is, * a search of the stack is made to find these values. When a match is made, it * is added to the stack_dump_trace[] array. The offset into the stack is saved * in the stack_trace_index[] array. The above example would show: * * stack_dump_trace[] | stack_trace_index[] * ------------------ + ------------------- * return addr to kernel_func_bar | 30 * return addr to sys_foo | 20 * return addr to entry | 10 * * The print_max_stack() function above, uses these values to print the size of * each function's portion of the stack. * * for (i = 0; i < nr_entries; i++) { * size = i == nr_entries - 1 ? stack_trace_index[i] : * stack_trace_index[i] - stack_trace_index[i+1] * print "%d %d %d %s\n", i, stack_trace_index[i], size, stack_dump_trace[i]); * } * * The above shows * * depth size location * ----- ---- -------- * 0 30 10 kernel_func_bar * 1 20 10 sys_foo * 2 10 10 entry code * * Now for architectures that might save the return address after the functions * local variables (saving the link register before calling nested functions), * this will cause the stack to look a little different: * * [ top of stack ] * 0: sys call entry frame * 10: start of sys_foo_frame * 19: return addr to entry code << lr saved before calling kernel_func_bar * 20: start of kernel_func_bar frame * 29: return addr to sys_foo_frame << lr saved before calling next function * 30: [ do trace stack here ] * * Although the functions returned by save_stack_trace() may be the same, the * placement in the stack will be different. Using the same algorithm as above * would yield: * * stack_dump_trace[] | stack_trace_index[] * ------------------ + ------------------- * return addr to kernel_func_bar | 30 * return addr to sys_foo | 29 * return addr to entry | 19 * * Where the mapping is off by one: * * kernel_func_bar stack frame size is 29 - 19 not 30 - 29! * * To fix this, if the architecture sets ARCH_RET_ADDR_AFTER_LOCAL_VARS the * values in stack_trace_index[] are shifted by one to and the number of * stack trace entries is decremented by one. * * stack_dump_trace[] | stack_trace_index[] * ------------------ + ------------------- * return addr to kernel_func_bar | 29 * return addr to sys_foo | 19 * * Although the entry function is not displayed, the first function (sys_foo) * will still include the stack size of it.
*/ staticvoid check_stack(unsignedlong ip, unsignedlong *stack)
{ unsignedlong this_size, flags; unsignedlong *p, *top, *start; staticint tracer_frame; int frame_size = READ_ONCE(tracer_frame); int i, x;
this_size = ((unsignedlong)stack) & (THREAD_SIZE-1);
this_size = THREAD_SIZE - this_size; /* Remove the frame of the tracer */
this_size -= frame_size;
if (this_size <= stack_trace_max_size) return;
/* we do not handle interrupt stacks yet */ if (!object_is_on_stack(stack)) return;
/* Can't do this from NMI context (can cause deadlocks) */ if (in_nmi()) return;
/* Skip over the overhead of the stack tracer itself */ for (i = 0; i < stack_trace_nr_entries; i++) { if (stack_dump_trace[i] == ip) break;
}
/* * Some archs may not have the passed in ip in the dump. * If that happens, we need to show everything.
*/ if (i == stack_trace_nr_entries)
i = 0;
/* * Now find where in the stack these are.
*/
x = 0;
start = stack;
top = (unsignedlong *)
(((unsignedlong)start & ~(THREAD_SIZE-1)) + THREAD_SIZE);
/* * Loop through all the entries. One of the entries may * for some reason be missed on the stack, so we may * have to account for them. If they are all there, this * loop will only happen once. This code only takes place * on a new max, so it is far from a fast path.
*/ while (i < stack_trace_nr_entries) { int found = 0;
stack_trace_index[x] = this_size;
p = start;
for (; p < top && i < stack_trace_nr_entries; p++) { /* * The READ_ONCE_NOCHECK is used to let KASAN know that * this is not a stack-out-of-bounds error.
*/ if ((READ_ONCE_NOCHECK(*p)) == stack_dump_trace[i]) {
stack_dump_trace[x] = stack_dump_trace[i++];
this_size = stack_trace_index[x++] =
(top - p) * sizeof(unsignedlong);
found = 1; /* Start the search from here */
start = p + 1; /* * We do not want to show the overhead * of the stack tracer stack in the * max stack. If we haven't figured * out what that is, then figure it out * now.
*/ if (unlikely(!tracer_frame)) {
tracer_frame = (p - stack) * sizeof(unsignedlong);
stack_trace_max_size -= tracer_frame;
}
}
}
if (!found)
i++;
}
#ifdef ARCH_FTRACE_SHIFT_STACK_TRACER /* * Some archs will store the link register before calling * nested functions. This means the saved return address * comes after the local storage, and we need to shift * for that.
*/ if (x > 1) {
memmove(&stack_trace_index[0], &stack_trace_index[1], sizeof(stack_trace_index[0]) * (x - 1));
x--;
} #endif
stack_trace_nr_entries = x;
if (task_stack_end_corrupted(current)) {
print_max_stack();
BUG();
}
/* no atomic needed, we only modify this variable by this cpu */
__this_cpu_inc(disable_stack_tracer); if (__this_cpu_read(disable_stack_tracer) != 1) goto out;
/* If rcu is not watching, then save stack trace can fail */ if (!rcu_is_watching()) goto out;
ip += MCOUNT_INSN_SIZE;
check_stack(ip, &stack);
out:
__this_cpu_dec(disable_stack_tracer); /* prevent recursion in schedule */
preempt_enable_notrace();
}
ret = kstrtoul_from_user(ubuf, count, 10, &val); if (ret) return ret;
local_irq_save(flags);
/* * In case we trace inside arch_spin_lock() or after (NMI), * we will cause circular lock, so we also need to increase * the percpu disable_stack_tracer here.
*/
__this_cpu_inc(disable_stack_tracer);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.