From 5c761256265b1ec48d02c462212d1238ddc0d6e2 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Fri, 19 Jun 2015 03:25:18 +0300 Subject: add call stacks to errors --- cxx/include/aesnixx/all.hpp | 1 + cxx/include/aesnixx/debug.hpp | 123 ++++++++++++++++++++++++++++++++++++++++++ cxx/include/aesnixx/error.hpp | 61 ++++++++++++++++++--- include/aesni/error.h | 5 ++ src/error.c | 17 ++++++ 5 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 cxx/include/aesnixx/debug.hpp diff --git a/cxx/include/aesnixx/all.hpp b/cxx/include/aesnixx/all.hpp index 72ea262..1452f68 100644 --- a/cxx/include/aesnixx/all.hpp +++ b/cxx/include/aesnixx/all.hpp @@ -10,4 +10,5 @@ #include "aes.hpp" #include "data.hpp" +#include "debug.hpp" #include "error.hpp" diff --git a/cxx/include/aesnixx/debug.hpp b/cxx/include/aesnixx/debug.hpp new file mode 100644 index 0000000..f87942d --- /dev/null +++ b/cxx/include/aesnixx/debug.hpp @@ -0,0 +1,123 @@ +/** + * \file + * \author Egor Tensin + * \date 2015 + * \copyright This file is licensed under the terms of the MIT License. + * See LICENSE.txt for details. + */ + +#pragma once + +#ifdef WIN32 +#include +#include +#pragma comment(lib, "dbghelp.lib") +#endif + +#include + +#include +#include + +namespace aesni +{ + namespace aux + { + class CallStackFormatter + { + public: + CallStackFormatter() + { + #ifdef WIN32 + m_valid = SymInitialize(GetCurrentProcess(), NULL, TRUE) ? true : false; + #endif + } + + std::string format(void* addr) const + { + #ifdef WIN32 + if (!m_valid) + return format_fallback(addr); + + DWORD64 symbol_info_buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + PSYMBOL_INFO symbol_info = (PSYMBOL_INFO) symbol_info_buf; + symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol_info->MaxNameLen = MAX_SYM_NAME; + + IMAGEHLP_MODULE64 module_info; + module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + + DWORD64 displacement_within_symbol; + + if (!SymFromAddr(GetCurrentProcess(), reinterpret_cast(addr), &displacement_within_symbol, symbol_info)) + { + if (!SymGetModuleInfo64(GetCurrentProcess(), reinterpret_cast(addr), &module_info)) + return format_fallback(addr); + + void* const displacement_within_module = reinterpret_cast(addr) - module_info.BaseOfImage; + return format_with_module(module_info.ModuleName, displacement_within_module); + } + + if (!SymGetModuleInfo64(GetCurrentProcess(), symbol_info->ModBase, &module_info)) + return format_with_symbol(symbol_info->Name, addr); + + return format_with_symbol_and_module(symbol_info->Name, module_info.ModuleName, reinterpret_cast(displacement_within_symbol)); + #else + return format_fallback(addr); + #endif + } + + ~CallStackFormatter() + { + #ifdef WIN32 + if (m_valid) + SymCleanup(GetCurrentProcess()); + #endif + } + + private: + template + static std::string put_between_brackets(const T& x) + { + std::ostringstream oss; + oss << "[" << x << "]"; + return oss.str(); + } + + template + static std::string stringify(const T& x) + { + std::ostringstream oss; + oss << x; + return oss.str(); + } + + std::string format_fallback(void* addr) const + { + return put_between_brackets(addr); + } + + std::string format_with_module(const std::string& module_name, void* displacement) const + { + if (displacement == NULL) + return put_between_brackets(module_name); + else + return put_between_brackets(module_name + "+" + stringify(displacement)); + } + + std::string format_with_symbol(const std::string& symbol_name, void* displacement) const + { + return format_with_module(symbol_name, displacement); + } + + std::string format_with_symbol_and_module(const std::string& symbol_name, const std::string& module_name, void* displacement) const + { + return format_with_symbol(module_name + "!" + symbol_name, displacement); + } + + #ifdef WIN32 + bool m_valid = false; + #endif + }; + } +} diff --git a/cxx/include/aesnixx/error.hpp b/cxx/include/aesnixx/error.hpp index 4c1fc98..013e2ef 100644 --- a/cxx/include/aesnixx/error.hpp +++ b/cxx/include/aesnixx/error.hpp @@ -8,16 +8,70 @@ #pragma once +#include "debug.hpp" + #include #include +#include +#include +#include +#include #include #include #include namespace aesni { + class Error : public std::runtime_error + { + public: + Error(const AesNI_ErrorDetails& err_details) + : std::runtime_error(format_error_message(err_details)) + { + copy_call_stack(err_details); + } + + void for_each_in_call_stack(const std::function& callback) const + { + aux::CallStackFormatter formatter; + std::for_each(m_call_stack, m_call_stack + m_call_stack_size, [&formatter, &callback] (void* addr) + { + callback(addr, formatter.format(addr)); + }); + } + + private: + static std::string format_error_message(const AesNI_ErrorDetails& err_details) + { + std::vector buf; + buf.resize(aesni_format_error(&err_details, NULL, 0)); + aesni_format_error(&err_details, buf.data(), buf.size()); + return { buf.begin(), buf.end() }; + } + + void copy_call_stack(const AesNI_ErrorDetails& err_details) + { + m_call_stack_size = err_details.call_stack_size; + std::memcpy(m_call_stack, err_details.call_stack, m_call_stack_size * sizeof(void*)); + } + + void* m_call_stack[AESNI_MAX_CALL_STACK_LENGTH]; + std::size_t m_call_stack_size; + }; + + std::ostream& operator<<(std::ostream& os, const Error& e) + { + os << "AesNI error: " << e.what() << '\n'; + os << "Call stack:\n"; + e.for_each_in_call_stack([&os] (void* addr, const std::string& name) + { + os << '\t' << addr << ' ' << name << '\n'; + }); + return os; + } + class ErrorDetailsThrowsInDestructor { public: @@ -29,12 +83,7 @@ namespace aesni ~ErrorDetailsThrowsInDestructor() { if (aesni_is_error(aesni_get_error_code(get()))) - { - std::vector msg; - msg.resize(aesni_format_error(get(), NULL, 0)); - aesni_format_error(get(), msg.data(), msg.size()); - throw std::runtime_error(std::string(msg.begin(), msg.end())); - } + throw Error(m_impl); } AesNI_ErrorDetails* get() { return &m_impl; } diff --git a/include/aesni/error.h b/include/aesni/error.h index 5aa2fb4..015b16d 100644 --- a/include/aesni/error.h +++ b/include/aesni/error.h @@ -72,6 +72,8 @@ static __inline int aesni_is_error(AesNI_StatusCode ec) */ const char* aesni_strerror(AesNI_StatusCode ec); +#define AESNI_MAX_CALL_STACK_LENGTH 32 + /** * \brief Stores error details: error code & possibly a few parameters. */ @@ -91,6 +93,9 @@ typedef struct struct { char what[128]; } not_implemented; } params; + + void* call_stack[AESNI_MAX_CALL_STACK_LENGTH]; + size_t call_stack_size; } AesNI_ErrorDetails; diff --git a/src/error.c b/src/error.c index 96f0e01..4985cca 100644 --- a/src/error.c +++ b/src/error.c @@ -115,6 +115,20 @@ size_t aesni_format_error( return err_formatters[err_details->ec](err_details, dest, dest_size); } +#ifdef WIN32 +#include + +static void aesni_collect_call_stack(AesNI_ErrorDetails* err_details) +{ + err_details->call_stack_size = CaptureStackBackTrace(1, AESNI_MAX_CALL_STACK_LENGTH, err_details->call_stack, NULL); +} +#else +static void aesni_collect_call_stack(AesNI_ErrorDetails* err_details) +{ + err_details->call_stack_size = 0; +} +#endif + static AesNI_StatusCode aesni_make_error( AesNI_ErrorDetails* err_details, AesNI_StatusCode ec) @@ -122,6 +136,9 @@ static AesNI_StatusCode aesni_make_error( if (err_details == NULL) return ec; + if (aesni_is_error(ec)) + aesni_collect_call_stack(err_details); + return err_details->ec = ec; } -- cgit v1.2.3