1 #include "Callstack.h"
2 #include <string>
3
4 #if defined(WIN32) && defined(_MSC_VER) && !defined(WIN8RT)
5
6 #include "../Math/InclWindows.h"
7 #include <DbgHelp.h>
8
9 #pragma comment(lib, "dbghelp.lib")
10
11 static std::string GetSymbolName(void *address, HANDLE hProcess)
12 {
13         // http://msdn.microsoft.com/en-us/library/ms680578(v=vs.85).aspx
14         DWORD64 dwDisplacement = 0;
15         DWORD64 dwAddress = (DWORD64)address;
16
17         static char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
18         PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
19
20         pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
21         pSymbol->MaxNameLen = MAX_SYM_NAME;
22
23         if (SymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol))
24         {
25                 // SymFromAddr returned success
26                 IMAGEHLP_LINE64 line = {};
27                 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
28                 if (SymGetLineFromAddr64(hProcess, dwAddress, (PDWORD)&dwDisplacement, &line))
29                 {
30                         // SymGetLineFromAddr64 returned success
31                         char str[128];
32                         sprintf(str, ":%u: ", line.LineNumber);
33                         return std::string(line.FileName) + str + pSymbol->Name;
34                 }
35                 else
36                 {
37                         // SymGetLineFromAddr64 failed
38 //                      DWORD error = GetLastError();
39 //                      printf("SymGetLineFromAddr64 returned error : %d\n", error);
40                         return pSymbol->Name;
41                 }
42         }
43         else
44         {
45                 // SymFromAddr failed
46 //              DWORD error = GetLastError();
47 //              printf("SymFromAddr returned error : %d\n", error);
48                 return std::string();
49         }
50 }
51
52 #ifdef _M_IX86
53 #pragma warning(push)
54 #pragma warning(disable : 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization
55 #endif
56
57 std::string NOINLINE GetCallstack(const char *indent, const char *ignoreFilter)
58 {
59         static bool symInitialized = false;
60
61         HANDLE currentProcess = GetCurrentProcess();
62         HANDLE currentThread = GetCurrentThread();
63
64         if (!symInitialized)
65         {
66                 SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
67                 SymInitialize(currentProcess, NULL, TRUE);
68                 symInitialized = true;
69         }
70
71         CONTEXT context = {};
72         STACKFRAME64 stack = {};
73
74 #ifdef _M_IX86
75         context.ContextFlags = CONTEXT_CONTROL;
76         _asm {
77                 call x
78                 x: pop eax
79                 mov context.Eip, eax
80                 mov context.Ebp, ebp
81                 mov context.Esp, esp
82         }
83 #else
84         RtlCaptureContext(&context);
85 #endif
86
87         stack.AddrPC.Mode         = AddrModeFlat;
88         stack.AddrStack.Mode      = AddrModeFlat;
89         stack.AddrFrame.Mode      = AddrModeFlat;
90
91 #ifdef _M_X64
92         // http://msdn.microsoft.com/en-us/library/windows/desktop/ms680646(v=vs.85).aspx
93         stack.AddrPC.Offset       = context.Rip;
94         stack.AddrStack.Offset    = context.Rsp;
95         stack.AddrFrame.Offset    = context.Rbp;
96         const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
97 #else
98         stack.AddrPC.Offset       = context.Eip;
99         stack.AddrStack.Offset    = context.Esp;
100         stack.AddrFrame.Offset    = context.Ebp;
101         const DWORD machineType = IMAGE_FILE_MACHINE_I386;
102 #endif
103         const PVOID contextRecord = &context;
104
105         std::string callstack;
106         for(int i = 0; i < 128; ++i)
107         {
108                 BOOL result = StackWalk64(machineType, currentProcess, currentThread, &stack, contextRecord, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
109                 if (!result)
110                         break;
111
112                 std::string symbolName = GetSymbolName((void*)stack.AddrPC.Offset, currentProcess);
113                 if (symbolName.find(" GetCallstack") == symbolName.length() - strlen(" GetCallstack"))
114                         continue;
115                 if (!ignoreFilter || symbolName.find(ignoreFilter) == symbolName.npos)
116                 {
117                         if (!symbolName.empty())
118                         {
119                                 callstack += indent;
120                                 callstack += symbolName;
121                                 callstack += '\n';
122                         }
123                         ignoreFilter = 0;
124                 }
125                 if (symbolName.find(" main") == symbolName.length() - strlen(" main"))
126                         break;
127                 if (stack.AddrReturn.Offset == 0)
128                         break;
129         }
130         return callstack;
131 }
132
133 #ifdef _M_IX86
134 #pragma warning(pop)
135 #endif
136
137 #elif defined(__APPLE__) || defined(LINUX)
138
139 #include <stdlib.h>
140 #include <execinfo.h>
141 #include <string.h>
142
143 std::string NOINLINE GetCallstack(const char *indent, const char *ignoreFilter)
144 {
145         const int N = 128;
146         void *callstack[N];
147         int n = backtrace(callstack, N);
148         char **strs = backtrace_symbols(callstack, n);
149         std::string stack;
150         for(int i = 0; i < n; ++i)
151         {
152                 if (strstr(strs[i], "_Z12GetCallstackPK") != 0)
153                         continue;
154                 if (!ignoreFilter || strstr(strs[i], ignoreFilter) != 0)
155                 {
156                         stack += indent;
157                         stack += strs[i];
158                         stack += '\n';
159                 }
160         }
161         free(strs);
162         return stack;
163 }
164
165 #else
166
167 std::string GetCallstack(const char *indent, const char *ignoreFilter)
168 {
169         // Not implemented on this platform.
170         return std::string();
171 }
172
173 #endif

Go back to previous page