1 Kernel threads
The kernel allocates 8K of stack space for each thread , with a struct thread_info structure at the top of each stack to hold thread-related information.
There are several important variables:
Preempt_count:
This variable is divided into four parts
0-7bit : Flag of whether the current process can be preempted
8-15BIT:SOFTIRQ Enable Flag
16-23BIT:HARDIRQ Enable Flag
24bit:preempt_active flag bit ( atomic context flag bit??) )
Task: process-related structures that contain richer information
cpu_context:cpu The register value, which should be the thread execution scenario that is preserved when the current process is switched.
struct Thread_info {
Unsignedlong flags; /* Low Level flags */
int preempt_count; /* 0 = preemptable, <0 = bug */
mm_segment_t Addr_limit; /* Address limit */
Structtask_struct *task; /* Main task structure */
__U32 CPU; /*CPU * *
Structcpu_context_save Cpu_context; /* CPU Context */
............................
}
Get current thread/process
only need to get the current SP pointer and then 8k byte alignment to find the THREAD_INFO structure
Register Unsignedlong SP ASM ("SP");
Return (Structthread_info *) (SP & ~ (thread_size-1));
Kernel-Provided correlation function structure
Static inline struct thread_info*current_thread_info (void)
and the current macro is used to get the currently process structure body.
Get CPSR Register in C language
Static inline unsigned longarch_local_cpsr_save (void)
{
Unsignedlong flags;
Asmvolatile ("Mrs%0, CPSR @ arch_local_irq_save\n"
:: "R" (Flags): "Memory", "CC");
Returnflags;
}
2 function call when stack frame
each process has its own stack. Considering the scenario in which a function call occurs when the process executes, the parent function and the child function use the same stack, and in general, it is not necessary to distinguish which part of the stack is used by the parent function and the child function respectively. However, this information is important when you need to backtrace a function call during execution .
simply put, the stack frame is part of a stack used by a function, and the stack frame of all functions forms a complete stack. The two boundaries of a stack frame are defined by the FP and SP, respectively.
All backtrace processes can be identified by the FP pointer
The SP and FP of the parent function can be obtained by stackframe the SP and FP to obtain a stack frame for the parent function (PC,LR,SP, which is usually required for debugging during the execution of the program). The FP will press the stack at the first time of the function call, so that all functions are called in sequence.
The Config_frame_pointer configuration must be turned on for the kernel to support FP pointers
3KernelSave_stack_traceAnalysis
Voidsave_stack_trace (struct stack_trace *trace)
{
Save_stack_trace_tsk (current, trace);
}
Then analyze:
Voidsave_stack_trace_tsk (struct task_struct *tsk, struct stack_trace *trace)
{
struct Stack_trace_data data;
struct StackFrame frame;
Data.trace = trace;
Data.skip = trace->skip; // set the call progression that needs to be ignored , generally set to 0
if (tsk! = current) {
#ifdefCONFIG_SMP
/*
* What guarantees does we have this ' tsk ' is not
* Running on another CPU? For now, ignore it as we
* can ' t guarantee we won ' t explode.
*/
// If you do not save the current CPU the current process on , then it's hard to determine tsk The value of the process register,
Maybe it's changing all the time . .
if (trace->nr_entries <trace->max_entries)
Trace->entries[trace->nr_entries++]= Ulong_max;
Return
#else // in a single CPU ,thetsk process has been switched and can be traced back to backtrace
Data.no_sched_functions = 1;
FRAME.FP = THREAD_SAVED_FP (tsk);
FRAME.SP = thread_saved_sp (tsk);
FRAME.LR = 0; /* Recovered from the stack */
frame.pc = THREAD_SAVED_PC (tsk);
#endif
} else {
Register unsigned long current_sp asm ("SP"); // through SP gets the current stack pointer
//__builtin_frame_address (0) returns the current function's FP Pointers
//__builtin_return_address (0) returns the return address of the current function (LR)
data.no_sched_functions = 0;
FRAME.FP = (unsignedlong) __builtin_frame_address (0);
FRAME.SP = current_sp;
FRAME.LR = (unsignedlong) __builtin_return_address (0);
frame.pc = (unsignedlong) Save_stack_trace_tsk; // the function name gets to the current PC
}
Walk_stackframe (&frame, save_trace,&data); // making a stack walk
if (trace->nr_entries <trace->max_entries)
Trace->entries[trace->nr_entries++]= Ulong_max;
}
which Save_trace Main Save current PC into the array .
Staticint 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) {// If there is a setting of Skip, The current call is skipped
data->skip--;
return 0;
}
trace->entries[trace->nr_entries++] =ADDR; // Save the current PC
return trace->nr_entries >=trace->max_entries;
}
then analyze Walk_stackframe function
void Notrace walk_stackframe (struct stackframe *frame,
Int (*FN) (struct stackframe *, void *), void *data)
{
while (1) {
int ret;
if (FN (frame, data))// call save_trace Save current pc
Break
ret = Unwind_frame (frame); // put FP pointer moves to the previous function
if (Ret < 0)
Break
}
}
Intnotrace unwind_frame (struct stackframe *frame)
{
unsigned long, low;
unsigned long fp = frame->fp;
/* Only go-a higher address on the stack */
Low = frame->sp; // Current Stack End
High = ALIGN (low, thread_size); // top of current stack
/* Check current frame pointer is within bounds*/
if (FP < low + | | fp > HIGH-4)
Return-einval;
/* Restore the registers from the stack frame*/
from the stack diagram above , The order in which these pointers are placed on the stack is
Pc,lr,sp,fp
FRAME->FP = * (unsigned long *) (FP-12);
FRAME->SP = * (unsigned long *) (FP-8);
frame->pc = * (unsigned long *) (FP-4);
return 0;
}
3.1 Getting a stack instance
Voidaee_get_traces (char *msg)
{
Structstack_trace Trace;
Inti
Intoffset;
if (trace_entry_ptr = = NULL)
Return
memset (trace_entry_ptr,0, max_stack_trace_depth * 4);
Trace.entries= trace_entry_ptr;
/*savebacktraces * *
trace.nr_entries= 0;
Trace.max_entries= 32;//32 Level Call
trace.skip= 0;
Save_stack_trace_tsk (Current,&trace);
for (i = 0; i < trace.nr_entries; i++) {//current
offset= strlen (msg);
According to the PC,%PF can print out the function name by%PF
snprintf (msg+ offset, kernel_report_length-offset, "[<%p>]%ps\n",
(void *) trace.entries[i], (void*) trace.entries[i]);
}
}
Linux kernel stack call implementation analysis