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")
|
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")
|
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")
|
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")
|
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 ()
|
else ()
|
||||||
error ("Unknown platform: ${CMAKE_SYSTEM_NAME}")
|
error ("Unknown platform: ${CMAKE_SYSTEM_NAME}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
@ -49,6 +49,7 @@ target_link_libraries (nano_lib
|
||||||
crypto_lib
|
crypto_lib
|
||||||
blake2
|
blake2
|
||||||
${CRYPTOPP_LIBRARY}
|
${CRYPTOPP_LIBRARY}
|
||||||
|
${CMAKE_DL_LIBS}
|
||||||
Boost::boost)
|
Boost::boost)
|
||||||
|
|
||||||
target_compile_definitions(nano_lib
|
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;
|
boost::system::error_code err;
|
||||||
auto running_executable_filepath = boost::dll::program_location (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";
|
auto rpc_filepath = running_executable_filepath.parent_path () / "nano_rpc";
|
||||||
if (running_executable_filepath.has_extension ())
|
if (running_executable_filepath.has_extension ())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,27 @@
|
||||||
#include <nano/lib/utility.hpp>
|
#include <nano/lib/utility.hpp>
|
||||||
|
|
||||||
|
#include <boost/dll/runtime_symbol_info.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#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
|
namespace nano
|
||||||
{
|
{
|
||||||
seq_con_info_composite::seq_con_info_composite (const std::string & name) :
|
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;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_crash_stacktrace ()
|
||||||
|
{
|
||||||
|
boost::stacktrace::safe_dump_to ("nano_node_backtrace.dump");
|
||||||
|
}
|
||||||
|
|
||||||
namespace thread_role
|
namespace thread_role
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
@ -210,6 +235,33 @@ void release_assert_internal (bool check, const char * check_expr, const char *
|
||||||
return;
|
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 ();
|
abort ();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,16 @@ bool is_windows_elevated ();
|
||||||
*/
|
*/
|
||||||
bool event_log_reg_entry_exists ();
|
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
|
* Functions for understanding the role of the current thread
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -22,89 +22,13 @@
|
||||||
#include <boost/process.hpp>
|
#include <boost/process.hpp>
|
||||||
#endif
|
#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
|
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)
|
void my_abort_signal_handler (int signum)
|
||||||
{
|
{
|
||||||
std::signal (signum, SIG_DFL);
|
std::signal (signum, SIG_DFL);
|
||||||
boost::stacktrace::safe_dump_to ("nano_node_backtrace.dump");
|
nano::dump_crash_stacktrace ();
|
||||||
|
nano::create_load_memory_address_files ();
|
||||||
#ifdef __linux__
|
|
||||||
dl_iterate_phdr (output_memory_load_address, nullptr);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue