mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
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:
parent
9abec85212
commit
f167c2f3e8
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -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',
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
17
toolkit/crashreporter/minidump-analyzer/minidump-analyzer.h
Normal file
17
toolkit/crashreporter/minidump-analyzer/minidump-analyzer.h
Normal 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__
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user