Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(508)

Side by Side Diff: tools/memory_watcher/call_stack.cc

Issue 314253003: Remove memory_watcher tool as well as --memory-profile command line flag. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix compile issue on non-ChromeOS platforms Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/memory_watcher/call_stack.h ('k') | tools/memory_watcher/dllmain.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 }
OLDNEW
« no previous file with comments | « tools/memory_watcher/call_stack.h ('k') | tools/memory_watcher/dllmain.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698