From 0314e487e46a45229e275eb78b09f0538a5a7769 Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Wed, 2 Dec 2009 17:43:57 +0000 Subject: [PATCH] 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 --- src/google_breakpad/processor/minidump.h | 41 ++++++ .../processor/minidump_processor.h | 5 + src/google_breakpad/processor/process_state.h | 6 + src/processor/minidump.cc | 117 ++++++++++++++++++ src/processor/minidump_dump.cc | 8 ++ src/processor/minidump_processor.cc | 56 +++++++++ src/processor/minidump_stackwalk.cc | 15 ++- src/processor/process_state.cc | 1 + 8 files changed, 248 insertions(+), 1 deletion(-) diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index f08a1c18..d3a7b92b 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -639,6 +639,46 @@ class MinidumpException : public MinidumpStream { 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 // the system on which the minidump was generated. See also MinidumpMiscInfo. @@ -788,6 +828,7 @@ class Minidump { MinidumpModuleList* GetModuleList(); MinidumpMemoryList* GetMemoryList(); MinidumpException* GetException(); + MinidumpAssertion* GetAssertion(); MinidumpSystemInfo* GetSystemInfo(); MinidumpMiscInfo* GetMiscInfo(); MinidumpBreakpadInfo* GetBreakpadInfo(); diff --git a/src/google_breakpad/processor/minidump_processor.h b/src/google_breakpad/processor/minidump_processor.h index f313bf71..756a868b 100644 --- a/src/google_breakpad/processor/minidump_processor.h +++ b/src/google_breakpad/processor/minidump_processor.h @@ -141,6 +141,11 @@ class MinidumpProcessor { 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: SymbolSupplier *supplier_; SourceLineResolverInterface *resolver_; diff --git a/src/google_breakpad/processor/process_state.h b/src/google_breakpad/processor/process_state.h index 8247f707..23014227 100644 --- a/src/google_breakpad/processor/process_state.h +++ b/src/google_breakpad/processor/process_state.h @@ -61,6 +61,7 @@ class ProcessState { bool crashed() const { return crashed_; } string crash_reason() const { return crash_reason_; } u_int64_t crash_address() const { return crash_address_; } + string assertion() const { return assertion_; } int requesting_thread() const { return requesting_thread_; } const vector* threads() const { return &threads_; } const vector* thread_memory_regions() const { @@ -92,6 +93,11 @@ class ProcessState { // this will be the address of the instruction that caused the fault. 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 // 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 diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index cd6da170..ab4e4286 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -243,6 +243,15 @@ static string* UTF16ToUTF8(const vector& in, 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 @@ -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 expression_utf16(word_length); + memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); + + scoped_ptr 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 function_utf16(word_length); + memcpy(&function_utf16[0], &assertion_.function[0], byte_length); + scoped_ptr 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 file_utf16(word_length); + memcpy(&file_utf16[0], &assertion_.file[0], byte_length); + scoped_ptr 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 @@ -3415,6 +3527,11 @@ MinidumpException* Minidump::GetException() { return GetStream(&exception); } +MinidumpAssertion* Minidump::GetAssertion() { + MinidumpAssertion* assertion; + return GetStream(&assertion); +} + MinidumpSystemInfo* Minidump::GetSystemInfo() { MinidumpSystemInfo* system_info; diff --git a/src/processor/minidump_dump.cc b/src/processor/minidump_dump.cc index 12a699de..b586252f 100644 --- a/src/processor/minidump_dump.cc +++ b/src/processor/minidump_dump.cc @@ -44,6 +44,7 @@ using google_breakpad::MinidumpThreadList; using google_breakpad::MinidumpModuleList; using google_breakpad::MinidumpMemoryList; using google_breakpad::MinidumpException; +using google_breakpad::MinidumpAssertion; using google_breakpad::MinidumpSystemInfo; using google_breakpad::MinidumpMiscInfo; using google_breakpad::MinidumpBreakpadInfo; @@ -89,6 +90,13 @@ static bool PrintMinidumpDump(const char *minidump_file) { exception->Print(); } + MinidumpAssertion *assertion = minidump.GetAssertion(); + if (!assertion) { + BPLOG(INFO) << "minidump.GetAssertion() failed"; + } else { + assertion->Print(); + } + MinidumpSystemInfo *system_info = minidump.GetSystemInfo(); if (!system_info) { ++errors; diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index 0ce19e38..24b03e9d 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -86,6 +86,9 @@ ProcessResult MinidumpProcessor::Process( 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(); // 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; } +// 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 diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 01f0c0c6..3701c466 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -361,6 +361,11 @@ static void PrintProcessState(const ProcessState& process_state) { 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. int requesting_thread = process_state.requesting_thread(); if (requesting_thread != -1) { @@ -413,7 +418,15 @@ static void PrintProcessStateMachineReadable(const ProcessState& process_state) StripSeparator(process_state.crash_reason()).c_str(), kOutputSeparator, process_state.crash_address(), kOutputSeparator); } else { - printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + // 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 { + printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + } } if (requesting_thread != -1) { diff --git a/src/processor/process_state.cc b/src/processor/process_state.cc index 934792b3..1d970bad 100644 --- a/src/processor/process_state.cc +++ b/src/processor/process_state.cc @@ -48,6 +48,7 @@ void ProcessState::Clear() { crashed_ = false; crash_reason_.clear(); crash_address_ = 0; + assertion_.clear(); requesting_thread_ = -1; for (vector::const_iterator iterator = threads_.begin(); iterator != threads_.end();