Bug 1307153 - Add stack traces to the crash pings in Fennec; r?jchen,ted.mielczarek
MozReview-Commit-ID: ZJKUwHFsuK
--- a/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
@@ -24,16 +24,17 @@ import java.net.URL;
import java.net.URLDecoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.zip.GZIPOutputStream;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.GeckoProfile;
+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;
import org.mozilla.gecko.util.INISection;
import org.mozilla.gecko.util.ProxySelector;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
@@ -146,17 +147,30 @@ public class CrashReporter extends AppCo
pendingDir.mkdirs();
mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
moveFile(passedMinidumpFile, mPendingMinidumpFile);
File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
+ // Compute the minidump hash and generate the stack traces
computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
+
+ try {
+ System.loadLibrary("mozglue");
+
+ 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);
try {
// Find the profile name and path. Since we don't have any other way of getting it within
// this context we extract it from the crash dump path.
final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
final String profileName = getProfileName(profileDir);
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -249,16 +249,23 @@ mgjar.sources += [geckoview_source_dir +
'mozglue/DirectBufferAllocator.java',
'mozglue/GeckoLoader.java',
'mozglue/JNIObject.java',
'mozglue/NativeReference.java',
'mozglue/NativeZip.java',
'mozglue/SafeIntent.java',
'mozglue/SharedMemory.java',
]]
+
+if CONFIG['MOZ_CRASHREPORTER']:
+ mgjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
+ 'mozglue/MinidumpAnalyzer.java',
+ ]]
+
+
mgjar.generated_sources = [] # Keep it this way.
mgjar.extra_jars += [
CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
'constants.jar',
]
mgjar.javac_flags += ['-Xlint:all']
gujar = add_java_jar('gecko-util')
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/MinidumpAnalyzer.java
@@ -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);
+}
--- a/mozglue/android/moz.build
+++ b/mozglue/android/moz.build
@@ -13,16 +13,25 @@ SOURCES += [
'NativeCrypto.cpp',
'nsGeckoUtils.cpp',
'NSSBridge.cpp',
'pbkdf2_sha256.c',
'SharedMemNatives.cpp',
'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',
'ANDROID_CPU_ARCH'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['MOZ_FOLD_LIBS']:
DEFINES['MOZ_FOLD_LIBS'] = True
--- a/mozglue/android/nsGeckoUtils.cpp
+++ b/mozglue/android/nsGeckoUtils.cpp
@@ -6,16 +6,20 @@
#include <jni.h>
#include <stdlib.h>
#include <fcntl.h>
#include "APKOpen.h"
#include "Zip.h"
#include "mozilla/RefPtr.h"
+#ifdef MOZ_CRASHREPORTER
+# include "minidump-analyzer.h"
+#endif
+
extern "C"
__attribute__ ((visibility("default")))
void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_putenv(JNIEnv *jenv, jclass, jstring map)
{
const char* str;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
@@ -117,8 +121,26 @@ Java_org_mozilla_gecko_mozglue_NativeZip
}
jclass nativeZip = jenv->GetObjectClass(jzip);
jmethodID method = jenv->GetMethodID(nativeZip, "createInputStream", "(Ljava/nio/ByteBuffer;I)Ljava/io/InputStream;");
// Since this function is only expected to be called from Java, it is safe
// to skip exception checking for the method call below, as long as no
// 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
--- a/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h
+++ b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h
@@ -120,30 +120,11 @@ static inline std::string
UTF8toMBCS(const std::string &inp) {
std::wstring wide = UTF8ToWide(inp);
std::string ret = WideToMBCS(wide);
return ret;
}
#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
--- a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
+++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
@@ -1,13 +1,15 @@
/* -*- 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/. */
+#include "minidump-analyzer.h"
+
#include <cstdio>
#include <cstring>
#include <fstream>
#include <string>
#include <sstream>
#include "json/json.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
@@ -227,35 +229,35 @@ ConvertModulesToJSON(const ProcessState&
return mainModuleIndex;
}
// Convert the process state to JSON, this includes information about the
// 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;
// Crash info
Json::Value crashInfo;
int requestingThread = aProcessState.requesting_thread();
if (aProcessState.crashed()) {
crashInfo["type"] = aProcessState.crash_reason();
crashInfo["address"] = ToHex(aProcessState.crash_address());
if (requestingThread != -1) {
// 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);
// Add assertion info, if available
string assertion = aProcessState.assertion();
if (!assertion.empty()) {
crashInfo["assertion"] = assertion;
@@ -273,17 +275,17 @@ ConvertProcessStateToJSON(const ProcessS
}
aRoot["modules"] = modules;
// Threads
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);
const CallStack* rawStack = aProcessState.threads()->at(requestingThread);
ConvertStackToJSON(aProcessState, orderedModules, rawStack, stack);
thread["frames"] = stack;
threads.append(thread);
@@ -301,17 +303,17 @@ ConvertProcessStateToJSON(const ProcessS
aRoot["threads"] = threads;
}
// Process the minidump file and append the JSON-formatted stack traces to
// 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);
#else
BasicSourceLineResolver resolver;
// We don't have a valid symbol resolver so we pass nullptr instead.
MinidumpProcessor minidumpProcessor(nullptr, &resolver);
#endif
@@ -322,17 +324,17 @@ ProcessMinidump(Json::Value& aRoot, cons
return false;
}
ProcessResult rv;
ProcessState processState;
rv = minidumpProcessor.Process(&dump, &processState);
aRoot["status"] = ResultString(rv);
- ConvertProcessStateToJSON(processState, aRoot);
+ ConvertProcessStateToJSON(processState, aRoot, aFullStacks);
return true;
}
// Open the specified file in append mode
static ofstream*
OpenAppend(const string& aFilename)
@@ -351,38 +353,53 @@ OpenAppend(const string& aFilename)
ofstream* file = new ofstream(aFilename.c_str(), mode);
#endif // XP_WIN
return file;
}
// 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
using namespace CrashReporter;
static void
ParseArguments(int argc, char** argv) {
@@ -403,21 +420,14 @@ ParseArguments(int argc, char** argv) {
gMinidumpPath = argv[argc - 1];
}
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);
}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.h
@@ -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__
--- a/toolkit/crashreporter/minidump-analyzer/moz.build
+++ b/toolkit/crashreporter/minidump-analyzer/moz.build
@@ -2,45 +2,50 @@
# vim: set filetype=python:
# 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/.
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.
DisableStlWrapping()
include('/toolkit/crashreporter/crashreporter.mozbuild')