Improve stacktrace output during release_assert (#2142)

This commit is contained in:
Wesley Shillingford 2019-07-12 13:17:06 +01:00 committed by GitHub
commit 7890e4e48d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 84 deletions

View file

@ -1,11 +1,11 @@
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set (platform_sources plat/default/priority.cpp plat/posix/perms.cpp plat/darwin/thread_role.cpp)
set (platform_sources plat/default/priority.cpp plat/posix/perms.cpp plat/darwin/thread_role.cpp plat/default/debugging.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set (platform_sources plat/windows/priority.cpp plat/windows/perms.cpp plat/windows/registry.cpp plat/windows/thread_role.cpp)
set (platform_sources plat/windows/priority.cpp plat/windows/perms.cpp plat/windows/registry.cpp plat/windows/thread_role.cpp plat/default/debugging.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (platform_sources plat/linux/priority.cpp plat/posix/perms.cpp plat/linux/thread_role.cpp)
set (platform_sources plat/linux/priority.cpp plat/posix/perms.cpp plat/linux/thread_role.cpp plat/linux/debugging.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
set (platform_sources plat/default/priority.cpp plat/posix/perms.cpp plat/freebsd/thread_role.cpp)
set (platform_sources plat/default/priority.cpp plat/posix/perms.cpp plat/freebsd/thread_role.cpp plat/plat/default/debugging.cpp)
else ()
error ("Unknown platform: ${CMAKE_SYSTEM_NAME}")
endif ()
@ -49,6 +49,7 @@ target_link_libraries (nano_lib
crypto_lib
blake2
${CRYPTOPP_LIBRARY}
${CMAKE_DL_LIBS}
Boost::boost)
target_compile_definitions(nano_lib

View file

@ -0,0 +1,5 @@
#include <nano/lib/utility.hpp>
void nano::create_load_memory_address_files ()
{
}

View file

@ -0,0 +1,60 @@
#include <nano/lib/utility.hpp>
#include <fcntl.h>
#include <link.h>
#include <sys/stat.h>
#include <unistd.h>
namespace
{
// This creates a file for the load address of an executable or shared library.
// Useful for debugging should the virtual addresses be randomized.
int create_load_memory_address_file (dl_phdr_info * info, size_t, void *)
{
static int counter = 0;
assert (counter <= 99);
// Create filename
const char file_prefix[] = "nano_node_crash_load_address_dump_";
// Holds the filename prefix, a unique (max 2 digits) number and extension (null terminator is included in file_prefix size)
char filename[sizeof (file_prefix) + 2 + 4];
snprintf (filename, sizeof (filename), "%s%d.txt", file_prefix, counter);
// Open file
const auto file_descriptor = ::open (filename, O_CREAT | O_WRONLY | O_TRUNC,
#if defined(S_IWRITE) && defined(S_IREAD)
S_IWRITE | S_IREAD
#else
0
#endif
);
// Write the name of shared library
::write (file_descriptor, "Name: ", 6);
::write (file_descriptor, info->dlpi_name, strlen (info->dlpi_name));
::write (file_descriptor, "\n", 1);
// Write the first load address found
for (auto i = 0; i < info->dlpi_phnum; ++i)
{
if (info->dlpi_phdr[i].p_type == PT_LOAD)
{
auto load_address = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
// Each byte of the pointer is two hexadecimal characters, plus the 0x prefix and null terminator
char load_address_as_hex_str[sizeof (load_address) * 2 + 2 + 1];
snprintf (load_address_as_hex_str, sizeof (load_address_as_hex_str), "%p", (void *)load_address);
::write (file_descriptor, load_address_as_hex_str, strlen (load_address_as_hex_str));
break;
}
}
::close (file_descriptor);
++counter;
return 0;
}
}
void nano::create_load_memory_address_files ()
{
dl_iterate_phdr (create_load_memory_address_file, nullptr);
}

View file

@ -117,7 +117,7 @@ std::string get_default_rpc_filepath ()
boost::system::error_code err;
auto running_executable_filepath = boost::dll::program_location (err);
// Construct the nano_rpc excutable file path based on where the currently running executable is found.
// Construct the nano_rpc executable file path based on where the currently running executable is found.
auto rpc_filepath = running_executable_filepath.parent_path () / "nano_rpc";
if (running_executable_filepath.has_extension ())
{

View file

@ -1,7 +1,27 @@
#include <nano/lib/utility.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <iostream>
// Some builds (mac) fail due to "Boost.Stacktrace requires `_Unwind_Backtrace` function".
#ifndef _WIN32
#ifndef _GNU_SOURCE
#define BEFORE_GNU_SOURCE 0
#define _GNU_SOURCE
#else
#define BEFORE_GNU_SOURCE 1
#endif
#endif
// On Windows this include defines min/max macros, so keep below other includes
// to reduce conflicts with other std functions
#include <boost/stacktrace.hpp>
#ifndef _WIN32
#if !BEFORE_GNU_SOURCE
#undef _GNU_SOURCE
#endif
#endif
namespace nano
{
seq_con_info_composite::seq_con_info_composite (const std::string & name) :
@ -42,6 +62,11 @@ const seq_con_info & seq_con_info_leaf::get_info () const
return info;
}
void dump_crash_stacktrace ()
{
boost::stacktrace::safe_dump_to ("nano_node_backtrace.dump");
}
namespace thread_role
{
/*
@ -210,6 +235,33 @@ void release_assert_internal (bool check, const char * check_expr, const char *
return;
}
std::cerr << "Assertion (" << check_expr << ") failed " << file << ":" << line << std::endl;
std::cerr << "Assertion (" << check_expr << ") failed " << file << ":" << line << "\n\n";
// Output stack trace to cerr
auto stacktrace = boost::stacktrace::stacktrace ();
std::stringstream ss;
ss << stacktrace;
auto backtrace_str = ss.str ();
std::cerr << backtrace_str << std::endl;
// "abort" at the end of this function will go into any signal handlers (the daemon ones will generate a stack trace and load memory address files on non-Windows systems).
// As there is no async-signal-safe way to generate stacktraces on Windows so must be done before aborting
#ifdef _WIN32
{
// Try construct the stacktrace dump in the same folder as the the running executable, otherwise use the current directory.
boost::system::error_code err;
auto running_executable_filepath = boost::dll::program_location (err);
std::string filename = "nano_node_backtrace_release_assert.txt";
std::string filepath = filename;
if (!err)
{
filepath = (running_executable_filepath.parent_path () / filename).string ();
}
std::ofstream file (filepath);
nano::set_secure_perm_file (filepath);
file << backtrace_str;
}
#endif
abort ();
}

View file

@ -77,6 +77,16 @@ bool is_windows_elevated ();
*/
bool event_log_reg_entry_exists ();
/*
* Create the load memory addresses for the executable and shared libraries.
*/
void create_load_memory_address_files ();
/*
* Dumps a stacktrace file which can be read using the --debug_output_last_backtrace_dump CLI command
*/
void dump_crash_stacktrace ();
/*
* Functions for understanding the role of the current thread
*/

View file

@ -22,89 +22,13 @@
#include <boost/process.hpp>
#endif
// Some builds (mac) fail due to "Boost.Stacktrace requires `_Unwind_Backtrace` function".
#ifndef _WIN32
#ifndef _GNU_SOURCE
#define BEFORE_GNU_SOURCE 0
#define _GNU_SOURCE
#else
#define BEFORE_GNU_SOURCE 1
#endif
#endif
// On Windows this include defines min/max macros, so keep below other includes
// to reduce conflicts with other std functions
#include <boost/stacktrace.hpp>
#ifndef _WIN32
#if !BEFORE_GNU_SOURCE
#undef _GNU_SOURCE
#endif
#endif
namespace
{
#ifdef __linux__
#include <fcntl.h>
#include <link.h>
#include <sys/stat.h>
#include <unistd.h>
// This outputs the load addresses for the executable and shared libraries.
// Useful for debugging should the virtual addresses be randomized.
int output_memory_load_address (dl_phdr_info * info, size_t, void *)
{
static int counter = 0;
assert (counter <= 99);
// Create filename
const char file_prefix[] = "nano_node_crash_load_address_dump_";
// Holds the filename prefix, a unique (max 2 digits) number and extension (null terminator is included in file_prefix size)
char filename[sizeof (file_prefix) + 2 + 4];
snprintf (filename, sizeof (filename), "%s%d.txt", file_prefix, counter);
// Open file
const auto file_descriptor = ::open (filename, O_CREAT | O_WRONLY | O_TRUNC,
#if defined(S_IWRITE) && defined(S_IREAD)
S_IWRITE | S_IREAD
#else
0
#endif
);
// Write the name of shared library
::write (file_descriptor, "Name: ", 6);
::write (file_descriptor, info->dlpi_name, strlen (info->dlpi_name));
::write (file_descriptor, "\n", 1);
// Write the first load address found
for (auto i = 0; i < info->dlpi_phnum; ++i)
{
if (info->dlpi_phdr[i].p_type == PT_LOAD)
{
auto load_address = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
// Each byte of the pointer is two hexadecimal characters, plus the 0x prefix and null terminator
char load_address_as_hex_str[sizeof (load_address) * 2 + 2 + 1];
snprintf (load_address_as_hex_str, sizeof (load_address_as_hex_str), "%p", (void *)load_address);
::write (file_descriptor, load_address_as_hex_str, strlen (load_address_as_hex_str));
break;
}
}
::close (file_descriptor);
++counter;
return 0;
}
#endif
// Only async-signal-safe functions are allowed to be called here
void my_abort_signal_handler (int signum)
{
std::signal (signum, SIG_DFL);
boost::stacktrace::safe_dump_to ("nano_node_backtrace.dump");
#ifdef __linux__
dl_iterate_phdr (output_memory_load_address, nullptr);
#endif
nano::dump_crash_stacktrace ();
nano::create_load_memory_address_files ();
}
}