[commit] r2370 - Add improved stack trace facilities (store native stack & frame pointers to

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[commit] r2370 - Add improved stack trace facilities (store native stack & frame pointers to

commits-3
 
Author: eliot
Date: 2011-03-18 16:10:49 -0700 (Fri, 18 Mar 2011)
New Revision: 2370

Modified:
   branches/Cog/platforms/Cross/vm/sqVirtualMachine.c
   branches/Cog/platforms/Mac OS/vm/sqMacMain.c
   branches/Cog/platforms/unix/plugins/UUIDPlugin/Makefile.inc
   branches/Cog/platforms/unix/plugins/UUIDPlugin/sqUnixUUID.c
   branches/Cog/platforms/unix/vm/sqUnixMain.c
   branches/Cog/platforms/win32/vm/sqWin32Intel.c
Log:
Add improved stack trace facilities (store native stack & frame pointers to
framePointer and stackPointer if the natve pointers are in the stack zone).
Fix sqUnixUUID.c for CentOS/RedHat (uuid/uuid.h, not sys/uuid.h or plain uuid.h)
Provide push/pop output file for better tracing when debugging.


Modified: branches/Cog/platforms/Cross/vm/sqVirtualMachine.c
===================================================================
--- branches/Cog/platforms/Cross/vm/sqVirtualMachine.c 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/Cross/vm/sqVirtualMachine.c 2011-03-18 23:10:49 UTC (rev 2370)
@@ -469,3 +469,83 @@
 
  return VM;
 }
+
+
+/* This lives here for now but belongs somewhere else.
+ * platforms/Cross/vm/sqStuff.c??
+ */
+#define STDOUT_STACK_SZ 5
+static int stdoutStackIdx = -1;
+static FILE stdoutStack[STDOUT_STACK_SZ];
+
+/* N.B. As of cygwin 1.5.25 fopen("crash.dmp","a") DOES NOT WORK!  crash.dmp
+ * contains garbled output as if the file pointer gets set to the start of the
+ * file, not the end.  So we synthesize our own append mode.
+ */
+#if __MINGW32__
+# include <io.h>
+static FILE *
+fopen_for_append(char *filename)
+{
+ FILE *f = !access(filename, F_OK) /* access is bass ackwards */
+ ? fopen(filename,"r+")
+ : fopen(filename,"w+");
+ if (f)
+ fseek(f,0,SEEK_END);
+ return f;
+}
+#elif defined(WIN32)
+# define fopen_for_append(filename) fopen(filename,"a+t")
+#else
+# define fopen_for_append(filename) fopen(filename,"a+")
+#endif
+
+void
+pushOutputFile(char *filenameOrStdioIndex)
+{
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+# define STDERR_FILENO 2
+#endif
+
+ FILE *output;
+
+ if (stdoutStackIdx + 2 >= STDOUT_STACK_SZ) {
+ fprintf(stderr,"output file stack is full.\n");
+ return;
+ }
+ switch ((unsigned)filenameOrStdioIndex) {
+ case STDOUT_FILENO: output = stdout; break;
+ case STDERR_FILENO: output = stderr; break;
+ default:
+ if (!(output = fopen_for_append(filenameOrStdioIndex))) {
+ fprintf(stderr,
+ "could not open \"%s\" for writing.\n",
+ filenameOrStdioIndex);
+ return;
+ }
+ }
+ stdoutStack[++stdoutStackIdx] = *stdout;
+ *stdout = *output;
+}
+
+void
+popOutputFile()
+{
+ if (stdoutStackIdx < 0) {
+ fprintf(stderr,"output file stack is empty.\n");
+ return;
+ }
+ fflush(stdout);
+ if (fileno(stdout) > STDERR_FILENO) {
+ /* as of Feb 2011 with fclose@@GLIBC_2.1 under e.g. CentOS 5.3, fclose
+ * hangs in _IO_un_link_internal.  This hack avoids that.
+ */
+#if __linux__
+ close(fileno(stdout));
+#else
+ fclose(stdout);
+#endif
+ }
+ *stdout = stdoutStack[stdoutStackIdx--];
+}

Modified: branches/Cog/platforms/Mac OS/vm/sqMacMain.c
===================================================================
--- branches/Cog/platforms/Mac OS/vm/sqMacMain.c 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/Mac OS/vm/sqMacMain.c 2011-03-18 23:10:49 UTC (rev 2370)
@@ -102,6 +102,11 @@
 #if !defined(PATH_MAX)
 # include <sys/syslimits.h>
 #endif
+#if !defined(NOEXECINFO)
+# include <execinfo.h>
+# define BACKTRACE_DEPTH 64
+#endif
+#include <sys/ucontext.h>
 
 extern pthread_mutex_t gEventQueueLock,gSleepLock;
 extern pthread_cond_t  gSleepLockCondition;
@@ -134,41 +139,145 @@
 sqInt printCallStack(void);
 extern void dumpPrimTraceLog(void);
 extern BOOL NSApplicationLoad(void);
+static void fetchPrefrences(void);
 
-/* Print an error message, possibly a stack trace, and exit. */
-/* Disable Intel compiler inlining of error which is used for breakpoints */
-#pragma auto_inline off
-void
-error(char *msg)
+
+/*** errors ***/
+
+/* Print an error message, possibly a stack trace, do /not/ exit.
+ * Allows e.g. writing to a log file and stderr.
+ */
+static void
+reportStackState(char *msg, char *date, int printAll, ucontext_t *uap)
 {
+#if !defined(NOEXECINFO)
+ void *addrs[BACKTRACE_DEPTH];
+ int depth;
+#endif
  /* flag prevents recursive error when trying to print a broken stack */
  static sqInt printingStack = false;
 
- printf("\n%s\n\n", msg);
+ printf("\n%s%s%s\n\n", msg, date ? " " : "", date ? date : "");
 
- printf("\nMost recent primitives\n");
- dumpPrimTraceLog();
+#if !defined(NOEXECINFO)
+ printf("C stack backtrace:\n");
+ fflush(stdout); /* backtrace_symbols_fd uses unbuffered i/o */
+ depth = backtrace(addrs, BACKTRACE_DEPTH);
+ backtrace_symbols_fd(addrs, depth, fileno(stdout));
+#endif
+
  if (ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
  if (!printingStack) {
+#if COGVM
+ /* If we're in generated machine code then the only way the stack
+ * dump machinery has of giving us an accurate report is if we set
+ * stackPointer & framePointer to the native stack & frame pointers.
+ */
+# if __APPLE__ && __MACH__ && __i386__
+# if __GNUC__ /* see sys/ucontext.h; two different namings */
+ void *fp = (void *)(uap ? uap->uc_mcontext->__ss.__ebp: 0);
+ void *sp = (void *)(uap ? uap->uc_mcontext->__ss.__esp: 0);
+# else
+ void *fp = (void *)(uap ? uap->uc_mcontext->ss.ebp: 0);
+ void *sp = (void *)(uap ? uap->uc_mcontext->ss.esp: 0);
+# endif
+# elif __linux__ && __i386__
+ void *fp = (void *)(uap ? uap->uc_mcontext.gregs[REG_EBP]: 0);
+ void *sp = (void *)(uap ? uap->uc_mcontext.gregs[REG_ESP]: 0);
+# else
+# error need to implement extracting pc from a ucontext_t on this system
+# endif
+ char *savedSP, *savedFP;
+
+ ifValidWriteBackStackPointersSaveTo(fp,sp,&savedFP,&savedSP);
+#endif
+
  printingStack = true;
- printf("\n\nSmalltalk stack dump:\n");
- printCallStack();
+ if (printAll) {
+ printf("\n\nAll Smalltalk process stacks (active first):\n");
+ printAllStacks();
+ }
+ else {
+ printf("\n\nSmalltalk stack dump:\n");
+ printCallStack();
+ }
+ printingStack = false;
+#if COGVM
+ /* Now restore framePointer and stackPointer via same function */
+ ifValidWriteBackStackPointersSaveTo(savedFP,savedSP,0,0);
+#endif
  }
  }
  else
- printf("\nCan't dump Smalltalk stack. Not in VM thread\n");
+ printf("\nCan't dump Smalltalk stack(s). Not in VM thread\n");
+ printf("\nMost recent primitives\n");
+ dumpPrimTraceLog();
+ fflush(stdout);
+}
+
+/* Print an error message, possibly a stack trace, and exit. */
+/* Disable Intel compiler inlining of error which is used for breakpoints */
+#pragma auto_inline off
+void
+error(char *msg)
+{
+ reportStackState(msg,0,0,0);
  abort();
 }
 #pragma auto_inline on
 
-static void sigsegv(int ignore)
+/* construct /dir/for/image/crash.dmp if a / in imageName else crash.dmp */
+static void
+getCrashDumpFilenameInto(char *buf)
 {
-#pragma unused(ignore)
+  char *slash;
 
-  error("Segmentation fault");
+  strcpy(buf,imageName);
+  slash = strrchr(buf,'/');
+  strcpy(slash ? slash + 1 : buf, "crash.dmp");
 }
 
+static void
+sigusr1(int sig, siginfo_t *info, ucontext_t *uap)
+{
+ int saved_errno = errno;
+ time_t now = time(NULL);
+ char ctimebuf[32];
+ char crashdump[IMAGE_NAME_SIZE+1];
+ unsigned long pc;
 
+ if (!ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
+ pthread_kill(getVMOSThread(),sig);
+ errno = saved_errno;
+ return;
+ }
+
+ getCrashDumpFilenameInto(crashdump);
+ ctime_r(&now,ctimebuf);
+ pushOutputFile(crashdump);
+ reportStackState("SIGUSR1", ctimebuf, 1, uap);
+ popOutputFile();
+ reportStackState("SIGUSR1", ctimebuf, 1, uap);
+
+ errno = saved_errno;
+}
+
+static void
+sigsegv(int sig, siginfo_t *info, ucontext_t *uap)
+{
+ time_t now = time(NULL);
+ char ctimebuf[32];
+ char crashdump[IMAGE_NAME_SIZE+1];
+
+ getCrashDumpFilenameInto(crashdump);
+ ctime_r(&now,ctimebuf);
+ pushOutputFile(crashdump);
+ reportStackState("Segmentation fault", ctimebuf, 0, uap);
+ popOutputFile();
+ reportStackState("Segmentation fault", ctimebuf, 0, uap);
+ abort();
+}
+
 int main(int argc, char **argv, char **envp);
 
 #if defined(__GNUC__) && ( defined(i386) || defined(__i386) || defined(__i386__)  \
@@ -194,12 +303,16 @@
 # define mtfsfi(fpscr)
 #endif
 
-int main(int argc, char **argv, char **envp) {
+int
+main(int argc, char **argv, char **envp)
+{
  EventRecord theEvent;
  sqImageFile f;
  OSErr err;
  char shortImageName[SHORTIMAGE_NAME_SIZE+1];
 
+ struct sigaction sigusr1_handler_action, sigsegv_handler_action;
+
 #if 0 /* Useful debugging stub?  Dump args to file ~/argvPID. */
   { char fname[PATH_MAX];
  FILE *f;
@@ -224,16 +337,24 @@
  error("This C compiler's time_t's are not 32 bits.");
  }
 
-  /* Make parameters global for access from pluggable primitives */
-  argCnt= argc;
-  argVec= argv;
-  envVec= envp;
+ /* Make parameters global for access from pluggable primitives */
+ argCnt= argc;
+ argVec= argv;
+ envVec= envp;
 
-  signal(SIGSEGV, sigsegv);
+ sigsegv_handler_action.sa_sigaction = sigsegv;
+ sigsegv_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO;
+ sigemptyset(&sigsegv_handler_action.sa_mask);
+    (void)sigaction(SIGSEGV, &sigsegv_handler_action, 0);
 
-  fldcw(0x12bf); /* signed infinity, round to nearest, REAL8, disable intrs, disable signals */
-  mtfsfi(0); /* disable signals, IEEE mode, round to nearest */
+ sigusr1_handler_action.sa_sigaction = sigusr1;
+ sigusr1_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO;
+ sigemptyset(&sigusr1_handler_action.sa_mask);
+    (void)sigaction(SIGUSR1, &sigusr1_handler_action, 0);
 
+ fldcw(0x12bf); /* signed infinity, round to nearest, REAL8, disable intrs, disable signals */
+ mtfsfi(0); /* disable signals, IEEE mode, round to nearest */
+
  LoadScrap();
  SetUpClipboard();
  fetchPrefrences();
@@ -541,7 +662,8 @@
 }
 
 
-void fetchPrefrences() {
+static void
+fetchPrefrences() {
     CFBundleRef  myBundle;
     CFDictionaryRef myDictionary;
     CFNumberRef SqueakWindowType,SqueakMaxHeapSizeType,SqueakUIFlushPrimaryDeferNMilliseconds,SqueakUIFlushSecondaryCleanupDelayMilliseconds,SqueakUIFlushSecondaryCheckForPossibleNeedEveryNMilliseconds,SqueakDebug;

Modified: branches/Cog/platforms/unix/plugins/UUIDPlugin/Makefile.inc
===================================================================
--- branches/Cog/platforms/unix/plugins/UUIDPlugin/Makefile.inc 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/unix/plugins/UUIDPlugin/Makefile.inc 2011-03-18 23:10:49 UTC (rev 2370)
@@ -1 +1,2 @@
+XCFLAGS=-DHAVE_UUID_UUID_H=1
 XLDFLAGS = $(LIB_UUID)

Modified: branches/Cog/platforms/unix/plugins/UUIDPlugin/sqUnixUUID.c
===================================================================
--- branches/Cog/platforms/unix/plugins/UUIDPlugin/sqUnixUUID.c 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/unix/plugins/UUIDPlugin/sqUnixUUID.c 2011-03-18 23:10:49 UTC (rev 2370)
@@ -4,8 +4,11 @@
 # include <sys/types.h>
 # include <sys/uuid.h>
 #endif
+#if defined(HAVE_UUID_UUID_H)
+# include <uuid/uuid.h>
+#endif
 #if defined(HAVE_UUID_H)
-  #include <uuid.h>
+# include <uuid.h>
 #endif
 
 #include "sq.h"

Modified: branches/Cog/platforms/unix/vm/sqUnixMain.c
===================================================================
--- branches/Cog/platforms/unix/vm/sqUnixMain.c 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/unix/vm/sqUnixMain.c 2011-03-18 23:10:49 UTC (rev 2370)
@@ -735,88 +735,150 @@
 
 /*** errors ***/
 
-
 static void outOfMemory(void)
 {
-  fprintf(stderr, "out of memory\n");
-  exit(1);
+  /* pushing stderr outputs the error report on stderr instead of stdout */
+  pushOutputFile((char *)STDERR_FILENO);
+  error("out of memory\n");
 }
 
-static void sigusr1(int ignore)
+/* Print an error message, possibly a stack trace, do /not/ exit.
+ * Allows e.g. writing to a log file and stderr.
+ */
+static void
+reportStackState(char *msg, char *date, int printAll, ucontext_t *uap)
 {
 #if !defined(NOEXECINFO)
  void *addrs[BACKTRACE_DEPTH];
  int depth;
- time_t now = time(NULL);
- /* ctime includes newline */
- printf("\nReceived user signal, printing active C stack at %s:", ctime(&now));
- depth = backtrace(addrs, BACKTRACE_DEPTH);
- backtrace_symbols_fd(addrs, depth, fileno(stdout));
 #endif
- printf("\nReceived user signal, printing active Smalltalk stack:\n\n");
- printCallStack();
- printf("\nReceived user signal, printing all Smalltalk processes:\n\n");
- printAllStacks();
- fflush(stdout);
- fflush(stderr);
-}
-
-/* Print an error message, possibly a stack trace, and exit. */
-/* Disable Intel compiler inlining of error which is used for breakpoints */
-#pragma auto_inline off
-void
-error(char *msg)
-{
-#if !defined(NOEXECINFO)
- void *addrs[BACKTRACE_DEPTH];
- int depth;
-#endif
  /* flag prevents recursive error when trying to print a broken stack */
  static sqInt printingStack = false;
 
- printf("\n%s\n\n", msg);
+ printf("\n%s%s%s\n\n", msg, date ? " " : "", date ? date : "");
 
 #if !defined(NOEXECINFO)
  printf("C stack backtrace:\n");
+ fflush(stdout); /* backtrace_symbols_fd uses unbuffered i/o */
  depth = backtrace(addrs, BACKTRACE_DEPTH);
  backtrace_symbols_fd(addrs, depth, fileno(stdout));
 #endif
 
  if (ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
  if (!printingStack) {
+#if COGVM
+ /* If we're in generated machine code then the only way the stack
+ * dump machinery has of giving us an accurate report is if we set
+ * stackPointer & framePointer to the native stack & frame pointers.
+ */
+# if __APPLE__ && __MACH__ && __i386__
+ void *fp = (void *)(uap ? uap->uc_mcontext->ss.ebp: 0);
+ void *sp = (void *)(uap ? uap->uc_mcontext->ss.esp: 0);
+# elif __linux__ && __i386__
+ void *fp = (void *)(uap ? uap->uc_mcontext.gregs[REG_EBP]: 0);
+ void *sp = (void *)(uap ? uap->uc_mcontext.gregs[REG_ESP]: 0);
+# else
+# error need to implement extracting pc from a ucontext_t on this system
+# endif
+ char *savedSP, *savedFP;
+
+ ifValidWriteBackStackPointersSaveTo(fp,sp,&savedFP,&savedSP);
+#endif
+
  printingStack = true;
- printf("\n\nSmalltalk stack dump:\n");
- printCallStack();
+ if (printAll) {
+ printf("\n\nAll Smalltalk process stacks (active first):\n");
+ printAllStacks();
+ }
+ else {
+ printf("\n\nSmalltalk stack dump:\n");
+ printCallStack();
+ }
+ printingStack = false;
+#if COGVM
+ /* Now restore framePointer and stackPointer via same function */
+ ifValidWriteBackStackPointersSaveTo(savedFP,savedSP,0,0);
+#endif
  }
  }
  else
- printf("\nCan't dump Smalltalk stack. Not in VM thread\n");
+ printf("\nCan't dump Smalltalk stack(s). Not in VM thread\n");
  printf("\nMost recent primitives\n");
  dumpPrimTraceLog();
+ fflush(stdout);
+}
+
+/* Print an error message, possibly a stack trace, and exit. */
+/* Disable Intel compiler inlining of error which is used for breakpoints */
+#pragma auto_inline off
+void
+error(char *msg)
+{
+ reportStackState(msg,0,0,0);
  abort();
 }
 #pragma auto_inline on
 
-static void sigsegv(int ignore)
+/* construct /dir/for/image/crash.dmp if a / in imageName else crash.dmp */
+static void
+getCrashDumpFilenameInto(char *buf)
 {
-#pragma unused(ignore)
+  char *slash;
 
-  error("Segmentation fault");
+  strcpy(buf,imageName);
+  slash = strrchr(buf,'/');
+  strcpy(slash ? slash + 1 : buf, "crash.dmp");
 }
 
+static void
+sigusr1(int sig, siginfo_t *info, ucontext_t *uap)
+{
+ int saved_errno = errno;
+ time_t now = time(NULL);
+ char ctimebuf[32];
+ char crashdump[IMAGE_NAME_SIZE+1];
+ unsigned long pc;
 
-#if defined(IMAGE_DUMP)
+ if (!ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
+ pthread_kill(getVMOSThread(),sig);
+ errno = saved_errno;
+ return;
+ }
 
-static void sighup(int ignore)
-{
-  dumpImageFile= 1;
+ getCrashDumpFilenameInto(crashdump);
+ ctime_r(&now,ctimebuf);
+ pushOutputFile(crashdump);
+ reportStackState("SIGUSR1", ctimebuf, 1, uap);
+ popOutputFile();
+ reportStackState("SIGUSR1", ctimebuf, 1, uap);
+
+ errno = saved_errno;
 }
 
-static void sigquit(int ignore)
+static void
+sigsegv(int sig, siginfo_t *info, ucontext_t *uap)
 {
-  emergencyDump(1);
+ time_t now = time(NULL);
+ char ctimebuf[32];
+ char crashdump[IMAGE_NAME_SIZE+1];
+
+ getCrashDumpFilenameInto(crashdump);
+ ctime_r(&now,ctimebuf);
+ pushOutputFile(crashdump);
+ reportStackState("Segmentation fault", ctimebuf, 0, uap);
+ popOutputFile();
+ reportStackState("Segmentation fault", ctimebuf, 0, uap);
+ abort();
 }
 
+
+
+#if defined(IMAGE_DUMP)
+static void
+sighup(int ignore) { dumpImageFile= 1; }
+
+static void
+sigquit(int ignore) { emergencyDump(1); }
 #endif
 
 
@@ -1645,8 +1707,17 @@
 #endif /* defined(HAVE_LIBDL) && !(STACKVM || COGVM) */
 
   if (installHandlers) {
- signal(SIGSEGV, sigsegv);
- signal(SIGUSR1, sigusr1);
+ struct sigaction sigusr1_handler_action, sigsegv_handler_action;
+
+ sigsegv_handler_action.sa_sigaction = sigsegv;
+ sigsegv_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO;
+ sigemptyset(&sigsegv_handler_action.sa_mask);
+    (void)sigaction(SIGSEGV, &sigsegv_handler_action, 0);
+
+ sigusr1_handler_action.sa_sigaction = sigusr1;
+ sigusr1_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO;
+ sigemptyset(&sigusr1_handler_action.sa_mask);
+    (void)sigaction(SIGUSR1, &sigusr1_handler_action, 0);
   }
 
 #if defined(IMAGE_DUMP)

Modified: branches/Cog/platforms/win32/vm/sqWin32Intel.c
===================================================================
--- branches/Cog/platforms/win32/vm/sqWin32Intel.c 2011-03-18 21:25:53 UTC (rev 2369)
+++ branches/Cog/platforms/win32/vm/sqWin32Intel.c 2011-03-18 23:10:49 UTC (rev 2370)
@@ -955,8 +955,10 @@
 
   TRY {
   if (inVMThread)
- ifValidWriteBackStackPointers((void *)exp->ContextRecord->Ebp,
-  (void *)exp->ContextRecord->Esp);
+ ifValidWriteBackStackPointersSaveTo((void *)exp->ContextRecord->Ebp,
+ (void *)exp->ContextRecord->Esp,
+ 0,
+ 0);
   callstack[0] = (void *)exp->ContextRecord->Eip;
   nframes = backtrace_from_fp((void*)exp->ContextRecord->Ebp,
  callstack+1,