前置须知:
GCC编译器选项必须提供:
-g 或 -g3,并且需求 -rdynamic 编译器选项开关,可以在 Release 下面使用该函数,实现上不支持捕获其它库的详细符号信息(当然你可以改,别人库的符号如非必要必须要搞那么清楚明白)。
打印结果:
Stack Trace:
at /home/dev/Server/src/GameServer/main.cpp:50(foo2()+0x19) [0x1289562]
at /home/dev/Server/src/GameServer/main.cpp:56(foo1()+0x9) [0x128957a]
at /home/dev/Server/src/GameServer/main.cpp:79(main+0x2ab) [0x128993c]
at /lib64/libc.so.6(__libc_start_main+0xf5) [0x7ffff5832555]
at /home/dev/lib/jemalloc-5.3.0/src/prof_recent.c:460 [0xf76ffa]
捕获实现(轻量的):
void printStackTrace(int skip)
{
if (skip < 1)
{
skip = 1;
}
std::string stacktraces = captureStackTrace(skip + 1);
fprintf(stdout, "%s\n", stacktraces.data());
}
std::string extractSymbol(const char* symbol)
{
if (NULL == symbol || *symbol == '\x0')
{
return std::string();
}
const char* symbolStart = NULL;
const char* symbolEnd = NULL;
const char* strPtr = symbol;
while (*strPtr)
{
if (*strPtr == '(')
{
symbolStart = strPtr + 1;
}
elif(*strPtr == ')' && symbolStart)
{
symbolEnd = strPtr;
break;
}
strPtr++;
}
if (symbolStart && symbolEnd)
{
char symbolBuf[symbolEnd - symbolStart + 1];
strncpy(symbolBuf, symbolStart, symbolEnd - symbolStart);
symbolBuf[symbolEnd - symbolStart] = '\0';
return symbolBuf;
}
else
{
return std::string();
}
}
std::string getExecutablePath()
{
char path[8192];
ssize_t count = readlink("/proc/self/exe", path, sizeof(path));
return std::string(path, count > 0 ? count : 0);
}
std::string captureStackTrace(int skip)
{
size_t constexpr max_stackframe_size = 3000;
void* stackframe_addrs[max_stackframe_size];
if (skip < 1)
{
skip = 1;
}
size_t stackframe_size = backtrace(stackframe_addrs, max_stackframe_size);
char** stackframe_symbols = backtrace_symbols(stackframe_addrs, stackframe_size);
std::string stacktraces = "Stack Trace:";
if (NULL != stackframe_symbols)
{
std::string executable_path = getExecutablePath();
std::string default_line = "\r\n at ";
for (int i = skip; i < stackframe_size; i++)
{
char buf[8192];
sprintf(buf, "addr2line -e %s %p", executable_path.data(), stackframe_addrs[i]);
FILE* f = popen(buf, "r");
if (NULL == f)
{
continue;
}
if (fgets(buf, sizeof(buf), f))
{
int symbol_size = strlen(buf);
if (buf[symbol_size - 1] == '\n')
{
buf[--symbol_size] = '\0';
}
std::string line = default_line;
if (symbol_size > 0)
{
if (*buf != '?')
{
line += buf;
}
}
std::string symbol = extractSymbol(stackframe_symbols[i]);
if (symbol.size() > 0)
{
int status = -1;
char* demangle = NULL;
char* p = strchr(symbol.data(), '+');
if (p)
{
*p = '\x0';
demangle = abi::__cxa_demangle(symbol.data(), NULL, 0, &status);
}
else
{
p = "??";
}
if (status == 0)
{
snprintf(buf, sizeof(buf), "(%s+%s) [%p]", demangle, p + 1, stackframe_addrs[i]);
line += buf;
}
else
{
if (default_line.size() == line.size())
{
line += stackframe_symbols[i];
}
else
{
snprintf(buf, sizeof(buf), "(%s+%s) [%p]", symbol.data(), p + 1, stackframe_addrs[i]);
line += buf;
}
}
if (NULL != demangle)
{
std::free(demangle);
}
}
else
{
snprintf(buf, sizeof(buf), " [%p]", stackframe_addrs[i]);
line += buf;
}
stacktraces += line;
}
pclose(f);
}
std::free(stackframe_symbols);
}
return stacktraces;
}