- add sources.
[platform/framework/web/crosswalk.git] / src / tools / memory_watcher / call_stack.cc
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/memory_watcher/call_stack.h"
6
7 #include <shlwapi.h>
8 #include <tlhelp32.h>
9
10 #include "base/strings/string_number_conversions.h"
11 #include "tools/memory_watcher/memory_hook.h"
12
13 // Typedefs for explicit dynamic linking with functions exported from
14 // dbghelp.dll.
15 typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE,
16                                         LPSTACKFRAME64, PVOID,
17                                         PREAD_PROCESS_MEMORY_ROUTINE64,
18                                         PFUNCTION_TABLE_ACCESS_ROUTINE64,
19                                         PGET_MODULE_BASE_ROUTINE64,
20                                         PTRANSLATE_ADDRESS_ROUTINE64);
21 typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64);
22 typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64);
23 typedef BOOL (__stdcall *t_SymCleanup)(HANDLE);
24 typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64,
25                                                PDWORD64, PIMAGEHLP_SYMBOL64);
26 typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,
27                                                  PIMAGEHLP_LINE64);
28 typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL);
29 typedef DWORD (__stdcall *t_SymGetOptions)(void);
30 typedef DWORD (__stdcall *t_SymSetOptions)(DWORD);
31 typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD);
32 typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR,
33                                                PCSTR, DWORD64, DWORD);
34 typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64,
35                                                PIMAGEHLP_MODULE64);
36
37 // static
38 base::Lock CallStack::dbghelp_lock_;
39 // static
40 bool CallStack::dbghelp_loaded_ = false;
41 // static
42 DWORD CallStack::active_thread_id_ = 0;
43
44
45 static t_StackWalk64 pStackWalk64 = NULL;
46 static t_SymCleanup pSymCleanup = NULL;
47 static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL;
48 static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL;
49 static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL;
50 static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL;
51 static t_SymInitialize pSymInitialize = NULL;
52 static t_SymGetOptions pSymGetOptions = NULL;
53 static t_SymSetOptions pSymSetOptions = NULL;
54 static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL;
55 static t_SymGetSearchPath pSymGetSearchPath = NULL;
56 static t_SymLoadModule64 pSymLoadModule64 = NULL;
57
58 #define LOADPROC(module, name)  do {                                    \
59   p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name));  \
60   if (p##name == NULL) return false;                                    \
61 } while (0)
62
63 // This code has to be VERY careful to not induce any allocations, as memory
64 // watching code may cause recursion, which may obscure the stack for the truly
65 // offensive issue.  We use this function to break into a debugger, and it
66 // is guaranteed to not do any allocations (in fact, not do anything).
67 static void UltraSafeDebugBreak() {
68   _asm int(3);
69 }
70
71 // static
72 bool CallStack::LoadDbgHelp() {
73   if (!dbghelp_loaded_) {
74     base::AutoLock Lock(dbghelp_lock_);
75
76     // Re-check if we've loaded successfully now that we have the lock.
77     if (dbghelp_loaded_)
78       return true;
79
80     // Load dbghelp.dll, and obtain pointers to the exported functions that we
81     // will be using.
82     HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll");
83     if (dbghelp_module) {
84       LOADPROC(dbghelp_module, StackWalk64);
85       LOADPROC(dbghelp_module, SymFunctionTableAccess64);
86       LOADPROC(dbghelp_module, SymGetModuleBase64);
87       LOADPROC(dbghelp_module, SymCleanup);
88       LOADPROC(dbghelp_module, SymGetSymFromAddr64);
89       LOADPROC(dbghelp_module, SymGetLineFromAddr64);
90       LOADPROC(dbghelp_module, SymInitialize);
91       LOADPROC(dbghelp_module, SymGetOptions);
92       LOADPROC(dbghelp_module, SymSetOptions);
93       LOADPROC(dbghelp_module, SymGetModuleInfo64);
94       LOADPROC(dbghelp_module, SymGetSearchPath);
95       LOADPROC(dbghelp_module, SymLoadModule64);
96       dbghelp_loaded_ = true;
97     } else {
98       UltraSafeDebugBreak();
99       return false;
100     }
101   }
102   return dbghelp_loaded_;
103 }
104
105 // Load the symbols for generating stack traces.
106 static bool LoadSymbols(HANDLE process_handle) {
107   static bool symbols_loaded = false;
108   if (symbols_loaded) return true;
109
110   BOOL ok;
111
112   // Initialize the symbol engine.
113   ok = pSymInitialize(process_handle,  /* hProcess */
114                       NULL,            /* UserSearchPath */
115                       FALSE);          /* fInvadeProcess */
116   if (!ok) return false;
117
118   DWORD options = pSymGetOptions();
119   options |= SYMOPT_LOAD_LINES;
120   options |= SYMOPT_FAIL_CRITICAL_ERRORS;
121   options |= SYMOPT_UNDNAME;
122   options = pSymSetOptions(options);
123
124   const DWORD kMaxSearchPath = 1024;
125   TCHAR buf[kMaxSearchPath] = {0};
126   ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath);
127   if (!ok)
128     return false;
129
130   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
131                                              GetCurrentProcessId());
132   if (snapshot == INVALID_HANDLE_VALUE)
133     return false;
134
135   MODULEENTRY32W module;
136   module.dwSize = sizeof(module);  // Set the size of the structure.
137   BOOL cont = Module32FirstW(snapshot, &module);
138   while (cont) {
139     DWORD64 base;
140     // NOTE the SymLoadModule64 function has the peculiarity of accepting a
141     // both unicode and ASCII strings even though the parameter is PSTR.
142     base = pSymLoadModule64(process_handle,
143                             0,
144                             reinterpret_cast<PSTR>(module.szExePath),
145                             reinterpret_cast<PSTR>(module.szModule),
146                             reinterpret_cast<DWORD64>(module.modBaseAddr),
147                             module.modBaseSize);
148     if (base == 0) {
149       int err = GetLastError();
150       if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE)
151           return false;
152     }
153     cont = Module32NextW(snapshot, &module);
154   }
155   CloseHandle(snapshot);
156
157   symbols_loaded = true;
158   return true;
159 }
160
161
162 CallStack::SymbolCache* CallStack::symbol_cache_;
163
164 bool CallStack::Initialize() {
165   // We need to delay load the symbol cache until after
166   // the MemoryHook heap is alive.
167   symbol_cache_ = new SymbolCache();
168   return LoadDbgHelp();
169 }
170
171 CallStack::CallStack() {
172   static LONG callstack_id = 0;
173   frame_count_ = 0;
174   hash_ = 0;
175   id_ = InterlockedIncrement(&callstack_id);
176   valid_ = false;
177
178   if (!dbghelp_loaded_) {
179     UltraSafeDebugBreak();  // Initialize should have been called.
180     return;
181   }
182
183   GetStackTrace();
184 }
185
186 bool CallStack::IsEqual(const CallStack &target) {
187   if (frame_count_ != target.frame_count_)
188     return false;  // They can't be equal if the sizes are different.
189
190   // Walk the frames array until we
191   // either find a mismatch, or until we reach the end of the call stacks.
192   for (int index = 0; index < frame_count_; index++) {
193     if (frames_[index] != target.frames_[index])
194       return false;  // Found a mismatch. They are not equal.
195   }
196
197   // Reached the end of the call stacks. They are equal.
198   return true;
199 }
200
201 void CallStack::AddFrame(DWORD_PTR pc) {
202   DCHECK(frame_count_ < kMaxTraceFrames);
203   frames_[frame_count_++] = pc;
204
205   // Create a unique id for this CallStack.
206   pc = pc + (frame_count_ * 13);  // Alter the PC based on position in stack.
207   hash_ = ~hash_ + (pc << 15);
208   hash_ = hash_ ^ (pc >> 12);
209   hash_ = hash_ + (pc << 2);
210   hash_ = hash_ ^ (pc >> 4);
211   hash_ = hash_ * 2057;
212   hash_ = hash_ ^ (pc >> 16);
213 }
214
215 bool CallStack::LockedRecursionDetected() const {
216   if (!active_thread_id_) return false;
217   DWORD thread_id = GetCurrentThreadId();
218   // TODO(jar): Perchance we should use atomic access to member.
219   return thread_id == active_thread_id_;
220 }
221
222 bool CallStack::GetStackTrace() {
223   if (LockedRecursionDetected())
224     return false;
225
226   // Initialize the context record.
227   CONTEXT context;
228   memset(&context, 0, sizeof(context));
229   context.ContextFlags = CONTEXT_FULL;
230   __asm    call x
231   __asm x: pop eax
232   __asm    mov context.Eip, eax
233   __asm    mov context.Ebp, ebp
234   __asm    mov context.Esp, esp
235
236   STACKFRAME64 frame;
237   memset(&frame, 0, sizeof(frame));
238
239 #ifdef _M_IX86
240   DWORD image_type = IMAGE_FILE_MACHINE_I386;
241   frame.AddrPC.Offset    = context.Eip;
242   frame.AddrPC.Mode      = AddrModeFlat;
243   frame.AddrFrame.Offset = context.Ebp;
244   frame.AddrFrame.Mode   = AddrModeFlat;
245   frame.AddrStack.Offset = context.Esp;
246   frame.AddrStack.Mode   = AddrModeFlat;
247 #elif
248   NOT IMPLEMENTED!
249 #endif
250
251   HANDLE current_process = GetCurrentProcess();
252   HANDLE current_thread = GetCurrentThread();
253
254   // Walk the stack.
255   unsigned int count = 0;
256   {
257     AutoDbgHelpLock thread_monitoring_lock;
258
259     while (count < kMaxTraceFrames) {
260       count++;
261       if (!pStackWalk64(image_type,
262                         current_process,
263                         current_thread,
264                         &frame,
265                         &context,
266                         0,
267                         pSymFunctionTableAccess64,
268                         pSymGetModuleBase64,
269                         NULL))
270         break;  // Couldn't trace back through any more frames.
271
272       if (frame.AddrFrame.Offset == 0)
273         continue;  // End of stack.
274
275       // Push this frame's program counter onto the provided CallStack.
276       AddFrame((DWORD_PTR)frame.AddrPC.Offset);
277     }
278     valid_ = true;
279   }
280   return true;
281 }
282
283 void CallStack::ToString(PrivateAllocatorString* output) {
284   static const int kStackWalkMaxNameLen = MAX_SYM_NAME;
285   HANDLE current_process = GetCurrentProcess();
286
287   if (!LoadSymbols(current_process)) {
288     *output = "Error";
289     return;
290   }
291
292   base::AutoLock lock(dbghelp_lock_);
293
294   // Iterate through each frame in the call stack.
295   for (int32 index = 0; index < frame_count_; index++) {
296     PrivateAllocatorString line;
297
298     DWORD_PTR intruction_pointer = frame(index);
299
300     SymbolCache::iterator it;
301     it = symbol_cache_->find(intruction_pointer);
302     if (it != symbol_cache_->end()) {
303       line = it->second;
304     } else {
305       // Try to locate a symbol for this frame.
306       DWORD64 symbol_displacement = 0;
307       ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) +
308                       sizeof(TCHAR)*kStackWalkMaxNameLen +
309                       sizeof(ULONG64) - 1) / sizeof(ULONG64)];
310       IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
311       memset(buffer, 0, sizeof(buffer));
312       symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
313       symbol->MaxNameLength = kStackWalkMaxNameLen;
314       BOOL ok = pSymGetSymFromAddr64(current_process,            // hProcess
315                                      intruction_pointer,         // Address
316                                      &symbol_displacement,       // Displacement
317                                      symbol);                    // Symbol
318       if (ok) {
319         // Try to locate more source information for the symbol.
320         IMAGEHLP_LINE64 Line;
321         memset(&Line, 0, sizeof(Line));
322         Line.SizeOfStruct = sizeof(Line);
323         DWORD line_displacement;
324         ok = pSymGetLineFromAddr64(current_process,
325                                    intruction_pointer,
326                                    &line_displacement,
327                                    &Line);
328         if (ok) {
329           // Skip junk symbols from our internal stuff.
330           if (strstr(symbol->Name, "CallStack::") ||
331               strstr(symbol->Name, "MemoryWatcher::") ||
332               strstr(symbol->Name, "Perftools_") ||
333               strstr(symbol->Name, "MemoryHook::") ) {
334             // Just record a blank string.
335             (*symbol_cache_)[intruction_pointer] = "";
336             continue;
337           }
338
339           line += "    ";
340           line += static_cast<char*>(Line.FileName);
341           line += " (";
342           // TODO(jar): get something like this template to work :-/
343           // line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber);
344           // ...and then delete this line, which uses std::string.
345           line += base::IntToString(Line.LineNumber).c_str();
346           line += "): ";
347           line += symbol->Name;
348           line += "\n";
349         } else {
350           line += "    unknown (0):";
351           line += symbol->Name;
352           line += "\n";
353         }
354       } else {
355         // OK - couldn't get any info.  Try for the module.
356         IMAGEHLP_MODULE64 module_info;
357         module_info.SizeOfStruct = sizeof(module_info);
358         if (pSymGetModuleInfo64(current_process, intruction_pointer,
359                                 &module_info)) {
360           line += "    (";
361           line += static_cast<char*>(module_info.ModuleName);
362           line += ")\n";
363         } else {
364           line += "    ???\n";
365         }
366       }
367     }
368
369     (*symbol_cache_)[intruction_pointer] = line;
370     *output += line;
371   }
372   *output += "==================\n";
373 }
374
375
376 base::Lock AllocationStack::freelist_lock_;
377 AllocationStack* AllocationStack::freelist_ = NULL;
378
379 void* AllocationStack::operator new(size_t size) {
380   DCHECK(size == sizeof(AllocationStack));
381   {
382     base::AutoLock lock(freelist_lock_);
383     if (freelist_ != NULL) {
384       AllocationStack* stack = freelist_;
385       freelist_ = freelist_->next_;
386       stack->next_ = NULL;
387       return stack;
388     }
389   }
390   return MemoryHook::Alloc(size);
391 }
392
393 void AllocationStack::operator delete(void* ptr) {
394   AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr);
395   base::AutoLock lock(freelist_lock_);
396   DCHECK(stack->next_ == NULL);
397   stack->next_ = freelist_;
398   freelist_ = stack;
399 }