From 16da716231fe3f73f378cecd297dfaf0b34da6be Mon Sep 17 00:00:00 2001 From: wezrule Date: Wed, 10 Jul 2019 15:27:18 +0100 Subject: [PATCH] Improve stacktrace output during release_assert --- nano/lib/CMakeLists.txt | 9 ++--- nano/lib/plat/default/debugging.cpp | 5 +++ nano/lib/plat/linux/debugging.cpp | 33 ++++++++++++++++++ nano/lib/rpcconfig.cpp | 2 +- nano/lib/utility.cpp | 54 ++++++++++++++++++++++++++++- nano/lib/utility.hpp | 10 ++++++ nano/nano_node/daemon.cpp | 51 ++------------------------- 7 files changed, 109 insertions(+), 55 deletions(-) create mode 100644 nano/lib/plat/default/debugging.cpp create mode 100644 nano/lib/plat/linux/debugging.cpp diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 4f6dc2b785..397d46c4aa 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -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 diff --git a/nano/lib/plat/default/debugging.cpp b/nano/lib/plat/default/debugging.cpp new file mode 100644 index 0000000000..1489fa80ac --- /dev/null +++ b/nano/lib/plat/default/debugging.cpp @@ -0,0 +1,5 @@ +#include + +void nano::create_load_memory_address_files () +{ +} diff --git a/nano/lib/plat/linux/debugging.cpp b/nano/lib/plat/linux/debugging.cpp new file mode 100644 index 0000000000..110ca2cb2e --- /dev/null +++ b/nano/lib/plat/linux/debugging.cpp @@ -0,0 +1,33 @@ +#include + +#include +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; + std::ostringstream ss; + ss << "nano_node_crash_load_address_dump_" << counter << ".txt"; + std::ofstream file (ss.str ()); + file << "Name: " << info->dlpi_name << "\n"; + + for (auto i = 0; i < info->dlpi_phnum; ++i) + { + // Only care about the first load address + if (info->dlpi_phdr[i].p_type == PT_LOAD) + { + file << std::hex << (void *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + break; + } + } + ++counter; + return 0; +} +} + +void nano::create_load_memory_address_files () +{ + dl_iterate_phdr (create_load_memory_address_file, nullptr); +} diff --git a/nano/lib/rpcconfig.cpp b/nano/lib/rpcconfig.cpp index 48c8a8b10f..00bfe44737 100644 --- a/nano/lib/rpcconfig.cpp +++ b/nano/lib/rpcconfig.cpp @@ -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 ()) { diff --git a/nano/lib/utility.cpp b/nano/lib/utility.cpp index 637e4ef170..a8c394a051 100644 --- a/nano/lib/utility.cpp +++ b/nano/lib/utility.cpp @@ -1,7 +1,27 @@ #include +#include + #include +// 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 +#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 (); } diff --git a/nano/lib/utility.hpp b/nano/lib/utility.hpp index 770929fd14..6c3c140d8e 100644 --- a/nano/lib/utility.hpp +++ b/nano/lib/utility.hpp @@ -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 */ diff --git a/nano/nano_node/daemon.cpp b/nano/nano_node/daemon.cpp index 1d4f6ede29..af365ddec9 100644 --- a/nano/nano_node/daemon.cpp +++ b/nano/nano_node/daemon.cpp @@ -22,60 +22,13 @@ #include #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 -#ifndef _WIN32 -#if !BEFORE_GNU_SOURCE -#undef _GNU_SOURCE -#endif -#endif - namespace { -#ifdef __linux__ -#include -// Only on linux. 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; - std::ostringstream ss; - ss << "nano_node_crash_load_address_dump_" << counter << ".txt"; - std::ofstream file (ss.str ()); - file << "Name: " << info->dlpi_name << "\n"; - - for (auto i = 0; i < info->dlpi_phnum; ++i) - { - // Only care about the first load address - if (info->dlpi_phdr[i].p_type == PT_LOAD) - { - file << std::hex << (void *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); - break; - } - } - ++counter; - return 0; -} -#endif - 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 (); } }