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
This commit is contained in:
parent
14bbefbd96
commit
f2e6c177e4
4 changed files with 185 additions and 14 deletions
|
@ -94,7 +94,7 @@ int PrintMicrodumpProcess(const char* microdump_file,
|
||||||
if (machine_readable) {
|
if (machine_readable) {
|
||||||
PrintProcessStateMachineReadable(process_state);
|
PrintProcessStateMachineReadable(process_state);
|
||||||
} else {
|
} else {
|
||||||
PrintProcessState(process_state);
|
PrintProcessState(process_state, false, &resolver);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "common/scoped_ptr.h"
|
#include "common/scoped_ptr.h"
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/processor/basic_source_line_resolver.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/minidump_processor.h"
|
||||||
#include "google_breakpad/processor/process_state.h"
|
#include "google_breakpad/processor/process_state.h"
|
||||||
#include "processor/logging.h"
|
#include "processor/logging.h"
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using google_breakpad::BasicSourceLineResolver;
|
using google_breakpad::BasicSourceLineResolver;
|
||||||
|
using google_breakpad::Minidump;
|
||||||
using google_breakpad::MinidumpProcessor;
|
using google_breakpad::MinidumpProcessor;
|
||||||
using google_breakpad::ProcessState;
|
using google_breakpad::ProcessState;
|
||||||
using google_breakpad::SimpleSymbolSupplier;
|
using google_breakpad::SimpleSymbolSupplier;
|
||||||
|
@ -68,7 +70,8 @@ using google_breakpad::scoped_ptr;
|
||||||
// is printed to stdout.
|
// is printed to stdout.
|
||||||
bool PrintMinidumpProcess(const string &minidump_file,
|
bool PrintMinidumpProcess(const string &minidump_file,
|
||||||
const std::vector<string> &symbol_paths,
|
const std::vector<string> &symbol_paths,
|
||||||
bool machine_readable) {
|
bool machine_readable,
|
||||||
|
bool output_stack_contents) {
|
||||||
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
|
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
|
||||||
if (!symbol_paths.empty()) {
|
if (!symbol_paths.empty()) {
|
||||||
// TODO(mmentovai): check existence of symbol_path if specified?
|
// 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);
|
MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
|
||||||
|
|
||||||
// Process the minidump.
|
// Process the minidump.
|
||||||
|
Minidump dump(minidump_file);
|
||||||
|
if (!dump.Read()) {
|
||||||
|
BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ProcessState process_state;
|
ProcessState process_state;
|
||||||
if (minidump_processor.Process(minidump_file, &process_state) !=
|
if (minidump_processor.Process(&dump, &process_state) !=
|
||||||
google_breakpad::PROCESS_OK) {
|
google_breakpad::PROCESS_OK) {
|
||||||
BPLOG(ERROR) << "MinidumpProcessor::Process failed";
|
BPLOG(ERROR) << "MinidumpProcessor::Process failed";
|
||||||
return false;
|
return false;
|
||||||
|
@ -89,15 +97,16 @@ bool PrintMinidumpProcess(const string &minidump_file,
|
||||||
if (machine_readable) {
|
if (machine_readable) {
|
||||||
PrintProcessStateMachineReadable(process_state);
|
PrintProcessStateMachineReadable(process_state);
|
||||||
} else {
|
} else {
|
||||||
PrintProcessState(process_state);
|
PrintProcessState(process_state, output_stack_contents, &resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage(const char *program_name) {
|
void usage(const char *program_name) {
|
||||||
fprintf(stderr, "usage: %s [-m] <minidump-file> [symbol-path ...]\n"
|
fprintf(stderr, "usage: %s [-m|-s] <minidump-file> [symbol-path ...]\n"
|
||||||
" -m : Output in machine-readable format\n",
|
" -m : Output in machine-readable format\n"
|
||||||
|
" -s : Output stack contents\n",
|
||||||
program_name);
|
program_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +121,8 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *minidump_file;
|
const char *minidump_file;
|
||||||
bool machine_readable;
|
bool machine_readable = false;
|
||||||
|
bool output_stack_contents = false;
|
||||||
int symbol_path_arg;
|
int symbol_path_arg;
|
||||||
|
|
||||||
if (strcmp(argv[1], "-m") == 0) {
|
if (strcmp(argv[1], "-m") == 0) {
|
||||||
|
@ -124,8 +134,16 @@ int main(int argc, char **argv) {
|
||||||
machine_readable = true;
|
machine_readable = true;
|
||||||
minidump_file = argv[2];
|
minidump_file = argv[2];
|
||||||
symbol_path_arg = 3;
|
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 {
|
} else {
|
||||||
machine_readable = false;
|
|
||||||
minidump_file = argv[1];
|
minidump_file = argv[1];
|
||||||
symbol_path_arg = 2;
|
symbol_path_arg = 2;
|
||||||
}
|
}
|
||||||
|
@ -139,5 +157,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
return PrintMinidumpProcess(minidump_file,
|
return PrintMinidumpProcess(minidump_file,
|
||||||
symbol_paths,
|
symbol_paths,
|
||||||
machine_readable) ? 0 : 1;
|
machine_readable,
|
||||||
|
output_stack_contents) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "google_breakpad/processor/code_module.h"
|
#include "google_breakpad/processor/code_module.h"
|
||||||
#include "google_breakpad/processor/code_modules.h"
|
#include "google_breakpad/processor/code_modules.h"
|
||||||
#include "google_breakpad/processor/process_state.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 "google_breakpad/processor/stack_frame_cpu.h"
|
||||||
#include "processor/logging.h"
|
#include "processor/logging.h"
|
||||||
#include "processor/pathname_stripper.h"
|
#include "processor/pathname_stripper.h"
|
||||||
|
@ -110,6 +111,134 @@ static string StripSeparator(const string &original) {
|
||||||
return result;
|
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
|
// PrintStack prints the call stack in |stack| to stdout, in a reasonably
|
||||||
// useful form. Module, function, and source file names are displayed if
|
// useful form. Module, function, and source file names are displayed if
|
||||||
// they are available. The code offset to the base code address of the
|
// 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
|
// If |cpu| is a recognized CPU name, relevant register state for each stack
|
||||||
// frame printed is also output, if available.
|
// 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();
|
int frame_count = stack->frames()->size();
|
||||||
if (frame_count == 0) {
|
if (frame_count == 0) {
|
||||||
printf(" <no frames>\n");
|
printf(" <no frames>\n");
|
||||||
|
@ -469,6 +603,13 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
||||||
sequence);
|
sequence);
|
||||||
}
|
}
|
||||||
printf("\n Found by: %s\n", frame->trust_description().c_str());
|
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
|
} // namespace
|
||||||
|
|
||||||
void PrintProcessState(const ProcessState& process_state) {
|
void PrintProcessState(const ProcessState& process_state,
|
||||||
|
bool output_stack_contents,
|
||||||
|
SourceLineResolverInterface* resolver) {
|
||||||
// Print OS and CPU information.
|
// Print OS and CPU information.
|
||||||
string cpu = process_state.system_info()->cpu;
|
string cpu = process_state.system_info()->cpu;
|
||||||
string cpu_info = process_state.system_info()->cpu_info;
|
string cpu_info = process_state.system_info()->cpu_info;
|
||||||
|
@ -692,7 +835,10 @@ void PrintProcessState(const ProcessState& process_state) {
|
||||||
requesting_thread,
|
requesting_thread,
|
||||||
process_state.crashed() ? "crashed" :
|
process_state.crashed() ? "crashed" :
|
||||||
"requested dump, did not crash");
|
"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.
|
// 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.
|
// Don't print the crash thread again, it was already printed.
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Thread %d\n", thread_index);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,12 @@
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
class ProcessState;
|
class ProcessState;
|
||||||
|
class SourceLineResolverInterface;
|
||||||
|
|
||||||
void PrintProcessStateMachineReadable(const ProcessState& process_state);
|
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
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue