Print stack call information when linux c program exits abnormally

Let's first understand the three functions

#include <execinfo.h>

 int backtrace(void **buffer, int size);

 char **backtrace_symbols(void *const *buffer, int size);

 void backtrace_symbols_fd(void *const *buffer, int size, int fd);

int backtrace(void **buffer,int size) 
   This function is used to obtain the call stack of the current thread. The information obtained will be stored in the buffer, which is an array of pointers. The size parameter is used to specify how many void* elements can be stored in the buffer. The return value of the function is the number of pointers actually obtained, the maximum size does not exceed the size of the pointer in the buffer is actually the return address obtained from the stack, and each stack frame has a return address. 
    Note that some compiler optimization options interfere with obtaining the correct call stack, and inline functions do not have a stack frame; deleting the frame pointer will also make it impossible to parse the stack content correctly 
   char ** backtrace_symbols (void *const *buffer, int size) 
    backtrace_symbols converts the information obtained from the backtrace function into an array of strings. The parameter buffer should be an array pointer obtained from the backtrace function, size is the number of elements in the array (the return value of backtrace), and the return value of the function is a pointer to a character The pointer to the array of strings, its size is the same as the buffer. Each string contains a printable information relative to the corresponding element in the buffer. It includes the function name, the offset address of the function, and the actual return address
Note: If you can't get enough space for the string, the return value of the function will be NULL 
    Now, only use ELF binary format programs and difficulties can obtain the function name and offset address. In other systems, only the hexadecimal return address can be obtained. In addition, you may need to pass the corresponding flag to the linker to support the function name function (For example, in a system using GNU ld, you need to pass (-rdynamic))
The strings generated by backtrace_symbols are all malloc, but do not free the last one, because backtrace_symbols is based on the call stack layer number given by backtrace, malloc generates a block of memory to store the result string at a time, so, like the above The code is the same, just at the end, the return pointer of free backtrace_symbols is OK. This is also specifically mentioned in the manual of backtrace. 
    void backtrace_symbols_fd (void *const *buffer, int size, int fd) 
    backtrace_symbols_fd has the same function as the backtrace_symbols function, the difference is that it does not return a string array to the caller, but writes the result to the file descriptor fd In the file, each function corresponds to one line. It does not need to call the malloc function, so it is suitable for situations where the function may fail to be called.

Program test:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <execinfo.h>

void out_stack(char *sig);

void signal_exit(int dunno) 
{ 
	char* signal_str = "";
	char dunno_str[10] = {0};
	sprintf(dunno_str, "%d", dunno);
	switch (dunno) 
	{
		case 1:
			signal_str = "SIGHUP(1)";
			break;
		case 2:
			signal_str = "SIGINT(2:CTRL_C)"; //CTRL_C
			break;
		case 3:
			signal_str = "SIGQUIT(3)";
			break;
		case 6:
		{
			signal_str = "SIGABRT(6)";
			out_stack(signal_str);
		}
		break;
		case 9:
			signal_str = "SIGKILL(9)";
			break;
		case 15:
			signal_str = "SIGTERM(15 KILL)"; //kill 
			break;
		case 11:
		{
			signal_str = "SIGSEGV(11)"; //SIGSEGV 
			out_stack(signal_str);
		}
		break;	
		default:
			signal_str = "OTHER";
			break;
	}
	exit(0);
}

static void output_addrline(char addr[])
{
	char cmd[256];
	char line[256];
	char addrline[32]={0,};
	char *str1, *str2;
	FILE* file;

	str1 = strchr(addr,'[');
	str2 = strchr(addr, ']');
	if(str1 == NULL || str2 == NULL)
	{
		return;
	}	
	memcpy(addrline, str1 + 1, str2 -str1);
	snprintf(cmd, sizeof(cmd), "addr2line -e /proc/%d/exe %s ", getpid(), addrline);
	file = popen(cmd, "r");
	if(NULL != fgets(line, 256, file)) 
	{
		printf("%s\n", line);
	}
	pclose(file);	
}
void out_stack(char *sig)
{
	void *array[32];
	size_t size;
	char **strings;
	int i;
	
	printf("%s\n", sig);
	size = backtrace (array, 32);
	strings = backtrace_symbols (array, size);
	if (NULL == strings)
	{
		printf("backtrace_symbols\n");
		return ;
	}
	
	for (i = 0; i < size; i++)
	{
		printf("%s",strings[i]);
		output_addrline(strings[i]);	
	}
	
	free(strings);
}
void test3(int n)
{
	char *str;
	printf("in test3 [%d]\n", n);
	strcpy(str, "123");
}
void test2(int n)
{
	printf("in test2 [%d]\n", n);
	test3(3);
}
void test1(int n)
{
	printf("in test1 [%d]\n", n);
	test2(2);
}
int main()
{
	signal(SIGHUP, signal_exit); 
	signal(SIGINT, signal_exit);
	signal(SIGQUIT, signal_exit);
	signal(SIGABRT, signal_exit);
	signal(SIGKILL, signal_exit);
	signal(SIGTERM, signal_exit);
	signal(SIGSEGV, signal_exit);
	
	test1(1);
}

Note that the -g and -rdynamic options should be added when compiling

Guess you like

Origin blog.csdn.net/u014608280/article/details/84974877