aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2007-01-25 00:17:02 +0000
committermmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2007-01-25 00:17:02 +0000
commita14ef803d70614a01725a269acb509027fe84942 (patch)
treed1de07f5d23ff9cacc65b1a9a71a43dab1c14b2f
parentUpdate crash_report tool and internal classes to reflect change to ProcessSta... (diff)
downloadbreakpad-a14ef803d70614a01725a269acb509027fe84942.tar.xz
Support machine-readable output format from minidump_stackwalk;
Support multiple symbol paths in SimpleSymbolSupplier (#113). Patch by Ted Mielczarek. r=me http://groups.google.com/group/airbag-dev/browse_thread/thread/44b91a9112618b26 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@112 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/processor/minidump_stackwalk.cc235
-rw-r--r--src/processor/simple_symbol_supplier.cc25
-rw-r--r--src/processor/simple_symbol_supplier.h37
3 files changed, 246 insertions, 51 deletions
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index e86fbdf6..de87b505 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -35,6 +35,7 @@
#include <cstdio>
#include <cstdlib>
#include <string>
+#include <vector>
#include "google_airbag/processor/basic_source_line_resolver.h"
#include "google_airbag/processor/call_stack.h"
@@ -51,6 +52,7 @@
namespace {
using std::string;
+using std::vector;
using google_airbag::BasicSourceLineResolver;
using google_airbag::CallStack;
using google_airbag::CodeModule;
@@ -65,6 +67,9 @@ using google_airbag::StackFrame;
using google_airbag::StackFramePPC;
using google_airbag::StackFrameX86;
+// 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 |sequence|. For registers in a set, pass the most recent
@@ -81,6 +86,17 @@ static int PrintRegister(const char *name, u_int32_t value, int sequence) {
return ++sequence;
}
+// 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);
+ }
+ 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
@@ -153,6 +169,62 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
}
}
+// 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);
+
+ 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%llx", kOutputSeparator,
+ StripSeparator(frame->source_file_name)
+ .c_str(),
+ kOutputSeparator,
+ frame->source_line,
+ kOutputSeparator,
+ frame->instruction -
+ frame->source_line_base);
+ } else {
+ printf("%c%c%c0x%llx", kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ frame->instruction - frame->function_base);
+ }
+ } else {
+ printf("%c%c%c%c0x%llx", kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ frame->instruction -
+ frame->module->base_address());
+ }
+ } else {
+ // the printf before this prints a trailing separator for module name
+ printf("%c%c%c%c0x%llx", kOutputSeparator, // empty function name
+ kOutputSeparator, // empty source file
+ kOutputSeparator, // empty source line
+ kOutputSeparator,
+ frame->instruction);
+ }
+ printf("\n");
+ }
+}
+
static void PrintModules(const CodeModules *modules) {
if (!modules)
return;
@@ -176,35 +248,7 @@ static void PrintModules(const CodeModules *modules) {
}
}
-// 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));
- }
-
- BasicSourceLineResolver resolver;
- MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
-
- // Process the minidump.
- ProcessState process_state;
- if (minidump_processor.Process(minidump_file, &process_state) !=
- MinidumpProcessor::PROCESS_OK) {
- fprintf(stderr, "MinidumpProcessor::Process failed\n");
- return false;
- }
-
+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;
@@ -249,23 +293,142 @@ static bool PrintMinidumpProcess(const string &minidump_file,
}
PrintModules(process_state.modules());
+}
+
+static void PrintProcessStateMachineReadable(const ProcessState& process_state)
+{
+ // Print OS and CPU information.
+ // OS|{OS Name}|{OS Version}
+ // CPU|{CPU Name}|{CPU Info}
+ 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\n", kOutputSeparator,
+ StripSeparator(process_state.system_info()->cpu).c_str(),
+ kOutputSeparator,
+ // this may be empty
+ StripSeparator(process_state.system_info()->cpu_info).c_str());
+
+ 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%llx%c",
+ StripSeparator(process_state.crash_reason()).c_str(),
+ kOutputSeparator, process_state.crash_address(), kOutputSeparator);
+ } else {
+ printf("No crash%c%c\n", kOutputSeparator, kOutputSeparator);
+ }
+
+ if (requesting_thread != -1) {
+ printf("%d\n", requesting_thread);
+ } else {
+ printf("\n");
+ }
+
+ // 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));
+ }
+ }
+}
+
+// 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 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?
+ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
+ }
+
+ BasicSourceLineResolver resolver;
+ MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
+
+ // Process the minidump.
+ ProcessState process_state;
+ if (minidump_processor.Process(minidump_file, &process_state) !=
+ MinidumpProcessor::PROCESS_OK) {
+ fprintf(stderr, "MinidumpProcessor::Process failed\n");
+ return false;
+ }
+
+ if (machine_readable) {
+ PrintProcessStateMachineReadable(process_state);
+ } else {
+ PrintProcessState(process_state);
+ }
return true;
}
} // namespace
+static 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);
+}
+
int main(int argc, char **argv) {
- if (argc < 2 || argc > 3) {
- fprintf(stderr, "usage: %s <minidump-file> [symbol-path]\n", argv[0]);
+ if (argc < 2) {
+ usage(argv[0]);
return 1;
}
- const char *minidump_file = argv[1];
- const char *symbol_path = "";
- if (argc == 3) {
- symbol_path = argv[2];
+ const char *minidump_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;
+ minidump_file = argv[2];
+ symbol_path_arg = 3;
+ } else {
+ machine_readable = false;
+ minidump_file = argv[1];
+ symbol_path_arg = 2;
+ }
+
+ // extra arguments are symbol paths
+ 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 PrintMinidumpProcess(minidump_file, symbol_path) ? 0 : 1;
+ return PrintMinidumpProcess(minidump_file,
+ symbol_paths,
+ machine_readable) ? 0 : 1;
}
diff --git a/src/processor/simple_symbol_supplier.cc b/src/processor/simple_symbol_supplier.cc
index fb16818d..3cbd59a3 100644
--- a/src/processor/simple_symbol_supplier.cc
+++ b/src/processor/simple_symbol_supplier.cc
@@ -33,6 +33,9 @@
//
// Author: Mark Mentovai
+#include <sys/types.h>
+#include <sys/stat.h>
+
#include <cassert>
#include "processor/simple_symbol_supplier.h"
@@ -42,6 +45,25 @@
namespace google_airbag {
+static bool file_exists(const string &file_name) {
+ struct stat sb;
+ return stat(file_name.c_str(), &sb) == 0;
+}
+
+SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile(
+ const CodeModule *module, const SystemInfo *system_info,
+ string *symbol_file) {
+ assert(symbol_file);
+ for (unsigned int path_index = 0; path_index < paths_.size(); ++path_index) {
+ SymbolResult result;
+ if ((result = GetSymbolFileAtPath(module, system_info, paths_[path_index],
+ symbol_file)) != NOT_FOUND) {
+ return result;
+ }
+ }
+ return NOT_FOUND;
+}
+
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
const CodeModule *module, const SystemInfo *system_info,
const string &root_path, string *symbol_file) {
@@ -81,6 +103,9 @@ SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
}
path.append(".sym");
+ if (!file_exists(path))
+ return NOT_FOUND;
+
*symbol_file = path;
return FOUND;
}
diff --git a/src/processor/simple_symbol_supplier.h b/src/processor/simple_symbol_supplier.h
index 6359da2a..e4083761 100644
--- a/src/processor/simple_symbol_supplier.h
+++ b/src/processor/simple_symbol_supplier.h
@@ -31,14 +31,14 @@
//
// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
-// created with a base directory, which is the root for all symbol files.
-// Each symbol file contained therien has a directory entry in the base
-// directory with a name identical to the corresponding debugging file (pdb).
-// Within each of these directories, there are subdirectories named for the
-// debugging file's identifier. For recent pdb files, this is a concatenation
-// of the pdb's uuid and age, presented in hexadecimal form, without any
-// dashes or separators. The uuid is in uppercase hexadecimal and the age
-// is in lowercase hexadecimal. Within that subdirectory,
+// created with one or more base directories, which are the root paths for all
+// symbol files. Each symbol file contained therein has a directory entry in
+// the base directory with a name identical to the corresponding debugging
+// file (pdb). Within each of these directories, there are subdirectories
+// named for the debugging file's identifier. For recent pdb files, this is
+// a concatenation of the pdb's uuid and age, presented in hexadecimal form,
+// without any dashes or separators. The uuid is in uppercase hexadecimal
+// and the age is in lowercase hexadecimal. Within that subdirectory,
// SimpleSymbolSupplier expects to find the symbol file, which is named
// identically to the debug file, but with a .sym extension. If the original
// debug file had a name ending in .pdb, the .pdb extension will be replaced
@@ -62,6 +62,9 @@
// SimpleSymbolServer, provided that the pdb files are transformed to dumped
// format using a tool such as dump_syms, and given a .sym extension.
//
+// SimpleSymbolSupplier will iterate over all root paths searching for
+// a symbol file existing in that path.
+//
// SimpleSymbolSupplier supports any debugging file which can be identified
// by a CodeModule object's debug_file and debug_identifier accessors. The
// expected ultimate source of these CodeModule objects are MinidumpModule
@@ -74,12 +77,14 @@
#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
#include <string>
+#include <vector>
#include "google_airbag/processor/symbol_supplier.h"
namespace google_airbag {
using std::string;
+using std::vector;
class CodeModule;
@@ -87,17 +92,19 @@ class SimpleSymbolSupplier : public SymbolSupplier {
public:
// Creates a new SimpleSymbolSupplier, using path as the root path where
// symbols are stored.
- explicit SimpleSymbolSupplier(const string &path) : path_(path) {}
+ explicit SimpleSymbolSupplier(const string &path) : paths_(1, path) {}
+
+ // Creates a new SimpleSymbolSupplier, using paths as a list of root
+ // paths where symbols may be stored.
+ explicit SimpleSymbolSupplier(const vector<string> &paths) : paths_(paths) {}
virtual ~SimpleSymbolSupplier() {}
// Returns the path to the symbol file for the given module. See the
// description above.
- virtual SymbolResult GetSymbolFile(const CodeModule *module,
- const SystemInfo *system_info,
- string *symbol_file) {
- return GetSymbolFileAtPath(module, system_info, path_, symbol_file);
- }
+ SymbolResult GetSymbolFile(const CodeModule *module,
+ const SystemInfo *system_info,
+ string *symbol_file);
protected:
SymbolResult GetSymbolFileAtPath(const CodeModule *module,
@@ -106,7 +113,7 @@ class SimpleSymbolSupplier : public SymbolSupplier {
string *symbol_file);
private:
- string path_;
+ vector<string> paths_;
};
} // namespace google_airbag