bug 761909 - crash reporter plumbing for gonk. r=bsmedberg

This commit is contained in:
Ted Mielczarek 2012-07-16 19:50:52 -04:00
parent 5bbf7af69a
commit e865ee4b36
5 changed files with 247 additions and 81 deletions

View File

@ -17,7 +17,7 @@ namespace dom {
void
CrashReporterParent::ActorDestroy(ActorDestroyReason why)
{
#if defined(__ANDROID__) && defined(MOZ_CRASHREPORTER)
#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
CrashReporter::RemoveLibraryMappingsForChild(ProcessId(OtherProcess()));
#endif
}
@ -25,7 +25,7 @@ CrashReporterParent::ActorDestroy(ActorDestroyReason why)
bool
CrashReporterParent::RecvAddLibraryMappings(const InfallibleTArray<Mapping>& mappings)
{
#if defined(__ANDROID__) && defined(MOZ_CRASHREPORTER)
#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
for (PRUint32 i = 0; i < mappings.Length(); i++) {
const Mapping& m = mappings[i];
CrashReporter::AddLibraryMappingForChild(ProcessId(OtherProcess()),

View File

@ -155,9 +155,12 @@ static google_breakpad::ExceptionHandler* gExceptionHandler = nsnull;
static XP_CHAR* pendingDirectory;
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
static bool doReport = true;
// If this is true, we don't have a crash reporter
static bool headlessClient = false;
// if this is true, we pass the exception on to the OS crash reporter
static bool showOSCrashReporter = false;
@ -166,6 +169,15 @@ static time_t lastCrashTime = 0;
// The pathname of a file to store the crash time in
static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
// A marker file to hold the path to the last dump written, which
// will be checked on startup.
static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
// Whether we've already looked for the marker file.
static bool lastRunCrashID_checked = false;
// The minidump ID contained in the marker file.
static nsString* lastRunCrashID = nsnull;
// these are just here for readability
static const char kCrashTimeParameter[] = "CrashTime=";
static const int kCrashTimeParameterLen = sizeof(kCrashTimeParameter)-1;
@ -314,7 +326,7 @@ static cpu_type_t pref_cpu_types[2] = {
static posix_spawnattr_t spawnattr;
#endif
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
// Android builds use a custom library loader,
// so the embedding will provide a list of shared
// libraries that are mapped into anonymous mappings.
@ -416,6 +428,29 @@ bool MinidumpCallback(const XP_CHAR* dump_path,
p = Concat(p, minidump_id, &size);
Concat(p, extraFileExtension, &size);
if (headlessClient) {
// Leave a marker indicating that there was a crash.
#if defined(XP_WIN32)
HANDLE hFile = CreateFile(crashMarkerFilename, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile != INVALID_HANDLE_VALUE) {
DWORD nBytes;
WriteFile(hFile, minidumpPath, 2*wcslen(minidumpPath), &nBytes, NULL);
CloseHandle(hFile);
}
#elif defined(XP_UNIX)
int fd = sys_open(crashMarkerFilename,
O_WRONLY | O_CREAT | O_TRUNC,
0600);
if (fd != -1) {
ssize_t ignored = sys_write(fd, minidumpPath, my_strlen(minidumpPath));
(void)ignored;
sys_close(fd);
}
#endif
}
char oomAllocationSizeBuffer[32];
int oomAllocationSizeBufferLen = 0;
if (gOOMAllocationSize) {
@ -471,14 +506,6 @@ bool MinidumpCallback(const XP_CHAR* dump_path,
}
#if defined(XP_WIN32)
XP_CHAR cmdLine[CMDLINE_SIZE];
size = CMDLINE_SIZE;
p = Concat(cmdLine, L"\"", &size);
p = Concat(p, crashReporterPath, &size);
p = Concat(p, L"\" \"", &size);
p = Concat(p, minidumpPath, &size);
Concat(p, L"\"", &size);
if (!crashReporterAPIData->IsEmpty()) {
// write out API data
HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
@ -539,6 +566,14 @@ bool MinidumpCallback(const XP_CHAR* dump_path,
return returnValue;
}
XP_CHAR cmdLine[CMDLINE_SIZE];
size = CMDLINE_SIZE;
p = Concat(cmdLine, L"\"", &size);
p = Concat(p, crashReporterPath, &size);
p = Concat(p, L"\" \"", &size);
p = Concat(p, minidumpPath, &size);
Concat(p, L"\"", &size);
STARTUPINFO si;
PROCESS_INFORMATION pi;
@ -617,7 +652,7 @@ bool MinidumpCallback(const XP_CHAR* dump_path,
if (pid == -1)
return false;
else if (pid == 0) {
#if !defined(__ANDROID__)
#if !defined(MOZ_WIDGET_ANDROID)
// need to clobber this, as libcurl might load NSS,
// and we want it to load the system NSS.
unsetenv("LD_LIBRARY_PATH");
@ -688,8 +723,6 @@ namespace {
nsresult SetExceptionHandler(nsIFile* aXREDirectory,
bool force/*=false*/)
{
nsresult rv;
if (gExceptionHandler)
return NS_ERROR_ALREADY_INITIALIZED;
@ -697,9 +730,14 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
if (envvar && *envvar && !force)
return NS_OK;
#if defined(MOZ_WIDGET_GONK)
doReport = false;
headlessClient = true;
#else
// this environment variable prevents us from launching
// the crash reporter client
doReport = ShouldReport();
#endif
// allocate our strings
crashReporterAPIData = new nsCString();
@ -719,37 +757,37 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
notesField = new nsCString();
NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
// locate crashreporter executable
nsCOMPtr<nsIFile> exePath;
rv = aXREDirectory->Clone(getter_AddRefs(exePath));
NS_ENSURE_SUCCESS(rv, rv);
if (!headlessClient) {
// locate crashreporter executable
nsCOMPtr<nsIFile> exePath;
nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
NS_ENSURE_SUCCESS(rv, rv);
#if defined(XP_MACOSX)
exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
exePath->Append(NS_LITERAL_STRING("Contents"));
exePath->Append(NS_LITERAL_STRING("MacOS"));
exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
exePath->Append(NS_LITERAL_STRING("Contents"));
exePath->Append(NS_LITERAL_STRING("MacOS"));
#endif
exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
#ifdef XP_WIN32
nsString crashReporterPath_temp;
nsString crashReporterPath_temp;
exePath->GetPath(crashReporterPath_temp);
crashReporterPath = ToNewUnicode(crashReporterPath_temp);
exePath->GetPath(crashReporterPath_temp);
crashReporterPath = ToNewUnicode(crashReporterPath_temp);
#elif !defined(__ANDROID__)
nsCString crashReporterPath_temp;
nsCString crashReporterPath_temp;
exePath->GetNativePath(crashReporterPath_temp);
crashReporterPath = ToNewCString(crashReporterPath_temp);
exePath->GetNativePath(crashReporterPath_temp);
crashReporterPath = ToNewCString(crashReporterPath_temp);
#else
// On Android, we launch using the application package name
// instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
//TODO: don't hardcode org.mozilla here, so other vendors can
// ship XUL apps with different package names on Android?
nsCString package(ANDROID_PACKAGE_NAME "/.CrashReporter");
crashReporterPath = ToNewCString(package);
// On Android, we launch using the application package name
// instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
nsCString package(ANDROID_PACKAGE_NAME "/.CrashReporter");
crashReporterPath = ToNewCString(package);
#endif
}
// get temp path to use for minidump path
#if defined(XP_WIN32)
@ -778,12 +816,11 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
tempPath = path;
#elif defined(__ANDROID__)
// GeckoAppShell sets this in the environment
// GeckoAppShell or Gonk's init.rc sets this in the environment
const char *tempenv = PR_GetEnv("TMPDIR");
if (!tempenv)
return NS_ERROR_FAILURE;
nsCString tempPath(tempenv);
#elif defined(XP_UNIX)
// we assume it's always /tmp on unix systems
nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
@ -897,7 +934,7 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
showOSCrashReporter = prefValue;
#endif
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
for (unsigned int i = 0; i < library_mappings.size(); i++) {
u_int8_t guid[sizeof(MDGUID)];
FileIDToGUID(library_mappings[i].debug_id.c_str(), guid);
@ -1122,6 +1159,34 @@ nsresult SetupExtraData(nsIFile* aAppDataDirectory,
strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
#endif
if (headlessClient) {
nsCOMPtr<nsIFile> markerFile;
rv = dataDirectory->Clone(getter_AddRefs(markerFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
NS_ENSURE_SUCCESS(rv, rv);
memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
#if defined(XP_WIN32)
nsAutoString markerFilename;
rv = markerFile->GetPath(markerFilename);
NS_ENSURE_SUCCESS(rv, rv);
if (markerFilename.Length() < XP_PATH_MAX)
wcsncpy(crashMarkerFilename, markerFilename.get(),
markerFilename.Length());
#else
nsCAutoString markerFilename;
rv = markerFile->GetNativePath(markerFilename);
NS_ENSURE_SUCCESS(rv, rv);
if (markerFilename.Length() < XP_PATH_MAX)
strncpy(crashMarkerFilename, markerFilename.get(),
markerFilename.Length());
#endif
}
return NS_OK;
}
@ -1153,6 +1218,9 @@ nsresult UnsetExceptionHandler()
delete notesField;
notesField = nsnull;
delete lastRunCrashID;
lastRunCrashID = nsnull;
if (pendingDirectory) {
NS_Free(pendingDirectory);
pendingDirectory = nsnull;
@ -1513,26 +1581,26 @@ static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
// We need to ensure the registry keys are created so we can properly
// write values to it
// Create appVendor key
if(!appVendor.IsEmpty()) {
regPath.Append(appVendor);
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
nsIWindowsRegKey::ACCESS_SET_VALUE);
regPath.AppendLiteral("\\");
}
// Create appName key
regPath.Append(appName);
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
nsIWindowsRegKey::ACCESS_SET_VALUE);
regPath.AppendLiteral("\\");
// Create Crash Reporter key
regPath.AppendLiteral("Crash Reporter");
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
// Create appVendor key
if(!appVendor.IsEmpty()) {
regPath.Append(appVendor);
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
nsIWindowsRegKey::ACCESS_SET_VALUE);
regPath.AppendLiteral("\\");
}
// Create appName key
regPath.Append(appName);
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
nsIWindowsRegKey::ACCESS_SET_VALUE);
regPath.AppendLiteral("\\");
// Create Crash Reporter key
regPath.AppendLiteral("Crash Reporter");
regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertUTF8toUTF16(regPath),
nsIWindowsRegKey::ACCESS_SET_VALUE);
// If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
@ -1695,6 +1763,33 @@ nsresult SetSubmitReports(bool aSubmitReports)
return NS_OK;
}
static void
FindPendingDir()
{
if (pendingDirectory)
return;
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
}
}
// The "pending" dir is Crash Reports/pending, from which minidumps
// can be submitted. Because this method may be called off the main thread,
// we store the pending directory as a path.
@ -1944,7 +2039,7 @@ OnChildProcessDumpRequested(void* aContext,
#endif
getter_AddRefs(minidump));
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
// Do dump generation here since the CrashGenerationServer doesn't
// have access to the library mappings.
MappingMap::const_iterator iter =
@ -2045,7 +2140,7 @@ OOPInit()
const std::string dumpPath = gExceptionHandler->dump_path();
bool generateDumps = true;
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
// On Android, the callback will do dump generation, since it needs
// to pass the library mappings.
generateDumps = false;
@ -2081,25 +2176,7 @@ OOPInit()
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
}
FindPendingDir();
}
static void
@ -2207,6 +2284,77 @@ UnregisterInjectorCallback(DWORD processID)
#endif // MOZ_CRASHREPORTER_INJECTOR
bool
CheckForLastRunCrash()
{
if (lastRunCrashID)
return true;
// The exception handler callback leaves the filename of the
// last minidump in a known file.
nsCOMPtr<nsIFile> lastCrashFile;
CreateFileFromPath(crashMarkerFilename,
getter_AddRefs(lastCrashFile));
bool exists;
if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
return false;
}
nsCAutoString lastMinidump_contents;
if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
return false;
}
lastCrashFile->Remove(false);
#ifdef XP_WIN
// Ugly but effective.
nsDependentString lastMinidump(
reinterpret_cast<const PRUnichar*>(lastMinidump_contents.get()));
#else
nsCAutoString lastMinidump = lastMinidump_contents;
#endif
nsCOMPtr<nsIFile> lastMinidumpFile;
CreateFileFromPath(lastMinidump.get(),
getter_AddRefs(lastMinidumpFile));
if (NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
return false;
}
nsCOMPtr<nsIFile> lastExtraFile;
if (!GetExtraFileForMinidump(lastMinidumpFile,
getter_AddRefs(lastExtraFile))) {
return false;
}
FindPendingDir();
// Move {dump,extra} to pending folder
if (!MoveToPending(lastMinidumpFile, lastExtraFile)) {
return false;
}
lastRunCrashID = new nsString();
return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
}
bool
GetLastRunCrashID(nsAString& id)
{
if (!lastRunCrashID_checked) {
CheckForLastRunCrash();
lastRunCrashID_checked = true;
}
if (!lastRunCrashID) {
return false;
}
id = *lastRunCrashID;
return true;
}
#if defined(XP_WIN)
// Child-side API
bool
@ -2479,7 +2627,7 @@ UnsetRemoteExceptionHandler()
return true;
}
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
void AddLibraryMapping(const char* library_name,
const char* file_id,
uintptr_t start_address,

View File

@ -43,6 +43,7 @@ nsresult AppendAppNotesToCrashReport(const nsACString& data);
nsresult SetRestartArgs(int argc, char** argv);
nsresult SetupExtraData(nsIFile* aAppDataDirectory,
const nsACString& aBuildID);
bool GetLastRunCrashID(nsAString& id);
// Registers an additional memory region to be included in the minidump
nsresult RegisterAppMemory(void* ptr, size_t length);
@ -164,7 +165,7 @@ bool SetRemoteExceptionHandler();
bool UnsetRemoteExceptionHandler();
#if defined(__ANDROID__)
#if defined(MOZ_WIDGET_ANDROID)
// Android builds use a custom library loader, so /proc/<pid>/maps
// will just show anonymous mappings for all the non-system
// shared libraries. This API is to work around that by providing

View File

@ -821,6 +821,17 @@ nsXULAppInfo::GetReplacedLockTime(PRInt64 *aReplacedLockTime)
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetLastRunCrashID(nsAString &aLastRunCrashID)
{
#ifdef MOZ_CRASHREPORTER
CrashReporter::GetLastRunCrashID(aLastRunCrashID);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
#ifdef XP_WIN
// Matches the enum in WinNT.h for the Vista SDK but renamed so that we can
// safely build with the Vista SDK and without it.

View File

@ -89,4 +89,10 @@ interface nsIXULRuntime : nsISupports
* closed cleanly. This is set to 0 if there was no existing profile lock.
*/
readonly attribute PRInt64 replacedLockTime;
/**
* Local ID of the minidump generated when the process crashed
* on the previous run. Can be passed directly to CrashSubmit.submit.
*/
readonly attribute DOMString lastRunCrashID;
};