Bug 766579 - Part 1: Add SP param to NS_StackWalk. r=dbaron
authorBenoit Girard <b56girard@gmail.com>
Wed, 27 Jun 2012 16:08:21 -0400
changeset 98832 461dd0c4f2b7ca651e28b3d3b5e3bcbe314a2e7e
parent 98831 a7264c9e51cbb5c40bcc4952ae18d252ac33695e
child 98833 83fc31dccd9147e01c47c429be504d0c60219325
push id11675
push userb56girard@gmail.com
push dateTue, 10 Jul 2012 17:01:54 +0000
treeherdermozilla-inbound@83fc31dccd91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs766579
milestone16.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 766579 - Part 1: Add SP param to NS_StackWalk. r=dbaron
toolkit/xre/nsSigHandlers.cpp
tools/profiler/TableTicker.cpp
tools/trace-malloc/lib/nsTraceMalloc.c
xpcom/base/nsStackWalk.cpp
xpcom/base/nsStackWalk.h
xpcom/base/nsTraceRefcntImpl.cpp
xpcom/threads/HangMonitor.cpp
--- a/toolkit/xre/nsSigHandlers.cpp
+++ b/toolkit/xre/nsSigHandlers.cpp
@@ -51,17 +51,17 @@ static const int kClientChannelFd = 3;
 #if defined(CRAWL_STACK_ON_SIGSEGV)
 
 #include <unistd.h>
 #include "nsISupportsUtils.h"
 #include "nsStackWalk.h"
 
 extern "C" {
 
-static void PrintStackFrame(void *aPC, void *aClosure)
+static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
 {
   char buf[1024];
   nsCodeAddressDetails details;
 
   NS_DescribeCodeAddress(aPC, &details);
   NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
   fputs(buf, stdout);
 }
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -581,17 +581,17 @@ void TableTicker::doBacktrace(ThreadProf
 #ifdef USE_NS_STACKWALK
 typedef struct {
   void** array;
   size_t size;
   size_t count;
 } PCArray;
 
 static
-void StackWalkCallback(void* aPC, void* aClosure)
+void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
 {
   PCArray* array = static_cast<PCArray*>(aClosure);
   if (array->count >= array->size) {
     // too many frames, ignore
     return;
   }
   array->array[array->count++] = aPC;
 }
@@ -605,17 +605,17 @@ void TableTicker::doBacktrace(ThreadProf
   void* pc_array[1000];
   PCArray array = {
     pc_array,
     mozilla::ArrayLength(pc_array),
     0
   };
 
   // Start with the current function.
-  StackWalkCallback(aSample->pc, &array);
+  StackWalkCallback(aSample->pc, aSample->sp, &array);
 
 #ifdef XP_MACOSX
   pthread_t pt = GetProfiledThread(platform_data());
   void *stackEnd = reinterpret_cast<void*>(-1);
   if (pt)
     stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
   nsresult rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
 #else
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -888,17 +888,17 @@ calltree(void **stack, size_t num_stack_
     return NULL;
 }
 
 /*
  * Buffer the stack from top at low index to bottom at high, so that we can
  * reverse it in calltree.
  */
 static void
-stack_callback(void *pc, void *closure)
+stack_callback(void *pc, void *sp, void *closure)
 {
     stack_buffer_info *info = (stack_buffer_info*) closure;
 
     /*
      * If we run out of buffer, keep incrementing entries so that
      * backtrace can call us again with a bigger buffer.
      */
     if (info->entries < info->size)
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -46,17 +46,17 @@ static CriticalAddress gCriticalAddress;
 #include <CoreServices/CoreServices.h>
 
 typedef void
 malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
                 uintptr_t result, uint32_t num_hot_frames_to_skip);
 extern malloc_logger_t *malloc_logger;
 
 static void
-stack_callback(void *pc, void *closure)
+stack_callback(void *pc, void *sp, void *closure)
 {
   const char *name = reinterpret_cast<char *>(closure);
   Dl_info info;
 
   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
   // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
   // correct one is the first that we find on our way up, so the
   // following check for gCriticalAddress.mAddr is critical.
@@ -205,16 +205,19 @@ struct WalkStackData {
   HANDLE thread;
   bool walkCallingThread;
   HANDLE process;
   HANDLE eventStart;
   HANDLE eventEnd;
   void **pcs;
   PRUint32 pc_size;
   PRUint32 pc_count;
+  void **sps;
+  PRUint32 sp_size;
+  PRUint32 sp_count;
 };
 
 void PrintError(char *prefix, WalkStackData* data);
 unsigned int WINAPI WalkStackThread(void* data);
 void WalkStackMain64(struct WalkStackData* data);
 
 
 DWORD gStackWalkThread;
@@ -283,16 +286,17 @@ WalkStackMain64(struct WalkStackData* da
 {
     // Get the context information for the thread. That way we will
     // know where our sp, fp, pc, etc. are and can fill in the
     // STACKFRAME64 with the initial values.
     CONTEXT context;
     HANDLE myProcess = data->process;
     HANDLE myThread = data->thread;
     DWORD64 addr;
+    DWORD64 spaddr;
     STACKFRAME64 frame64;
     // skip our own stack walking frames
     int skip = (data->walkCallingThread ? 3 : 0) + data->skipFrames;
     BOOL ok;
 
     // Get a context for the specified thread.
     memset(&context, 0, sizeof(CONTEXT));
     context.ContextFlags = CONTEXT_FULL;
@@ -344,35 +348,41 @@ WalkStackMain64(struct WalkStackData* da
           &context,
           NULL,
           SymFunctionTableAccess64, // function table access routine
           SymGetModuleBase64,       // module base routine
           0
         );
         LeaveCriticalSection(&gDbgHelpCS);
 
-        if (ok)
+        if (ok) {
             addr = frame64.AddrPC.Offset;
-        else {
+            spaddr = frame64.AddrStack.Offset;
+         } else {
             addr = 0;
+            spaddr = 0;
             PrintError("WalkStack64");
         }
 
         if (!ok || (addr == 0)) {
             break;
         }
 
         if (skip-- > 0) {
             continue;
         }
 
         if (data->pc_count < data->pc_size)
             data->pcs[data->pc_count] = (void*)addr;
         ++data->pc_count;
 
+        if (data->sp_count < data->sp_size)
+            data->sps[data->sp_count] = (void*)spaddr;
+        ++data->sp_count;
+
         if (frame64.AddrReturn.Offset == 0)
             break;
     }
     return;
 }
 
 
 unsigned int WINAPI
@@ -479,16 +489,20 @@ NS_StackWalk(NS_WalkStackCallback aCallb
 
     data.skipFrames = aSkipFrames;
     data.thread = myThread;
     data.process = myProcess;
     void *local_pcs[1024];
     data.pcs = local_pcs;
     data.pc_count = 0;
     data.pc_size = ArrayLength(local_pcs);
+    void *local_sps[1024];
+    data.sps = local_sps;
+    data.sp_count = 0;
+    data.sp_size = ArrayLength(local_pcs);
 
     if (aThread) {
         // If we're walking the stack of another thread, we don't need to
         // use a separate walker thread.
         WalkStackMain64(&data);
 
         if (data.pc_count > data.pc_size) {
             data.pcs = (void**) _alloca(data.pc_count * sizeof(void*));
@@ -507,31 +521,37 @@ NS_StackWalk(NS_WalkStackCallback aCallb
         walkerReturn = ::SignalObjectAndWait(data.eventStart,
                            data.eventEnd, INFINITE, FALSE);
         if (walkerReturn != WAIT_OBJECT_0)
             PrintError("SignalObjectAndWait (1)");
         if (data.pc_count > data.pc_size) {
             data.pcs = (void**) _alloca(data.pc_count * sizeof(void*));
             data.pc_size = data.pc_count;
             data.pc_count = 0;
+            data.sps = (void**) _alloca(data.sp_count * sizeof(void*));
+            data.sp_size = data.sp_count;
+            data.sp_count = 0;
             ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
             walkerReturn = ::SignalObjectAndWait(data.eventStart,
                                data.eventEnd, INFINITE, FALSE);
             if (walkerReturn != WAIT_OBJECT_0)
                 PrintError("SignalObjectAndWait (2)");
         }
 
         ::CloseHandle(data.eventStart);
         ::CloseHandle(data.eventEnd);
     }
 
     ::CloseHandle(myThread);
 
     for (PRUint32 i = 0; i < data.pc_count; ++i)
-        (*aCallback)(data.pcs[i], aClosure);
+        (*aCallback)(data.pcs[i], data.sps[i], aClosure);
+
+    if (data.sp_size > ArrayLength(local_sps))
+        free(data.sps);
 
     return NS_OK;
 }
 
 
 static BOOL CALLBACK callbackEspecial64(
   PCSTR aModuleName,
   DWORD64 aModuleBase,
@@ -818,19 +838,19 @@ void DemangleSymbol(const char * aSymbol
 #include <sys/regset.h>
 #include <sys/stack.h>
 
 static int    load_address ( void * pc, void * arg );
 static struct bucket * newbucket ( void * pc );
 static struct frame * cs_getmyframeptr ( void );
 static void   cs_walk_stack ( void * (*read_func)(char * address),
                               struct frame * fp,
-                              int (*operate_func)(void *, void *),
+                              int (*operate_func)(void *, void *, void *),
                               void * usrarg );
-static void   cs_operate ( void (*operate_func)(void *, void *),
+static void   cs_operate ( void (*operate_func)(void *, void *, void *),
                            void * usrarg );
 
 #ifndef STACK_BIAS
 #define STACK_BIAS 0
 #endif /*STACK_BIAS*/
 
 #define LOGSIZE 4096
 
@@ -948,37 +968,37 @@ csgetframeptr()
 
     /* make sure to return parents frame pointer.... */
 
     return ((struct frame *)((ulong_t)fp->fr_savfp + STACK_BIAS));
 }
 
 
 static void
-cswalkstack(struct frame *fp, int (*operate_func)(void *, void *),
+cswalkstack(struct frame *fp, int (*operate_func)(void *, void *, void *),
     void *usrarg)
 {
 
     while (fp != 0 && fp->fr_savpc != 0) {
 
-        if (operate_func((void *)fp->fr_savpc, usrarg) != 0)
+        if (operate_func((void *)fp->fr_savpc, NULL, usrarg) != 0)
             break;
         /*
          * watch out - libthread stacks look funny at the top
          * so they may not have their STACK_BIAS set
          */
 
         fp = (struct frame *)((ulong_t)fp->fr_savfp +
             (fp->fr_savfp?(ulong_t)STACK_BIAS:0));
     }
 }
 
 
 static void
-cs_operate(int (*operate_func)(void *, void *), void * usrarg)
+cs_operate(int (*operate_func)(void *, void *, void *), void * usrarg)
 {
     cswalkstack(csgetframeptr(), operate_func, usrarg);
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure, uintptr_t aThread)
 {
@@ -1082,17 +1102,21 @@ FramePointerStackWalk(NS_WalkStackCallba
 #else // i386 or powerpc32 linux
     void *pc = *(bp+1);
 #endif
     if (IsCriticalAddress(pc)) {
       printf("Aborting stack trace, PC is critical\n");
       return NS_ERROR_UNEXPECTED;
     }
     if (--skip < 0) {
-      (*aCallback)(pc, aClosure);
+      // Assume that the SP points to the BP of the function
+      // it called. We can't know the exact location of the SP
+      // but this should be sufficient for our use the SP
+      // to order elements on the stack.
+      (*aCallback)(pc, (bp), aClosure);
     }
     bp = next;
   }
   return NS_OK;
 }
 
 }
 
@@ -1139,25 +1163,26 @@ struct unwind_info {
     void *closure;
 };
 
 static _Unwind_Reason_Code
 unwind_callback (struct _Unwind_Context *context, void *closure)
 {
     unwind_info *info = static_cast<unwind_info *>(closure);
     void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
+    // TODO Use something like 'unw_get_reg(&cursor, UNW_REG_SP, &sp)' to get sp
     if (IsCriticalAddress(pc)) {
         printf("Aborting stack trace, PC is critical\n");
         /* We just want to stop the walk, so any error code will do.
            Using _URC_NORMAL_STOP would probably be the most accurate,
            but it is not defined on Android for ARM. */
         return _URC_FOREIGN_EXCEPTION_CAUGHT;
     }
     if (--info->skip < 0)
-        (*info->callback)(pc, info->closure);
+        (*info->callback)(pc, NULL, info->closure);
     return _URC_NO_REASON;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure, uintptr_t aThread)
 {
     MOZ_ASSERT(gCriticalAddress.mInit);
--- a/xpcom/base/nsStackWalk.h
+++ b/xpcom/base/nsStackWalk.h
@@ -10,18 +10,21 @@
 
 /* WARNING: This file is intended to be included from C or C++ files. */
 
 #include "nscore.h"
 #include <mozilla/StandardInteger.h>
 
 PR_BEGIN_EXTERN_C
 
+// aSP will be the best approximation possible of what the stack pointer will be
+// pointing to when the execution returns to executing that at that PC.
+// If no approximation can be made it will be NULL.
 typedef void
-(* NS_WalkStackCallback)(void *aPC, void *aClosure);
+(* NS_WalkStackCallback)(void *aPC, void *aSP, 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
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -831,17 +831,17 @@ static void InitTraceLog(void)
 
   gTraceLock = PR_NewLock();
 }
 
 #endif
 
 extern "C" {
 
-static void PrintStackFrame(void *aPC, void *aClosure)
+static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
 {
   FILE *stream = (FILE*)aClosure;
   nsCodeAddressDetails details;
   char buf[1024];
 
   NS_DescribeCodeAddress(aPC, &details);
   NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
   fputs(buf, stream);
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -101,17 +101,17 @@ Crash()
                                      NS_LITERAL_CSTRING("1"));
 #endif
 
   NS_RUNTIMEABORT("HangMonitor triggered");
 }
 
 #ifdef REPORT_CHROME_HANGS
 static void
-ChromeStackWalker(void *aPC, void *aClosure)
+ChromeStackWalker(void *aPC, void *aSP, void *aClosure)
 {
   MOZ_ASSERT(aClosure);
   Telemetry::HangStack *callStack =
     reinterpret_cast< Telemetry::HangStack* >(aClosure);
   callStack->AppendElement(reinterpret_cast<uintptr_t>(aPC));
 }
 
 static void