Upgrade Boehm GC to 7.2alpha4.
[cacao.git] / src / mm / boehm-gc / extra / msvc_dbg.c
1 /*
2   Copyright (c) 2004 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 deal
6   in the Software without restriction,  including without limitation the rights
7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8   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 FROM,
19   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   THE SOFTWARE.
21 */
22 #ifndef _M_AMD64
23
24 /* X86_64 is ccurrently missing some meachine-dependent code below. */
25
26 #include "private/msvc_dbg.h"
27
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30
31 #pragma pack(push, 8)
32 #include <imagehlp.h>
33 #pragma pack(pop)
34
35 #pragma comment(lib, "dbghelp.lib")
36 #pragma optimize("gy", off)
37
38 #ifdef _WIN64
39         typedef ULONG_PTR ULONG_ADDR;
40 #else
41         typedef ULONG     ULONG_ADDR;
42 #endif
43
44 static HANDLE GetSymHandle()
45 {
46         static HANDLE symHandle = NULL;
47         if (!symHandle) {
48                 BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE);
49                 if (bRet) {
50                         DWORD dwOptions = SymGetOptions();
51                         dwOptions &= ~SYMOPT_UNDNAME;
52                         dwOptions |= SYMOPT_LOAD_LINES;
53                         SymSetOptions(dwOptions);
54                 }
55         }
56         return symHandle;
57 }
58
59 static void* CALLBACK FunctionTableAccess(HANDLE hProcess, ULONG_ADDR dwAddrBase)
60 {
61         return SymFunctionTableAccess(hProcess, dwAddrBase);
62 }
63
64 static ULONG_ADDR CALLBACK GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress)
65 {
66         MEMORY_BASIC_INFORMATION memoryInfo;
67         ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress);
68         if (dwAddrBase) {
69                 return dwAddrBase;
70         }
71         if (VirtualQueryEx(hProcess, (void*)(ULONG_PTR)dwAddress, &memoryInfo, sizeof(memoryInfo))) {
72                 char filePath[_MAX_PATH];
73                 char curDir[_MAX_PATH];
74                 char exePath[_MAX_PATH];
75                 DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase, filePath, sizeof(filePath));
76
77                 // Save and restore current directory around SymLoadModule, see KB article Q189780
78                 GetCurrentDirectoryA(sizeof(curDir), curDir);
79                 GetModuleFileNameA(NULL, exePath, sizeof(exePath));
80 #if defined(_MSC_VER) && _MSC_VER == 1200
81                 /* use strcat for VC6 */
82                 strcat(exePath, "\\..");
83 #else
84                 strcat_s(exePath, sizeof(exePath), "\\..");
85 #endif /* _MSC_VER >= 1200 */
86                 SetCurrentDirectoryA(exePath);
87 #ifdef _DEBUG
88                 GetCurrentDirectoryA(sizeof(exePath), exePath);
89 #endif
90                 SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL, (ULONG_ADDR)(ULONG_PTR)memoryInfo.AllocationBase, 0);
91                 SetCurrentDirectoryA(curDir);
92         }
93         return (ULONG_ADDR)(ULONG_PTR)memoryInfo.AllocationBase;
94 }
95
96 static ULONG_ADDR CheckAddress(void* address)
97 {
98         ULONG_ADDR dwAddress = (ULONG_ADDR)(ULONG_PTR)address;
99         GetModuleBase(GetSymHandle(), dwAddress);
100         return dwAddress;
101 }
102
103 size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames)
104 {
105         HANDLE hProcess = GetSymHandle();
106         HANDLE hThread = GetCurrentThread();
107         CONTEXT context;
108         context.ContextFlags = CONTEXT_FULL;
109         if (!GetThreadContext(hThread, &context)) {
110                 return 0;
111         }
112         // GetThreadContext might return invalid context for the current thread
113 #if defined(_M_IX86)
114     __asm mov context.Ebp, ebp
115 #endif
116         return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1, frames, maxFrames);
117 }
118
119 size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT* context, size_t skip, void* frames[], size_t maxFrames)
120 {
121         size_t frameIndex;
122         DWORD machineType;
123         STACKFRAME stackFrame = { 0 };
124         stackFrame.AddrPC.Mode      = AddrModeFlat;
125 #if defined(_M_IX86)
126         machineType                 = IMAGE_FILE_MACHINE_I386;
127         stackFrame.AddrPC.Offset    = context->Eip;
128         stackFrame.AddrStack.Mode   = AddrModeFlat;
129         stackFrame.AddrStack.Offset = context->Esp;
130         stackFrame.AddrFrame.Mode   = AddrModeFlat;
131         stackFrame.AddrFrame.Offset = context->Ebp;
132 #elif defined(_M_MRX000)
133         machineType                 = IMAGE_FILE_MACHINE_R4000;
134         stackFrame.AddrPC.Offset    = context->Fir;
135 #elif defined(_M_ALPHA)
136         machineType                 = IMAGE_FILE_MACHINE_ALPHA;
137         stackFrame.AddrPC.Offset    = (unsigned long)context->Fir;
138 #elif defined(_M_PPC)
139         machineType                 = IMAGE_FILE_MACHINE_POWERPC;
140         stackFrame.AddrPC.Offset    = context->Iar;
141 #elif defined(_M_IA64)
142         machineType                 = IMAGE_FILE_MACHINE_IA64;
143         stackFrame.AddrPC.Offset    = context->StIIP;
144 #elif defined(_M_ALPHA64)
145         machineType                 = IMAGE_FILE_MACHINE_ALPHA64;
146         stackFrame.AddrPC.Offset    = context->Fir;
147 #else
148 #error Unknown CPU
149 #endif
150         for (frameIndex = 0; frameIndex < maxFrames; ) {
151                 BOOL bRet = StackWalk(machineType, hProcess, hThread, &stackFrame, &context, NULL, FunctionTableAccess, GetModuleBase, NULL);
152                 if (!bRet) {
153                         break;
154                 }
155                 if (skip) {
156                         skip--;
157                 } else {
158                         frames[frameIndex++] = (void*)(ULONG_PTR)stackFrame.AddrPC.Offset;
159                 }
160         }
161         return frameIndex;
162 }
163
164 size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size)
165 {
166         if (size) *moduleName = 0;
167         {
168                 const char* sourceName;
169                 IMAGEHLP_MODULE moduleInfo = { sizeof (moduleInfo) };
170                 if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address), &moduleInfo)) {
171                         return 0;
172                 }
173                 sourceName = strrchr(moduleInfo.ImageName, '\\');
174                 if (sourceName) {
175                         sourceName++;
176                 } else {
177                         sourceName = moduleInfo.ImageName;
178                 }
179                 if (size) {
180                         strncpy(moduleName, sourceName, size)[size - 1] = 0;
181                 }
182                 return strlen(sourceName);
183         }
184 }
185
186 size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size)
187 {
188         void* address = NULL;
189         GetStackFrames(skip + 1, &address, 1);
190         if (address) {
191                 return GetModuleNameFromAddress(address, moduleName, size);
192         }
193         return 0;
194 }
195
196 size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, size_t* offsetBytes)
197 {
198         if (size) *symbolName = 0;
199         if (offsetBytes) *offsetBytes = 0;
200         __try {
201                 ULONG_ADDR dwOffset = 0;
202                 union {
203                         IMAGEHLP_SYMBOL sym;
204                         char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME];
205                 } u;
206                 u.sym.SizeOfStruct  = sizeof(u.sym);
207                 u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym);
208
209                 if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, &u.sym)) {
210                         return 0;
211                 } else {
212                         const char* sourceName = u.sym.Name;
213                         char undName[1024];
214                         if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName), UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) {
215                                 sourceName = undName;
216                         } else if (SymUnDName(&u.sym, undName, sizeof(undName))) {
217                                 sourceName = undName;
218                         }
219                         if (offsetBytes) {
220                                 *offsetBytes = dwOffset;
221                         }
222                         if (size) {
223                                 strncpy(symbolName, sourceName, size)[size - 1] = 0;
224                         }
225                         return strlen(sourceName);
226                 }
227         } __except (EXCEPTION_EXECUTE_HANDLER) {
228                 SetLastError(GetExceptionCode());
229         }
230         return 0;
231 }
232
233 size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, size_t* offsetBytes)
234 {
235         void* address = NULL;
236         GetStackFrames(skip + 1, &address, 1);
237         if (address) {
238                 return GetSymbolNameFromAddress(address, symbolName, size, offsetBytes);
239         }
240         return 0;
241 }
242
243 size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes)
244 {
245         if (size) *fileName = 0;
246         if (lineNumber) *lineNumber = 0;
247         if (offsetBytes) *offsetBytes = 0;
248         {
249                 char* sourceName;
250                 IMAGEHLP_LINE line = { sizeof (line) };
251                 ULONG_PTR dwOffset = 0;
252                 if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, &line)) {
253                         return 0;
254                 }
255                 if (lineNumber) {
256                         *lineNumber = line.LineNumber;
257                 }
258                 if (offsetBytes) {
259                         *offsetBytes = dwOffset;
260                 }
261                 sourceName = line.FileName;
262                 // TODO: resolve relative filenames, found in 'source directories' registered with MSVC IDE.
263                 if (size) {
264                         strncpy(fileName, sourceName, size)[size - 1] = 0;
265                 }
266                 return strlen(sourceName);
267         }
268 }
269
270 size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes)
271 {
272         void* address = NULL;
273         GetStackFrames(skip + 1, &address, 1);
274         if (address) {
275                 return GetFileLineFromAddress(address, fileName, size, lineNumber, offsetBytes);
276         }
277         return 0;
278 }
279
280 size_t GetDescriptionFromAddress(void* address, const char* format, char* buffer, size_t size)
281 {
282         char*const begin = buffer;
283         char*const end = buffer + size;
284         size_t line_number = 0;
285         char   str[128];
286
287         if (size) {
288                 *buffer = 0;
289         }
290         buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL);
291         size = end < buffer ? 0 : end - buffer;
292
293         if (line_number) {
294                 wsprintf(str, "(%d) : ", line_number);
295                 if (size) {
296                         strncpy(buffer, str, size)[size - 1] = 0;
297                 }
298                 buffer += strlen(str);
299                 size = end < buffer ? 0 : end - buffer;
300         }
301
302         if (size) {
303                 strncpy(buffer, "at ", size)[size - 1] = 0;
304         }
305         buffer += strlen("at ");
306         size = end < buffer ? 0 : end - buffer;
307
308         buffer += GetSymbolNameFromAddress(address, buffer, size, NULL);
309         size = end < buffer ? 0 : end - buffer;
310
311         if (size) {
312                 strncpy(buffer, " in ", size)[size - 1] = 0;
313         }
314         buffer += strlen(" in ");
315         size = end < buffer ? 0 : end - buffer;
316         
317         buffer += GetModuleNameFromAddress(address, buffer, size);
318         size = end < buffer ? 0 : end - buffer;
319         
320         return buffer - begin;
321 }
322
323 size_t GetDescriptionFromStack(void*const frames[], size_t count, const char* format, char* description[], size_t size)
324 {
325         char*const begin = (char*)description;
326         char*const end = begin + size;
327         char* buffer = begin + (count + 1) * sizeof(char*);
328         size_t i;
329         for (i = 0; i < count; ++i) {
330                 if (description) description[i] = buffer;
331                 size = end < buffer ? 0 : end - buffer;
332                 buffer += 1 + GetDescriptionFromAddress(frames[i], NULL, buffer, size);
333         }
334         if (description) description[count] = NULL;
335         return buffer - begin;
336 }
337
338 /* Compatibility with <execinfo.h> */
339
340 int backtrace(void* addresses[], int count)
341 {
342         return GetStackFrames(1, addresses, count);
343 }
344
345 char** backtrace_symbols(void*const* addresses, int count)
346 {
347         size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0);
348         char** symbols = (char**)malloc(size);
349         GetDescriptionFromStack(addresses, count, NULL, symbols, size);
350         return symbols;
351 }
352
353 #endif /* !_M_AMD64 */