summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-27 18:03:47 +0000
committermaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-27 18:03:47 +0000
commitd6fc9fd286eb7a3a6cad937da3b99cf8f4acd5c2 (patch)
treeff8de9ff438626c6d26a9df0acb94fadfd640431
parent30be1ce62471386dbdebf8d8f4f87e31a772661c (diff)
downloadchromium_src-d6fc9fd286eb7a3a6cad937da3b99cf8f4acd5c2.zip
chromium_src-d6fc9fd286eb7a3a6cad937da3b99cf8f4acd5c2.tar.gz
chromium_src-d6fc9fd286eb7a3a6cad937da3b99cf8f4acd5c2.tar.bz2
Move console stack dumping code to a function so it can be reused in test_shell_tests.
TEST=none BUG=13770 Review URL: http://codereview.chromium.org/339024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30220 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/process_util.h9
-rw-r--r--base/process_util_posix.cc144
-rw-r--r--base/process_util_win.cc64
-rw-r--r--base/test/test_suite.h56
-rw-r--r--webkit/tools/test_shell/run_all_tests.cc3
-rw-r--r--webkit/tools/test_shell/test_shell.gyp5
-rw-r--r--webkit/tools/test_shell/test_shell_main.cc7
-rw-r--r--webkit/tools/test_shell/test_shell_test.h2
8 files changed, 170 insertions, 120 deletions
diff --git a/base/process_util.h b/base/process_util.h
index caf39e8..cc91c67 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -420,10 +420,17 @@ class ProcessMetrics {
// Note: Returns true on Windows 2000 without doing anything.
bool EnableLowFragmentationHeap();
-// Enable 'terminate on heap corruption' flag. Helps protect against heap
+// Enables 'terminate on heap corruption' flag. Helps protect against heap
// overflow. Has no effect if the OS doesn't provide the necessary facility.
void EnableTerminationOnHeapCorruption();
+#if defined(UNIT_TEST)
+// 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!
+bool EnableInProcessStackDumping();
+#endif // defined(UNIT_TEST)
+
// If supported on the platform, and the user has sufficent rights, increase
// the current process's scheduling priority to a high priority.
void RaiseProcessToHighPriority();
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index d365d01..d5eb4ee 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -16,7 +16,7 @@
#include <limits>
#include <set>
-#include "base/basictypes.h"
+#include "base/debug_util.h"
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/platform_thread.h"
@@ -30,6 +30,68 @@ const int kMicrosecondsPerSecond = 1000000;
namespace base {
+namespace {
+
+int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
+ bool* success) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait_milliseconds| for the proces to exit. The child process may
+ // exit sometime before the timeout has ended but we may still block for
+ // up to 0.25 seconds after the fact.
+ //
+ // waitpid() has no direct support on POSIX for specifying a timeout, you can
+ // either ask it to block indefinitely or return immediately (WNOHANG).
+ // When a child process terminates a SIGCHLD signal is sent to the parent.
+ // Catching this signal would involve installing a signal handler which may
+ // affect other parts of the application and would be difficult to debug.
+ //
+ // Our strategy is to call waitpid() once up front to check if the process
+ // has already exited, otherwise to loop for wait_milliseconds, sleeping for
+ // at most 0.25 secs each time using usleep() and then calling waitpid().
+ //
+ // usleep() is speced to exit if a signal is received for which a handler
+ // has been installed. This means that when a SIGCHLD is sent, it will exit
+ // depending on behavior external to this function.
+ //
+ // This function is used primarily for unit tests, if we want to use it in
+ // the application itself it would probably be best to examine other routes.
+ int status = -1;
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ static const int64 kQuarterSecondInMicroseconds = kMicrosecondsPerSecond / 4;
+
+ // If the process hasn't exited yet, then sleep and try again.
+ Time wakeup_time = Time::Now() + TimeDelta::FromMilliseconds(
+ wait_milliseconds);
+ while (ret_pid == 0) {
+ Time now = Time::Now();
+ if (now > wakeup_time)
+ break;
+ // Guaranteed to be non-negative!
+ int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
+ // Don't sleep for more than 0.25 secs at a time.
+ if (sleep_time_usecs > kQuarterSecondInMicroseconds) {
+ sleep_time_usecs = kQuarterSecondInMicroseconds;
+ }
+
+ // usleep() will return 0 and set errno to EINTR on receipt of a signal
+ // such as SIGCHLD.
+ usleep(sleep_time_usecs);
+ ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ }
+
+ if (success)
+ *success = (ret_pid != -1);
+
+ return status;
+}
+
+void StackDumpSignalHandler(int signal) {
+ StackTrace().PrintBacktrace();
+ _exit(1);
+}
+
+} // namespace
+
ProcessId GetCurrentProcId() {
return getpid();
}
@@ -323,6 +385,29 @@ void EnableTerminationOnHeapCorruption() {
// On POSIX, there nothing to do AFAIK.
}
+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);
+
+ // TODO(phajdan.jr): Catch other crashy signals, like SIGABRT.
+ success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR);
+ return success;
+}
+
+void AttachToConsole() {
+ // On POSIX, there nothing to do AFAIK. Maybe create a new console if none
+ // exist?
+}
+
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.
@@ -381,63 +466,6 @@ bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
return false;
}
-namespace {
-
-int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
- bool* success) {
- // This POSIX version of this function only guarantees that we wait no less
- // than |wait_milliseconds| for the proces to exit. The child process may
- // exit sometime before the timeout has ended but we may still block for
- // up to 0.25 seconds after the fact.
- //
- // waitpid() has no direct support on POSIX for specifying a timeout, you can
- // either ask it to block indefinitely or return immediately (WNOHANG).
- // When a child process terminates a SIGCHLD signal is sent to the parent.
- // Catching this signal would involve installing a signal handler which may
- // affect other parts of the application and would be difficult to debug.
- //
- // Our strategy is to call waitpid() once up front to check if the process
- // has already exited, otherwise to loop for wait_milliseconds, sleeping for
- // at most 0.25 secs each time using usleep() and then calling waitpid().
- //
- // usleep() is speced to exit if a signal is received for which a handler
- // has been installed. This means that when a SIGCHLD is sent, it will exit
- // depending on behavior external to this function.
- //
- // This function is used primarily for unit tests, if we want to use it in
- // the application itself it would probably be best to examine other routes.
- int status = -1;
- pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
- static const int64 kQuarterSecondInMicroseconds = kMicrosecondsPerSecond/4;
-
- // If the process hasn't exited yet, then sleep and try again.
- Time wakeup_time = Time::Now() + TimeDelta::FromMilliseconds(
- wait_milliseconds);
- while (ret_pid == 0) {
- Time now = Time::Now();
- if (now > wakeup_time)
- break;
- // Guaranteed to be non-negative!
- int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
- // Don't sleep for more than 0.25 secs at a time.
- if (sleep_time_usecs > kQuarterSecondInMicroseconds) {
- sleep_time_usecs = kQuarterSecondInMicroseconds;
- }
-
- // usleep() will return 0 and set errno to EINTR on receipt of a signal
- // such as SIGCHLD.
- usleep(sleep_time_usecs);
- ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
- }
-
- if (success)
- *success = (ret_pid != -1);
-
- return status;
-}
-
-} // namespace
-
bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) {
bool waitpid_success;
int status;
diff --git a/base/process_util_win.cc b/base/process_util_win.cc
index eaa6c88..ea1df60 100644
--- a/base/process_util_win.cc
+++ b/base/process_util_win.cc
@@ -4,15 +4,22 @@
#include "base/process_util.h"
+#include <fcntl.h>
+#include <io.h>
#include <windows.h>
#include <winternl.h>
#include <psapi.h>
+#include <ios>
+
+#include "base/debug_util.h"
#include "base/histogram.h"
#include "base/logging.h"
#include "base/scoped_handle_win.h"
#include "base/scoped_ptr.h"
+namespace base {
+
namespace {
// System pagesize. This value remains constant on x86/64 architectures.
@@ -21,9 +28,54 @@ const int PAGESIZE_KB = 4;
// HeapSetInformation function pointer.
typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
-} // 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) {
+ StackTrace(info).PrintBacktrace();
+ if (g_previous_filter)
+ return g_previous_filter(info);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// Connects back to a console if available.
+// Only necessary on Windows, no-op on other platforms.
+void AttachToConsole() {
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ unsigned int result = GetLastError();
+ // Was probably already attached.
+ if (result == ERROR_ACCESS_DENIED)
+ return;
-namespace base {
+ if (result == ERROR_INVALID_HANDLE || result == ERROR_INVALID_HANDLE) {
+ // TODO(maruel): Walk up the process chain if deemed necessary.
+ }
+ // Continue even if the function call fails.
+ AllocConsole();
+ }
+ // http://support.microsoft.com/kb/105305
+ int raw_out = _open_osfhandle(
+ reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT);
+ *stdout = *_fdopen(raw_out, "w");
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ int raw_err = _open_osfhandle(
+ reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT);
+ *stderr = *_fdopen(raw_err, "w");
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ int raw_in = _open_osfhandle(
+ reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT);
+ *stdin = *_fdopen(raw_in, "r");
+ setvbuf(stdin, NULL, _IONBF, 0);
+ // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
+ std::ios::sync_with_stdio();
+}
+
+} // namespace
ProcessId GetCurrentProcId() {
return ::GetCurrentProcessId();
@@ -763,6 +815,14 @@ void EnableTerminationOnHeapCorruption() {
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
}
+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);
+ AttachToConsole();
+ return true;
+}
+
void RaiseProcessToHighPriority() {
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
}
diff --git a/base/test/test_suite.h b/base/test/test_suite.h
index 5fd1acc..42ad027 100644
--- a/base/test/test_suite.h
+++ b/base/test/test_suite.h
@@ -11,52 +11,19 @@
#include "base/at_exit.h"
#include "base/base_paths.h"
-#include "base/command_line.h"
#include "base/debug_on_start.h"
-#include "base/debug_util.h"
-#include "base/file_path.h"
#include "base/i18n/icu_util.h"
-#include "base/logging.h"
#include "base/multiprocess_test.h"
+#include "base/process_util.h"
#include "base/scoped_nsautorelease_pool.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-#if defined(OS_POSIX)
-#include <signal.h>
-#endif
-
#if defined(OS_LINUX)
#include <gtk/gtk.h>
#endif
-#if defined(OS_POSIX)
-static void TestSuiteCrashHandler(int signal) {
- StackTrace().PrintBacktrace();
- _exit(1);
-}
-#endif // OS_POSIX
-
-#if defined(OS_WIN)
-// Previous unhandled filter. Will be called if not NULL when we intercept an
-// exception.
-__declspec(selectany) LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
-
-// Prints the exception call stack.
-// This is the unit tests exception filter.
-inline long WINAPI UnitTestExceptionFilter(EXCEPTION_POINTERS* info) {
- StackTrace(info).PrintBacktrace();
- if (g_previous_filter)
- return g_previous_filter(info);
- return EXCEPTION_EXECUTE_HANDLER;
-}
-#endif // OS_WIN
-
// Match function used by the GetTestCount method.
typedef bool (*TestMatch)(const testing::TestInfo&);
@@ -185,23 +152,7 @@ class TestSuite {
// Note: temporarily enabled timestamps in an effort to catch bug 6361.
logging::SetLogItems(true, true, true, true);
-#if defined(OS_POSIX)
- // 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);
- CHECK(sigaction(SIGPIPE, &action, NULL) == 0);
-
- // TODO(phajdan.jr): Catch other crashy signals, like SIGABRT.
- CHECK(signal(SIGSEGV, &TestSuiteCrashHandler) != SIG_ERR);
- CHECK(signal(SIGILL, &TestSuiteCrashHandler) != SIG_ERR);
- CHECK(signal(SIGBUS, &TestSuiteCrashHandler) != SIG_ERR);
- CHECK(signal(SIGFPE, &TestSuiteCrashHandler) != SIG_ERR);
-#endif // OS_POSIX
-
+ CHECK(base::EnableInProcessStackDumping());
#if defined(OS_WIN)
// For unit tests we turn on the high resolution timer and disable
// base::Time's use of SystemMonitor. Tests create and destroy the message
@@ -217,9 +168,6 @@ class TestSuite {
// As a hack workaround, just #ifdef out this code for Purify builds.
logging::SetLogAssertHandler(UnitTestAssertHandler);
#endif // !defined(PURIFY)
- // Add stack dumping support on exception on windows. Similar to OS_POSIX
- // signal() handling above.
- g_previous_filter = SetUnhandledExceptionFilter(&UnitTestExceptionFilter);
}
#endif // defined(OS_WIN)
diff --git a/webkit/tools/test_shell/run_all_tests.cc b/webkit/tools/test_shell/run_all_tests.cc
index 3c6ec3f..560604b 100644
--- a/webkit/tools/test_shell/run_all_tests.cc
+++ b/webkit/tools/test_shell/run_all_tests.cc
@@ -31,7 +31,7 @@
#include "webkit/tools/test_shell/test_shell_webkit_init.h"
#include "testing/gtest/include/gtest/gtest.h"
-const char* TestShellTest::kJavascriptDelayExitScript =
+const char* const TestShellTest::kJavascriptDelayExitScript =
"<script>"
"window.layoutTestController.waitUntilDone();"
"window.addEventListener('load', function() {"
@@ -42,6 +42,7 @@ const char* TestShellTest::kJavascriptDelayExitScript =
int main(int argc, char* argv[]) {
base::ScopedNSAutoreleasePool autorelease_pool;
+ base::EnableInProcessStackDumping();
base::EnableTerminationOnHeapCorruption();
// Some unittests may use base::Singleton<>, thus we need to instanciate
// the AtExitManager or else we will leak objects.
diff --git a/webkit/tools/test_shell/test_shell.gyp b/webkit/tools/test_shell/test_shell.gyp
index 6870e10..f0fe364 100644
--- a/webkit/tools/test_shell/test_shell.gyp
+++ b/webkit/tools/test_shell/test_shell.gyp
@@ -244,6 +244,11 @@
'test_shell_common',
'../../../tools/imagediff/image_diff.gyp:image_diff',
],
+ 'defines': [
+ # Technically not a unit test but require functions available only to
+ # unit tests.
+ 'UNIT_TEST'
+ ],
'sources': [
'test_shell_main.cc',
],
diff --git a/webkit/tools/test_shell/test_shell_main.cc b/webkit/tools/test_shell/test_shell_main.cc
index 9f2ae67..b069b2f 100644
--- a/webkit/tools/test_shell/test_shell_main.cc
+++ b/webkit/tools/test_shell/test_shell_main.cc
@@ -45,13 +45,14 @@ using WebKit::WebScriptController;
namespace {
// StatsTable initialization parameters.
-static const char* kStatsFilePrefix = "testshell_";
-static int kStatsFileThreads = 20;
-static int kStatsFileCounters = 200;
+const char* const kStatsFilePrefix = "testshell_";
+int kStatsFileThreads = 20;
+int kStatsFileCounters = 200;
} // namespace
int main(int argc, char* argv[]) {
+ base::EnableInProcessStackDumping();
base::EnableTerminationOnHeapCorruption();
// Some tests may use base::Singleton<>, thus we need to instanciate
diff --git a/webkit/tools/test_shell/test_shell_test.h b/webkit/tools/test_shell/test_shell_test.h
index a5e0705..48dc8f93 100644
--- a/webkit/tools/test_shell/test_shell_test.h
+++ b/webkit/tools/test_shell/test_shell_test.h
@@ -29,7 +29,7 @@ class TestShellTest : public testing::Test {
// Don't refactor away; some unittests override this!
virtual void CreateEmptyWindow();
- static const char* kJavascriptDelayExitScript;
+ static const char* const kJavascriptDelayExitScript;
protected:
// Location of SOURCE_ROOT/webkit/data/