diff --git a/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java b/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java index d8d6ec476b64..f1305cfc5398 100644 --- a/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java +++ b/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java @@ -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(); readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/MinidumpAnalyzer.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/MinidumpAnalyzer.java new file mode 100644 index 000000000000..63498ac33cfd --- /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 true if the operation was successful, + * false otherwise. + */ + public static native boolean GenerateStacks(String minidumpPath, boolean fullStacks); +} diff --git a/mozglue/android/moz.build b/mozglue/android/moz.build index 7c7c8da6fb85..3a07e4850e83 100644 --- a/mozglue/android/moz.build +++ b/mozglue/android/moz.build @@ -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', diff --git a/mozglue/android/nsGeckoUtils.cpp b/mozglue/android/nsGeckoUtils.cpp index 2c1b2304e44e..bb5603f9935b 100644 --- a/mozglue/android/nsGeckoUtils.cpp +++ b/mozglue/android/nsGeckoUtils.cpp @@ -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 diff --git a/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h index e2a8bd826bfa..00f2bf2053e3 100644 --- a/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h +++ b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h @@ -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 diff --git a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp index a68c4e585b43..2702fba6715d 100644 --- a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp +++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp @@ -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 #include #include @@ -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); } diff --git a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.h b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.h new file mode 100644 index 000000000000..189ab409a7ee --- /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 + +namespace CrashReporter { + +bool GenerateStacks(const std::string& aDumpPath, const bool aFullStacks); + +} + +#endif // MINIDUMP_ANALYZER_H__ diff --git a/toolkit/crashreporter/minidump-analyzer/moz.build b/toolkit/crashreporter/minidump-analyzer/moz.build index f40d1c139494..3734de4a09c0 100644 --- a/toolkit/crashreporter/minidump-analyzer/moz.build +++ b/toolkit/crashreporter/minidump-analyzer/moz.build @@ -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.