2004-11-26 Andrew J. Schorr <ajschorr@alumni.princeton.edu>

	* log.c: (zlog_backtrace) New function to log a backtrace.
	  (zlog_backtrace_safe) Log a backtrace in an async-signal-safe way.
	  Unfortunately, this function does not support syslog logging yet.
	  (zlog_signal) Move backtrace code into separate function
	  zlog_backtrace_safe.
	  (_zlog_assert_failed) Call zlog_backtrace before aborting.
	* log.h: Declare new functions zlog_backtrace and zlog_backtrace_safe.
	* memory.c: (zerror) Call zlog_backtrace before aborting.
diff --git a/lib/ChangeLog b/lib/ChangeLog
index 64c0ae8..19fb03f 100644
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,5 +1,16 @@
 2004-11-26 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
 
+	* log.c: (zlog_backtrace) New function to log a backtrace.
+	  (zlog_backtrace_safe) Log a backtrace in an async-signal-safe way.
+	  Unfortunately, this function does not support syslog logging yet.
+	  (zlog_signal) Move backtrace code into separate function
+	  zlog_backtrace_safe.
+	  (_zlog_assert_failed) Call zlog_backtrace before aborting.
+	* log.h: Declare new functions zlog_backtrace and zlog_backtrace_safe.
+	* memory.c: (zerror) Call zlog_backtrace before aborting.
+
+2004-11-26 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
+
 	* Makefile.am: Need to add zassert.h to pkginclude_HEADERS.
 
 2004-11-25 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
diff --git a/lib/log.c b/lib/log.c
index 9d56680..6623b5c 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -186,14 +186,14 @@
   return str_append(s,len,t);
 }
 
-/* Note: the goal here is to use only async-signal-safe functions. */
+/* Note: the goal here is to use only async-signal-safe functions.
+   Needs to be enhanced to support syslog logging. */
 void
 zlog_signal(int signo, const char *action)
 {
   time_t now;
   char buf[sizeof("DEFAULT: Received signal S at T; aborting...")+60];
   char *s = buf;
-
 #define LOC s,buf+sizeof(buf)-s
 
   time(&now);
@@ -226,18 +226,33 @@
     }
 #undef DUMP
 
-  /* Now try for a backtrace. */
-#ifdef HAVE_GLIBC_BACKTRACE
-  {
-    void *array[20];
-    int size;
+  zlog_backtrace_safe(LOG_ERR);
+#undef LOC
+}
 
-    if ((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0)
-      return;
-    s = buf;
-    s = str_append(LOC,"Backtrace for ");
-    s = num_append(LOC,size);
-    s = str_append(LOC," stack frames:\n");
+/* Log a backtrace using only async-signal-safe functions.
+   Needs to be enhanced to support syslog logging. */
+void
+zlog_backtrace_safe(int priority)
+{
+#ifdef HAVE_GLIBC_BACKTRACE
+  void *array[20];
+  int size;
+  char buf[100];
+  char *s;
+#define LOC s,buf+sizeof(buf)-s
+
+  /* only log this information if it has not been masked out */
+  if (zlog_default && (priority > zlog_default->maskpri))
+    return;
+
+  if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
+      ((size_t)size > sizeof(array)/sizeof(array[0])))
+    return;
+  s = buf;
+  s = str_append(LOC,"Backtrace for ");
+  s = num_append(LOC,size);
+  s = str_append(LOC," stack frames:\n");
 
 #define DUMP(FP) { \
   write(fileno(FP),buf,s-buf);	\
@@ -249,17 +264,50 @@
   else
     {
       if ((zlog_default->flags & ZLOG_FILE) && zlog_default->fp)
-        DUMP(zlog_default->fp)
+	DUMP(zlog_default->fp)
       if (zlog_default->flags & ZLOG_STDOUT)
-        DUMP(stdout)
+	DUMP(stdout)
       if (zlog_default->flags & ZLOG_STDERR)
-        DUMP(stderr)
+	DUMP(stderr)
       /* Is there a signal-safe way to send a syslog message? */
     }
 #undef DUMP
-  }
-#endif /* HAVE_GLIBC_BACKTRACE */
 #undef LOC
+#endif /* HAVE_GLIBC_BACKTRACE */
+}
+
+void
+zlog_backtrace(int priority)
+{
+#ifndef HAVE_GLIBC_BACKTRACE
+  zlog(NULL, priority, "No backtrace available on this platform.");
+#else
+  void *array[20];
+  int size, i;
+  char **strings;
+
+  if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
+      ((size_t)size > sizeof(array)/sizeof(array[0])))
+    {
+      zlog_err("Cannot get backtrace, returned invalid # of frames %d "
+	       "(valid range is between 1 and %u)",
+	       size, sizeof(array)/sizeof(array[0]));
+      return;
+    }
+  zlog(NULL, priority, "Backtrace for %d stack frames:", size);
+  if (!(strings = backtrace_symbols(array, size)))
+    {
+      zlog_err("Cannot get backtrace symbols (out of memory?)");
+      for (i = 0; i < size; i++)
+	zlog(NULL, priority, "[bt %d] %p",i,array[i]);
+    }
+  else
+    {
+      for (i = 0; i < size; i++)
+	zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+      free(strings);
+    }
+#endif /* HAVE_GLIBC_BACKTRACE */
 }
 
 void
@@ -322,6 +370,7 @@
 {
   zlog_err("Assertion `%s' failed in file %s, line %u, function %s",
 	   assertion,file,line,(function ? function : "?"));
+  zlog_backtrace(LOG_ERR);
   abort();
 }
 
diff --git a/lib/log.h b/lib/log.h
index 1fd4fe0..0df75e7 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -125,4 +125,13 @@
 /* To be called when a fatal signal is caught. */
 extern void zlog_signal(int signo, const char *action);
 
+/* Log a backtrace. */
+extern void zlog_backtrace(int priority);
+
+/* Log a backtrace, but in an async-signal-safe way.  Should not be
+   called unless the program is about to exit or abort, since it messes
+   up the state of zlog file pointers.  This function needs to be enhanced
+   to support syslog logging. */
+extern void zlog_backtrace_safe(int priority);
+
 #endif /* _ZEBRA_LOG_H */
diff --git a/lib/memory.c b/lib/memory.c
index 6eb135f..0d23e54 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -46,6 +46,11 @@
   zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n", 
 	    fname, lookup (mstr, type), (int) size, safe_strerror(errno));
   log_memstats(LOG_WARNING);
+  /* N.B. It might be preferable to call zlog_backtrace_safe here, since
+     that function should definitely be safe in an OOM condition.  But
+     unfortunately zlog_backtrace_safe does not support syslog logging at
+     this time... */
+  zlog_backtrace(LOG_WARNING);
   abort();
 }