Linux Kernel stack call Implementation Analysis-save_stack_trace, printstacktrace

Source: Internet
Author: User

Linux Kernel stack call Implementation Analysis-save_stack_trace, printstacktrace
1 kernel thread

The kernel allocates 8 K stack space for each thread, and a struct thread_info struct is placed at the top of each stack to save thread-related information.

There are several important variables:

Preempt_count:

This variable is divided into four parts

0-7bit: indicates whether the current process can be preemptible.

8-15bit: softirq enable flag

16-23bit: hardirq enable flag

24bit: PREEMPT_ACTIVE flag bit (atomic context flag ??)

Task: process-related structure, which contains more information.

Cpu_context: cpu register value, which is the thread execution scenario reserved 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

You only need to obtain the current sp pointer and then perform 8 K Bytes alignment to find the thread_info structure.

Register unsignedlong sp asm ("sp ");

Return (structthread_info *) (sp &~ (THREAD_SIZE-1 ));

Function structure provided by the kernel

Static inline struct thread_info * current_thread_info (void)

And the current macro is used to obtain the structure of the current process.

Obtain the cpsr register in c

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. stack frame

Each process has its own stack. Function call occurs when a process is executed. The primary function and the subfunction use the same stack. In general, it is not necessary to distinguish which part of the stack is used by the primary and subfunctions respectively. However, this information is very important when you need to backtrace function calls during execution.

Simply put, stack frame is a part of the stack used by a function. The stack frame of all functions forms a complete stack. The boundary of stack frame is defined by FP and SP respectively.

The FP pointer can be used to find all the backtrace processes.

During program execution (debugging is usually required due to some unexpected situation), the SP and FP of the master function can be obtained through the stackframe defined by SP and FP, in this way, the stack frame (PC, LR, SP, and FP will be pushed to the stack at the first time of the function call) of the primary function is obtained, and the call sequence of all functions can be obtained through this trace.

To support FP pointers in the kernel, you must enable CONFIG_FRAME_POINTER configuration.

3 kernel save_stack_trace Analysis

Voidsave_stack_trace (struct stack_trace * trace)

{

Save_stack_trace_tsk (current, trace );

}

Further analysis:

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 level to be ignored, which is generally set to 0.

 

If (tsk! = Current ){

# IfdefCONFIG_SMP

/*

* What guarantees do we have here that 'tsk' is not

* Running on another CPU? For now, ignore it as we

* Can't guarantee we won't explode.

*/

// If the current process on the current cpu is not saved, it is difficult to determine the tsk process register value,

It may be changing at all times.

If (trace-> nr_entries <trace-> max_entries)

Trace-> entries [trace-> nr_entries ++] = ULONG_MAX;

Return;

# Else // when a single cpu is used, the tsk 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"); // get the current stack pointer through sp

// _ Builtin_frame_address (0) returns the FP pointer of the current function.

// _ Builtin_return_address (0) returns the return address (LR) of the current function)

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; // obtain the current pc using the function name

}

 

Performance_stackframe (& frame, save_trace, & data); // stack Traversal

If (trace-> nr_entries <trace-> max_entries)

Trace-> entries [trace-> nr_entries ++] = ULONG_MAX;

}

The save_trace mainly saves the current pc to 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 skip is set, 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;

}

Next, analyze the performance_stackframe function.

Void notrace performance_stackframe (struct stackframe * frame,

Int (* fn) (struct stackframe *, void *), void * data)

{

While (1 ){

Int ret;

 

If (fn (frame, data) // call save_trace to save the current pc

Break;

Ret = unwind_frame (frame); // move the FP pointer to the previous function.

If (ret <0)

Break;

}

}


 

Intnotrace unwind_frame (struct stackframe * frame)

{

Unsigned long high, low;

Unsigned long fp = frame-> fp;

 

/* Only go to a higher address on the stack */

Low = frame-> sp; // the end of the current stack

High = ALIGN (low, THREAD_SIZE); // The top of the current stack

 

/* Check current frame pointer is within bounds */

If (fp <low + 12 | fp> high-4)

Return-EINVAL;

 

/* Restore the registers from the stack frame */

The stack diagram above shows that the storage order of these pointers 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 obtain a stack instance

Voidaee_get_traces (char * msg)

{

Structstack_trace trace;

Inti;

Effecffset;

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, the function name can be printed through % pf and % pf.

Snprintf (msg + offset, KERNEL_REPORT_LENGTH-offset, "[<% p>] % pS \ n ",

(Void *) trace. entries [I], (void *) trace. entries [I]);

}

}


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.