-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStackWalker.h
More file actions
232 lines (200 loc) · 5.21 KB
/
StackWalker.h
File metadata and controls
232 lines (200 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#pragma once
#include <windows.h>
#include "dbghelp.h"
#include "stdio.h"
namespace Stackwalk {
struct StackFrame
{
DWORD64 address;
HMODULE module;
std::string name;
std::string sModName;
unsigned int line;
std::string file;
};
class raii_context {
CONTEXT * ctx;
bool owned = true;
public:
raii_context() {
owned = true;
ctx = new CONTEXT;
}
raii_context(CONTEXT* _ctx) {
ctx = _ctx;
}
raii_context& operator=(CONTEXT* _ctx) {
if (ctx != nullptr && owned) {
owned = false;
delete ctx;
}
ctx = _ctx;
return *this;
}
CONTEXT* operator()() {
return ctx;
}
operator bool() {
return nullptr == ctx;
}
~raii_context() {
if (owned)
delete ctx;
}
};
class StackWalker
{
using FrameVect = std::vector<StackFrame>;
template<typename... Arguments>
static std::string strf(const char* s, Arguments&&... args)
{
std::string str(1, '\0');
int n = snprintf(&str[0], 0, s, std::forward<Arguments>(args)...);
n += 1;
str.resize(n);
snprintf(&str[0], n, s, std::forward<Arguments>(args)...);
str.resize(n - 1);
return str;
}
static std::string basename(const std::string& file)
{
unsigned int i = file.find_last_of("\\/");
if (i == std::string::npos)
{
return file;
}
else
{
return file.substr(i + 1);
}
}
public:
static inline std::unique_ptr<FrameVect> trace(CONTEXT * _pContext = nullptr)
{
std::unique_ptr<FrameVect> frames;
#if _WIN64
DWORD machine = IMAGE_FILE_MACHINE_AMD64;
#else
DWORD machine = IMAGE_FILE_MACHINE_I386;
#endif
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
if (SymInitialize(process, NULL, TRUE) == FALSE)
{
printf(__FUNCTION__ ": Failed to call SymInitialize.\n");
return frames;
}
SymSetOptions(SYMOPT_LOAD_LINES);
raii_context context;
if (_pContext == nullptr) {
context()->ContextFlags = CONTEXT_FULL;
RtlCaptureContext(context());
}
else
context = _pContext;
#if _WIN64
STACKFRAME frame = {};
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#else
STACKFRAME frame = {};
frame.AddrPC.Offset = context()->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context()->Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context()->Esp;
frame.AddrStack.Mode = AddrModeFlat;
#endif
bool first = true;
frames = std::make_unique<FrameVect>();
while (StackWalk(machine, process, thread, &frame, context(), NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
{
StackFrame f = {};
f.address = frame.AddrPC.Offset;
#if _WIN64
DWORD64 moduleBase = 0;
#else
DWORD moduleBase = 0;
#endif
moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset);
f.module = (HMODULE)moduleBase;
char moduelBuff[MAX_PATH];
if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH))
{
f.module = (HMODULE)moduleBase;
f.sModName = basename(moduelBuff);
}
else
{
f.sModName = "Unknown Module";
}
#if _WIN64
DWORD64 offset = 0;
#else
DWORD offset = 0;
#endif
char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
symbol->SizeOfStruct = (sizeof IMAGEHLP_SYMBOL) + 255;
symbol->MaxNameLength = 254;
if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol))
{
f.name = symbol->Name;
}
else
{
DWORD error = GetLastError();
printf(__FUNCTION__ ": Failed to resolve address 0x%X: %u\n", frame.AddrPC.Offset, error);
f.name = "Unknown Function";
}
IMAGEHLP_LINE line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
DWORD offset_ln = 0;
if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line))
{
f.file = line.FileName;
f.line = line.LineNumber;
}
else
{
DWORD error = GetLastError();
printf(__FUNCTION__ ": Failed to resolve line for 0x%X: %u\n", frame.AddrPC.Offset, error);
f.line = 0;
}
//if (!first)
{
frames->push_back(f);
}
first = false;
}
SymCleanup(process);
return frames;
}
template<typename F>
static bool walk(F& f, CONTEXT* ctx = nullptr)
{
auto frames = trace(ctx);
if (frames) {
for (auto& frame : *frames) {
f(frame);
}
}
return (frames ? true : false);
}
template<typename F>
static void passPrettyTrace(F& f, CONTEXT* ctx = nullptr)
{
std::string strTrace;
walk([&](StackFrame& f) {
std::string sf =
strf("%s!+0x%llX -- %s, line %u in file %s ---- Abs: {add: 0x%llX, mod: 0x%llX}\n", f.sModName.c_str(), f.address - (DWORD64)f.module, f.name.c_str(), f.line, f.file.c_str(), f.address, (DWORD64)f.module);
strTrace.append(sf);
}, ctx);
f(strTrace);
}
};
} // namespace Stackwalk