diff options
author | hashimoto@chromium.org <hashimoto@chromium.org> | 2015-02-27 04:52:21 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org> | 2015-02-27 04:52:21 +0000 |
commit | f2e6c177e445c1a215b9c1b6852d0a867c3879d6 (patch) | |
tree | 1363c0fa93882d0db32cd385ef1a64a18f4e9809 | |
parent | Update license on convert_UTF.* to the standard Unicode license. (diff) | |
download | breakpad-f2e6c177e445c1a215b9c1b6852d0a867c3879d6.tar.xz |
Add stack contents output functionality to minidump_stackwalk
This feature is enabled only when "-s" is provided as a commandline option.
minidump_stackwalk.cc:
- Add a new commandline option "-s" to output stack contents.
- Instantiate Minidump object in PrintMinidumpProcess() to keep it alive longer so that accessing process_state.thread_memory_regions() in stackwalk_common.cc doesn't result in use-after-free.
stackwalk_common.cc:
- Add a new function PrintStackContents() to output stack contents.
R=mark@chromium.org
Review URL: https://breakpad.appspot.com/9774002
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1429 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r-- | src/processor/microdump_stackwalk.cc | 2 | ||||
-rw-r--r-- | src/processor/minidump_stackwalk.cc | 35 | ||||
-rw-r--r-- | src/processor/stackwalk_common.cc | 157 | ||||
-rw-r--r-- | src/processor/stackwalk_common.h | 5 |
4 files changed, 185 insertions, 14 deletions
diff --git a/src/processor/microdump_stackwalk.cc b/src/processor/microdump_stackwalk.cc index 68f8e195..7ea80495 100644 --- a/src/processor/microdump_stackwalk.cc +++ b/src/processor/microdump_stackwalk.cc @@ -94,7 +94,7 @@ int PrintMicrodumpProcess(const char* microdump_file, if (machine_readable) { PrintProcessStateMachineReadable(process_state); } else { - PrintProcessState(process_state); + PrintProcessState(process_state, false, &resolver); } return 0; } diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index af45f1ff..8f83969f 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -41,6 +41,7 @@ #include "common/scoped_ptr.h" #include "common/using_std_string.h" #include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/minidump.h" #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/process_state.h" #include "processor/logging.h" @@ -51,6 +52,7 @@ namespace { using google_breakpad::BasicSourceLineResolver; +using google_breakpad::Minidump; using google_breakpad::MinidumpProcessor; using google_breakpad::ProcessState; using google_breakpad::SimpleSymbolSupplier; @@ -68,7 +70,8 @@ using google_breakpad::scoped_ptr; // is printed to stdout. bool PrintMinidumpProcess(const string &minidump_file, const std::vector<string> &symbol_paths, - bool machine_readable) { + bool machine_readable, + bool output_stack_contents) { scoped_ptr<SimpleSymbolSupplier> symbol_supplier; if (!symbol_paths.empty()) { // TODO(mmentovai): check existence of symbol_path if specified? @@ -79,8 +82,13 @@ bool PrintMinidumpProcess(const string &minidump_file, MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver); // Process the minidump. + Minidump dump(minidump_file); + if (!dump.Read()) { + BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read"; + return false; + } ProcessState process_state; - if (minidump_processor.Process(minidump_file, &process_state) != + if (minidump_processor.Process(&dump, &process_state) != google_breakpad::PROCESS_OK) { BPLOG(ERROR) << "MinidumpProcessor::Process failed"; return false; @@ -89,15 +97,16 @@ bool PrintMinidumpProcess(const string &minidump_file, if (machine_readable) { PrintProcessStateMachineReadable(process_state); } else { - PrintProcessState(process_state); + PrintProcessState(process_state, output_stack_contents, &resolver); } return true; } void usage(const char *program_name) { - fprintf(stderr, "usage: %s [-m] <minidump-file> [symbol-path ...]\n" - " -m : Output in machine-readable format\n", + fprintf(stderr, "usage: %s [-m|-s] <minidump-file> [symbol-path ...]\n" + " -m : Output in machine-readable format\n" + " -s : Output stack contents\n", program_name); } @@ -112,7 +121,8 @@ int main(int argc, char **argv) { } const char *minidump_file; - bool machine_readable; + bool machine_readable = false; + bool output_stack_contents = false; int symbol_path_arg; if (strcmp(argv[1], "-m") == 0) { @@ -124,8 +134,16 @@ int main(int argc, char **argv) { machine_readable = true; minidump_file = argv[2]; symbol_path_arg = 3; + } else if (strcmp(argv[1], "-s") == 0) { + if (argc < 3) { + usage(argv[0]); + return 1; + } + + output_stack_contents = true; + minidump_file = argv[2]; + symbol_path_arg = 3; } else { - machine_readable = false; minidump_file = argv[1]; symbol_path_arg = 2; } @@ -139,5 +157,6 @@ int main(int argc, char **argv) { return PrintMinidumpProcess(minidump_file, symbol_paths, - machine_readable) ? 0 : 1; + machine_readable, + output_stack_contents) ? 0 : 1; } diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc index 0a1707e2..5c66ea3d 100644 --- a/src/processor/stackwalk_common.cc +++ b/src/processor/stackwalk_common.cc @@ -47,6 +47,7 @@ #include "google_breakpad/processor/code_module.h" #include "google_breakpad/processor/code_modules.h" #include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" #include "google_breakpad/processor/stack_frame_cpu.h" #include "processor/logging.h" #include "processor/pathname_stripper.h" @@ -110,6 +111,134 @@ static string StripSeparator(const string &original) { return result; } +// PrintStackContents prints the stack contents of the current frame to stdout. +static void PrintStackContents(const std::string &indent, + const StackFrame *frame, + const StackFrame *prev_frame, + const std::string &cpu, + const MemoryRegion *memory, + const CodeModules* modules, + SourceLineResolverInterface *resolver) { + // Find stack range. + int word_length = 0; + uint64_t stack_begin = 0, stack_end = 0; + if (cpu == "x86") { + word_length = 4; + const StackFrameX86 *frame_x86 = static_cast<const StackFrameX86*>(frame); + const StackFrameX86 *prev_frame_x86 = + static_cast<const StackFrameX86*>(prev_frame); + if ((frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) && + (prev_frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)) { + stack_begin = frame_x86->context.esp; + stack_end = prev_frame_x86->context.esp; + } + } else if (cpu == "amd64") { + word_length = 8; + const StackFrameAMD64 *frame_amd64 = + static_cast<const StackFrameAMD64*>(frame); + const StackFrameAMD64 *prev_frame_amd64 = + static_cast<const StackFrameAMD64*>(prev_frame); + if ((frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) && + (prev_frame_amd64->context_validity & + StackFrameAMD64::CONTEXT_VALID_RSP)) { + stack_begin = frame_amd64->context.rsp; + stack_end = prev_frame_amd64->context.rsp; + } + } else if (cpu == "arm") { + word_length = 4; + const StackFrameARM *frame_arm = static_cast<const StackFrameARM*>(frame); + const StackFrameARM *prev_frame_arm = + static_cast<const StackFrameARM*>(prev_frame); + if ((frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) && + (prev_frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)) { + stack_begin = frame_arm->context.iregs[13]; + stack_end = prev_frame_arm->context.iregs[13]; + } + } else if (cpu == "arm64") { + word_length = 8; + const StackFrameARM64 *frame_arm64 = + static_cast<const StackFrameARM64*>(frame); + const StackFrameARM64 *prev_frame_arm64 = + static_cast<const StackFrameARM64*>(prev_frame); + if ((frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) && + (prev_frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_SP)) { + stack_begin = frame_arm64->context.iregs[31]; + stack_end = prev_frame_arm64->context.iregs[31]; + } + } + if (!word_length || !stack_begin || !stack_end) + return; + + // Print stack contents. + printf("\n%sStack contents:", indent.c_str()); + for(uint64_t address = stack_begin; address < stack_end; ) { + // Print the start address of this row. + if (word_length == 4) + printf("\n%s %08x", indent.c_str(), static_cast<uint32_t>(address)); + else + printf("\n%s %016" PRIx64, indent.c_str(), address); + + // Print data in hex. + const int kBytesPerRow = 16; + std::string data_as_string; + for (int i = 0; i < kBytesPerRow; ++i, ++address) { + uint8_t value = 0; + if (address < stack_end && + memory->GetMemoryAtAddress(address, &value)) { + printf(" %02x", value); + data_as_string.push_back(isprint(value) ? value : '.'); + } else { + printf(" "); + data_as_string.push_back(' '); + } + } + // Print data as string. + printf(" %s", data_as_string.c_str()); + } + + // Try to find instruction pointers from stack. + printf("\n%sPossible instruction pointers:", indent.c_str()); + for (uint64_t address = stack_begin; address < stack_end; + address += word_length) { + StackFrame pointee_frame; + + // Read a word (possible instruction pointer) from stack. + if (word_length == 4) { + uint32_t data32 = 0; + memory->GetMemoryAtAddress(address, &data32); + pointee_frame.instruction = data32; + } else { + uint64_t data64 = 0; + memory->GetMemoryAtAddress(address, &data64); + pointee_frame.instruction = data64; + } + pointee_frame.module = + modules->GetModuleForAddress(pointee_frame.instruction); + + // Try to look up the function name. + if (pointee_frame.module) + resolver->FillSourceLineInfo(&pointee_frame); + + // Print function name. + if (!pointee_frame.function_name.empty()) { + if (word_length == 4) { + printf("\n%s *(0x%08x) = 0x%08x", indent.c_str(), + static_cast<uint32_t>(address), + static_cast<uint32_t>(pointee_frame.instruction)); + } else { + printf("\n%s *(0x%016" PRIx64 ") = 0x%016" PRIx64, + indent.c_str(), address, pointee_frame.instruction); + } + printf(" <%s> [%s : %d + 0x%" PRIx64 "]", + pointee_frame.function_name.c_str(), + PathnameStripper::File(pointee_frame.source_file_name).c_str(), + pointee_frame.source_line, + pointee_frame.instruction - pointee_frame.source_line_base); + } + } +} + // PrintStack prints the call stack in |stack| to stdout, in a reasonably // useful form. Module, function, and source file names are displayed if // they are available. The code offset to the base code address of the @@ -119,7 +248,12 @@ static string StripSeparator(const string &original) { // // If |cpu| is a recognized CPU name, relevant register state for each stack // frame printed is also output, if available. -static void PrintStack(const CallStack *stack, const string &cpu) { +static void PrintStack(const CallStack *stack, + const string &cpu, + bool output_stack_contents, + const MemoryRegion* memory, + const CodeModules* modules, + SourceLineResolverInterface* resolver) { int frame_count = stack->frames()->size(); if (frame_count == 0) { printf(" <no frames>\n"); @@ -469,6 +603,13 @@ static void PrintStack(const CallStack *stack, const string &cpu) { sequence); } printf("\n Found by: %s\n", frame->trust_description().c_str()); + + // Print stack contents. + if (output_stack_contents && frame_index + 1 < frame_count) { + const std::string indent(" "); + PrintStackContents(indent, frame, stack->frames()->at(frame_index + 1), + cpu, memory, modules, resolver); + } } } @@ -642,7 +783,9 @@ static void PrintModulesMachineReadable(const CodeModules *modules) { } // namespace -void PrintProcessState(const ProcessState& process_state) { +void PrintProcessState(const ProcessState& process_state, + bool output_stack_contents, + SourceLineResolverInterface* resolver) { // Print OS and CPU information. string cpu = process_state.system_info()->cpu; string cpu_info = process_state.system_info()->cpu_info; @@ -692,7 +835,10 @@ void PrintProcessState(const ProcessState& process_state) { requesting_thread, process_state.crashed() ? "crashed" : "requested dump, did not crash"); - PrintStack(process_state.threads()->at(requesting_thread), cpu); + PrintStack(process_state.threads()->at(requesting_thread), cpu, + output_stack_contents, + process_state.thread_memory_regions()->at(requesting_thread), + process_state.modules(), resolver); } // Print all of the threads in the dump. @@ -702,7 +848,10 @@ void PrintProcessState(const ProcessState& process_state) { // Don't print the crash thread again, it was already printed. printf("\n"); printf("Thread %d\n", thread_index); - PrintStack(process_state.threads()->at(thread_index), cpu); + PrintStack(process_state.threads()->at(thread_index), cpu, + output_stack_contents, + process_state.thread_memory_regions()->at(thread_index), + process_state.modules(), resolver); } } diff --git a/src/processor/stackwalk_common.h b/src/processor/stackwalk_common.h index 7ee6e75b..a74f7b6d 100644 --- a/src/processor/stackwalk_common.h +++ b/src/processor/stackwalk_common.h @@ -37,9 +37,12 @@ namespace google_breakpad { class ProcessState; +class SourceLineResolverInterface; void PrintProcessStateMachineReadable(const ProcessState& process_state); -void PrintProcessState(const ProcessState& process_state); +void PrintProcessState(const ProcessState& process_state, + bool output_stack_contents, + SourceLineResolverInterface* resolver); } // namespace google_breakpad |