diff options
author | mmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2007-01-25 00:17:02 +0000 |
---|---|---|
committer | mmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2007-01-25 00:17:02 +0000 |
commit | a14ef803d70614a01725a269acb509027fe84942 (patch) | |
tree | d1de07f5d23ff9cacc65b1a9a71a43dab1c14b2f | |
parent | Update crash_report tool and internal classes to reflect change to ProcessSta... (diff) | |
download | breakpad-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.cc | 235 | ||||
-rw-r--r-- | src/processor/simple_symbol_supplier.cc | 25 | ||||
-rw-r--r-- | src/processor/simple_symbol_supplier.h | 37 |
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 |