Bug 516759: Use OOP crash reporting in Gecko. r=luser
authorChris Jones <jones.chris.g@gmail.com>
Tue, 12 Jan 2010 15:14:38 -0600
changeset 37203 854866e02b61f1d3d375f146137a9f0300c3d646
parent 37202 832fb29e91ddd5281238a3c07f7c55a9be7af69e
child 37204 76bd5fa371deb14e71894858482981a9e9743aa0
push idunknown
push userunknown
push dateunknown
reviewersluser
bugs516759
milestone1.9.3a1pre
Bug 516759: Use OOP crash reporting in Gecko. r=luser
ipc/app/MozillaRuntimeMain.cpp
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/Makefile.in
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/xre/Makefile.in
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
--- a/ipc/app/MozillaRuntimeMain.cpp
+++ b/ipc/app/MozillaRuntimeMain.cpp
@@ -49,16 +49,39 @@
 #include <windows.h>
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
 #endif
 
 int
 main(int argc, char* argv[])
 {
+#if defined(MOZ_CRASHREPORTER)
+    if (argc < 2)
+        return 1;
+    const char* const crashReporterArg = argv[--argc];
+
+#  if defined(XP_WIN)
+    // on windows, |crashReporterArg| is the named pipe on which the
+    // server is listening for requests, or "-" if crash reporting is
+    // disabled.
+    if (0 != strcmp("-", crashReporterArg)
+        && !XRE_SetRemoteExceptionHandler(crashReporterArg))
+        return 1;
+#  elif defined(OS_LINUX)
+    // on POSIX, |crashReporterArg| is "true" if crash reporting is
+    // enabled, false otherwise
+    if (0 != strcmp("false", crashReporterArg)
+        && !XRE_SetRemoteExceptionHandler())
+        return 1;
+#  else
+#    error "OOP crash reporting unsupported on this platform"
+#  endif   
+#endif // if defined(MOZ_CRASHREPORTER)
+
 #if defined(XP_WIN) && defined(DEBUG_bent)
     MessageBox(NULL, L"Hi", L"Hi", MB_OK);
 #endif
 
     GeckoProcessType proctype =
         XRE_StringToChildProcessType(argv[argc - 1]);
 
     nsresult rv = XRE_InitChildProcess(argc - 1, argv, proctype);
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -41,16 +41,21 @@
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/process_watcher.h"
 
 #include "prprf.h"
 
+#if defined(OS_LINUX)
+#  define XP_LINUX 1
+#endif
+#include "nsExceptionHandler.h"
+
 #include "mozilla/ipc/GeckoThread.h"
 
 using mozilla::MonitorAutoEnter;
 using mozilla::ipc::GeckoChildProcessHost;
 
 template<>
 struct RunnableMethodTraits<GeckoChildProcessHost>
 {
@@ -174,29 +179,45 @@ GeckoChildProcessHost::PerformAsyncLaunc
   exePath = exePath.DirName();
   exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
 
   // remap the IPC socket fd to a well-known int, as the OS does for
   // STDOUT_FILENO, for example
   int srcChannelFd, dstChannelFd;
   channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
   mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
-  
+
   // no need for kProcessChannelID, the child process inherits the
   // other end of the socketpair() from us
 
   std::vector<std::string> childArgv;
 
   childArgv.push_back(exePath.value());
 
   childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
 
   childArgv.push_back(pidstring);
   childArgv.push_back(childProcessType);
 
+#if defined(MOZ_CRASHREPORTER)
+  int childCrashFd, childCrashRemapFd;
+  if (!CrashReporter::CreateNotificationPipeForChild(
+        &childCrashFd, &childCrashRemapFd))
+    return false;
+  if (0 <= childCrashFd) {
+    mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
+    // "true" == crash reporting enabled
+    childArgv.push_back("true");
+  }
+  else {
+    // "false" == crash reporting disabled
+    childArgv.push_back("false");
+  }
+#endif
+
   base::LaunchApp(childArgv, mFileMap, false, &process);
 
 //--------------------------------------------------
 #elif defined(OS_WIN)
 
   FilePath exePath =
     FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
   exePath = exePath.DirName();
@@ -209,16 +230,20 @@ GeckoChildProcessHost::PerformAsyncLaunc
   for (std::vector<std::string>::iterator it = aExtraOpts.begin();
        it != aExtraOpts.end();
        ++it) {
       cmdLine.AppendLooseValue(UTF8ToWide(*it));
   }
 
   cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
   cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
+#if defined(MOZ_CRASHREPORTER)
+  cmdLine.AppendLooseValue(
+    UTF8ToWide(CrashReporter::GetChildNotificationPipe().BeginReading()));
+#endif
 
   base::LaunchApp(cmdLine, false, false, &process);
 
 #else
 #  error Sorry
 #endif
 
   if (!process) {
--- a/ipc/glue/Makefile.in
+++ b/ipc/glue/Makefile.in
@@ -36,16 +36,18 @@
 
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/crashreporter
+
 MODULE = ipc
 LIBRARY_NAME = mozipc_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 
 EXPORTS_NAMESPACES = IPC mozilla/ipc
 
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in
@@ -43,18 +43,19 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= crash_generation
 LIBRARY_NAME	= crash_generation_s
 
 LOCAL_INCLUDES 	= -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src
 DEFINES += -DUNICODE -D_UNICODE
 
-#XXX: We're not currently building the other parts,
-# which would only be needed on the server side of the equation.
 CPPSRCS		= \
+		client_info.cc \
 		crash_generation_client.cc \
+		crash_generation_server.cc \
+		minidump_generator.cc \
 		$(NULL)
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -38,27 +38,30 @@
 
 #include "nsExceptionHandler.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
 
+#include "client/windows/crash_generation/crash_generation_server.h"
 #include "client/windows/handler/exception_handler.h"
+#include <DbgHelp.h>
 #include <string.h>
 #elif defined(XP_MACOSX)
 #include "client/mac/handler/exception_handler.h"
 #include <string>
 #include <Carbon/Carbon.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include "mac_utils.h"
 #elif defined(XP_LINUX)
+#include "client/linux/crash_generation/crash_generation_server.h"
 #include "client/linux/handler/exception_handler.h"
 #include <fcntl.h>
 #include <sys/types.h>
 #include <unistd.h>
 #elif defined(XP_SOLARIS)
 #include "client/solaris/handler/exception_handler.h"
 #include <fcntl.h>
 #include <sys/types.h>
@@ -72,16 +75,19 @@
 #include <prenv.h>
 #include <prio.h>
 #include <prmem.h>
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsILocalFile.h"
 #include "nsDataHashtable.h"
 
+using google_breakpad::CrashGenerationServer;
+using google_breakpad::ClientInfo;
+
 namespace CrashReporter {
 
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 #define CONVERT_UTF16_TO_XP_CHAR(x) x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
 #define XP_STRLEN(x) wcslen(x)
 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
@@ -136,16 +142,32 @@ static const char kTimeSinceLastCrashPar
 static const int kTimeSinceLastCrashParameterLen =
                                      sizeof(kTimeSinceLastCrashParameter)-1;
 
 // this holds additional data sent via the API
 static nsDataHashtable<nsCStringHashKey,nsCString>* crashReporterAPIData_Hash;
 static nsCString* crashReporterAPIData = nsnull;
 static nsCString* notesField = nsnull;
 
+// OOP crash reporting
+static CrashGenerationServer* crashServer; // chrome process has this
+
+#if defined(XP_WIN)
+// If crash reporting is disabled, we hand out this "null" pipe to the
+// child process and don't attempt to connect to a parent server.
+static const char kNullNotifyPipe[] = "-";
+static nsCString* childCrashNotifyPipe;
+
+#elif defined(XP_LINUX)
+static int serverSocketFd = -1;
+static int clientSocketFd = -1;
+static const int kMagicChildCrashReportFd = 42;
+#endif
+
+
 static XP_CHAR*
 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
 {
   int appendLen = XP_STRLEN(toAppend);
   if (appendLen >= *size) appendLen = *size - 1;
 
   memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
   str += appendLen;
@@ -904,9 +926,163 @@ nsresult AppendObjCExceptionInfoToAppNot
 {
   nsCAutoString excString;
   GetObjCExceptionInfo(inException, excString);
   AppendAppNotesToCrashReport(excString);
   return NS_OK;
 }
 #endif
 
+//-----------------------------------------------------------------------------
+// Out-of-process crash reporting API wrappers
+static void
+OnChildProcessDumpRequested(void* aContext,
+                            const ClientInfo* aClientInfo,
+#if defined(XP_WIN)
+                            const std::wstring*
+#else
+                            const std::string*
+#endif
+                              aFilePath)
+{
+  printf("CHILD DUMP REQUEST\n");
+}
+
+static bool
+OOPInitialized()
+{
+  return crashServer != NULL;
+}
+
+static void
+OOPInit()
+{
+  NS_ABORT_IF_FALSE(!OOPInitialized(),
+                    "OOP crash reporter initialized more than once!");
+  NS_ABORT_IF_FALSE(gExceptionHandler != NULL,
+                    "attempt to initialize OOP crash reporter before in-process crashreporter!");
+
+#if defined(XP_WIN)
+  // this is a CString to make it more convenient to pass on the
+  // command line
+  childCrashNotifyPipe = 
+    new nsCString("\\\\.\\pipe\\gecko-crash-server-pipe.");
+  long pid = static_cast<long>(::GetCurrentProcessId());
+  childCrashNotifyPipe->AppendInt(pid);
+
+  const std::wstring dumpPath = gExceptionHandler->dump_path();
+  crashServer = new CrashGenerationServer(
+    NS_ConvertASCIItoUTF16(*childCrashNotifyPipe).BeginReading(),
+    NULL,                       // default security attributes
+    NULL, NULL,                 // we don't care about process connect here
+    OnChildProcessDumpRequested, NULL,
+    NULL, NULL,                 // we don't care about process exit here
+    true,                       // automatically generate dumps
+    &dumpPath);
+
+#elif defined(XP_LINUX)
+  if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
+                                                  &clientSocketFd))
+    NS_RUNTIMEABORT("can't create crash reporter socketpair()");
+
+  const std::string dumpPath = gExceptionHandler->dump_path();
+  crashServer = new CrashGenerationServer(
+    serverSocketFd,
+    OnChildProcessDumpRequested, NULL,
+    NULL, NULL,                 // we don't care about process exit here
+    true,                       // automatically generate dumps
+    &dumpPath);
+#endif
+
+  if (!crashServer->Start())
+    NS_RUNTIMEABORT("can't start crash reporter server()");
+}
+
+#if defined(XP_WIN)
+// Parent-side API for children
+nsCString
+GetChildNotificationPipe()
+{
+  if (!GetEnabled())
+    return nsDependentCString(kNullNotifyPipe);
+
+  if (!OOPInitialized())
+    OOPInit();
+
+  return *childCrashNotifyPipe;
+}
+
+// Child-side API
+bool
+SetRemoteExceptionHandler(const nsACString& crashPipe)
+{
+  // crash reporting is disabled
+  if (crashPipe.Equals(kNullNotifyPipe))
+    return true;
+
+  NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
+
+  gExceptionHandler = new google_breakpad::
+    ExceptionHandler(L"",
+                     NULL,    // no filter callback
+                     NULL,    // no minidump callback
+                     NULL,    // no callback context
+                     google_breakpad::ExceptionHandler::HANDLER_ALL,
+                     MiniDumpNormal,
+                     NS_ConvertASCIItoUTF16(crashPipe).BeginReading(),
+                     NULL);
+
+  // we either do remote or nothing, no fallback to regular crash reporting
+  return gExceptionHandler->IsOutOfProcess();
+}
+
+//--------------------------------------------------
+#elif defined(XP_UNIX)
+
+// Parent-side API for children
+bool
+CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
+{
+  if (!GetEnabled()) {
+    *childCrashFd = -1;
+    *childCrashRemapFd = -1;
+    return true;
+  }
+
+  if (!OOPInitialized())
+    OOPInit();
+
+  *childCrashFd = clientSocketFd;
+  *childCrashRemapFd = kMagicChildCrashReportFd;
+
+  return true;
+}
+
+// Child-side API
+bool
+SetRemoteExceptionHandler()
+{
+  NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
+
+  gExceptionHandler = new google_breakpad::
+    ExceptionHandler("",
+                     NULL,    // no filter callback
+                     NULL,    // no minidump callback
+                     NULL,    // no callback context
+                     true,    // install signal handlers
+                     kMagicChildCrashReportFd);
+
+  // we either do remote or nothing, no fallback to regular crash reporting
+  return gExceptionHandler->IsOutOfProcess();
+}
+
+
+#endif
+
+bool
+UnsetRemoteExceptionHandler()
+{
+  delete gExceptionHandler;
+  gExceptionHandler = NULL;
+  return true;
+}
+
 } // namespace CrashReporter
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -59,15 +59,38 @@ bool     GetMinidumpPath(nsAString& aPat
 nsresult SetMinidumpPath(const nsAString& aPath);
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
                         const nsACString& aBuildID);
 #ifdef XP_WIN32
   nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo);
+
+// Parent-side API for children
+nsCString GetChildNotificationPipe();
+// Child-side API
+bool SetRemoteExceptionHandler(const nsACString& crashPipe);
 #endif
 #ifdef XP_MACOSX
   nsresult AppendObjCExceptionInfoToAppNotes(void *inException);
 #endif
+#ifdef XP_LINUX
+// Parent-side API for children
+
+// Set the outparams for crash reporter server's fd (|childCrashFd|)
+// and the magic fd number it should be remapped to
+// (|childCrashRemapFd|) before exec() in the child process.
+// |SetRemoteExceptionHandler()| in the child process expects to find
+// the server at |childCrashRemapFd|.  Return true iff successful.
+//
+// If crash reporting is disabled, both outparams will be set to -1
+// and |true| will be returned.
+bool CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd);
+
+// Child-side API
+bool SetRemoteExceptionHandler();
+#endif
+
+bool UnsetRemoteExceptionHandler();
 }
 
 #endif /* nsExceptionHandler_h__ */
--- a/toolkit/xre/Makefile.in
+++ b/toolkit/xre/Makefile.in
@@ -169,17 +169,20 @@ endif
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
-LOCAL_INCLUDES += -I$(topsrcdir)/dom/ipc
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/ipc \
+  -I$(topsrcdir)/toolkit/crashreporter \
+  $(NULL)
 
 ifdef BUILD_STATIC_LIBS
 export::
 	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_LINK_COMP_NAMES) Apprunner
 endif
 
 LOCAL_INCLUDES += \
 	-I$(srcdir) \
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -50,20 +50,25 @@
 
 #include "nsIAppShell.h"
 #include "nsIAppStartupNotifier.h"
 #include "nsIDirectoryService.h"
 #include "nsILocalFile.h"
 #include "nsIToolkitChromeRegistry.h"
 #include "nsIToolkitProfile.h"
 
+#if defined(OS_LINUX)
+#  define XP_LINUX
+#endif
+
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsAutoRef.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsExceptionHandler.h"
 #include "nsStaticComponents.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsXPFEComponentsCID.h"
 #include "nsXREDirProvider.h"
 
 #ifdef MOZ_IPC
@@ -237,16 +242,30 @@ XRE_StringToChildProcessType(const char*
 namespace mozilla {
 namespace startup {
 GeckoProcessType sChildProcessType = GeckoProcessType_Default;
 }
 }
 
 static MessageLoop* sIOMessageLoop;
 
+#if defined(MOZ_CRASHREPORTER)
+PRBool
+XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/)
+{
+#if defined(XP_WIN)
+  return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe));
+#elif defined(OS_LINUX)
+  return CrashReporter::SetRemoteExceptionHandler();
+#else
+#  error "OOP crash reporter unsupported on this platform"
+#endif
+}
+#endif // if defined(MOZ_CRASHREPORTER)
+
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      GeckoProcessType aProcess)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -444,16 +444,21 @@ PR_STATIC_ASSERT(sizeof(kGeckoProcessTyp
 
 
 XRE_API(const char*,
         XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
 
 XRE_API(GeckoProcessType,
         XRE_StringToChildProcessType, (const char* aProcessTypeString))
 
+#if defined(MOZ_CRASHREPORTER)
+XRE_API(PRBool,
+        XRE_SetRemoteExceptionHandler, (const char* aPipe=0))
+#endif
+
 XRE_API(nsresult,
         XRE_InitChildProcess, (int aArgc,
                                char* aArgv[],
                                GeckoProcessType aProcess))
 
 XRE_API(GeckoProcessType,
         XRE_GetProcessType, ())