mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 17:25:27 +00:00
issue 170 - Report assertion type in minidump_stackwalk output. r=mark at http://breakpad.appspot.com/45001
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@433 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
096992fac7
commit
0314e487e4
|
@ -639,6 +639,46 @@ class MinidumpException : public MinidumpStream {
|
||||||
MinidumpContext* context_;
|
MinidumpContext* context_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// MinidumpAssertion wraps MDRawAssertionInfo, which contains information
|
||||||
|
// about an assertion that caused the minidump to be generated.
|
||||||
|
class MinidumpAssertion : public MinidumpStream {
|
||||||
|
public:
|
||||||
|
virtual ~MinidumpAssertion();
|
||||||
|
|
||||||
|
const MDRawAssertionInfo* assertion() const {
|
||||||
|
return valid_ ? &assertion_ : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
string expression() const {
|
||||||
|
return valid_ ? expression_ : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
string function() const {
|
||||||
|
return valid_ ? function_ : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
string file() const {
|
||||||
|
return valid_ ? file_ : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a human-readable representation of the object to stdout.
|
||||||
|
void Print();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Minidump;
|
||||||
|
|
||||||
|
static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM;
|
||||||
|
|
||||||
|
explicit MinidumpAssertion(Minidump* minidump);
|
||||||
|
|
||||||
|
bool Read(u_int32_t expected_size);
|
||||||
|
|
||||||
|
MDRawAssertionInfo assertion_;
|
||||||
|
string expression_;
|
||||||
|
string function_;
|
||||||
|
string file_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// MinidumpSystemInfo wraps MDRawSystemInfo and provides information about
|
// MinidumpSystemInfo wraps MDRawSystemInfo and provides information about
|
||||||
// the system on which the minidump was generated. See also MinidumpMiscInfo.
|
// the system on which the minidump was generated. See also MinidumpMiscInfo.
|
||||||
|
@ -788,6 +828,7 @@ class Minidump {
|
||||||
MinidumpModuleList* GetModuleList();
|
MinidumpModuleList* GetModuleList();
|
||||||
MinidumpMemoryList* GetMemoryList();
|
MinidumpMemoryList* GetMemoryList();
|
||||||
MinidumpException* GetException();
|
MinidumpException* GetException();
|
||||||
|
MinidumpAssertion* GetAssertion();
|
||||||
MinidumpSystemInfo* GetSystemInfo();
|
MinidumpSystemInfo* GetSystemInfo();
|
||||||
MinidumpMiscInfo* GetMiscInfo();
|
MinidumpMiscInfo* GetMiscInfo();
|
||||||
MinidumpBreakpadInfo* GetBreakpadInfo();
|
MinidumpBreakpadInfo* GetBreakpadInfo();
|
||||||
|
|
|
@ -141,6 +141,11 @@ class MinidumpProcessor {
|
||||||
return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
|
return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a textual representation of an assertion included
|
||||||
|
// in the minidump. Returns an empty string if this information
|
||||||
|
// does not exist or cannot be determined.
|
||||||
|
static string GetAssertion(Minidump *dump);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SymbolSupplier *supplier_;
|
SymbolSupplier *supplier_;
|
||||||
SourceLineResolverInterface *resolver_;
|
SourceLineResolverInterface *resolver_;
|
||||||
|
|
|
@ -61,6 +61,7 @@ class ProcessState {
|
||||||
bool crashed() const { return crashed_; }
|
bool crashed() const { return crashed_; }
|
||||||
string crash_reason() const { return crash_reason_; }
|
string crash_reason() const { return crash_reason_; }
|
||||||
u_int64_t crash_address() const { return crash_address_; }
|
u_int64_t crash_address() const { return crash_address_; }
|
||||||
|
string assertion() const { return assertion_; }
|
||||||
int requesting_thread() const { return requesting_thread_; }
|
int requesting_thread() const { return requesting_thread_; }
|
||||||
const vector<CallStack*>* threads() const { return &threads_; }
|
const vector<CallStack*>* threads() const { return &threads_; }
|
||||||
const vector<MinidumpMemoryRegion*>* thread_memory_regions() const {
|
const vector<MinidumpMemoryRegion*>* thread_memory_regions() const {
|
||||||
|
@ -92,6 +93,11 @@ class ProcessState {
|
||||||
// this will be the address of the instruction that caused the fault.
|
// this will be the address of the instruction that caused the fault.
|
||||||
u_int64_t crash_address_;
|
u_int64_t crash_address_;
|
||||||
|
|
||||||
|
// If there was an assertion that was hit, a textual representation
|
||||||
|
// of that assertion, possibly including the file and line at which
|
||||||
|
// it occurred.
|
||||||
|
string assertion_;
|
||||||
|
|
||||||
// The index of the thread that requested a dump be written in the
|
// The index of the thread that requested a dump be written in the
|
||||||
// threads vector. If a dump was produced as a result of a crash, this
|
// threads vector. If a dump was produced as a result of a crash, this
|
||||||
// will point to the thread that crashed. If the dump was produced as
|
// will point to the thread that crashed. If the dump was produced as
|
||||||
|
|
|
@ -243,6 +243,15 @@ static string* UTF16ToUTF8(const vector<u_int16_t>& in,
|
||||||
return out.release();
|
return out.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the smaller of the number of code units in the UTF-16 string,
|
||||||
|
// not including the terminating null word, or maxlen.
|
||||||
|
static size_t UTF16codeunits(const u_int16_t *string, size_t maxlen) {
|
||||||
|
size_t count = 0;
|
||||||
|
while (count < maxlen && string[count] != 0)
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MinidumpObject
|
// MinidumpObject
|
||||||
|
@ -2768,6 +2777,109 @@ void MinidumpException::Print() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MinidumpAssertion
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
MinidumpAssertion::MinidumpAssertion(Minidump* minidump)
|
||||||
|
: MinidumpStream(minidump),
|
||||||
|
assertion_(),
|
||||||
|
expression_(),
|
||||||
|
function_(),
|
||||||
|
file_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MinidumpAssertion::~MinidumpAssertion() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MinidumpAssertion::Read(u_int32_t expected_size) {
|
||||||
|
// Invalidate cached data.
|
||||||
|
valid_ = false;
|
||||||
|
|
||||||
|
if (expected_size != sizeof(assertion_)) {
|
||||||
|
BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size <<
|
||||||
|
" != " << sizeof(assertion_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) {
|
||||||
|
BPLOG(ERROR) << "MinidumpAssertion cannot read assertion";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each of {expression, function, file} is a UTF-16 string,
|
||||||
|
// we'll convert them to UTF-8 for ease of use.
|
||||||
|
// expression
|
||||||
|
// Since we don't have an explicit byte length for each string,
|
||||||
|
// we use UTF16codeunits to calculate word length, then derive byte
|
||||||
|
// length from that.
|
||||||
|
u_int32_t word_length = UTF16codeunits(assertion_.expression,
|
||||||
|
sizeof(assertion_.expression));
|
||||||
|
if (word_length > 0) {
|
||||||
|
u_int32_t byte_length = word_length * 2;
|
||||||
|
vector<u_int16_t> expression_utf16(word_length);
|
||||||
|
memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length);
|
||||||
|
|
||||||
|
scoped_ptr<string> new_expression(UTF16ToUTF8(expression_utf16,
|
||||||
|
minidump_->swap()));
|
||||||
|
expression_ = *new_expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertion
|
||||||
|
word_length = UTF16codeunits(assertion_.function,
|
||||||
|
sizeof(assertion_.function));
|
||||||
|
if (word_length) {
|
||||||
|
u_int32_t byte_length = word_length * 2;
|
||||||
|
vector<u_int16_t> function_utf16(word_length);
|
||||||
|
memcpy(&function_utf16[0], &assertion_.function[0], byte_length);
|
||||||
|
scoped_ptr<string> new_function(UTF16ToUTF8(function_utf16,
|
||||||
|
minidump_->swap()));
|
||||||
|
function_ = *new_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
// file
|
||||||
|
word_length = UTF16codeunits(assertion_.file,
|
||||||
|
sizeof(assertion_.file));
|
||||||
|
if (word_length > 0) {
|
||||||
|
u_int32_t byte_length = word_length * 2;
|
||||||
|
vector<u_int16_t> file_utf16(word_length);
|
||||||
|
memcpy(&file_utf16[0], &assertion_.file[0], byte_length);
|
||||||
|
scoped_ptr<string> new_file(UTF16ToUTF8(file_utf16,
|
||||||
|
minidump_->swap()));
|
||||||
|
file_ = *new_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minidump_->swap()) {
|
||||||
|
Swap(&assertion_.line);
|
||||||
|
Swap(&assertion_.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpAssertion::Print() {
|
||||||
|
if (!valid_) {
|
||||||
|
BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("MDAssertion\n");
|
||||||
|
printf(" expression = %s\n",
|
||||||
|
expression_.c_str());
|
||||||
|
printf(" function = %s\n",
|
||||||
|
function_.c_str());
|
||||||
|
printf(" file = %s\n",
|
||||||
|
file_.c_str());
|
||||||
|
printf(" line = %u\n",
|
||||||
|
assertion_.line);
|
||||||
|
printf(" type = %u\n",
|
||||||
|
assertion_.type);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// MinidumpSystemInfo
|
// MinidumpSystemInfo
|
||||||
|
@ -3415,6 +3527,11 @@ MinidumpException* Minidump::GetException() {
|
||||||
return GetStream(&exception);
|
return GetStream(&exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MinidumpAssertion* Minidump::GetAssertion() {
|
||||||
|
MinidumpAssertion* assertion;
|
||||||
|
return GetStream(&assertion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MinidumpSystemInfo* Minidump::GetSystemInfo() {
|
MinidumpSystemInfo* Minidump::GetSystemInfo() {
|
||||||
MinidumpSystemInfo* system_info;
|
MinidumpSystemInfo* system_info;
|
||||||
|
|
|
@ -44,6 +44,7 @@ using google_breakpad::MinidumpThreadList;
|
||||||
using google_breakpad::MinidumpModuleList;
|
using google_breakpad::MinidumpModuleList;
|
||||||
using google_breakpad::MinidumpMemoryList;
|
using google_breakpad::MinidumpMemoryList;
|
||||||
using google_breakpad::MinidumpException;
|
using google_breakpad::MinidumpException;
|
||||||
|
using google_breakpad::MinidumpAssertion;
|
||||||
using google_breakpad::MinidumpSystemInfo;
|
using google_breakpad::MinidumpSystemInfo;
|
||||||
using google_breakpad::MinidumpMiscInfo;
|
using google_breakpad::MinidumpMiscInfo;
|
||||||
using google_breakpad::MinidumpBreakpadInfo;
|
using google_breakpad::MinidumpBreakpadInfo;
|
||||||
|
@ -89,6 +90,13 @@ static bool PrintMinidumpDump(const char *minidump_file) {
|
||||||
exception->Print();
|
exception->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MinidumpAssertion *assertion = minidump.GetAssertion();
|
||||||
|
if (!assertion) {
|
||||||
|
BPLOG(INFO) << "minidump.GetAssertion() failed";
|
||||||
|
} else {
|
||||||
|
assertion->Print();
|
||||||
|
}
|
||||||
|
|
||||||
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
|
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
|
||||||
if (!system_info) {
|
if (!system_info) {
|
||||||
++errors;
|
++errors;
|
||||||
|
|
|
@ -86,6 +86,9 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
dump, &process_state->crash_address_);
|
dump, &process_state->crash_address_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will just return an empty string if it doesn't exist.
|
||||||
|
process_state->assertion_ = GetAssertion(dump);
|
||||||
|
|
||||||
MinidumpModuleList *module_list = dump->GetModuleList();
|
MinidumpModuleList *module_list = dump->GetModuleList();
|
||||||
|
|
||||||
// Put a copy of the module list into ProcessState object. This is not
|
// Put a copy of the module list into ProcessState object. This is not
|
||||||
|
@ -1006,4 +1009,57 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
string MinidumpProcessor::GetAssertion(Minidump *dump)
|
||||||
|
{
|
||||||
|
MinidumpAssertion *assertion = dump->GetAssertion();
|
||||||
|
if (!assertion)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
const MDRawAssertionInfo *raw_assertion = assertion->assertion();
|
||||||
|
if (!raw_assertion)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
string assertion_string;
|
||||||
|
switch (raw_assertion->type) {
|
||||||
|
case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER:
|
||||||
|
assertion_string = "Invalid parameter passed to library function";
|
||||||
|
break;
|
||||||
|
case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL:
|
||||||
|
assertion_string = "Pure virtual function called";
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
char assertion_type[32];
|
||||||
|
sprintf(assertion_type, "0x%08x", raw_assertion->type);
|
||||||
|
assertion_string = "Unknown assertion type ";
|
||||||
|
assertion_string += assertion_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string expression = assertion->expression();
|
||||||
|
if (!expression.empty()) {
|
||||||
|
assertion_string.append(" " + expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
string function = assertion->function();
|
||||||
|
if (!function.empty()) {
|
||||||
|
assertion_string.append(" in function " + function);
|
||||||
|
}
|
||||||
|
|
||||||
|
string file = assertion->file();
|
||||||
|
if (!file.empty()) {
|
||||||
|
assertion_string.append(", in file " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw_assertion->line != 0) {
|
||||||
|
char assertion_line[32];
|
||||||
|
sprintf(assertion_line, "%u", raw_assertion->line);
|
||||||
|
assertion_string.append(" at line ");
|
||||||
|
assertion_string.append(assertion_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return assertion_string;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -361,6 +361,11 @@ static void PrintProcessState(const ProcessState& process_state) {
|
||||||
printf("No crash\n");
|
printf("No crash\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string assertion = process_state.assertion();
|
||||||
|
if (!assertion.empty()) {
|
||||||
|
printf("Assertion: %s\n", assertion.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// If the thread that requested the dump is known, print it first.
|
// If the thread that requested the dump is known, print it first.
|
||||||
int requesting_thread = process_state.requesting_thread();
|
int requesting_thread = process_state.requesting_thread();
|
||||||
if (requesting_thread != -1) {
|
if (requesting_thread != -1) {
|
||||||
|
@ -412,9 +417,17 @@ static void PrintProcessStateMachineReadable(const ProcessState& process_state)
|
||||||
printf("%s%c0x%" PRIx64 "%c",
|
printf("%s%c0x%" PRIx64 "%c",
|
||||||
StripSeparator(process_state.crash_reason()).c_str(),
|
StripSeparator(process_state.crash_reason()).c_str(),
|
||||||
kOutputSeparator, process_state.crash_address(), kOutputSeparator);
|
kOutputSeparator, process_state.crash_address(), kOutputSeparator);
|
||||||
|
} else {
|
||||||
|
// print assertion info, if available, in place of crash reason,
|
||||||
|
// instead of the unhelpful "No crash"
|
||||||
|
string assertion = process_state.assertion();
|
||||||
|
if (!assertion.empty()) {
|
||||||
|
printf("%s%c%c", StripSeparator(assertion).c_str(),
|
||||||
|
kOutputSeparator, kOutputSeparator);
|
||||||
} else {
|
} else {
|
||||||
printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
|
printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (requesting_thread != -1) {
|
if (requesting_thread != -1) {
|
||||||
printf("%d\n", requesting_thread);
|
printf("%d\n", requesting_thread);
|
||||||
|
|
|
@ -48,6 +48,7 @@ void ProcessState::Clear() {
|
||||||
crashed_ = false;
|
crashed_ = false;
|
||||||
crash_reason_.clear();
|
crash_reason_.clear();
|
||||||
crash_address_ = 0;
|
crash_address_ = 0;
|
||||||
|
assertion_.clear();
|
||||||
requesting_thread_ = -1;
|
requesting_thread_ = -1;
|
||||||
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
|
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
|
||||||
iterator != threads_.end();
|
iterator != threads_.end();
|
||||||
|
|
Loading…
Reference in a new issue