diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index c620daec..8f932e10 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -150,7 +150,7 @@ DISABLE_VS_WARNINGS(4100) {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);}} #define LOG_ERROR2(log_name, x) { \ - std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "[ERROR] Location: " << std::endl << LOCATION_SS << epee::misc_utils::print_trace() << " Message:" << std::endl << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name); LOCAL_ASSERT(0); epee::log_space::increase_error_count(LOG_DEFAULT_CHANNEL); } + std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "[ERROR] Location: " << std::endl << LOCATION_SS << epee::misc_utils::get_callstack() << " Message:" << std::endl << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name); LOCAL_ASSERT(0); epee::log_space::increase_error_count(LOG_DEFAULT_CHANNEL); } #define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name) diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 116826ce..c755d70c 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -113,7 +113,7 @@ namespace misc_utils #include #include #endif - inline std::string print_trace() + inline std::string print_trace_default() { std::stringstream ss; #if defined(__GNUC__) @@ -134,5 +134,33 @@ namespace misc_utils #endif return ss.str(); } + + typedef std::string (stack_retrieving_function_t)(); + + // + // To get stack trace call it with the defaults. + // + inline std::string get_callstack(stack_retrieving_function_t* p_stack_retrieving_function_to_be_added = nullptr, bool remove_func = false) + { + static stack_retrieving_function_t* p_srf = nullptr; + + if (remove_func) + { + p_srf = nullptr; + return ""; + } + + if (p_stack_retrieving_function_to_be_added != nullptr) + { + p_srf = p_stack_retrieving_function_to_be_added; + return ""; + } + + if (p_srf != nullptr) + return p_srf(); + + return print_trace_default(); + } + } } diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h index 72bb34b2..1b2c74a4 100644 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -126,7 +126,7 @@ namespace epee t_result result_struct = AUTO_VAL_INIT(result_struct); if( code <=0 ) { - LOG_PRINT_L2("BACKTRACE: " << ENDL << epee::misc_utils::print_trace()); + LOG_PRINT_L2("BACKTRACE: " << ENDL << epee::misc_utils::get_callstack()); LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code << "(" << epee::levin::get_err_descr(code) << ")context:" << print_connection_context(context)); TRY_ENTRY() cb(code, result_struct, context); @@ -150,7 +150,7 @@ namespace epee }, inv_timeout); if( res <=0 ) { - LOG_PRINT_L2("BACKTRACE: " << ENDL << epee::misc_utils::print_trace()); + LOG_PRINT_L2("BACKTRACE: " << ENDL << epee::misc_utils::get_callstack()); LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res << "(" << epee::levin::get_err_descr(res) << ") conn_id=" << conn_id); return false; } diff --git a/src/common/callstack_helper.cpp b/src/common/callstack_helper.cpp new file mode 100644 index 00000000..eb1be7eb --- /dev/null +++ b/src/common/callstack_helper.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2019 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") + +#pragma pack( push, before_imagehlp, 8 ) +#include +#pragma pack( pop, before_imagehlp ) + +#include "include_base_utils.h" +#include "callstack_helper.h" + +namespace +{ + struct module_data + { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; + }; + + + class get_mod_info + { + HANDLE process; + static const int buffer_length = 4096; + public: + get_mod_info(HANDLE h) : process(h) {} + + module_data operator()(HMODULE module) + { + module_data ret; + char temp[buffer_length]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameEx(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseName(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size); + return ret; + } + }; + + std::string get_symbol_undecorated_name(HANDLE process, DWORD64 address, std::stringstream& ss) + { + SYMBOL_INFO* sym; + static const int max_name_len = 1024; + std::vector sym_buffer(sizeof(SYMBOL_INFO) + max_name_len, '\0'); + sym = (SYMBOL_INFO *)sym_buffer.data(); + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = max_name_len; + + DWORD64 displacement; + if (!SymFromAddr(process, address, &displacement, sym)) + return std::string("SymFromAddr failed1: ") + epee::string_tools::num_to_string_fast(GetLastError()); + + if (*sym->Name == '\0') + return std::string("SymFromAddr failed2: ") + epee::string_tools::num_to_string_fast(GetLastError()); + + /* + ss << " SizeOfStruct : " << sym->SizeOfStruct << ENDL; + ss << " TypeIndex : " << sym->TypeIndex << ENDL; // Type Index of symbol + ss << " Index : " << sym->Index << ENDL; + ss << " Size : " << sym->Size << ENDL; + ss << " ModBase : " << sym->ModBase << ENDL; // Base Address of module comtaining this symbol + ss << " Flags : " << sym->Flags << ENDL; + ss << " Value : " << sym->Value << ENDL; // Value of symbol, ValuePresent should be 1 + ss << " Address : " << sym->Address << ENDL; // Address of symbol including base address of module + ss << " Register : " << sym->Register << ENDL; // register holding value or pointer to value + ss << " Scope : " << sym->Scope << ENDL; // scope of the symbol + ss << " Tag : " << sym->Tag << ENDL; // pdb classification + ss << " NameLen : " << sym->NameLen << ENDL; // Actual length of name + ss << " MaxNameLen : " << sym->MaxNameLen << ENDL; + ss << " Name[1] : " << &sym->Name << ENDL; // Name of symbol + */ + + std::string und_name(max_name_len, '\0'); + DWORD chars_written = UnDecorateSymbolName(sym->Name, &und_name.front(), max_name_len, UNDNAME_COMPLETE); + und_name.resize(chars_written); + return und_name; + } + +} // namespace + +namespace tools +{ + + std::string get_callstack_win_x64() + { + HANDLE h_process = GetCurrentProcess(); + HANDLE h_thread = GetCurrentThread(); + + PCSTR user_search_path = NULL; // may be path to a pdb? + if (!SymInitialize(h_process, user_search_path, false)) + return ""; + + DWORD sym_options = SymGetOptions(); + sym_options |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions(sym_options); + + DWORD cb_needed; + std::vector module_handles(1); + EnumProcessModules(h_process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cb_needed); + module_handles.resize(cb_needed / sizeof(HMODULE)); + EnumProcessModules(h_process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cb_needed); + + std::vector modules; + std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(h_process)); + void *base = modules[0].base_address; + + CONTEXT context; + memset(&context, 0, sizeof context); + RtlCaptureContext( &context ); + + STACKFRAME64 frame; + memset(&frame, 0, sizeof frame); + frame.AddrPC.Offset = context.Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Rsp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Rbp; + frame.AddrFrame.Mode = AddrModeFlat; + + IMAGEHLP_LINE64 line = { 0 }; + line.SizeOfStruct = sizeof line; + IMAGE_NT_HEADERS *image_nt_header = ImageNtHeader(base); + + std::stringstream ss; + ss << ENDL; + // ss << "main module loaded at 0x" << std::hex << std::setw(16) << std::setfill('0') << base << std::dec << " from " << modules[0].image_name << ENDL; + for (size_t n = 0; n < 250; ++n) + { + if (!StackWalk64(image_nt_header->FileHeader.Machine, h_process, h_thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + if (frame.AddrReturn.Offset == 0) + break; + + std::string fnName = get_symbol_undecorated_name(h_process, frame.AddrPC.Offset, ss); + ss << "0x" << std::setw(16) << std::setfill('0') << std::hex << frame.AddrPC.Offset << " " << std::dec << fnName; + DWORD offset_from_line = 0; + if (SymGetLineFromAddr64(h_process, frame.AddrPC.Offset, &offset_from_line, &line)) + ss << "+" << offset_from_line << " " << line.FileName << "(" << line.LineNumber << ")"; + + for (auto el : modules) + { + if ((DWORD64)el.base_address <= frame.AddrPC.Offset && frame.AddrPC.Offset < (DWORD64)el.base_address + (DWORD64)el.load_size) + { + ss << " : " << el.module_name << " @ 0x" << std::setw(0) << std::hex << (DWORD64)el.base_address << ENDL; + break; + } + } + + } + SymCleanup(h_process); + + return ss.str(); + } + +} // namespace tools + +#endif // #if defined(WIN32) diff --git a/src/common/callstack_helper.h b/src/common/callstack_helper.h new file mode 100644 index 00000000..1f3ef8e5 --- /dev/null +++ b/src/common/callstack_helper.h @@ -0,0 +1,24 @@ +// Copyright (c) 2019 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include + +namespace tools +{ +#if defined(WIN32) + extern std::string get_callstack_win_x64(); +#endif + + inline std::string get_callstack() + { +#if defined(__GNUC__) + return epee::misc_utils::print_trace(); +#elif defined(WIN32) + return get_callstack_win_x64(); +#else + return ""; +#endif + } + +} // namespace tools diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 65b58cb1..e939e06a 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -26,6 +26,7 @@ using namespace epee; #include "common/miniupnp_helper.h" #include "version.h" #include "currency_core/core_tools.h" +#include "common/callstack_helper.h" #include @@ -108,6 +109,8 @@ int main(int argc, char* argv[]) std::fflush(nullptr); // all open output streams are flushed }); + epee::misc_utils::get_callstack(tools::get_callstack); + po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options", 130, 83);