From 273e53136eeaece2ce3cfafb2fa62ba5ce0b5df3 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 14 Jan 2010 17:38:00 -0500 Subject: [PATCH] bug 539552 - keep track of child process minidumps, offer pid based lookup API, write out .extra data in child minidump callback. r=ted,bsmedberg --HG-- extra : transplant_source : %C2x%AA%8C%9E%FF%05%9A%9A%86aw%09L%A1%FAQ%DATG --- .../crashreporter/content/oopcrashdialog.js | 40 +----- toolkit/crashreporter/nsExceptionHandler.cpp | 125 ++++++++++++++++-- toolkit/crashreporter/nsExceptionHandler.h | 27 +++- toolkit/xre/nsEmbedFunctions.cpp | 9 ++ xpcom/build/nsXULAppAPI.h | 5 + 5 files changed, 155 insertions(+), 51 deletions(-) diff --git a/toolkit/crashreporter/content/oopcrashdialog.js b/toolkit/crashreporter/content/oopcrashdialog.js index 539e96d0ad54..4de6ae47fdf4 100644 --- a/toolkit/crashreporter/content/oopcrashdialog.js +++ b/toolkit/crashreporter/content/oopcrashdialog.js @@ -3,22 +3,6 @@ var id; -function getExtraData() { - let appData = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime); - appData.QueryInterface(Ci.nsICrashReporter); - appData.QueryInterface(Ci.nsIXULAppInfo); - - let r = ""; - r += "ServerURL=" + appData.serverURL.spec + "\n"; - r += "BuildID=" + appData.appBuildID + "\n"; - r += "ProductName=" + appData.name + "\n"; - r += "Vendor=" + appData.vendor + "\n"; - r += "Version=" + appData.version + "\n"; - r += "CrashTime=" + ((new Date()).getTime() / 1000).toFixed() + "\n"; - r += "ProcessType=plugin\n"; - return r; -} - function collectData() { // HACK: crashes.js uses document.body, so we just alias it document.body = document.getElementById('iframe-holder'); @@ -40,27 +24,11 @@ function collectData() { reportsDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770); let dumpFile = window.arguments[0].QueryInterface(Ci.nsIFile); + let extraFile = dumpFile.clone(); + id = dumpFile.leafName.replace(/.dmp$/, ""); + extraFile.leafName = id + ".extra"; dumpFile.moveTo(pendingDir, ""); - let leafName = dumpFile.leafName; - - id = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.dmp/(leafName)[1]; - - dumpFile = pendingDir.clone(); - dumpFile.append(leafName); - - let extraFile = pendingDir.clone(); - extraFile.append(id + ".extra"); - - let fstream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - fstream.init(extraFile, -1, -1, 0); - - var os = Cc["@mozilla.org/intl/converter-output-stream;1"]. - createInstance(Ci.nsIConverterOutputStream); - os.init(fstream, "UTF-8", 0, 0x0000); - os.writeString(getExtraData()); - os.close(); - fstream.close(); + extraFile.moveTo(pendingDir, ""); } function onSubmit() diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index 133e6167e630..a6d26a4642a2 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -59,6 +59,7 @@ #include "mac_utils.h" #elif defined(XP_LINUX) #if defined(MOZ_IPC) +# include "client/linux/crash_generation/client_info.h" # include "client/linux/crash_generation/crash_generation_server.h" #endif #include "client/linux/handler/exception_handler.h" @@ -79,16 +80,22 @@ #include #include #include +#include "mozilla/Mutex.h" #include "nsDebug.h" #include "nsCRT.h" #include "nsILocalFile.h" +#include "nsIFileStreams.h" #include "nsDataHashtable.h" +#include "nsInterfaceHashtable.h" #include "prprf.h" #if defined(MOZ_IPC) using google_breakpad::CrashGenerationServer; using google_breakpad::ClientInfo; +using mozilla::Mutex; +using mozilla::MutexAutoLock; + #include "nsThreadUtils.h" #include "nsIWindowWatcher.h" #include "nsIDOMWindow.h" @@ -173,6 +180,12 @@ static int serverSocketFd = -1; static int clientSocketFd = -1; static const int kMagicChildCrashReportFd = 42; # endif + +// |dumpMapLock| must protect all access to |pidToMinidump|. +static Mutex* dumpMapLock; +typedef nsInterfaceHashtable ChildMinidumpMap; +static ChildMinidumpMap* pidToMinidump; + #endif // MOZ_IPC static XP_CHAR* @@ -247,7 +260,8 @@ bool MinidumpCallback(const XP_CHAR* dump_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { - write(fd, crashTimeString, crashTimeStringLen); + ssize_t ignored = write(fd, crashTimeString, crashTimeStringLen); + (void)ignored; close(fd); } #endif @@ -315,14 +329,17 @@ bool MinidumpCallback(const XP_CHAR* dump_path, if (fd != -1) { // not much we can do in case of error - write(fd, crashReporterAPIData->get(), crashReporterAPIData->Length()); - write(fd, kCrashTimeParameter, kCrashTimeParameterLen); - write(fd, crashTimeString, crashTimeStringLen); - write(fd, "\n", 1); + ssize_t ignored = write(fd, crashReporterAPIData->get(), + crashReporterAPIData->Length()); + ignored = write(fd, kCrashTimeParameter, kCrashTimeParameterLen); + ignored = write(fd, crashTimeString, crashTimeStringLen); + ignored = write(fd, "\n", 1); if (timeSinceLastCrash != 0) { - write(fd, kTimeSinceLastCrashParameter,kTimeSinceLastCrashParameterLen); - write(fd, timeSinceLastCrashString, timeSinceLastCrashStringLen); - write(fd, "\n", 1); + ignored = write(fd, kTimeSinceLastCrashParameter, + kTimeSinceLastCrashParameterLen); + ignored = write(fd, timeSinceLastCrashString, + timeSinceLastCrashStringLen); + ignored = write(fd, "\n", 1); } close (fd); } @@ -971,6 +988,37 @@ private: nsCOMPtr mDumpFile; }; +static PLDHashOperator EnumerateChildAnnotations(const nsACString& key, + nsCString entry, + void* userData) +{ + // blacklist of entries from the parent process that we don't want to + // submit with the child process + static const char* kBlacklist[] = { + "FramePoisonBase", + "FramePoisonSize", + "StartupTime", + "URL" + }; + static const int kBlacklistLength = + sizeof(kBlacklist) / sizeof(kBlacklist[0]); + + // skip entries in the blacklist + for (int i = 0; i < kBlacklistLength; i++) { + if (key.EqualsASCII(kBlacklist[i])) + return PL_DHASH_NEXT; + } + + nsIFileOutputStream* extraStream = + reinterpret_cast(userData); + PRUint32 written; + extraStream->Write(key.BeginReading(), key.Length(), &written); + extraStream->Write("=", 1, &written); + extraStream->Write(entry.BeginReading(), entry.Length(), &written); + extraStream->Write("\n", 1, &written); + return PL_DHASH_NEXT; +} + static void OnChildProcessDumpRequested(void* aContext, const ClientInfo* aClientInfo, @@ -982,13 +1030,59 @@ OnChildProcessDumpRequested(void* aContext, aFilePath) { nsCOMPtr lf; + PRUint32 pid; + #ifdef XP_WIN NS_NewLocalFile(nsDependentString(aFilePath->c_str()), PR_FALSE, getter_AddRefs(lf)); + pid = aClientInfo->pid(); #else NS_NewNativeLocalFile(nsDependentCString(aFilePath->c_str()), PR_FALSE, getter_AddRefs(lf)); + pid = aClientInfo->pid_; #endif + + // Get an .extra file with the same base name as the .dmp file + nsCOMPtr extraFile; + nsresult rv = lf->Clone(getter_AddRefs(extraFile)); + if (NS_FAILED(rv)) + return; + + nsAutoString leafName; + rv = extraFile->GetLeafName(leafName); + if (NS_FAILED(rv)) + return; + + leafName.Replace(leafName.Length() - 3, 3, + NS_LITERAL_STRING("extra")); + rv = extraFile->SetLeafName(leafName); + if (NS_FAILED(rv)) + return; + + // Now write out the annotations to it + nsCOMPtr stream = + do_CreateInstance("@mozilla.org/network/file-output-stream;1"); + rv = stream->Init(extraFile, -1, 0600, 0); + if (NS_FAILED(rv)) + return; + crashReporterAPIData_Hash->EnumerateRead(EnumerateChildAnnotations, + stream.get()); + // Add CrashTime to extra data + time_t crashTime = time(NULL); + char crashTimeString[32]; + XP_TTOA(crashTime, crashTimeString, 10); + + PRUint32 written; + stream->Write(kCrashTimeParameter, kCrashTimeParameterLen, &written); + stream->Write(crashTimeString, strlen(crashTimeString), &written); + stream->Write("\n", 1, &written); + stream->Close(); + + { + MutexAutoLock lock(*dumpMapLock); + pidToMinidump->Put(pid, lf); + } + nsCOMPtr r = new SubmitCrashReport(lf); NS_DispatchToMainThread(r); } @@ -1039,6 +1133,11 @@ OOPInit() if (!crashServer->Start()) NS_RUNTIMEABORT("can't start crash reporter server()"); + + pidToMinidump = new ChildMinidumpMap(); + pidToMinidump->Init(); + + dumpMapLock = new Mutex("CrashReporter::dumpMapLock"); } #if defined(XP_WIN) @@ -1122,6 +1221,16 @@ SetRemoteExceptionHandler() #endif // XP_WIN +bool +GetMinidumpForChild(PRUint32 childPid, nsIFile** dump) +{ + if (!GetEnabled()) + return false; + + MutexAutoLock lock(*dumpMapLock); + return pidToMinidump->Get(childPid, dump); +} + bool UnsetRemoteExceptionHandler() { diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index 1d676dbc7b56..baf698f58152 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -42,6 +42,8 @@ #include "nsXPCOM.h" #include "nsStringGlue.h" +#include "nsIFile.h" + #if defined(XP_WIN32) #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN @@ -64,16 +66,26 @@ nsresult SetupExtraData(nsILocalFile* aAppDataDirectory, const nsACString& aBuildID); #ifdef XP_WIN32 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo); - -// Parent-side API for children -const char* GetChildNotificationPipe(); -// Child-side API -bool SetRemoteExceptionHandler(const nsACString& crashPipe); #endif #ifdef XP_MACOSX nsresult AppendObjCExceptionInfoToAppNotes(void *inException); #endif -#ifdef XP_LINUX + +#ifdef MOZ_IPC +// Out-of-process crash reporter API. + +// Return true iff a dump was found for |childPid|, and return the +// path in |dump|. +bool GetMinidumpForChild(PRUint32 childPid, nsIFile** dump NS_OUTPARAM); + +# if defined(XP_WIN32) +// Parent-side API for children +const char* GetChildNotificationPipe(); + +// Child-side API +bool SetRemoteExceptionHandler(const nsACString& crashPipe); + +# elif defined(XP_LINUX) // Parent-side API for children // Set the outparams for crash reporter server's fd (|childCrashFd|) @@ -88,9 +100,10 @@ bool CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd); // Child-side API bool SetRemoteExceptionHandler(); -#endif +#endif // XP_WIN32 bool UnsetRemoteExceptionHandler(); +#endif // MOZ_IPC } #endif /* nsExceptionHandler_h__ */ diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index c442a8f387f7..81a8ed4de351 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -248,6 +248,15 @@ GeckoProcessType sChildProcessType = GeckoProcessType_Default; static MessageLoop* sIOMessageLoop; #if defined(MOZ_CRASHREPORTER) +// FIXME/bug 539522: this out-of-place function is stuck here because +// IPDL wants access to this crashreporter interface, and +// crashreporter is built in such a way to make that awkward +PRBool +XRE_GetMinidumpForChild(PRUint32 aChildPid, nsIFile** aDump) +{ + return CrashReporter::GetMinidumpForChild(aChildPid, aDump); +} + PRBool XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/) { diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index 0fc1bad220ce..4f3c307c1a9c 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -450,6 +450,11 @@ XRE_API(GeckoProcessType, XRE_StringToChildProcessType, (const char* aProcessTypeString)) #if defined(MOZ_CRASHREPORTER) +// Used in the "master" parent process hosting the crash server +XRE_API(PRBool, + XRE_GetMinidumpForChild, (PRUint32 aChildPid, nsIFile** aDump)) + +// Used in child processes. XRE_API(PRBool, XRE_SetRemoteExceptionHandler, (const char* aPipe)) #endif