lib: include thread information in backtraces

now that we know what thread we're currently executing, let's add that
information to SEGV / assert backtraces.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/lib/log.c b/lib/log.c
index 1058844..04f8fab 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -425,6 +425,40 @@
 			 NULL
 #endif
 			);
+
+  s = buf;
+  if (!thread_current)
+    s = str_append (LOC, "no thread information available\n");
+  else
+    {
+      s = str_append (LOC, "in thread ");
+      s = str_append (LOC, thread_current->funcname);
+      s = str_append (LOC, " scheduled from ");
+      s = str_append (LOC, thread_current->schedfrom);
+      s = str_append (LOC, ":");
+      s = num_append (LOC, thread_current->schedfrom_line);
+      s = str_append (LOC, "\n");
+    }
+
+#define DUMP(FD) write(FD, buf, s-buf);
+  /* If no file logging configured, try to write to fallback log file. */
+  if (logfile_fd >= 0)
+    DUMP(logfile_fd)
+  if (!zlog_default)
+    DUMP(STDERR_FILENO)
+  else
+    {
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
+        DUMP(STDOUT_FILENO)
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+        vty_log_fixed(buf,s-buf);
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+	syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
+    }
+#undef DUMP
+
 #undef PRI
 #undef LOC
 }
@@ -604,6 +638,16 @@
 
 #undef PLOG_FUNC
 
+void zlog_thread_info (int log_level)
+{
+  if (thread_current)
+    zlog(NULL, log_level, "Current thread function %s, scheduled from "
+	 "file %s, line %u", thread_current->funcname,
+	 thread_current->schedfrom, thread_current->schedfrom_line);
+  else
+    zlog(NULL, log_level, "Current thread not known/applicable");
+}
+
 void
 _zlog_assert_failed (const char *assertion, const char *file,
 		     unsigned int line, const char *function)
@@ -616,6 +660,7 @@
   zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
        assertion,file,line,(function ? function : "?"));
   zlog_backtrace(LOG_CRIT);
+  zlog_thread_info(LOG_CRIT);
   abort();
 }
 
diff --git a/lib/log.h b/lib/log.h
index cf247a8..f3b43ad 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -132,6 +132,8 @@
 extern void plog_debug (struct zlog *, const char *format, ...)
   PRINTF_ATTRIBUTE(2, 3);
 
+extern void zlog_thread_info (int log_level);
+
 /* Set logging level for the given destination.  If the log_level
    argument is ZLOG_DISABLED, then the destination is disabled.
    This function should not be used for file logging (use zlog_set_file
diff --git a/lib/thread.c b/lib/thread.c
index 9de5f94..9c3ee82 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -1217,6 +1217,8 @@
 #endif /* HAVE_CLOCK_MONOTONIC */
 }
 
+struct thread *thread_current = NULL;
+
 /* We check thread consumed time. If the system has getrusage, we'll
    use that to get in-depth stats on the performance of the thread in addition
    to wall clock time stats from gettimeofday. */
@@ -1246,7 +1248,9 @@
   GETRUSAGE (&before);
   thread->real = before.real;
 
+  thread_current = thread;
   (*thread->func) (thread);
+  thread_current = NULL;
 
   GETRUSAGE (&after);
 
diff --git a/lib/thread.h b/lib/thread.h
index a088b47..9743a22 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -234,4 +234,8 @@
 extern struct timeval recent_time;
 /* Similar to recent_time, but a monotonically increasing time value */
 extern struct timeval recent_relative_time (void);
+
+/* only for use in logging functions! */
+extern struct thread *thread_current;
+
 #endif /* _ZEBRA_THREAD_H */