mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-01-23 01:11:10 +00:00
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
This commit is contained in:
parent
4b987df62b
commit
a14ef803d7
|
@ -35,6 +35,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "google_airbag/processor/basic_source_line_resolver.h"
|
#include "google_airbag/processor/basic_source_line_resolver.h"
|
||||||
#include "google_airbag/processor/call_stack.h"
|
#include "google_airbag/processor/call_stack.h"
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
using google_airbag::BasicSourceLineResolver;
|
using google_airbag::BasicSourceLineResolver;
|
||||||
using google_airbag::CallStack;
|
using google_airbag::CallStack;
|
||||||
using google_airbag::CodeModule;
|
using google_airbag::CodeModule;
|
||||||
|
@ -65,6 +67,9 @@ using google_airbag::StackFrame;
|
||||||
using google_airbag::StackFramePPC;
|
using google_airbag::StackFramePPC;
|
||||||
using google_airbag::StackFrameX86;
|
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
|
// 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,
|
// 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
|
// 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;
|
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
|
// PrintStack prints the call stack in |stack| to stdout, in a reasonably
|
||||||
// useful form. Module, function, and source file names are displayed if
|
// useful form. Module, function, and source file names are displayed if
|
||||||
// they are available. The code offset to the base code address of the
|
// they are available. The code offset to the base code address of the
|
||||||
|
@ -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) {
|
static void PrintModules(const CodeModules *modules) {
|
||||||
if (!modules)
|
if (!modules)
|
||||||
return;
|
return;
|
||||||
|
@ -176,35 +248,7 @@ static void PrintModules(const CodeModules *modules) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
|
static void PrintProcessState(const ProcessState& process_state) {
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print OS and CPU information.
|
// Print OS and CPU information.
|
||||||
string cpu = process_state.system_info()->cpu;
|
string cpu = process_state.system_info()->cpu;
|
||||||
string cpu_info = process_state.system_info()->cpu_info;
|
string cpu_info = process_state.system_info()->cpu_info;
|
||||||
|
@ -249,23 +293,142 @@ static bool PrintMinidumpProcess(const string &minidump_file,
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintModules(process_state.modules());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // 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) {
|
int main(int argc, char **argv) {
|
||||||
if (argc < 2 || argc > 3) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "usage: %s <minidump-file> [symbol-path]\n", argv[0]);
|
usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *minidump_file = argv[1];
|
const char *minidump_file;
|
||||||
const char *symbol_path = "";
|
bool machine_readable;
|
||||||
if (argc == 3) {
|
int symbol_path_arg;
|
||||||
symbol_path = argv[2];
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PrintMinidumpProcess(minidump_file, symbol_path) ? 0 : 1;
|
// 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_paths,
|
||||||
|
machine_readable) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@
|
||||||
//
|
//
|
||||||
// Author: Mark Mentovai
|
// Author: Mark Mentovai
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "processor/simple_symbol_supplier.h"
|
#include "processor/simple_symbol_supplier.h"
|
||||||
|
@ -42,6 +45,25 @@
|
||||||
|
|
||||||
namespace google_airbag {
|
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(
|
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
|
||||||
const CodeModule *module, const SystemInfo *system_info,
|
const CodeModule *module, const SystemInfo *system_info,
|
||||||
const string &root_path, string *symbol_file) {
|
const string &root_path, string *symbol_file) {
|
||||||
|
@ -81,6 +103,9 @@ SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
|
||||||
}
|
}
|
||||||
path.append(".sym");
|
path.append(".sym");
|
||||||
|
|
||||||
|
if (!file_exists(path))
|
||||||
|
return NOT_FOUND;
|
||||||
|
|
||||||
*symbol_file = path;
|
*symbol_file = path;
|
||||||
return FOUND;
|
return FOUND;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@
|
||||||
//
|
//
|
||||||
// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
|
// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
|
||||||
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
|
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
|
||||||
// created with a base directory, which is the root for all symbol files.
|
// created with one or more base directories, which are the root paths for all
|
||||||
// Each symbol file contained therien has a directory entry in the base
|
// symbol files. Each symbol file contained therein has a directory entry in
|
||||||
// directory with a name identical to the corresponding debugging file (pdb).
|
// the base directory with a name identical to the corresponding debugging
|
||||||
// Within each of these directories, there are subdirectories named for the
|
// file (pdb). Within each of these directories, there are subdirectories
|
||||||
// debugging file's identifier. For recent pdb files, this is a concatenation
|
// named for the debugging file's identifier. For recent pdb files, this is
|
||||||
// of the pdb's uuid and age, presented in hexadecimal form, without any
|
// a concatenation of the pdb's uuid and age, presented in hexadecimal form,
|
||||||
// dashes or separators. The uuid is in uppercase hexadecimal and the age
|
// without any dashes or separators. The uuid is in uppercase hexadecimal
|
||||||
// is in lowercase hexadecimal. Within that subdirectory,
|
// and the age is in lowercase hexadecimal. Within that subdirectory,
|
||||||
// SimpleSymbolSupplier expects to find the symbol file, which is named
|
// SimpleSymbolSupplier expects to find the symbol file, which is named
|
||||||
// identically to the debug file, but with a .sym extension. If the original
|
// 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
|
// 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
|
// SimpleSymbolServer, provided that the pdb files are transformed to dumped
|
||||||
// format using a tool such as dump_syms, and given a .sym extension.
|
// 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
|
// SimpleSymbolSupplier supports any debugging file which can be identified
|
||||||
// by a CodeModule object's debug_file and debug_identifier accessors. The
|
// by a CodeModule object's debug_file and debug_identifier accessors. The
|
||||||
// expected ultimate source of these CodeModule objects are MinidumpModule
|
// expected ultimate source of these CodeModule objects are MinidumpModule
|
||||||
|
@ -74,12 +77,14 @@
|
||||||
#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
|
#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "google_airbag/processor/symbol_supplier.h"
|
#include "google_airbag/processor/symbol_supplier.h"
|
||||||
|
|
||||||
namespace google_airbag {
|
namespace google_airbag {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
class CodeModule;
|
class CodeModule;
|
||||||
|
|
||||||
|
@ -87,17 +92,19 @@ class SimpleSymbolSupplier : public SymbolSupplier {
|
||||||
public:
|
public:
|
||||||
// Creates a new SimpleSymbolSupplier, using path as the root path where
|
// Creates a new SimpleSymbolSupplier, using path as the root path where
|
||||||
// symbols are stored.
|
// 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() {}
|
virtual ~SimpleSymbolSupplier() {}
|
||||||
|
|
||||||
// Returns the path to the symbol file for the given module. See the
|
// Returns the path to the symbol file for the given module. See the
|
||||||
// description above.
|
// description above.
|
||||||
virtual SymbolResult GetSymbolFile(const CodeModule *module,
|
SymbolResult GetSymbolFile(const CodeModule *module,
|
||||||
const SystemInfo *system_info,
|
const SystemInfo *system_info,
|
||||||
string *symbol_file) {
|
string *symbol_file);
|
||||||
return GetSymbolFileAtPath(module, system_info, path_, symbol_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SymbolResult GetSymbolFileAtPath(const CodeModule *module,
|
SymbolResult GetSymbolFileAtPath(const CodeModule *module,
|
||||||
|
@ -106,7 +113,7 @@ class SimpleSymbolSupplier : public SymbolSupplier {
|
||||||
string *symbol_file);
|
string *symbol_file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string path_;
|
vector<string> paths_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_airbag
|
} // namespace google_airbag
|
||||||
|
|
Loading…
Reference in a new issue