diff options
-rw-r--r-- | base/base.gypi | 1 | ||||
-rw-r--r-- | base/debug/stack_trace.h | 5 | ||||
-rw-r--r-- | base/debug/stack_trace_android.cc | 12 | ||||
-rw-r--r-- | base/debug/stack_trace_ios.mm | 55 | ||||
-rw-r--r-- | base/debug/stack_trace_posix.cc | 79 | ||||
-rw-r--r-- | base/debug/stack_trace_win.cc | 22 | ||||
-rw-r--r-- | base/process_util.h | 5 | ||||
-rw-r--r-- | base/process_util_ios.mm | 29 | ||||
-rw-r--r-- | base/process_util_posix.cc | 84 | ||||
-rw-r--r-- | base/process_util_unittest.cc | 3 | ||||
-rw-r--r-- | base/process_util_win.cc | 21 | ||||
-rw-r--r-- | base/test/test_suite.cc | 3 | ||||
-rw-r--r-- | content/renderer/renderer_main.cc | 3 | ||||
-rw-r--r-- | webkit/support/webkit_support.cc | 3 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell_main.cc | 3 |
15 files changed, 185 insertions, 143 deletions
diff --git a/base/base.gypi b/base/base.gypi index b8b5fa0..0798fa8 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -110,6 +110,7 @@ 'debug/stack_trace.cc', 'debug/stack_trace.h', 'debug/stack_trace_android.cc', + 'debug/stack_trace_ios.mm', 'debug/stack_trace_posix.cc', 'debug/stack_trace_win.cc', 'debug/trace_event.cc', diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h index e192868..60d5821 100644 --- a/base/debug/stack_trace.h +++ b/base/debug/stack_trace.h @@ -18,6 +18,11 @@ struct _EXCEPTION_POINTERS; namespace base { namespace debug { +// Enables stack dump to console output on exception and signals. +// When enabled, the process will quit immediately. This is meant to be used in +// unit_tests only! This is not thread-safe: only call from main thread. +BASE_EXPORT bool EnableInProcessStackDumping(); + // A stacktrace can be helpful in debugging. For example, you can include a // stacktrace member in a object (probably around #ifndef NDEBUG) so that you // can later see where the given object was created from. diff --git a/base/debug/stack_trace_android.cc b/base/debug/stack_trace_android.cc index 7d74bec5..cc03d60 100644 --- a/base/debug/stack_trace_android.cc +++ b/base/debug/stack_trace_android.cc @@ -13,6 +13,18 @@ namespace base { namespace debug { +bool EnableInProcessStackDumping() { + // When running in an application, our code typically expects SIGPIPE + // to be ignored. Therefore, when testing that same code, it should run + // with SIGPIPE ignored as well. + // TODO(phajdan.jr): De-duplicate this SIGPIPE code. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigemptyset(&action.sa_mask); + return (sigaction(SIGPIPE, &action, NULL) == 0); +} + StackTrace::StackTrace() { } diff --git a/base/debug/stack_trace_ios.mm b/base/debug/stack_trace_ios.mm new file mode 100644 index 0000000..ab0abc4 --- /dev/null +++ b/base/debug/stack_trace_ios.mm @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#import <Foundation/Foundation.h> +#include <mach/task.h> +#include <stdio.h> + +#include "base/logging.h" + +// This is just enough of a shim to let the support needed by test_support +// link. + +namespace base { +namespace debug { + +namespace { + +void StackDumpSignalHandler(int signal) { + // TODO(phajdan.jr): Fix async-signal unsafety. + LOG(ERROR) << "Received signal " << signal; + NSArray *stack_symbols = [NSThread callStackSymbols]; + for (NSString* stack_symbol in stack_symbols) { + fprintf(stderr, "\t%s\n", [stack_symbol UTF8String]); + } + _exit(1); +} + +} // namespace + +// TODO(phajdan.jr): Deduplicate, see copy in stack_trace_posix.cc. +bool EnableInProcessStackDumping() { + // When running in an application, our code typically expects SIGPIPE + // to be ignored. Therefore, when testing that same code, it should run + // with SIGPIPE ignored as well. + struct sigaction action; + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + bool success = (sigaction(SIGPIPE, &action, NULL) == 0); + + success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR); + success &= (signal(SIGABRT, &StackDumpSignalHandler) != SIG_ERR); + success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR); + success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR); + success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR); + success &= (signal(SIGSYS, &StackDumpSignalHandler) != SIG_ERR); + + return success; +} + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc index aef80b3..ff756fc0 100644 --- a/base/debug/stack_trace_posix.cc +++ b/base/debug/stack_trace_posix.cc @@ -7,6 +7,7 @@ #include <errno.h> #include <execinfo.h> #include <fcntl.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/param.h> @@ -145,8 +146,86 @@ bool GetBacktraceStrings(void *const *trace, int size, return symbolized; } +void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { + if (BeingDebugged()) + BreakDebugger(); + +#if defined(OS_MACOSX) + // TODO(phajdan.jr): Fix async-signal non-safety (http://crbug.com/101155). + DLOG(ERROR) << "Received signal " << signal; + StackTrace().PrintBacktrace(); +#endif + + // TODO(shess): Port to Linux. +#if defined(OS_MACOSX) + // TODO(shess): Port to 64-bit. +#if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS + char buf[1024]; + size_t len; + + // NOTE: Even |snprintf()| is not on the approved list for signal + // handlers, but buffered I/O is definitely not on the list due to + // potential for |malloc()|. + len = static_cast<size_t>( + snprintf(buf, sizeof(buf), + "ax: %x, bx: %x, cx: %x, dx: %x\n", + context->uc_mcontext->__ss.__eax, + context->uc_mcontext->__ss.__ebx, + context->uc_mcontext->__ss.__ecx, + context->uc_mcontext->__ss.__edx)); + write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); + + len = static_cast<size_t>( + snprintf(buf, sizeof(buf), + "di: %x, si: %x, bp: %x, sp: %x, ss: %x, flags: %x\n", + context->uc_mcontext->__ss.__edi, + context->uc_mcontext->__ss.__esi, + context->uc_mcontext->__ss.__ebp, + context->uc_mcontext->__ss.__esp, + context->uc_mcontext->__ss.__ss, + context->uc_mcontext->__ss.__eflags)); + write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); + + len = static_cast<size_t>( + snprintf(buf, sizeof(buf), + "ip: %x, cs: %x, ds: %x, es: %x, fs: %x, gs: %x\n", + context->uc_mcontext->__ss.__eip, + context->uc_mcontext->__ss.__cs, + context->uc_mcontext->__ss.__ds, + context->uc_mcontext->__ss.__es, + context->uc_mcontext->__ss.__fs, + context->uc_mcontext->__ss.__gs)); + write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); +#endif // ARCH_CPU_32_BITS +#endif // defined(OS_MACOSX) + _exit(1); +} + } // namespace +#if !defined(OS_IOS) +bool EnableInProcessStackDumping() { + // When running in an application, our code typically expects SIGPIPE + // to be ignored. Therefore, when testing that same code, it should run + // with SIGPIPE ignored as well. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigemptyset(&action.sa_mask); + bool success = (sigaction(SIGPIPE, &action, NULL) == 0); + + sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler); + success &= (signal(SIGILL, handler) != SIG_ERR); + success &= (signal(SIGABRT, handler) != SIG_ERR); + success &= (signal(SIGFPE, handler) != SIG_ERR); + success &= (signal(SIGBUS, handler) != SIG_ERR); + success &= (signal(SIGSEGV, handler) != SIG_ERR); + success &= (signal(SIGSYS, handler) != SIG_ERR); + + return success; +} +#endif // !defined(OS_IOS) + StackTrace::StackTrace() { // Though the backtrace API man page does not list any possible negative // return values, we take no chance. diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc index 8069da7..da75b0b 100644 --- a/base/debug/stack_trace_win.cc +++ b/base/debug/stack_trace_win.cc @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/memory/singleton.h" +#include "base/process_util.h" #include "base/synchronization/lock.h" namespace base { @@ -19,6 +20,19 @@ namespace debug { namespace { +// Previous unhandled filter. Will be called if not NULL when we intercept an +// exception. Only used in unit tests. +LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; + +// Prints the exception call stack. +// This is the unit tests exception filter. +long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { + debug::StackTrace(info).PrintBacktrace(); + if (g_previous_filter) + return g_previous_filter(info); + return EXCEPTION_CONTINUE_SEARCH; +} + // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family // of functions. The Sym* family of functions may only be invoked by one // thread at a time. SymbolContext code may access a symbol server over the @@ -135,6 +149,14 @@ class SymbolContext { } // namespace +bool EnableInProcessStackDumping() { + // Add stack dumping support on exception on windows. Similar to OS_POSIX + // signal() handling in process_util_posix.cc. + g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter); + RouteStdioToConsole(); + return true; +} + // Disable optimizations for the StackTrace::StackTrace function. It is // important to disable at least frame pointer optimization ("y"), since // that breaks CaptureStackBackTrace() and prevents StackTrace from working diff --git a/base/process_util.h b/base/process_util.h index 3fb7e2ba..3a1bc26 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -842,11 +842,6 @@ BASE_EXPORT void EnableTerminationOnHeapCorruption(); // Turns on process termination if memory runs out. BASE_EXPORT void EnableTerminationOnOutOfMemory(); -// Enables stack dump to console output on exception and signals. -// When enabled, the process will quit immediately. This is meant to be used in -// unit_tests only! This is not thread-safe: only call from main thread. -BASE_EXPORT bool EnableInProcessStackDumping(); - // If supported on the platform, and the user has sufficent rights, increase // the current process's scheduling priority to a high priority. BASE_EXPORT void RaiseProcessToHighPriority(); diff --git a/base/process_util_ios.mm b/base/process_util_ios.mm index 6cc4926..161d396 100644 --- a/base/process_util_ios.mm +++ b/base/process_util_ios.mm @@ -17,15 +17,6 @@ namespace base { namespace { -void StackDumpSignalHandler(int signal) { - LOG(ERROR) << "Received signal " << signal; - NSArray *stack_symbols = [NSThread callStackSymbols]; - for (NSString* stack_symbol in stack_symbols) { - fprintf(stderr, "\t%s\n", [stack_symbol UTF8String]); - } - _exit(1); -} - bool GetTaskInfo(task_basic_info_64* task_info_data) { mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; kern_return_t kr = task_info(mach_task_self(), @@ -53,26 +44,6 @@ void EnableTerminationOnOutOfMemory() { // iOS provides this for free! } -bool EnableInProcessStackDumping() { - // When running in an application, our code typically expects SIGPIPE - // to be ignored. Therefore, when testing that same code, it should run - // with SIGPIPE ignored as well. - struct sigaction action; - action.sa_handler = SIG_IGN; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); - bool success = (sigaction(SIGPIPE, &action, NULL) == 0); - - success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGABRT, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGSYS, &StackDumpSignalHandler) != SIG_ERR); - - return success; -} - void RaiseProcessToHighPriority() { // Impossible on iOS. Do nothing. } diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index 8b29b14..3d5c930 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -135,69 +135,11 @@ int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, return status; } -// Android has built-in crash handling. -#if !defined(OS_ANDROID) -void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { - if (debug::BeingDebugged()) - debug::BreakDebugger(); - -#if defined(OS_MACOSX) - // TODO(phajdan.jr): Fix async-signal non-safety (http://crbug.com/101155). - DLOG(ERROR) << "Received signal " << signal; - debug::StackTrace().PrintBacktrace(); -#endif - - // TODO(shess): Port to Linux. -#if defined(OS_MACOSX) - // TODO(shess): Port to 64-bit. -#if ARCH_CPU_32_BITS - char buf[1024]; - size_t len; - - // NOTE: Even |snprintf()| is not on the approved list for signal - // handlers, but buffered I/O is definitely not on the list due to - // potential for |malloc()|. - len = static_cast<size_t>( - snprintf(buf, sizeof(buf), - "ax: %x, bx: %x, cx: %x, dx: %x\n", - context->uc_mcontext->__ss.__eax, - context->uc_mcontext->__ss.__ebx, - context->uc_mcontext->__ss.__ecx, - context->uc_mcontext->__ss.__edx)); - write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); - - len = static_cast<size_t>( - snprintf(buf, sizeof(buf), - "di: %x, si: %x, bp: %x, sp: %x, ss: %x, flags: %x\n", - context->uc_mcontext->__ss.__edi, - context->uc_mcontext->__ss.__esi, - context->uc_mcontext->__ss.__ebp, - context->uc_mcontext->__ss.__esp, - context->uc_mcontext->__ss.__ss, - context->uc_mcontext->__ss.__eflags)); - write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); - - len = static_cast<size_t>( - snprintf(buf, sizeof(buf), - "ip: %x, cs: %x, ds: %x, es: %x, fs: %x, gs: %x\n", - context->uc_mcontext->__ss.__eip, - context->uc_mcontext->__ss.__cs, - context->uc_mcontext->__ss.__ds, - context->uc_mcontext->__ss.__es, - context->uc_mcontext->__ss.__fs, - context->uc_mcontext->__ss.__gs)); - write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); -#endif // ARCH_CPU_32_BITS -#endif // defined(OS_MACOSX) - _exit(1); -} -#endif // !defined(OS_ANDROID) - void ResetChildSignalHandlersToDefaults() { // The previous signal handlers are likely to be meaningless in the child's // context so we reset them to the defaults for now. http://crbug.com/44953 // These signal handlers are set up at least in browser_main_posix.cc: - // BrowserMainPartsPosix::PreEarlyInitialization and process_util_posix.cc: + // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc: // EnableInProcessStackDumping. signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); @@ -806,30 +748,6 @@ void LaunchSynchronize(LaunchSynchronizationHandle handle) { ProcessMetrics::~ProcessMetrics() { } -bool EnableInProcessStackDumping() { - // When running in an application, our code typically expects SIGPIPE - // to be ignored. Therefore, when testing that same code, it should run - // with SIGPIPE ignored as well. - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = SIG_IGN; - sigemptyset(&action.sa_mask); - bool success = (sigaction(SIGPIPE, &action, NULL) == 0); - - // Android has built-in crash handling, so no need to hook the signals. -#if !defined(OS_ANDROID) - sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler); - success &= (signal(SIGILL, handler) != SIG_ERR); - success &= (signal(SIGABRT, handler) != SIG_ERR); - success &= (signal(SIGFPE, handler) != SIG_ERR); - success &= (signal(SIGBUS, handler) != SIG_ERR); - success &= (signal(SIGSEGV, handler) != SIG_ERR); - success &= (signal(SIGSYS, handler) != SIG_ERR); -#endif - - return success; -} - void RaiseProcessToHighPriority() { // On POSIX, we don't actually do anything here. We could try to nice() or // setpriority() or sched_getscheduler, but these all require extra rights. diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index f729c1a..0242479 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/debug/alias.h" +#include "base/debug/stack_trace.h" #include "base/eintr_wrapper.h" #include "base/file_path.h" #include "base/logging.h" @@ -284,7 +285,7 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { base::CloseProcessHandle(handle); // Reset signal handlers back to "normal". - base::EnableInProcessStackDumping(); + base::debug::EnableInProcessStackDumping(); remove(signal_file.c_str()); } #endif // !defined(OS_MACOSX) diff --git a/base/process_util_win.cc b/base/process_util_win.cc index da2b6e6..a1a55ea 100644 --- a/base/process_util_win.cc +++ b/base/process_util_win.cc @@ -56,19 +56,6 @@ const DWORD kProcessKilledExitCode = 1; // HeapSetInformation function pointer. typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); -// Previous unhandled filter. Will be called if not NULL when we intercept an -// exception. Only used in unit tests. -LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; - -// Prints the exception call stack. -// This is the unit tests exception filter. -long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { - debug::StackTrace(info).PrintBacktrace(); - if (g_previous_filter) - return g_previous_filter(info); - return EXCEPTION_CONTINUE_SEARCH; -} - void OnNoMemory() { // Kill the process. This is important for security, since WebKit doesn't // NULL-check many memory allocations. If a malloc fails, returns NULL, and @@ -949,14 +936,6 @@ void EnableTerminationOnOutOfMemory() { std::set_new_handler(&OnNoMemory); } -bool EnableInProcessStackDumping() { - // Add stack dumping support on exception on windows. Similar to OS_POSIX - // signal() handling in process_util_posix.cc. - g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter); - RouteStdioToConsole(); - return true; -} - void RaiseProcessToHighPriority() { SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); } diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index 2ab5c36..824fbc6 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/debug/debug_on_start_win.h" #include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" #include "base/file_path.h" #include "base/i18n/icu_util.h" #include "base/logging.h" @@ -270,7 +271,7 @@ void TestSuite::Initialize() { logging::SetLogItems(true, true, true, true); #endif // else defined(OS_ANDROID) - CHECK(base::EnableInProcessStackDumping()); + CHECK(base::debug::EnableInProcessStackDumping()); #if defined(OS_WIN) // Make sure we run with high resolution timer to minimize differences // between production code and test code. diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc index 1191ce4..a63a270 100644 --- a/content/renderer/renderer_main.cc +++ b/content/renderer/renderer_main.cc @@ -5,6 +5,7 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" #include "base/debug/trace_event.h" #include "base/hi_res_timer_manager.h" #include "base/i18n/rtl.h" @@ -219,7 +220,7 @@ int RendererMain(const MainFunctionParams& parameters) { // For convenience, we print the stack trace for crashes. We can't get // symbols when the sandbox is enabled, so only try when the sandbox is // disabled. - base::EnableInProcessStackDumping(); + base::debug::EnableInProcessStackDumping(); #endif } #if defined(OS_POSIX) && !defined(OS_MACOSX) diff --git a/webkit/support/webkit_support.cc b/webkit/support/webkit_support.cc index 97b963d..b1603e8 100644 --- a/webkit/support/webkit_support.cc +++ b/webkit/support/webkit_support.cc @@ -10,6 +10,7 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/i18n/icu_util.h" @@ -297,7 +298,7 @@ TestEnvironment* test_environment; void SetUpTestEnvironmentImpl(bool unit_test_mode, WebKit::Platform* shadow_platform_delegate) { - base::EnableInProcessStackDumping(); + base::debug::EnableInProcessStackDumping(); base::EnableTerminationOnHeapCorruption(); // Initialize the singleton CommandLine with fixed values. Some code refer to diff --git a/webkit/tools/test_shell/test_shell_main.cc b/webkit/tools/test_shell/test_shell_main.cc index 2d66087..baccf0b 100644 --- a/webkit/tools/test_shell/test_shell_main.cc +++ b/webkit/tools/test_shell/test_shell_main.cc @@ -5,6 +5,7 @@ #include "base/at_exit.h" #include "base/basictypes.h" #include "base/command_line.h" +#include "base/debug/stack_trace.h" #include "base/debug/trace_event.h" #include "base/environment.h" #include "base/event_recorder.h" @@ -67,7 +68,7 @@ void RemoveSharedMemoryFile(std::string& filename) { } // namespace int main(int argc, char* argv[]) { - base::EnableInProcessStackDumping(); + base::debug::EnableInProcessStackDumping(); base::EnableTerminationOnHeapCorruption(); // Some tests may use base::Singleton<>, thus we need to instanciate |