forked from lthn/blockchain
an attempt to make all callstack-retrieval routines exception-safe
This commit is contained in:
parent
686c137332
commit
8a91bccea3
3 changed files with 195 additions and 153 deletions
|
|
@ -114,26 +114,33 @@ namespace misc_utils
|
|||
#include <execinfo.h>
|
||||
#include <boost/core/demangle.hpp>
|
||||
#endif
|
||||
inline std::string print_trace_default()
|
||||
inline std::string print_trace_default() noexcept
|
||||
{
|
||||
std::stringstream ss;
|
||||
try
|
||||
{
|
||||
std::stringstream ss;
|
||||
#if defined(__GNUC__) && !defined(__ANDROID__)
|
||||
ss << std::endl << "STACK" << std::endl;
|
||||
const size_t max_depth = 100;
|
||||
size_t stack_depth;
|
||||
void *stack_addrs[max_depth];
|
||||
char **stack_strings;
|
||||
ss << std::endl << "STACK" << std::endl;
|
||||
const size_t max_depth = 100;
|
||||
size_t stack_depth;
|
||||
void *stack_addrs[max_depth];
|
||||
char **stack_strings;
|
||||
|
||||
stack_depth = backtrace(stack_addrs, max_depth);
|
||||
stack_strings = backtrace_symbols(stack_addrs, stack_depth);
|
||||
stack_depth = backtrace(stack_addrs, max_depth);
|
||||
stack_strings = backtrace_symbols(stack_addrs, stack_depth);
|
||||
|
||||
for (size_t i = 1; i < stack_depth; i++)
|
||||
{
|
||||
ss << boost::core::demangle(stack_strings[i]) << std::endl;
|
||||
}
|
||||
free(stack_strings); // malloc()ed by backtrace_symbols
|
||||
for (size_t i = 1; i < stack_depth; i++)
|
||||
{
|
||||
ss << boost::core::demangle(stack_strings[i]) << std::endl;
|
||||
}
|
||||
free(stack_strings); // malloc()ed by backtrace_symbols
|
||||
#endif
|
||||
return ss.str();
|
||||
return ss.str();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return std::string("(no callstack due to an exception)");
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::string (stack_retrieving_function_t)();
|
||||
|
|
@ -141,27 +148,35 @@ namespace misc_utils
|
|||
//
|
||||
// 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)
|
||||
inline std::string get_callstack(stack_retrieving_function_t* p_stack_retrieving_function_to_be_added = nullptr, bool remove_func = false) noexcept
|
||||
{
|
||||
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();
|
||||
try
|
||||
{
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
return print_trace_default();
|
||||
if (p_srf != nullptr)
|
||||
return p_srf();
|
||||
|
||||
return print_trace_default();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return std::string("(no callstack due to an exception)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace misc_utils
|
||||
} // namespace epee
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2019 Zano Project
|
||||
// Copyright (c) 2019-2025 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
|
@ -38,64 +38,84 @@ namespace
|
|||
public:
|
||||
get_mod_info(HANDLE h) : process(h) {}
|
||||
|
||||
module_data operator()(HMODULE module)
|
||||
module_data operator()(HMODULE module) noexcept
|
||||
{
|
||||
module_data ret;
|
||||
char temp[buffer_length];
|
||||
MODULEINFO mi;
|
||||
module_data ret{};
|
||||
try
|
||||
{
|
||||
char temp[buffer_length];
|
||||
MODULEINFO mi;
|
||||
|
||||
GetModuleInformation(process, module, &mi, sizeof(mi));
|
||||
ret.base_address = mi.lpBaseOfDll;
|
||||
ret.load_size = mi.SizeOfImage;
|
||||
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<char> img(ret.image_name.begin(), ret.image_name.end());
|
||||
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
|
||||
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
|
||||
GetModuleFileNameEx(process, module, temp, sizeof(temp));
|
||||
ret.image_name = temp;
|
||||
GetModuleBaseName(process, module, temp, sizeof(temp));
|
||||
ret.module_name = temp;
|
||||
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
|
||||
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
|
||||
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
ret = module_data{};
|
||||
ret.module_name = std::string("!!std::exception ") + e.what();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ret = module_data{};
|
||||
ret.module_name = std::string("!!exception");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
std::string get_symbol_undecorated_name(HANDLE process, DWORD64 address, std::stringstream& ss)
|
||||
std::string get_symbol_undecorated_name(HANDLE process, DWORD64 address, std::stringstream& ss) noexcept
|
||||
{
|
||||
SYMBOL_INFO* sym;
|
||||
static const int max_name_len = 1024;
|
||||
std::vector<char> 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;
|
||||
try
|
||||
{
|
||||
SYMBOL_INFO* sym;
|
||||
static const int max_name_len = 1024;
|
||||
std::vector<char> 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());
|
||||
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());
|
||||
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
|
||||
*/
|
||||
/*
|
||||
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;
|
||||
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;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return std::string("!!exception");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -103,86 +123,93 @@ namespace
|
|||
namespace tools
|
||||
{
|
||||
|
||||
std::string get_callstack_win_x64()
|
||||
std::string get_callstack_win_x64() noexcept
|
||||
{
|
||||
// @TODO@
|
||||
// static epee::static_helpers::wrapper<std::recursive_mutex> cs;
|
||||
static std::recursive_mutex cs;
|
||||
std::lock_guard<std::recursive_mutex> lock(cs);
|
||||
|
||||
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 "<win callstack error 1>";
|
||||
|
||||
DWORD sym_options = SymGetOptions();
|
||||
sym_options |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
|
||||
SymSetOptions(sym_options);
|
||||
|
||||
DWORD cb_needed;
|
||||
std::vector<HMODULE> module_handles(1);
|
||||
EnumProcessModules(h_process, &module_handles[0], static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)), &cb_needed);
|
||||
module_handles.resize(cb_needed / sizeof(HMODULE));
|
||||
EnumProcessModules(h_process, &module_handles[0], static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)), &cb_needed);
|
||||
|
||||
std::vector<module_data> 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);
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
#endif
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
#endif
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
#endif
|
||||
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)
|
||||
try
|
||||
{
|
||||
if (!StackWalk64(image_nt_header->FileHeader.Machine, h_process, h_thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
||||
break;
|
||||
if (frame.AddrReturn.Offset == 0)
|
||||
break;
|
||||
// @TODO@
|
||||
// static epee::static_helpers::wrapper<std::recursive_mutex> cs;
|
||||
static std::recursive_mutex cs;
|
||||
std::lock_guard<std::recursive_mutex> lock(cs);
|
||||
|
||||
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 << ")";
|
||||
HANDLE h_process = GetCurrentProcess();
|
||||
HANDLE h_thread = GetCurrentThread();
|
||||
|
||||
for (auto el : modules)
|
||||
PCSTR user_search_path = NULL; // may be path to a pdb?
|
||||
if (!SymInitialize(h_process, user_search_path, false))
|
||||
return "<win callstack error 1>";
|
||||
|
||||
DWORD sym_options = SymGetOptions();
|
||||
sym_options |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
|
||||
SymSetOptions(sym_options);
|
||||
|
||||
DWORD cb_needed;
|
||||
std::vector<HMODULE> module_handles(1);
|
||||
EnumProcessModules(h_process, &module_handles[0], static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)), &cb_needed);
|
||||
module_handles.resize(cb_needed / sizeof(HMODULE));
|
||||
EnumProcessModules(h_process, &module_handles[0], static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)), &cb_needed);
|
||||
|
||||
std::vector<module_data> 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);
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
#endif
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
#endif
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#ifndef _M_ARM64
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
#endif
|
||||
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 ((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;
|
||||
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();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return std::string("(no callstack due to an exception)");
|
||||
}
|
||||
SymCleanup(h_process);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2019 Zano Project
|
||||
// Copyright (c) 2019-2025 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
|
||||
|
|
@ -7,10 +7,10 @@
|
|||
namespace tools
|
||||
{
|
||||
#if defined(WIN32)
|
||||
extern std::string get_callstack_win_x64();
|
||||
extern std::string get_callstack_win_x64() noexcept;
|
||||
#endif
|
||||
|
||||
inline std::string get_callstack()
|
||||
inline std::string get_callstack() noexcept
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
return epee::misc_utils::print_trace_default();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue