xpcom/base/nsStackWalk.cpp

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

mercurial