Skip to content

Commit 116b86c

Browse files
committed
Add forgotten stack trace helpers file from 5c9f4b8
1 parent f234efd commit 116b86c

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

Client/core/StackTraceHelpers.h

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: core/StackTraceHelpers.h
6+
* PURPOSE: Crash handler component
7+
*
8+
* Multi Theft Auto is available from https://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include <array>
15+
#include <cstdint>
16+
#include <format>
17+
#include <optional>
18+
#include <ranges>
19+
#include <span>
20+
#include <string>
21+
#include <string_view>
22+
23+
#include <windows.h>
24+
#include <DbgHelp.h>
25+
26+
namespace StackTraceHelpers
27+
{
28+
struct StackWalkRoutines
29+
{
30+
PREAD_PROCESS_MEMORY_ROUTINE64 readMemory = nullptr;
31+
PFUNCTION_TABLE_ACCESS_ROUTINE64 functionTableAccess = nullptr;
32+
PGET_MODULE_BASE_ROUTINE64 moduleBase = nullptr;
33+
34+
bool operator==(const StackWalkRoutines&) const noexcept = default;
35+
};
36+
37+
inline BOOL __stdcall LocalReadProcessMemory(HANDLE /*process*/, DWORD64 baseAddress, PVOID buffer, DWORD size, LPDWORD bytesRead) noexcept
38+
{
39+
if (buffer == nullptr || size == 0) [[unlikely]]
40+
{
41+
if (bytesRead)
42+
*bytesRead = 0;
43+
return FALSE;
44+
}
45+
46+
if (baseAddress > static_cast<DWORD64>(UINT32_MAX)) [[unlikely]]
47+
{
48+
if (bytesRead)
49+
*bytesRead = 0;
50+
return FALSE;
51+
}
52+
53+
__try
54+
{
55+
const auto srcAddr = static_cast<std::uint32_t>(baseAddress);
56+
const auto srcPtr = reinterpret_cast<const std::byte*>(static_cast<std::uintptr_t>(srcAddr));
57+
const auto dest = std::span{reinterpret_cast<std::byte*>(buffer), size};
58+
const auto srcSpan = std::span{srcPtr, size};
59+
std::ranges::copy(srcSpan, dest.begin());
60+
if (bytesRead)
61+
*bytesRead = size;
62+
return TRUE;
63+
}
64+
__except (EXCEPTION_EXECUTE_HANDLER)
65+
{
66+
if (bytesRead)
67+
*bytesRead = 0;
68+
return FALSE;
69+
}
70+
}
71+
72+
inline DWORD64 __stdcall LocalGetModuleBase(HANDLE /*process*/, DWORD64 address) noexcept
73+
{
74+
constexpr DWORD64 max32BitAddress = static_cast<DWORD64>(UINT32_MAX);
75+
if (address > max32BitAddress) [[unlikely]]
76+
return 0;
77+
78+
MEMORY_BASIC_INFORMATION mbi{};
79+
const auto addr32 = static_cast<std::uint32_t>(address);
80+
if (VirtualQuery(reinterpret_cast<LPCVOID>(static_cast<std::uintptr_t>(addr32)), &mbi, sizeof(mbi)) == 0) [[unlikely]]
81+
return 0;
82+
83+
if (mbi.AllocationBase == nullptr) [[unlikely]]
84+
return 0;
85+
86+
return static_cast<DWORD64>(reinterpret_cast<std::uintptr_t>(mbi.AllocationBase));
87+
}
88+
89+
[[nodiscard]] inline StackWalkRoutines MakeStackWalkRoutines(bool useDbgHelp) noexcept
90+
{
91+
if (useDbgHelp)
92+
{
93+
return StackWalkRoutines{
94+
.readMemory = &LocalReadProcessMemory,
95+
.functionTableAccess = SymFunctionTableAccess64,
96+
.moduleBase = SymGetModuleBase64
97+
};
98+
}
99+
100+
return StackWalkRoutines{
101+
.readMemory = &LocalReadProcessMemory,
102+
.functionTableAccess = nullptr,
103+
.moduleBase = &LocalGetModuleBase
104+
};
105+
}
106+
107+
struct ModuleAddressInfo
108+
{
109+
std::string name;
110+
DWORD64 base;
111+
112+
bool operator==(const ModuleAddressInfo&) const noexcept = default;
113+
};
114+
115+
[[nodiscard]] inline std::optional<ModuleAddressInfo> DescribeModule(DWORD64 address)
116+
{
117+
if (address == 0) [[unlikely]]
118+
return std::nullopt;
119+
120+
constexpr DWORD64 max32BitAddress = static_cast<DWORD64>(UINT32_MAX);
121+
if (address > max32BitAddress) [[unlikely]]
122+
return std::nullopt;
123+
124+
MEMORY_BASIC_INFORMATION mbi{};
125+
if (const auto addr32 = static_cast<std::uint32_t>(address);
126+
VirtualQuery(reinterpret_cast<LPCVOID>(static_cast<std::uintptr_t>(addr32)), &mbi, sizeof(mbi)) == 0) [[unlikely]]
127+
return std::nullopt;
128+
129+
if (mbi.AllocationBase == nullptr) [[unlikely]]
130+
return std::nullopt;
131+
132+
std::array<char, MAX_PATH> modulePath{};
133+
const DWORD pathLen = GetModuleFileNameA(reinterpret_cast<HMODULE>(mbi.AllocationBase), modulePath.data(), static_cast<DWORD>(modulePath.size()));
134+
135+
if (pathLen == 0) [[unlikely]]
136+
return std::nullopt;
137+
138+
if (pathLen >= modulePath.size()) [[unlikely]]
139+
{
140+
modulePath.back() = '\0';
141+
return std::nullopt;
142+
}
143+
144+
const std::string_view modulePathView{modulePath.data(), pathLen};
145+
const auto lastSlash = modulePathView.find_last_of("\\/");
146+
const std::string_view moduleName = (lastSlash != std::string_view::npos)
147+
? modulePathView.substr(lastSlash + 1)
148+
: modulePathView;
149+
150+
ModuleAddressInfo info{
151+
.name = std::string{moduleName},
152+
.base = static_cast<DWORD64>(reinterpret_cast<std::uintptr_t>(mbi.AllocationBase))
153+
};
154+
return info;
155+
}
156+
157+
[[nodiscard]] inline std::string FormatAddressWithModule(DWORD64 address)
158+
{
159+
try
160+
{
161+
if (const auto moduleInfo = DescribeModule(address)) [[likely]]
162+
{
163+
const auto& [name, base] = *moduleInfo;
164+
165+
if (address < base) [[unlikely]]
166+
return std::format("0x{:X}", address);
167+
168+
const DWORD64 offset = address - base;
169+
return std::format("{}+0x{:X}", name, offset);
170+
}
171+
172+
return std::format("0x{:X}", address);
173+
}
174+
catch (const std::format_error&)
175+
{
176+
return "<format_error>";
177+
}
178+
}
179+
180+
[[nodiscard]] inline std::string FormatAddressWithModuleAndAbsolute(DWORD64 address)
181+
{
182+
try
183+
{
184+
return std::format("{} [0x{:X}]", FormatAddressWithModule(address), address);
185+
}
186+
catch (const std::format_error&)
187+
{
188+
return "<format_error>";
189+
}
190+
}
191+
} // namespace StackTraceHelpers

0 commit comments

Comments
 (0)