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
	
	 Wesley Shillingford
				Wesley Shillingford