Add stacktrace generation to maxbase

The stacktrace generation is now a part of the maxbase library. The code
is the same code that was previously defined in gateway.cc as a part of
MaxScale.
This commit is contained in:
Markus Mäkelä 2018-06-19 14:51:09 +03:00
parent be853a448c
commit c32e6a7c3f
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
4 changed files with 204 additions and 1 deletions

View File

@ -1,4 +1,21 @@
project(maxutils)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "-fPIC -std=c++11 -ggdb -Wall -Werror -Wno-unused-function")
# Check for GLIBC. The XSI version of strerror_r return an int and the GNU version a
# char*. Depending on this check, we make assumptions about the system.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#define _GNU_SOURCE 1
#include <string.h>\n
int main(){\n
char errbuf[200];\n
return strerror_r(13, errbuf, sizeof(errbuf)) == errbuf;\n
}\n"
HAVE_GLIBC)
if(HAVE_GLIBC)
add_definitions(-DHAVE_GLIBC=1)
endif()
add_subdirectory(maxbase)

View File

@ -0,0 +1,32 @@
#pragma once
/*
* Copyright (c) 2018 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxbase/ccdefs.hh>
#include <unistd.h>
#include <cstring>
namespace maxbase
{
static inline void default_stacktrace_handler(const char* symbol, const char* command)
{
write(STDOUT_FILENO, symbol, strlen(symbol));
write(STDOUT_FILENO, ": ", 2);
write(STDOUT_FILENO, command, strlen(command));
write(STDOUT_FILENO, "\n", 1);
}
void dump_stacktrace(void (*handler)(const char* symbol, const char* command) = default_stacktrace_handler);
}

View File

@ -1,3 +1,3 @@
add_library(maxbase STATIC eventcount.cc stopwatch.cc)
add_library(maxbase STATIC eventcount.cc stopwatch.cc stacktrace.cc)
set_target_properties(maxbase PROPERTIES VERSION "1.0.0" LINK_FLAGS -Wl,-z,defs)
install(TARGETS maxbase DESTINATION lib)

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2018 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxbase/stacktrace.hh>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#ifdef HAVE_GLIBC
#include <execinfo.h>
#include <limits.h>
#include <stdarg.h>
namespace
{
static void get_command_output(char* output, size_t size, const char* format, ...)
{
va_list valist;
va_start(valist, format);
int cmd_len = vsnprintf(NULL, 0, format, valist);
va_end(valist);
va_start(valist, format);
char cmd[cmd_len + 1];
vsnprintf(cmd, cmd_len + 1, format, valist);
va_end(valist);
*output = '\0';
FILE* file = popen(cmd, "r");
if (file)
{
size_t nread = fread(output, 1, size, file);
nread = nread < size ? nread : size - 1;
output[nread--] = '\0';
// Trim trailing newlines
while (output + nread > output && output[nread] == '\n')
{
output[nread--] = '\0';
}
pclose(file);
}
}
static void extract_file_and_line(const char* symbols, char* cmd, size_t size)
{
const char* filename_end = strchr(symbols, '(');
const char* symname_end = strchr(symbols, ')');
if (filename_end && symname_end)
{
// This appears to be a symbol in a library
char filename[PATH_MAX + 1];
char symname[512];
char offset[512];
snprintf(filename, sizeof(filename), "%.*s", (int)(filename_end - symbols), symbols);
const char* symname_start = filename_end + 1;
if (*symname_start != '+')
{
// We have a string form symbol name and an offset, we need to
// extract the symbol address
const char* addr_offset = symname_start;
while (addr_offset < symname_end && *addr_offset != '+')
{
addr_offset++;
}
snprintf(symname, sizeof(symname), "%.*s", (int)(addr_offset - symname_start), symname_start);
if (addr_offset < symname_end && *addr_offset == '+')
{
addr_offset++;
}
snprintf(offset, sizeof(offset), "%.*s", (int)(symname_end - addr_offset), addr_offset);
// Get the hexadecimal address of the symbol
get_command_output(cmd, size,
"nm %s |grep ' %s$'|sed -e 's/ .*//' -e 's/^/0x/'",
filename, symname);
long long symaddr = strtoll(cmd, NULL, 16);
long long offsetaddr = strtoll(offset, NULL, 16);
// Calculate the file and line now that we have the raw offset into
// the library
get_command_output(cmd, size,
"addr2line -e %s 0x%x",
filename, symaddr + offsetaddr);
}
else
{
// Raw offset into library
symname_start++;
snprintf(symname, sizeof(symname), "%.*s", (int)(symname_end - symname_start), symname_start);
get_command_output(cmd, size, "addr2line -e %s %s", filename, symname);
}
}
}
}
namespace maxbase
{
void dump_stacktrace(void (*handler)(const char* symbol, const char* command))
{
void *addrs[128];
int count = backtrace(addrs, 128);
char** symbols = backtrace_symbols(addrs, count);
if (symbols)
{
for (int n = 0; n < count; n++)
{
char cmd[PATH_MAX + 1024] = "<not found>";
extract_file_and_line(symbols[n], cmd, sizeof(cmd));
handler(symbols[n], cmd);
}
free(symbols);
}
}
}
#else
namespace maxbase
{
void dump_stacktrace(void (*handler)(const char*, const char*))
{
// We can't dump stacktraces on non-GLIBC systems
}
}
#endif