Bug 1307153 - Add stack traces to the crash pings in Fennec; r=jchen,ted.mielczarek

MozReview-Commit-ID: ZJKUwHFsuK

--HG--
extra : amend_source : 9a57ff1d2cf15391f1f30fa63585220adbb1a49b
This commit is contained in:
Gabriele Svelto 2018-01-19 16:48:00 +01:00
parent 9abec85212
commit f167c2f3e8
8 changed files with 151 additions and 61 deletions

View File

@ -29,6 +29,8 @@ import java.util.zip.GZIPOutputStream;
import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder; import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
import org.mozilla.gecko.telemetry.TelemetryDispatcher; import org.mozilla.gecko.telemetry.TelemetryDispatcher;
import org.mozilla.gecko.util.INIParser; import org.mozilla.gecko.util.INIParser;
@ -153,7 +155,20 @@ public class CrashReporter extends AppCompatActivity
mPendingExtrasFile = new File(pendingDir, extrasFile.getName()); mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile); moveFile(extrasFile, mPendingExtrasFile);
// Compute the minidump hash and generate the stack traces
computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile); computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
try {
GeckoLoader.loadMozGlue(this);
if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
}
} catch (UnsatisfiedLinkError e) {
Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
}
// Extract the annotations from the .extra file
mExtrasStringMap = new HashMap<String, String>(); mExtrasStringMap = new HashMap<String, String>();
readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap); readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);

View File

@ -0,0 +1,31 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.gecko.mozglue;
/**
* JNI wrapper for accessing the minidump analyzer tool. This is used to
* generate stack traces and other process information from a crash minidump.
*/
public final class MinidumpAnalyzer {
private MinidumpAnalyzer() {
// prevent instantiation
}
/**
* Generate the stacks from the minidump file specified in minidumpPath
* and adds the StackTraces annotation to the associated .extra file.
* If fullStacks is false then only the stack trace for the crashing thread
* will be generated, otherwise stacks will be generated for all threads.
*
* This JNI method is implemented in mozglue/android/nsGeckoUtils.cpp.
*
* @param minidumpPath The path to the minidump file to be analyzed.
* @param fullStacks Specifies if stacks must be generated for all threads.
* @return <code>true</code> if the operation was successful,
* <code>false</code> otherwise.
*/
public static native boolean GenerateStacks(String minidumpPath, boolean fullStacks);
}

View File

@ -18,6 +18,15 @@ SOURCES += [
'SQLiteBridge.cpp', 'SQLiteBridge.cpp',
] ]
if CONFIG['MOZ_CRASHREPORTER']:
USE_LIBS += [
'minidump-analyzer',
]
LOCAL_INCLUDES += [
'/toolkit/crashreporter/minidump-analyzer',
]
FINAL_LIBRARY = 'mozglue' FINAL_LIBRARY = 'mozglue'
for var in ('ANDROID_PACKAGE_NAME', for var in ('ANDROID_PACKAGE_NAME',

View File

@ -11,6 +11,10 @@
#include "Zip.h" #include "Zip.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#ifdef MOZ_CRASHREPORTER
# include "minidump-analyzer.h"
#endif
extern "C" extern "C"
__attribute__ ((visibility("default"))) __attribute__ ((visibility("default")))
void MOZ_JNICALL void MOZ_JNICALL
@ -138,3 +142,21 @@ Java_org_mozilla_gecko_mozglue_NativeZip__1getInputStream(JNIEnv *jenv, jobject
// other Native -> Java call doesn't happen before returning to Java. // other Native -> Java call doesn't happen before returning to Java.
return jenv->CallObjectMethod(jzip, method, buf, (jint) stream.GetType()); return jenv->CallObjectMethod(jzip, method, buf, (jint) stream.GetType());
} }
#ifdef MOZ_CRASHREPORTER
extern "C"
__attribute__ ((visibility("default")))
jboolean MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_MinidumpAnalyzer_GenerateStacks(JNIEnv *jenv, jclass, jstring minidumpPath, jboolean fullStacks)
{
const char* str;
str = jenv->GetStringUTFChars(minidumpPath, nullptr);
bool res = CrashReporter::GenerateStacks(str, fullStacks);
jenv->ReleaseStringUTFChars(minidumpPath, str);
return res;
}
#endif // MOZ_CRASHREPORTER

View File

@ -125,25 +125,6 @@ UTF8toMBCS(const std::string &inp) {
#endif // XP_WIN #endif // XP_WIN
// Check if a file exists at the specified path } // namespace CrashReporter
static inline bool
FileExists(const std::string& aPath)
{
#if defined(XP_WIN)
DWORD attrs = GetFileAttributes(UTF8ToWide(aPath).c_str());
return (attrs != INVALID_FILE_ATTRIBUTES);
#else // Non-Windows
struct stat sb;
int ret = stat(aPath.c_str(), &sb);
if (ret == -1 || !(sb.st_mode & S_IFREG)) {
return false;
}
return true;
#endif // XP_WIN
}
} // namespace
#endif // MinidumpAnalyzerUtils_h #endif // MinidumpAnalyzerUtils_h

View File

@ -3,6 +3,8 @@
* 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/. */
#include "minidump-analyzer.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
@ -232,7 +234,8 @@ ConvertModulesToJSON(const ProcessState& aProcessState,
// crash, the module list and stack traces for every thread // crash, the module list and stack traces for every thread
static void static void
ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot) ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot,
const bool aFullStacks)
{ {
// We use this map to get the index of a module when listed by address // We use this map to get the index of a module when listed by address
OrderedModulesMap orderedModules; OrderedModulesMap orderedModules;
@ -249,8 +252,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
// Record the crashing thread index only if this is a full minidump // Record the crashing thread index only if this is a full minidump
// and all threads' stacks are present, otherwise only the crashing // and all threads' stacks are present, otherwise only the crashing
// thread stack is written out and this field is set to 0. // thread stack is written out and this field is set to 0.
crashInfo["crashing_thread"] = crashInfo["crashing_thread"] = aFullStacks ? requestingThread : 0;
gMinidumpAnalyzerOptions.fullMinidump ? requestingThread : 0;
} }
} else { } else {
crashInfo["type"] = Json::Value(Json::nullValue); crashInfo["type"] = Json::Value(Json::nullValue);
@ -278,7 +280,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
Json::Value threads(Json::arrayValue); Json::Value threads(Json::arrayValue);
int threadCount = aProcessState.threads()->size(); int threadCount = aProcessState.threads()->size();
if (!gMinidumpAnalyzerOptions.fullMinidump && (requestingThread != -1)) { if (!aFullStacks && (requestingThread != -1)) {
// Only add the crashing thread // Only add the crashing thread
Json::Value thread; Json::Value thread;
Json::Value stack(Json::arrayValue); Json::Value stack(Json::arrayValue);
@ -306,7 +308,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
// the node specified in |aRoot| // the node specified in |aRoot|
static bool static bool
ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) { ProcessMinidump(Json::Value& aRoot, const string& aDumpFile, const bool aFullStacks) {
#if XP_WIN && HAVE_64BIT_BUILD #if XP_WIN && HAVE_64BIT_BUILD
MozStackFrameSymbolizer symbolizer; MozStackFrameSymbolizer symbolizer;
MinidumpProcessor minidumpProcessor(&symbolizer, false); MinidumpProcessor minidumpProcessor(&symbolizer, false);
@ -327,7 +329,7 @@ ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) {
rv = minidumpProcessor.Process(&dump, &processState); rv = minidumpProcessor.Process(&dump, &processState);
aRoot["status"] = ResultString(rv); aRoot["status"] = ResultString(rv);
ConvertProcessStateToJSON(processState, aRoot); ConvertProcessStateToJSON(processState, aRoot, aFullStacks);
return true; return true;
} }
@ -356,28 +358,43 @@ OpenAppend(const string& aFilename)
// Update the extra data file by adding the StackTraces field holding the // Update the extra data file by adding the StackTraces field holding the
// JSON output of this program. // JSON output of this program.
static void static bool
UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aRoot) UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aRoot)
{ {
string extraDataPath(aDumpPath); string extraDataPath(aDumpPath);
int dot = extraDataPath.rfind('.'); int dot = extraDataPath.rfind('.');
if (dot < 0) { if (dot < 0) {
return; // Not a valid dump path return false; // Not a valid dump path
} }
extraDataPath.replace(dot, extraDataPath.length() - dot, kExtraDataExtension); extraDataPath.replace(dot, extraDataPath.length() - dot, kExtraDataExtension);
ofstream* f = OpenAppend(extraDataPath.c_str()); ofstream* f = OpenAppend(extraDataPath.c_str());
bool res = false;
if (f->is_open()) { if (f->is_open()) {
Json::FastWriter writer; Json::FastWriter writer;
*f << "StackTraces=" << writer.write(aRoot); *f << "StackTraces=" << writer.write(aRoot);
res = !f->fail();
f->close(); f->close();
} }
delete f; delete f;
return res;
}
bool
GenerateStacks(const string& aDumpPath, const bool aFullStacks) {
Json::Value root;
if (!ProcessMinidump(root, aDumpPath, aFullStacks)) {
return false;
}
return UpdateExtraDataFile(aDumpPath , root);
} }
} // namespace CrashReporter } // namespace CrashReporter
@ -408,16 +425,9 @@ int main(int argc, char** argv)
{ {
ParseArguments(argc, argv); ParseArguments(argc, argv);
if (!FileExists(gMinidumpPath)) { if (!GenerateStacks(gMinidumpPath, gMinidumpAnalyzerOptions.fullMinidump)) {
// The dump file does not exist
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Try processing the minidump
Json::Value root;
if (ProcessMinidump(root, gMinidumpPath)) {
UpdateExtraDataFile(gMinidumpPath, root);
}
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

View File

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef MINIDUMP_ANALYZER_H__
#define MINIDUMP_ANALYZER_H__
#include <string>
namespace CrashReporter {
bool GenerateStacks(const std::string& aDumpPath, const bool aFullStacks);
}
#endif // MINIDUMP_ANALYZER_H__

View File

@ -7,37 +7,42 @@
if CONFIG['OS_TARGET'] != 'Android': if CONFIG['OS_TARGET'] != 'Android':
Program('minidump-analyzer') Program('minidump-analyzer')
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
UNIFIED_SOURCES += [
'minidump-analyzer.cpp',
]
USE_LIBS += [
'breakpad_processor',
'jsoncpp',
]
LOCAL_INCLUDES += [
'/toolkit/components/jsoncpp/include',
]
if CONFIG['OS_TARGET'] == 'Darwin': if CONFIG['OS_TARGET'] == 'Darwin':
DIST_SUBDIR = 'crashreporter.app/Contents/MacOS' DIST_SUBDIR = 'crashreporter.app/Contents/MacOS'
if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64': if CONFIG['OS_TARGET'] == 'WINNT':
UNIFIED_SOURCES += [ DEFINES['UNICODE'] = True
'MozStackFrameSymbolizer.cpp', DEFINES['_UNICODE'] = True
'Win64ModuleUnwindMetadata.cpp',
if CONFIG['CPU_ARCH'] == 'x86_64':
UNIFIED_SOURCES += [
'MozStackFrameSymbolizer.cpp',
'Win64ModuleUnwindMetadata.cpp',
]
OS_LIBS += [
'Dbghelp',
'Imagehlp'
]
else:
Library('minidump-analyzer')
EXPORTS += [
'minidump-analyzer.h',
] ]
OS_LIBS += [ UNIFIED_SOURCES += [
'Dbghelp', 'minidump-analyzer.cpp',
'Imagehlp' ]
]
USE_LIBS += [
'breakpad_processor',
'jsoncpp',
]
LOCAL_INCLUDES += [
'/toolkit/components/jsoncpp/include',
]
# Don't use the STL wrappers in the crashreporter clients; they don't # Don't use the STL wrappers in the crashreporter clients; they don't
# link with -lmozalloc, and it really doesn't matter here anyway. # link with -lmozalloc, and it really doesn't matter here anyway.