Bug 1333126: use win64 PE unwind metadata to improve client-side stack walking; r?gsvelto
MozReview-Commit-ID: GDARnPSemyu
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h
@@ -0,0 +1,108 @@
+/* -*- 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 MinidumpAnalyzerUtils_h
+#define MinidumpAnalyzerUtils_h
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#include <vector>
+#include <map>
+#include <string>
+#include <algorithm>
+
+namespace CrashReporter {
+
+struct MinidumpAnalyzerOptions {
+ std::string forceUseModule;
+};
+
+extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
+
+#ifdef XP_WIN
+
+inline std::wstring
+UTF8ToWide(const std::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"";
+ }
+
+ buffer[0] = 0;
+ MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
+ -1, buffer, buffer_size);
+ std::wstring str = buffer;
+ delete [] buffer;
+
+ if (aSuccess) {
+ *aSuccess = true;
+ }
+
+ return str;
+}
+
+inline std::string
+WideToMBCS(const std::wstring &inp) {
+ int buffer_size = WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1,
+ nullptr, 0, NULL, NULL);
+ if (buffer_size == 0)
+ return "";
+
+ std::vector<char> buffer(buffer_size);
+ buffer[0] = 0;
+
+ WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1,
+ buffer.data(), buffer_size, NULL, NULL);
+
+ return buffer.data();
+}
+
+inline std::string UTF8toMBCS(const std::string &inp) {
+ std::wstring wide = UTF8ToWide(inp);
+ std::string ret = WideToMBCS(wide);
+ return ret;
+}
+
+#endif
+
+// Check if a file exists at the specified path
+
+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
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.cpp
@@ -0,0 +1,120 @@
+/* -*- 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/. */
+
+#if XP_WIN && HAVE_64BIT_BUILD
+
+#include "MozStackFrameSymbolizer.h"
+
+#include "MinidumpAnalyzerUtils.h"
+
+#include "processor/cfi_frame_info.h"
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+
+namespace CrashReporter {
+
+ extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
+
+ using google_breakpad::CFIFrameInfo;
+
+MozStackFrameSymbolizer::MozStackFrameSymbolizer() :
+ StackFrameSymbolizer(nullptr, nullptr)
+{
+}
+
+MozStackFrameSymbolizer::SymbolizerResult
+MozStackFrameSymbolizer::FillSourceLineInfo(const CodeModules* modules,
+ const SystemInfo* system_info,
+ StackFrame* stack_frame)
+{
+ SymbolizerResult ret = StackFrameSymbolizer::FillSourceLineInfo(
+ modules, system_info, stack_frame);
+
+ if (ret == kNoError && this->HasImplementation() &&
+ stack_frame->function_name.empty()) {
+ // Breakpad's Stackwalker::InstructionAddressSeemsValid only considers an
+ // address valid if it has associated symbols.
+ //
+ // This makes sense for complete & accurate symbols, but ours may be
+ // incomplete or wrong. Returning a function name tells Breakpad we
+ // recognize this address as code, so it's OK to use in stack scanning.
+ // This function is only called with addresses that land in this module.
+ //
+ // This allows us to fall back to stack scanning in the case where we were
+ // unable to provide CFI.
+ stack_frame->function_name = "<unknown code>";
+ }
+ return ret;
+}
+
+CFIFrameInfo*
+MozStackFrameSymbolizer::FindCFIFrameInfo(const StackFrame* frame)
+{
+ std::string modulePath;
+
+ // For unit testing, support loading a specified module instead of
+ // the real one.
+ bool moduleHasBeenReplaced = false;
+ if (gMinidumpAnalyzerOptions.forceUseModule.size() > 0) {
+ modulePath = gMinidumpAnalyzerOptions.forceUseModule;
+ moduleHasBeenReplaced = true;
+ } else {
+ if (!frame->module) {
+ return nullptr;
+ }
+ modulePath = frame->module->code_file();
+ }
+
+ // Get/create the unwind parser.
+ auto itMod = mModuleMap.find(modulePath);
+ std::shared_ptr<ModuleUnwindParser> unwindParser;
+ if (itMod != mModuleMap.end()) {
+ unwindParser = itMod->second;
+ } else {
+ unwindParser.reset(new ModuleUnwindParser(modulePath));
+ mModuleMap[modulePath] = unwindParser;
+ }
+
+ UnwindCFI cfi;
+ DWORD offsetAddr;
+
+ if (moduleHasBeenReplaced) {
+ // If we are replacing a module, addresses will never line up.
+ // So just act like the 1st entry is correct.
+ offsetAddr = unwindParser->GetAnyOffsetAddr();
+ } else {
+ offsetAddr = frame->instruction - frame->module->base_address();
+ }
+
+ if (!unwindParser->GetCFI(offsetAddr, cfi)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
+
+ static const size_t exprSize = 50;
+ char expr[exprSize];
+ if (cfi.stackSize == 0) {
+ snprintf(expr, exprSize, "$rsp");
+ } else {
+ snprintf(expr, exprSize, "$rsp %d +", cfi.stackSize);
+ }
+ rules->SetCFARule(expr);
+
+ if (cfi.ripOffset == 0) {
+ snprintf(expr, exprSize, ".cfa ^");
+ } else {
+ snprintf(expr, exprSize, ".cfa %d - ^", cfi.ripOffset);
+ }
+ rules->SetRARule(expr);
+
+ return rules.release();
+}
+
+} // namespace CrashReporter
+
+#endif // XP_WIN && HAVE_64BIT_BUILD
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.h
@@ -0,0 +1,48 @@
+/* -*- 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 MozStackFrameSymbolizer_h
+#define MozStackFrameSymbolizer_h
+
+#if XP_WIN && HAVE_64BIT_BUILD
+
+#include "Win64ModuleUnwindMetadata.h"
+
+#include "google_breakpad/processor/stack_frame_symbolizer.h"
+#include "google_breakpad/processor/stack_frame.h"
+
+#include <memory>
+
+namespace CrashReporter {
+
+using google_breakpad::CodeModule;
+using google_breakpad::CodeModules;
+using google_breakpad::SourceLineResolverInterface;
+using google_breakpad::StackFrame;
+using google_breakpad::StackFrameSymbolizer;
+using google_breakpad::SymbolSupplier;
+using google_breakpad::SystemInfo;
+
+class MozStackFrameSymbolizer : public StackFrameSymbolizer {
+ using google_breakpad::StackFrameSymbolizer::SymbolizerResult;
+
+ std::map<std::string, std::shared_ptr<ModuleUnwindParser>> mModuleMap;
+
+public:
+ MozStackFrameSymbolizer();
+
+ virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules,
+ const SystemInfo* system_info,
+ StackFrame* stack_frame);
+
+ virtual class google_breakpad::CFIFrameInfo* FindCFIFrameInfo(
+ const StackFrame* frame);
+};
+
+} // namespace CrashReporter
+
+#endif // XP_WIN && HAVE_64BIT_BUILD
+
+#endif // MozStackFrameSymbolizer_h
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.cpp
@@ -0,0 +1,265 @@
+/* -*- 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/. */
+
+#if XP_WIN && HAVE_64BIT_BUILD
+
+#include "Win64ModuleUnwindMetadata.h"
+
+#include "MinidumpAnalyzerUtils.h"
+
+#include <windows.h>
+#include <winnt.h>
+#include <ImageHlp.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+namespace CrashReporter {
+
+union UnwindCode {
+ struct {
+ uint8_t offset_in_prolog;
+ uint8_t unwind_operation_code : 4;
+ uint8_t operation_info : 4;
+ };
+ USHORT frame_offset;
+};
+
+enum UnwindOperationCodes {
+ UWOP_PUSH_NONVOL = 0, // info == register number
+ UWOP_ALLOC_LARGE = 1, // no info, alloc size in next 2 slots
+ UWOP_ALLOC_SMALL = 2, // info == size of allocation / 8 - 1
+ UWOP_SET_FPREG = 3, // no info, FP = RSP + UNWIND_INFO.FPRegOffset*16
+ UWOP_SAVE_NONVOL = 4, // info == register number, offset in next slot
+ UWOP_SAVE_NONVOL_FAR = 5, // info == register number, offset in next 2 slots
+ UWOP_SAVE_XMM = 6, // Version 1; undocumented
+ UWOP_EPILOG = 6, // Version 2; undocumented
+ UWOP_SAVE_XMM_FAR = 7, // Version 1; undocumented
+ UWOP_SPARE = 7, // Version 2; undocumented
+ UWOP_SAVE_XMM128 = 8, // info == XMM reg number, offset in next slot
+ UWOP_SAVE_XMM128_FAR = 9, // info == XMM reg number, offset in next 2 slots
+ UWOP_PUSH_MACHFRAME = 10 // info == 0: no error-code, 1: error-code
+};
+
+struct UnwindInfo {
+ uint8_t version : 3;
+ uint8_t flags : 5;
+ uint8_t size_of_prolog;
+ uint8_t count_of_codes;
+ uint8_t frame_register : 4;
+ uint8_t frame_offset : 4;
+ UnwindCode unwind_code[1];
+};
+
+ModuleUnwindParser::~ModuleUnwindParser()
+{
+ if (mImg) {
+ ImageUnload(mImg);
+ }
+}
+
+void*
+ModuleUnwindParser::RvaToVa(ULONG aRva)
+{
+ return ImageRvaToVa(
+ mImg->FileHeader, mImg->MappedAddress, aRva, &mImg->LastRvaSection);
+}
+
+ModuleUnwindParser::ModuleUnwindParser(const std::string& aPath)
+ : mPath(aPath)
+{
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ std::string code_file = UTF8toMBCS(aPath);
+
+ mImg = ImageLoad((PSTR)code_file.c_str(), NULL);
+ if (!mImg || !mImg->FileHeader) {
+ return;
+ }
+
+ PIMAGE_OPTIONAL_HEADER64 optional_header = &mImg->FileHeader->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ return;
+ }
+
+ DWORD exception_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
+
+ DWORD exception_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
+
+ auto funcs = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(exception_rva);
+ if (!funcs) {
+ return;
+ }
+
+ for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
+ mUnwindMap[funcs[i].BeginAddress] = &funcs[i];
+ }
+}
+
+bool
+ModuleUnwindParser::GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc,
+ UnwindCFI& aRet)
+{
+ DWORD unwind_rva = aFunc.UnwindInfoAddress;
+ // Holds RVA to all visited IMAGE_RUNTIME_FUNCTION_ENTRY, to avoid
+ // circular references.
+ std::set<DWORD> visited;
+
+ // Follow chained function entries
+ while (unwind_rva & 0x1) {
+ unwind_rva ^= 0x1;
+
+ if (visited.end() != visited.find(unwind_rva)) {
+ return false;
+ }
+ visited.insert(unwind_rva);
+
+ auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(unwind_rva);
+ if (!chained_func) {
+ return false;
+ }
+ unwind_rva = chained_func->UnwindInfoAddress;
+ }
+
+ visited.insert(unwind_rva);
+
+ auto unwind_info = (UnwindInfo*)RvaToVa(unwind_rva);
+ if (!unwind_info) {
+ return false;
+ }
+
+ DWORD stack_size = 8; // minimal stack size is 8 for RIP
+ DWORD rip_offset = 8;
+ do {
+ for (uint8_t c = 0; c < unwind_info->count_of_codes; c++) {
+ UnwindCode* unwind_code = &unwind_info->unwind_code[c];
+ switch (unwind_code->unwind_operation_code) {
+ case UWOP_PUSH_NONVOL: {
+ stack_size += 8;
+ break;
+ }
+ case UWOP_ALLOC_LARGE: {
+ if (unwind_code->operation_info == 0) {
+ c++;
+ if (c < unwind_info->count_of_codes) {
+ stack_size += (unwind_code + 1)->frame_offset * 8;
+ }
+ } else {
+ c += 2;
+ if (c < unwind_info->count_of_codes) {
+ stack_size += (unwind_code + 1)->frame_offset |
+ ((unwind_code + 2)->frame_offset << 16);
+ }
+ }
+ break;
+ }
+ case UWOP_ALLOC_SMALL: {
+ stack_size += unwind_code->operation_info * 8 + 8;
+ break;
+ }
+ case UWOP_SET_FPREG:
+ // To correctly track RSP when it's been transferred to another
+ // register, we would need to emit CFI records for every unwind op.
+ // For simplicity, don't emit CFI records for this function as
+ // we know it will be incorrect after this point.
+ return false;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM: // also v2 UWOP_EPILOG
+ case UWOP_SAVE_XMM128: {
+ c++; // skip slot with offset
+ break;
+ }
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM_FAR: // also v2 UWOP_SPARE
+ case UWOP_SAVE_XMM128_FAR: {
+ c += 2; // skip 2 slots with offset
+ break;
+ }
+ case UWOP_PUSH_MACHFRAME: {
+ if (unwind_code->operation_info) {
+ stack_size += 88;
+ } else {
+ stack_size += 80;
+ }
+ rip_offset += 80;
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+
+ if (unwind_info->flags & UNW_FLAG_CHAININFO) {
+ auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(
+ (unwind_info->unwind_code +
+ ((unwind_info->count_of_codes + 1) & ~1)));
+
+ if (visited.end() != visited.find(chained_func->UnwindInfoAddress)) {
+ return false; // Circular reference
+ }
+
+ visited.insert(chained_func->UnwindInfoAddress);
+
+ unwind_info = (UnwindInfo*)RvaToVa(chained_func->UnwindInfoAddress);
+ } else {
+ unwind_info = nullptr;
+ }
+ } while (unwind_info);
+
+ aRet.beginAddress = aFunc.BeginAddress;
+ aRet.size = aFunc.EndAddress - aFunc.BeginAddress;
+ aRet.stackSize = stack_size;
+ aRet.ripOffset = rip_offset;
+ return true;
+}
+
+// For unit testing we sometimes need any address that's valid in this module.
+// Just return the first address we know of.
+DWORD
+ModuleUnwindParser::GetAnyOffsetAddr() const {
+ if (mUnwindMap.size() < 1) {
+ return 0;
+ }
+ return mUnwindMap.begin()->first;
+}
+
+bool
+ModuleUnwindParser::GetCFI(DWORD aAddress, UnwindCFI& aRet)
+{
+ // Figure out the begin address of the requested address.
+ auto itUW = mUnwindMap.lower_bound(aAddress + 1);
+ if (itUW == mUnwindMap.begin()) {
+ return false; // address before this module.
+ }
+ --itUW;
+
+ // Ensure that the function entry is big enough to contain this address.
+ IMAGE_RUNTIME_FUNCTION_ENTRY& func = *itUW->second;
+ if (aAddress > func.EndAddress) {
+ return false;
+ }
+
+ // Do we have CFI for this function already?
+ auto itCFI = mCFIMap.find(aAddress);
+ if (itCFI != mCFIMap.end()) {
+ aRet = itCFI->second;
+ return true;
+ }
+
+ // No, generate it.
+ if (!GenerateCFIForFunction(func, aRet)) {
+ return false;
+ }
+
+ mCFIMap[func.BeginAddress] = aRet;
+ return true;
+}
+
+} // namespace
+
+#endif // XP_WIN && HAVE_64BIT_BUILD
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.h
@@ -0,0 +1,57 @@
+/* -*- 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 Win64ModuleUnwindMetadata_h
+#define Win64ModuleUnwindMetadata_h
+
+#if XP_WIN && HAVE_64BIT_BUILD
+
+#include <functional>
+#include <map>
+#include <string>
+
+#include <windows.h>
+#include <winnt.h>
+#include <ImageHlp.h>
+
+namespace CrashReporter {
+
+struct UnwindCFI
+{
+ uint32_t beginAddress;
+ uint32_t size;
+ uint32_t stackSize;
+ uint32_t ripOffset;
+};
+
+// Does lazy-parsing of unwind info.
+class ModuleUnwindParser {
+ PLOADED_IMAGE mImg;
+ std::string mPath;
+
+ // Maps begin address to exception record.
+ // Populated upon construction.
+ std::map<DWORD, PIMAGE_RUNTIME_FUNCTION_ENTRY> mUnwindMap;
+
+ // Maps begin address to CFI.
+ // Populated as needed.
+ std::map<DWORD, UnwindCFI> mCFIMap;
+
+ bool GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc,
+ UnwindCFI& aRet);
+ void* RvaToVa(ULONG aRva);
+
+public:
+ explicit ModuleUnwindParser(const std::string& aPath);
+ ~ModuleUnwindParser();
+ bool GetCFI(DWORD aAddress, UnwindCFI& aRet);
+ DWORD GetAnyOffsetAddr() const;
+};
+
+} // namespace
+
+#endif // XP_WIN && HAVE_64BIT_BUILD
+
+#endif // Win64ModuleUnwindMetadata_h
--- a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
+++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp
@@ -1,16 +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/. */
#include <cstdio>
#include <fstream>
#include <string>
+#include <cstring>
#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"
@@ -26,16 +27,22 @@
#elif defined(XP_UNIX) || defined(XP_MACOSX)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
+#include "MinidumpAnalyzerUtils.h"
+
+#if XP_WIN && HAVE_64BIT_BUILD
+#include "MozStackFrameSymbolizer.h"
+#endif
+
namespace CrashReporter {
using std::ios;
using std::ios_base;
using std::hex;
using std::ofstream;
using std::map;
using std::showbase;
@@ -49,54 +56,17 @@ 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 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
+MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
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;
@@ -184,16 +154,17 @@ ConvertStackToJSON(const ProcessState& a
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
@@ -314,31 +285,35 @@ 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) {
+#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
// Process the minidump.
Minidump dump(aDumpFile);
if (!dump.Read()) {
return false;
}
ProcessResult rv;
ProcessState processState;
rv = minidumpProcessor.Process(&dump, &processState);
aRoot["status"] = ResultString(rv);
-
ConvertProcessStateToJSON(processState, aRoot);
return true;
}
// Open the specified file in append mode
static ofstream*
@@ -355,35 +330,16 @@ OpenAppend(const string& aFilename)
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 extra data file by adding the StackTraces field holding the
// JSON output of this program.
static void
UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aRoot)
{
string extraDataPath(aDumpPath);
int dot = extraDataPath.rfind('.');
@@ -422,16 +378,27 @@ int main(int argc, char** argv)
exit(EXIT_FAILURE);
}
if (!FileExists(dumpPath)) {
// The dump file does not exist
return 1;
}
+ // Process command-line arguments
+ for (int i = 2; i < argc; ++i) {
+ if (std::strcmp(argv[i], "--force-use-module") == 0) {
+ if ((++ i) < argc) {
+ gMinidumpAnalyzerOptions.forceUseModule = argv[i];
+ } else {
+ return 1; // The module name wasn't specified on the command line.
+ }
+ }
+ }
+
// Try processing the minidump
Json::Value root;
if (ProcessMinidump(root, dumpPath)) {
UpdateExtraDataFile(dumpPath, root);
}
exit(EXIT_SUCCESS);
}
--- a/toolkit/crashreporter/minidump-analyzer/moz.build
+++ b/toolkit/crashreporter/minidump-analyzer/moz.build
@@ -22,13 +22,25 @@ if CONFIG['OS_TARGET'] != 'Android':
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',
+ ]
+
+ OS_LIBS += [
+ 'Dbghelp',
+ 'Imagehlp'
+ ]
+
+
# 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')