Provide a mechanism for SymbolSuppliers to interrupt processing (#93)
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@80 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
283fd39248
commit
f33b8d2d07
@ -56,17 +56,18 @@ template<typename T> class linked_ptr;
|
|||||||
|
|
||||||
class CallStack {
|
class CallStack {
|
||||||
public:
|
public:
|
||||||
|
CallStack() { Clear(); }
|
||||||
~CallStack();
|
~CallStack();
|
||||||
|
|
||||||
|
// Resets the CallStack to its initial empty state
|
||||||
|
void Clear();
|
||||||
|
|
||||||
const vector<StackFrame*>* frames() const { return &frames_; }
|
const vector<StackFrame*>* frames() const { return &frames_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Stackwalker is responsible for building the frames_ vector.
|
// Stackwalker is responsible for building the frames_ vector.
|
||||||
friend class Stackwalker;
|
friend class Stackwalker;
|
||||||
|
|
||||||
// Disallow instantiation other than by friends.
|
|
||||||
CallStack() : frames_() {}
|
|
||||||
|
|
||||||
// Storage for pushed frames.
|
// Storage for pushed frames.
|
||||||
vector<StackFrame*> frames_;
|
vector<StackFrame*> frames_;
|
||||||
};
|
};
|
||||||
|
@ -42,15 +42,21 @@ class SymbolSupplier;
|
|||||||
|
|
||||||
class MinidumpProcessor {
|
class MinidumpProcessor {
|
||||||
public:
|
public:
|
||||||
|
// Return type for Process()
|
||||||
|
enum ProcessResult {
|
||||||
|
PROCESS_OK, // the minidump was processed successfully
|
||||||
|
PROCESS_ERROR, // there was an error processing the minidump
|
||||||
|
PROCESS_INTERRUPTED, // processing was interrupted by the SymbolSupplier
|
||||||
|
};
|
||||||
|
|
||||||
// Initializes this MinidumpProcessor. supplier should be an
|
// Initializes this MinidumpProcessor. supplier should be an
|
||||||
// implementation of the SymbolSupplier abstract base class.
|
// implementation of the SymbolSupplier abstract base class.
|
||||||
explicit MinidumpProcessor(SymbolSupplier *supplier);
|
explicit MinidumpProcessor(SymbolSupplier *supplier);
|
||||||
~MinidumpProcessor();
|
~MinidumpProcessor();
|
||||||
|
|
||||||
// Returns a new ProcessState object produced by processing the minidump
|
// Processes the minidump file and fills process_state with the result.
|
||||||
// file. The caller takes ownership of the ProcessState. Returns NULL on
|
ProcessResult Process(const string &minidump_file,
|
||||||
// failure.
|
ProcessState *process_state);
|
||||||
ProcessState* Process(const string &minidump_file);
|
|
||||||
|
|
||||||
// Returns a textual representation of the base CPU type that the minidump
|
// Returns a textual representation of the base CPU type that the minidump
|
||||||
// in dump was produced on. Returns an empty string if this information
|
// in dump was produced on. Returns an empty string if this information
|
||||||
|
@ -47,8 +47,12 @@ class CodeModules;
|
|||||||
|
|
||||||
class ProcessState {
|
class ProcessState {
|
||||||
public:
|
public:
|
||||||
|
ProcessState() : modules_(NULL) { Clear(); }
|
||||||
~ProcessState();
|
~ProcessState();
|
||||||
|
|
||||||
|
// Resets the ProcessState to its default values
|
||||||
|
void Clear();
|
||||||
|
|
||||||
// Accessors. See the data declarations below.
|
// Accessors. See the data declarations below.
|
||||||
u_int32_t time_date_stamp() const { return time_date_stamp_; }
|
u_int32_t time_date_stamp() const { return time_date_stamp_; }
|
||||||
bool crashed() const { return crashed_; }
|
bool crashed() const { return crashed_; }
|
||||||
@ -66,11 +70,6 @@ class ProcessState {
|
|||||||
// MinidumpProcessor is responsible for building ProcessState objects.
|
// MinidumpProcessor is responsible for building ProcessState objects.
|
||||||
friend class MinidumpProcessor;
|
friend class MinidumpProcessor;
|
||||||
|
|
||||||
// Disallow instantiation other than by friends.
|
|
||||||
ProcessState() : time_date_stamp_(0), crashed_(false), crash_reason_(),
|
|
||||||
crash_address_(0), requesting_thread_(-1), threads_(),
|
|
||||||
os_(), os_version_(), cpu_(), cpu_info_(), modules_(NULL) {}
|
|
||||||
|
|
||||||
// The time-date stamp of the minidump (time_t format)
|
// The time-date stamp of the minidump (time_t format)
|
||||||
u_int32_t time_date_stamp_;
|
u_int32_t time_date_stamp_;
|
||||||
|
|
||||||
|
@ -61,10 +61,11 @@ class Stackwalker {
|
|||||||
public:
|
public:
|
||||||
virtual ~Stackwalker() {}
|
virtual ~Stackwalker() {}
|
||||||
|
|
||||||
// Creates a new CallStack and populates it by calling GetContextFrame and
|
// Populates the given CallStack by calling GetContextFrame and
|
||||||
// GetCallerFrame. The frames are further processed to fill all available
|
// GetCallerFrame. The frames are further processed to fill all available
|
||||||
// data. The caller takes ownership of the CallStack returned by Walk.
|
// data. Returns true if the stackwalk completed, or false if it was
|
||||||
CallStack* Walk();
|
// interrupted by SymbolSupplier::GetSymbolFile().
|
||||||
|
bool Walk(CallStack *stack);
|
||||||
|
|
||||||
// Returns a new concrete subclass suitable for the CPU that a stack was
|
// Returns a new concrete subclass suitable for the CPU that a stack was
|
||||||
// generated on, according to the CPU type indicated by the context
|
// generated on, according to the CPU type indicated by the context
|
||||||
|
@ -42,10 +42,24 @@ class CodeModule;
|
|||||||
|
|
||||||
class SymbolSupplier {
|
class SymbolSupplier {
|
||||||
public:
|
public:
|
||||||
|
// Result type for GetSymbolFile
|
||||||
|
enum SymbolResult {
|
||||||
|
// no symbols were found, but continue processing
|
||||||
|
NOT_FOUND,
|
||||||
|
|
||||||
|
// symbols were found, and the path has been placed in symbol_file
|
||||||
|
FOUND,
|
||||||
|
|
||||||
|
// stops processing the minidump immediately
|
||||||
|
INTERRUPT,
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~SymbolSupplier() {}
|
virtual ~SymbolSupplier() {}
|
||||||
|
|
||||||
// Returns the path to the symbol file for the given module.
|
// Retrieves the symbol file for the given CodeModule, placing the
|
||||||
virtual string GetSymbolFile(const CodeModule *module) = 0;
|
// path in symbol_file if successful.
|
||||||
|
virtual SymbolResult GetSymbolFile(const CodeModule *module,
|
||||||
|
string *symbol_file) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_airbag
|
} // namespace google_airbag
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
namespace google_airbag {
|
namespace google_airbag {
|
||||||
|
|
||||||
CallStack::~CallStack() {
|
CallStack::~CallStack() {
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStack::Clear() {
|
||||||
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
|
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
|
||||||
iterator != frames_.end();
|
iterator != frames_.end();
|
||||||
++iterator) {
|
++iterator) {
|
||||||
|
@ -45,13 +45,14 @@ MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier)
|
|||||||
MinidumpProcessor::~MinidumpProcessor() {
|
MinidumpProcessor::~MinidumpProcessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
||||||
|
const string &minidump_file, ProcessState *process_state) {
|
||||||
Minidump dump(minidump_file);
|
Minidump dump(minidump_file);
|
||||||
if (!dump.Read()) {
|
if (!dump.Read()) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<ProcessState> process_state(new ProcessState());
|
process_state->Clear();
|
||||||
|
|
||||||
const MDRawHeader *header = dump.header();
|
const MDRawHeader *header = dump.header();
|
||||||
assert(header);
|
assert(header);
|
||||||
@ -91,7 +92,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||||||
|
|
||||||
MinidumpThreadList *threads = dump.GetThreadList();
|
MinidumpThreadList *threads = dump.GetThreadList();
|
||||||
if (!threads) {
|
if (!threads) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found_requesting_thread = false;
|
bool found_requesting_thread = false;
|
||||||
@ -101,12 +102,12 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||||||
++thread_index) {
|
++thread_index) {
|
||||||
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
|
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int32_t thread_id;
|
u_int32_t thread_id;
|
||||||
if (!thread->GetThreadID(&thread_id)) {
|
if (!thread->GetThreadID(&thread_id)) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this thread is the thread that produced the minidump, don't process
|
// If this thread is the thread that produced the minidump, don't process
|
||||||
@ -122,7 +123,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||||||
if (has_requesting_thread && thread_id == requesting_thread_id) {
|
if (has_requesting_thread && thread_id == requesting_thread_id) {
|
||||||
if (found_requesting_thread) {
|
if (found_requesting_thread) {
|
||||||
// There can't be more than one requesting thread.
|
// There can't be more than one requesting thread.
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use processed_state->threads_.size() instead of thread_index.
|
// Use processed_state->threads_.size() instead of thread_index.
|
||||||
@ -148,7 +149,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||||||
|
|
||||||
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
||||||
if (!thread_memory) {
|
if (!thread_memory) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use process_state->modules_ instead of module_list, because the
|
// Use process_state->modules_ instead of module_list, because the
|
||||||
@ -165,23 +166,22 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||||||
process_state->modules_,
|
process_state->modules_,
|
||||||
supplier_));
|
supplier_));
|
||||||
if (!stackwalker.get()) {
|
if (!stackwalker.get()) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<CallStack> stack(stackwalker->Walk());
|
scoped_ptr<CallStack> stack(new CallStack());
|
||||||
if (!stack.get()) {
|
if (!stackwalker->Walk(stack.get())) {
|
||||||
return NULL;
|
return PROCESS_INTERRUPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_state->threads_.push_back(stack.release());
|
process_state->threads_.push_back(stack.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a requesting thread was indicated, it must be present.
|
// If a requesting thread was indicated, it must be present.
|
||||||
if (has_requesting_thread && !found_requesting_thread) {
|
if (has_requesting_thread && !found_requesting_thread) {
|
||||||
return NULL;
|
return PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_state.release();
|
return PROCESS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the MDRawSystemInfo from a minidump, or NULL if system info is
|
// Returns the MDRawSystemInfo from a minidump, or NULL if system info is
|
||||||
|
@ -62,18 +62,33 @@ using google_airbag::SymbolSupplier;
|
|||||||
|
|
||||||
class TestSymbolSupplier : public SymbolSupplier {
|
class TestSymbolSupplier : public SymbolSupplier {
|
||||||
public:
|
public:
|
||||||
virtual string GetSymbolFile(const CodeModule *module);
|
TestSymbolSupplier() : interrupt_(false) {}
|
||||||
|
|
||||||
|
virtual SymbolResult GetSymbolFile(const CodeModule *module,
|
||||||
|
string *symbol_file);
|
||||||
|
|
||||||
|
// When set to true, causes the SymbolSupplier to return INTERRUPT
|
||||||
|
void set_interrupt(bool interrupt) { interrupt_ = interrupt; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool interrupt_;
|
||||||
};
|
};
|
||||||
|
|
||||||
string TestSymbolSupplier::GetSymbolFile(const CodeModule *module) {
|
SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
|
||||||
|
const CodeModule *module, string *symbol_file) {
|
||||||
|
if (interrupt_) {
|
||||||
|
return INTERRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
if (module && module->code_file() == "C:\\test_app.exe") {
|
if (module && module->code_file() == "C:\\test_app.exe") {
|
||||||
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
*symbol_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||||
"/src/processor/testdata/symbols/test_app.pdb/" +
|
"/src/processor/testdata/symbols/test_app.pdb/" +
|
||||||
module->debug_identifier() +
|
module->debug_identifier() +
|
||||||
"/test_app.sym";
|
"/test_app.sym";
|
||||||
|
return FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool RunTests() {
|
static bool RunTests() {
|
||||||
@ -83,19 +98,20 @@ static bool RunTests() {
|
|||||||
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||||
"/src/processor/testdata/minidump2.dmp";
|
"/src/processor/testdata/minidump2.dmp";
|
||||||
|
|
||||||
scoped_ptr<ProcessState> state(processor.Process(minidump_file));
|
ProcessState state;
|
||||||
ASSERT_TRUE(state.get());
|
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||||
ASSERT_EQ(state->cpu(), "x86");
|
MinidumpProcessor::PROCESS_OK);
|
||||||
ASSERT_EQ(state->cpu_info(), "GenuineIntel family 6 model 13 stepping 8");
|
ASSERT_EQ(state.cpu(), "x86");
|
||||||
ASSERT_EQ(state->os(), "Windows NT");
|
ASSERT_EQ(state.cpu_info(), "GenuineIntel family 6 model 13 stepping 8");
|
||||||
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
|
ASSERT_EQ(state.os(), "Windows NT");
|
||||||
ASSERT_TRUE(state->crashed());
|
ASSERT_EQ(state.os_version(), "5.1.2600 Service Pack 2");
|
||||||
ASSERT_EQ(state->crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
ASSERT_TRUE(state.crashed());
|
||||||
ASSERT_EQ(state->crash_address(), 0x45);
|
ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
||||||
ASSERT_EQ(state->threads()->size(), 1);
|
ASSERT_EQ(state.crash_address(), 0x45);
|
||||||
ASSERT_EQ(state->requesting_thread(), 0);
|
ASSERT_EQ(state.threads()->size(), 1);
|
||||||
|
ASSERT_EQ(state.requesting_thread(), 0);
|
||||||
|
|
||||||
CallStack *stack = state->threads()->at(0);
|
CallStack *stack = state.threads()->at(0);
|
||||||
ASSERT_TRUE(stack);
|
ASSERT_TRUE(stack);
|
||||||
ASSERT_EQ(stack->frames()->size(), 4);
|
ASSERT_EQ(stack->frames()->size(), 4);
|
||||||
|
|
||||||
@ -131,17 +147,23 @@ static bool RunTests() {
|
|||||||
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
|
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
|
||||||
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
|
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
|
||||||
|
|
||||||
ASSERT_EQ(state->modules()->module_count(), 13);
|
ASSERT_EQ(state.modules()->module_count(), 13);
|
||||||
ASSERT_TRUE(state->modules()->GetMainModule());
|
ASSERT_TRUE(state.modules()->GetMainModule());
|
||||||
ASSERT_EQ(state->modules()->GetMainModule()->code_file(), "C:\\test_app.exe");
|
ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "C:\\test_app.exe");
|
||||||
ASSERT_FALSE(state->modules()->GetModuleForAddress(0));
|
ASSERT_FALSE(state.modules()->GetModuleForAddress(0));
|
||||||
ASSERT_EQ(state->modules()->GetMainModule(),
|
ASSERT_EQ(state.modules()->GetMainModule(),
|
||||||
state->modules()->GetModuleForAddress(0x400000));
|
state.modules()->GetModuleForAddress(0x400000));
|
||||||
ASSERT_EQ(state->modules()->GetModuleForAddress(0x7c801234)->debug_file(),
|
ASSERT_EQ(state.modules()->GetModuleForAddress(0x7c801234)->debug_file(),
|
||||||
"kernel32.pdb");
|
"kernel32.pdb");
|
||||||
ASSERT_EQ(state->modules()->GetModuleForAddress(0x77d43210)->version(),
|
ASSERT_EQ(state.modules()->GetModuleForAddress(0x77d43210)->version(),
|
||||||
"5.1.2600.2622");
|
"5.1.2600.2622");
|
||||||
|
|
||||||
|
// Test that the symbol supplier can interrupt processing
|
||||||
|
state.Clear();
|
||||||
|
supplier.set_interrupt(true);
|
||||||
|
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||||
|
MinidumpProcessor::PROCESS_INTERRUPTED);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,18 +195,18 @@ static bool PrintMinidumpProcess(const string &minidump_file,
|
|||||||
MinidumpProcessor minidump_processor(symbol_supplier.get());
|
MinidumpProcessor minidump_processor(symbol_supplier.get());
|
||||||
|
|
||||||
// Process the minidump.
|
// Process the minidump.
|
||||||
scoped_ptr<ProcessState> process_state(
|
ProcessState process_state;
|
||||||
minidump_processor.Process(minidump_file));
|
if (minidump_processor.Process(minidump_file, &process_state) !=
|
||||||
if (!process_state.get()) {
|
MinidumpProcessor::PROCESS_OK) {
|
||||||
fprintf(stderr, "MinidumpProcessor::Process failed\n");
|
fprintf(stderr, "MinidumpProcessor::Process failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print OS and CPU information.
|
// Print OS and CPU information.
|
||||||
string cpu = process_state->cpu();
|
string cpu = process_state.cpu();
|
||||||
string cpu_info = process_state->cpu_info();
|
string cpu_info = process_state.cpu_info();
|
||||||
printf("Operating system: %s\n", process_state->os().c_str());
|
printf("Operating system: %s\n", process_state.os().c_str());
|
||||||
printf(" %s\n", process_state->os_version().c_str());
|
printf(" %s\n", process_state.os_version().c_str());
|
||||||
printf("CPU: %s\n", cpu.c_str());
|
printf("CPU: %s\n", cpu.c_str());
|
||||||
if (!cpu_info.empty()) {
|
if (!cpu_info.empty()) {
|
||||||
// This field is optional.
|
// This field is optional.
|
||||||
@ -215,36 +215,36 @@ static bool PrintMinidumpProcess(const string &minidump_file,
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
// Print crash information.
|
// Print crash information.
|
||||||
if (process_state->crashed()) {
|
if (process_state.crashed()) {
|
||||||
printf("Crash reason: %s\n", process_state->crash_reason().c_str());
|
printf("Crash reason: %s\n", process_state.crash_reason().c_str());
|
||||||
printf("Crash address: 0x%llx\n", process_state->crash_address());
|
printf("Crash address: 0x%llx\n", process_state.crash_address());
|
||||||
} else {
|
} else {
|
||||||
printf("No crash\n");
|
printf("No crash\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the thread that requested the dump is known, print it first.
|
// If the thread that requested the dump is known, print it first.
|
||||||
int requesting_thread = process_state->requesting_thread();
|
int requesting_thread = process_state.requesting_thread();
|
||||||
if (requesting_thread != -1) {
|
if (requesting_thread != -1) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Thread %d (%s)\n",
|
printf("Thread %d (%s)\n",
|
||||||
requesting_thread,
|
requesting_thread,
|
||||||
process_state->crashed() ? "crashed" :
|
process_state.crashed() ? "crashed" :
|
||||||
"requested dump, did not crash");
|
"requested dump, did not crash");
|
||||||
PrintStack(process_state->threads()->at(requesting_thread), cpu);
|
PrintStack(process_state.threads()->at(requesting_thread), cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print all of the threads in the dump.
|
// Print all of the threads in the dump.
|
||||||
int thread_count = process_state->threads()->size();
|
int thread_count = process_state.threads()->size();
|
||||||
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
||||||
if (thread_index != requesting_thread) {
|
if (thread_index != requesting_thread) {
|
||||||
// Don't print the crash thread again, it was already printed.
|
// Don't print the crash thread again, it was already printed.
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Thread %d\n", thread_index);
|
printf("Thread %d\n", thread_index);
|
||||||
PrintStack(process_state->threads()->at(thread_index), cpu);
|
PrintStack(process_state.threads()->at(thread_index), cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintModules(process_state->modules());
|
PrintModules(process_state.modules());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,27 @@
|
|||||||
namespace google_airbag {
|
namespace google_airbag {
|
||||||
|
|
||||||
ProcessState::~ProcessState() {
|
ProcessState::~ProcessState() {
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessState::Clear() {
|
||||||
|
time_date_stamp_ = 0;
|
||||||
|
crashed_ = false;
|
||||||
|
crash_reason_.clear();
|
||||||
|
crash_address_ = 0;
|
||||||
|
requesting_thread_ = -1;
|
||||||
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
|
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
|
||||||
iterator != threads_.end();
|
iterator != threads_.end();
|
||||||
++iterator) {
|
++iterator) {
|
||||||
delete *iterator;
|
delete *iterator;
|
||||||
}
|
}
|
||||||
|
threads_.clear();
|
||||||
|
os_.clear();
|
||||||
|
os_version_.clear();
|
||||||
|
cpu_.clear();
|
||||||
|
cpu_info_.clear();
|
||||||
delete modules_;
|
delete modules_;
|
||||||
|
modules_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace google_airbag
|
} // namespace google_airbag
|
||||||
|
@ -33,16 +33,19 @@
|
|||||||
//
|
//
|
||||||
// Author: Mark Mentovai
|
// Author: Mark Mentovai
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "processor/simple_symbol_supplier.h"
|
#include "processor/simple_symbol_supplier.h"
|
||||||
#include "google_airbag/processor/code_module.h"
|
#include "google_airbag/processor/code_module.h"
|
||||||
#include "processor/pathname_stripper.h"
|
#include "processor/pathname_stripper.h"
|
||||||
|
|
||||||
namespace google_airbag {
|
namespace google_airbag {
|
||||||
|
|
||||||
string SimpleSymbolSupplier::GetSymbolFileAtPath(const CodeModule *module,
|
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
|
||||||
const string &root_path) {
|
const CodeModule *module, const string &root_path, string *symbol_file) {
|
||||||
|
assert(symbol_file);
|
||||||
if (!module)
|
if (!module)
|
||||||
return "";
|
return NOT_FOUND;
|
||||||
|
|
||||||
// Start with the base path.
|
// Start with the base path.
|
||||||
string path = root_path;
|
string path = root_path;
|
||||||
@ -51,14 +54,14 @@ string SimpleSymbolSupplier::GetSymbolFileAtPath(const CodeModule *module,
|
|||||||
path.append("/");
|
path.append("/");
|
||||||
string debug_file_name = PathnameStripper::File(module->debug_file());
|
string debug_file_name = PathnameStripper::File(module->debug_file());
|
||||||
if (debug_file_name.empty())
|
if (debug_file_name.empty())
|
||||||
return "";
|
return NOT_FOUND;
|
||||||
path.append(debug_file_name);
|
path.append(debug_file_name);
|
||||||
|
|
||||||
// Append the identifier as a directory name.
|
// Append the identifier as a directory name.
|
||||||
path.append("/");
|
path.append("/");
|
||||||
string identifier = module->debug_identifier();
|
string identifier = module->debug_identifier();
|
||||||
if (identifier.empty())
|
if (identifier.empty())
|
||||||
return "";
|
return NOT_FOUND;
|
||||||
path.append(identifier);
|
path.append(identifier);
|
||||||
|
|
||||||
// Transform the debug file name into one ending in .sym. If the existing
|
// Transform the debug file name into one ending in .sym. If the existing
|
||||||
@ -76,7 +79,8 @@ string SimpleSymbolSupplier::GetSymbolFileAtPath(const CodeModule *module,
|
|||||||
}
|
}
|
||||||
path.append(".sym");
|
path.append(".sym");
|
||||||
|
|
||||||
return path;
|
*symbol_file = path;
|
||||||
|
return FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace google_airbag
|
} // namespace google_airbag
|
||||||
|
@ -92,13 +92,15 @@ class SimpleSymbolSupplier : public SymbolSupplier {
|
|||||||
|
|
||||||
// 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 string GetSymbolFile(const CodeModule *module) {
|
virtual SymbolResult GetSymbolFile(const CodeModule *module,
|
||||||
return GetSymbolFileAtPath(module, path_);
|
string *symbol_file) {
|
||||||
|
return GetSymbolFileAtPath(module, path_, symbol_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
string GetSymbolFileAtPath(const CodeModule *module,
|
SymbolResult GetSymbolFileAtPath(const CodeModule *module,
|
||||||
const string &root_path);
|
const string &root_path,
|
||||||
|
string *symbol_file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string path_;
|
string path_;
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
// Author: Mark Mentovai
|
// Author: Mark Mentovai
|
||||||
|
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "google_airbag/processor/stackwalker.h"
|
#include "google_airbag/processor/stackwalker.h"
|
||||||
#include "google_airbag/processor/call_stack.h"
|
#include "google_airbag/processor/call_stack.h"
|
||||||
#include "google_airbag/processor/code_module.h"
|
#include "google_airbag/processor/code_module.h"
|
||||||
@ -57,10 +59,10 @@ Stackwalker::Stackwalker(MemoryRegion *memory, const CodeModules *modules,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CallStack* Stackwalker::Walk() {
|
bool Stackwalker::Walk(CallStack *stack) {
|
||||||
|
assert(stack);
|
||||||
SourceLineResolver resolver;
|
SourceLineResolver resolver;
|
||||||
|
stack->Clear();
|
||||||
scoped_ptr<CallStack> stack(new CallStack());
|
|
||||||
|
|
||||||
// stack_frame_info parallels the CallStack. The vector is passed to the
|
// stack_frame_info parallels the CallStack. The vector is passed to the
|
||||||
// GetCallerFrame function. It contains information that may be helpful
|
// GetCallerFrame function. It contains information that may be helpful
|
||||||
@ -87,9 +89,18 @@ CallStack* Stackwalker::Walk() {
|
|||||||
if (module) {
|
if (module) {
|
||||||
frame->module = module;
|
frame->module = module;
|
||||||
if (!resolver.HasModule(frame->module->code_file()) && supplier_) {
|
if (!resolver.HasModule(frame->module->code_file()) && supplier_) {
|
||||||
string symbol_file = supplier_->GetSymbolFile(module);
|
string symbol_file;
|
||||||
if (!symbol_file.empty()) {
|
SymbolSupplier::SymbolResult symbol_result =
|
||||||
|
supplier_->GetSymbolFile(module, &symbol_file);
|
||||||
|
|
||||||
|
switch (symbol_result) {
|
||||||
|
case SymbolSupplier::FOUND:
|
||||||
resolver.LoadModule(frame->module->code_file(), symbol_file);
|
resolver.LoadModule(frame->module->code_file(), symbol_file);
|
||||||
|
break;
|
||||||
|
case SymbolSupplier::NOT_FOUND:
|
||||||
|
break; // nothing to do
|
||||||
|
case SymbolSupplier::INTERRUPT:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame_info.reset(resolver.FillSourceLineInfo(frame.get()));
|
frame_info.reset(resolver.FillSourceLineInfo(frame.get()));
|
||||||
@ -105,10 +116,10 @@ CallStack* Stackwalker::Walk() {
|
|||||||
frame_info.reset(NULL);
|
frame_info.reset(NULL);
|
||||||
|
|
||||||
// Get the next frame and take ownership.
|
// Get the next frame and take ownership.
|
||||||
frame.reset(GetCallerFrame(stack.get(), stack_frame_info));
|
frame.reset(GetCallerFrame(stack, stack_frame_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack.release();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user