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