aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorprimiano@chromium.org <primiano@chromium.org>2014-11-25 11:36:38 +0000
committerprimiano@chromium.org <primiano@chromium.org>2014-11-25 11:36:38 +0000
commit57e5b074f6a56295a126ff025797bde13b4e2ccf (patch)
treee3619b1d0f371e2b95b1a6e1a26b8a38a9307044 /src
parentFix microdump_writer and add unittest. (diff)
downloadbreakpad-57e5b074f6a56295a126ff025797bde13b4e2ccf.tar.xz
Introduce microdump_stackwalk comand line executable
This introduces the microdump_stackwalk binary which takes advantage of the MicrodumpProcessor to symbolize microdumps. Its operation is identical to the one of minidump_stackwalk. This CL, in fact, is also refactoring most of the common bits into stackwalk_common. BUG=chromium:410294 R=mmandlis@chromium.org Review URL: https://breakpad.appspot.com/4704002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1405 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/processor/microdump_stackwalk.cc151
-rwxr-xr-xsrc/processor/microdump_stackwalk_machine_readable_test37
-rwxr-xr-xsrc/processor/microdump_stackwalk_test37
-rw-r--r--src/processor/minidump_stackwalk.cc743
-rw-r--r--src/processor/stackwalk_common.cc767
-rw-r--r--src/processor/stackwalk_common.h46
-rw-r--r--src/processor/testdata/microdump.stackwalk.machine_readable.out129
-rw-r--r--src/processor/testdata/microdump.stackwalk.out178
8 files changed, 1354 insertions, 734 deletions
diff --git a/src/processor/microdump_stackwalk.cc b/src/processor/microdump_stackwalk.cc
new file mode 100644
index 00000000..68f8e195
--- /dev/null
+++ b/src/processor/microdump_stackwalk.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2014 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// microdump_stackwalk.cc: Process a microdump with MicrodumpProcessor, printing
+// the results, including stack traces.
+
+#include <stdio.h>
+#include <string.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/microdump_processor.h"
+#include "google_breakpad/processor/process_state.h"
+#include "google_breakpad/processor/stack_frame_symbolizer.h"
+#include "processor/logging.h"
+#include "processor/simple_symbol_supplier.h"
+#include "processor/stackwalk_common.h"
+
+
+namespace {
+
+using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::MicrodumpProcessor;
+using google_breakpad::ProcessResult;
+using google_breakpad::ProcessState;
+using google_breakpad::scoped_ptr;
+using google_breakpad::SimpleSymbolSupplier;
+using google_breakpad::StackFrameSymbolizer;
+
+// Processes |microdump_file| using MicrodumpProcessor. |symbol_path|, if
+// non-empty, is the base directory of a symbol storage area, laid out in
+// the format required by SimpleSymbolSupplier. If such a storage area
+// is specified, it is made available for use by the MicrodumpProcessor.
+//
+// Returns the value of MicrodumpProcessor::Process. If processing succeeds,
+// prints identifying OS and CPU information from the microdump, crash
+// information and call stacks for the crashing thread.
+// All information is printed to stdout.
+int PrintMicrodumpProcess(const char* microdump_file,
+ const std::vector<string>& symbol_paths,
+ bool machine_readable) {
+ std::ifstream file_stream(microdump_file);
+ std::vector<char> bytes;
+ file_stream.seekg(0, std::ios_base::end);
+ bytes.resize(file_stream.tellg());
+ file_stream.seekg(0, std::ios_base::beg);
+ file_stream.read(&bytes[0], bytes.size());
+ string microdump_content(&bytes[0], bytes.size());
+
+ scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
+ if (!symbol_paths.empty()) {
+ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
+ }
+
+ BasicSourceLineResolver resolver;
+ StackFrameSymbolizer frame_symbolizer(symbol_supplier.get(), &resolver);
+ ProcessState process_state;
+ MicrodumpProcessor microdump_processor(&frame_symbolizer);
+ ProcessResult res = microdump_processor.Process(microdump_content,
+ &process_state);
+
+ if (res == google_breakpad::PROCESS_OK) {
+ if (machine_readable) {
+ PrintProcessStateMachineReadable(process_state);
+ } else {
+ PrintProcessState(process_state);
+ }
+ return 0;
+ }
+
+ BPLOG(ERROR) << "MicrodumpProcessor::Process failed (code = " << res << ")";
+ return 1;
+}
+
+void usage(const char *program_name) {
+ fprintf(stderr, "usage: %s [-m] <microdump-file> [symbol-path ...]\n"
+ " -m : Output in machine-readable format\n",
+ program_name);
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ BPLOG_INIT(&argc, &argv);
+
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ const char* microdump_file;
+ bool machine_readable;
+ int symbol_path_arg;
+
+ if (strcmp(argv[1], "-m") == 0) {
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ machine_readable = true;
+ microdump_file = argv[2];
+ symbol_path_arg = 3;
+ } else {
+ machine_readable = false;
+ microdump_file = argv[1];
+ symbol_path_arg = 2;
+ }
+
+ // extra arguments are symbol paths
+ std::vector<string> symbol_paths;
+ if (argc > symbol_path_arg) {
+ for (int argi = symbol_path_arg; argi < argc; ++argi)
+ symbol_paths.push_back(argv[argi]);
+ }
+
+ return PrintMicrodumpProcess(microdump_file,
+ symbol_paths,
+ machine_readable);
+}
diff --git a/src/processor/microdump_stackwalk_machine_readable_test b/src/processor/microdump_stackwalk_machine_readable_test
new file mode 100755
index 00000000..06762b4b
--- /dev/null
+++ b/src/processor/microdump_stackwalk_machine_readable_test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (c) 2014, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/microdump_stackwalk -m $testdata_dir/microdump.dmp \
+ $testdata_dir/symbols/microdump | \
+ tr -d '\015' | \
+ diff -u $testdata_dir/microdump.stackwalk.machine_readable.out -
+exit $?
diff --git a/src/processor/microdump_stackwalk_test b/src/processor/microdump_stackwalk_test
new file mode 100755
index 00000000..c5390f94
--- /dev/null
+++ b/src/processor/microdump_stackwalk_test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (c) 2014, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/microdump_stackwalk $testdata_dir/microdump.dmp \
+ $testdata_dir/symbols/microdump | \
+ tr -d '\015' | \
+ diff -u $testdata_dir/microdump.stackwalk.out -
+exit $?
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index 47e08575..af45f1ff 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -33,7 +33,6 @@
// Author: Mark Mentovai
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <string>
@@ -42,744 +41,20 @@
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
-#include "google_breakpad/processor/call_stack.h"
-#include "google_breakpad/processor/code_module.h"
-#include "google_breakpad/processor/code_modules.h"
-#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
-#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
-#include "processor/pathname_stripper.h"
#include "processor/simple_symbol_supplier.h"
+#include "processor/stackwalk_common.h"
+
namespace {
-using std::vector;
using google_breakpad::BasicSourceLineResolver;
-using google_breakpad::CallStack;
-using google_breakpad::CodeModule;
-using google_breakpad::CodeModules;
-using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpProcessor;
-using google_breakpad::PathnameStripper;
using google_breakpad::ProcessState;
-using google_breakpad::scoped_ptr;
using google_breakpad::SimpleSymbolSupplier;
-using google_breakpad::StackFrame;
-using google_breakpad::StackFramePPC;
-using google_breakpad::StackFrameSPARC;
-using google_breakpad::StackFrameX86;
-using google_breakpad::StackFrameAMD64;
-using google_breakpad::StackFrameARM;
-using google_breakpad::StackFrameARM64;
-using google_breakpad::StackFrameMIPS;
-
-// Separator character for machine readable output.
-static const char kOutputSeparator = '|';
-
-// PrintRegister prints a register's name and value to stdout. It will
-// print four registers on a line. For the first register in a set,
-// pass 0 for |start_col|. For registers in a set, pass the most recent
-// return value of PrintRegister.
-// The caller is responsible for printing the final newline after a set
-// of registers is completely printed, regardless of the number of calls
-// to PrintRegister.
-static const int kMaxWidth = 80; // optimize for an 80-column terminal
-static int PrintRegister(const char *name, uint32_t value, int start_col) {
- char buffer[64];
- snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value);
-
- if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
- start_col = 0;
- printf("\n ");
- }
- fputs(buffer, stdout);
-
- return start_col + strlen(buffer);
-}
-
-// PrintRegister64 does the same thing, but for 64-bit registers.
-static int PrintRegister64(const char *name, uint64_t value, int start_col) {
- char buffer[64];
- snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value);
-
- if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
- start_col = 0;
- printf("\n ");
- }
- fputs(buffer, stdout);
-
- return start_col + strlen(buffer);
-}
-
-// StripSeparator takes a string |original| and returns a copy
-// of the string with all occurences of |kOutputSeparator| removed.
-static string StripSeparator(const string &original) {
- string result = original;
- string::size_type position = 0;
- while ((position = result.find(kOutputSeparator, position)) != string::npos) {
- result.erase(position, 1);
- }
- position = 0;
- while ((position = result.find('\n', position)) != string::npos) {
- result.erase(position, 1);
- }
- return result;
-}
-
-// 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
-// source line, function, or module is printed, preferring them in that
-// order. If no source line, function, or module information is available,
-// an absolute code offset is printed.
-//
-// 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) {
- int frame_count = stack->frames()->size();
- if (frame_count == 0) {
- printf(" <no frames>\n");
- }
- for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
- const StackFrame *frame = stack->frames()->at(frame_index);
- printf("%2d ", frame_index);
-
- uint64_t instruction_address = frame->ReturnAddress();
-
- if (frame->module) {
- printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
- if (!frame->function_name.empty()) {
- printf("!%s", frame->function_name.c_str());
- if (!frame->source_file_name.empty()) {
- string source_file = PathnameStripper::File(frame->source_file_name);
- printf(" [%s : %d + 0x%" PRIx64 "]",
- source_file.c_str(),
- frame->source_line,
- instruction_address - frame->source_line_base);
- } else {
- printf(" + 0x%" PRIx64, instruction_address - frame->function_base);
- }
- } else {
- printf(" + 0x%" PRIx64,
- instruction_address - frame->module->base_address());
- }
- } else {
- printf("0x%" PRIx64, instruction_address);
- }
- printf("\n ");
-
- int sequence = 0;
- if (cpu == "x86") {
- const StackFrameX86 *frame_x86 =
- reinterpret_cast<const StackFrameX86*>(frame);
-
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
- sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
- sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
- sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
- sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
- sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
- if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
- sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
- if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
- sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
- sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
- sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
- sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
- }
- } else if (cpu == "ppc") {
- const StackFramePPC *frame_ppc =
- reinterpret_cast<const StackFramePPC*>(frame);
-
- if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
- sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
- if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
- sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
- } else if (cpu == "amd64") {
- const StackFrameAMD64 *frame_amd64 =
- reinterpret_cast<const StackFrameAMD64*>(frame);
-
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX)
- sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX)
- sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX)
- sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
- sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI)
- sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI)
- sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
- sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
- sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8)
- sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9)
- sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10)
- sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11)
- sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
- sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
- sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
- sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
- sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
- if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
- sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
- } else if (cpu == "sparc") {
- const StackFrameSPARC *frame_sparc =
- reinterpret_cast<const StackFrameSPARC*>(frame);
-
- if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
- sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
- if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
- sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
- if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
- sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
- } else if (cpu == "arm") {
- const StackFrameARM *frame_arm =
- reinterpret_cast<const StackFrameARM*>(frame);
-
- // Argument registers (caller-saves), which will likely only be valid
- // for the youngest frame.
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
- sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
- sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
- sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
- sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
-
- // General-purpose callee-saves registers.
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
- sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
- sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
- sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
- sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
- sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
- sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
- sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12)
- sequence = PrintRegister("r12", frame_arm->context.iregs[12], sequence);
-
- // Registers with a dedicated or conventional purpose.
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
- sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
- sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
- sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
- if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
- sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
- } else if (cpu == "arm64") {
- const StackFrameARM64 *frame_arm64 =
- reinterpret_cast<const StackFrameARM64*>(frame);
-
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
- sequence =
- PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
- sequence =
- PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
- sequence =
- PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
- sequence =
- PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
- sequence =
- PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
- sequence =
- PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
- sequence =
- PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
- sequence =
- PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
- sequence =
- PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
- sequence =
- PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
- sequence =
- PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
- sequence =
- PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
- sequence =
- PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
- sequence =
- PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
- sequence =
- PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
- sequence =
- PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
- sequence =
- PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
- sequence =
- PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
- sequence =
- PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
- sequence =
- PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
- sequence =
- PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
- sequence =
- PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
- sequence =
- PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
- sequence =
- PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
- sequence =
- PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
- sequence =
- PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
- sequence =
- PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
- sequence =
- PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
- sequence =
- PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
- }
-
- // Registers with a dedicated or conventional purpose.
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
- sequence =
- PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
- sequence =
- PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
- sequence =
- PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
- }
- if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
- sequence =
- PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
- }
- } else if (cpu == "mips") {
- const StackFrameMIPS* frame_mips =
- reinterpret_cast<const StackFrameMIPS*>(frame);
-
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
- sequence = PrintRegister64("gp",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
- sequence = PrintRegister64("sp",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
- sequence = PrintRegister64("fp",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
- sequence = PrintRegister64("ra",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
- sequence = PrintRegister64("pc", frame_mips->context.epc, sequence);
-
- // Save registers s0-s7
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
- sequence = PrintRegister64("s0",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
- sequence = PrintRegister64("s1",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
- sequence = PrintRegister64("s2",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
- sequence = PrintRegister64("s3",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
- sequence = PrintRegister64("s4",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
- sequence = PrintRegister64("s5",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
- sequence = PrintRegister64("s6",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
- sequence);
- if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
- sequence = PrintRegister64("s7",
- frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
- sequence);
- }
- printf("\n Found by: %s\n", frame->trust_description().c_str());
- }
-}
-
-// PrintStackMachineReadable prints the call stack in |stack| to stdout,
-// in the following machine readable pipe-delimited text format:
-// thread number|frame number|module|function|source file|line|offset
-//
-// Module, function, source file, and source line may all be empty
-// depending on availability. The code offset follows the same rules as
-// PrintStack above.
-static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
- int frame_count = stack->frames()->size();
- for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
- const StackFrame *frame = stack->frames()->at(frame_index);
- printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
- kOutputSeparator);
-
- uint64_t instruction_address = frame->ReturnAddress();
-
- if (frame->module) {
- assert(!frame->module->code_file().empty());
- printf("%s", StripSeparator(PathnameStripper::File(
- frame->module->code_file())).c_str());
- if (!frame->function_name.empty()) {
- printf("%c%s", kOutputSeparator,
- StripSeparator(frame->function_name).c_str());
- if (!frame->source_file_name.empty()) {
- printf("%c%s%c%d%c0x%" PRIx64,
- kOutputSeparator,
- StripSeparator(frame->source_file_name).c_str(),
- kOutputSeparator,
- frame->source_line,
- kOutputSeparator,
- instruction_address - frame->source_line_base);
- } else {
- printf("%c%c%c0x%" PRIx64,
- kOutputSeparator, // empty source file
- kOutputSeparator, // empty source line
- kOutputSeparator,
- instruction_address - frame->function_base);
- }
- } else {
- printf("%c%c%c%c0x%" PRIx64,
- kOutputSeparator, // empty function name
- kOutputSeparator, // empty source file
- kOutputSeparator, // empty source line
- kOutputSeparator,
- instruction_address - frame->module->base_address());
- }
- } else {
- // the printf before this prints a trailing separator for module name
- printf("%c%c%c%c0x%" PRIx64,
- kOutputSeparator, // empty function name
- kOutputSeparator, // empty source file
- kOutputSeparator, // empty source line
- kOutputSeparator,
- instruction_address);
- }
- printf("\n");
- }
-}
-
-// ContainsModule checks whether a given |module| is in the vector
-// |modules_without_symbols|.
-static bool ContainsModule(
- const vector<const CodeModule*> *modules,
- const CodeModule *module) {
- assert(modules);
- assert(module);
- vector<const CodeModule*>::const_iterator iter;
- for (iter = modules->begin(); iter != modules->end(); ++iter) {
- if (module->debug_file().compare((*iter)->debug_file()) == 0 &&
- module->debug_identifier().compare((*iter)->debug_identifier()) == 0) {
- return true;
- }
- }
- return false;
-}
-
-// PrintModule prints a single |module| to stdout.
-// |modules_without_symbols| should contain the list of modules that were
-// confirmed to be missing their symbols during the stack walk.
-static void PrintModule(
- const CodeModule *module,
- const vector<const CodeModule*> *modules_without_symbols,
- const vector<const CodeModule*> *modules_with_corrupt_symbols,
- uint64_t main_address) {
- string symbol_issues;
- if (ContainsModule(modules_without_symbols, module)) {
- symbol_issues = " (WARNING: No symbols, " +
- PathnameStripper::File(module->debug_file()) + ", " +
- module->debug_identifier() + ")";
- } else if (ContainsModule(modules_with_corrupt_symbols, module)) {
- symbol_issues = " (WARNING: Corrupt symbols, " +
- PathnameStripper::File(module->debug_file()) + ", " +
- module->debug_identifier() + ")";
- }
- uint64_t base_address = module->base_address();
- printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
- base_address, base_address + module->size() - 1,
- PathnameStripper::File(module->code_file()).c_str(),
- module->version().empty() ? "???" : module->version().c_str(),
- main_address != 0 && base_address == main_address ? " (main)" : "",
- symbol_issues.c_str());
-}
-
-// PrintModules prints the list of all loaded |modules| to stdout.
-// |modules_without_symbols| should contain the list of modules that were
-// confirmed to be missing their symbols during the stack walk.
-static void PrintModules(
- const CodeModules *modules,
- const vector<const CodeModule*> *modules_without_symbols,
- const vector<const CodeModule*> *modules_with_corrupt_symbols) {
- if (!modules)
- return;
-
- printf("\n");
- printf("Loaded modules:\n");
-
- uint64_t main_address = 0;
- const CodeModule *main_module = modules->GetMainModule();
- if (main_module) {
- main_address = main_module->base_address();
- }
-
- unsigned int module_count = modules->module_count();
- for (unsigned int module_sequence = 0;
- module_sequence < module_count;
- ++module_sequence) {
- const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
- PrintModule(module, modules_without_symbols, modules_with_corrupt_symbols,
- main_address);
- }
-}
-
-// PrintModulesMachineReadable outputs a list of loaded modules,
-// one per line, in the following machine-readable pipe-delimited
-// text format:
-// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
-// {Base Address}|{Max Address}|{Main}
-static void PrintModulesMachineReadable(const CodeModules *modules) {
- if (!modules)
- return;
-
- uint64_t main_address = 0;
- const CodeModule *main_module = modules->GetMainModule();
- if (main_module) {
- main_address = main_module->base_address();
- }
-
- unsigned int module_count = modules->module_count();
- for (unsigned int module_sequence = 0;
- module_sequence < module_count;
- ++module_sequence) {
- const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
- uint64_t base_address = module->base_address();
- printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
- kOutputSeparator,
- StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
- kOutputSeparator, StripSeparator(module->version()).c_str(),
- kOutputSeparator,
- StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
- kOutputSeparator,
- StripSeparator(module->debug_identifier()).c_str(),
- kOutputSeparator, base_address,
- kOutputSeparator, base_address + module->size() - 1,
- kOutputSeparator,
- main_module != NULL && base_address == main_address ? 1 : 0);
- }
-}
-
-static void PrintProcessState(const ProcessState& process_state) {
- // Print OS and CPU information.
- string cpu = process_state.system_info()->cpu;
- string cpu_info = process_state.system_info()->cpu_info;
- printf("Operating system: %s\n", process_state.system_info()->os.c_str());
- printf(" %s\n",
- process_state.system_info()->os_version.c_str());
- printf("CPU: %s\n", cpu.c_str());
- if (!cpu_info.empty()) {
- // This field is optional.
- printf(" %s\n", cpu_info.c_str());
- }
- printf(" %d CPU%s\n",
- process_state.system_info()->cpu_count,
- process_state.system_info()->cpu_count != 1 ? "s" : "");
- printf("\n");
-
- // Print crash information.
- if (process_state.crashed()) {
- printf("Crash reason: %s\n", process_state.crash_reason().c_str());
- printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
- } else {
- 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) {
- printf("\n");
- printf("Thread %d (%s)\n",
- requesting_thread,
- process_state.crashed() ? "crashed" :
- "requested dump, did not crash");
- PrintStack(process_state.threads()->at(requesting_thread), cpu);
- }
-
- // Print all of the threads in the dump.
- int thread_count = process_state.threads()->size();
- for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
- if (thread_index != requesting_thread) {
- // 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);
- }
- }
-
- PrintModules(process_state.modules(),
- process_state.modules_without_symbols(),
- process_state.modules_with_corrupt_symbols());
-}
-
-static void PrintProcessStateMachineReadable(const ProcessState& process_state)
-{
- // Print OS and CPU information.
- // OS|{OS Name}|{OS Version}
- // CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
- printf("OS%c%s%c%s\n", kOutputSeparator,
- StripSeparator(process_state.system_info()->os).c_str(),
- kOutputSeparator,
- StripSeparator(process_state.system_info()->os_version).c_str());
- printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
- StripSeparator(process_state.system_info()->cpu).c_str(),
- kOutputSeparator,
- // this may be empty
- StripSeparator(process_state.system_info()->cpu_info).c_str(),
- kOutputSeparator,
- process_state.system_info()->cpu_count);
-
- int requesting_thread = process_state.requesting_thread();
-
- // Print crash information.
- // Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
- printf("Crash%c", kOutputSeparator);
- if (process_state.crashed()) {
- printf("%s%c0x%" PRIx64 "%c",
- StripSeparator(process_state.crash_reason()).c_str(),
- 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 {
- printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
- }
- }
-
- if (requesting_thread != -1) {
- printf("%d\n", requesting_thread);
- } else {
- printf("\n");
- }
-
- PrintModulesMachineReadable(process_state.modules());
-
- // blank line to indicate start of threads
- printf("\n");
-
- // If the thread that requested the dump is known, print it first.
- if (requesting_thread != -1) {
- PrintStackMachineReadable(requesting_thread,
- process_state.threads()->at(requesting_thread));
- }
-
- // Print all of the threads in the dump.
- int thread_count = process_state.threads()->size();
- for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
- if (thread_index != requesting_thread) {
- // Don't print the crash thread again, it was already printed.
- PrintStackMachineReadable(thread_index,
- process_state.threads()->at(thread_index));
- }
- }
-}
+using google_breakpad::scoped_ptr;
// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
// non-empty, is the base directory of a symbol storage area, laid out in
@@ -791,9 +66,9 @@ static void PrintProcessStateMachineReadable(const ProcessState& process_state)
// information if the minidump was produced as a result of a crash, and
// call stacks for each thread contained in the minidump. All information
// is printed to stdout.
-static bool PrintMinidumpProcess(const string &minidump_file,
- const vector<string> &symbol_paths,
- bool machine_readable) {
+bool PrintMinidumpProcess(const string &minidump_file,
+ const std::vector<string> &symbol_paths,
+ bool machine_readable) {
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
if (!symbol_paths.empty()) {
// TODO(mmentovai): check existence of symbol_path if specified?
@@ -820,14 +95,14 @@ static bool PrintMinidumpProcess(const string &minidump_file,
return true;
}
-} // namespace
-
-static void usage(const char *program_name) {
+void usage(const char *program_name) {
fprintf(stderr, "usage: %s [-m] <minidump-file> [symbol-path ...]\n"
" -m : Output in machine-readable format\n",
program_name);
}
+} // namespace
+
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc
new file mode 100644
index 00000000..72398700
--- /dev/null
+++ b/src/processor/stackwalk_common.cc
@@ -0,0 +1,767 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalk_common.cc: Module shared by the {micro,mini}dump_stackwalck
+// executables to print the content of dumps (w/ stack traces) on the console.
+//
+// Author: Mark Mentovai
+
+#include "processor/stackwalk_common.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "common/using_std_string.h"
+#include "google_breakpad/processor/call_stack.h"
+#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/stack_frame_cpu.h"
+#include "processor/logging.h"
+#include "processor/pathname_stripper.h"
+
+namespace google_breakpad {
+
+namespace {
+
+using std::vector;
+
+// Separator character for machine readable output.
+static const char kOutputSeparator = '|';
+
+// PrintRegister prints a register's name and value to stdout. It will
+// print four registers on a line. For the first register in a set,
+// pass 0 for |start_col|. For registers in a set, pass the most recent
+// return value of PrintRegister.
+// The caller is responsible for printing the final newline after a set
+// of registers is completely printed, regardless of the number of calls
+// to PrintRegister.
+static const int kMaxWidth = 80; // optimize for an 80-column terminal
+static int PrintRegister(const char *name, uint32_t value, int start_col) {
+ char buffer[64];
+ snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value);
+
+ if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
+ start_col = 0;
+ printf("\n ");
+ }
+ fputs(buffer, stdout);
+
+ return start_col + strlen(buffer);
+}
+
+// PrintRegister64 does the same thing, but for 64-bit registers.
+static int PrintRegister64(const char *name, uint64_t value, int start_col) {
+ char buffer[64];
+ snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value);
+
+ if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
+ start_col = 0;
+ printf("\n ");
+ }
+ fputs(buffer, stdout);
+
+ return start_col + strlen(buffer);
+}
+
+// StripSeparator takes a string |original| and returns a copy
+// of the string with all occurences of |kOutputSeparator| removed.
+static string StripSeparator(const string &original) {
+ string result = original;
+ string::size_type position = 0;
+ while ((position = result.find(kOutputSeparator, position)) != string::npos) {
+ result.erase(position, 1);
+ }
+ position = 0;
+ while ((position = result.find('\n', position)) != string::npos) {
+ result.erase(position, 1);
+ }
+ return result;
+}
+
+// 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
+// source line, function, or module is printed, preferring them in that
+// order. If no source line, function, or module information is available,
+// an absolute code offset is printed.
+//
+// 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) {
+ int frame_count = stack->frames()->size();
+ if (frame_count == 0) {
+ printf(" <no frames>\n");
+ }
+ for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
+ const StackFrame *frame = stack->frames()->at(frame_index);
+ printf("%2d ", frame_index);
+
+ uint64_t instruction_address = frame->ReturnAddress();
+
+ if (frame->module) {
+ printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
+ if (!frame->function_name.empty()) {
+ printf("!%s", frame->function_name.c_str());
+ if (!frame->source_file_name.empty()) {
+ string source_file = PathnameStripper::File(frame->source_file_name);
+ printf(" [%s : %d + 0x%" PRIx64 "]",
+ source_file.c_str(),
+ frame->source_line,
+ instruction_address - frame->source_line_base);
+ } else {
+ printf(" + 0x%" PRIx64, instruction_address - frame->function_base);
+ }
+ } else {
+ printf(" + 0x%" PRIx64,
+ instruction_address - frame->module->base_address());
+ }
+ } else {
+ printf("0x%" PRIx64, instruction_address);
+ }
+ printf("\n ");
+
+ int sequence = 0;
+ if (cpu == "x86") {
+ const StackFrameX86 *frame_x86 =
+ reinterpret_cast<const StackFrameX86*>(frame);
+
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
+ sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
+ sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
+ sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
+ sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
+ sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
+ if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
+ sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
+ if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
+ sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
+ sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
+ sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
+ sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
+ }
+ } else if (cpu == "ppc") {
+ const StackFramePPC *frame_ppc =
+ reinterpret_cast<const StackFramePPC*>(frame);
+
+ if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
+ sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
+ if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
+ sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
+ } else if (cpu == "amd64") {
+ const StackFrameAMD64 *frame_amd64 =
+ reinterpret_cast<const StackFrameAMD64*>(frame);
+
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX)
+ sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX)
+ sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX)
+ sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
+ sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI)
+ sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI)
+ sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
+ sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
+ sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8)
+ sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9)
+ sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10)
+ sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11)
+ sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
+ sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
+ sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
+ sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
+ sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
+ if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
+ sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
+ } else if (cpu == "sparc") {
+ const StackFrameSPARC *frame_sparc =
+ reinterpret_cast<const StackFrameSPARC*>(frame);
+
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
+ sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
+ sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
+ if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
+ sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
+ } else if (cpu == "arm") {
+ const StackFrameARM *frame_arm =
+ reinterpret_cast<const StackFrameARM*>(frame);
+
+ // Argument registers (caller-saves), which will likely only be valid
+ // for the youngest frame.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
+ sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
+ sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
+ sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
+ sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
+
+ // General-purpose callee-saves registers.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
+ sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
+ sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
+ sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
+ sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
+ sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
+ sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
+ sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12)
+ sequence = PrintRegister("r12", frame_arm->context.iregs[12], sequence);
+
+ // Registers with a dedicated or conventional purpose.
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
+ sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
+ sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
+ sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
+ if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
+ sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
+ } else if (cpu == "arm64") {
+ const StackFrameARM64 *frame_arm64 =
+ reinterpret_cast<const StackFrameARM64*>(frame);
+
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
+ sequence =
+ PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
+ sequence =
+ PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
+ sequence =
+ PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
+ sequence =
+ PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
+ sequence =
+ PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
+ sequence =
+ PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
+ sequence =
+ PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
+ sequence =
+ PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
+ sequence =
+ PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
+ sequence =
+ PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
+ sequence =
+ PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
+ sequence =
+ PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
+ sequence =
+ PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
+ sequence =
+ PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
+ sequence =
+ PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
+ sequence =
+ PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
+ sequence =
+ PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
+ sequence =
+ PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
+ sequence =
+ PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
+ sequence =
+ PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
+ sequence =
+ PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
+ sequence =
+ PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
+ sequence =
+ PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
+ sequence =
+ PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
+ sequence =
+ PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
+ sequence =
+ PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
+ sequence =
+ PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
+ sequence =
+ PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
+ sequence =
+ PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
+ }
+
+ // Registers with a dedicated or conventional purpose.
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
+ sequence =
+ PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
+ sequence =
+ PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
+ sequence =
+ PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
+ }
+ if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
+ sequence =
+ PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
+ }
+ } else if (cpu == "mips") {
+ const StackFrameMIPS* frame_mips =
+ reinterpret_cast<const StackFrameMIPS*>(frame);
+
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
+ sequence = PrintRegister64("gp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
+ sequence = PrintRegister64("sp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
+ sequence = PrintRegister64("fp",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
+ sequence = PrintRegister64("ra",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
+ sequence = PrintRegister64("pc", frame_mips->context.epc, sequence);
+
+ // Save registers s0-s7
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
+ sequence = PrintRegister64("s0",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
+ sequence = PrintRegister64("s1",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
+ sequence = PrintRegister64("s2",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
+ sequence = PrintRegister64("s3",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
+ sequence = PrintRegister64("s4",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
+ sequence = PrintRegister64("s5",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
+ sequence = PrintRegister64("s6",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
+ sequence);
+ if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
+ sequence = PrintRegister64("s7",
+ frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
+ sequence);
+ }
+ printf("\n Found by: %s\n", frame->trust_description().c_str());
+ }
+}
+
+// PrintStackMachineReadable prints the call stack in |stack| to stdout,
+// in the following machine readable pipe-delimited text format:
+// thread number|frame number|module|function|source file|line|offset
+//
+// Module, function, source file, and source line may all be empty
+// depending on availability. The code offset follows the same rules as
+// PrintStack above.
+static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
+ int frame_count = stack->frames()->size();
+ for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
+ const StackFrame *frame = stack->frames()->at(frame_index);
+ printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
+ kOutputSeparator);
+
+ uint64_t instruction_address = frame->ReturnAddress();
+
+ if (frame->module) {
+ assert(!frame->module->code_file().empty());
+ printf("%s", StripSeparator(PathnameStripper::File(
+ frame->module->code_file())).c_str());
+ if (!frame->function_name.empty()) {
+ printf("%c%s", kOutputSeparator,
+ StripSeparator(frame->function_name).c_str());
+ if (!frame->source_file_name.empty()) {
+ printf("%c%s%c%d%c0x%" PRIx64,
+ kOutputSeparator,
+ StripSeparator(frame->source_file_name).c_str(),
+ kOutputSeparator,
+ frame->source_line,
+ kOutputSeparator,
+ instruction_address - frame->source_line_base);
+ } else {
+ printf("%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address - frame->function_base);
+ }
+ } else {
+ printf("%c%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address - frame->module->base_address());
+ }
+ } else {
+ // the printf before this prints a trailing separator for module name
+ printf("%c%c%c%c0x%" PRIx64,
+ kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ instruction_address);
+ }
+ printf("\n");
+ }
+}
+
+// ContainsModule checks whether a given |module| is in the vector
+// |modules_without_symbols|.
+static bool ContainsModule(
+ const vector<const CodeModule*> *modules,
+ const CodeModule *module) {
+ assert(modules);
+ assert(module);
+ vector<const CodeModule*>::const_iterator iter;
+ for (iter = modules->begin(); iter != modules->end(); ++iter) {
+ if (module->debug_file().compare((*iter)->debug_file()) == 0 &&
+ module->debug_identifier().compare((*iter)->debug_identifier()) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// PrintModule prints a single |module| to stdout.
+// |modules_without_symbols| should contain the list of modules that were
+// confirmed to be missing their symbols during the stack walk.
+static void PrintModule(
+ const CodeModule *module,
+ const vector<const CodeModule*> *modules_without_symbols,
+ const vector<const CodeModule*> *modules_with_corrupt_symbols,
+ uint64_t main_address) {
+ string symbol_issues;
+ if (ContainsModule(modules_without_symbols, module)) {
+ symbol_issues = " (WARNING: No symbols, " +
+ PathnameStripper::File(module->debug_file()) + ", " +
+ module->debug_identifier() + ")";
+ } else if (ContainsModule(modules_with_corrupt_symbols, module)) {
+ symbol_issues = " (WARNING: Corrupt symbols, " +
+ PathnameStripper::File(module->debug_file()) + ", " +
+ module->debug_identifier() + ")";
+ }
+ uint64_t base_address = module->base_address();
+ printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
+ base_address, base_address + module->size() - 1,
+ PathnameStripper::File(module->code_file()).c_str(),
+ module->version().empty() ? "???" : module->version().c_str(),
+ main_address != 0 && base_address == main_address ? " (main)" : "",
+ symbol_issues.c_str());
+}
+
+// PrintModules prints the list of all loaded |modules| to stdout.
+// |modules_without_symbols| should contain the list of modules that were
+// confirmed to be missing their symbols during the stack walk.
+static void PrintModules(
+ const CodeModules *modules,
+ const vector<const CodeModule*> *modules_without_symbols,
+ const vector<const CodeModule*> *modules_with_corrupt_symbols) {
+ if (!modules)
+ return;
+
+ printf("\n");
+ printf("Loaded modules:\n");
+
+ uint64_t main_address = 0;
+ const CodeModule *main_module = modules->GetMainModule();
+ if (main_module) {
+ main_address = main_module->base_address();
+ }
+
+ unsigned int module_count = modules->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < module_count;
+ ++module_sequence) {
+ const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
+ PrintModule(module, modules_without_symbols, modules_with_corrupt_symbols,
+ main_address);
+ }
+}
+
+// PrintModulesMachineReadable outputs a list of loaded modules,
+// one per line, in the following machine-readable pipe-delimited
+// text format:
+// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
+// {Base Address}|{Max Address}|{Main}
+static void PrintModulesMachineReadable(const CodeModules *modules) {
+ if (!modules)
+ return;
+
+ uint64_t main_address = 0;
+ const CodeModule *main_module = modules->GetMainModule();
+ if (main_module) {
+ main_address = main_module->base_address();
+ }
+
+ unsigned int module_count = modules->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < module_count;
+ ++module_sequence) {
+ const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
+ uint64_t base_address = module->base_address();
+ printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
+ kOutputSeparator,
+ StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
+ kOutputSeparator, StripSeparator(module->version()).c_str(),
+ kOutputSeparator,
+ StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
+ kOutputSeparator,
+ StripSeparator(module->debug_identifier()).c_str(),
+ kOutputSeparator, base_address,
+ kOutputSeparator, base_address + module->size() - 1,
+ kOutputSeparator,
+ main_module != NULL && base_address == main_address ? 1 : 0);
+ }
+}
+
+} // namespace
+
+void PrintProcessState(const ProcessState& process_state) {
+ // Print OS and CPU information.
+ string cpu = process_state.system_info()->cpu;
+ string cpu_info = process_state.system_info()->cpu_info;
+ printf("Operating system: %s\n", process_state.system_info()->os.c_str());
+ printf(" %s\n",
+ process_state.system_info()->os_version.c_str());
+ printf("CPU: %s\n", cpu.c_str());
+ if (!cpu_info.empty()) {
+ // This field is optional.
+ printf(" %s\n", cpu_info.c_str());
+ }
+ printf(" %d CPU%s\n",
+ process_state.system_info()->cpu_count,
+ process_state.system_info()->cpu_count != 1 ? "s" : "");
+ printf("\n");
+
+ // Print crash information.
+ if (process_state.crashed()) {
+ printf("Crash reason: %s\n", process_state.crash_reason().c_str());
+ printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
+ } else {
+ 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) {
+ printf("\n");
+ printf("Thread %d (%s)\n",
+ requesting_thread,
+ process_state.crashed() ? "crashed" :
+ "requested dump, did not crash");
+ PrintStack(process_state.threads()->at(requesting_thread), cpu);
+ }
+
+ // Print all of the threads in the dump.
+ int thread_count = process_state.threads()->size();
+ for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
+ if (thread_index != requesting_thread) {
+ // 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);
+ }
+ }
+
+ PrintModules(process_state.modules(),
+ process_state.modules_without_symbols(),
+ process_state.modules_with_corrupt_symbols());
+}
+
+void PrintProcessStateMachineReadable(const ProcessState& process_state) {
+ // Print OS and CPU information.
+ // OS|{OS Name}|{OS Version}
+ // CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
+ printf("OS%c%s%c%s\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->os).c_str(),
+ kOutputSeparator,
+ StripSeparator(process_state.system_info()->os_version).c_str());
+ printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->cpu).c_str(),
+ kOutputSeparator,
+ // this may be empty
+ StripSeparator(process_state.system_info()->cpu_info).c_str(),
+ kOutputSeparator,
+ process_state.system_info()->cpu_count);
+
+ int requesting_thread = process_state.requesting_thread();
+
+ // Print crash information.
+ // Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
+ printf("Crash%c", kOutputSeparator);
+ if (process_state.crashed()) {
+ printf("%s%c0x%" PRIx64 "%c",
+ StripSeparator(process_state.crash_reason()).c_str(),
+ 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 {
+ printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
+ }
+ }
+
+ if (requesting_thread != -1) {
+ printf("%d\n", requesting_thread);
+ } else {
+ printf("\n");
+ }
+
+ PrintModulesMachineReadable(process_state.modules());
+
+ // blank line to indicate start of threads
+ printf("\n");
+
+ // If the thread that requested the dump is known, print it first.
+ if (requesting_thread != -1) {
+ PrintStackMachineReadable(requesting_thread,
+ process_state.threads()->at(requesting_thread));
+ }
+
+ // Print all of the threads in the dump.
+ int thread_count = process_state.threads()->size();
+ for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
+ if (thread_index != requesting_thread) {
+ // Don't print the crash thread again, it was already printed.
+ PrintStackMachineReadable(thread_index,
+ process_state.threads()->at(thread_index));
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/src/processor/stackwalk_common.h b/src/processor/stackwalk_common.h
new file mode 100644
index 00000000..7ee6e75b
--- /dev/null
+++ b/src/processor/stackwalk_common.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalk_common.cc: Module shared by the {micro,mini}dump_stackwalck
+// executables to print the content of dumps (w/ stack traces) on the console.
+
+
+#ifndef PROCESSOR_STACKWALK_COMMON_H__
+#define PROCESSOR_STACKWALK_COMMON_H__
+
+namespace google_breakpad {
+
+class ProcessState;
+
+void PrintProcessStateMachineReadable(const ProcessState& process_state);
+void PrintProcessState(const ProcessState& process_state);
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STACKWALK_COMMON_H__
diff --git a/src/processor/testdata/microdump.stackwalk.machine_readable.out b/src/processor/testdata/microdump.stackwalk.machine_readable.out
new file mode 100644
index 00000000..4a62a9dc
--- /dev/null
+++ b/src/processor/testdata/microdump.stackwalk.machine_readable.out
@@ -0,0 +1,129 @@
+OS||
+CPU|||0
+Crash||0x0|0
+Module|libchromeshell.so||libchromeshell.so|76304586D0CD2C8FF899C602BF1756A20|0x987ab000|0x9b359fff|0
+Module|RELRO:libchromeshell.so||RELRO:libchromeshell.so|000000000000000000000000000000000|0x9b35a000|0x9b4b5fff|0
+Module|libchromeshell.so||libchromeshell.so|000000000000000000000000000000000|0x9b4b6000|0x9b4cbfff|0
+Module|libchromium_android_linker.so||libchromium_android_linker.so|AC480D966558562B46D4583A55D904E60|0xa47a3000|0xa47b3fff|0
+Module|libwebviewchromium_loader.so||libwebviewchromium_loader.so|932CC9935B065A05D39E38681E0A5F2E0|0xac35d000|0xac35ffff|0
+Module|libandroid.so||libandroid.so|E2C6CBE7D4588EF8111B01903B27FEA80|0xac360000|0xac36ffff|0
+Module|libGLESv2_adreno.so||libGLESv2_adreno.so|83D249BAACD2F2C10C13B70C1F64252C0|0xac370000|0xac4b1fff|0
+Module|libGLESv1_CM_adreno.so||libGLESv1_CM_adreno.so|58A32EAAFDADB17139F988E96B860C8E0|0xac4b3000|0xac4e6fff|0
+Module|libgsl.so||libgsl.so|C71008FE91C209D6D5E9EA5DBB44BF080|0xac4e7000|0xac51afff|0
+Module|libEGL_adreno.so||libEGL_adreno.so|134B50AAA945C8C16B0021DF014AA6150|0xac51b000|0xac544fff|0
+Module|libjavacrypto.so||libjavacrypto.so|9D372357993C7650365479A34E676E290|0xace37000|0xace4efff|0
+Module|librs_jni.so||librs_jni.so|0D2147262F4305E3AED211CB96FC96BA0|0xace4f000|0xace57fff|0
+Module|libaudioeffect_jni.so||libaudioeffect_jni.so|3F276EEAE72C2C9FC768FF2E482957510|0xace58000|0xace5dfff|0
+Module|libsoundpool.so||libsoundpool.so|CDA9BA072D4DECC71C5E63467275E7EE0|0xace5e000|0xace61fff|0
+Module|libstagefright_amrnb_common.so||libstagefright_amrnb_common.so|D02CB251CF8787A770DE2CFDC52A2B040|0xace62000|0xace6ffff|0
+Module|libvorbisidec.so||libvorbisidec.so|F59669C665FE4B073886A8DAAECEA86F0|0xace70000|0xace8afff|0
+Module|libstagefright_yuv.so||libstagefright_yuv.so|9FC00D0B150FE9FC57763A76206D1D550|0xace8b000|0xace8efff|0
+Module|libstagefright_omx.so||libstagefright_omx.so|C2D6408786A8582A0EAE64D047577F800|0xace90000|0xaceaefff|0
+Module|libstagefright_enc_common.so||libstagefright_enc_common.so|E11DA546CE9E08D1D647E8E1135DAD810|0xaceaf000|0xaceb1fff|0
+Module|libstagefright_avc_common.so||libstagefright_avc_common.so|741CC494F5299870A1C62FD71E37AE5F0|0xaceb2000|0xaceb8fff|0
+Module|libpowermanager.so||libpowermanager.so|D3EA22EB9F383751AB500AE5951F0DB30|0xaceb9000|0xacebdfff|0
+Module|libopus.so||libopus.so|F19A7DFF3B6F3AF94EAE23CA21905BAD0|0xacebe000|0xacef9fff|0
+Module|libdrmframework.so||libdrmframework.so|87AFCC5E47ED503148F1AA03777E88590|0xacefa000|0xacf14fff|0
+Module|libstagefright.so||libstagefright.so|A0D663218D972496B104B7B9FF0727BD0|0xacf16000|0xad029fff|0
+Module|libmtp.so||libmtp.so|55B5F5E024DB37CABEC6A8F31DD950870|0xad02a000|0xad03ffff|0
+Module|libjhead.so||libjhead.so|D2AB743CCCD8D2EB9B66A47707CC1E530|0xad040000|0xad04afff|0
+Module|libexif.so||libexif.so|4D1E2F0F58012FABA091A21E4CEEFFE20|0xad04c000|0xad077fff|0
+Module|libmedia_jni.so||libmedia_jni.so|20D19E07A0CA5B5790EEB83C8F6915570|0xad078000|0xad0b3fff|0
+Module|libjnigraphics.so||libjnigraphics.so|8FF5949AE957364C270D0F448DAD4FE20|0xafe8a000|0xafe8cfff|0
+Module|libcompiler_rt.so||libcompiler_rt.so|F2B1298EE4C6EA0900CDACD17FB5C16B0|0xafe8d000|0xafe94fff|0
+Module|libadreno_utils.so||libadreno_utils.so|37FA52E92BF716E0268B9619EE8D61630|0xafe95000|0xafe98fff|0
+Module|memtrack.msm8960.so||memtrack.msm8960.so|F36630BD0C6DC4B52A0E5D5D57BE682A0|0xafe9b000|0xafe9dfff|0
+Module|libjavacore.so||libjavacore.so|FE4EB304B0639D600DFB5871136831C50|0xb15ab000|0xb15e2fff|0
+Module|libbacktrace_libc++.so||libbacktrace_libc++.so|CF0326786BDECFB45A519C7005E851000|0xb4fc8000|0xb4fd0fff|0
+Module|libart.so||libart.so|3E343A5B3F9534F5E40AD9BEAC46C8E80|0xb4fd2000|0xb52d5fff|0
+Module|libusbhost.so||libusbhost.so|3CBDF0DE27B9554508AD60FDC96CBC620|0xb52f9000|0xb52fcfff|0
+Module|libssl.so||libssl.so|36F280D15D51F4EEFA92279E8BBD84360|0xb52fd000|0xb533dfff|0
+Module|libsqlite.so||libsqlite.so|D02DDA779C053EF3CC279A1E1789C4250|0xb533e000|0xb539cfff|0
+Module|libsoundtrigger.so||libsoundtrigger.so|F954BA248E12C9AF32F54434F977FEF80|0xb539e000|0xb53acfff|0
+Module|libselinux.so||libselinux.so|125671BDF56FEE67902BE338575373000|0xb53ad000|0xb53bafff|0
+Module|libprocessgroup.so||libprocessgroup.so|4E6C8C876BA563C3C4B0B3BA562093920|0xb53bb000|0xb53befff|0
+Module|libpdfium.so||libpdfium.so|83C5B450634DDB5C4FC41CA61A35B3740|0xb53bf000|0xb5819fff|0
+Module|libnetd_client.so||libnetd_client.so|56B149396A4DAF176E26B4A85DA87BF30|0xb581f000|0xb5822fff|0
+Module|libnativehelper.so||libnativehelper.so|A20D742D5BF711D12587563B5C4BF64A0|0xb5823000|0xb5829fff|0
+Module|libnativebridge.so||libnativebridge.so|495F8887F27909EE1E3E69A657E0A9AB0|0xb582a000|0xb582dfff|0
+Module|libminikin.so||libminikin.so|31B45FE1FA6CC789F945332C6FECF9750|0xb582e000|0xb5839fff|0
+Module|libmemtrack.so||libmemtrack.so|CCA8BE0D07D24523C8D02FEE5F724EA70|0xb583a000|0xb583cfff|0
+Module|libstagefright_foundation.so||libstagefright_foundation.so|60A6E0B998632198B80EB0941121CD710|0xb583d000|0xb5851fff|0
+Module|libsonivox.so||libsonivox.so|6E42AB0836D73C21533C08A98EE7B24C0|0xb5852000|0xb58a2fff|0
+Module|libcommon_time_client.so||libcommon_time_client.so|E43E7FA869E4BCDAA3CC9601DF5A6A520|0xb58a8000|0xb58b6fff|0
+Module|libnbaio.so||libnbaio.so|C84019C90BCA7E95A773B5A75A460D880|0xb58b7000|0xb58c0fff|0
+Module|libmedia.so||libmedia.so|895A37F76D93E51B0ED7E90E5B71A9330|0xb58c1000|0xb595afff|0
+Module|libinputflinger.so||libinputflinger.so|C5323479053FDA41E355482925A30D120|0xb595b000|0xb5996fff|0
+Module|libinput.so||libinput.so|74F168649BE8583159D23A6406D107740|0xb5997000|0xb59b1fff|0
+Module|libimg_utils.so||libimg_utils.so|41712F8718C430707F8CEE5532CB59200|0xb59b2000|0xb59c0fff|0
+Module|libjpeg.so||libjpeg.so|89A4F06810290EEAF309C12642A3ECFA0|0xb59c1000|0xb59f2fff|0
+Module|libskia.so||libskia.so|65E6AAFD915FB244BFE6BE6C4E5E3EC40|0xb59f4000|0xb5c22fff|0
+Module|libRScpp.so||libRScpp.so|23FBB5490C5DBF1E99CD76AC2AA151410|0xb5c28000|0xb5c44fff|0
+Module|libpng.so||libpng.so|3BFE44EFE288C7A1AD7BE317B9F1BDBC0|0xb5c45000|0xb5c6cfff|0
+Module|libft2.so||libft2.so|659F1470013A04F7702E4BAB65F034E70|0xb5c6d000|0xb5cc6fff|0
+Module|libbcinfo.so||libbcinfo.so|FB93884C5EC21F71C68861C20993EB6C0|0xb5cc7000|0xb5d03fff|0
+Module|libbcc.so||libbcc.so|21B734672A14ED6267B966640C16823E0|0xb5d04000|0xb5d26fff|0
+Module|libc++.so||libc++.so|BE6F28596E6CE20F5E49A9E0BC824DA00|0xb5d47000|0xb5ddafff|0
+Module|libLLVM.so||libLLVM.so|377A25164D6ACDC0F7A38F5E378EC8670|0xb5ddd000|0xb6714fff|0
+Module|libRS.so||libRS.so|8D7E15A890FAF52D874044C9F02C6D130|0xb671c000|0xb6755fff|0
+Module|libhwui.so||libhwui.so|670166E37528A7F183D1B05F52020ABD0|0xb6756000|0xb67a1fff|0
+Module|libicuuc.so||libicuuc.so|1B1FD653750DE88B86D7E83095EE37160|0xb67a2000|0xb68b1fff|0
+Module|libgabi++.so||libgabi++.so|F4F99E4A6E63BF8C8005D96B2BAC8CEB0|0xb68b6000|0xb68bbfff|0
+Module|libicui18n.so||libicui18n.so|237F53C12084A7F42405B410FDE8B4020|0xb68bc000|0xb6a22fff|0
+Module|libharfbuzz_ng.so||libharfbuzz_ng.so|AC5AE16EC01F4362C8E63DF66565090D0|0xb6a23000|0xb6a6afff|0
+Module|libwpa_client.so||libwpa_client.so|254351EC49125FE3A72F78EAB38381410|0xb6a6b000|0xb6a6ffff|0
+Module|libnetutils.so||libnetutils.so|F8BA9819E3B7FA0BE155985B6C09FC570|0xb6a70000|0xb6a76fff|0
+Module|libhardware_legacy.so||libhardware_legacy.so|C08391F010B5A32265D4D781325FFCEE0|0xb6a77000|0xb6a7cfff|0
+Module|libexpat.so||libexpat.so|B98E65710C415C83E972EBDC1EB52AC00|0xb6a7e000|0xb6a94fff|0
+Module|libcrypto.so||libcrypto.so|E26A420F0A5BCD2AC04E9B7B19F67C3D0|0xb6a95000|0xb6b96fff|0
+Module|libhardware.so||libhardware.so|914425D16565257955F7E1574360B71F0|0xb6b99000|0xb6b9bfff|0
+Module|libui.so||libui.so|4F3E1A188AE72585AF4278E4FA5266730|0xb6b9c000|0xb6ba7fff|0
+Module|libsync.so||libsync.so|2D9083CB8C22C02E1412DA13B1CA43AE0|0xb6ba8000|0xb6baafff|0
+Module|libgui.so||libgui.so|559E784386AC5E53A9D4D7F9916AA7F90|0xb6bab000|0xb6bf9fff|0
+Module|libcamera_metadata.so||libcamera_metadata.so|D8696CD80D9B725AFEA007DFCE2562E60|0xb6bfa000|0xb6c01fff|0
+Module|libcamera_client.so||libcamera_client.so|116B760482930C77445ABAC1A3276D0B0|0xb6c02000|0xb6c3bfff|0
+Module|libspeexresampler.so||libspeexresampler.so|AC1D054A26491BE96E8BCCB1E5F2926F0|0xb6c3c000|0xb6c41fff|0
+Module|libaudioutils.so||libaudioutils.so|D330B25521C028B95D7E9360F7C6D6510|0xb6c42000|0xb6c47fff|0
+Module|libz.so||libz.so|03AD92B0BEDAA751C0016E97AE374CAE0|0xb6c48000|0xb6c61fff|0
+Module|libbinder.so||libbinder.so|AC6C388A36224541CD74B35818111B760|0xb6c62000|0xb6c91fff|0
+Module|libandroidfw.so||libandroidfw.so|102764B5F4D4D6F6F1A0C7360182BDEE0|0xb6c92000|0xb6cb9fff|0
+Module|libGLESv2.so||libGLESv2.so|E2F67EE50006FD0ED1482E1463C5CCFF0|0xb6cba000|0xb6cc4fff|0
+Module|libGLESv1_CM.so||libGLESv1_CM.so|EFE6AB19F4060BCECF8CF0E68958C2F60|0xb6cc5000|0xb6ccbfff|0
+Module|libETC1.so||libETC1.so|C91EAF69D18F0D499BD58532BBA173690|0xb6ccc000|0xb6ccffff|0
+Module|libunwind-ptrace.so||libunwind-ptrace.so|7AE0C00FAEDEA3E81109CC784D49A6960|0xb6cd0000|0xb6cd3fff|0
+Module|libunwind.so||libunwind.so|EF89B10946BDF6079AAF789F56192FDA0|0xb6cd4000|0xb6ce1fff|0
+Module|libgccdemangle.so||libgccdemangle.so|3874D35A672DF92604963290963F44990|0xb6d28000|0xb6d2efff|0
+Module|libbacktrace.so||libbacktrace.so|45B51BF91D330E793C9142C2617D7A8E0|0xb6d31000|0xb6d38fff|0
+Module|libutils.so||libutils.so|4939CC7D9325757DDAB52A218D55F4720|0xb6d3a000|0xb6d51fff|0
+Module|libstlport.so||libstlport.so|2FE003E5119C67BABE1A9FAB459A5A5D0|0xb6d52000|0xb6d8cfff|0
+Module|libcutils.so||libcutils.so|8AE85E0A2B96C0006F4BC72D01BDB7BD0|0xb6d8d000|0xb6d99fff|0
+Module|libGLES_trace.so||libGLES_trace.so|A12BAF7D82BE28EC681730452D351E880|0xb6d9b000|0xb6e0bfff|0
+Module|libEGL.so||libEGL.so|924D4C5446BF1132A902C15519E5C4FD0|0xb6e0c000|0xb6e73fff|0
+Module|libandroid_runtime.so||libandroid_runtime.so|EE75A142ED1607624B6579D4590DD7CB0|0xb6e77000|0xb6f52fff|0
+Module|libstdc++.so||libstdc++.so|DFCD7772F3A5BD1E84A50C4DBFDE6F570|0xb6f53000|0xb6f56fff|0
+Module|libm.so||libm.so|AE3467401278371A956801500FC8187D0|0xb6f57000|0xb6f6ffff|0
+Module|liblog.so||liblog.so|0A492DEF82842051996A468D87F23F010|0xb6f71000|0xb6f77fff|0
+Module|libc.so||libc.so|10EC186B04E97F6A24DAA891779479380|0xb6f79000|0xb6fd2fff|0
+Module|libsigchain.so||libsigchain.so|D773C773634B82249E887ECBC5D28C900|0xb6fdd000|0xb6fdffff|0
+
+0|0|libchromeshell.so|content::::CrashIntentionally|/s/chrome-brkpad/src/out/Debug/../../content/renderer/render_frame_impl.cc|267|0x2
+0|1|libchromeshell.so|content::::MaybeHandleDebugURL|/s/chrome-brkpad/src/out/Debug/../../content/renderer/render_frame_impl.cc|310|0x3
+0|2|libchromeshell.so|content::RenderFrameImpl::PrepareRenderViewForNavigation|/s/chrome-brkpad/src/out/Debug/../../content/renderer/render_frame_impl.cc|4008|0x3
+0|3|libchromeshell.so|content::RenderFrameImpl::OnNavigate|/s/chrome-brkpad/src/out/Debug/../../content/renderer/render_frame_impl.cc|936|0x1f
+0|4|libchromeshell.so|content::RenderFrameImpl::OnMessageReceived|/s/chrome-brkpad/src/out/Debug/../../base/tuple.h|548|0x7
+0|5|libchromeshell.so|content::MessageRouter::RouteMessage|/s/chrome-brkpad/src/out/Debug/../../content/common/message_router.cc|54|0x7
+0|6|libchromeshell.so|content::ChildThread::OnMessageReceived|/s/chrome-brkpad/src/out/Debug/../../content/child/child_thread.cc|502|0x9
+0|7|libchromeshell.so|IPC::ChannelProxy::Context::OnDispatchMessage|/s/chrome-brkpad/src/out/Debug/../../ipc/ipc_channel_proxy.cc|274|0x9
+0|8|libchromeshell.so|base::debug::TaskAnnotator::RunTask|/s/chrome-brkpad/src/out/Debug/../../base/callback.h|401|0x5
+0|9|libchromeshell.so|base::MessageLoop::RunTask|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_loop.cc|447|0x11
+0|10|libchromeshell.so|base::MessageLoop::DeferOrRunPendingTask|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_loop.cc|456|0x7
+0|11|libchromeshell.so|base::MessageLoop::DoWork|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_loop.cc|565|0x7
+0|12|libchromeshell.so|base::MessagePumpDefault::Run|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_pump_default.cc|32|0x7
+0|13|libchromeshell.so|base::MessageLoop::RunHandler|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_loop.cc|415|0x5
+0|14|libchromeshell.so|base::RunLoop::Run|/s/chrome-brkpad/src/out/Debug/../../base/run_loop.cc|54|0x5
+0|15|libchromeshell.so|base::MessageLoop::Run|/s/chrome-brkpad/src/out/Debug/../../base/message_loop/message_loop.cc|308|0x5
+0|16|libchromeshell.so|content::RendererMain|/s/chrome-brkpad/src/out/Debug/../../content/renderer/renderer_main.cc|235|0x3
+0|17|libchromeshell.so|content::RunNamedProcessTypeMain|/s/chrome-brkpad/src/out/Debug/../../content/app/content_main_runner.cc|423|0xb
+0|18|libchromeshell.so|content::ContentMainRunnerImpl::Run|/s/chrome-brkpad/src/out/Debug/../../content/app/content_main_runner.cc|789|0x3
+0|19|libchromeshell.so|content::Start|/s/chrome-brkpad/src/out/Debug/../../content/app/android/content_main.cc|48|0x3
+0|20|||||0xa4d56459
diff --git a/src/processor/testdata/microdump.stackwalk.out b/src/processor/testdata/microdump.stackwalk.out
new file mode 100644
index 00000000..c596e769
--- /dev/null
+++ b/src/processor/testdata/microdump.stackwalk.out
@@ -0,0 +1,178 @@
+Operating system:
+
+CPU:
+ 0 CPUs
+
+Crash reason:
+Crash address: 0x0
+
+Thread 0 (crashed)
+ 0 libchromeshell.so!content::::CrashIntentionally [render_frame_impl.cc : 267 + 0x2]
+
+ Found by: given as instruction pointer in context
+ 1 libchromeshell.so!content::::MaybeHandleDebugURL [render_frame_impl.cc : 310 + 0x3]
+
+ Found by: call frame info
+ 2 libchromeshell.so!content::RenderFrameImpl::PrepareRenderViewForNavigation [render_frame_impl.cc : 4008 + 0x3]
+
+ Found by: call frame info
+ 3 libchromeshell.so!content::RenderFrameImpl::OnNavigate [render_frame_impl.cc : 936 + 0x1f]
+
+ Found by: call frame info
+ 4 libchromeshell.so!content::RenderFrameImpl::OnMessageReceived [tuple.h : 548 + 0x7]
+
+ Found by: call frame info
+ 5 libchromeshell.so!content::MessageRouter::RouteMessage [message_router.cc : 54 + 0x7]
+
+ Found by: call frame info
+ 6 libchromeshell.so!content::ChildThread::OnMessageReceived [child_thread.cc : 502 + 0x9]
+
+ Found by: call frame info
+ 7 libchromeshell.so!IPC::ChannelProxy::Context::OnDispatchMessage [ipc_channel_proxy.cc : 274 + 0x9]
+
+ Found by: call frame info
+ 8 libchromeshell.so!base::debug::TaskAnnotator::RunTask [callback.h : 401 + 0x5]
+
+ Found by: call frame info
+ 9 libchromeshell.so!base::MessageLoop::RunTask [message_loop.cc : 447 + 0x11]
+
+ Found by: call frame info
+10 libchromeshell.so!base::MessageLoop::DeferOrRunPendingTask [message_loop.cc : 456 + 0x7]
+
+ Found by: call frame info
+11 libchromeshell.so!base::MessageLoop::DoWork [message_loop.cc : 565 + 0x7]
+
+ Found by: call frame info
+12 libchromeshell.so!base::MessagePumpDefault::Run [message_pump_default.cc : 32 + 0x7]
+
+ Found by: call frame info
+13 libchromeshell.so!base::MessageLoop::RunHandler [message_loop.cc : 415 + 0x5]
+
+ Found by: call frame info
+14 libchromeshell.so!base::RunLoop::Run [run_loop.cc : 54 + 0x5]
+
+ Found by: call frame info
+15 libchromeshell.so!base::MessageLoop::Run [message_loop.cc : 308 + 0x5]
+
+ Found by: call frame info
+16 libchromeshell.so!content::RendererMain [renderer_main.cc : 235 + 0x3]
+
+ Found by: call frame info
+17 libchromeshell.so!content::RunNamedProcessTypeMain [content_main_runner.cc : 423 + 0xb]
+
+ Found by: call frame info
+18 libchromeshell.so!content::ContentMainRunnerImpl::Run [content_main_runner.cc : 789 + 0x3]
+
+ Found by: call frame info
+19 libchromeshell.so!content::Start [content_main.cc : 48 + 0x3]
+
+ Found by: call frame info
+20 0xa4d56459
+
+ Found by: call frame info
+
+Loaded modules:
+0x987ab000 - 0x9b359fff libchromeshell.so ???
+0x9b35a000 - 0x9b4b5fff RELRO:libchromeshell.so ???
+0x9b4b6000 - 0x9b4cbfff libchromeshell.so ???
+0xa47a3000 - 0xa47b3fff libchromium_android_linker.so ???
+0xac35d000 - 0xac35ffff libwebviewchromium_loader.so ???
+0xac360000 - 0xac36ffff libandroid.so ???
+0xac370000 - 0xac4b1fff libGLESv2_adreno.so ???
+0xac4b3000 - 0xac4e6fff libGLESv1_CM_adreno.so ???
+0xac4e7000 - 0xac51afff libgsl.so ???
+0xac51b000 - 0xac544fff libEGL_adreno.so ???
+0xace37000 - 0xace4efff libjavacrypto.so ???
+0xace4f000 - 0xace57fff librs_jni.so ???
+0xace58000 - 0xace5dfff libaudioeffect_jni.so ???
+0xace5e000 - 0xace61fff libsoundpool.so ???
+0xace62000 - 0xace6ffff libstagefright_amrnb_common.so ???
+0xace70000 - 0xace8afff libvorbisidec.so ???
+0xace8b000 - 0xace8efff libstagefright_yuv.so ???
+0xace90000 - 0xaceaefff libstagefright_omx.so ???
+0xaceaf000 - 0xaceb1fff libstagefright_enc_common.so ???
+0xaceb2000 - 0xaceb8fff libstagefright_avc_common.so ???
+0xaceb9000 - 0xacebdfff libpowermanager.so ???
+0xacebe000 - 0xacef9fff libopus.so ???
+0xacefa000 - 0xacf14fff libdrmframework.so ???
+0xacf16000 - 0xad029fff libstagefright.so ???
+0xad02a000 - 0xad03ffff libmtp.so ???
+0xad040000 - 0xad04afff libjhead.so ???
+0xad04c000 - 0xad077fff libexif.so ???
+0xad078000 - 0xad0b3fff libmedia_jni.so ???
+0xafe8a000 - 0xafe8cfff libjnigraphics.so ???
+0xafe8d000 - 0xafe94fff libcompiler_rt.so ???
+0xafe95000 - 0xafe98fff libadreno_utils.so ???
+0xafe9b000 - 0xafe9dfff memtrack.msm8960.so ???
+0xb15ab000 - 0xb15e2fff libjavacore.so ???
+0xb4fc8000 - 0xb4fd0fff libbacktrace_libc++.so ???
+0xb4fd2000 - 0xb52d5fff libart.so ???
+0xb52f9000 - 0xb52fcfff libusbhost.so ???
+0xb52fd000 - 0xb533dfff libssl.so ???
+0xb533e000 - 0xb539cfff libsqlite.so ???
+0xb539e000 - 0xb53acfff libsoundtrigger.so ???
+0xb53ad000 - 0xb53bafff libselinux.so ???
+0xb53bb000 - 0xb53befff libprocessgroup.so ???
+0xb53bf000 - 0xb5819fff libpdfium.so ???
+0xb581f000 - 0xb5822fff libnetd_client.so ???
+0xb5823000 - 0xb5829fff libnativehelper.so ???
+0xb582a000 - 0xb582dfff libnativebridge.so ???
+0xb582e000 - 0xb5839fff libminikin.so ???
+0xb583a000 - 0xb583cfff libmemtrack.so ???
+0xb583d000 - 0xb5851fff libstagefright_foundation.so ???
+0xb5852000 - 0xb58a2fff libsonivox.so ???
+0xb58a8000 - 0xb58b6fff libcommon_time_client.so ???
+0xb58b7000 - 0xb58c0fff libnbaio.so ???
+0xb58c1000 - 0xb595afff libmedia.so ???
+0xb595b000 - 0xb5996fff libinputflinger.so ???
+0xb5997000 - 0xb59b1fff libinput.so ???
+0xb59b2000 - 0xb59c0fff libimg_utils.so ???
+0xb59c1000 - 0xb59f2fff libjpeg.so ???
+0xb59f4000 - 0xb5c22fff libskia.so ???
+0xb5c28000 - 0xb5c44fff libRScpp.so ???
+0xb5c45000 - 0xb5c6cfff libpng.so ???
+0xb5c6d000 - 0xb5cc6fff libft2.so ???
+0xb5cc7000 - 0xb5d03fff libbcinfo.so ???
+0xb5d04000 - 0xb5d26fff libbcc.so ???
+0xb5d47000 - 0xb5ddafff libc++.so ???
+0xb5ddd000 - 0xb6714fff libLLVM.so ???
+0xb671c000 - 0xb6755fff libRS.so ???
+0xb6756000 - 0xb67a1fff libhwui.so ???
+0xb67a2000 - 0xb68b1fff libicuuc.so ???
+0xb68b6000 - 0xb68bbfff libgabi++.so ???
+0xb68bc000 - 0xb6a22fff libicui18n.so ???
+0xb6a23000 - 0xb6a6afff libharfbuzz_ng.so ???
+0xb6a6b000 - 0xb6a6ffff libwpa_client.so ???
+0xb6a70000 - 0xb6a76fff libnetutils.so ???
+0xb6a77000 - 0xb6a7cfff libhardware_legacy.so ???
+0xb6a7e000 - 0xb6a94fff libexpat.so ???
+0xb6a95000 - 0xb6b96fff libcrypto.so ???
+0xb6b99000 - 0xb6b9bfff libhardware.so ???
+0xb6b9c000 - 0xb6ba7fff libui.so ???
+0xb6ba8000 - 0xb6baafff libsync.so ???
+0xb6bab000 - 0xb6bf9fff libgui.so ???
+0xb6bfa000 - 0xb6c01fff libcamera_metadata.so ???
+0xb6c02000 - 0xb6c3bfff libcamera_client.so ???
+0xb6c3c000 - 0xb6c41fff libspeexresampler.so ???
+0xb6c42000 - 0xb6c47fff libaudioutils.so ???
+0xb6c48000 - 0xb6c61fff libz.so ???
+0xb6c62000 - 0xb6c91fff libbinder.so ???
+0xb6c92000 - 0xb6cb9fff libandroidfw.so ???
+0xb6cba000 - 0xb6cc4fff libGLESv2.so ???
+0xb6cc5000 - 0xb6ccbfff libGLESv1_CM.so ???
+0xb6ccc000 - 0xb6ccffff libETC1.so ???
+0xb6cd0000 - 0xb6cd3fff libunwind-ptrace.so ???
+0xb6cd4000 - 0xb6ce1fff libunwind.so ???
+0xb6d28000 - 0xb6d2efff libgccdemangle.so ???
+0xb6d31000 - 0xb6d38fff libbacktrace.so ???
+0xb6d3a000 - 0xb6d51fff libutils.so ???
+0xb6d52000 - 0xb6d8cfff libstlport.so ???
+0xb6d8d000 - 0xb6d99fff libcutils.so ???
+0xb6d9b000 - 0xb6e0bfff libGLES_trace.so ???
+0xb6e0c000 - 0xb6e73fff libEGL.so ???
+0xb6e77000 - 0xb6f52fff libandroid_runtime.so ???
+0xb6f53000 - 0xb6f56fff libstdc++.so ???
+0xb6f57000 - 0xb6f6ffff libm.so ???
+0xb6f71000 - 0xb6f77fff liblog.so ???
+0xb6f79000 - 0xb6fd2fff libc.so ???
+0xb6fdd000 - 0xb6fdffff libsigchain.so ???