msvc_dbg.c raw
1 /*
2 * Copyright (c) 2004-2005 Andrei Polushin
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22
23 #if !defined(_M_ARM) && !defined(_M_ARM64) && !defined(_M_X64) \
24 && defined(_MSC_VER)
25
26 /* TODO: arm[64], x86_64 currently miss some machine-dependent code below. */
27
28 /* See also `GC_HAVE_BUILTIN_BACKTRACE` in `gc_config_macros.h` file. */
29
30 # include <stdio.h>
31 # include <stdlib.h>
32
33 # define GC_BUILD
34 # include "gc/gc.h"
35
36 # ifndef WIN32_LEAN_AND_MEAN
37 # define WIN32_LEAN_AND_MEAN 1
38 # endif
39 # define NOSERVICE
40 # include <windows.h>
41
42 # pragma pack(push, 8)
43 # include <imagehlp.h>
44 # pragma pack(pop)
45
46 # ifdef __cplusplus
47 extern "C" {
48 # endif
49
50 /* Compatibility with the platform `execinfo.h` file. */
51 int backtrace(void *addresses[], int count);
52 char **backtrace_symbols(void *const addresses[], int count);
53
54 # ifdef __cplusplus
55 } /* extern "C" */
56 # endif
57
58 # pragma comment(lib, "dbghelp.lib")
59 # pragma optimize("gy", off)
60
61 /*
62 * Disable a warning that `/GS` cannot protect parameters and local
63 * variables from local buffer overrun because optimizations are off.
64 */
65 # pragma warning(disable : 4748)
66
67 typedef GC_word word;
68 # define GC_ULONG_PTR word
69
70 # ifdef _WIN64
71 typedef GC_ULONG_PTR ULONG_ADDR;
72 # else
73 typedef ULONG ULONG_ADDR;
74 # endif
75
76 # ifndef MAX_SYM_NAME
77 # define MAX_SYM_NAME 2000
78 # endif
79
80 static HANDLE
81 GetSymHandle(void)
82 {
83 static HANDLE symHandle = NULL;
84 if (!symHandle) {
85 BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE);
86 if (bRet) {
87 DWORD dwOptions = SymGetOptions();
88 SymSetOptions((dwOptions & ~(DWORD)SYMOPT_UNDNAME) | SYMOPT_LOAD_LINES);
89 }
90 }
91 return symHandle;
92 }
93
94 static void *CALLBACK
95 FunctionTableAccess(HANDLE hProcess, ULONG_ADDR dwAddrBase)
96 {
97 return SymFunctionTableAccess(hProcess, dwAddrBase);
98 }
99
100 static ULONG_ADDR CALLBACK
101 GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress)
102 {
103 MEMORY_BASIC_INFORMATION memoryInfo;
104 ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress);
105 if (dwAddrBase != 0) {
106 return dwAddrBase;
107 }
108 if (VirtualQueryEx(hProcess, (void *)(GC_ULONG_PTR)dwAddress, &memoryInfo,
109 sizeof(memoryInfo))) {
110 char filePath[_MAX_PATH];
111 char curDir[_MAX_PATH];
112 char exePath[_MAX_PATH];
113 DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase,
114 filePath, sizeof(filePath));
115
116 /*
117 * Save and restore current directory around `SymLoadModule` (see
118 * the KB article Q189780).
119 */
120 GetCurrentDirectoryA(sizeof(curDir), curDir);
121 GetModuleFileNameA(NULL, exePath, sizeof(exePath));
122 # if _MSC_VER > 1200
123 strcat_s(exePath, sizeof(exePath), "\\..");
124 # else /* VC 6 or earlier */
125 strcat(exePath, "\\..");
126 # endif
127 SetCurrentDirectoryA(exePath);
128 # ifdef _DEBUG
129 GetCurrentDirectoryA(sizeof(exePath), exePath);
130 # endif
131 SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL,
132 (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase, 0);
133 SetCurrentDirectoryA(curDir);
134 }
135 return (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase;
136 }
137
138 static ULONG_ADDR
139 CheckAddress(void *address)
140 {
141 ULONG_ADDR dwAddress = (ULONG_ADDR)(GC_ULONG_PTR)address;
142 GetModuleBase(GetSymHandle(), dwAddress);
143 return dwAddress;
144 }
145
146 static size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread,
147 CONTEXT *context, size_t skip,
148 void *frames[], size_t maxFrames);
149
150 static size_t
151 GetStackFrames(size_t skip, void *frames[], size_t maxFrames)
152 {
153 HANDLE hProcess = GetSymHandle();
154 HANDLE hThread = GetCurrentThread();
155 CONTEXT context;
156 context.ContextFlags = CONTEXT_FULL;
157 if (!GetThreadContext(hThread, &context)) {
158 return 0;
159 }
160 /* `GetThreadContext` might return invalid context for the current thread. */
161 # if defined(_M_IX86)
162 __asm mov context.Ebp, ebp
163 # endif
164 return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1,
165 frames, maxFrames);
166 }
167
168 static size_t
169 GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT *context,
170 size_t skip, void *frames[], size_t maxFrames)
171 {
172 size_t frameIndex;
173 DWORD machineType;
174 STACKFRAME stackFrame = { 0 };
175 stackFrame.AddrPC.Mode = AddrModeFlat;
176 # if defined(_M_IX86)
177 machineType = IMAGE_FILE_MACHINE_I386;
178 stackFrame.AddrPC.Offset = context->Eip;
179 stackFrame.AddrStack.Mode = AddrModeFlat;
180 stackFrame.AddrStack.Offset = context->Esp;
181 stackFrame.AddrFrame.Mode = AddrModeFlat;
182 stackFrame.AddrFrame.Offset = context->Ebp;
183 # elif defined(_M_MRX000)
184 machineType = IMAGE_FILE_MACHINE_R4000;
185 stackFrame.AddrPC.Offset = context->Fir;
186 # elif defined(_M_ALPHA)
187 machineType = IMAGE_FILE_MACHINE_ALPHA;
188 stackFrame.AddrPC.Offset = (unsigned long)context->Fir;
189 # elif defined(_M_PPC)
190 machineType = IMAGE_FILE_MACHINE_POWERPC;
191 stackFrame.AddrPC.Offset = context->Iar;
192 # elif defined(_M_IA64)
193 machineType = IMAGE_FILE_MACHINE_IA64;
194 stackFrame.AddrPC.Offset = context->StIIP;
195 # elif defined(_M_ALPHA64)
196 machineType = IMAGE_FILE_MACHINE_ALPHA64;
197 stackFrame.AddrPC.Offset = context->Fir;
198 # elif !defined(CPPCHECK)
199 # error Unknown CPU
200 # endif
201 for (frameIndex = 0; frameIndex < maxFrames;) {
202 BOOL bRet
203 = StackWalk(machineType, hProcess, hThread, &stackFrame, &context,
204 NULL, FunctionTableAccess, GetModuleBase, NULL);
205 if (!bRet) {
206 break;
207 }
208 if (skip) {
209 skip--;
210 } else {
211 frames[frameIndex++] = (void *)(GC_ULONG_PTR)stackFrame.AddrPC.Offset;
212 }
213 }
214 return frameIndex;
215 }
216
217 static size_t
218 GetModuleNameFromAddress(void *address, char *moduleName, size_t size)
219 {
220 const char *sourceName;
221 IMAGEHLP_MODULE moduleInfo = { sizeof(moduleInfo) };
222
223 if (size)
224 *moduleName = 0;
225 if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address), &moduleInfo)) {
226 return 0;
227 }
228 sourceName = strrchr(moduleInfo.ImageName, '\\');
229 if (sourceName) {
230 sourceName++;
231 } else {
232 sourceName = moduleInfo.ImageName;
233 }
234 if (size) {
235 strncpy(moduleName, sourceName, size)[size - 1] = 0;
236 }
237 return strlen(sourceName);
238 }
239
240 union sym_namebuf_u {
241 IMAGEHLP_SYMBOL sym;
242 char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME];
243 };
244
245 static size_t
246 GetSymbolNameFromAddress(void *address, char *symbolName, size_t size,
247 size_t *offsetBytes)
248 {
249 if (size)
250 *symbolName = 0;
251 if (offsetBytes)
252 *offsetBytes = 0;
253 __try {
254 ULONG_ADDR dwOffset = 0;
255 union sym_namebuf_u u;
256
257 u.sym.SizeOfStruct = sizeof(u.sym);
258 u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym);
259
260 if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
261 &u.sym)) {
262 return 0;
263 } else {
264 const char *sourceName = u.sym.Name;
265 char undName[1024];
266 if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName),
267 UNDNAME_NO_MS_KEYWORDS
268 | UNDNAME_NO_ACCESS_SPECIFIERS)) {
269 sourceName = undName;
270 } else if (SymUnDName(&u.sym, undName, sizeof(undName))) {
271 sourceName = undName;
272 }
273 if (offsetBytes) {
274 *offsetBytes = dwOffset;
275 }
276 if (size) {
277 strncpy(symbolName, sourceName, size)[size - 1] = 0;
278 }
279 return strlen(sourceName);
280 }
281 } __except (EXCEPTION_EXECUTE_HANDLER) {
282 SetLastError(GetExceptionCode());
283 }
284 return 0;
285 }
286
287 static size_t
288 GetFileLineFromAddress(void *address, char *fileName, size_t size,
289 size_t *lineNumber, size_t *offsetBytes)
290 {
291 const char *sourceName;
292 IMAGEHLP_LINE line = { sizeof(line) };
293 GC_ULONG_PTR dwOffset = 0;
294
295 if (size)
296 *fileName = 0;
297 if (lineNumber)
298 *lineNumber = 0;
299 if (offsetBytes)
300 *offsetBytes = 0;
301 if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
302 &line)) {
303 return 0;
304 }
305 if (lineNumber) {
306 *lineNumber = line.LineNumber;
307 }
308 if (offsetBytes) {
309 *offsetBytes = dwOffset;
310 }
311 sourceName = line.FileName;
312 /*
313 * TODO: Resolve relative filenames, found in "source directories"
314 * registered with MSVC IDE.
315 */
316 if (size) {
317 strncpy(fileName, sourceName, size)[size - 1] = 0;
318 }
319 return strlen(sourceName);
320 }
321
322 # define GC_SNPRINTF _snprintf
323
324 static size_t
325 GetDescriptionFromAddress(void *address, const char *format, char *buffer,
326 size_t size)
327 {
328 char *const begin = buffer;
329 char *const end = buffer + size;
330 size_t line_number = 0;
331
332 (void)format;
333 if (size) {
334 *buffer = 0;
335 }
336 buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL);
337 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
338
339 if (line_number) {
340 char str[20];
341
342 (void)GC_SNPRINTF(str, sizeof(str), "(%d) : ", (int)line_number);
343 str[sizeof(str) - 1] = '\0';
344 if (size) {
345 strncpy(buffer, str, size)[size - 1] = 0;
346 }
347 buffer += strlen(str);
348 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
349 }
350
351 if (size) {
352 strncpy(buffer, "at ", size)[size - 1] = 0;
353 }
354 buffer += sizeof("at ") - 1;
355 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
356
357 buffer += GetSymbolNameFromAddress(address, buffer, size, NULL);
358 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
359
360 if (size) {
361 strncpy(buffer, " in ", size)[size - 1] = 0;
362 }
363 buffer += sizeof(" in ") - 1;
364 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
365
366 buffer += GetModuleNameFromAddress(address, buffer, size);
367 return buffer - begin;
368 }
369
370 static size_t
371 GetDescriptionFromStack(void *const frames[], size_t count, const char *format,
372 char *description[], size_t size)
373 {
374 const GC_ULONG_PTR begin = (GC_ULONG_PTR)description;
375 const GC_ULONG_PTR end = begin + size;
376 GC_ULONG_PTR buffer = begin + (count + 1) * sizeof(char *);
377 size_t i;
378
379 for (i = 0; i < count; ++i) {
380 if (description)
381 description[i] = (char *)buffer;
382 buffer += 1
383 + GetDescriptionFromAddress(frames[i], format, (char *)buffer,
384 end < buffer ? 0 : end - buffer);
385 }
386 if (description)
387 description[count] = NULL;
388 return buffer - begin;
389 }
390
391 /* The below is for compatibility with the platform `execinfo.h` file. */
392
393 int
394 backtrace(void *addresses[], int count)
395 {
396 return GetStackFrames(1, addresses, count);
397 }
398
399 char **
400 backtrace_symbols(void *const addresses[], int count)
401 {
402 size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0);
403 char **symbols = (char **)malloc(size);
404 if (symbols != NULL)
405 GetDescriptionFromStack(addresses, count, NULL, symbols, size);
406 return symbols;
407 }
408
409 #else
410
411 /* ANSI C does not allow translation units to be empty. */
412 extern int GC_quiet;
413
414 #endif
415