Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2012-07-12 21:12:17 -04:00
commit 1b61fbb7ca
16 changed files with 282 additions and 139 deletions

View File

@ -3,7 +3,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
llvm_revision = "159409" llvm_revision = "160105"
moz_version = "moz0" moz_version = "moz0"
############################################## ##############################################

View File

@ -191,7 +191,7 @@ endif
profiledbuild:: profiledbuild::
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1 $(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1
$(MAKE) -C $(PGO_OBJDIR) stage-package MOZ_PGO_INSTRUMENTED=1 $(MAKE) -C $(PGO_OBJDIR) package MOZ_PGO_INSTRUMENTED=1 MOZ_INTERNAL_SIGNING_FORMAT= MOZ_EXTERNAL_SIGNING_FORMAT=
MOZ_PGO_INSTRUMENTED=1 OBJDIR=${PGO_OBJDIR} JARLOG_DIR=${PGO_OBJDIR}/jarlog/en-US $(PROFILE_GEN_SCRIPT) MOZ_PGO_INSTRUMENTED=1 OBJDIR=${PGO_OBJDIR} JARLOG_DIR=${PGO_OBJDIR}/jarlog/en-US $(PROFILE_GEN_SCRIPT)
$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild $(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1 $(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1

View File

@ -98,6 +98,15 @@ CrashReporterParent::GenerateHangCrashReport(const AnnotationTable* processNotes
return true; return true;
} }
bool
CrashReporterParent::GenerateCrashReportForMinidump(nsIFile* minidump,
const AnnotationTable* processNotes)
{
if (!CrashReporter::GetIDFromMinidump(minidump, mChildDumpID))
return false;
return GenerateChildData(processNotes);
}
bool bool
CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes) CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes)
{ {

View File

@ -49,6 +49,10 @@ public:
bool bool
GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes); GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes);
bool
GenerateCrashReportForMinidump(nsIFile* minidump,
const AnnotationTable* processNotes);
/* Instantiate a new crash reporter actor from a given parent that manages /* Instantiate a new crash reporter actor from a given parent that manages
the protocol. the protocol.
*/ */
@ -135,7 +139,7 @@ CrashReporterParent::GenerateCrashReport(Toplevel* t,
const AnnotationTable* processNotes) const AnnotationTable* processNotes)
{ {
nsCOMPtr<nsIFile> crashDump; nsCOMPtr<nsIFile> crashDump;
if (t->TakeMinidump(getter_AddRefs(crashDump)) && if (t->TakeMinidump(getter_AddRefs(crashDump), NULL) &&
CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) { CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) {
return GenerateChildData(processNotes); return GenerateChildData(processNotes);
} }

View File

@ -27,9 +27,6 @@
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsCRT.h" #include "nsCRT.h"
#ifdef MOZ_CRASHREPORTER
#include "mozilla/dom/CrashReporterParent.h"
#endif
#include "nsNPAPIPlugin.h" #include "nsNPAPIPlugin.h"
#include "nsIFile.h" #include "nsIFile.h"
@ -49,6 +46,12 @@ using namespace mozilla;
using namespace mozilla::plugins; using namespace mozilla::plugins;
using namespace mozilla::plugins::parent; using namespace mozilla::plugins::parent;
#ifdef MOZ_CRASHREPORTER
#include "mozilla/dom/CrashReporterParent.h"
using namespace CrashReporter;
#endif
static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs"; static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs"; static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs"; static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
@ -133,9 +136,9 @@ PluginModuleParent::~PluginModuleParent()
#ifdef MOZ_CRASHREPORTER_INJECTOR #ifdef MOZ_CRASHREPORTER_INJECTOR
if (mFlashProcess1) if (mFlashProcess1)
CrashReporter::UnregisterInjectorCallback(mFlashProcess1); UnregisterInjectorCallback(mFlashProcess1);
if (mFlashProcess2) if (mFlashProcess2)
CrashReporter::UnregisterInjectorCallback(mFlashProcess2); UnregisterInjectorCallback(mFlashProcess2);
#endif #endif
Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this); Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
@ -144,7 +147,7 @@ PluginModuleParent::~PluginModuleParent()
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
void void
PluginModuleParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes) PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
{ {
typedef nsDependentCString CS; typedef nsDependentCString CS;
@ -200,19 +203,17 @@ bool
PluginModuleParent::ShouldContinueFromReplyTimeout() PluginModuleParent::ShouldContinueFromReplyTimeout()
{ {
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
if (mPluginDumpID.IsEmpty()) { CrashReporterParent* crashReporter = CrashReporter();
CrashReporterParent* crashReporter = CrashReporter(); if (crashReporter->GeneratePairedMinidump(this)) {
if (crashReporter->GeneratePairedMinidump(this)) { mBrowserDumpID = crashReporter->ParentDumpID();
mBrowserDumpID = crashReporter->ParentDumpID(); mPluginDumpID = crashReporter->ChildDumpID();
mPluginDumpID = crashReporter->ChildDumpID(); PLUGIN_LOG_DEBUG(
PLUGIN_LOG_DEBUG( ("generated paired browser/plugin minidumps: %s/%s (ID=%s)",
("generated paired browser/plugin minidumps: %s/%s (ID=%s)", NS_ConvertUTF16toUTF8(mBrowserDumpID).get(),
NS_ConvertUTF16toUTF8(mBrowserDumpID).get(), NS_ConvertUTF16toUTF8(mPluginDumpID).get(),
NS_ConvertUTF16toUTF8(mPluginDumpID).get(), NS_ConvertUTF16toUTF8(crashReporter->HangID()).get()));
NS_ConvertUTF16toUTF8(crashReporter->HangID()).get())); } else {
} else { NS_WARNING("failed to capture paired minidumps from hang");
NS_WARNING("failed to capture paired minidumps from hang");
}
} }
#endif #endif
@ -237,33 +238,99 @@ PluginModuleParent::CrashReporter()
} }
#endif #endif
#ifdef MOZ_CRASHREPORTER
static void
RemoveMinidump(nsIFile* minidump)
{
if (!minidump)
return;
minidump->Remove(false);
nsCOMPtr<nsIFile> extraFile;
if (GetExtraFileForMinidump(minidump,
getter_AddRefs(extraFile))) {
extraFile->Remove(true);
}
}
void
PluginModuleParent::ProcessFirstMinidump()
{
CrashReporterParent* crashReporter = CrashReporter();
if (!crashReporter)
return;
AnnotationTable notes;
notes.Init(4);
WriteExtraDataForMinidump(notes);
if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) {
crashReporter->GenerateHangCrashReport(&notes);
return;
}
PRUint32 sequence = PR_UINT32_MAX;
nsCOMPtr<nsIFile> dumpFile;
nsCAutoString flashProcessType;
TakeMinidump(getter_AddRefs(dumpFile), &sequence);
#ifdef MOZ_CRASHREPORTER_INJECTOR
nsCOMPtr<nsIFile> childDumpFile;
PRUint32 childSequence;
if (mFlashProcess1 &&
TakeMinidumpForChild(mFlashProcess1,
getter_AddRefs(childDumpFile),
&childSequence)) {
if (childSequence < sequence) {
RemoveMinidump(dumpFile);
dumpFile = childDumpFile;
sequence = childSequence;
flashProcessType.AssignLiteral("Broker");
}
else {
RemoveMinidump(childDumpFile);
}
}
if (mFlashProcess2 &&
TakeMinidumpForChild(mFlashProcess2,
getter_AddRefs(childDumpFile),
&childSequence)) {
if (childSequence < sequence) {
RemoveMinidump(dumpFile);
dumpFile = childDumpFile;
sequence = childSequence;
flashProcessType.AssignLiteral("Sandbox");
}
else {
RemoveMinidump(childDumpFile);
}
}
#endif
if (!dumpFile) {
NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
return;
}
PLUGIN_LOG_DEBUG(("got child minidump: %s",
NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
GetIDFromMinidump(dumpFile, mPluginDumpID);
if (!flashProcessType.IsEmpty()) {
notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType);
}
crashReporter->GenerateCrashReportForMinidump(dumpFile, &notes);
}
#endif
void void
PluginModuleParent::ActorDestroy(ActorDestroyReason why) PluginModuleParent::ActorDestroy(ActorDestroyReason why)
{ {
switch (why) { switch (why) {
case AbnormalShutdown: { case AbnormalShutdown: {
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
CrashReporterParent* crashReporter = CrashReporter(); ProcessFirstMinidump();
CrashReporter::AnnotationTable notes;
notes.Init(4);
WriteExtraDataForMinidump(notes);
if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) {
crashReporter->GenerateHangCrashReport(&notes);
}
else if (!mPluginDumpID.IsEmpty()) {
// Nothing to do, we've already written this minidump in
// PluginModuleParent::OnCrash
}
else if (crashReporter->GenerateCrashReport(this, &notes)) {
mPluginDumpID = crashReporter->ChildDumpID();
PLUGIN_LOG_DEBUG(("got child minidump: %s",
NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
}
else {
NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
}
#endif #endif
mShutdown = true; mShutdown = true;
@ -1248,33 +1315,8 @@ PluginModuleParent::InitializeInjector()
} }
void void
PluginModuleParent::OnCrash(DWORD processID, const nsAString& aDumpID) PluginModuleParent::OnCrash(DWORD processID)
{ {
if (!mPluginDumpID.IsEmpty()) {
// One process has already crashed: we assume that the first-to-crash
// is the interesting one
return;
}
mPluginDumpID = aDumpID;
CrashReporter::AnnotationTable notes;
notes.Init(4);
WriteExtraDataForMinidump(notes);
notes.Put(NS_LITERAL_CSTRING("ProcessType"), NS_LITERAL_CSTRING("plugin"));
if (processID == mFlashProcess1) {
notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"),
NS_LITERAL_CSTRING("Broker"));
}
else if (processID == mFlashProcess2) {
notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"),
NS_LITERAL_CSTRING("Sandbox"));
}
else {
NS_ERROR("Got minidump for Flash process neither broker nor sandbox.");
}
CrashReporter::AppendExtraData(aDumpID, notes);
GetIPCChannel()->CloseWithError(); GetIPCChannel()->CloseWithError();
KillProcess(OtherProcess(), 1, false); KillProcess(OtherProcess(), 1, false);
} }

View File

@ -288,6 +288,7 @@ private:
CrashReporterParent* CrashReporter(); CrashReporterParent* CrashReporter();
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
void ProcessFirstMinidump();
void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes); void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
#endif #endif
void CleanupFromTimeout(); void CleanupFromTimeout();
@ -319,7 +320,7 @@ private:
#ifdef MOZ_CRASHREPORTER_INJECTOR #ifdef MOZ_CRASHREPORTER_INJECTOR
void InitializeInjector(); void InitializeInjector();
NS_OVERRIDE void OnCrash(DWORD processID, const nsAString& aDumpID); NS_OVERRIDE void OnCrash(DWORD processID);
DWORD mFlashProcess1; DWORD mFlashProcess1;
DWORD mFlashProcess2; DWORD mFlashProcess2;

View File

@ -253,6 +253,10 @@ void GeckoChildProcessHost::InitWindowsGroupID()
bool bool
GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch) GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
{ {
#ifdef MOZ_CRASHREPORTER
CrashReporter::OOPInit();
#endif
#ifdef XP_WIN #ifdef XP_WIN
InitWindowsGroupID(); InitWindowsGroupID();
#endif #endif
@ -294,6 +298,10 @@ GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTime
bool bool
GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts) GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
{ {
#ifdef MOZ_CRASHREPORTER
CrashReporter::OOPInit();
#endif
#ifdef XP_WIN #ifdef XP_WIN
InitWindowsGroupID(); InitWindowsGroupID();
#endif #endif

View File

@ -330,6 +330,7 @@ Type.INT = Type('int')
Type.INT32 = Type('int32') Type.INT32 = Type('int32')
Type.INTPTR = Type('intptr_t') Type.INTPTR = Type('intptr_t')
Type.UINT32 = Type('uint32') Type.UINT32 = Type('uint32')
Type.UINT32PTR = Type('uint32', ptr=1)
Type.SIZE = Type('size_t') Type.SIZE = Type('size_t')
Type.VOID = Type('void') Type.VOID = Type('void')
Type.VOIDPTR = Type('void', ptr=1) Type.VOIDPTR = Type('void', ptr=1)

View File

@ -3089,16 +3089,18 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
]) ])
dumpvar = ExprVar('aDump') dumpvar = ExprVar('aDump')
seqvar = ExprVar('aSequence')
getdump = MethodDefn(MethodDecl( getdump = MethodDefn(MethodDecl(
'TakeMinidump', 'TakeMinidump',
params=[ Decl(Type('nsIFile', ptrptr=1), dumpvar.name) ], params=[ Decl(Type('nsIFile', ptrptr=1), dumpvar.name),
Decl(Type.UINT32PTR, seqvar.name)],
ret=Type.BOOL, ret=Type.BOOL,
const=1)) const=1))
getdump.addstmts([ getdump.addstmts([
CppDirective('ifdef', 'MOZ_CRASHREPORTER'), CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
StmtReturn(ExprCall( StmtReturn(ExprCall(
ExprVar('XRE_TakeMinidumpForChild'), ExprVar('XRE_TakeMinidumpForChild'),
args=[ ExprCall(otherpidvar), dumpvar ])), args=[ ExprCall(otherpidvar), dumpvar, seqvar ])),
CppDirective('else'), CppDirective('else'),
StmtReturn.FALSE, StmtReturn.FALSE,
CppDirective('endif') CppDirective('endif')

View File

@ -169,7 +169,7 @@ FENNEC_PP_XML_FILES = \
res/menu/gecko_menu.xml \ res/menu/gecko_menu.xml \
res/menu-v11/gecko_menu.xml \ res/menu-v11/gecko_menu.xml \
res/menu-v11/titlebar_contextmenu.xml \ res/menu-v11/titlebar_contextmenu.xml \
res/menu-large/gecko_menu.xml \ res/menu-large-v11/gecko_menu.xml \
$(NULL) $(NULL)
@ -941,7 +941,11 @@ RES_DIRS= \
res/color \ res/color \
res/menu \ res/menu \
res/menu-v11 \ res/menu-v11 \
<<<<<<< local
res/menu-large \ res/menu-large \
=======
res/menu-large-v11 \
>>>>>>> other
$(NULL) $(NULL)

View File

@ -23,6 +23,7 @@
#include "client/windows/handler/exception_handler.h" #include "client/windows/handler/exception_handler.h"
#include <DbgHelp.h> #include <DbgHelp.h>
#include <string.h> #include <string.h>
#include "nsDirectoryServiceUtils.h"
#include "nsWindowsDllInterceptor.h" #include "nsWindowsDllInterceptor.h"
#elif defined(XP_MACOSX) #elif defined(XP_MACOSX)
@ -40,7 +41,6 @@
#include <unistd.h> #include <unistd.h>
#include "mac_utils.h" #include "mac_utils.h"
#elif defined(XP_LINUX) #elif defined(XP_LINUX)
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceDefs.h"
#include "nsIINIParser.h" #include "nsIINIParser.h"
#include "common/linux/linux_libc_support.h" #include "common/linux/linux_libc_support.h"
@ -152,6 +152,7 @@ static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
static google_breakpad::ExceptionHandler* gExceptionHandler = nsnull; static google_breakpad::ExceptionHandler* gExceptionHandler = nsnull;
static XP_CHAR* pendingDirectory;
static XP_CHAR* crashReporterPath; static XP_CHAR* crashReporterPath;
// if this is false, we don't launch the crash reporter // if this is false, we don't launch the crash reporter
@ -221,13 +222,32 @@ static const int kMagicChildCrashReportFd = 4;
// |dumpMapLock| must protect all access to |pidToMinidump|. // |dumpMapLock| must protect all access to |pidToMinidump|.
static Mutex* dumpMapLock; static Mutex* dumpMapLock;
typedef nsInterfaceHashtable<nsUint32HashKey, nsIFile> ChildMinidumpMap; struct ChildProcessData : public nsUint32HashKey
{
ChildProcessData(KeyTypePointer aKey)
: nsUint32HashKey(aKey)
, sequence(0)
#ifdef MOZ_CRASHREPORTER_INJECTOR
, callback(NULL)
#endif
{ }
nsCOMPtr<nsIFile> minidump;
// Each crashing process is assigned an increasing sequence number to
// indicate which process crashed first.
PRUint32 sequence;
#ifdef MOZ_CRASHREPORTER_INJECTOR
InjectorCrashCallback* callback;
#endif
};
typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
static ChildMinidumpMap* pidToMinidump; static ChildMinidumpMap* pidToMinidump;
static PRUint32 crashSequence;
static bool OOPInitialized();
#ifdef MOZ_CRASHREPORTER_INJECTOR #ifdef MOZ_CRASHREPORTER_INJECTOR
static nsIThread* sInjectorThread; static nsIThread* sInjectorThread;
typedef nsDataHashtable<nsUint32HashKey, InjectorCrashCallback*> InjectorPIDMap;
static InjectorPIDMap* pidToInjectorCallback;
class ReportInjectedCrash : public nsRunnable class ReportInjectedCrash : public nsRunnable
{ {
@ -714,13 +734,13 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
#ifdef XP_WIN32 #ifdef XP_WIN32
nsString crashReporterPath_temp; nsString crashReporterPath_temp;
exePath->GetPath(crashReporterPath_temp);
exePath->GetPath(crashReporterPath_temp);
crashReporterPath = ToNewUnicode(crashReporterPath_temp); crashReporterPath = ToNewUnicode(crashReporterPath_temp);
#elif !defined(__ANDROID__) #elif !defined(__ANDROID__)
nsCString crashReporterPath_temp; nsCString crashReporterPath_temp;
exePath->GetNativePath(crashReporterPath_temp);
exePath->GetNativePath(crashReporterPath_temp);
crashReporterPath = ToNewCString(crashReporterPath_temp); crashReporterPath = ToNewCString(crashReporterPath_temp);
#else #else
// On Android, we launch using the application package name // On Android, we launch using the application package name
@ -1133,6 +1153,11 @@ nsresult UnsetExceptionHandler()
delete notesField; delete notesField;
notesField = nsnull; notesField = nsnull;
if (pendingDirectory) {
NS_Free(pendingDirectory);
pendingDirectory = nsnull;
}
if (crashReporterPath) { if (crashReporterPath) {
NS_Free(crashReporterPath); NS_Free(crashReporterPath);
crashReporterPath = nsnull; crashReporterPath = nsnull;
@ -1671,23 +1696,23 @@ nsresult SetSubmitReports(bool aSubmitReports)
} }
// The "pending" dir is Crash Reports/pending, from which minidumps // The "pending" dir is Crash Reports/pending, from which minidumps
// can be submitted // can be submitted. Because this method may be called off the main thread,
// we store the pending directory as a path.
static bool static bool
GetPendingDir(nsIFile** dir) GetPendingDir(nsIFile** dir)
{ {
nsCOMPtr<nsIProperties> dirSvc = MOZ_ASSERT(OOPInitialized());
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); if (!pendingDirectory) {
if (!dirSvc)
return false; return false;
nsCOMPtr<nsIFile> pendingDir; }
if (NS_FAILED(dirSvc->Get("UAppData",
NS_GET_IID(nsIFile), nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
getter_AddRefs(pendingDir))) || #ifdef XP_WIN
NS_FAILED(pendingDir->Append(NS_LITERAL_STRING("Crash Reports"))) || pending->InitWithPath(nsDependentString(pendingDirectory));
NS_FAILED(pendingDir->Append(NS_LITERAL_STRING("pending")))) #else
return false; pending->InitWithNativePath(nsDependentCString(pendingDirectory));
*dir = NULL; #endif
pendingDir.swap(*dir); pending.swap(*dir);
return true; return true;
} }
@ -1952,12 +1977,22 @@ OnChildProcessDumpRequested(void* aContext,
aClientInfo->pid(); aClientInfo->pid();
#endif #endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
bool runCallback;
#endif
{ {
MutexAutoLock lock(*dumpMapLock); MutexAutoLock lock(*dumpMapLock);
pidToMinidump->Put(pid, minidump); ChildProcessData* pd = pidToMinidump->PutEntry(pid);
MOZ_ASSERT(!pd->minidump);
pd->minidump = minidump;
pd->sequence = ++crashSequence;
#ifdef MOZ_CRASHREPORTER_INJECTOR
runCallback = NULL != pd->callback;
#endif
} }
#ifdef MOZ_CRASHREPORTER_INJECTOR #ifdef MOZ_CRASHREPORTER_INJECTOR
NS_DispatchToMainThread(new ReportInjectedCrash(pid)); if (runCallback)
NS_DispatchToMainThread(new ReportInjectedCrash(pid));
#endif #endif
} }
} }
@ -1973,11 +2008,14 @@ static bool ChildFilter(void *context) {
return true; return true;
} }
static void void
OOPInit() OOPInit()
{ {
NS_ABORT_IF_FALSE(!OOPInitialized(), if (OOPInitialized())
"OOP crash reporter initialized more than once!"); return;
MOZ_ASSERT(NS_IsMainThread());
NS_ABORT_IF_FALSE(gExceptionHandler != NULL, NS_ABORT_IF_FALSE(gExceptionHandler != NULL,
"attempt to initialize OOP crash reporter before in-process crashreporter!"); "attempt to initialize OOP crash reporter before in-process crashreporter!");
@ -2038,6 +2076,26 @@ OOPInit()
pidToMinidump->Init(); pidToMinidump->Init();
dumpMapLock = new Mutex("CrashReporter::dumpMapLock"); dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
nsCOMPtr<nsIFile> pendingDir;
nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
}
else {
pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
pendingDir->Append(NS_LITERAL_STRING("pending"));
#ifdef XP_WIN
nsString path;
pendingDir->GetPath(path);
pendingDirectory = ToNewUnicode(path);
#else
nsCString path;
pendingDir->GetNativePath(path);
pendingDirectory = ToNewCString(path);
#endif
}
} }
static void static void
@ -2053,9 +2111,6 @@ OOPDeinit()
sInjectorThread->Shutdown(); sInjectorThread->Shutdown();
NS_RELEASE(sInjectorThread); NS_RELEASE(sInjectorThread);
} }
delete pidToInjectorCallback;
pidToInjectorCallback = NULL;
#endif #endif
delete crashServer; delete crashServer;
@ -2081,8 +2136,7 @@ GetChildNotificationPipe()
if (!GetEnabled()) if (!GetEnabled())
return kNullNotifyPipe; return kNullNotifyPipe;
if (!OOPInitialized()) MOZ_ASSERT(OOPInitialized());
OOPInit();
return childCrashNotifyPipe; return childCrashNotifyPipe;
} }
@ -2098,17 +2152,17 @@ InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
if (!OOPInitialized()) if (!OOPInitialized())
OOPInit(); OOPInit();
if (!pidToInjectorCallback) {
pidToInjectorCallback = new InjectorPIDMap;
pidToInjectorCallback->Init();
}
if (!sInjectorThread) { if (!sInjectorThread) {
if (NS_FAILED(NS_NewThread(&sInjectorThread))) if (NS_FAILED(NS_NewThread(&sInjectorThread)))
return; return;
} }
pidToInjectorCallback->Put(processID, cb); {
MutexAutoLock lock(*dumpMapLock);
ChildProcessData* pd = pidToMinidump->PutEntry(processID);
MOZ_ASSERT(!pd->minidump && !pd->callback);
pd->callback = cb;
}
nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID); nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL); sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
@ -2118,23 +2172,22 @@ NS_IMETHODIMP
ReportInjectedCrash::Run() ReportInjectedCrash::Run()
{ {
// Crash reporting may have been disabled after this method was dispatched // Crash reporting may have been disabled after this method was dispatched
if (!pidToInjectorCallback) if (!OOPInitialized())
return NS_OK; return NS_OK;
InjectorCrashCallback* cb = pidToInjectorCallback->Get(mPID); InjectorCrashCallback* cb;
if (!cb) {
return NS_OK; MutexAutoLock lock(*dumpMapLock);
ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
if (!pd || !pd->callback)
return NS_OK;
nsCOMPtr<nsIFile> minidump; MOZ_ASSERT(pd->minidump);
if (!TakeMinidumpForChild(mPID, getter_AddRefs(minidump))) {
NS_WARNING("No minidump for crash notification."); cb = pd->callback;
return NS_OK;
} }
nsString id; cb->OnCrash(mPID);
GetIDFromMinidump(minidump, id);
cb->OnCrash(mPID, id);
return NS_OK; return NS_OK;
} }
@ -2144,7 +2197,8 @@ UnregisterInjectorCallback(DWORD processID)
if (!OOPInitialized()) if (!OOPInitialized())
return; return;
pidToInjectorCallback->Remove(processID); MutexAutoLock lock(*dumpMapLock);
pidToMinidump->RemoveEntry(processID);
} }
#endif // MOZ_CRASHREPORTER_INJECTOR #endif // MOZ_CRASHREPORTER_INJECTOR
@ -2190,8 +2244,7 @@ CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
return true; return true;
} }
if (!OOPInitialized()) MOZ_ASSERT(OOPInitialized());
OOPInit();
*childCrashFd = clientSocketFd; *childCrashFd = clientSocketFd;
*childCrashRemapFd = kMagicChildCrashReportFd; *childCrashRemapFd = kMagicChildCrashReportFd;
@ -2251,22 +2304,25 @@ SetRemoteExceptionHandler(const nsACString& crashPipe)
bool bool
TakeMinidumpForChild(PRUint32 childPid, nsIFile** dump) TakeMinidumpForChild(PRUint32 childPid, nsIFile** dump, PRUint32* aSequence)
{ {
if (!GetEnabled()) if (!GetEnabled())
return false; return false;
MutexAutoLock lock(*dumpMapLock); MutexAutoLock lock(*dumpMapLock);
nsCOMPtr<nsIFile> d; ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
bool found = pidToMinidump->Get(childPid, getter_AddRefs(d)); if (!pd)
if (found) return false;
pidToMinidump->Remove(childPid);
*dump = NULL; NS_IF_ADDREF(*dump = pd->minidump);
d.swap(*dump); if (aSequence) {
*aSequence = pd->sequence;
}
pidToMinidump->RemoveEntry(childPid);
return found; return !!*dump;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -69,11 +69,17 @@ nsresult SetSubmitReports(bool aSubmitReport);
// Out-of-process crash reporter API. // Out-of-process crash reporter API.
// Return true iff a dump was found for |childPid|, and return the // Initializes out-of-process crash reporting. This method must be called
// before the platform-specifi notificationpipe APIs are called.
void OOPInit();
// Return true if a dump was found for |childPid|, and return the
// path in |dump|. The caller owns the last reference to |dump| if it // path in |dump|. The caller owns the last reference to |dump| if it
// is non-NULL. // is non-NULL. The sequence parameter will be filled with an ordinal
// indicating which remote process crashed first.
bool TakeMinidumpForChild(PRUint32 childPid, bool TakeMinidumpForChild(PRUint32 childPid,
nsIFile** dump NS_OUTPARAM); nsIFile** dump NS_OUTPARAM,
PRUint32* aSequence = NULL);
#if defined(XP_WIN) #if defined(XP_WIN)
typedef HANDLE ProcessHandle; typedef HANDLE ProcessHandle;
@ -120,9 +126,17 @@ class InjectorCrashCallback
public: public:
InjectorCrashCallback() { } InjectorCrashCallback() { }
virtual void OnCrash(DWORD processID, const nsAString& aDumpID) = 0; /**
* Inform the callback of a crash. The client code should call
* TakeMinidumpForChild to remove it from the PID mapping table.
*
* The callback will not be fired if the client has already called
* TakeMinidumpForChild for this process ID.
*/
virtual void OnCrash(DWORD processID) = 0;
}; };
// This method implies OOPInit
void InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb); void InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb);
void UnregisterInjectorCallback(DWORD processID); void UnregisterInjectorCallback(DWORD processID);
#endif #endif

View File

@ -29,7 +29,7 @@ function run_test()
if (is_windows) { if (is_windows) {
['SystemMemoryUsePercentage', 'TotalVirtualMemory', 'AvailableVirtualMemory', ['SystemMemoryUsePercentage', 'TotalVirtualMemory', 'AvailableVirtualMemory',
'AvailablePageFile', 'AvailablePhysicalMemory'].forEach(function(prop) { 'AvailablePageFile', 'AvailablePhysicalMemory'].forEach(function(prop) {
do_check_true(extra[prop].toString().match(/^\d+$/)); do_check_true(/^\d+$/.test(extra[prop].toString()));
}); });
} }
if (is_win7_or_newer) if (is_win7_or_newer)

View File

@ -226,9 +226,10 @@ GeckoProcessType sChildProcessType = GeckoProcessType_Default;
// IPDL wants access to this crashreporter interface, and // IPDL wants access to this crashreporter interface, and
// crashreporter is built in such a way to make that awkward // crashreporter is built in such a way to make that awkward
bool bool
XRE_TakeMinidumpForChild(PRUint32 aChildPid, nsIFile** aDump) XRE_TakeMinidumpForChild(PRUint32 aChildPid, nsIFile** aDump,
PRUint32* aSequence)
{ {
return CrashReporter::TakeMinidumpForChild(aChildPid, aDump); return CrashReporter::TakeMinidumpForChild(aChildPid, aDump, aSequence);
} }
bool bool

View File

@ -367,7 +367,8 @@ XRE_API(GeckoProcessType,
#if defined(MOZ_CRASHREPORTER) #if defined(MOZ_CRASHREPORTER)
// Used in the "master" parent process hosting the crash server // Used in the "master" parent process hosting the crash server
XRE_API(bool, XRE_API(bool,
XRE_TakeMinidumpForChild, (PRUint32 aChildPid, nsIFile** aDump)) XRE_TakeMinidumpForChild, (PRUint32 aChildPid, nsIFile** aDump,
PRUint32* aSequence))
// Used in child processes. // Used in child processes.
XRE_API(bool, XRE_API(bool,