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:
ted.mielczarek 2009-12-02 17:43:57 +00:00
parent 096992fac7
commit 0314e487e4
8 changed files with 248 additions and 1 deletions

View file

@ -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();

View file

@ -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_;

View file

@ -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<CallStack*>* threads() const { return &threads_; }
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.
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

View file

@ -243,6 +243,15 @@ static string* UTF16ToUTF8(const vector<u_int16_t>& 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<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
@ -3415,6 +3527,11 @@ MinidumpException* Minidump::GetException() {
return GetStream(&exception);
}
MinidumpAssertion* Minidump::GetAssertion() {
MinidumpAssertion* assertion;
return GetStream(&assertion);
}
MinidumpSystemInfo* Minidump::GetSystemInfo() {
MinidumpSystemInfo* system_info;

View file

@ -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;

View file

@ -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

View file

@ -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) {

View file

@ -48,6 +48,7 @@ void ProcessState::Clear() {
crashed_ = false;
crash_reason_.clear();
crash_address_ = 0;
assertion_.clear();
requesting_thread_ = -1;
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
iterator != threads_.end();