On Windows, WebKitTestRunner now detects when the web process is crashing and waits to exit
until it has finished crashing, which guarantees that the crash log will have had time to be
saved, too. On Mac, we always wait until ReportCrash has exited before capturing the crash
log, so all we have to do is choose the right crash log out of the CrashReporter directory.
Fixes <http://webkit.org/b/44121> <rdar://problem/
8320759> When the web process crashes and
a crash log is being saved, WebKitTestRunner thinks the web process has become unresponsive
Reviewed by Sam Weinig.
* Scripts/old-run-webkit-tests:
(testCrashedOrTimedOut): Don't kill WebKitTestRunner when the web process crashes. It will
kill itself. On Windows, this will cause us to wait until the crash log has been saved. On
Mac, it should have no effect. Capture saved crash logs for web process crashes, too.
(captureSavedCrashLog): Added $webProcessCrashed argument. On Mac, look for
WebProces_*.crash files when the web process crashes.
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::initialize): Added an initializationUserData argument. Updated for
initializePlatformDefaults -> platformInitialize rename. Pass the initializationUserData
along to platformInitialize.
* WebKitTestRunner/InjectedBundle/InjectedBundle.h: See above.
* WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp:
(WKBundleInitialize): Pass along the initializationUserData to the InjectedBundle.
* WebKitTestRunner/InjectedBundle/mac/InjectedBundleMac.mm:
(WTR::InjectedBundle::platformInitialize):
* WebKitTestRunner/InjectedBundle/qt/InjectedBundleQt.cpp:
(WTR::InjectedBundle::platformInitialize):
Updated function signature.
* WebKitTestRunner/InjectedBundle/win/InjectedBundleWin.cpp:
(WTR::exceptionFilter): Added. Tells the UI process we're crashing by signaling the
webProcessCrashingEvent, then lets the crash continue as normal.
(WTR::InjectedBundle::platformInitialize): Hook up exceptionFilter. Retrieve the name of the
event we should use to tell the UI process we're crashing from the initializationUserData,
and get a handle to that event.
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::TestController): Initialize new members.
(WTR::TestController::processDidCrash): Removed unnecessary WKPageRef argument. Changed to
only print the "#CRASHED - WebProcess" message once, since this can be called more than once
when a crash log is being saved on Windows. Exit right away if specified. (This is the
default.)
* WebKitTestRunner/TestController.h: Added new members.
* WebKitTestRunner/win/TestControllerWin.cpp:
(WTR::TestController::platformInitialize): Set up the event the web process will use to tell
us it's crashing.
(WTR::TestController::platformRunUntil): Pass MWMO_INPUTAVAILABLE to
::MsgWaitForMultipleObjectsEx so we'll process messages that have already been seen by
::PeekMessage. (This is unrelated to the bug fix.) Notice when the webProcessCrashingEvent
has been signaled. When this happens, print the "#CRASHED - WebProcess" message right away
so the test harness will know the web process has crashed and not try to kill us, then wait
for the web process to finish crashing so a crash log will have time to be saved.
(WTR::toWK): Simple hepler function.
(WTR::TestController::platformInitializeContext): Pass along the name of the event the web
process should use to tell us it is crashing in the context's initialization user data.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@80009
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2011-03-01 Adam Roben <aroben@apple.com>
+
+ Save a crash log when the web process crashes
+
+ On Windows, WebKitTestRunner now detects when the web process is crashing and waits to exit
+ until it has finished crashing, which guarantees that the crash log will have had time to be
+ saved, too. On Mac, we always wait until ReportCrash has exited before capturing the crash
+ log, so all we have to do is choose the right crash log out of the CrashReporter directory.
+
+ Fixes <http://webkit.org/b/44121> <rdar://problem/8320759> When the web process crashes and
+ a crash log is being saved, WebKitTestRunner thinks the web process has become unresponsive
+
+ Reviewed by Sam Weinig.
+
+ * Scripts/old-run-webkit-tests:
+ (testCrashedOrTimedOut): Don't kill WebKitTestRunner when the web process crashes. It will
+ kill itself. On Windows, this will cause us to wait until the crash log has been saved. On
+ Mac, it should have no effect. Capture saved crash logs for web process crashes, too.
+ (captureSavedCrashLog): Added $webProcessCrashed argument. On Mac, look for
+ WebProces_*.crash files when the web process crashes.
+
+ * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+ (WTR::InjectedBundle::initialize): Added an initializationUserData argument. Updated for
+ initializePlatformDefaults -> platformInitialize rename. Pass the initializationUserData
+ along to platformInitialize.
+
+ * WebKitTestRunner/InjectedBundle/InjectedBundle.h: See above.
+
+ * WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp:
+ (WKBundleInitialize): Pass along the initializationUserData to the InjectedBundle.
+
+ * WebKitTestRunner/InjectedBundle/mac/InjectedBundleMac.mm:
+ (WTR::InjectedBundle::platformInitialize):
+ * WebKitTestRunner/InjectedBundle/qt/InjectedBundleQt.cpp:
+ (WTR::InjectedBundle::platformInitialize):
+ Updated function signature.
+
+ * WebKitTestRunner/InjectedBundle/win/InjectedBundleWin.cpp:
+ (WTR::exceptionFilter): Added. Tells the UI process we're crashing by signaling the
+ webProcessCrashingEvent, then lets the crash continue as normal.
+
+ (WTR::InjectedBundle::platformInitialize): Hook up exceptionFilter. Retrieve the name of the
+ event we should use to tell the UI process we're crashing from the initializationUserData,
+ and get a handle to that event.
+
+ * WebKitTestRunner/TestController.cpp:
+ (WTR::TestController::TestController): Initialize new members.
+ (WTR::TestController::processDidCrash): Removed unnecessary WKPageRef argument. Changed to
+ only print the "#CRASHED - WebProcess" message once, since this can be called more than once
+ when a crash log is being saved on Windows. Exit right away if specified. (This is the
+ default.)
+
+ * WebKitTestRunner/TestController.h: Added new members.
+
+ * WebKitTestRunner/win/TestControllerWin.cpp:
+ (WTR::TestController::platformInitialize): Set up the event the web process will use to tell
+ us it's crashing.
+ (WTR::TestController::platformRunUntil): Pass MWMO_INPUTAVAILABLE to
+ ::MsgWaitForMultipleObjectsEx so we'll process messages that have already been seen by
+ ::PeekMessage. (This is unrelated to the bug fix.) Notice when the webProcessCrashingEvent
+ has been signaled. When this happens, print the "#CRASHED - WebProcess" message right away
+ so the test harness will know the web process has crashed and not try to kill us, then wait
+ for the web process to finish crashing so a crash log will have time to be saved.
+ (WTR::toWK): Simple hepler function.
+ (WTR::TestController::platformInitializeContext): Pass along the name of the event the web
+ process should use to tell us it is crashing in the context's initialization user data.
+
2011-03-01 Dimitri Glazkov <dglazkov@chromium.org>
Reviewed by Tony Gentilcore.
sub buildPlatformResultHierarchy();
sub buildPlatformTestHierarchy(@);
-sub captureSavedCrashLog($);
+sub captureSavedCrashLog($$);
sub checkPythonVersion();
sub closeCygpaths();
sub closeDumpTool();
recordActualResultsAndDiff($base, $actual);
- kill 9, $dumpToolPID unless $didCrash;
+ # There's no point in killing the dump tool when it's crashed. And it will kill itself when the
+ # web process crashes.
+ kill 9, $dumpToolPID unless $didCrash || $webProcessCrashed;
closeDumpTool();
- captureSavedCrashLog($base) if $didCrash;
+ captureSavedCrashLog($base, $webProcessCrashed) if $didCrash || $webProcessCrashed;
return unless isCygwin() && !$didCrash && $base =~ /^http/;
# On Cygwin, http tests timing out can be a symptom of a non-responsive httpd.
configureAndOpenHTTPDIfNeeded();
}
-sub captureSavedCrashLog($)
+sub captureSavedCrashLog($$)
{
- my ($base) = @_;
+ my ($base, $webProcessCrashed) = @_;
my $crashLog;
if (isCygwin()) {
$glob = File::Spec->catfile($testResultsDirectory, $windowsCrashLogFilePrefix . "*.txt");
} elsif (isAppleMacWebKit()) {
- $glob = File::Spec->catfile("~", "Library", "Logs", "CrashReporter", $dumpToolName . "_*.crash");
+ $glob = File::Spec->catfile("~", "Library", "Logs", "CrashReporter", ($webProcessCrashed ? "WebProcess" : $dumpToolName) . "_*.crash");
# Even though the dump tool has exited, CrashReporter might still be running. We need to
# wait for it to exit to ensure it has saved its crash log to disk. For simplicitly, we'll
static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didReceiveMessage(messageName, messageBody);
}
-void InjectedBundle::initialize(WKBundleRef bundle)
+void InjectedBundle::initialize(WKBundleRef bundle, WKTypeRef initializationUserData)
{
m_bundle = bundle;
};
WKBundleSetClient(m_bundle, &client);
- initializePlatformDefaults();
+ platformInitialize(initializationUserData);
activateFonts();
WKBundleActivateMacFontAscentHack(m_bundle);
static InjectedBundle& shared();
// Initialize the InjectedBundle.
- void initialize(WKBundleRef);
+ void initialize(WKBundleRef, WKTypeRef initializationUserData);
WKBundleRef bundle() const { return m_bundle; }
WKBundlePageGroupRef pageGroup() const { return m_pageGroup; }
void didInitializePageGroup(WKBundlePageGroupRef);
void didReceiveMessage(WKStringRef messageName, WKTypeRef messageBody);
- void initializePlatformDefaults();
+ void platformInitialize(WKTypeRef initializationUserData);
void resetLocalSettings();
void beginTesting();
#endif
void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)
{
- WTR::InjectedBundle::shared().initialize(bundle);
+ WTR::InjectedBundle::shared().initialize(bundle, initializationUserData);
}
namespace WTR {
-void InjectedBundle::initializePlatformDefaults()
+void InjectedBundle::platformInitialize(WKTypeRef)
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:4], @"AppleAntiAliasingThreshold",
namespace WTR {
-void InjectedBundle::initializePlatformDefaults()
+void InjectedBundle::platformInitialize(WKTypeRef)
{
}
namespace WTR {
-void InjectedBundle::initializePlatformDefaults()
+static HANDLE webProcessCrashingEvent;
+
+static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
+{
+ // Let the UI process know right away that we crashed. It might take a long time for us to
+ // finish crashing if a crash log is being saved.
+ ::SetEvent(webProcessCrashingEvent);
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void InjectedBundle::platformInitialize(WKTypeRef initializationUserData)
{
+ ::SetUnhandledExceptionFilter(exceptionFilter);
+
+ ASSERT_ARG(initializationUserData, initializationUserData);
+ ASSERT_ARG(initializationUserData, WKGetTypeID(initializationUserData) == WKStringGetTypeID());
+
+ WKStringRef string = static_cast<WKStringRef>(initializationUserData);
+ Vector<char> buffer(WKStringGetMaximumUTF8CStringSize(string));
+ WKStringGetUTF8CString(string, buffer.data(), buffer.size());
+
+ // The UI process should already have created this event. We're just getting another HANDLE to it.
+ webProcessCrashingEvent = ::CreateEventA(0, FALSE, FALSE, buffer.data());
+ ASSERT(webProcessCrashingEvent);
}
} // namespace WTR
, m_doneResetting(false)
, m_longTimeout(defaultLongTimeout)
, m_shortTimeout(defaultShortTimeout)
+ , m_didPrintWebProcessCrashedMessage(false)
+ , m_shouldExitWhenWebProcessCrashes(true)
{
initialize(argc, argv);
controller = this;
void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
{
- static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash(page);
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
}
void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
shared().notifyDone();
}
-void TestController::processDidCrash(WKPageRef page)
+void TestController::processDidCrash()
{
- fputs("#CRASHED - WebProcess\n", stderr);
- fflush(stderr);
+ // This function can be called multiple times when crash logs are being saved on Windows, so
+ // ensure we only print the crashed message once.
+ if (!m_didPrintWebProcessCrashedMessage) {
+ fputs("#CRASHED - WebProcess\n", stderr);
+ fflush(stderr);
+ m_didPrintWebProcessCrashedMessage = true;
+ }
+
+ if (m_shouldExitWhenWebProcessCrashes)
+ exit(1);
}
} // namespace WTR
void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame);
static void processDidCrash(WKPageRef, const void* clientInfo);
- void processDidCrash(WKPageRef);
+ void processDidCrash();
static WKPageRef createOtherPage(WKPageRef oldPage, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*);
double m_longTimeout;
double m_shortTimeout;
+
+ bool m_didPrintWebProcessCrashedMessage;
+ bool m_shouldExitWhenWebProcessCrashes;
};
} // namespace WTR
namespace WTR {
+static HANDLE webProcessCrashingEvent;
+static const char webProcessCrashingEventName[] = "WebKitTestRunner.WebProcessCrashing";
+
#ifdef DEBUG_ALL
const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin_Debug";
const char* injectedBundleDLL = "\\InjectedBundle_debug.dll";
// Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems
// linked with older versions of qtmlclientlib.dll.
addQTDirToPATH();
+
+ webProcessCrashingEvent = ::CreateEventA(0, FALSE, FALSE, webProcessCrashingEventName);
}
void TestController::initializeInjectedBundlePath()
if (now > end)
return;
- DWORD result = ::MsgWaitForMultipleObjectsEx(0, 0, end - now, QS_ALLINPUT, 0);
+ DWORD result = ::MsgWaitForMultipleObjectsEx(1, &webProcessCrashingEvent, end - now, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
if (result == WAIT_TIMEOUT)
return;
- ASSERT(result == WAIT_OBJECT_0);
+ if (result == WAIT_OBJECT_0) {
+ // The web process is crashing. A crash log might be being saved, which can take a long
+ // time, and we don't want to time out while that happens.
+
+ // First, let the test harness know this happened so it won't think we've hung. But
+ // make sure we don't exit just yet!
+ m_shouldExitWhenWebProcessCrashes = false;
+ processDidCrash();
+ m_shouldExitWhenWebProcessCrashes = true;
+
+ // Then spin a run loop until it finishes crashing to give time for a crash log to be saved.
+ MSG msg;
+ while (BOOL bRet = ::GetMessageW(&msg, 0, 0, 0)) {
+ if (bRet == -1)
+ break;
+ ::TranslateMessage(&msg);
+ ::DispatchMessageW(&msg);
+ }
+
+ exit(1);
+ }
+
+ ASSERT(result == WAIT_OBJECT_0 + 1);
// There are messages in the queue. Process them.
MSG msg;
while (::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
}
}
+static WKRetainPtr<WKStringRef> toWK(const char* string)
+{
+ return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(string));
+}
+
void TestController::platformInitializeContext()
{
// FIXME: Make DRT pass with Windows native controls. <http://webkit.org/b/25592>
WKContextSetShouldPaintNativeControls(m_context.get(), false);
+
+ WKContextSetInitializationUserDataForInjectedBundle(m_context.get(), toWK(webProcessCrashingEventName).get());
}
void TestController::runModal(PlatformWebView*)