Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve stacktrace output during release_assert #2142

Merged
merged 2 commits into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions nano/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 ()
Expand Down Expand Up @@ -49,6 +49,7 @@ target_link_libraries (nano_lib
crypto_lib
blake2
${CRYPTOPP_LIBRARY}
${CMAKE_DL_LIBS}
Boost::boost)

target_compile_definitions(nano_lib
Expand Down
5 changes: 5 additions & 0 deletions nano/lib/plat/default/debugging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <nano/lib/utility.hpp>

void nano::create_load_memory_address_files ()
{
}
33 changes: 33 additions & 0 deletions nano/lib/plat/linux/debugging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <nano/lib/utility.hpp>

#include <link.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;
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);
}
2 changes: 1 addition & 1 deletion nano/lib/rpcconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ())
{
Expand Down
54 changes: 53 additions & 1 deletion nano/lib/utility.cpp
Original file line number Diff line number Diff line change
@@ -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) :
Expand Down Expand Up @@ -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
{
/*
Expand Down Expand Up @@ -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 ();
}
10 changes: 10 additions & 0 deletions nano/lib/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
51 changes: 2 additions & 49 deletions nano/nano_node/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,60 +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 <link.h>
// 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 ();
}
}

Expand Down