Bug 713278 - Teach NS_StackWalk to walk the stack of other threads too on Windows; r=dbaron
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 23 Dec 2011 18:14:09 -0500
changeset 83454 1b72fc52dfe19f82772772f4428dabcc2fbd3949
parent 83453 2d36b9fd44ca489b57016178b73be95f81f15188
child 83455 7884cb42b9de882164c575cc284916b6ff726459
push id4428
push usereakhgari@mozilla.com
push dateWed, 28 Dec 2011 16:43:11 +0000
treeherdermozilla-inbound@1b72fc52dfe1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs713278
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 713278 - Teach NS_StackWalk to walk the stack of other threads too on Windows; r=dbaron
toolkit/xre/nsSigHandlers.cpp
tools/trace-malloc/lib/nsTraceMalloc.c
xpcom/base/nsStackWalk.cpp
xpcom/base/nsStackWalk.h
xpcom/base/nsTraceRefcntImpl.cpp
--- a/toolkit/xre/nsSigHandlers.cpp
+++ b/toolkit/xre/nsSigHandlers.cpp
@@ -106,17 +106,17 @@ void
 ah_crap_handler(int signum)
 {
   printf("\nProgram %s (pid = %d) received signal %d.\n",
          _progname,
          getpid(),
          signum);
 
   printf("Stack:\n");
-  NS_StackWalk(PrintStackFrame, 2, nsnull);
+  NS_StackWalk(PrintStackFrame, 2, nsnull, 0);
 
   printf("Sleeping for %d seconds.\n",_gdb_sleep_duration);
   printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
          _progname,
          getpid());
 
   sleep(_gdb_sleep_duration);
 
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -957,17 +957,17 @@ backtrace(tm_thread *t, int skip, int *i
 
     t->suppress_tracing++;
 
     if (!stacks_enabled) {
 #if defined(XP_MACOSX)
         /* Walk the stack, even if stacks_enabled is false. We do this to
            check if we must set immediate_abort. */
         info->entries = 0;
-        rv = NS_StackWalk(stack_callback, skip, info);
+        rv = NS_StackWalk(stack_callback, skip, info, 0);
         *immediate_abort = rv == NS_ERROR_UNEXPECTED;
         if (rv == NS_ERROR_UNEXPECTED || info->entries == 0) {
             t->suppress_tracing--;
             return NULL;
         }
 #endif
 
         /*
@@ -992,17 +992,17 @@ backtrace(tm_thread *t, int skip, int *i
          * (when loading a shared library).  So we can't be in tmlock during
          * this call.  For details, see
          * https://bugzilla.mozilla.org/show_bug.cgi?id=374829#c8
          */
 
         /* skip == 0 means |backtrace| should show up, so don't use skip + 1 */
         /* NB: this call is repeated below if the buffer is too small */
         info->entries = 0;
-        rv = NS_StackWalk(stack_callback, skip, info);
+        rv = NS_StackWalk(stack_callback, skip, info, 0);
         *immediate_abort = rv == NS_ERROR_UNEXPECTED;
         if (rv == NS_ERROR_UNEXPECTED || info->entries == 0) {
             t->suppress_tracing--;
             return NULL;
         }
 
         /*
          * To avoid allocating in stack_callback (which, on Windows, is
@@ -1016,17 +1016,17 @@ backtrace(tm_thread *t, int skip, int *i
                                    new_stack_buffer_size * sizeof(void*));
             if (!new_stack_buffer)
                 return NULL;
             info->buffer = new_stack_buffer;
             info->size = new_stack_buffer_size;
 
             /* and call NS_StackWalk again */
             info->entries = 0;
-            NS_StackWalk(stack_callback, skip, info);
+            NS_StackWalk(stack_callback, skip, info, 0);
 
             /* same stack */
             PR_ASSERT(info->entries * 2 == new_stack_buffer_size);
         }
     }
 
     TM_ENTER_LOCK(t);
 
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -128,17 +128,17 @@ my_malloc_logger(uint32_t type, uintptr_
   if (once)
     return;
   once = true;
 
   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
   // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
   const char *name = OnSnowLeopardOrLater() ? "new_sem_from_pool" :
     "pthread_cond_wait$UNIX2003";
-  NS_StackWalk(stack_callback, 0, const_cast<char*>(name));
+  NS_StackWalk(stack_callback, 0, const_cast<char*>(name), 0);
 }
 
 void
 StackWalkInitCriticalAddress()
 {
   if(gCriticalAddress.mInit)
     return;
   gCriticalAddress.mInit = true;
@@ -797,37 +797,44 @@ WalkStackThread(void* aData)
  * chain in aBuffer. For this to work properly, the DLLs must be rebased
  * so that the address in the file agrees with the address in memory.
  * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
  * whose in memory address doesn't match its in-file address.
  */
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure)
+             void *aClosure, uintptr_t aThread)
 {
     MOZ_ASSERT(gCriticalAddress.mInit);
     HANDLE myProcess, myThread;
     DWORD walkerReturn;
     struct WalkStackData data;
 
     if (!EnsureImageHlpInitialized())
         return false;
 
+    HANDLE targetThread;
+    if (aThread) {
+        targetThread = reinterpret_cast<HANDLE> (aThread);
+    } else {
+        targetThread = ::GetCurrentThread();
+    }
+
     // Have to duplicate handle to get a real handle.
     if (!::DuplicateHandle(::GetCurrentProcess(),
                            ::GetCurrentProcess(),
                            ::GetCurrentProcess(),
                            &myProcess,
                            PROCESS_ALL_ACCESS, FALSE, 0)) {
         PrintError("DuplicateHandle (process)");
         return NS_ERROR_FAILURE;
     }
     if (!::DuplicateHandle(::GetCurrentProcess(),
-                           ::GetCurrentThread(),
+                           targetThread,
                            ::GetCurrentProcess(),
                            &myThread,
                            THREAD_ALL_ACCESS, FALSE, 0)) {
         PrintError("DuplicateHandle (thread)");
         ::CloseHandle(myProcess);
         return NS_ERROR_FAILURE;
     }
 
@@ -1476,19 +1483,20 @@ cswalkstack(struct frame *fp, int (*oper
 static void
 cs_operate(int (*operate_func)(void *, void *), void * usrarg)
 {
     cswalkstack(csgetframeptr(), operate_func, usrarg);
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure)
+             void *aClosure, uintptr_t aThread)
 {
     MOZ_ASSERT(gCriticalAddress.mInit);
+    MOZ_ASSERT(!aThread);
     struct my_user_args args;
 
     if (!initialized)
         myinit();
 
     args.callback = aCallback;
     args.skipFrames = aSkipFrames; /* XXX Not handled! */
     args.closure = aClosure;
@@ -1556,19 +1564,20 @@ NS_FormatCodeAddressDetails(void *aPC, c
 #endif
 
 #if HAVE___LIBC_STACK_END
 extern void *__libc_stack_end; // from ld-linux.so
 #endif
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure)
+             void *aClosure, uintptr_t aThread)
 {
   MOZ_ASSERT(gCriticalAddress.mInit);
+  MOZ_ASSERT(!aThread);
   // Stack walking code courtesy Kipp's "leaky".
 
   // Get the frame pointer
   void **bp;
 #if defined(__i386) 
   __asm__( "movl %%ebp, %0" : "=g"(bp));
 #else
   // It would be nice if this worked uniformly, but at least on i386 and
@@ -1634,19 +1643,20 @@ unwind_callback (struct _Unwind_Context 
     }
     if (--info->skip < 0)
         (*info->callback)(pc, info->closure);
     return _URC_NO_REASON;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure)
+             void *aClosure, uintptr_t aThread)
 {
     MOZ_ASSERT(gCriticalAddress.mInit);
+    MOZ_ASSERT(!aThread);
     unwind_info info;
     info.callback = aCallback;
     info.skip = aSkipFrames + 1;
     info.closure = aClosure;
 
     _Unwind_Reason_Code t = _Unwind_Backtrace(unwind_callback, &info);
     if (t != _URC_END_OF_STACK)
         return NS_ERROR_UNEXPECTED;
@@ -1712,19 +1722,20 @@ NS_FormatCodeAddressDetails(void *aPC, c
 }
 
 #endif
 
 #else // unsupported platform.
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure)
+             void *aClosure, uintptr_t aThread)
 {
     MOZ_ASSERT(gCriticalAddress.mInit);
+    MOZ_ASSERT(!aThread);
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
 {
     aDetails->library[0] = '\0';
     aDetails->loffset = 0;
--- a/xpcom/base/nsStackWalk.h
+++ b/xpcom/base/nsStackWalk.h
@@ -38,44 +38,49 @@
 /* API for getting a stack trace of the C/C++ stack on the current thread */
 
 #ifndef nsStackWalk_h_
 #define nsStackWalk_h_
 
 /* WARNING: This file is intended to be included from C or C++ files. */
 
 #include "nscore.h"
+#include <mozilla/StdInt.h>
 
 PR_BEGIN_EXTERN_C
 
 typedef void
 (* NS_WalkStackCallback)(void *aPC, void *aClosure);
 
 /**
  * Call aCallback for the C/C++ stack frames on the current thread, from
  * the caller of NS_StackWalk to main (or above).
  *
  * @param aCallback    Callback function, called once per frame.
  * @param aSkipFrames  Number of initial frames to skip.  0 means that
  *                     the first callback will be for the caller of
  *                     NS_StackWalk.
  * @param aClosure     Caller-supplied data passed through to aCallback.
+ * @param aThread      The thread for which the stack is to be retrieved.
+ *                     Passing null causes us to walk the stack of the
+ *                     current thread. On Windows, this is a thread HANDLE.
+ *                     It is currently not supported on any other platform.
  *
  * Returns NS_ERROR_NOT_IMPLEMENTED on platforms where it is
  * unimplemented.
  * Returns NS_ERROR_UNEXPECTED when the stack indicates that the thread
  * is in a very dangerous situation (e.g., holding sem_pool_lock in 
  * Mac OS X pthreads code). Callers should then bail out immediately.
  *
  * May skip some stack frames due to compiler optimizations or code
  * generation.
  */
 XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure);
+             void *aClosure, uintptr_t aThread);
 
 typedef struct {
     /*
      * The name of the shared library or executable containing an
      * address and the address's offset within that library, or empty
      * string and zero if unknown.
      */
     char library[256];
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -878,17 +878,17 @@ static void PrintStackFrame(void *aPC, v
   fputs(buf, stream);
 }
 
 }
 
 void
 nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 {
-  NS_StackWalk(PrintStackFrame, 2, aStream);
+  NS_StackWalk(PrintStackFrame, 2, aStream, 0);
 }
 
 //----------------------------------------------------------------------
 
 // This thing is exported by libstdc++
 // Yes, this is a gcc only hack
 #if defined(MOZ_DEMANGLE_SYMBOLS)
 #include <cxxabi.h>