platform: port mcastcapture's stacktrace.c

We are switching mcastcapture to using libstacktrace instead of its own
version. Since the latter has recently been improved, I thought it makes
sense to use that version in libstacktrace.

Change-Id: I3bc3929f6dfc3a8f4bdd7d6231fd05e319d1acf4
diff --git a/libstacktrace/Makefile b/libstacktrace/Makefile
index 7ddf116..bd5dfd0 100644
--- a/libstacktrace/Makefile
+++ b/libstacktrace/Makefile
@@ -1,6 +1,7 @@
 # Copyright 2012 Google Inc. All Rights Reserved.
 # Author: irinams@google.com (Irina Stanescu)
 CC=$(CROSS_COMPILE)gcc
+CXX=$(CROSS_COMPILE)g++
 RM=rm -f
 INSTALL=install
 PREFIX=/usr
@@ -9,16 +10,21 @@
 
 all: libstacktrace.so
 
-CFLAGS=-Wall -fPIC -Os -Wextra -Werror -Wswitch-enum
-CPPFLAGS=-DPOSIX -DLINUX -D_DEBUG -DLOGGING=1
-OBJS=$(patsubst %.c,%.o,$(wildcard *.c))
+CFLAGS=-Wall -fPIC -Os -Wextra -Werror -Wswitch-enum $(EXTRACFLAGS)
+CXXFLAGS=-Wall -fPIC -Os -Wextra -Werror -Wswitch-enum -std=c++0x $(EXTRACFLAGS)
+CPPFLAGS=-DPOSIX -DLINUX -D_DEBUG -DLOGGING=1 $(EXTRACFLAGS)
 
 %.o: %.c
 	$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
+%.o: %.cc
+	$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) $< -o $@
 
-libstacktrace.so: $(OBJS)
+libstacktrace.so: stacktrace.o
 	$(CC) -shared -Wl,-soname,libstacktrace.so -Wl,-export-dynamic -o $@ $^
 
+stacktrace_test: stacktrace.o stacktrace_test.o
+	$(CXX) -o $@ $^ $(LDFLAGS) $(CPPFLAGS) -lgtest -lpthread
+
 install: all
 	echo 'target-install=$(INSTALL)'
 	mkdir -p $(LIBDIR)
@@ -30,8 +36,8 @@
 	$(INSTALL) -m 0644 *.h $(INCLUDEDIR)/
 	$(INSTALL) -m 0755 libstacktrace.so $(LIBDIR)/
 
-test: all
-	@echo "Nothing to test."
+test: stacktrace_test
+	./stacktrace_test
 
 clean:
 	$(RM) *.[oa] *.so *~
diff --git a/libstacktrace/stacktrace.c b/libstacktrace/stacktrace.c
index d346d91..85e8c1b 100644
--- a/libstacktrace/stacktrace.c
+++ b/libstacktrace/stacktrace.c
@@ -1,30 +1,33 @@
 #include "stacktrace.h"
-#include <signal.h>
+
 #include <errno.h>
+#include <pthread.h>
+#include <signal.h>
 #include <stdlib.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/types.h>  // for waitpid
+#include <sys/wait.h>  // for waitpid
 #include <unistd.h>
-#include <stdio.h>
 
-#define WRITE(s) do { if (write(2, s, my_strlen(s)) < 0) { perror("write"); } } while (0);
+// We need this because strlen() isn't safe to call from a signal handler.
+static int safe_strlen(const char *s) {
+  int i = 0;
+  while (s[i] != '\0')
+    ++i;
+  return i;
+}
 
-static size_t my_strlen(char* s)
-{
-  size_t length = 0;
-
-  if (s != NULL) {
-    while (*s++ != '\0') {
-      length++;
-    }
-  }
-  return length;
+#define WRITELEN(s, l) do {if (write(2, s, l)) {}} while (0)
+#define WRITE(s) do {if (write(2, s, sizeof(s))) {}} while (0)
+#define WRITEINT(i) { \
+    char *str = format_uint(i); \
+    WRITELEN(str, safe_strlen(str)); \
 }
 
 
 // We need this because sprintf() isn't safe to call from a signal handler.
-static char *format_uint(unsigned int i)
-{
+char *format_uint(unsigned int i) {
   static char str[100];
   char *p = str + sizeof(str) - 1;
   *(--p) = '\0';
@@ -35,25 +38,23 @@
   return p;
 }
 
-
-static pid_t gettid(void)
-{
+static pid_t gettid(void) {
   // According to 'man gettid', this function is not in libc, so you need
   // to call syscall() yourself.  Seems to be true.
   return syscall(__NR_gettid);
 }
 
-
-void stacktrace(void)
-{
+void stacktrace(void) {
   pid_t pid, trace_tid = gettid();
 
   if ((pid = fork()) > 0) {
-    // For some reason, if we call waitpid() here, gdb (7.3.1) isn't able to
-    // get a valid stack trace.  If we use syscall(__NR_waitpid), then it
-    // works fine.
+// For some reason, if we call waitpid() here, gdb (7.3.1) isn't able to
+// get a valid stack trace.  If we use syscall(__NR_waitpid), then it
+// works fine.
 #if defined(__MIPSEL__) || defined(_MIPSEB_)
     syscall(__NR_waitpid, pid, 0);
+#else
+    waitpid(pid, NULL, 0);
 #endif
   } else if (pid == 0) {
     char *argv[] = {(char*)"stacktrace", format_uint(trace_tid), NULL};
@@ -61,15 +62,16 @@
   } else {
     int e = errno;
     WRITE("ERROR: fork failed?!  code=");
-    WRITE(format_uint(e));
+    WRITEINT(e);
   }
 }
 
-
-void stacktrace_sighandler(int sig)
-{
-  WRITE("\nExiting on signal ");
-  WRITE(format_uint(sig));
+void stacktrace_sighandler(int sig) {
+  WRITE("\nExiting thread ");
+  pid_t tid = gettid();
+  WRITEINT(tid);
+  WRITE(" on signal ");
+  WRITEINT(sig);
   WRITE("\n");
 
   stacktrace();
@@ -88,9 +90,7 @@
   abort();
 }
 
-
-void stacktrace_setup(void)
-{
+void stacktrace_setup(void) {
   signal(SIGSEGV, stacktrace_sighandler);
   signal(SIGBUS, stacktrace_sighandler);
   signal(SIGFPE, stacktrace_sighandler);
diff --git a/libstacktrace/stacktrace.h b/libstacktrace/stacktrace.h
index 51968a5..957ba04 100644
--- a/libstacktrace/stacktrace.h
+++ b/libstacktrace/stacktrace.h
@@ -14,6 +14,9 @@
 /* Call this to setup common signal handlers automatically. */
 void stacktrace_setup(void);
 
+/* Need to export this in order to allow testing it */
+char *format_uint(unsigned int i);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libstacktrace/stacktrace_test.cc b/libstacktrace/stacktrace_test.cc
new file mode 100644
index 0000000..dc2571c
--- /dev/null
+++ b/libstacktrace/stacktrace_test.cc
@@ -0,0 +1,24 @@
+#include "stacktrace.h"
+#include "gtest/gtest.h"
+
+TEST(StacktraceTest, Output) {
+  struct format_uint_test {
+    int line;
+    unsigned int uint;
+    const char *expected_str;
+  } test_arr[] = {
+    {__LINE__, 0, "0" },
+    {__LINE__, 1, "1" },
+    {__LINE__, 12, "12" },
+    {__LINE__, 123, "123" },
+    {__LINE__, 1234, "1234" },
+    {__LINE__, 12345, "12345" },
+    {__LINE__, 22865, "22865" },
+    {__LINE__, 54321, "54321" },
+  };
+
+  for (const auto &test_item : test_arr) {
+    EXPECT_STREQ(test_item.expected_str, format_uint(test_item.uint))
+        << test_item.line;
+  }
+}