Improve stacktrace output during release_assert (#2142)
This commit is contained in:
parent
53384d7342
commit
7890e4e48d
7 changed files with 136 additions and 84 deletions
|
@ -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
|
||||
|
|
5
nano/lib/plat/default/debugging.cpp
Normal file
5
nano/lib/plat/default/debugging.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <nano/lib/utility.hpp>
|
||||
|
||||
void nano::create_load_memory_address_files ()
|
||||
{
|
||||
}
|
60
nano/lib/plat/linux/debugging.cpp
Normal file
60
nano/lib/plat/linux/debugging.cpp
Normal 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);
|
||||
}
|
|
@ -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 ())
|
||||
{
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue