1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla stack walking code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 2000
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Michael Judge, 20-December-2000
25 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /* API for getting a stack trace of the C/C++ stack on the current thread */
42 :
43 : #include "mozilla/Util.h"
44 : #include "mozilla/StackWalk.h"
45 : #include "nsDebug.h"
46 : #include "nsStackWalkPrivate.h"
47 :
48 : #include "nsStackWalk.h"
49 :
50 : using namespace mozilla;
51 :
52 : // The presence of this address is the stack must stop the stack walk. If
53 : // there is no such address, the structure will be {NULL, true}.
54 : struct CriticalAddress {
55 : void* mAddr;
56 : bool mInit;
57 : };
58 : static CriticalAddress gCriticalAddress;
59 :
60 : #if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
61 : #include <dlfcn.h>
62 : #endif
63 :
64 : #define NSSTACKWALK_SUPPORTS_MACOSX \
65 : (defined(XP_MACOSX) && \
66 : (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
67 :
68 : #define NSSTACKWALK_SUPPORTS_LINUX \
69 : (defined(linux) && \
70 : ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
71 : defined(HAVE__UNWIND_BACKTRACE)))
72 :
73 : #define NSSTACKWALK_SUPPORTS_SOLARIS \
74 : (defined(__sun) && \
75 : (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
76 :
77 : #if NSSTACKWALK_SUPPORTS_MACOSX
78 : #include <pthread.h>
79 : #include <errno.h>
80 : #include <CoreServices/CoreServices.h>
81 :
82 : typedef void
83 : malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
84 : uintptr_t result, uint32_t num_hot_frames_to_skip);
85 : extern malloc_logger_t *malloc_logger;
86 :
87 : static void
88 : stack_callback(void *pc, void *closure)
89 : {
90 : const char *name = reinterpret_cast<char *>(closure);
91 : Dl_info info;
92 :
93 : // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
94 : // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
95 : // correct one is the first that we find on our way up, so the
96 : // following check for gCriticalAddress.mAddr is critical.
97 : if (gCriticalAddress.mAddr || dladdr(pc, &info) == 0 ||
98 : info.dli_sname == NULL || strcmp(info.dli_sname, name) != 0)
99 : return;
100 : gCriticalAddress.mAddr = pc;
101 : }
102 :
103 : #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
104 : #define MAC_OS_X_VERSION_10_6_HEX 0x00001060
105 :
106 : static PRInt32 OSXVersion()
107 : {
108 : static PRInt32 gOSXVersion = 0x0;
109 : if (gOSXVersion == 0x0) {
110 : OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
111 : MOZ_ASSERT(err == noErr);
112 : }
113 : return gOSXVersion;
114 : }
115 :
116 : static bool OnLionOrLater()
117 : {
118 : return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
119 : }
120 :
121 : static bool OnSnowLeopardOrLater()
122 : {
123 : return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
124 : }
125 :
126 : static void
127 : my_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
128 : uintptr_t result, uint32_t num_hot_frames_to_skip)
129 : {
130 : static bool once = false;
131 : if (once)
132 : return;
133 : once = true;
134 :
135 : // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
136 : // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
137 : const char *name = OnSnowLeopardOrLater() ? "new_sem_from_pool" :
138 : "pthread_cond_wait$UNIX2003";
139 : NS_StackWalk(stack_callback, 0, const_cast<char*>(name), 0);
140 : }
141 :
142 : void
143 : StackWalkInitCriticalAddress()
144 : {
145 : if(gCriticalAddress.mInit)
146 : return;
147 : gCriticalAddress.mInit = true;
148 : // We must not do work when 'new_sem_from_pool' calls realloc, since
149 : // it holds a non-reentrant spin-lock and we will quickly deadlock.
150 : // new_sem_from_pool is not directly accessible using dlsym, so
151 : // we force a situation where new_sem_from_pool is on the stack and
152 : // use dladdr to check the addresses.
153 :
154 : MOZ_ASSERT(malloc_logger == NULL);
155 : malloc_logger = my_malloc_logger;
156 :
157 : pthread_cond_t cond;
158 : int r = pthread_cond_init(&cond, 0);
159 : MOZ_ASSERT(r == 0);
160 : pthread_mutex_t mutex;
161 : r = pthread_mutex_init(&mutex,0);
162 : MOZ_ASSERT(r == 0);
163 : r = pthread_mutex_lock(&mutex);
164 : MOZ_ASSERT(r == 0);
165 : struct timespec abstime = {0, 1};
166 : r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
167 : malloc_logger = NULL;
168 :
169 : // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents
170 : // us from finding the address, but that is fine, since with no call to malloc
171 : // there is no critical address.
172 : MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != NULL);
173 : MOZ_ASSERT(r == ETIMEDOUT);
174 : r = pthread_mutex_unlock(&mutex);
175 : MOZ_ASSERT(r == 0);
176 : r = pthread_mutex_destroy(&mutex);
177 : MOZ_ASSERT(r == 0);
178 : r = pthread_cond_destroy(&cond);
179 : MOZ_ASSERT(r == 0);
180 : }
181 :
182 : static bool IsCriticalAddress(void* aPC)
183 : {
184 : return gCriticalAddress.mAddr == aPC;
185 : }
186 : #else
187 0 : static bool IsCriticalAddress(void* aPC)
188 : {
189 0 : return false;
190 : }
191 : // We still initialize gCriticalAddress.mInit so that this code behaves
192 : // the same on all platforms. Otherwise a failure to init would be visible
193 : // only on OS X.
194 : void
195 2855 : StackWalkInitCriticalAddress()
196 : {
197 2855 : gCriticalAddress.mInit = true;
198 2855 : }
199 : #endif
200 :
201 : #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
202 :
203 : #include "nscore.h"
204 : #include <windows.h>
205 : #include <process.h>
206 : #include <stdio.h>
207 : #include "plstr.h"
208 : #include "mozilla/FunctionTimer.h"
209 :
210 : #include "nspr.h"
211 : #include <imagehlp.h>
212 : // We need a way to know if we are building for WXP (or later), as if we are, we
213 : // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
214 : // A value of 9 indicates we want to use the new APIs.
215 : #if API_VERSION_NUMBER < 9
216 : #error Too old imagehlp.h
217 : #endif
218 :
219 : // Define these as static pointers so that we can load the DLL on the
220 : // fly (and not introduce a link-time dependency on it). Tip o' the
221 : // hat to Matt Pietrick for this idea. See:
222 : //
223 : // http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm
224 : //
225 : PR_BEGIN_EXTERN_C
226 :
227 : extern HANDLE hStackWalkMutex;
228 :
229 : bool EnsureSymInitialized();
230 :
231 : bool EnsureImageHlpInitialized();
232 :
233 : struct WalkStackData {
234 : PRUint32 skipFrames;
235 : HANDLE thread;
236 : bool walkCallingThread;
237 : HANDLE process;
238 : HANDLE eventStart;
239 : HANDLE eventEnd;
240 : void **pcs;
241 : PRUint32 pc_size;
242 : PRUint32 pc_count;
243 : };
244 :
245 : void PrintError(char *prefix, WalkStackData* data);
246 : unsigned int WINAPI WalkStackThread(void* data);
247 : void WalkStackMain64(struct WalkStackData* data);
248 :
249 :
250 : DWORD gStackWalkThread;
251 : CRITICAL_SECTION gDbgHelpCS;
252 :
253 : PR_END_EXTERN_C
254 :
255 : // Routine to print an error message to standard error.
256 : // Will also call callback with error, if data supplied.
257 : void PrintError(char *prefix)
258 : {
259 : LPVOID lpMsgBuf;
260 : DWORD lastErr = GetLastError();
261 : FormatMessageA(
262 : FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
263 : NULL,
264 : lastErr,
265 : MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
266 : (LPSTR) &lpMsgBuf,
267 : 0,
268 : NULL
269 : );
270 : fprintf(stderr, "### ERROR: %s: %s",
271 : prefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
272 : fflush(stderr);
273 : LocalFree(lpMsgBuf);
274 : }
275 :
276 : bool
277 : EnsureImageHlpInitialized()
278 : {
279 : static bool gInitialized = false;
280 :
281 : if (gInitialized)
282 : return gInitialized;
283 :
284 : // Hope that our first call doesn't happen during static
285 : // initialization. If it does, this CreateThread call won't
286 : // actually start the thread until after the static initialization
287 : // is done, which means we'll deadlock while waiting for it to
288 : // process a stack.
289 : HANDLE readyEvent = ::CreateEvent(NULL, FALSE /* auto-reset*/,
290 : FALSE /* initially non-signaled */, NULL);
291 : unsigned int threadID;
292 : HANDLE hStackWalkThread = (HANDLE)
293 : _beginthreadex(NULL, 0, WalkStackThread, (void*)readyEvent,
294 : 0, &threadID);
295 : gStackWalkThread = threadID;
296 : if (hStackWalkThread == NULL) {
297 : PrintError("CreateThread");
298 : return false;
299 : }
300 : ::CloseHandle(hStackWalkThread);
301 :
302 : // Wait for the thread's event loop to start before posting events to it.
303 : ::WaitForSingleObject(readyEvent, INFINITE);
304 : ::CloseHandle(readyEvent);
305 :
306 : ::InitializeCriticalSection(&gDbgHelpCS);
307 :
308 : return gInitialized = true;
309 : }
310 :
311 : void
312 : WalkStackMain64(struct WalkStackData* data)
313 : {
314 : // Get the context information for the thread. That way we will
315 : // know where our sp, fp, pc, etc. are and can fill in the
316 : // STACKFRAME64 with the initial values.
317 : CONTEXT context;
318 : HANDLE myProcess = data->process;
319 : HANDLE myThread = data->thread;
320 : DWORD64 addr;
321 : STACKFRAME64 frame64;
322 : // skip our own stack walking frames
323 : int skip = (data->walkCallingThread ? 3 : 0) + data->skipFrames;
324 : BOOL ok;
325 :
326 : // Get a context for the specified thread.
327 : memset(&context, 0, sizeof(CONTEXT));
328 : context.ContextFlags = CONTEXT_FULL;
329 : if (!GetThreadContext(myThread, &context)) {
330 : PrintError("GetThreadContext");
331 : return;
332 : }
333 :
334 : // Setup initial stack frame to walk from
335 : memset(&frame64, 0, sizeof(frame64));
336 : #ifdef _M_IX86
337 : frame64.AddrPC.Offset = context.Eip;
338 : frame64.AddrStack.Offset = context.Esp;
339 : frame64.AddrFrame.Offset = context.Ebp;
340 : #elif defined _M_AMD64
341 : frame64.AddrPC.Offset = context.Rip;
342 : frame64.AddrStack.Offset = context.Rsp;
343 : frame64.AddrFrame.Offset = context.Rbp;
344 : #elif defined _M_IA64
345 : frame64.AddrPC.Offset = context.StIIP;
346 : frame64.AddrStack.Offset = context.SP;
347 : frame64.AddrFrame.Offset = context.RsBSP;
348 : #else
349 : #error "Should not have compiled this code"
350 : #endif
351 : frame64.AddrPC.Mode = AddrModeFlat;
352 : frame64.AddrStack.Mode = AddrModeFlat;
353 : frame64.AddrFrame.Mode = AddrModeFlat;
354 : frame64.AddrReturn.Mode = AddrModeFlat;
355 :
356 : // Now walk the stack
357 : while (1) {
358 :
359 : // debug routines are not threadsafe, so grab the lock.
360 : EnterCriticalSection(&gDbgHelpCS);
361 : ok = StackWalk64(
362 : #ifdef _M_AMD64
363 : IMAGE_FILE_MACHINE_AMD64,
364 : #elif defined _M_IA64
365 : IMAGE_FILE_MACHINE_IA64,
366 : #elif defined _M_IX86
367 : IMAGE_FILE_MACHINE_I386,
368 : #else
369 : #error "Should not have compiled this code"
370 : #endif
371 : myProcess,
372 : myThread,
373 : &frame64,
374 : &context,
375 : NULL,
376 : SymFunctionTableAccess64, // function table access routine
377 : SymGetModuleBase64, // module base routine
378 : 0
379 : );
380 : LeaveCriticalSection(&gDbgHelpCS);
381 :
382 : if (ok)
383 : addr = frame64.AddrPC.Offset;
384 : else {
385 : addr = 0;
386 : PrintError("WalkStack64");
387 : }
388 :
389 : if (!ok || (addr == 0)) {
390 : break;
391 : }
392 :
393 : if (skip-- > 0) {
394 : continue;
395 : }
396 :
397 : if (data->pc_count < data->pc_size)
398 : data->pcs[data->pc_count] = (void*)addr;
399 : ++data->pc_count;
400 :
401 : if (frame64.AddrReturn.Offset == 0)
402 : break;
403 : }
404 : return;
405 : }
406 :
407 :
408 : unsigned int WINAPI
409 : WalkStackThread(void* aData)
410 : {
411 : BOOL msgRet;
412 : MSG msg;
413 :
414 : // Call PeekMessage to force creation of a message queue so that
415 : // other threads can safely post events to us.
416 : ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
417 :
418 : // and tell the thread that created us that we're ready.
419 : HANDLE readyEvent = (HANDLE)aData;
420 : ::SetEvent(readyEvent);
421 :
422 : while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
423 : if (msgRet == -1) {
424 : PrintError("GetMessage");
425 : } else {
426 : DWORD ret;
427 :
428 : struct WalkStackData *data = (WalkStackData *)msg.lParam;
429 : if (!data)
430 : continue;
431 :
432 : // Don't suspend the calling thread until it's waiting for
433 : // us; otherwise the number of frames on the stack could vary.
434 : ret = ::WaitForSingleObject(data->eventStart, INFINITE);
435 : if (ret != WAIT_OBJECT_0)
436 : PrintError("WaitForSingleObject");
437 :
438 : // Suspend the calling thread, dump his stack, and then resume him.
439 : // He's currently waiting for us to finish so now should be a good time.
440 : ret = ::SuspendThread( data->thread );
441 : if (ret == -1) {
442 : PrintError("ThreadSuspend");
443 : }
444 : else {
445 : WalkStackMain64(data);
446 :
447 : ret = ::ResumeThread(data->thread);
448 : if (ret == -1) {
449 : PrintError("ThreadResume");
450 : }
451 : }
452 :
453 : ::SetEvent(data->eventEnd);
454 : }
455 : }
456 :
457 : return 0;
458 : }
459 :
460 : /**
461 : * Walk the stack, translating PC's found into strings and recording the
462 : * chain in aBuffer. For this to work properly, the DLLs must be rebased
463 : * so that the address in the file agrees with the address in memory.
464 : * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
465 : * whose in memory address doesn't match its in-file address.
466 : */
467 :
468 : EXPORT_XPCOM_API(nsresult)
469 : NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
470 : void *aClosure, uintptr_t aThread)
471 : {
472 : MOZ_ASSERT(gCriticalAddress.mInit);
473 : HANDLE myProcess, myThread;
474 : DWORD walkerReturn;
475 : struct WalkStackData data;
476 :
477 : if (!EnsureImageHlpInitialized())
478 : return false;
479 :
480 : HANDLE targetThread = ::GetCurrentThread();
481 : data.walkCallingThread = true;
482 : if (aThread) {
483 : HANDLE threadToWalk = reinterpret_cast<HANDLE> (aThread);
484 : // walkCallingThread indicates whether we are walking the caller's stack
485 : data.walkCallingThread = (threadToWalk == targetThread);
486 : targetThread = threadToWalk;
487 : }
488 :
489 : // Have to duplicate handle to get a real handle.
490 : if (!::DuplicateHandle(::GetCurrentProcess(),
491 : ::GetCurrentProcess(),
492 : ::GetCurrentProcess(),
493 : &myProcess,
494 : PROCESS_ALL_ACCESS, FALSE, 0)) {
495 : PrintError("DuplicateHandle (process)");
496 : return NS_ERROR_FAILURE;
497 : }
498 : if (!::DuplicateHandle(::GetCurrentProcess(),
499 : targetThread,
500 : ::GetCurrentProcess(),
501 : &myThread,
502 : THREAD_ALL_ACCESS, FALSE, 0)) {
503 : PrintError("DuplicateHandle (thread)");
504 : ::CloseHandle(myProcess);
505 : return NS_ERROR_FAILURE;
506 : }
507 :
508 : data.skipFrames = aSkipFrames;
509 : data.thread = myThread;
510 : data.process = myProcess;
511 : void *local_pcs[1024];
512 : data.pcs = local_pcs;
513 : data.pc_count = 0;
514 : data.pc_size = ArrayLength(local_pcs);
515 :
516 : if (aThread) {
517 : // If we're walking the stack of another thread, we don't need to
518 : // use a separate walker thread.
519 : WalkStackMain64(&data);
520 : } else {
521 : data.eventStart = ::CreateEvent(NULL, FALSE /* auto-reset*/,
522 : FALSE /* initially non-signaled */, NULL);
523 : data.eventEnd = ::CreateEvent(NULL, FALSE /* auto-reset*/,
524 : FALSE /* initially non-signaled */, NULL);
525 :
526 : ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
527 :
528 : walkerReturn = ::SignalObjectAndWait(data.eventStart,
529 : data.eventEnd, INFINITE, FALSE);
530 : if (walkerReturn != WAIT_OBJECT_0)
531 : PrintError("SignalObjectAndWait (1)");
532 : if (data.pc_count > data.pc_size) {
533 : data.pcs = (void**) malloc(data.pc_count * sizeof(void*));
534 : data.pc_size = data.pc_count;
535 : data.pc_count = 0;
536 : ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
537 : walkerReturn = ::SignalObjectAndWait(data.eventStart,
538 : data.eventEnd, INFINITE, FALSE);
539 : if (walkerReturn != WAIT_OBJECT_0)
540 : PrintError("SignalObjectAndWait (2)");
541 : }
542 :
543 : ::CloseHandle(data.eventStart);
544 : ::CloseHandle(data.eventEnd);
545 : }
546 :
547 : ::CloseHandle(myThread);
548 : ::CloseHandle(myProcess);
549 :
550 : for (PRUint32 i = 0; i < data.pc_count; ++i)
551 : (*aCallback)(data.pcs[i], aClosure);
552 :
553 : if (data.pc_size > ArrayLength(local_pcs))
554 : free(data.pcs);
555 :
556 : return NS_OK;
557 : }
558 :
559 :
560 : static BOOL CALLBACK callbackEspecial64(
561 : PCSTR aModuleName,
562 : DWORD64 aModuleBase,
563 : ULONG aModuleSize,
564 : PVOID aUserContext)
565 : {
566 : BOOL retval = TRUE;
567 : DWORD64 addr = *(DWORD64*)aUserContext;
568 :
569 : /*
570 : * You'll want to control this if we are running on an
571 : * architecture where the addresses go the other direction.
572 : * Not sure this is even a realistic consideration.
573 : */
574 : const BOOL addressIncreases = TRUE;
575 :
576 : /*
577 : * If it falls in side the known range, load the symbols.
578 : */
579 : if (addressIncreases
580 : ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
581 : : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
582 : ) {
583 : retval = SymLoadModule64(GetCurrentProcess(), NULL, (PSTR)aModuleName, NULL, aModuleBase, aModuleSize);
584 : if (!retval)
585 : PrintError("SymLoadModule64");
586 : }
587 :
588 : return retval;
589 : }
590 :
591 : /*
592 : * SymGetModuleInfoEspecial
593 : *
594 : * Attempt to determine the module information.
595 : * Bug 112196 says this DLL may not have been loaded at the time
596 : * SymInitialize was called, and thus the module information
597 : * and symbol information is not available.
598 : * This code rectifies that problem.
599 : */
600 :
601 : // New members were added to IMAGEHLP_MODULE64 (that show up in the
602 : // Platform SDK that ships with VC8, but not the Platform SDK that ships
603 : // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
604 : // use them, and it's useful to be able to function correctly with the
605 : // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
606 : // version 5.1.) Since Platform SDK version need not correspond to
607 : // compiler version, and the version number in debughlp.h was NOT bumped
608 : // when these changes were made, ifdef based on a constant that was
609 : // added between these versions.
610 : #ifdef SSRVOPT_SETCONTEXT
611 : #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
612 : #else
613 : #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
614 : #endif
615 :
616 : BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo)
617 : {
618 : BOOL retval = FALSE;
619 :
620 : /*
621 : * Init the vars if we have em.
622 : */
623 : aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
624 : if (nsnull != aLineInfo) {
625 : aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
626 : }
627 :
628 : /*
629 : * Give it a go.
630 : * It may already be loaded.
631 : */
632 : retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
633 :
634 : if (FALSE == retval) {
635 : BOOL enumRes = FALSE;
636 :
637 : /*
638 : * Not loaded, here's the magic.
639 : * Go through all the modules.
640 : */
641 : // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
642 : // constness of the first parameter of
643 : // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
644 : // non-const to const over time). See bug 391848 and bug
645 : // 415426.
646 : enumRes = EnumerateLoadedModules64(aProcess, (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64, (PVOID)&aAddr);
647 : if (FALSE != enumRes)
648 : {
649 : /*
650 : * One final go.
651 : * If it fails, then well, we have other problems.
652 : */
653 : retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
654 : if (!retval)
655 : PrintError("SymGetModuleInfo64");
656 : }
657 : }
658 :
659 : /*
660 : * If we got module info, we may attempt line info as well.
661 : * We will not report failure if this does not work.
662 : */
663 : if (FALSE != retval && nsnull != aLineInfo) {
664 : DWORD displacement = 0;
665 : BOOL lineRes = FALSE;
666 : lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
667 : if (!lineRes) {
668 : // Clear out aLineInfo to indicate that it's not valid
669 : memset(aLineInfo, 0, sizeof(*aLineInfo));
670 : }
671 : }
672 :
673 : return retval;
674 : }
675 :
676 : bool
677 : EnsureSymInitialized()
678 : {
679 : static bool gInitialized = false;
680 : bool retStat;
681 :
682 : if (gInitialized)
683 : return gInitialized;
684 :
685 : NS_TIME_FUNCTION;
686 :
687 : if (!EnsureImageHlpInitialized())
688 : return false;
689 :
690 : SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
691 : retStat = SymInitialize(GetCurrentProcess(), NULL, TRUE);
692 : if (!retStat)
693 : PrintError("SymInitialize");
694 :
695 : gInitialized = retStat;
696 : /* XXX At some point we need to arrange to call SymCleanup */
697 :
698 : return retStat;
699 : }
700 :
701 :
702 : EXPORT_XPCOM_API(nsresult)
703 : NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
704 : {
705 : aDetails->library[0] = '\0';
706 : aDetails->loffset = 0;
707 : aDetails->filename[0] = '\0';
708 : aDetails->lineno = 0;
709 : aDetails->function[0] = '\0';
710 : aDetails->foffset = 0;
711 :
712 : if (!EnsureSymInitialized())
713 : return NS_ERROR_FAILURE;
714 :
715 : HANDLE myProcess = ::GetCurrentProcess();
716 : BOOL ok;
717 :
718 : // debug routines are not threadsafe, so grab the lock.
719 : EnterCriticalSection(&gDbgHelpCS);
720 :
721 : //
722 : // Attempt to load module info before we attempt to resolve the symbol.
723 : // This just makes sure we get good info if available.
724 : //
725 :
726 : DWORD64 addr = (DWORD64)aPC;
727 : IMAGEHLP_MODULE64 modInfo;
728 : IMAGEHLP_LINE64 lineInfo;
729 : BOOL modInfoRes;
730 : modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
731 :
732 : if (modInfoRes) {
733 : PL_strncpyz(aDetails->library, modInfo.ModuleName,
734 : sizeof(aDetails->library));
735 : aDetails->loffset = (char*) aPC - (char*) modInfo.BaseOfImage;
736 :
737 : if (lineInfo.FileName) {
738 : PL_strncpyz(aDetails->filename, lineInfo.FileName,
739 : sizeof(aDetails->filename));
740 : aDetails->lineno = lineInfo.LineNumber;
741 : }
742 : }
743 :
744 : ULONG64 buffer[(sizeof(SYMBOL_INFO) +
745 : MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
746 : PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
747 : pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
748 : pSymbol->MaxNameLen = MAX_SYM_NAME;
749 :
750 : DWORD64 displacement;
751 : ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
752 :
753 : if (ok) {
754 : PL_strncpyz(aDetails->function, pSymbol->Name,
755 : sizeof(aDetails->function));
756 : aDetails->foffset = displacement;
757 : }
758 :
759 : LeaveCriticalSection(&gDbgHelpCS); // release our lock
760 : return NS_OK;
761 : }
762 :
763 : EXPORT_XPCOM_API(nsresult)
764 : NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
765 : char *aBuffer, PRUint32 aBufferSize)
766 : {
767 : if (aDetails->function[0])
768 : _snprintf(aBuffer, aBufferSize, "%s!%s+0x%016lX",
769 : aDetails->library, aDetails->function, aDetails->foffset);
770 : else
771 : _snprintf(aBuffer, aBufferSize, "0x%016lX", aPC);
772 :
773 : aBuffer[aBufferSize - 1] = '\0';
774 :
775 : PRUint32 len = strlen(aBuffer);
776 : if (aDetails->filename[0]) {
777 : _snprintf(aBuffer + len, aBufferSize - len, " (%s, line %d)\n",
778 : aDetails->filename, aDetails->lineno);
779 : } else {
780 : aBuffer[len] = '\n';
781 : if (++len != aBufferSize)
782 : aBuffer[len] = '\0';
783 : }
784 : aBuffer[aBufferSize - 2] = '\n';
785 : aBuffer[aBufferSize - 1] = '\0';
786 : return NS_OK;
787 : }
788 :
789 : // WIN32 x86 stack walking code
790 : // i386 or PPC Linux stackwalking code or Solaris
791 : #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_SOLARIS || NSSTACKWALK_SUPPORTS_MACOSX)
792 :
793 : #include <stdlib.h>
794 : #include <string.h>
795 : #include <math.h>
796 : #include "nscore.h"
797 : #include <stdio.h>
798 : #include "plstr.h"
799 :
800 : // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
801 : // if __USE_GNU is defined. I suppose its some kind of standards
802 : // adherence thing.
803 : //
804 : #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
805 : #define __USE_GNU
806 : #endif
807 :
808 : // This thing is exported by libstdc++
809 : // Yes, this is a gcc only hack
810 : #if defined(MOZ_DEMANGLE_SYMBOLS)
811 : #include <cxxabi.h>
812 : #include <stdlib.h> // for free()
813 : #endif // MOZ_DEMANGLE_SYMBOLS
814 :
815 0 : void DemangleSymbol(const char * aSymbol,
816 : char * aBuffer,
817 : int aBufLen)
818 : {
819 0 : aBuffer[0] = '\0';
820 :
821 : #if defined(MOZ_DEMANGLE_SYMBOLS)
822 : /* See demangle.h in the gcc source for the voodoo */
823 0 : char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
824 :
825 0 : if (demangled)
826 : {
827 0 : strncpy(aBuffer,demangled,aBufLen);
828 0 : free(demangled);
829 : }
830 : #endif // MOZ_DEMANGLE_SYMBOLS
831 0 : }
832 :
833 :
834 : #if NSSTACKWALK_SUPPORTS_SOLARIS
835 :
836 : /*
837 : * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak".
838 : */
839 :
840 : #include <synch.h>
841 : #include <ucontext.h>
842 : #include <sys/frame.h>
843 : #include <sys/regset.h>
844 : #include <sys/stack.h>
845 :
846 : static int load_address ( void * pc, void * arg );
847 : static struct bucket * newbucket ( void * pc );
848 : static struct frame * cs_getmyframeptr ( void );
849 : static void cs_walk_stack ( void * (*read_func)(char * address),
850 : struct frame * fp,
851 : int (*operate_func)(void *, void *),
852 : void * usrarg );
853 : static void cs_operate ( void (*operate_func)(void *, void *),
854 : void * usrarg );
855 :
856 : #ifndef STACK_BIAS
857 : #define STACK_BIAS 0
858 : #endif /*STACK_BIAS*/
859 :
860 : #define LOGSIZE 4096
861 :
862 : /* type of demangling function */
863 : typedef int demf_t(const char *, char *, size_t);
864 :
865 : static demf_t *demf;
866 :
867 : static int initialized = 0;
868 :
869 : #if defined(sparc) || defined(__sparc)
870 : #define FRAME_PTR_REGISTER REG_SP
871 : #endif
872 :
873 : #if defined(i386) || defined(__i386)
874 : #define FRAME_PTR_REGISTER EBP
875 : #endif
876 :
877 : struct bucket {
878 : void * pc;
879 : int index;
880 : struct bucket * next;
881 : };
882 :
883 : struct my_user_args {
884 : NS_WalkStackCallback callback;
885 : PRUint32 skipFrames;
886 : void *closure;
887 : };
888 :
889 :
890 : static void myinit();
891 :
892 : #pragma init (myinit)
893 :
894 : static void
895 : myinit()
896 : {
897 :
898 : if (! initialized) {
899 : #ifndef __GNUC__
900 : void *handle;
901 : const char *libdem = "libdemangle.so.1";
902 :
903 : /* load libdemangle if we can and need to (only try this once) */
904 : if ((handle = dlopen(libdem, RTLD_LAZY)) != NULL) {
905 : demf = (demf_t *)dlsym(handle,
906 : "cplus_demangle"); /*lint !e611 */
907 : /*
908 : * lint override above is to prevent lint from
909 : * complaining about "suspicious cast".
910 : */
911 : }
912 : #endif /*__GNUC__*/
913 : }
914 : initialized = 1;
915 : }
916 :
917 :
918 : static int
919 : load_address(void * pc, void * arg )
920 : {
921 : static struct bucket table[2048];
922 : static mutex_t lock;
923 : struct bucket * ptr;
924 : struct my_user_args * args = (struct my_user_args *) arg;
925 :
926 : unsigned int val = NS_PTR_TO_INT32(pc);
927 :
928 : ptr = table + ((val >> 2)&2047);
929 :
930 : mutex_lock(&lock);
931 : while (ptr->next) {
932 : if (ptr->next->pc == pc)
933 : break;
934 : ptr = ptr->next;
935 : }
936 :
937 : if (ptr->next) {
938 : mutex_unlock(&lock);
939 : } else {
940 : (args->callback)(pc, args->closure);
941 :
942 : ptr->next = newbucket(pc);
943 : mutex_unlock(&lock);
944 : }
945 : return 0;
946 : }
947 :
948 :
949 : static struct bucket *
950 : newbucket(void * pc)
951 : {
952 : struct bucket * ptr = (struct bucket *) malloc(sizeof (*ptr));
953 : static int index; /* protected by lock in caller */
954 :
955 : ptr->index = index++;
956 : ptr->next = NULL;
957 : ptr->pc = pc;
958 : return (ptr);
959 : }
960 :
961 :
962 : static struct frame *
963 : csgetframeptr()
964 : {
965 : ucontext_t u;
966 : struct frame *fp;
967 :
968 : (void) getcontext(&u);
969 :
970 : fp = (struct frame *)
971 : ((char *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER] +
972 : STACK_BIAS);
973 :
974 : /* make sure to return parents frame pointer.... */
975 :
976 : return ((struct frame *)((ulong_t)fp->fr_savfp + STACK_BIAS));
977 : }
978 :
979 :
980 : static void
981 : cswalkstack(struct frame *fp, int (*operate_func)(void *, void *),
982 : void *usrarg)
983 : {
984 :
985 : while (fp != 0 && fp->fr_savpc != 0) {
986 :
987 : if (operate_func((void *)fp->fr_savpc, usrarg) != 0)
988 : break;
989 : /*
990 : * watch out - libthread stacks look funny at the top
991 : * so they may not have their STACK_BIAS set
992 : */
993 :
994 : fp = (struct frame *)((ulong_t)fp->fr_savfp +
995 : (fp->fr_savfp?(ulong_t)STACK_BIAS:0));
996 : }
997 : }
998 :
999 :
1000 : static void
1001 : cs_operate(int (*operate_func)(void *, void *), void * usrarg)
1002 : {
1003 : cswalkstack(csgetframeptr(), operate_func, usrarg);
1004 : }
1005 :
1006 : EXPORT_XPCOM_API(nsresult)
1007 : NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1008 : void *aClosure, uintptr_t aThread)
1009 : {
1010 : MOZ_ASSERT(gCriticalAddress.mInit);
1011 : MOZ_ASSERT(!aThread);
1012 : struct my_user_args args;
1013 :
1014 : if (!initialized)
1015 : myinit();
1016 :
1017 : args.callback = aCallback;
1018 : args.skipFrames = aSkipFrames; /* XXX Not handled! */
1019 : args.closure = aClosure;
1020 : cs_operate(load_address, &args);
1021 : return NS_OK;
1022 : }
1023 :
1024 : EXPORT_XPCOM_API(nsresult)
1025 : NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
1026 : {
1027 : aDetails->library[0] = '\0';
1028 : aDetails->loffset = 0;
1029 : aDetails->filename[0] = '\0';
1030 : aDetails->lineno = 0;
1031 : aDetails->function[0] = '\0';
1032 : aDetails->foffset = 0;
1033 :
1034 : char dembuff[4096];
1035 : Dl_info info;
1036 :
1037 : if (dladdr(aPC, & info)) {
1038 : if (info.dli_fname) {
1039 : PL_strncpyz(aDetails->library, info.dli_fname,
1040 : sizeof(aDetails->library));
1041 : aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
1042 : }
1043 : if (info.dli_sname) {
1044 : aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
1045 : #ifdef __GNUC__
1046 : DemangleSymbol(info.dli_sname, dembuff, sizeof(dembuff));
1047 : #else
1048 : if (!demf || demf(info.dli_sname, dembuff, sizeof (dembuff)))
1049 : dembuff[0] = 0;
1050 : #endif /*__GNUC__*/
1051 : PL_strncpyz(aDetails->function,
1052 : (dembuff[0] != '\0') ? dembuff : info.dli_sname,
1053 : sizeof(aDetails->function));
1054 : }
1055 : }
1056 :
1057 : return NS_OK;
1058 : }
1059 :
1060 : EXPORT_XPCOM_API(nsresult)
1061 : NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
1062 : char *aBuffer, PRUint32 aBufferSize)
1063 : {
1064 : snprintf(aBuffer, aBufferSize, "%p %s:%s+0x%lx\n",
1065 : aPC,
1066 : aDetails->library[0] ? aDetails->library : "??",
1067 : aDetails->function[0] ? aDetails->function : "??",
1068 : aDetails->foffset);
1069 : return NS_OK;
1070 : }
1071 :
1072 : #else // not __sun-specific
1073 :
1074 : #if __GLIBC__ > 2 || __GLIBC_MINOR > 1
1075 : #define HAVE___LIBC_STACK_END 1
1076 : #else
1077 : #define HAVE___LIBC_STACK_END 0
1078 : #endif
1079 :
1080 : #if HAVE___LIBC_STACK_END
1081 : extern void *__libc_stack_end; // from ld-linux.so
1082 : #endif
1083 : namespace mozilla {
1084 : nsresult
1085 0 : FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1086 : void *aClosure, void **bp, void *aStackEnd)
1087 : {
1088 : // Stack walking code courtesy Kipp's "leaky".
1089 :
1090 0 : int skip = aSkipFrames;
1091 0 : while (1) {
1092 0 : void **next = (void**)*bp;
1093 : // bp may not be a frame pointer on i386 if code was compiled with
1094 : // -fomit-frame-pointer, so do some sanity checks.
1095 : // (bp should be a frame pointer on ppc(64) but checking anyway may help
1096 : // a little if the stack has been corrupted.)
1097 : // We don't need to check against the begining of the stack because
1098 : // we can assume that bp > sp
1099 0 : if (next <= bp ||
1100 : next > aStackEnd ||
1101 : (long(next) & 3)) {
1102 : break;
1103 : }
1104 : #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
1105 : // ppc mac or powerpc64 linux
1106 : void *pc = *(bp+2);
1107 : #else // i386 or powerpc32 linux
1108 0 : void *pc = *(bp+1);
1109 : #endif
1110 0 : if (IsCriticalAddress(pc)) {
1111 0 : printf("Aborting stack trace, PC is critical\n");
1112 0 : return NS_ERROR_UNEXPECTED;
1113 : }
1114 0 : if (--skip < 0) {
1115 0 : (*aCallback)(pc, aClosure);
1116 : }
1117 0 : bp = next;
1118 : }
1119 0 : return NS_OK;
1120 : }
1121 :
1122 : }
1123 :
1124 : #define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
1125 : #if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
1126 :
1127 : EXPORT_XPCOM_API(nsresult)
1128 0 : NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1129 : void *aClosure, uintptr_t aThread)
1130 : {
1131 0 : MOZ_ASSERT(gCriticalAddress.mInit);
1132 0 : MOZ_ASSERT(!aThread);
1133 :
1134 : // Get the frame pointer
1135 : void **bp;
1136 : #if defined(__i386)
1137 0 : __asm__( "movl %%ebp, %0" : "=g"(bp));
1138 : #else
1139 : // It would be nice if this worked uniformly, but at least on i386 and
1140 : // x86_64, it stopped working with gcc 4.1, because it points to the
1141 : // end of the saved registers instead of the start.
1142 : bp = (void**) __builtin_frame_address(0);
1143 : #endif
1144 :
1145 : void *stackEnd;
1146 : #if HAVE___LIBC_STACK_END
1147 : stackEnd = __libc_stack_end;
1148 : #else
1149 0 : stackEnd = reinterpret_cast<void*>(-1);
1150 : #endif
1151 : return FramePointerStackWalk(aCallback, aSkipFrames,
1152 0 : aClosure, bp, stackEnd);
1153 :
1154 : }
1155 :
1156 : #elif defined(HAVE__UNWIND_BACKTRACE)
1157 :
1158 : // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
1159 : #include <unwind.h>
1160 :
1161 : struct unwind_info {
1162 : NS_WalkStackCallback callback;
1163 : int skip;
1164 : void *closure;
1165 : };
1166 :
1167 : static _Unwind_Reason_Code
1168 : unwind_callback (struct _Unwind_Context *context, void *closure)
1169 : {
1170 : unwind_info *info = static_cast<unwind_info *>(closure);
1171 : void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
1172 : if (IsCriticalAddress(pc)) {
1173 : printf("Aborting stack trace, PC is critical\n");
1174 : /* We just want to stop the walk, so any error code will do.
1175 : Using _URC_NORMAL_STOP would probably be the most accurate,
1176 : but it is not defined on Android for ARM. */
1177 : return _URC_FOREIGN_EXCEPTION_CAUGHT;
1178 : }
1179 : if (--info->skip < 0)
1180 : (*info->callback)(pc, info->closure);
1181 : return _URC_NO_REASON;
1182 : }
1183 :
1184 : EXPORT_XPCOM_API(nsresult)
1185 : NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1186 : void *aClosure, uintptr_t aThread)
1187 : {
1188 : MOZ_ASSERT(gCriticalAddress.mInit);
1189 : MOZ_ASSERT(!aThread);
1190 : unwind_info info;
1191 : info.callback = aCallback;
1192 : info.skip = aSkipFrames + 1;
1193 : info.closure = aClosure;
1194 :
1195 : _Unwind_Reason_Code t = _Unwind_Backtrace(unwind_callback, &info);
1196 : if (t != _URC_END_OF_STACK)
1197 : return NS_ERROR_UNEXPECTED;
1198 : return NS_OK;
1199 : }
1200 :
1201 : #endif
1202 :
1203 : EXPORT_XPCOM_API(nsresult)
1204 0 : NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
1205 : {
1206 0 : aDetails->library[0] = '\0';
1207 0 : aDetails->loffset = 0;
1208 0 : aDetails->filename[0] = '\0';
1209 0 : aDetails->lineno = 0;
1210 0 : aDetails->function[0] = '\0';
1211 0 : aDetails->foffset = 0;
1212 :
1213 : Dl_info info;
1214 0 : int ok = dladdr(aPC, &info);
1215 0 : if (!ok) {
1216 0 : return NS_OK;
1217 : }
1218 :
1219 0 : PL_strncpyz(aDetails->library, info.dli_fname, sizeof(aDetails->library));
1220 0 : aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
1221 :
1222 0 : const char * symbol = info.dli_sname;
1223 : int len;
1224 0 : if (!symbol || !(len = strlen(symbol))) {
1225 0 : return NS_OK;
1226 : }
1227 :
1228 0 : char demangled[4096] = "\0";
1229 :
1230 0 : DemangleSymbol(symbol, demangled, sizeof(demangled));
1231 :
1232 0 : if (strlen(demangled)) {
1233 0 : symbol = demangled;
1234 0 : len = strlen(symbol);
1235 : }
1236 :
1237 0 : PL_strncpyz(aDetails->function, symbol, sizeof(aDetails->function));
1238 0 : aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
1239 0 : return NS_OK;
1240 : }
1241 :
1242 : EXPORT_XPCOM_API(nsresult)
1243 0 : NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
1244 : char *aBuffer, PRUint32 aBufferSize)
1245 : {
1246 0 : if (!aDetails->library[0]) {
1247 0 : snprintf(aBuffer, aBufferSize, "UNKNOWN %p\n", aPC);
1248 0 : } else if (!aDetails->function[0]) {
1249 : snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%08lX]\n",
1250 0 : aDetails->library, aDetails->loffset);
1251 : } else {
1252 : snprintf(aBuffer, aBufferSize, "%s+0x%08lX [%s +0x%08lX]\n",
1253 : aDetails->function, aDetails->foffset,
1254 0 : aDetails->library, aDetails->loffset);
1255 : }
1256 0 : return NS_OK;
1257 : }
1258 :
1259 : #endif
1260 :
1261 : #else // unsupported platform.
1262 :
1263 : EXPORT_XPCOM_API(nsresult)
1264 : NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1265 : void *aClosure, uintptr_t aThread)
1266 : {
1267 : MOZ_ASSERT(gCriticalAddress.mInit);
1268 : MOZ_ASSERT(!aThread);
1269 : return NS_ERROR_NOT_IMPLEMENTED;
1270 : }
1271 :
1272 : namespace mozilla {
1273 : nsresult
1274 : FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
1275 : void *aClosure, void **bp)
1276 : {
1277 : return NS_ERROR_NOT_IMPLEMENTED;
1278 : }
1279 : }
1280 :
1281 : EXPORT_XPCOM_API(nsresult)
1282 : NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
1283 : {
1284 : aDetails->library[0] = '\0';
1285 : aDetails->loffset = 0;
1286 : aDetails->filename[0] = '\0';
1287 : aDetails->lineno = 0;
1288 : aDetails->function[0] = '\0';
1289 : aDetails->foffset = 0;
1290 : return NS_ERROR_NOT_IMPLEMENTED;
1291 : }
1292 :
1293 : EXPORT_XPCOM_API(nsresult)
1294 : NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
1295 : char *aBuffer, PRUint32 aBufferSize)
1296 : {
1297 : aBuffer[0] = '\0';
1298 : return NS_ERROR_NOT_IMPLEMENTED;
1299 : }
1300 :
1301 : #endif
|