mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1280477 - Write the stack traces extracted from a crash dump into the crash.main event file r=ted
This commit is contained in:
parent
da2f825015
commit
54fdfadaf7
@ -492,6 +492,17 @@ bool CheckEndOfLifed(string version)
|
||||
return UIFileExists(reportPath);
|
||||
}
|
||||
|
||||
static string
|
||||
GetMinidumpAnalyzerPath()
|
||||
{
|
||||
string path = gArgv[0];
|
||||
size_t pos = path.rfind(UI_CRASH_REPORTER_FILENAME BIN_SUFFIX);
|
||||
path.erase(pos);
|
||||
path.append(UI_MINIDUMP_ANALYZER_FILENAME BIN_SUFFIX);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
gArgc = argc;
|
||||
@ -513,6 +524,10 @@ int main(int argc, char** argv)
|
||||
// no dump file specified, run the default UI
|
||||
UIShowDefaultUI();
|
||||
} else {
|
||||
// start by running minidump analyzer
|
||||
UIRunMinidumpAnalyzer(GetMinidumpAnalyzerPath(), gReporterDumpFile);
|
||||
|
||||
// go ahead with the crash reporter
|
||||
gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
|
||||
if (gExtraFile.empty()) {
|
||||
UIError(gStrings[ST_ERROR_BADARGUMENTS]);
|
||||
|
@ -37,6 +37,9 @@ std::string WideToUTF8(const std::wstring& wide, bool* success = 0);
|
||||
|
||||
#endif
|
||||
|
||||
#define UI_CRASH_REPORTER_FILENAME "crashreporter"
|
||||
#define UI_MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
|
||||
|
||||
typedef std::map<std::string, std::string> StringTable;
|
||||
|
||||
#define ST_CRASHREPORTERTITLE "CrashReporterTitle"
|
||||
@ -145,6 +148,8 @@ std::ofstream* UIOpenWrite(const std::string& filename,
|
||||
bool append=false,
|
||||
bool binary=false);
|
||||
void UIPruneSavedDumps(const std::string& directory);
|
||||
void UIRunMinidumpAnalyzer(const std::string& exename,
|
||||
const std::string& filename);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning( pop )
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
#include "crashreporter.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace CrashReporter;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
@ -67,3 +69,17 @@ void UIPruneSavedDumps(const std::string& directory)
|
||||
dumpfiles.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void UIRunMinidumpAnalyzer(const string& exename, const string& filename)
|
||||
{
|
||||
// Run the minidump analyzer and wait for it to finish
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
return; // Nothing to do upon failure
|
||||
} else if (pid == 0) {
|
||||
execl(exename.c_str(), exename.c_str(), filename.c_str(), nullptr);
|
||||
} else {
|
||||
waitpid(pid, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1544,3 +1544,25 @@ void UIPruneSavedDumps(const std::string& directory)
|
||||
dumpfiles.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void UIRunMinidumpAnalyzer(const string& exename, const string& filename)
|
||||
{
|
||||
wstring cmdLine;
|
||||
|
||||
cmdLine += L"\"" + UTF8ToWide(exename) + L"\" ";
|
||||
cmdLine += L"\"" + UTF8ToWide(filename) + L"\" ";
|
||||
|
||||
STARTUPINFO si = {};
|
||||
PROCESS_INFORMATION pi = {};
|
||||
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_SHOWNORMAL;
|
||||
|
||||
if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
|
||||
0, nullptr, nullptr, &si, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'SunOS':
|
||||
'/toolkit/themes/windows/global/throbber/Throbber-small.gif',
|
||||
]
|
||||
|
||||
DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
|
||||
|
||||
RCINCLUDE = 'crashreporter.rc'
|
||||
|
||||
# Don't use the STL wrappers in the crashreporter clients; they don't
|
||||
|
@ -32,6 +32,11 @@ Crash Reporter Client
|
||||
to handle dump files. This application optionally submits crashes to
|
||||
Mozilla (or the configured server).
|
||||
|
||||
Minidump Analyzer
|
||||
The minidump analyzer is a standalone executable that is launched by the
|
||||
crash reporter client or by the browser itself to extract stack traces from
|
||||
the dump files generated during a crash.
|
||||
|
||||
How Main-Process Crash Handling Works
|
||||
=====================================
|
||||
|
||||
@ -83,8 +88,11 @@ argument.
|
||||
|
||||
The *crash reporter client* performs a number of roles. There's a lot going
|
||||
on, so you may want to look at ``main()`` in ``crashreporter.cpp``. First,
|
||||
it verifies the dump data is sane. If it isn't (e.g. required metadata is
|
||||
missing), the dump data is ignored. If dump data looks sane, the dump data
|
||||
stack traces are extracted from the dump via the *minidump analyzer* tool.
|
||||
The resulting traces are appended to the main crash event file. Then, the
|
||||
*crash reporter client* verifies that the dump data is sane. If it isn't
|
||||
(e.g. required metadata is missing), the dump data is ignored. If dump data
|
||||
looks sane, the dump data
|
||||
is moved into the *pending* directory for the configured data directory
|
||||
(defined via the ``MOZ_CRASHREPORTER_DATA_DIRECTORY`` environment variable
|
||||
or from the UI). Once this is done, the main crash reporter UI is displayed
|
||||
|
516
toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
Normal file
516
toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
Normal file
@ -0,0 +1,516 @@
|
||||
/* -*- 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 <cstdio>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "json/json.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "google_breakpad/processor/minidump_processor.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/pathname_stripper.h"
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define DIR_SEPARATOR "\\"
|
||||
|
||||
#elif defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DIR_SEPARATOR "/"
|
||||
|
||||
#endif
|
||||
|
||||
namespace CrashReporter {
|
||||
|
||||
using std::ios;
|
||||
using std::ios_base;
|
||||
using std::hex;
|
||||
using std::ofstream;
|
||||
using std::map;
|
||||
using std::showbase;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::wstring;
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::CodeModules;
|
||||
using google_breakpad::Minidump;
|
||||
using google_breakpad::MinidumpProcessor;
|
||||
using google_breakpad::PathnameStripper;
|
||||
using google_breakpad::ProcessResult;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::StackFrame;
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
static string
|
||||
WideToUTF8(const wstring& aWideStr, bool* aSuccess = nullptr)
|
||||
{
|
||||
char* buffer = nullptr;
|
||||
int buffer_size = WideCharToMultiByte(CP_UTF8, 0, aWideStr.c_str(),
|
||||
-1, nullptr, 0, nullptr, nullptr);
|
||||
if (buffer_size == 0) {
|
||||
if (aSuccess) {
|
||||
*aSuccess = false;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
buffer = new char[buffer_size];
|
||||
|
||||
if (buffer == nullptr) {
|
||||
if (aSuccess) {
|
||||
*aSuccess = false;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
WideCharToMultiByte(CP_UTF8, 0, aWideStr.c_str(),
|
||||
-1, buffer, buffer_size, nullptr, nullptr);
|
||||
string mb = buffer;
|
||||
delete [] buffer;
|
||||
|
||||
if (aSuccess) {
|
||||
*aSuccess = true;
|
||||
}
|
||||
|
||||
return mb;
|
||||
}
|
||||
|
||||
static wstring UTF8ToWide(const string& aUtf8Str, bool *aSuccess = nullptr)
|
||||
{
|
||||
wchar_t* buffer = nullptr;
|
||||
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
||||
-1, nullptr, 0);
|
||||
if (buffer_size == 0) {
|
||||
if (aSuccess) {
|
||||
*aSuccess = false;
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
buffer = new wchar_t[buffer_size];
|
||||
|
||||
if (buffer == nullptr) {
|
||||
if (aSuccess) {
|
||||
*aSuccess = false;
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
||||
-1, buffer, buffer_size);
|
||||
wstring str = buffer;
|
||||
delete [] buffer;
|
||||
|
||||
if (aSuccess) {
|
||||
*aSuccess = true;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static string gReporterDumpFile;
|
||||
|
||||
struct ModuleCompare {
|
||||
bool operator() (const CodeModule* aLhs, const CodeModule* aRhs) const {
|
||||
return aLhs->base_address() < aRhs->base_address();
|
||||
}
|
||||
};
|
||||
|
||||
typedef map<const CodeModule*, unsigned int, ModuleCompare> OrderedModulesMap;
|
||||
|
||||
static string
|
||||
ToHex(uint64_t aValue) {
|
||||
stringstream output;
|
||||
|
||||
output << hex << showbase << aValue;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
// Convert the stack frame trust value into a readable string.
|
||||
|
||||
static string
|
||||
FrameTrust(const StackFrame::FrameTrust aTrust) {
|
||||
switch (aTrust) {
|
||||
case StackFrame::FRAME_TRUST_NONE:
|
||||
return "none";
|
||||
case StackFrame::FRAME_TRUST_SCAN:
|
||||
return "scan";
|
||||
case StackFrame::FRAME_TRUST_CFI_SCAN:
|
||||
return "cfi_scan";
|
||||
case StackFrame::FRAME_TRUST_FP:
|
||||
return "frame_pointer";
|
||||
case StackFrame::FRAME_TRUST_CFI:
|
||||
return "cfi";
|
||||
case StackFrame::FRAME_TRUST_PREWALKED:
|
||||
return "prewalked";
|
||||
case StackFrame::FRAME_TRUST_CONTEXT:
|
||||
return "context";
|
||||
}
|
||||
|
||||
return "none";
|
||||
}
|
||||
|
||||
// Convert the result value of the minidump processing step into a readable
|
||||
// string.
|
||||
|
||||
static string
|
||||
ResultString(ProcessResult aResult) {
|
||||
switch (aResult) {
|
||||
case google_breakpad::PROCESS_OK:
|
||||
return "OK";
|
||||
case google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND:
|
||||
return "ERROR_MINIDUMP_NOT_FOUND";
|
||||
case google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER:
|
||||
return "ERROR_NO_MINIDUMP_HEADER";
|
||||
case google_breakpad::PROCESS_ERROR_NO_THREAD_LIST:
|
||||
return "ERROR_NO_THREAD_LIST";
|
||||
case google_breakpad::PROCESS_ERROR_GETTING_THREAD:
|
||||
return "ERROR_GETTING_THREAD";
|
||||
case google_breakpad::PROCESS_ERROR_GETTING_THREAD_ID:
|
||||
return "ERROR_GETTING_THREAD_ID";
|
||||
case google_breakpad::PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS:
|
||||
return "ERROR_DUPLICATE_REQUESTING_THREADS";
|
||||
case google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED:
|
||||
return "SYMBOL_SUPPLIER_INTERRUPTED";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the list of stack frames to JSON and append them to the array
|
||||
// specified in the |aNode| parameter.
|
||||
|
||||
static void
|
||||
ConvertStackToJSON(const ProcessState& aProcessState,
|
||||
const OrderedModulesMap& aOrderedModules,
|
||||
const CallStack *aStack,
|
||||
Json::Value& aNode)
|
||||
{
|
||||
int frameCount = aStack->frames()->size();
|
||||
unsigned int moduleIndex = 0;
|
||||
|
||||
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
|
||||
const StackFrame *frame = aStack->frames()->at(frameIndex);
|
||||
Json::Value frameNode;
|
||||
|
||||
if (frame->module) {
|
||||
auto itr = aOrderedModules.find(frame->module);
|
||||
|
||||
if (itr != aOrderedModules.end()) {
|
||||
moduleIndex = (*itr).second;
|
||||
frameNode["module_index"] = moduleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
frameNode["trust"] = FrameTrust(frame->trust);
|
||||
// The 'ip' field is equivalent to socorro's 'offset' field
|
||||
frameNode["ip"] = ToHex(frame->instruction);
|
||||
|
||||
aNode.append(frameNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the list of modules to JSON and append them to the array specified
|
||||
// in the |aNode| parameter.
|
||||
|
||||
static int
|
||||
ConvertModulesToJSON(const ProcessState& aProcessState,
|
||||
OrderedModulesMap& aOrderedModules,
|
||||
Json::Value& aNode)
|
||||
{
|
||||
const CodeModules* modules = aProcessState.modules();
|
||||
|
||||
if (!modules) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create a sorted set of modules so that we'll be able to lookup the index
|
||||
// of a particular module.
|
||||
for (unsigned int i = 0; i < modules->module_count(); ++i) {
|
||||
aOrderedModules.insert(
|
||||
std::pair<const CodeModule*, unsigned int>(
|
||||
modules->GetModuleAtSequence(i), i
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
uint64_t mainAddress = 0;
|
||||
const CodeModule *mainModule = modules->GetMainModule();
|
||||
|
||||
if (mainModule) {
|
||||
mainAddress = mainModule->base_address();
|
||||
}
|
||||
|
||||
unsigned int moduleCount = modules->module_count();
|
||||
int mainModuleIndex = -1;
|
||||
|
||||
for (unsigned int moduleSequence = 0;
|
||||
moduleSequence < moduleCount;
|
||||
++moduleSequence)
|
||||
{
|
||||
const CodeModule *module = modules->GetModuleAtSequence(moduleSequence);
|
||||
|
||||
if (module->base_address() == mainAddress) {
|
||||
mainModuleIndex = moduleSequence;
|
||||
}
|
||||
|
||||
Json::Value moduleNode;
|
||||
moduleNode["filename"] = PathnameStripper::File(module->code_file());
|
||||
moduleNode["code_id"] = PathnameStripper::File(module->code_identifier());
|
||||
moduleNode["version"] = module->version();
|
||||
moduleNode["debug_file"] = PathnameStripper::File(module->debug_file());
|
||||
moduleNode["debug_id"] = module->debug_identifier();
|
||||
moduleNode["base_addr"] = ToHex(module->base_address());
|
||||
moduleNode["end_addr"] = ToHex(module->base_address() + module->size());
|
||||
|
||||
aNode.append(moduleNode);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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) {
|
||||
crashInfo["crashing_thread"] = requestingThread;
|
||||
}
|
||||
} else {
|
||||
crashInfo["type"] = Json::Value(Json::nullValue);
|
||||
// Add assertion info, if available
|
||||
string assertion = aProcessState.assertion();
|
||||
|
||||
if (!assertion.empty()) {
|
||||
crashInfo["assertion"] = assertion;
|
||||
}
|
||||
}
|
||||
|
||||
aRoot["crash_info"] = crashInfo;
|
||||
|
||||
// Modules
|
||||
Json::Value modules(Json::arrayValue);
|
||||
int mainModule = ConvertModulesToJSON(aProcessState, orderedModules, modules);
|
||||
|
||||
if (mainModule != -1) {
|
||||
aRoot["main_module"] = mainModule;
|
||||
}
|
||||
|
||||
aRoot["modules"] = modules;
|
||||
|
||||
// Threads
|
||||
Json::Value threads(Json::arrayValue);
|
||||
int threadCount = aProcessState.threads()->size();
|
||||
|
||||
for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
|
||||
Json::Value thread;
|
||||
Json::Value stack(Json::arrayValue);
|
||||
const CallStack* rawStack = aProcessState.threads()->at(threadIndex);
|
||||
|
||||
ConvertStackToJSON(aProcessState, orderedModules, rawStack, stack);
|
||||
thread["frames"] = stack;
|
||||
threads.append(thread);
|
||||
}
|
||||
|
||||
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) {
|
||||
BasicSourceLineResolver resolver;
|
||||
// We don't have a valid symbol resolver so we pass nullptr instead.
|
||||
MinidumpProcessor minidumpProcessor(nullptr, &resolver);
|
||||
|
||||
// Process the minidump.
|
||||
Minidump dump(gReporterDumpFile);
|
||||
if (!dump.Read()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessResult rv;
|
||||
ProcessState processState;
|
||||
rv = minidumpProcessor.Process(&dump, &processState);
|
||||
aRoot["status"] = ResultString(rv);
|
||||
|
||||
ConvertProcessStateToJSON(processState, aRoot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string
|
||||
Basename(const string& file)
|
||||
{
|
||||
string::size_type slashIndex = file.rfind(DIR_SEPARATOR);
|
||||
if (slashIndex != string::npos)
|
||||
return file.substr(slashIndex + 1);
|
||||
else
|
||||
return file;
|
||||
}
|
||||
|
||||
// Return the ID of the crash report being processed
|
||||
|
||||
static string
|
||||
GetDumpLocalID()
|
||||
{
|
||||
string localId = Basename(gReporterDumpFile);
|
||||
string::size_type dot = localId.rfind('.');
|
||||
|
||||
if (dot == string::npos)
|
||||
return "";
|
||||
|
||||
return localId.substr(0, dot);
|
||||
}
|
||||
|
||||
// Open the specified file in append mode
|
||||
|
||||
static ofstream*
|
||||
OpenAppend(const string& aFilename)
|
||||
{
|
||||
ios_base::openmode mode = ios::out | ios::app;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#if defined(_MSC_VER)
|
||||
ofstream* file = new ofstream();
|
||||
file->open(UTF8ToWide(aFilename).c_str(), mode);
|
||||
#else // GCC
|
||||
ofstream* file =
|
||||
new ofstream(WideToMBCP(UTF8ToWide(aFilename), CP_ACP).c_str(), mode);
|
||||
#endif // _MSC_VER
|
||||
#else // Non-Windows
|
||||
ofstream* file = new ofstream(aFilename.c_str(), mode);
|
||||
#endif // XP_WIN
|
||||
return file;
|
||||
}
|
||||
|
||||
// Check if a file exists at the specified path
|
||||
|
||||
static bool
|
||||
FileExists(const 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
|
||||
}
|
||||
|
||||
// Update the crash event file by adding the StackTraces field holding the
|
||||
// JSON output of this program.
|
||||
|
||||
static void
|
||||
UpdateCrashEvent(string& aEventsPath, const Json::Value& aRoot)
|
||||
{
|
||||
if (aEventsPath.empty()) {
|
||||
// If there is no path to find the crash event in, skip it.
|
||||
return;
|
||||
}
|
||||
|
||||
string localId = GetDumpLocalID();
|
||||
string fpath = aEventsPath + DIR_SEPARATOR + localId;
|
||||
ofstream* f = OpenAppend(fpath.c_str());
|
||||
|
||||
if (f->is_open()) {
|
||||
Json::FastWriter writer;
|
||||
|
||||
*f << "StackTraces=" << writer.write(aRoot);
|
||||
|
||||
f->close();
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
} // namespace CrashReporter
|
||||
|
||||
using namespace CrashReporter;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1) {
|
||||
gReporterDumpFile = argv[1];
|
||||
}
|
||||
|
||||
if (gReporterDumpFile.empty()) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
string eventsPath;
|
||||
#ifdef XP_WIN32
|
||||
static const wchar_t kEventsDirKey[] = L"MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
|
||||
const wchar_t *eventsPathEnv = _wgetenv(kEventsDirKey);
|
||||
if (eventsPathEnv && *eventsPathEnv) {
|
||||
eventsPath = WideToUTF8(eventsPathEnv);
|
||||
}
|
||||
#else
|
||||
static const char kEventsDirKey[] = "MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
|
||||
const char *eventsPathEnv = getenv(kEventsDirKey);
|
||||
if (eventsPathEnv && *eventsPathEnv) {
|
||||
eventsPath = eventsPathEnv;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
eventsPath.clear();
|
||||
}
|
||||
|
||||
if (!FileExists(gReporterDumpFile)) {
|
||||
// The dump file does not exist
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Try processing the minidump
|
||||
Json::Value root;
|
||||
if (ProcessMinidump(root)) {
|
||||
UpdateCrashEvent(eventsPath, root);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
34
toolkit/crashreporter/minidump-analyzer/moz.build
Normal file
34
toolkit/crashreporter/minidump-analyzer/moz.build
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/crashreporter/jsoncpp/include',
|
||||
]
|
||||
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Darwin':
|
||||
DIST_SUBDIR = 'crashreporter.app/Contents/MacOS'
|
||||
|
||||
# Don't use the STL wrappers in the crashreporter clients; they don't
|
||||
# link with -lmozalloc, and it really doesn't matter here anyway.
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
||||
include('/toolkit/crashreporter/crashreporter.mozbuild')
|
@ -56,6 +56,7 @@ DIRS += [
|
||||
if CONFIG['NIGHTLY_BUILD']:
|
||||
DIRS += [
|
||||
'jsoncpp/src/lib_json',
|
||||
'minidump-analyzer',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
|
||||
|
Loading…
Reference in New Issue
Block a user