I. Purpose:
It is mainly used to find the cause of the error when the program exits unexpectedly.
Ii. functions:
Trace the stack. In short, you can list the call relationships of the current function.
Iii. Principles:
1. Through the analysis of the current stack, find the frame address of the Upper-layer function in the stack, analyze the stack of the Upper-layer function, and find the upper-layer frame address ...... Until the top layer is found, the frame address refers to a piece of space that stores local variables, the return address of the upper layer, and the register value on the stack.
2. because different processor stack methods are different, the specific implementation of this function is in the compiler's built-in functions _ buildin_frame_address and _ buildin_return_address. It involves tools glibc and gcc. If the compiler does not support this function, you can also implement this function by yourself. For example, the Implementation on arm
Iv. Method:
Add backtrace and related function calls to the program
V. Example:
1. General Implementation of backtrace
I. Program
# Include <signal. h>
# Include <stdio. h>
# Include <stdlib. h>
# Include <execinfo. h>
# Include <sys/types. h>
# Include <sys/stat. h>
# Include <fcntl. h>
# Include <string. h>
# Include <unistd. h>
# Define PRINT_DEBUG
Static void print_reason (int sig, siginfo_t * info, void * secret)
{
Void * array [10];
Size_t size;
# Ifdef PRINT_DEBUG
Char ** strings;
Size_t I;
Size = backtrace (array, 10 );
Strings = backtrace_symbols (array, size );
Printf ("Obtained % zd stack frames. \ n", size );
For (I = 0; I <size; I ++)
Printf ("% s \ n", strings [I]);
Free (strings );
# Else
Int fd = open ("err. log", O_CREAT | O_WRONLY );
Size = backtrace (array, 10 );
Backtrace_symbols_fd (array, size, fd );
Close (fd );
# Endif
Exit (0 );
}
Void die ()
{
Char * test1;
Char * test2;
Char * test3;
Char * test4 = NULL;
Strcpy (test4, "AB ");
}
Void test1 ()
{
Die ();
}
Int main (int argc, char ** argv)
{
Struct sigaction myAction;
MyAction. sa_sigaction = print_reason;
Sigemptyset (& myAction. sa_mask );
MyAction. sa_flags = SA_RESTART | SA_SIGINFO;
Sigaction (SIGSEGV, & myAction, NULL );
Sigaction (SIGUSR1, & myAction, NULL );
Sigaction (SIGFPE, & myAction, NULL );
Sigaction (SIGILL, & myAction, NULL );
Sigaction (SIGBUS, & myAction, NULL );
Sigaction (SIGABRT, & myAction, NULL );
Sigaction (SIGSYS, & myAction, NULL );
Test1 ();
}
Ii. Compilation Parameters
Gcc main. c-o test-g-rdynamic
2. Implement backtrace based on different processors
I. arm's backtrace function implementation
Static int backtrace_xy (void ** BUFFER, int SIZE)
{
Volatile int n = 0;
Volatile int * p;
Volatile int * q;
Volatile int ebp1;
Volatile int eip1;
Volatile int I = 0;
P = & n;
Ebp1 = p [4];
Eip1 = p [6];
Fprintf (stderr, "====================== backtrace_xy addr: 0x % 0x, param1: 0x % 0x, param2: 0x % 0x \ n ",
Backtrace_xy, & BUFFER, & SIZE );
Fprintf (stderr, "n addr is 0x % 0x \ n", & n );
Fprintf (stderr, "p addr is 0x % 0x \ n", & p );
For (I = 0; I & lt; SIZE; I ++)
{
Fprintf (stderr, "ebp1 is 0x % 0x, eip1 is 0x % 0x \ n", ebp1, eip1 );
BUFFER [I] = (void *) eip1;
P = (int *) ebp1;
Q = p-5;
Eip1 = q [5];
Ebp1 = q [2];
If (ebp1 = 0 | eip1 = 0)
Break;
}
Fprintf (stderr, "total level: % d \ n", I );
Return I;
}
6. Example 2:
/* Main. c */
# Include "sigsegv. h"
# Include & lt; string. h>
Int die (){
Char * err = NULL;
Strcpy (err, "gonner ");
Return 0;
}
Int main (){
Return die ();
}
/* Sigsegv. c */
# Define _ GNU_SOURCE
# Include <memory. h>
# Include <stdlib. h>
# Include <stdio. h>
# Include <signal. h>
# Include <ucontext. h>
# Include <dlfcn. h>
# Include <execinfo. h>
# Define NO_CPP_DEMANGLE
# Ifndef NO_CPP_DEMANGLE
# Include <cxxabi. h>
# Endif
# If defined (REG_RIP)
# Define SIGSEGV_STACK_IA64
# Define REGFORMAT "% 016lx"
# Elif defined (REG_EIP)
# Define SIGSEGV_STACK_X86
# Define REGFORMAT "% 08x"
# Else
# Define SIGSEGV_STACK_GENERIC
# Define REGFORMAT "% x"
# Endif
Static void signal_segv (int signum, siginfo_t * info, void * ptr ){
Static const char * si_codes [3] = {"", "SEGV_MAPERR", "SEGV_ACCERR "};
Size_t I;
Ucontext_t * ucontext = (ucontext_t *) ptr;
# If defined (SIGSEGV_STACK_X86) | defined (SIGSEGV_STACK_IA64)
Int f = 0;
Dl_info dlinfo;
Void ** bp = 0;
Void * ip = 0;
# Else
Void * bt [20];
Char ** strings;
Size_t sz;
# Endif
Fprintf (stderr, "Segmentation Fault! \ N ");
Fprintf (stderr, "info-& gt; si_signo = % d \ n", signum );
Fprintf (stderr, "info-& gt; si_errno = % d \ n", info-& gt; si_errno );
// Fprintf (stderr, "info-& gt; si_code = % d (% s) \ n", info-& gt; si_code, info-& gt; si_codes [si_code]);
Fprintf (stderr, "info-& gt; si_addr = % p \ n", info-& gt; si_addr );
For (I = 0; I <NGREG; I ++)
Fprintf (stderr, "reg [% 02d] = 0x" REGFORMAT "\ n", I, ucontext-> uc_mcontext.gregs [I]);
# If defined (SIGSEGV_STACK_X86) | defined (SIGSEGV_STACK_IA64)
# If defined (SIGSEGV_STACK_IA64)
Ip = (void *) ucontext-& gt; uc_mcontext.gregs [REG_RIP];
Bp = (void **) ucontext-& gt; uc_mcontext.gregs [REG_RBP];
# Elif defined (SIGSEGV_STACK_X86)
Ip = (void *) ucontext-& gt; uc_mcontext.gregs [REG_EIP];
Bp = (void **) ucontext-& gt; uc_mcontext.gregs [REG_EBP];
# Endif
Fprintf (stderr, "Stack trace: \ n ");
While (bp! = & Ip ){
If (! Dladdr (ip, & dlinfo ))
Break;
Const char * symname = dlinfo. dli_sname;
# Ifndef NO_CPP_DEMANGLE
Int status;
Char * tmp = _ cxa_demangle (symname, NULL, 0, & status );
If (status = 0! = & Tmp)
Symname = tmp;
# Endif
Fprintf (stderr, "% 2d: % p <% s + % u> (% s) \ n ",
++ F,
Ip,
Symname,
(Unsigned) (ip-dlinfo. dli_saddr ),
Dlinfo. dli_fname );
# Ifndef NO_CPP_DEMANGLE
If (tmp)
Free (tmp );
# Endif
If (dlinfo. dli_sname! =! Strcmp (dlinfo. dli_sname, "main "))
Break;
Ip = bp [1];
Bp = (void **) bp [0];
}
# Else
Fprintf (stderr, "Stack trace (non-dedicated): \ n ");
Sz = backtrace (bt, 20 );
Strings = backtrace_symbols (bt, sz );
For (I = 0; I <sz; ++ I)
Fprintf (stderr, "% s \ n", strings [I]);
# Endif
Fprintf (stderr, "End of stack trace \ n ");
Exit (-1 );
}
Int setup_sigsegv (){
Struct sigaction action;
Memset (& action, 0, sizeof (action ));
Action. sa_sigaction = signal_segv;
Action. sa_flags = SA_SIGINFO;
If (sigaction (SIGSEGV, & action, NULL) & lt; 0 ){
Perror ("sigaction ");
Return 0;
}
Return 1;
}
# Ifndef SIGSEGV_NO_AUTO_INIT
Static void _ attribute (constructor) init (void)
{
Setup_sigsegv ();
}
# Endif
/* Sigsegv. h */
# Ifndef _ sigsegv_h __
# Define _ sigsegv_h __
# Ifdef _ cplusplus
Extern "C "{
# Endif
Int setup_sigsegv ();
# Ifdef _ cplusplus
}
# Endif
# Endif/* _ sigsegv_h __*/
-Rdynamic-ldl-ggdb must be added during compilation.
Void
Handle_signal_error (int rec_signal, siginfo_t * signal_info, void * context)
{
NE_Info * _ attribute _ (unused) ne_info = NULL;
Struct sigaction action;
FILE * file;
Void * backtr [NUMBER_OF_BACKTRACE];
Cpal_uns32 _ attribute _ (unused) I = 0;
Cpal_uns32 backtr_size = 0;
Ucontext_t * u_context;
Time_t seconds_time;
Struct tm * time_struct;
Cpal_si32 ret_t;
Char filename [SIZE_OF_FILENAME];
If (g_handler_running)
Return;
G_handler_running = CPAL_TRUE;
Ret_t = time (& seconds_time );
If (ret_t! =-1)
{
Time_struct = gmtime (& seconds_time );
Snprintf (filename, SIZE_OF_FILENAME, "% s % d-% s", BACKTRACE_FILE_PATH, time_struct-> tm_mon, time_struct-& gt; tm_mday,
(Time_struct-& gt; tm_year-100) + 2000, time_struct-& gt; tm_hour, time_struct-& gt; tm_min, time_struct-& gt; tm_sec, BACKTRACE_FILE );
}
Else
{
Snprintf (filename, SIZE_OF_FILENAME, "% s", BACKTRACE_FILE );
}
File = fopen (filename, "w ");
If (file = NULL)
{
Return;
}
If (signal_info = NULL)
{
Return;
}
If (context = NULL)
{
Return;
}
U_context = (ucontext_t *) context;
/* Restore the default action for this signal and re-raise it, so that the default action occurs .*/
Action. sa_sigaction = SIG_DFL;
Sigemptyset (& action. sa_mask );
Action. sa_flags = SA_RESTART;
Sigaction (rec_signal, & action, NULL );
/* Print out the backtrace .*/
Backtr_size = backtrace (backtr, 20 );
/* The backtrace points to sigaction in libc, not to where the signal was actually raised.
This overwrites the sigaction with where the signal was sent, so we can resolve the sender .*/
# If _ WORDSIZE = 64
Backtr [1] = (void *) u_context-& gt; uc_mcontext.gregs [REG_RIP];
# Else
Backtr [1] = (void *) u_context-& gt; uc_mcontext.gregs [REG_EIP];
# Endif // _ WORDSIZE
Backtrace_symbols_fd (backtr, backtr_size, fileno (file ));
Fprintf (file, "Backtrace is abve. \ nFatal signal % d received. \ n", rec_signal );
# If _ WORDSIZE = 64
Fprintf (file, "Signal received at address % p from 0x % 08x. \ n", signal_info-& gt; si_addr,
U_context-& gt; uc_mcontext.gregs [REG_RIP]);
# Else
Fprintf (file, "Signal received at address % p from 0x % 08x. \ n", signal_info-& gt; si_addr,
U_context-& gt; uc_mcontext.gregs [REG_EIP]);
# Endif // _ WORDSIZE
# If CPAL_LM_DEBUG
/* Print all NE_Infos */
For (; I <MAX_NO_OF_CONNS; I ++)
{
Ne_info = g_ne_hash_tab [I];
While (ne_info! = NULL)
{
Ne_info = ne_info-> next_ne;
}
}
# Endif
Fflush (file );
Fclose (file );
Sleep (50);/* Sleep for 50 seconds */
G_handler_running = * _ FALSE;
Raise (rec_signal );
}