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 98809 461dd0c4f2b7ca651e28b3d3b5e3bcbe314a2e7e
parent 98808 a7264c9e51cbb5c40bcc4952ae18d252ac33695e
child 98810 83fc31dccd9147e01c47c429be504d0c60219325
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-esr52@6fdf9985acfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs766579
milestone16.0a1
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