in src/processor/minidump_processor.cc [80:328]
ProcessResult MinidumpProcessor::Process(
Minidump *dump, ProcessState *process_state) {
assert(dump);
assert(process_state);
process_state->Clear();
const MDRawHeader *header = dump->header();
if (!header) {
BPLOG(ERROR) << "Minidump " << dump->path() << " has no header";
return PROCESS_ERROR_NO_MINIDUMP_HEADER;
}
process_state->time_date_stamp_ = header->time_date_stamp;
bool has_process_create_time =
GetProcessCreateTime(dump, &process_state->process_create_time_);
bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_);
bool has_os_info = GetOSInfo(dump, &process_state->system_info_);
uint32_t dump_thread_id = 0;
bool has_dump_thread = false;
uint32_t requesting_thread_id = 0;
bool has_requesting_thread = false;
MinidumpBreakpadInfo *breakpad_info = dump->GetBreakpadInfo();
if (breakpad_info) {
has_dump_thread = breakpad_info->GetDumpThreadID(&dump_thread_id);
has_requesting_thread =
breakpad_info->GetRequestingThreadID(&requesting_thread_id);
}
MinidumpException *exception = dump->GetException();
if (exception) {
process_state->crashed_ = true;
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
process_state->crash_reason_ = GetCrashReason(
dump, &process_state->crash_address_);
}
// This will just return an empty string if it doesn't exist.
process_state->assertion_ = GetAssertion(dump);
MinidumpModuleList *module_list = dump->GetModuleList();
// Put a copy of the module list into ProcessState object. This is not
// necessarily a MinidumpModuleList, but it adheres to the CodeModules
// interface, which is all that ProcessState needs to expose.
if (module_list) {
process_state->modules_ = module_list->Copy();
process_state->shrunk_range_modules_ =
process_state->modules_->GetShrunkRangeModules();
for (unsigned int i = 0;
i < process_state->shrunk_range_modules_.size();
i++) {
linked_ptr<const CodeModule> module =
process_state->shrunk_range_modules_[i];
BPLOG(INFO) << "The range for module " << module->code_file()
<< " was shrunk down by " << HexString(
module->shrink_down_delta()) << " bytes. ";
}
}
MinidumpUnloadedModuleList *unloaded_module_list =
dump->GetUnloadedModuleList();
if (unloaded_module_list) {
process_state->unloaded_modules_ = unloaded_module_list->Copy();
}
MinidumpMemoryList *memory_list = dump->GetMemoryList();
if (memory_list) {
BPLOG(INFO) << "Found " << memory_list->region_count()
<< " memory regions.";
}
MinidumpThreadList *threads = dump->GetThreadList();
if (!threads) {
BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list";
return PROCESS_ERROR_NO_THREAD_LIST;
}
BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
(has_cpu_info ? "" : "no ") << "CPU info, " <<
(has_os_info ? "" : "no ") << "OS info, " <<
(breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
(exception != NULL ? "" : "no ") << "exception, " <<
(module_list != NULL ? "" : "no ") << "module list, " <<
(threads != NULL ? "" : "no ") << "thread list, " <<
(has_dump_thread ? "" : "no ") << "dump thread, " <<
(has_requesting_thread ? "" : "no ") << "requesting thread, and " <<
(has_process_create_time ? "" : "no ") << "process create time";
bool interrupted = false;
bool found_requesting_thread = false;
unsigned int thread_count = threads->thread_count();
// Reset frame_symbolizer_ at the beginning of stackwalk for each minidump.
frame_symbolizer_->Reset();
for (unsigned int thread_index = 0;
thread_index < thread_count;
++thread_index) {
char thread_string_buffer[64];
snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d",
thread_index, thread_count);
string thread_string = dump->path() + ":" + thread_string_buffer;
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
if (!thread) {
BPLOG(ERROR) << "Could not get thread for " << thread_string;
return PROCESS_ERROR_GETTING_THREAD;
}
uint32_t thread_id;
if (!thread->GetThreadID(&thread_id)) {
BPLOG(ERROR) << "Could not get thread ID for " << thread_string;
return PROCESS_ERROR_GETTING_THREAD_ID;
}
thread_string += " id " + HexString(thread_id);
BPLOG(INFO) << "Looking at thread " << thread_string;
// If this thread is the thread that produced the minidump, don't process
// it. Because of the problems associated with a thread producing a
// dump of itself (when both its context and its stack are in flux),
// processing that stack wouldn't provide much useful data.
if (has_dump_thread && thread_id == dump_thread_id) {
continue;
}
MinidumpContext *context = thread->GetContext();
if (has_requesting_thread && thread_id == requesting_thread_id) {
if (found_requesting_thread) {
// There can't be more than one requesting thread.
BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string;
return PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS;
}
// Use processed_state->threads_.size() instead of thread_index.
// thread_index points to the thread index in the minidump, which
// might be greater than the thread index in the threads vector if
// any of the minidump's threads are skipped and not placed into the
// processed threads vector. The thread vector's current size will
// be the index of the current thread when it's pushed into the
// vector.
process_state->requesting_thread_ = process_state->threads_.size();
found_requesting_thread = true;
if (process_state->crashed_) {
// Use the exception record's context for the crashed thread, instead
// of the thread's own context. For the crashed thread, the thread's
// own context is the state inside the exception handler. Using it
// would not result in the expected stack trace from the time of the
// crash. If the exception context is invalid, however, we fall back
// on the thread context.
MinidumpContext *ctx = exception->GetContext();
context = ctx ? ctx : thread->GetContext();
}
}
// If the memory region for the stack cannot be read using the RVA stored
// in the memory descriptor inside MINIDUMP_THREAD, try to locate and use
// a memory region (containing the stack) from the minidump memory list.
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
if (!thread_memory && memory_list) {
uint64_t start_stack_memory_range = thread->GetStartOfStackMemoryRange();
if (start_stack_memory_range) {
thread_memory = memory_list->GetMemoryRegionForAddress(
start_stack_memory_range);
}
}
if (!thread_memory) {
BPLOG(ERROR) << "No memory region for " << thread_string;
}
// Use process_state->modules_ instead of module_list, because the
// |modules| argument will be used to populate the |module| fields in
// the returned StackFrame objects, which will be placed into the
// returned ProcessState object. module_list's lifetime is only as
// long as the Minidump object: it will be deleted when this function
// returns. process_state->modules_ is owned by the ProcessState object
// (just like the StackFrame objects), and is much more suitable for this
// task.
scoped_ptr<Stackwalker> stackwalker(
Stackwalker::StackwalkerForCPU(process_state->system_info(),
context,
thread_memory,
process_state->modules_,
process_state->unloaded_modules_,
frame_symbolizer_));
scoped_ptr<CallStack> stack(new CallStack());
if (stackwalker.get()) {
if (!stackwalker->Walk(stack.get(),
&process_state->modules_without_symbols_,
&process_state->modules_with_corrupt_symbols_)) {
BPLOG(INFO) << "Stackwalker interrupt (missing symbols?) at "
<< thread_string;
interrupted = true;
}
} else {
// Threads with missing CPU contexts will hit this, but
// don't abort processing the rest of the dump just for
// one bad thread.
BPLOG(ERROR) << "No stackwalker for " << thread_string;
}
stack->set_tid(thread_id);
process_state->threads_.push_back(stack.release());
process_state->thread_memory_regions_.push_back(thread_memory);
}
if (interrupted) {
BPLOG(INFO) << "Processing interrupted for " << dump->path();
return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
}
// If a requesting thread was indicated, it must be present.
if (has_requesting_thread && !found_requesting_thread) {
// Don't mark as an error, but invalidate the requesting thread
BPLOG(ERROR) << "Minidump indicated requesting thread " <<
HexString(requesting_thread_id) << ", not found in " <<
dump->path();
process_state->requesting_thread_ = -1;
}
// Exploitability defaults to EXPLOITABILITY_NOT_ANALYZED
process_state->exploitability_ = EXPLOITABILITY_NOT_ANALYZED;
// If an exploitability run was requested we perform the platform specific
// rating.
if (enable_exploitability_) {
scoped_ptr<Exploitability> exploitability(
Exploitability::ExploitabilityForPlatform(dump,
process_state,
enable_objdump_));
// The engine will be null if the platform is not supported
if (exploitability != NULL) {
process_state->exploitability_ = exploitability->CheckExploitability();
} else {
process_state->exploitability_ = EXPLOITABILITY_ERR_NOENGINE;
}
}
BPLOG(INFO) << "Processed " << dump->path();
return PROCESS_OK;
}