diff options
Diffstat (limited to 'include/winapi')
-rw-r--r-- | include/winapi/debug.hpp | 13 | ||||
-rw-r--r-- | include/winapi/debug/address.hpp | 34 | ||||
-rw-r--r-- | include/winapi/debug/call_stack.hpp | 53 | ||||
-rw-r--r-- | include/winapi/debug/dbghelp.hpp | 72 | ||||
-rw-r--r-- | include/winapi/debug/module.hpp | 54 | ||||
-rw-r--r-- | include/winapi/debug/repo.hpp | 58 | ||||
-rw-r--r-- | include/winapi/debug/symbol.hpp | 97 |
7 files changed, 381 insertions, 0 deletions
diff --git a/include/winapi/debug.hpp b/include/winapi/debug.hpp new file mode 100644 index 0000000..dc42557 --- /dev/null +++ b/include/winapi/debug.hpp @@ -0,0 +1,13 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "debug/address.hpp" +#include "debug/call_stack.hpp" +#include "debug/dbghelp.hpp" +#include "debug/module.hpp" +#include "debug/repo.hpp" +#include "debug/symbol.hpp" diff --git a/include/winapi/debug/address.hpp b/include/winapi/debug/address.hpp new file mode 100644 index 0000000..3d7acd6 --- /dev/null +++ b/include/winapi/debug/address.hpp @@ -0,0 +1,34 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include <windows.h> + +#include <sstream> +#include <string> + +namespace pdb { + +typedef DWORD64 Address; + +inline std::string format_address(Address address) { + std::ostringstream oss; + oss << std::hex << std::showbase << address; + return oss.str(); +} + +inline std::string format_address(void* address) { + return format_address(reinterpret_cast<Address>(address)); +} + +inline bool parse_address(Address& dest, const std::string& src) { + std::istringstream iss{src}; + iss >> std::hex; + char c; + return iss >> dest && !iss.get(c); +} + +} // namespace pdb diff --git a/include/winapi/debug/call_stack.hpp b/include/winapi/debug/call_stack.hpp new file mode 100644 index 0000000..30f642c --- /dev/null +++ b/include/winapi/debug/call_stack.hpp @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "address.hpp" +#include "dbghelp.hpp" + +#include <windows.h> + +#include <array> +#include <cstddef> +#include <functional> +#include <ostream> +#include <string> + +namespace pdb { + +class CallStack { +public: + static constexpr std::size_t frames_to_skip = 0; + static constexpr std::size_t frames_to_capture = 62; + + // Imposed by CaptureStackBackTrace: + static constexpr std::size_t max_length = 62; + + static_assert(frames_to_skip + frames_to_capture <= max_length, + "Call stack length is too large"); + + static CallStack capture(); + + using AddressCallback = std::function<bool(Address)>; + bool for_each_address(const AddressCallback& callback) const; + + static std::string pretty_print_address(const DbgHelp& dbghelp, Address addr); + + void dump(std::ostream& os, const DbgHelp&) const; + + const std::array<Address, max_length> frames; + const std::size_t length; + + const Address* begin() const { return frames.data(); } + const Address* cbegin() const { return begin(); } + const Address* end() const { return begin() + length; } + const Address* cend() const { return end(); } + +private: + CallStack() = default; +}; + +} // namespace pdb diff --git a/include/winapi/debug/dbghelp.hpp b/include/winapi/debug/dbghelp.hpp new file mode 100644 index 0000000..6965823 --- /dev/null +++ b/include/winapi/debug/dbghelp.hpp @@ -0,0 +1,72 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "address.hpp" +#include "module.hpp" +#include "symbol.hpp" + +#include <windows.h> + +#include <functional> +#include <string> + +namespace pdb { + +class DbgHelp { +public: + static DbgHelp current_process() { return DbgHelp{true}; } + static DbgHelp post_mortem() { return DbgHelp{false}; } + + void swap(DbgHelp& other) noexcept; + + DbgHelp(DbgHelp&& other) noexcept; + DbgHelp& operator=(DbgHelp) noexcept; + ~DbgHelp(); + + ModuleInfo load_pdb(const std::string& path) const; + + typedef std::function<void(const ModuleInfo&)> OnModule; + void enum_modules(const OnModule&) const; + + ModuleInfo resolve_module(Address) const; + + typedef std::function<void(const SymbolInfo&)> OnSymbol; + static constexpr auto all_symbols = "*!*"; + void enum_symbols(const ModuleInfo&, const std::string& mask, const OnSymbol&) const; + void enum_symbols(const ModuleInfo&, const OnSymbol&) const; + void enum_symbols(const std::string& mask, const OnSymbol&) const; + void enum_symbols(const OnSymbol&) const; + + SymbolInfo resolve_symbol(Address) const; + SymbolInfo resolve_symbol(const std::string&) const; + + LineInfo resolve_line(Address) const; + +private: + explicit DbgHelp(bool invade_current_process); + + void close(); + + HANDLE id = NULL; + + DbgHelp(const DbgHelp&) = delete; +}; + +inline void swap(DbgHelp& a, DbgHelp& b) noexcept { + a.swap(b); +} + +} // namespace pdb + +namespace std { + +template <> +inline void swap(pdb::DbgHelp& a, pdb::DbgHelp& b) noexcept { + a.swap(b); +} + +} // namespace std diff --git a/include/winapi/debug/module.hpp b/include/winapi/debug/module.hpp new file mode 100644 index 0000000..5cfa69d --- /dev/null +++ b/include/winapi/debug/module.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "address.hpp" + +#include <dbghelp.h> +#include <windows.h> + +#include <string> + +namespace pdb { + +class ModuleInfo { +public: + typedef IMAGEHLP_MODULEW64 Impl; + + ModuleInfo(); + explicit ModuleInfo(const Impl& impl); + + explicit operator Impl&() { return impl; } + explicit operator const Impl&() const { return impl; } + + Address get_offline_base() const { return impl.BaseOfImage; } + + std::string get_name() const; + +private: + static Impl create_impl(); + + Impl impl; +}; + +class Module : public ModuleInfo { +public: + Module(Address online_base, const ModuleInfo& info) + : ModuleInfo{info}, online_base{online_base} {} + + Address get_online_base() const { return online_base; } + + Address translate_offline_address(Address offline) const; + Address translate_online_address(Address online) const; + +private: + std::string invalid_offline_address(Address offline) const; + std::string invalid_online_address(Address online) const; + + const Address online_base; +}; + +} // namespace pdb diff --git a/include/winapi/debug/repo.hpp b/include/winapi/debug/repo.hpp new file mode 100644 index 0000000..3b194ff --- /dev/null +++ b/include/winapi/debug/repo.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "address.hpp" +#include "dbghelp.hpp" +#include "module.hpp" +#include "symbol.hpp" + +#include <winapi/file.hpp> + +#include <functional> +#include <map> +#include <string> +#include <unordered_set> + +namespace pdb { + +class Repo { +public: + Repo() = default; + + Address add_pdb(Address online_base, const std::string& path); + + typedef std::function<void(const Symbol&)> OnSymbol; + void enum_symbols(const OnSymbol&) const; + void enum_symbols(Address offline_base, const OnSymbol&) const; + void enum_symbols(const Module&, const OnSymbol&) const; + + Symbol resolve_symbol(Address) const; + Symbol resolve_symbol(const std::string&) const; + + LineInfo resolve_line(Address) const; + + const Module& module_with_online_base(Address) const; + const Module& module_with_offline_base(Address) const; + +private: + Symbol symbol_from_buffer(const SymbolInfo&) const; + static Symbol symbol_from_buffer(const Module&, const SymbolInfo&); + + const Module& module_from_online_address(Address) const; + const Module& module_from_offline_address(Address) const; + + Address address_offline_to_online(Address) const; + Address address_online_to_offline(Address) const; + + const DbgHelp dbghelp{DbgHelp::post_mortem()}; + + std::unordered_set<winapi::File::ID> file_ids; + std::map<Address, Module> online_bases; + std::map<Address, const Module&> offline_bases; +}; + +} // namespace pdb diff --git a/include/winapi/debug/symbol.hpp b/include/winapi/debug/symbol.hpp new file mode 100644 index 0000000..47e4fb1 --- /dev/null +++ b/include/winapi/debug/symbol.hpp @@ -0,0 +1,97 @@ +// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-debug" project. +// For details, see https://github.com/egor-tensin/winapi-debug. +// Distributed under the MIT License. + +#pragma once + +#include "address.hpp" +#include "module.hpp" + +#include <dbghelp.h> +#include <windows.h> + +#include <array> +#include <climits> +#include <cstddef> +#include <string> +#include <type_traits> + +namespace pdb { +namespace symbol { + +// MinGW-w64 (as of version 7.0) doesn't have SymTagEnum +typedef ULONG Tag; + +constexpr Tag SYM_TAG_FUNCTION = 5; + +#ifdef _MSC_VER +static_assert(static_cast<Tag>(SymTagFunction) == SYM_TAG_FUNCTION, + "unexpected SymTagFunction value"); +#endif + +} // namespace symbol + +class SymbolInfo { +public: + typedef SYMBOL_INFOW Impl; + + SymbolInfo(); + explicit SymbolInfo(const Impl& impl); + + explicit operator Impl&() { return get_impl(); } + explicit operator const Impl&() const { return get_impl(); } + + Address get_displacement() const { return displacement; } + void set_displacement(Address new_value) { displacement = new_value; } + + std::string get_name() const; + + Address get_offline_base() const { return get_impl().ModBase; } + Address get_offline_address() const { return get_impl().Address; } + + symbol::Tag get_tag() const { return get_impl().Tag; } + + enum class Type : symbol::Tag { + Function = symbol::SYM_TAG_FUNCTION, + RESERVED = ULONG_MAX, + }; + + Type get_type() const { return static_cast<Type>(get_tag()); } + + bool is_function() const { return get_type() == Type::Function; } + +private: + static constexpr std::size_t char_size = sizeof(std::remove_extent<decltype(Impl::Name)>::type); + static_assert(char_size == sizeof(wchar_t), "Aren't we using the wide WinAPI?"); + static constexpr std::size_t max_buffer_size = sizeof(Impl) + (MAX_SYM_NAME - 1) * char_size; + + std::array<unsigned char, max_buffer_size> buffer; + Address displacement = 0; + + const Impl& get_impl() const { return *reinterpret_cast<const Impl*>(buffer.data()); } + Impl& get_impl() { return *reinterpret_cast<Impl*>(buffer.data()); } +}; + +class Symbol : public SymbolInfo { +public: + Symbol(Address online_address, const SymbolInfo& info) + : SymbolInfo{info}, online_address{online_address} {} + + Address get_online_address() const { return online_address; } + +private: + const Address online_address; +}; + +class LineInfo { +public: + typedef IMAGEHLP_LINEW64 Impl; + + explicit LineInfo(const Impl& impl); + + const std::string file_path; + const unsigned long line_number; +}; + +} // namespace pdb |