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

commits at squeakvm.org commits at squeakvm.org
Fri Mar 18 23:10:49 UTC 2011


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,



More information about the Vm-dev mailing list