зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1333126: use win64 PE unwind metadata to improve client-side stack walking; r=gsvelto
MozReview-Commit-ID: GDARnPSemyu --HG-- extra : rebase_source : 2b727cd57c5bddbc2959fa096dea102eeff7d41f
This commit is contained in:
Родитель
fb75aa4149
Коммит
03ac54b366
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "json/json.h"
|
||||
|
@ -31,6 +32,12 @@
|
|||
|
||||
#endif
|
||||
|
||||
#include "MinidumpAnalyzerUtils.h"
|
||||
|
||||
#if XP_WIN && HAVE_64BIT_BUILD
|
||||
#include "MozStackFrameSymbolizer.h"
|
||||
#endif
|
||||
|
||||
namespace CrashReporter {
|
||||
|
||||
using std::ios;
|
||||
|
@ -54,44 +61,7 @@ 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 {
|
||||
|
@ -189,6 +159,7 @@ ConvertStackToJSON(const ProcessState& aProcessState,
|
|||
}
|
||||
|
||||
frameNode["trust"] = FrameTrust(frame->trust);
|
||||
|
||||
// The 'ip' field is equivalent to socorro's 'offset' field
|
||||
frameNode["ip"] = ToHex(frame->instruction);
|
||||
|
||||
|
@ -319,9 +290,14 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& 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);
|
||||
|
@ -333,7 +309,6 @@ ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) {
|
|||
ProcessState processState;
|
||||
rv = minidumpProcessor.Process(&dump, &processState);
|
||||
aRoot["status"] = ResultString(rv);
|
||||
|
||||
ConvertProcessStateToJSON(processState, aRoot);
|
||||
|
||||
return true;
|
||||
|
@ -360,25 +335,6 @@ OpenAppend(const string& aFilename)
|
|||
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.
|
||||
|
||||
|
@ -427,6 +383,17 @@ int main(int argc, char** argv)
|
|||
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)) {
|
||||
|
|
|
@ -27,6 +27,18 @@ if CONFIG['OS_TARGET'] != 'Android':
|
|||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче