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;
+ }
+}