Bug 602891 part B - Backend support for jetpack-process crashes and crash recovery, r=cjones

On crash, an event will be submitted to the parent with the name "core:process-error" with a context object. If crash reporting is enabled and a crash report is available, the context object will have a .dumpID property which can be used to submit the crash report.

--HG--
extra : rebase_source : d089bb451271999ae3861a83f2b21ba8ec9e0122
This commit is contained in:
Benjamin Smedberg 2010-11-17 15:57:02 -05:00
parent e5050e3722
commit dd3cf56a97
7 changed files with 167 additions and 7 deletions

View File

@ -42,6 +42,7 @@
#include "mozilla/jetpack/JetpackChild.h"
#include "mozilla/jetpack/Handle.h"
#include "mozilla/IntentionalCrash.h"
#include "jsarray.h"
@ -75,6 +76,8 @@ JetpackChild::sImplMethods[] = {
#ifdef JS_GC_ZEAL
JS_FN("gczeal", GCZeal, 1, IMPL_METHOD_FLAGS),
#endif
JS_FN("_noteIntentionalCrash", NoteIntentionalCrash, 0,
IMPL_METHOD_FLAGS),
JS_FS_END
};
@ -563,5 +566,12 @@ JetpackChild::GCZeal(JSContext* cx, uintN argc, jsval *vp)
}
#endif
JSBool
JetpackChild::NoteIntentionalCrash(JSContext* cx, uintN argc, jsval *vp)
{
mozilla::NoteIntentionalCrash("jetpack");
return JS_TRUE;
}
} // namespace jetpack
} // namespace mozilla

View File

@ -93,6 +93,7 @@ private:
#ifdef JS_GC_ZEAL
static JSBool GCZeal(JSContext* cx, uintN argc, jsval *vp);
#endif
static JSBool NoteIntentionalCrash(JSContext* cx, uintN argc, jsval *vp);
static void ReportError(JSContext* cx, const char* message,
JSErrorReport* report);

View File

@ -45,12 +45,17 @@
#include "nsIXPConnect.h"
#include "nsIJSContextStack.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
namespace mozilla {
namespace jetpack {
JetpackParent::JetpackParent(JSContext* cx)
: mSubprocess(new JetpackProcessParent())
, mContext(cx)
, mTaskFactory(this)
{
mSubprocess->Launch();
Open(mSubprocess->GetChannel(),
@ -152,6 +157,7 @@ public:
AutoCXPusher(JSContext* cx)
: mCXStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"))
{
NS_ASSERTION(mCXStack, "No JS context stack?");
if (mCXStack)
mCXStack->Push(cx);
}
@ -166,6 +172,39 @@ private:
JSContext* mCX;
};
void
JetpackParent::ActorDestroy(ActorDestroyReason why)
{
switch (why) {
case AbnormalShutdown: {
nsAutoString dumpID;
#ifdef MOZ_CRASHREPORTER
nsCOMPtr<nsILocalFile> crashDump;
TakeMinidump(getter_AddRefs(crashDump)) &&
CrashReporter::GetIDFromMinidump(crashDump, dumpID);
#endif
MessageLoop::current()->
PostTask(FROM_HERE,
mTaskFactory.NewRunnableMethod(
&JetpackParent::DispatchFailureMessage,
dumpID));
break;
}
case NormalShutdown:
break;
default:
NS_ERROR("Unexpected actordestroy reason for toplevel actor.");
}
XRE_GetIOMessageLoop()
->PostTask(FROM_HERE, new DeleteTask<JetpackProcessParent>(mSubprocess));
mSubprocess = NULL;
}
bool
JetpackParent::RecvSendMessage(const nsString& messageName,
const InfallibleTArray<Variant>& data)
@ -222,14 +261,10 @@ JetpackParent::CreateHandle(nsIVariant** aResult)
NS_IMETHODIMP
JetpackParent::Destroy()
{
if (!mSubprocess)
return NS_ERROR_NOT_INITIALIZED;
Close();
XRE_GetIOMessageLoop()
->PostTask(FROM_HERE, new DeleteTask<JetpackProcessParent>(mSubprocess));
mSubprocess = NULL;
if (mSubprocess)
Close();
NS_ASSERTION(!mSubprocess, "ActorDestroy should have been called.");
return NS_OK;
}
@ -256,5 +291,20 @@ JetpackParent::OnChannelConnected(int32 pid)
SetOtherProcess(handle);
}
void
JetpackParent::DispatchFailureMessage(const nsString& aDumpID)
{
KeyValue kv(NS_LITERAL_STRING("dumpID"), PrimVariant(aDumpID));
InfallibleTArray<KeyValue> keyvalues;
keyvalues.AppendElement(kv);
CompVariant object(keyvalues);
InfallibleTArray<Variant> arguments;
arguments.AppendElement(object);
RecvSendMessage(NS_LITERAL_STRING("core:process-error"), arguments);
}
} // namespace jetpack
} // namespace mozilla

View File

@ -68,6 +68,8 @@ public:
void OnChannelConnected(int32 pid);
protected:
NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why);
NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName,
const InfallibleTArray<Variant>& data);
NS_OVERRIDE virtual bool AnswerCallMessage(const nsString& messageName,
@ -80,6 +82,9 @@ protected:
private:
JetpackProcessParent* mSubprocess;
JSContext* mContext;
ScopedRunnableMethodFactory<JetpackParent> mTaskFactory;
void DispatchFailureMessage(const nsString& aDumpID);
DISALLOW_EVIL_CONSTRUCTORS(JetpackParent);
};

View File

@ -49,3 +49,12 @@ XPCSHELL_TESTS = unit
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) # Disabled for bug 599475
MOCHICHROME_FILES = \
test_jetpack_crash.xul \
$(NULL)
libs:: $(MOCHICHROME_FILES)
$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
endif

View File

@ -0,0 +1,84 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css" ?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css" ?>
<window title="Jetpack Crash Test"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<description id="remoteScript">
<![CDATA[
registerReceiver("testCTypes", function(name, libfile) {
var library = ctypes.open(libfile);
var zero = new ctypes.intptr_t(8);
_noteIntentionalCrash();
var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
sendMessage("testCTypes:response", badptr.contents); // should crash here!
});
]]>
</description>
<script class="testbody" type="application/javascript">
<![CDATA[
Components.utils.import("resource://gre/modules/ctypes.jsm");
const libURL = "chrome://mochitests/content/chrome/libraries/" +
ctypes.libraryName("jsctypes-test");
SimpleTest.waitForExplicitFinish();
var jp = Components.classes["@mozilla.org/jetpack/service;1"].
getService(Components.interfaces.nsIJetpackService).
createJetpack();
jp.registerReceiver("core:exception", function(msgName, e) {
ok(false, "Received exception from remote code: " + uneval(e));
});
jp.registerReceiver("testCTypes:response", function(msgName, v) {
ok(false, "Should not have received testCTypes response!");
jp.destroy();
jp = null;
SimpleTest.finish();
});
jp.registerReceiver("core:process-error", function(msgName, e) {
ok(true, "Received process-error notification.");
if ('nsICrashReporter' in Components.interfaces)
ok(e.dumpID, "Process error has a dumpID");
jp.destroy();
jp = null;
SimpleTest.executeSoon(SimpleTest.finish);
});
var remoteScript = document.getElementById('remoteScript').textContent;
jp.evalScript(remoteScript);
var downloadObserver = {
onDownloadComplete: function(downloader, request, cx, status, file) {
Components.utils.reportError("download status: " + status);
try {
file.permission = 0700;
}
catch (e) {
Components.utils.reportError(e);
}
jp.sendMessage('testCTypes', file.path);
}
};
var downloader = Components.classes["@mozilla.org/network/downloader;1"].
createInstance(Components.interfaces.nsIDownloader);
downloader.init(downloadObserver, null);
var channel = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService).
newChannel(libURL, null, null);
channel.asyncOpen(downloader, null);
]]>
</script>
</window>

View File

@ -84,6 +84,7 @@ libs:: unit/test_jsctypes.js.in
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$^ > $(xpctestdir)/test_jsctypes.js
$(TEST_INSTALLER) $(SHARED_LIBRARY) $(xpctestdir)
$(TEST_INSTALLER) $(SHARED_LIBRARY) $(DEPTH)/_tests/testing/mochitest/chrome/libraries
$(TEST_INSTALLER) $(xpctestdir)/test_jsctypes.js $(chrometestdir)
$(TEST_INSTALLER) $(xpctestdir)/$(SHARED_LIBRARY) $(chrometestdir)
$(RM) $(xpctestdir)/test_jsctypes.js.in