Bug 769048 part E - Core crashreporter support for injecting a crashreporter DLL into an arbitrary process and callbacks for notifications when that process has crashed, r=ehsan

--HG--
extra : rebase_source : eec98ad1d72106a34dfcd0764d85092a0725565c
This commit is contained in:
Benjamin Smedberg 2012-07-02 14:55:23 -04:00
parent 605abb1afb
commit d0173d7909
5 changed files with 224 additions and 2 deletions

View File

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "InjectCrashReporter.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "client/windows/crash_generation/crash_generation_client.h"
#include "nsExceptionHandler.h"
#include "LoadLibraryRemote.h"
#include "nsWindowsHelpers.h"
using google_breakpad::CrashGenerationClient;
using CrashReporter::GetChildNotificationPipe;
namespace mozilla {
InjectCrashRunnable::InjectCrashRunnable(DWORD pid)
: mPID(pid)
{
nsCOMPtr<nsIFile> dll;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(dll));
if (NS_SUCCEEDED(rv)) {
dll->Append(NS_LITERAL_STRING("breakpadinjector.dll"));
dll->GetPath(mInjectorPath);
}
}
NS_IMETHODIMP
InjectCrashRunnable::Run()
{
if (mInjectorPath.IsEmpty())
return NS_OK;
nsAutoHandle hProcess(
OpenProcess(PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION |
PROCESS_DUP_HANDLE |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE |
PROCESS_VM_READ, FALSE, mPID));
if (!hProcess) {
NS_WARNING("Unable to open remote process handle for crashreporter injection.");
return NS_OK;
}
void* proc = LoadRemoteLibraryAndGetAddress(hProcess, mInjectorPath.get(),
"Start");
if (!proc) {
NS_WARNING("Unable to inject crashreporter DLL.");
return NS_OK;
}
HANDLE hRemotePipe =
CrashGenerationClient::DuplicatePipeToClientProcess(
NS_ConvertASCIItoUTF16(GetChildNotificationPipe()).get(),
hProcess);
if (INVALID_HANDLE_VALUE == hRemotePipe) {
NS_WARNING("Unable to duplicate crash reporter pipe to process.");
return NS_OK;
}
nsAutoHandle hThread(CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) proc,
(void*) hRemotePipe, 0, NULL));
if (!hThread) {
NS_WARNING("Unable to CreateRemoteThread");
// We have to close the remote pipe or else our crash generation client
// will be stuck unable to accept other remote requests.
HANDLE toClose = INVALID_HANDLE_VALUE;
if (DuplicateHandle(hProcess, hRemotePipe, ::GetCurrentProcess(),
&toClose, 0, FALSE,
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
CloseHandle(toClose);
return NS_OK;
}
}
return NS_OK;
}
} // namespace mozilla

View File

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "nsThreadUtils.h"
#include <windows.h>
namespace mozilla {
class InjectCrashRunnable : public nsRunnable
{
public:
InjectCrashRunnable(DWORD pid);
NS_IMETHOD Run();
private:
DWORD mPID;
nsString mInjectorPath;
};
} // Namespace mozilla

View File

@ -90,6 +90,7 @@ CPPSRCS = \
ifdef MOZ_CRASHREPORTER_INJECTOR
CPPSRCS += \
LoadLibraryRemote.cpp \
InjectCrashReporter.cpp \
$(NULL)
endif

View File

@ -62,6 +62,11 @@
#error "Not yet implemented for this platform"
#endif // defined(XP_WIN32)
#ifdef MOZ_CRASHREPORTER_INJECTOR
#include "InjectCrashReporter.h"
using mozilla::InjectCrashRunnable;
#endif
#include <stdlib.h>
#include <time.h>
#include <prenv.h>
@ -218,6 +223,23 @@ static Mutex* dumpMapLock;
typedef nsInterfaceHashtable<nsUint32HashKey, nsIFile> ChildMinidumpMap;
static ChildMinidumpMap* pidToMinidump;
#ifdef MOZ_CRASHREPORTER_INJECTOR
static nsIThread* sInjectorThread;
typedef nsDataHashtable<nsUint32HashKey, InjectorCrashCallback*> InjectorPIDMap;
static InjectorPIDMap* pidToInjectorCallback;
class ReportInjectedCrash : public nsRunnable
{
public:
ReportInjectedCrash(PRUint32 pid) : mPID(pid) { }
NS_IMETHOD Run();
private:
PRUint32 mPID;
};
#endif // MOZ_CRASHREPORTER_INJECTOR
// Crashreporter annotations that we don't send along in subprocess
// reports
static const char* kSubprocessBlacklist[] = {
@ -1902,8 +1924,13 @@ OnChildProcessDumpRequested(void* aContext,
aClientInfo->pid();
#endif
MutexAutoLock lock(*dumpMapLock);
pidToMinidump->Put(pid, minidump);
{
MutexAutoLock lock(*dumpMapLock);
pidToMinidump->Put(pid, minidump);
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
NS_DispatchToMainThread(new ReportInjectedCrash(pid));
#endif
}
}
@ -1986,6 +2013,16 @@ OOPDeinit()
return;
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
if (sInjectorThread) {
sInjectorThread->Shutdown();
NS_RELEASE(sInjectorThread);
}
delete pidToInjectorCallback;
pidToInjectorCallback = NULL;
#endif
delete crashServer;
crashServer = NULL;
@ -2016,6 +2053,67 @@ GetChildNotificationPipe()
}
#endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
void
InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
{
if (!GetEnabled())
return;
if (!OOPInitialized())
OOPInit();
if (!pidToInjectorCallback) {
pidToInjectorCallback = new InjectorPIDMap;
pidToInjectorCallback->Init();
}
if (!sInjectorThread) {
if (NS_FAILED(NS_NewThread(&sInjectorThread)))
return;
}
pidToInjectorCallback->Put(processID, cb);
nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
}
NS_IMETHODIMP
ReportInjectedCrash::Run()
{
// Crash reporting may have been disabled after this method was dispatched
if (!pidToInjectorCallback)
return NS_OK;
InjectorCrashCallback* cb = pidToInjectorCallback->Get(mPID);
if (!cb)
return NS_OK;
nsCOMPtr<nsIFile> minidump;
if (!TakeMinidumpForChild(mPID, getter_AddRefs(minidump))) {
NS_WARNING("No minidump for crash notification.");
return NS_OK;
}
nsString id;
GetIDFromMinidump(minidump, id);
cb->OnCrash(mPID, id);
return NS_OK;
}
void
UnregisterInjectorCallback(DWORD processID)
{
if (!OOPInitialized())
return;
pidToInjectorCallback->Remove(processID);
}
#endif // MOZ_CRASHREPORTER_INJECTOR
#if defined(XP_WIN)
// Child-side API
bool

View File

@ -111,6 +111,22 @@ bool CreatePairedMinidumps(ProcessHandle childPid,
// Parent-side API for children
const char* GetChildNotificationPipe();
#ifdef MOZ_CRASHREPORTER_INJECTOR
// Inject a crash report client into an arbitrary process, and inform the
// callback object when it crashes. Parent process only.
class InjectorCrashCallback
{
public:
InjectorCrashCallback() { }
virtual void OnCrash(DWORD processID, const nsAString& aDumpID) = 0;
};
void InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb);
void UnregisterInjectorCallback(DWORD processID);
#endif
// Child-side API
bool SetRemoteExceptionHandler(const nsACString& crashPipe);