Since we introduced the API to set signal handler and print a stacktrace, we should avoid glibc's backtrace, which may call malloc. Basically, we choose the way to produce a stacktrace as same as perftools. Also, I removed GetStackFrames, which is not used and not implemented with glibc. git-svn-id: https://google-glog.googlecode.com/svn/trunk@16 eb4d4688-79bd-11dd-afb4-1d65580434c0
191 lines
5.2 KiB
C++
191 lines
5.2 KiB
C++
// Copyright 2008 Google Inc. All Rights Reserved.
|
|
// Author: hamaji@google.com (Shinichiro Hamaji)
|
|
|
|
#include "utilities.h"
|
|
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#include "base/googleinit.h"
|
|
#include "stacktrace.h"
|
|
#include "symbolize.h"
|
|
|
|
using std::string;
|
|
|
|
_START_GOOGLE_NAMESPACE_
|
|
|
|
static const char* g_program_invocation_short_name = NULL;
|
|
static pthread_t g_main_thread_id;
|
|
|
|
// The following APIs are all internal.
|
|
#ifdef HAVE_STACKTRACE
|
|
|
|
#include "stacktrace.h"
|
|
#include "symbolize.h"
|
|
#include "base/commandlineflags.h"
|
|
|
|
DEFINE_bool(symbolize_stacktrace, true,
|
|
"Symbolize the stack trace in the tombstone");
|
|
|
|
typedef void DebugWriter(const char*, void*);
|
|
|
|
// The %p field width for printf() functions is two characters per byte.
|
|
// For some environments, add two extra bytes for the leading "0x".
|
|
static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
|
|
|
|
static void DebugWriteToStderr(const char* data, void *unused) {
|
|
// This one is signal-safe.
|
|
write(STDERR_FILENO, data, strlen(data));
|
|
}
|
|
|
|
// Print a program counter and its symbol name.
|
|
static void DumpPCAndSymbol(DebugWriter *writerfn, void *arg, void *pc,
|
|
const char * const prefix) {
|
|
char tmp[1024];
|
|
const char *symbol = "(unknown)";
|
|
// Symbolizes the previous address of pc because pc may be in the
|
|
// next function. The overrun happens when the function ends with
|
|
// a call to a function annotated noreturn (e.g. CHECK).
|
|
if (Symbolize(reinterpret_cast<char *>(pc) - 1, tmp, sizeof(tmp))) {
|
|
symbol = tmp;
|
|
}
|
|
char buf[1024];
|
|
snprintf(buf, sizeof(buf), "%s@ %*p %s\n",
|
|
prefix, kPrintfPointerFieldWidth, pc, symbol);
|
|
writerfn(buf, arg);
|
|
}
|
|
|
|
// Print a program counter and the corresponding stack frame size.
|
|
static void DumpPCAndFrameSize(DebugWriter *writerfn, void *arg, void *pc,
|
|
int framesize, const char * const prefix) {
|
|
char buf[100];
|
|
if (framesize <= 0) {
|
|
snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n",
|
|
prefix, kPrintfPointerFieldWidth, pc);
|
|
} else {
|
|
snprintf(buf, sizeof(buf), "%s@ %*p %9d\n",
|
|
prefix, kPrintfPointerFieldWidth, pc, framesize);
|
|
}
|
|
writerfn(buf, arg);
|
|
}
|
|
|
|
static void DumpPC(DebugWriter *writerfn, void *arg, void *pc,
|
|
const char * const prefix) {
|
|
char buf[100];
|
|
snprintf(buf, sizeof(buf), "%s@ %*p\n",
|
|
prefix, kPrintfPointerFieldWidth, pc);
|
|
writerfn(buf, arg);
|
|
}
|
|
|
|
// Dump current stack trace as directed by writerfn
|
|
static void DumpStackTrace(int skip_count, DebugWriter *writerfn, void *arg) {
|
|
// Print stack trace
|
|
void* stack[32];
|
|
int depth = GetStackTrace(stack, sizeof(stack)/sizeof(*stack), skip_count+1);
|
|
for (int i = 0; i < depth; i++) {
|
|
#if defined(HAVE_SYMBOLIZE)
|
|
if (FLAGS_symbolize_stacktrace) {
|
|
DumpPCAndSymbol(writerfn, arg, stack[i], " ");
|
|
} else {
|
|
DumpPC(writerfn, arg, stack[i], " ");
|
|
}
|
|
#else
|
|
DumpPC(writerfn, arg, stack[i], " ");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void DumpStackTraceAndExit() {
|
|
DumpStackTrace(1, DebugWriteToStderr, NULL);
|
|
|
|
// Set the default signal handler for SIGABRT, to avoid invoking our
|
|
// own signal handler installed by InstallFailedSignalHandler().
|
|
struct sigaction sig_action = {}; // Zero-clear.
|
|
sigemptyset(&sig_action.sa_mask);
|
|
sig_action.sa_handler = SIG_DFL;
|
|
sigaction(SIGABRT, &sig_action, NULL);
|
|
|
|
abort();
|
|
}
|
|
|
|
#endif // HAVE_STACKTRACE
|
|
|
|
namespace glog_internal_namespace_ {
|
|
|
|
const char* ProgramInvocationShortName() {
|
|
if (g_program_invocation_short_name != NULL) {
|
|
return g_program_invocation_short_name;
|
|
} else {
|
|
// TODO(hamaji): Use /proc/self/cmdline and so?
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
bool IsGoogleLoggingInitialized() {
|
|
return g_program_invocation_short_name != NULL;
|
|
}
|
|
|
|
bool is_default_thread() {
|
|
if (g_program_invocation_short_name == NULL) {
|
|
// InitGoogleLogging() not yet called, so unlikely to be in a different
|
|
// thread
|
|
return true;
|
|
} else {
|
|
return pthread_equal(pthread_self(), g_main_thread_id);
|
|
}
|
|
}
|
|
|
|
int64 CycleClock_Now() {
|
|
// TODO(hamaji): temporary impementation - it might be too slow.
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return static_cast<int64>(tv.tv_sec) * 1000000 + tv.tv_usec;
|
|
}
|
|
|
|
int64 UsecToCycles(int64 usec) {
|
|
return usec;
|
|
}
|
|
|
|
static int32 g_main_thread_pid = getpid();
|
|
int32 GetMainThreadPid() {
|
|
return g_main_thread_pid;
|
|
}
|
|
|
|
static string g_my_user_name;
|
|
const string& MyUserName() {
|
|
return g_my_user_name;
|
|
}
|
|
static void MyUserNameInitializer() {
|
|
// TODO(hamaji): Probably this is not portable.
|
|
const char* user = getenv("USER");
|
|
if (user != NULL) {
|
|
g_my_user_name = user;
|
|
} else {
|
|
g_my_user_name = "invalid-user";
|
|
}
|
|
}
|
|
REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer());
|
|
|
|
} // namespace glog_internal_namespace_
|
|
|
|
void InitGoogleLogging(const char* argv0) {
|
|
const char* slash = strrchr(argv0, '/');
|
|
#ifdef OS_WINDOWS
|
|
if (!slash) slash = strrchr(argv0, '\\');
|
|
#endif
|
|
g_program_invocation_short_name = slash ? slash + 1 : argv0;
|
|
g_main_thread_id = pthread_self();
|
|
|
|
#ifdef HAVE_STACKTRACE
|
|
InstallFailureFunction(&DumpStackTraceAndExit);
|
|
#endif
|
|
}
|
|
|
|
_END_GOOGLE_NAMESPACE_
|
|
|
|
// Make an implementation of stacktrace compiled.
|
|
#ifdef STACKTRACE_H
|
|
# include STACKTRACE_H
|
|
#endif
|