CLI command to automate a crash report (#2455)

This commit is contained in:
Wesley Shillingford 2020-01-02 14:17:16 +00:00 committed by GitHub
commit ad1691acd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 238 additions and 2 deletions

View file

@ -124,7 +124,7 @@ void release_assert_internal (bool check, const char * check_expr, const char *
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
// As there is no async-signal-safe way to generate stacktraces on Windows it 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.

View file

@ -7,10 +7,12 @@
#include <nano/node/node.hpp>
#include <nano/node/testing.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <sstream>
@ -35,6 +37,27 @@
#endif
#endif
namespace
{
class uint64_from_hex // For use with boost::lexical_cast to read hexadecimal strings
{
public:
uint64_t value;
};
std::istream & operator>> (std::istream & in, uint64_from_hex & out_val);
class address_library_pair
{
public:
uint64_t address;
std::string library;
address_library_pair (uint64_t address, std::string library);
bool operator< (const address_library_pair & other) const;
bool operator== (const address_library_pair & other) const;
};
}
int main (int argc, char * const * argv)
{
nano::set_umask ();
@ -58,6 +81,7 @@ int main (int argc, char * const * argv)
("debug_opencl", "OpenCL work generation")
("debug_profile_kdf", "Profile kdf function")
("debug_output_last_backtrace_dump", "Displays the contents of the latest backtrace in the event of a nano_node crash")
("debug_generate_crash_report", "Consolidates the nano_node_backtrace.dump file. Requires addr2line installed on Linux")
("debug_sys_logging", "Test the system logger")
("debug_verify_profile", "Profile signature verification")
("debug_verify_profile_batch", "Profile batch signature verification")
@ -76,7 +100,8 @@ int main (int argc, char * const * argv)
("device", boost::program_options::value<std::string> (), "Defines <device> for OpenCL command")
("threads", boost::program_options::value<std::string> (), "Defines <threads> count for OpenCL command")
("difficulty", boost::program_options::value<std::string> (), "Defines <difficulty> for OpenCL command, HEX")
("pow_sleep_interval", boost::program_options::value<std::string> (), "Defines the amount to sleep inbetween each pow calculation attempt");
("pow_sleep_interval", boost::program_options::value<std::string> (), "Defines the amount to sleep inbetween each pow calculation attempt")
("address_column", boost::program_options::value<std::string> (), "Defines which column the addresses are located, 0 indexed (check --debug_output_last_backtrace_dump output)");
// clang-format on
nano::add_node_options (description);
nano::add_node_flag_options (description);
@ -442,6 +467,193 @@ int main (int argc, char * const * argv)
<< st << std::endl;
}
}
else if (vm.count ("debug_generate_crash_report"))
{
if (boost::filesystem::exists ("nano_node_backtrace.dump"))
{
// There is a backtrace, so output the contents
std::ifstream ifs ("nano_node_backtrace.dump");
boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump (ifs);
std::string crash_report_filename = "nano_node_crash_report.txt";
#if defined(_WIN32) || defined(__APPLE__)
// Only linux has load addresses, so just write the dump to a readable file.
// It's the best we can do to keep consistency.
std::ofstream ofs (crash_report_filename);
ofs << st;
#else
// Read all the nano node files
boost::system::error_code err;
auto running_executable_filepath = boost::dll::program_location (err);
if (!err)
{
auto num = 0;
auto format = boost::format ("nano_node_crash_load_address_dump_%1%.txt");
std::vector<address_library_pair> base_addresses;
// The first one only has the load address
uint64_from_hex base_address;
std::string line;
if (boost::filesystem::exists (boost::str (format % num)))
{
std::getline (std::ifstream (boost::str (format % num)), line);
if (boost::conversion::try_lexical_convert (line, base_address))
{
base_addresses.emplace_back (base_address.value, running_executable_filepath.string ());
}
}
++num;
// Now do the rest of the files
while (boost::filesystem::exists (boost::str (format % num)))
{
std::ifstream ifs_dump_filename (boost::str (format % num));
// 2 lines, the path to the dynamic library followed by the load address
std::string dynamic_lib_path;
std::getline (ifs_dump_filename, dynamic_lib_path);
std::getline (ifs_dump_filename, line);
if (boost::conversion::try_lexical_convert (line, base_address))
{
base_addresses.emplace_back (base_address.value, dynamic_lib_path);
}
++num;
}
std::sort (base_addresses.begin (), base_addresses.end ());
auto address_column_it = vm.find ("address_column");
auto column = -1;
if (address_column_it != vm.end ())
{
if (!boost::conversion::try_lexical_convert (address_column_it->second.as<std::string> (), column))
{
std::cerr << "Error: Invalid address column\n";
result = -1;
}
}
// Extract the addresses from the dump file.
std::stringstream stacktrace_ss;
stacktrace_ss << st;
std::vector<uint64_t> backtrace_addresses;
while (std::getline (stacktrace_ss, line))
{
std::istringstream iss (line);
std::vector<std::string> results (std::istream_iterator<std::string>{ iss }, std::istream_iterator<std::string> ());
if (column != -1)
{
if (column < results.size ())
{
uint64_from_hex address_hex;
if (boost::conversion::try_lexical_convert (results[column], address_hex))
{
backtrace_addresses.push_back (address_hex.value);
}
else
{
std::cerr << "Error: Address column does not point to valid addresses\n";
result = -1;
}
}
else
{
std::cerr << "Error: Address column too high\n";
result = -1;
}
}
else
{
for (const auto & text : results)
{
uint64_from_hex address_hex;
if (boost::conversion::try_lexical_convert (text, address_hex))
{
backtrace_addresses.push_back (address_hex.value);
break;
}
}
}
}
// Recreate the crash report with an empty file
boost::filesystem::remove (crash_report_filename);
{
std::ofstream ofs (crash_report_filename);
nano::set_secure_perm_file (crash_report_filename);
}
// Hold the results from all addr2line calls, if all fail we can assume that addr2line is not installed,
// and inform the user that it needs installing
std::vector<int> system_codes;
auto run_addr2line = [&backtrace_addresses, &base_addresses, &system_codes, &crash_report_filename](bool use_relative_addresses) {
for (auto backtrace_address : backtrace_addresses)
{
// Find the closest address to it
for (auto base_address : boost::adaptors::reverse (base_addresses))
{
if (backtrace_address > base_address.address)
{
// Addresses need to be in hex for addr2line to work
auto address = use_relative_addresses ? backtrace_address - base_address.address : backtrace_address;
std::stringstream ss;
ss << std::uppercase << std::hex << address;
// Call addr2line to convert the address into something readable.
auto res = std::system (boost::str (boost::format ("addr2line -fCi %1% -e %2% >> %3%") % ss.str () % base_address.library % crash_report_filename).c_str ());
system_codes.push_back (res);
break;
}
}
}
};
// First run addr2line using absolute addresses
run_addr2line (false);
{
std::ofstream ofs (crash_report_filename, std::ios_base::out | std::ios_base::app);
ofs << std::endl << "Using relative addresses:" << std::endl; // Add an empty line to separate the absolute & relative output
}
// Now run using relative addresses. This will give actual results for other dlls, the results from the nano_node executable.
run_addr2line (true);
if (std::find (system_codes.begin (), system_codes.end (), 0) == system_codes.end ())
{
std::cerr << "Error: Check that addr2line is installed and that nano_node_crash_load_address_dump_*.txt files exist." << std::endl;
result = -1;
}
else
{
// Delete the crash dump files. The user won't care about them after this.
num = 0;
while (boost::filesystem::exists (boost::str (format % num)))
{
boost::filesystem::remove (boost::str (format % num));
++num;
}
boost::filesystem::remove ("nano_node_backtrace.dump");
}
}
else
{
std::cerr << "Error: Could not determine running executable path" << std::endl;
result = -1;
}
#endif
}
else
{
std::cerr << "Error: nano_node_backtrace.dump could not be found";
result = -1;
}
}
else if (vm.count ("debug_verify_profile"))
{
nano::keypair key;
@ -1129,3 +1341,27 @@ int main (int argc, char * const * argv)
}
return result;
}
namespace
{
std::istream & operator>> (std::istream & in, uint64_from_hex & out_val)
{
in >> std::hex >> out_val.value;
return in;
}
address_library_pair::address_library_pair (uint64_t address, std::string library) :
address (address), library (library)
{
}
bool address_library_pair::operator< (const address_library_pair & other) const
{
return address < other.address;
}
bool address_library_pair::operator== (const address_library_pair & other) const
{
return address == other.address;
}
}