aboutsummaryrefslogtreecommitdiff
path: root/src/processor/minidump_stackwalk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/processor/minidump_stackwalk.cc')
-rw-r--r--src/processor/minidump_stackwalk.cc238
1 files changed, 170 insertions, 68 deletions
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index fa499362..5f343a29 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -27,104 +27,206 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// minidump_stackwalk.cc: Print the stack of the exception thread from a
-// minidump.
+// minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing
+// the results, including stack traces.
//
// Author: Mark Mentovai
-#include <stdlib.h>
-#include <stdio.h>
-
+#include <cstdio>
+#include <cstdlib>
#include <string>
#include "google/call_stack.h"
-#include "google/stack_frame.h"
+#include "google/minidump_processor.h"
+#include "google/process_state.h"
+#include "google/stack_frame_cpu.h"
#include "processor/minidump.h"
+#include "processor/pathname_stripper.h"
#include "processor/scoped_ptr.h"
-#include "processor/stackwalker_x86.h"
-
+#include "processor/simple_symbol_supplier.h"
using std::string;
using google_airbag::CallStack;
-using google_airbag::MemoryRegion;
-using google_airbag::Minidump;
-using google_airbag::MinidumpContext;
-using google_airbag::MinidumpException;
-using google_airbag::MinidumpModuleList;
-using google_airbag::MinidumpThread;
-using google_airbag::MinidumpThreadList;
+using google_airbag::MinidumpModule;
+using google_airbag::MinidumpProcessor;
+using google_airbag::PathnameStripper;
+using google_airbag::ProcessState;
using google_airbag::scoped_ptr;
+using google_airbag::SimpleSymbolSupplier;
using google_airbag::StackFrame;
-using google_airbag::Stackwalker;
-
-
-int main(int argc, char **argv) {
- if (argc != 2) {
- fprintf(stderr, "usage: %s <file>\n", argv[0]);
- exit(1);
+using google_airbag::StackFramePPC;
+using google_airbag::StackFrameX86;
+
+// 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 |sequence|. For registers in a set, pass the most recent
+// return value of PrintRegister. Note that PrintRegister will print a
+// newline before the first register (with |sequence| set to 0) is printed.
+// 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 int PrintRegister(const char *name, u_int32_t value, int sequence) {
+ if (sequence % 4 == 0) {
+ printf("\n ");
}
+ printf(" %5s = 0x%08x", name, value);
+ return ++sequence;
+}
- Minidump minidump(argv[1]);
- if (!minidump.Read()) {
- fprintf(stderr, "minidump.Read() failed\n");
- exit(1);
+// 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();
+ for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
+ const StackFrame *frame = stack->frames()->at(frame_index);
+ printf("%2d ", frame_index);
+
+ if (!frame->module_name.empty()) {
+ printf("%s", PathnameStripper::File(frame->module_name).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%llx]", source_file.c_str(),
+ frame->source_line,
+ frame->instruction -
+ frame->source_line_base);
+ } else {
+ printf(" + 0x%llx", frame->instruction - frame->function_base);
+ }
+ } else {
+ printf(" + 0x%llx", frame->instruction - frame->module_base);
+ }
+ } else {
+ printf("0x%llx", frame->instruction);
+ }
+
+ 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);
+ }
+
+ printf("\n");
}
+}
- MinidumpException *exception = minidump.GetException();
- if (!exception) {
- fprintf(stderr, "minidump.GetException() failed\n");
- exit(1);
+// Processes |minidump_file| using MinidumpProcessor. |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 MinidumpProcessor.
+//
+// Returns the value of MinidumpProcessor::Process. If processing succeeds,
+// prints identifying OS and CPU information from the minidump, crash
+// 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 string &symbol_path) {
+ scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
+ if (!symbol_path.empty()) {
+ // TODO(mmentovai): check existence of symbol_path if specified?
+ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_path));
}
- MinidumpThreadList *thread_list = minidump.GetThreadList();
- if (!thread_list) {
- fprintf(stderr, "minidump.GetThreadList() failed\n");
- exit(1);
- }
+ MinidumpProcessor minidump_processor(symbol_supplier.get());
- MinidumpThread *exception_thread =
- thread_list->GetThreadByID(exception->GetThreadID());
- if (!exception_thread) {
- fprintf(stderr, "thread_list->GetThreadByID() failed\n");
- exit(1);
+ // Process the minidump.
+ scoped_ptr<ProcessState> process_state(
+ minidump_processor.Process(minidump_file));
+ if (!process_state.get()) {
+ fprintf(stderr, "MinidumpProcessor::Process failed\n");
+ return false;
}
- MemoryRegion *stack_memory = exception_thread->GetMemory();
- if (!stack_memory) {
- fprintf(stderr, "exception_thread->GetStackMemory() failed\n");
- exit(1);
+ // Print OS and CPU information.
+ string cpu = process_state->cpu();
+ string cpu_info = process_state->cpu_info();
+ printf("Operating system: %s\n", process_state->os().c_str());
+ printf(" %s\n", process_state->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());
}
-
- MinidumpContext *context = exception->GetContext();
- if (!context) {
- fprintf(stderr, "exception->GetContext() failed\n");
- exit(1);
+ printf("\n");
+
+ // Print crash information.
+ if (process_state->crashed()) {
+ printf("Crash reason: %s\n", process_state->crash_reason().c_str());
+ printf("Crash address: 0x%llx\n", process_state->crash_address());
+ } else {
+ printf("No crash\n");
}
- MinidumpModuleList *modules = minidump.GetModuleList();
- if (!modules) {
- fprintf(stderr, "minidump.GetModuleList() failed\n");
- exit(1);
+ // If there's a crash thread, print it first.
+ int crash_thread = -1;
+ if (process_state->crashed()) {
+ crash_thread = process_state->crash_thread();
+ printf("\n");
+ printf("Thread %d (crashed)\n", crash_thread);
+ PrintStack(process_state->threads()->at(crash_thread), cpu);
}
- scoped_ptr<Stackwalker> stackwalker(
- Stackwalker::StackwalkerForCPU(context, stack_memory, modules, NULL));
- if (!stackwalker.get()) {
- fprintf(stderr, "Stackwalker::StackwalkerForCPU failed\n");
- exit(1);
+ // 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 != crash_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);
+ }
}
- scoped_ptr<CallStack> stack(stackwalker->Walk());
+ return true;
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "usage: %s <minidump-file> [symbol-path]\n", argv[0]);
+ return 1;
+ }
- unsigned int index;
- for (index = 0; index < stack->frames()->size(); ++index) {
- StackFrame *frame = stack->frames()->at(index);
- printf("[%2d] instruction = 0x%08llx \"%s\" + 0x%08llx\n",
- index,
- frame->instruction,
- frame->module_base ? frame->module_name.c_str() : "0x0",
- frame->instruction - frame->module_base);
+ const char *minidump_file = argv[1];
+ const char *symbol_path = "";
+ if (argc == 3) {
+ symbol_path = argv[2];
}
- return 0;
+ return PrintMinidumpProcess(minidump_file, symbol_path) ? 0 : 1;
}