[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