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.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.TelemetryDispatcher;
import org.mozilla.gecko.util.INIParser;
@ -153,7 +155,20 @@ public class CrashReporter extends AppCompatActivity
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
// Compute the minidump hash and generate the stack traces
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>();
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',
]
if CONFIG['MOZ_CRASHREPORTER']:
USE_LIBS += [
'minidump-analyzer',
]
LOCAL_INCLUDES += [
'/toolkit/crashreporter/minidump-analyzer',
]
FINAL_LIBRARY = 'mozglue'
for var in ('ANDROID_PACKAGE_NAME',

View File

@ -11,6 +11,10 @@
#include "Zip.h"
#include "mozilla/RefPtr.h"
#ifdef MOZ_CRASHREPORTER
# include "minidump-analyzer.h"
#endif
extern "C"
__attribute__ ((visibility("default")))
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.
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
// Check if a file exists at the specified path
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
} // namespace CrashReporter
#endif // MinidumpAnalyzerUtils_h

View File

@ -3,6 +3,8 @@
* 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 "minidump-analyzer.h"
#include <cstdio>
#include <cstring>
#include <fstream>
@ -232,7 +234,8 @@ ConvertModulesToJSON(const ProcessState& aProcessState,
// crash, the module list and stack traces for every thread
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
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
// and all threads' stacks are present, otherwise only the crashing
// thread stack is written out and this field is set to 0.
crashInfo["crashing_thread"] =
gMinidumpAnalyzerOptions.fullMinidump ? requestingThread : 0;
crashInfo["crashing_thread"] = aFullStacks ? requestingThread : 0;
}
} else {
crashInfo["type"] = Json::Value(Json::nullValue);
@ -278,7 +280,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
Json::Value threads(Json::arrayValue);
int threadCount = aProcessState.threads()->size();
if (!gMinidumpAnalyzerOptions.fullMinidump && (requestingThread != -1)) {
if (!aFullStacks && (requestingThread != -1)) {
// Only add the crashing thread
Json::Value thread;
Json::Value stack(Json::arrayValue);
@ -306,7 +308,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
// the node specified in |aRoot|
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
MozStackFrameSymbolizer symbolizer;
MinidumpProcessor minidumpProcessor(&symbolizer, false);
@ -327,7 +329,7 @@ ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) {
rv = minidumpProcessor.Process(&dump, &processState);
aRoot["status"] = ResultString(rv);
ConvertProcessStateToJSON(processState, aRoot);
ConvertProcessStateToJSON(processState, aRoot, aFullStacks);
return true;
}
@ -356,28 +358,43 @@ OpenAppend(const string& aFilename)
// Update the extra data file by adding the StackTraces field holding the
// JSON output of this program.
static void
static bool
UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aRoot)
{
string extraDataPath(aDumpPath);
int dot = extraDataPath.rfind('.');
if (dot < 0) {
return; // Not a valid dump path
return false; // Not a valid dump path
}
extraDataPath.replace(dot, extraDataPath.length() - dot, kExtraDataExtension);
ofstream* f = OpenAppend(extraDataPath.c_str());
bool res = false;
if (f->is_open()) {
Json::FastWriter writer;
*f << "StackTraces=" << writer.write(aRoot);
res = !f->fail();
f->close();
}
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
@ -408,16 +425,9 @@ int main(int argc, char** argv)
{
ParseArguments(argc, argv);
if (!FileExists(gMinidumpPath)) {
// The dump file does not exist
if (!GenerateStacks(gMinidumpPath, gMinidumpAnalyzerOptions.fullMinidump)) {
exit(EXIT_FAILURE);
}
// Try processing the minidump
Json::Value root;
if (ProcessMinidump(root, gMinidumpPath)) {
UpdateExtraDataFile(gMinidumpPath, root);
}
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':
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':
DIST_SUBDIR = 'crashreporter.app/Contents/MacOS'
if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64':
UNIFIED_SOURCES += [
'MozStackFrameSymbolizer.cpp',
'Win64ModuleUnwindMetadata.cpp',
if CONFIG['OS_TARGET'] == 'WINNT':
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
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 += [
'Dbghelp',
'Imagehlp'
]
UNIFIED_SOURCES += [
'minidump-analyzer.cpp',
]
USE_LIBS += [
'breakpad_processor',
'jsoncpp',
]
LOCAL_INCLUDES += [
'/toolkit/components/jsoncpp/include',
]
# Don't use the STL wrappers in the crashreporter clients; they don't
# link with -lmozalloc, and it really doesn't matter here anyway.