C language function calls

1, backtrace
some memory detection tool such as Valgrind, debugging tools, such as GDB, you can view the runtime stack information function calls, sometimes in the analysis of the program to obtain information on the stack by means of backtrace is helpful, its prototype is as follows:

#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);

Header "execinfo.h" provides three related functions, simply put, backtrace function is used to get the stack address information, backtrace_symbols function to translate our stack address to a string recognizable, backtrace_symbols_fd function put a stack of strings information output to a file.

backtrace: This function is used to get the function call stack of the current thread, information acquired will be stored in the buffer, the pointer is a second buffer, may be used as a pointer array, the array element type is void *, i.e., from the stack return addresses acquired, each stack frame has a return address stack frame, buffer size parameter specifies the maximum value may be stored in elements void *, void * function return value is the number of buffer pointer actually acquired is not the maximum size than the size of the parameters.

backtrace_symbols: The conversion function from the function information acquired backtrace buffer is an array of strings char **, each string buffer containing the corresponding element with respect to the printable information, including offset and the actual function name, the function return address, size specifies the number of elements in the array, the return value may be a backtrace function, may be less than this value. Note that, backtrace_symbols the return value of a call to malloc to allocate storage space, in order to prevent memory leaks, we have to manually call free to release this memory.

backtrace_symbols_fd: This function backtrace_symbols function similar to the function, the difference is that this function is a direct result of the output to the file descriptor fd file, and does not call malloc.

When using the above three functions, also need to pay attention to the following points:

(1) If you are using the GCC compiler links, it suggested adding "-rdynamic" parameter, this parameter mean to tell ELF connectors add "-export-dynamic" mark, so that all the symbolic information will be added to the dynamic symbols symbol table to see the complete stack information.

(2) static function symbol information is not exported symbols, the backtrace invalid.

(3) Certain compiler optimization options have to get the correct function call stack interference, inline functions not stack frame, remove the frame pointer can lead to stack content can not be parsed correctly.

The following is a simple example:

//backtrace_ex.cpp
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

void my_backtrace()
{
void *buffer[100] = { NULL };
char **trace = NULL;

int size = backtrace(buffer, 100);
trace = backtrace_symbols(buffer, size);
if (NULL == trace) {
return;
}
for (int i = 0; i < size; ++i) {
printf("%s\n", trace[i]);
}
free(trace);
printf("----------done----------\n");
}

void func2()
{
my_backtrace();

}

void func()
{
func2();
}

int main()
{
func();
return 0;
}

Compiler implementation of the above file:

g++ backtrace_ex.cpp
./a.out
./a.out() [0x400811]
./a.out() [0x400baf]
./a.out() [0x400bba]
./a.out() [0x400bc5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f2473cf5ec5]
./a.out() [0x400709]
----------done----------

what! Although the stack information to break out, but the call stack is not very clear, because less "-rdynamic" parameter, recompile performed as follows:

g++ -rdynamic backtrace_ex.cpp
./a.out
./a.out(_Z12my_backtracev+0x44) [0x400b11]
./a.out(_Z5func2v+0x9) [0x400eaf]
./a.out(_Z4funcv+0x9) [0x400eba]
./a.out(main+0x9) [0x400ec5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f006bdfbec5]
./a.out() [0x400a09]
----------done----------

Plus the "-rdynamic" parameter after the very good, we can see the name of the function, due to the different platforms, different compiler to compile rules, so parsing out the form with the backtrace function names are different, with "./ a.out (_Z4funcv + 0x9) [0x400eba] "as an example, wherein the contents key parentheses," _ the Z "is a function name identifier starts behind the" 4 "represents a function name length, then this is a real function name "func", behind "v" expressed as a function argument of type void, then the "+ 0x9" address offset. Although there are certain rules to compile, but readability is not very good, we can use the following method demangle introduced to resolve these symbols.

2, the demangle
the demangle i.e. recombinant symbol, the following function prototype:

#include <cxxabi.h>
char* __cxa_demangle(const char* __mangled_name,
char* __output_buffer,
size_t* __length,
int* __status);


cxxabi.h is a C ++ runtime library function, use the link g ++ compiler, gcc there will be problems. __mangled_name i.e., the original information symbol, a character string to null-terminated, __ output_buffer used to save the information symbol recombinant length of __length, __ status indicates demangle result is 0 indicates success, returned recombinant string of symbolic value the first address, in order to null-terminated strings.

We use demangle to improve the above example :( replace my_backtrace as my_backtrace2)

void my_backtrace2()
{
void *buffer[100] = { NULL };
char **trace = NULL;
int size = backtrace(buffer, 100);
trace = backtrace_symbols(buffer, size);
if (NULL == trace) {
return;
}

= 100 name_size size_t;
char * name = (char *) the malloc (name_size);
for (int I = 0; I <size; I ++) {
char * begin_name = 0;
char * begin_offset = 0;
char * = end_offset 0;
for (char * P = the trace [I]; * P; P ++) {// symbol information using a format
if (* p == '(' ) {// left bracket
begin_name = P;
}
the else if (* p == '+' && begin_name) {// the address offset symbols
begin_offset = P;
}
the else IF (* P == ')' && begin_offset) {// right parenthesis
end_offset = P;
BREAK;
}
}
IF (begin_name && && begin_offset end_offset) {
* begin_name ++ = '\ 0';
* begin_offset ++ = '\ 0';
* end_offset = '\ 0';
int status = -4; // 0 -1 -2 -3
char *ret = abi::__cxa_demangle(begin_name, name, &name_size, &status);
if (0 == status) {
name = ret;
printf("%s:%s+%s\n", trace[i], name, begin_offset);
}
else {
printf("%s:%s()+%s\n", trace[i], begin_name, begin_offset);
}
}
else {
printf("%s\n", trace[i]);
}
}
free(name);
free(trace);
printf("----------done----------\n");
}

The results are as follows:

g++ -rdynamic backtrace_ex.cpp
./a.out
./a.out:my_backtrace2()+0x44
./a.out:func2()+0x9
./a.out:func()+0x9
./a.out:main()+0x9
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf5
./a.out() [0x400a09]
----------done----------

Can be seen, the function name is clearly displayed after demangle, not those weird symbols of.

Guess you like

Origin www.cnblogs.com/wangshuyi/p/12066955.html