// Copyright (c) 2006, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "google_breakpad/processor/minidump_processor.h" #include #include #include "common/scoped_ptr.h" #include "common/stdio_wrapper.h" #include "common/using_std_string.h" #include "google_breakpad/processor/call_stack.h" #include "google_breakpad/processor/minidump.h" #include "google_breakpad/processor/process_state.h" #include "google_breakpad/processor/exploitability.h" #include "google_breakpad/processor/stack_frame_symbolizer.h" #include "processor/logging.h" #include "processor/stackwalker_x86.h" #include "processor/symbolic_constants_win.h" namespace google_breakpad { MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier, SourceLineResolverInterface *resolver) : frame_symbolizer_(new StackFrameSymbolizer(supplier, resolver)), own_frame_symbolizer_(true), enable_exploitability_(false), enable_objdump_(false) { } MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier, SourceLineResolverInterface *resolver, bool enable_exploitability) : frame_symbolizer_(new StackFrameSymbolizer(supplier, resolver)), own_frame_symbolizer_(true), enable_exploitability_(enable_exploitability), enable_objdump_(false) { } MinidumpProcessor::MinidumpProcessor(StackFrameSymbolizer *frame_symbolizer, bool enable_exploitability) : frame_symbolizer_(frame_symbolizer), own_frame_symbolizer_(false), enable_exploitability_(enable_exploitability), enable_objdump_(false) { assert(frame_symbolizer_); } MinidumpProcessor::~MinidumpProcessor() { if (own_frame_symbolizer_) delete frame_symbolizer_; } 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_); process_state->exception_record_.set_code( exception->exception()->exception_record.exception_code, // TODO(ivanpe): Populate description. /* description = */ ""); process_state->exception_record_.set_flags( exception->exception()->exception_record.exception_flags, // TODO(ivanpe): Populate description. /* description = */ ""); process_state->exception_record_.set_nested_exception_record_address( exception->exception()->exception_record.exception_record); process_state->exception_record_.set_address(process_state->crash_address_); for (uint32_t i = 0; i < exception->exception()->exception_record.number_parameters; i++) { process_state->exception_record_.add_parameter( exception->exception()->exception_record.exception_information[i], // TODO(ivanpe): Populate description. /* description = */ ""); } } MinidumpMacCrashInfo *crash_info = dump->GetMacCrashInfo(); if (crash_info) { process_state->mac_crash_info_ = crash_info->description(); process_state->mac_crash_info_records_ = crash_info->records(); } // 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 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."; } MinidumpThreadNamesList* thread_names_list = dump->GetThreadNamesList(); if (thread_names_list) { BPLOG(INFO) << "Found " << thread_names_list->name_count() << " thread names."; } 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::StackwalkerForCPU(process_state->system_info(), context, thread_memory, process_state->modules_, process_state->unloaded_modules_, frame_symbolizer_)); scoped_ptr 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); stack->set_last_error(thread->GetLastError()); if (thread_names_list) { stack->set_name(thread_names_list->GetNameForThreadId(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::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; } ProcessResult MinidumpProcessor::Process( const string &minidump_file, ProcessState *process_state) { BPLOG(INFO) << "Processing minidump in file " << minidump_file; Minidump dump(minidump_file); if (!dump.Read()) { BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read"; return PROCESS_ERROR_MINIDUMP_NOT_FOUND; } return Process(&dump, process_state); } // Returns the MDRawSystemInfo from a minidump, or NULL if system info is // not available from the minidump. If system_info is non-NULL, it is used // to pass back the MinidumpSystemInfo object. static const MDRawSystemInfo* GetSystemInfo(Minidump *dump, MinidumpSystemInfo **system_info) { MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo(); if (!minidump_system_info) return NULL; if (system_info) *system_info = minidump_system_info; return minidump_system_info->system_info(); } static uint64_t GetAddressForArchitecture(const MDCPUArchitecture architecture, size_t raw_address) { switch (architecture) { case MD_CPU_ARCHITECTURE_X86: case MD_CPU_ARCHITECTURE_MIPS: case MD_CPU_ARCHITECTURE_PPC: case MD_CPU_ARCHITECTURE_SHX: case MD_CPU_ARCHITECTURE_ARM: case MD_CPU_ARCHITECTURE_X86_WIN64: // 32-bit architectures, mask the upper bits. return raw_address & 0xffffffffULL; default: // All other architectures either have 64-bit pointers or it's impossible // to tell from the minidump (e.g. MSIL or SPARC) so use 64-bits anyway. return raw_address; } } // Extract CPU info string from ARM-specific MDRawSystemInfo structure. // raw_info: pointer to source MDRawSystemInfo. // cpu_info: address of target string, cpu info text will be appended to it. static void GetARMCpuInfo(const MDRawSystemInfo* raw_info, string* cpu_info) { assert(raw_info != NULL && cpu_info != NULL); // Write ARM architecture version. char cpu_string[32]; snprintf(cpu_string, sizeof(cpu_string), "ARMv%d", raw_info->processor_level); cpu_info->append(cpu_string); // There is no good list of implementer id values, but the following // pages provide some help: // http://comments.gmane.org/gmane.linux.linaro.devel/6903 // http://forum.xda-developers.com/archive/index.php/t-480226.html const struct { uint32_t id; const char* name; } vendors[] = { { 0x41, "ARM" }, { 0x51, "Qualcomm" }, { 0x56, "Marvell" }, { 0x69, "Intel/Marvell" }, }; const struct { uint32_t id; const char* name; } parts[] = { { 0x4100c050, "Cortex-A5" }, { 0x4100c080, "Cortex-A8" }, { 0x4100c090, "Cortex-A9" }, { 0x4100c0f0, "Cortex-A15" }, { 0x4100c140, "Cortex-R4" }, { 0x4100c150, "Cortex-R5" }, { 0x4100b360, "ARM1136" }, { 0x4100b560, "ARM1156" }, { 0x4100b760, "ARM1176" }, { 0x4100b020, "ARM11-MPCore" }, { 0x41009260, "ARM926" }, { 0x41009460, "ARM946" }, { 0x41009660, "ARM966" }, { 0x510006f0, "Krait" }, { 0x510000f0, "Scorpion" }, }; const struct { uint32_t hwcap; const char* name; } features[] = { { MD_CPU_ARM_ELF_HWCAP_SWP, "swp" }, { MD_CPU_ARM_ELF_HWCAP_HALF, "half" }, { MD_CPU_ARM_ELF_HWCAP_THUMB, "thumb" }, { MD_CPU_ARM_ELF_HWCAP_26BIT, "26bit" }, { MD_CPU_ARM_ELF_HWCAP_FAST_MULT, "fastmult" }, { MD_CPU_ARM_ELF_HWCAP_FPA, "fpa" }, { MD_CPU_ARM_ELF_HWCAP_VFP, "vfpv2" }, { MD_CPU_ARM_ELF_HWCAP_EDSP, "edsp" }, { MD_CPU_ARM_ELF_HWCAP_JAVA, "java" }, { MD_CPU_ARM_ELF_HWCAP_IWMMXT, "iwmmxt" }, { MD_CPU_ARM_ELF_HWCAP_CRUNCH, "crunch" }, { MD_CPU_ARM_ELF_HWCAP_THUMBEE, "thumbee" }, { MD_CPU_ARM_ELF_HWCAP_NEON, "neon" }, { MD_CPU_ARM_ELF_HWCAP_VFPv3, "vfpv3" }, { MD_CPU_ARM_ELF_HWCAP_VFPv3D16, "vfpv3d16" }, { MD_CPU_ARM_ELF_HWCAP_TLS, "tls" }, { MD_CPU_ARM_ELF_HWCAP_VFPv4, "vfpv4" }, { MD_CPU_ARM_ELF_HWCAP_IDIVA, "idiva" }, { MD_CPU_ARM_ELF_HWCAP_IDIVT, "idivt" }, }; uint32_t cpuid = raw_info->cpu.arm_cpu_info.cpuid; if (cpuid != 0) { // Extract vendor name from CPUID const char* vendor = NULL; uint32_t vendor_id = (cpuid >> 24) & 0xff; for (size_t i = 0; i < sizeof(vendors)/sizeof(vendors[0]); ++i) { if (vendors[i].id == vendor_id) { vendor = vendors[i].name; break; } } cpu_info->append(" "); if (vendor) { cpu_info->append(vendor); } else { snprintf(cpu_string, sizeof(cpu_string), "vendor(0x%x)", vendor_id); cpu_info->append(cpu_string); } // Extract part name from CPUID uint32_t part_id = (cpuid & 0xff00fff0); const char* part = NULL; for (size_t i = 0; i < sizeof(parts)/sizeof(parts[0]); ++i) { if (parts[i].id == part_id) { part = parts[i].name; break; } } cpu_info->append(" "); if (part != NULL) { cpu_info->append(part); } else { snprintf(cpu_string, sizeof(cpu_string), "part(0x%x)", part_id); cpu_info->append(cpu_string); } } uint32_t elf_hwcaps = raw_info->cpu.arm_cpu_info.elf_hwcaps; if (elf_hwcaps != 0) { cpu_info->append(" features: "); const char* comma = ""; for (size_t i = 0; i < sizeof(features)/sizeof(features[0]); ++i) { if (elf_hwcaps & features[i].hwcap) { cpu_info->append(comma); cpu_info->append(features[i].name); comma = ","; } } } } // static bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) { assert(dump); assert(info); info->cpu.clear(); info->cpu_info.clear(); MinidumpSystemInfo *system_info; const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); if (!raw_system_info) return false; switch (raw_system_info->processor_architecture) { case MD_CPU_ARCHITECTURE_X86: case MD_CPU_ARCHITECTURE_AMD64: { if (raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_X86) info->cpu = "x86"; else info->cpu = "amd64"; const string *cpu_vendor = system_info->GetCPUVendor(); if (cpu_vendor) { info->cpu_info = *cpu_vendor; info->cpu_info.append(" "); } char x86_info[36]; snprintf(x86_info, sizeof(x86_info), "family %u model %u stepping %u", raw_system_info->processor_level, raw_system_info->processor_revision >> 8, raw_system_info->processor_revision & 0xff); info->cpu_info.append(x86_info); break; } case MD_CPU_ARCHITECTURE_PPC: { info->cpu = "ppc"; break; } case MD_CPU_ARCHITECTURE_PPC64: { info->cpu = "ppc64"; break; } case MD_CPU_ARCHITECTURE_SPARC: { info->cpu = "sparc"; break; } case MD_CPU_ARCHITECTURE_ARM: { info->cpu = "arm"; GetARMCpuInfo(raw_system_info, &info->cpu_info); break; } case MD_CPU_ARCHITECTURE_ARM64: case MD_CPU_ARCHITECTURE_ARM64_OLD: { info->cpu = "arm64"; break; } case MD_CPU_ARCHITECTURE_MIPS: { info->cpu = "mips"; break; } case MD_CPU_ARCHITECTURE_MIPS64: { info->cpu = "mips64"; break; } default: { // Assign the numeric architecture ID into the CPU string. char cpu_string[7]; snprintf(cpu_string, sizeof(cpu_string), "0x%04x", raw_system_info->processor_architecture); info->cpu = cpu_string; break; } } info->cpu_count = raw_system_info->number_of_processors; return true; } // static bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) { assert(dump); assert(info); info->os.clear(); info->os_short.clear(); info->os_version.clear(); MinidumpSystemInfo *system_info; const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); if (!raw_system_info) return false; info->os_short = system_info->GetOS(); switch (raw_system_info->platform_id) { case MD_OS_WIN32_NT: { info->os = "Windows NT"; break; } case MD_OS_WIN32_WINDOWS: { info->os = "Windows"; break; } case MD_OS_MAC_OS_X: { info->os = "Mac OS X"; break; } case MD_OS_IOS: { info->os = "iOS"; break; } case MD_OS_LINUX: { info->os = "Linux"; break; } case MD_OS_SOLARIS: { info->os = "Solaris"; break; } case MD_OS_ANDROID: { info->os = "Android"; break; } case MD_OS_PS3: { info->os = "PS3"; break; } case MD_OS_NACL: { info->os = "NaCl"; break; } case MD_OS_FUCHSIA: { info->os = "Fuchsia"; break; } default: { // Assign the numeric platform ID into the OS string. char os_string[11]; snprintf(os_string, sizeof(os_string), "0x%08x", raw_system_info->platform_id); info->os = os_string; break; } } char os_version_string[33]; snprintf(os_version_string, sizeof(os_version_string), "%u.%u.%u", raw_system_info->major_version, raw_system_info->minor_version, raw_system_info->build_number); info->os_version = os_version_string; const string *csd_version = system_info->GetCSDVersion(); if (csd_version) { info->os_version.append(" "); info->os_version.append(*csd_version); } return true; } // static bool MinidumpProcessor::GetProcessCreateTime(Minidump* dump, uint32_t* process_create_time) { assert(dump); assert(process_create_time); *process_create_time = 0; MinidumpMiscInfo* minidump_misc_info = dump->GetMiscInfo(); if (!minidump_misc_info) { return false; } const MDRawMiscInfo* md_raw_misc_info = minidump_misc_info->misc_info(); if (!md_raw_misc_info) { return false; } if (!(md_raw_misc_info->flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES)) { return false; } *process_create_time = md_raw_misc_info->process_create_time; return true; } // static string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) { MinidumpException *exception = dump->GetException(); if (!exception) return ""; const MDRawExceptionStream *raw_exception = exception->exception(); if (!raw_exception) return ""; if (address) *address = raw_exception->exception_record.exception_address; // The reason value is OS-specific and possibly CPU-specific. Set up // sensible numeric defaults for the reason string in case we can't // map the codes to a string (because there's no system info, or because // it's an unrecognized platform, or because it's an unrecognized code.) char reason_string[24]; char flags_string[11]; uint32_t exception_code = raw_exception->exception_record.exception_code; uint32_t exception_flags = raw_exception->exception_record.exception_flags; snprintf(flags_string, sizeof(flags_string), "0x%08x", exception_flags); snprintf(reason_string, sizeof(reason_string), "0x%08x / %s", exception_code, flags_string); string reason = reason_string; const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, NULL); if (!raw_system_info) return reason; switch (raw_system_info->platform_id) { case MD_OS_FUCHSIA: { switch (exception_code) { case MD_EXCEPTION_CODE_FUCHSIA_GENERAL: reason = "GENERAL / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_FATAL_PAGE_FAULT: reason = "FATAL_PAGE_FAULT / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_UNDEFINED_INSTRUCTION: reason = "UNDEFINED_INSTRUCTION / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_SW_BREAKPOINT: reason = "SW_BREAKPOINT / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_HW_BREAKPOINT: reason = "HW_BREAKPOINT / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_UNALIGNED_ACCESS: reason = "UNALIGNED_ACCESS / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_THREAD_STARTING: reason = "THREAD_STARTING / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_THREAD_EXITING: reason = "THREAD_EXITING / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_POLICY_ERROR: reason = "POLICY_ERROR / "; reason.append(flags_string); break; case MD_EXCEPTION_CODE_FUCHSIA_PROCESS_STARTING: reason = "PROCESS_STARTING / "; reason.append(flags_string); break; default: BPLOG(INFO) << "Unknown exception reason " << reason; } break; } case MD_OS_MAC_OS_X: case MD_OS_IOS: { switch (exception_code) { case MD_EXCEPTION_MAC_BAD_ACCESS: reason = "EXC_BAD_ACCESS / "; switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS: reason.append("KERN_INVALID_ADDRESS"); break; case MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE: reason.append("KERN_PROTECTION_FAILURE"); break; case MD_EXCEPTION_CODE_MAC_NO_ACCESS: reason.append("KERN_NO_ACCESS"); break; case MD_EXCEPTION_CODE_MAC_MEMORY_FAILURE: reason.append("KERN_MEMORY_FAILURE"); break; case MD_EXCEPTION_CODE_MAC_MEMORY_ERROR: reason.append("KERN_MEMORY_ERROR"); break; case MD_EXCEPTION_CODE_MAC_CODESIGN_ERROR: reason.append("KERN_CODESIGN_ERROR"); break; default: // arm and ppc overlap if (raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_ARM || raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_ARM64_OLD) { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN: reason.append("EXC_ARM_DA_ALIGN"); break; case MD_EXCEPTION_CODE_MAC_ARM_DA_DEBUG: reason.append("EXC_ARM_DA_DEBUG"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } } else if (raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_PPC) { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ: reason.append("EXC_PPC_VM_PROT_READ"); break; case MD_EXCEPTION_CODE_MAC_PPC_BADSPACE: reason.append("EXC_PPC_BADSPACE"); break; case MD_EXCEPTION_CODE_MAC_PPC_UNALIGNED: reason.append("EXC_PPC_UNALIGNED"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } } else if (raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_X86 || raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT: reason.append("EXC_I386_GPFLT"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } } else { reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; } break; } break; case MD_EXCEPTION_MAC_BAD_INSTRUCTION: reason = "EXC_BAD_INSTRUCTION / "; switch (raw_system_info->processor_architecture) { case MD_CPU_ARCHITECTURE_ARM: case MD_CPU_ARCHITECTURE_ARM64_OLD: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED: reason.append("EXC_ARM_UNDEFINED"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_CPU_ARCHITECTURE_PPC: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_PPC_INVALID_SYSCALL: reason.append("EXC_PPC_INVALID_SYSCALL"); break; case MD_EXCEPTION_CODE_MAC_PPC_UNIMPLEMENTED_INSTRUCTION: reason.append("EXC_PPC_UNIPL_INST"); break; case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_INSTRUCTION: reason.append("EXC_PPC_PRIVINST"); break; case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_REGISTER: reason.append("EXC_PPC_PRIVREG"); break; case MD_EXCEPTION_CODE_MAC_PPC_TRACE: reason.append("EXC_PPC_TRACE"); break; case MD_EXCEPTION_CODE_MAC_PPC_PERFORMANCE_MONITOR: reason.append("EXC_PPC_PERFMON"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_CPU_ARCHITECTURE_AMD64: case MD_CPU_ARCHITECTURE_X86: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_X86_INVALID_OPERATION: reason.append("EXC_I386_INVOP"); break; case MD_EXCEPTION_CODE_MAC_X86_INVALID_TASK_STATE_SEGMENT: reason.append("EXC_I386_INVTSSFLT"); break; case MD_EXCEPTION_CODE_MAC_X86_SEGMENT_NOT_PRESENT: reason.append("EXC_I386_SEGNPFLT"); break; case MD_EXCEPTION_CODE_MAC_X86_STACK_FAULT: reason.append("EXC_I386_STKFLT"); break; case MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT: reason.append("EXC_I386_GPFLT"); break; case MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT: reason.append("EXC_I386_ALIGNFLT"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_MAC_ARITHMETIC: reason = "EXC_ARITHMETIC / "; switch (raw_system_info->processor_architecture) { case MD_CPU_ARCHITECTURE_PPC: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_PPC_OVERFLOW: reason.append("EXC_PPC_OVERFLOW"); break; case MD_EXCEPTION_CODE_MAC_PPC_ZERO_DIVIDE: reason.append("EXC_PPC_ZERO_DIVIDE"); break; case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_INEXACT: reason.append("EXC_FLT_INEXACT"); break; case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_ZERO_DIVIDE: reason.append("EXC_PPC_FLT_ZERO_DIVIDE"); break; case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_UNDERFLOW: reason.append("EXC_PPC_FLT_UNDERFLOW"); break; case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_OVERFLOW: reason.append("EXC_PPC_FLT_OVERFLOW"); break; case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_NOT_A_NUMBER: reason.append("EXC_PPC_FLT_NOT_A_NUMBER"); break; case MD_EXCEPTION_CODE_MAC_PPC_NO_EMULATION: reason.append("EXC_PPC_NOEMULATION"); break; case MD_EXCEPTION_CODE_MAC_PPC_ALTIVEC_ASSIST: reason.append("EXC_PPC_ALTIVECASSIST"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_CPU_ARCHITECTURE_AMD64: case MD_CPU_ARCHITECTURE_X86: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_X86_DIV: reason.append("EXC_I386_DIV"); break; case MD_EXCEPTION_CODE_MAC_X86_INTO: reason.append("EXC_I386_INTO"); break; case MD_EXCEPTION_CODE_MAC_X86_NOEXT: reason.append("EXC_I386_NOEXT"); break; case MD_EXCEPTION_CODE_MAC_X86_EXTOVR: reason.append("EXC_I386_EXTOVR"); break; case MD_EXCEPTION_CODE_MAC_X86_EXTERR: reason.append("EXC_I386_EXTERR"); break; case MD_EXCEPTION_CODE_MAC_X86_EMERR: reason.append("EXC_I386_EMERR"); break; case MD_EXCEPTION_CODE_MAC_X86_BOUND: reason.append("EXC_I386_BOUND"); break; case MD_EXCEPTION_CODE_MAC_X86_SSEEXTERR: reason.append("EXC_I386_SSEEXTERR"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_MAC_EMULATION: reason = "EXC_EMULATION / "; reason.append(flags_string); break; case MD_EXCEPTION_MAC_SOFTWARE: reason = "EXC_SOFTWARE / "; switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_ABORT: reason.append("SIGABRT"); break; case MD_EXCEPTION_CODE_MAC_NS_EXCEPTION: reason.append("UNCAUGHT_NS_EXCEPTION"); break; // These are ppc only but shouldn't be a problem as they're // unused on x86 case MD_EXCEPTION_CODE_MAC_PPC_TRAP: reason.append("EXC_PPC_TRAP"); break; case MD_EXCEPTION_CODE_MAC_PPC_MIGRATE: reason.append("EXC_PPC_MIGRATE"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_MAC_BREAKPOINT: reason = "EXC_BREAKPOINT / "; switch (raw_system_info->processor_architecture) { case MD_CPU_ARCHITECTURE_ARM: case MD_CPU_ARCHITECTURE_ARM64_OLD: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN: reason.append("EXC_ARM_DA_ALIGN"); break; case MD_EXCEPTION_CODE_MAC_ARM_DA_DEBUG: reason.append("EXC_ARM_DA_DEBUG"); break; case MD_EXCEPTION_CODE_MAC_ARM_BREAKPOINT: reason.append("EXC_ARM_BREAKPOINT"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_CPU_ARCHITECTURE_PPC: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_PPC_BREAKPOINT: reason.append("EXC_PPC_BREAKPOINT"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_CPU_ARCHITECTURE_AMD64: case MD_CPU_ARCHITECTURE_X86: { switch (exception_flags) { case MD_EXCEPTION_CODE_MAC_X86_SGL: reason.append("EXC_I386_SGL"); break; case MD_EXCEPTION_CODE_MAC_X86_BPT: reason.append("EXC_I386_BPT"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_MAC_SYSCALL: reason = "EXC_SYSCALL / "; reason.append(flags_string); break; case MD_EXCEPTION_MAC_MACH_SYSCALL: reason = "EXC_MACH_SYSCALL / "; reason.append(flags_string); break; case MD_EXCEPTION_MAC_RPC_ALERT: reason = "EXC_RPC_ALERT / "; reason.append(flags_string); break; case MD_EXCEPTION_MAC_RESOURCE: reason = "EXC_RESOURCE / "; { uint32_t type = (exception_flags >> 29) & 0x7ULL; uint32_t flavor = (exception_flags >> 26) & 0x7ULL; char flavor_string[4] = {}; switch (type) { case MD_MAC_EXC_RESOURCE_TYPE_CPU: reason.append("RESOURCE_TYPE_CPU / "); switch (flavor) { case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR: reason.append("FLAVOR_CPU_MONITOR"); break; case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR_FATAL: reason.append("FLAVOR_CPU_MONITOR_FATAL"); break; default: snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor); reason.append(flavor_string); break; } break; case MD_MAC_EXC_RESOURCE_TYPE_WAKEUPS: reason.append("RESOURCE_TYPE_WAKEUPS / "); if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_WAKEUPS_MONITOR) { reason.append("FLAVOR_WAKEUPS_MONITOR"); } else { snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor); reason.append(flavor_string); } break; case MD_MAC_EXC_RESOURCE_TYPE_MEMORY: reason.append("RESOURCE_TYPE_MEMORY / "); if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_HIGH_WATERMARK) { reason.append("FLAVOR_HIGH_WATERMARK"); } else { snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor); reason.append(flavor_string); } break; case MD_MAC_EXC_RESOURCE_TYPE_IO: reason.append("EXC_RESOURCE_TYPE_IO / "); switch (flavor) { case MD_MAC_EXC_RESOURCE_FLAVOR_IO_PHYSICAL_WRITES: reason.append("FLAVOR_IO_PHYSICAL_WRITES"); break; case MD_MAC_EXC_RESOURCE_FLAVOR_IO_LOGICAL_WRITES: reason.append("FLAVOR_IO_LOGICAL_WRITES"); break; default: snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor); reason.append(flavor_string); break; } break; case MD_MAC_EXC_RESOURCE_TYPE_THREADS: reason.append("EXC_RESOURCE_TYPE_THREADS / "); if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_THREADS_HIGH_WATERMARK) { reason.append("FLAVOR_THREADS_HIGH_WATERMARK"); } else { snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor); reason.append(flavor_string); } break; default: reason.append(flags_string); break; } } break; case MD_EXCEPTION_MAC_GUARD: reason = "EXC_GUARD / "; { uint32_t type = (exception_flags >> 29) & 0x7ULL; uint32_t flavor = exception_flags & 0x1FFFFFFFULL; switch (type) { case MD_MAC_EXC_GUARD_TYPE_NONE: reason.append("GUARD_TYPE_NONE"); break; case MD_MAC_EXC_GUARD_TYPE_MACH_PORT: reason.append("GUARD_TYPE_MACH_PORT"); if (flavor) { std::vector flavors; if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_DESTROY) { flavors.push_back("GUARD_EXC_DESTROY"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_MOD_REFS) { flavors.push_back("GUARD_EXC_MOD_REFS"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_SET_CONTEXT) { flavors.push_back("GUARD_EXC_SET_CONTEXT"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_SET_CONTEXT) { flavors.push_back("GUARD_EXC_SET_CONTEXT"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_UNGUARDED) { flavors.push_back("GUARD_EXC_UNGUARDED"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_INCORRECT_GUARD) { flavors.push_back("GUARD_EXC_INCORRECT_GUARD"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_IMMOVABLE) { flavors.push_back("GUARD_EXC_IMMOVABLE"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_STRICT_REPLY) { flavors.push_back("GUARD_EXC_STRICT_REPLY"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_MSG_FILTERED) { flavors.push_back("GUARD_EXC_MSG_FILTERED"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_INVALID_RIGHT) { flavors.push_back("GUARD_EXC_INVALID_RIGHT"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_INVALID_NAME) { flavors.push_back("GUARD_EXC_INVALID_NAME"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_INVALID_VALUE) { flavors.push_back("GUARD_EXC_INVALID_VALUE"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_INVALID_ARGUMENT) { flavors.push_back("GUARD_EXC_INVALID_ARGUMENT"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_RIGHT_EXISTS) { flavors.push_back("GUARD_EXC_RIGHT_EXISTS"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_KERN_NO_SPACE) { flavors.push_back("GUARD_EXC_KERN_NO_SPACE"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_KERN_FAILURE) { flavors.push_back("GUARD_EXC_KERN_FAILURE"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_KERN_RESOURCE) { flavors.push_back("GUARD_EXC_KERN_RESOURCE"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_SEND_INVALID_REPLY) { flavors.push_back("GUARD_EXC_SEND_INVALID_REPLY"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_SEND_INVALID_VOUCHER) { flavors.push_back("GUARD_EXC_SEND_INVALID_VOUCHER"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_SEND_INVALID_RIGHT) { flavors.push_back("GUARD_EXC_SEND_INVALID_RIGHT"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_RCV_INVALID_NAME) { flavors.push_back("GUARD_EXC_RCV_INVALID_NAME"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_RCV_GUARDED_DESC) { flavors.push_back("GUARD_EXC_RCV_GUARDED_DESC"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_MOD_REFS_NON_FATAL) { flavors.push_back("GUARD_EXC_MOD_REFS_NON_FATAL"); } if (flavor & MD_MAC_EXC_GUARD_MACH_PORT_FLAVOR_GUARD_EXC_IMMOVABLE_NON_FATAL) { flavors.push_back("GUARD_EXC_IMMOVABLE_NON_FATAL"); } reason.append(" / "); for (size_t i = 0; i < flavors.size(); i++) { if (i > 0) { reason.append(" | "); } reason.append(flavors[i]); } } break; case MD_MAC_EXC_GUARD_TYPE_FD: reason.append("GUARD_TYPE_FD"); if (flavor) { std::vector flavors; if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_CLOSE) { flavors.push_back("GUARD_EXC_CLOSE"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_DUP) { flavors.push_back("GUARD_EXC_DUP"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_NOCLOEXEC) { flavors.push_back("GUARD_EXC_NOCLOEXEC"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_SOCKET_IPC) { flavors.push_back("GUARD_EXC_SOCKET_IPC"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_FILEPORT) { flavors.push_back("GUARD_EXC_FILEPORT"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_MISMATCH) { flavors.push_back("GUARD_EXC_MISMATCH"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_WRITE) { flavors.push_back("GUARD_EXC_WRITE"); } reason.append(" / "); for (size_t i = 0; i < flavors.size(); i++) { if (i > 0) { reason.append(" | "); } reason.append(flavors[i]); } } break; case MD_MAC_EXC_GUARD_TYPE_USER: reason.append("GUARD_TYPE_USER"); break; case MD_MAC_EXC_GUARD_TYPE_VN: reason.append("GUARD_TYPE_VN"); if (flavor) { std::vector flavors; if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_RENAME_TO) { flavors.push_back("GUARD_EXC_RENAME_TO"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_RENAME_FROM) { flavors.push_back("GUARD_EXC_RENAME_FROM"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_UNLINK) { flavors.push_back("GUARD_EXC_UNLINK"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_WRITE_OTHER) { flavors.push_back("GUARD_EXC_WRITE_OTHER"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_TRUNC_OTHER) { flavors.push_back("GUARD_EXC_TRUNC_OTHER"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_LINK) { flavors.push_back("GUARD_EXC_LINK"); } if (flavor & MD_MAC_EXC_GUARD_FD_FLAVOR_GUARD_EXC_EXCHDATA) { flavors.push_back("GUARD_EXC_EXCHDATA"); } reason.append(" / "); for (size_t i = 0; i < flavors.size(); i++) { if (i > 0) { reason.append(" | "); } reason.append(flavors[i]); } } break; case MD_MAC_EXC_GUARD_TYPE_VIRT_MEMORY: reason.append("GUARD_TYPE_VIRT_MEMORY"); if (flavor & MD_MAC_EXC_GUARD_VIRT_MEMORY_FLAVOR_GUARD_EXC_DEALLOC_GAP) { reason.append(" / GUARD_EXC_DEALLOC_GAP"); } break; default: reason.append(flags_string); break; } } break; case MD_EXCEPTION_MAC_SIMULATED: reason = "Simulated Exception"; break; } break; } case MD_OS_WIN32_NT: case MD_OS_WIN32_WINDOWS: { switch (exception_code) { case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION: reason = "EXCEPTION_GUARD_PAGE"; break; case MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT: reason = "EXCEPTION_DATATYPE_MISALIGNMENT"; break; case MD_EXCEPTION_CODE_WIN_BREAKPOINT: reason = "EXCEPTION_BREAKPOINT"; break; case MD_EXCEPTION_CODE_WIN_SINGLE_STEP: reason = "EXCEPTION_SINGLE_STEP"; break; case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: // For EXCEPTION_ACCESS_VIOLATION, Windows puts the address that // caused the fault in exception_information[1]. // exception_information[0] is 0 if the violation was caused by // an attempt to read data, 1 if it was an attempt to write data, // and 8 if this was a data execution violation. // This information is useful in addition to the code address, which // will be present in the crash thread's instruction field anyway. if (raw_exception->exception_record.number_parameters >= 1) { MDAccessViolationTypeWin av_type = static_cast (raw_exception->exception_record.exception_information[0]); switch (av_type) { case MD_ACCESS_VIOLATION_WIN_READ: reason = "EXCEPTION_ACCESS_VIOLATION_READ"; break; case MD_ACCESS_VIOLATION_WIN_WRITE: reason = "EXCEPTION_ACCESS_VIOLATION_WRITE"; break; case MD_ACCESS_VIOLATION_WIN_EXEC: reason = "EXCEPTION_ACCESS_VIOLATION_EXEC"; break; default: reason = "EXCEPTION_ACCESS_VIOLATION"; break; } } else { reason = "EXCEPTION_ACCESS_VIOLATION"; } if (address && raw_exception->exception_record.number_parameters >= 2) { *address = raw_exception->exception_record.exception_information[1]; } break; case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR: // For EXCEPTION_IN_PAGE_ERROR, Windows puts the address that // caused the fault in exception_information[1]. // exception_information[0] is 0 if the violation was caused by // an attempt to read data, 1 if it was an attempt to write data, // and 8 if this was a data execution violation. // exception_information[2] contains the underlying NTSTATUS code, // which is the explanation for why this error occured. // This information is useful in addition to the code address, which // will be present in the crash thread's instruction field anyway. if (raw_exception->exception_record.number_parameters >= 1) { MDInPageErrorTypeWin av_type = static_cast (raw_exception->exception_record.exception_information[0]); switch (av_type) { case MD_IN_PAGE_ERROR_WIN_READ: reason = "EXCEPTION_IN_PAGE_ERROR_READ"; break; case MD_IN_PAGE_ERROR_WIN_WRITE: reason = "EXCEPTION_IN_PAGE_ERROR_WRITE"; break; case MD_IN_PAGE_ERROR_WIN_EXEC: reason = "EXCEPTION_IN_PAGE_ERROR_EXEC"; break; default: reason = "EXCEPTION_IN_PAGE_ERROR"; break; } } else { reason = "EXCEPTION_IN_PAGE_ERROR"; } if (address && raw_exception->exception_record.number_parameters >= 2) { *address = raw_exception->exception_record.exception_information[1]; } if (raw_exception->exception_record.number_parameters >= 3) { uint32_t ntstatus = static_cast (raw_exception->exception_record.exception_information[2]); reason.append(" / "); const char* ntstatus_str = NTStatusToString(ntstatus); if (ntstatus_str) { reason.append(ntstatus_str); } else { snprintf(reason_string, sizeof(reason_string), "%#010x", ntstatus); reason.append(reason_string); } } break; case MD_EXCEPTION_CODE_WIN_INVALID_HANDLE: reason = "EXCEPTION_INVALID_HANDLE"; break; case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION: reason = "EXCEPTION_ILLEGAL_INSTRUCTION"; break; case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION: reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION"; break; case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION: reason = "EXCEPTION_INVALID_DISPOSITION"; break; case MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED: reason = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND: reason = "EXCEPTION_FLT_DENORMAL_OPERAND"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO: reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT: reason = "EXCEPTION_FLT_INEXACT_RESULT"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION: reason = "EXCEPTION_FLT_INVALID_OPERATION"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW: reason = "EXCEPTION_FLT_OVERFLOW"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK: reason = "EXCEPTION_FLT_STACK_CHECK"; break; case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW: reason = "EXCEPTION_FLT_UNDERFLOW"; break; case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO: reason = "EXCEPTION_INT_DIVIDE_BY_ZERO"; break; case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW: reason = "EXCEPTION_INT_OVERFLOW"; break; case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION: reason = "EXCEPTION_PRIV_INSTRUCTION"; break; case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: reason = "EXCEPTION_STACK_OVERFLOW"; break; case MD_EXCEPTION_CODE_WIN_BAD_FUNCTION_TABLE: reason = "EXCEPTION_BAD_FUNCTION_TABLE"; break; case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: reason = "EXCEPTION_POSSIBLE_DEADLOCK"; break; case MD_NTSTATUS_WIN_STATUS_STACK_BUFFER_OVERRUN: reason = "STATUS_STACK_BUFFER_OVERRUN"; if (raw_exception->exception_record.number_parameters > 0) { uint32_t fast_fail_code = static_cast (raw_exception->exception_record.exception_information[0]); char fast_fail_buff[11] = {}; const char* fast_fail_string = FastFailToString(fast_fail_code); if (!fast_fail_string) { snprintf(fast_fail_buff, sizeof(fast_fail_buff), "%#010x", fast_fail_code); fast_fail_string = fast_fail_buff; } reason.append(" / "); reason.append(fast_fail_string); } break; case MD_EXCEPTION_OUT_OF_MEMORY: reason = "Out of Memory"; break; case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION: reason = "Unhandled C++ Exception"; break; case MD_EXCEPTION_CODE_WIN_SIMULATED: reason = "Simulated Exception"; break; default: fprintf(stderr, "exception_code = %u\n", exception_code); const char* exception_str = NTStatusToString(exception_code); fprintf(stderr, "exception_str = %s\n", exception_str); if (exception_str == nullptr) { exception_str = WinErrorToString(exception_code); } if (exception_str != nullptr) { reason = exception_str; } break; } break; } case MD_OS_ANDROID: case MD_OS_LINUX: { switch (exception_code) { case MD_EXCEPTION_CODE_LIN_SIGHUP: reason = "SIGHUP"; break; case MD_EXCEPTION_CODE_LIN_SIGINT: reason = "SIGINT"; break; case MD_EXCEPTION_CODE_LIN_SIGQUIT: reason = "SIGQUIT"; break; case MD_EXCEPTION_CODE_LIN_SIGILL: reason = "SIGILL / "; switch (exception_flags) { case MD_EXCEPTION_FLAG_LIN_ILL_ILLOPC: reason.append("ILL_ILLOPC"); break; case MD_EXCEPTION_FLAG_LIN_ILL_ILLOPN: reason.append("ILL_ILLOPN"); break; case MD_EXCEPTION_FLAG_LIN_ILL_ILLADR: reason.append("ILL_ILLADR"); break; case MD_EXCEPTION_FLAG_LIN_ILL_ILLTRP: reason.append("ILL_ILLTRP"); break; case MD_EXCEPTION_FLAG_LIN_ILL_PRVOPC: reason.append("ILL_PRVOPC"); break; case MD_EXCEPTION_FLAG_LIN_ILL_PRVREG: reason.append("ILL_PRVREG"); break; case MD_EXCEPTION_FLAG_LIN_ILL_COPROC: reason.append("ILL_COPROC"); break; case MD_EXCEPTION_FLAG_LIN_ILL_BADSTK: reason.append("ILL_BADSTK"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_CODE_LIN_SIGTRAP: reason = "SIGTRAP"; break; case MD_EXCEPTION_CODE_LIN_SIGABRT: reason = "SIGABRT"; break; case MD_EXCEPTION_CODE_LIN_SIGBUS: reason = "SIGBUS / "; switch (exception_flags) { case MD_EXCEPTION_FLAG_LIN_SI_USER: reason.append("SI_USER"); break; case MD_EXCEPTION_FLAG_LIN_SI_KERNEL: reason.append("SI_KERNEL"); break; case MD_EXCEPTION_FLAG_LIN_BUS_ADRALN: reason.append("BUS_ADRALN"); break; case MD_EXCEPTION_FLAG_LIN_BUS_ADRERR: reason.append("BUS_ADRERR"); break; case MD_EXCEPTION_FLAG_LIN_BUS_OBJERR: reason.append("BUS_OBJERR"); break; case MD_EXCEPTION_FLAG_LIN_BUS_MCEERR_AR: reason.append("BUS_MCEERR_AR"); break; case MD_EXCEPTION_FLAG_LIN_BUS_MCEERR_AO: reason.append("BUS_MCEERR_AO"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_CODE_LIN_SIGFPE: reason = "SIGFPE / "; switch (exception_flags) { case MD_EXCEPTION_FLAG_LIN_FPE_INTDIV: reason.append("FPE_INTDIV"); break; case MD_EXCEPTION_FLAG_LIN_FPE_INTOVF: reason.append("FPE_INTOVF"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTDIV: reason.append("FPE_FLTDIV"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTOVF: reason.append("FPE_FLTOVF"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTUND: reason.append("FPE_FLTUND"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTRES: reason.append("FPE_FLTRES"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTINV: reason.append("FPE_FLTINV"); break; case MD_EXCEPTION_FLAG_LIN_FPE_FLTSUB: reason.append("FPE_FLTSUB"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_CODE_LIN_SIGKILL: reason = "SIGKILL"; break; case MD_EXCEPTION_CODE_LIN_SIGUSR1: reason = "SIGUSR1"; break; case MD_EXCEPTION_CODE_LIN_SIGSEGV: reason = "SIGSEGV / "; switch (exception_flags) { case MD_EXCEPTION_FLAG_LIN_SI_USER: reason.append("SI_USER"); break; case MD_EXCEPTION_FLAG_LIN_SI_KERNEL: reason.append("SI_KERNEL"); break; case MD_EXCEPTION_FLAG_LIN_SEGV_MAPERR: reason.append("SEGV_MAPERR"); break; case MD_EXCEPTION_FLAG_LIN_SEGV_ACCERR: reason.append("SEGV_ACCERR"); break; case MD_EXCEPTION_FLAG_LIN_SEGV_BNDERR: reason.append("SEGV_BNDERR"); break; case MD_EXCEPTION_FLAG_LIN_SEGV_PKUERR: reason.append("SEGV_PKUERR"); break; default: reason.append(flags_string); BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; case MD_EXCEPTION_CODE_LIN_SIGUSR2: reason = "SIGUSR2"; break; case MD_EXCEPTION_CODE_LIN_SIGPIPE: reason = "SIGPIPE"; break; case MD_EXCEPTION_CODE_LIN_SIGALRM: reason = "SIGALRM"; break; case MD_EXCEPTION_CODE_LIN_SIGTERM: reason = "SIGTERM"; break; case MD_EXCEPTION_CODE_LIN_SIGSTKFLT: reason = "SIGSTKFLT"; break; case MD_EXCEPTION_CODE_LIN_SIGCHLD: reason = "SIGCHLD"; break; case MD_EXCEPTION_CODE_LIN_SIGCONT: reason = "SIGCONT"; break; case MD_EXCEPTION_CODE_LIN_SIGSTOP: reason = "SIGSTOP"; break; case MD_EXCEPTION_CODE_LIN_SIGTSTP: reason = "SIGTSTP"; break; case MD_EXCEPTION_CODE_LIN_SIGTTIN: reason = "SIGTTIN"; break; case MD_EXCEPTION_CODE_LIN_SIGTTOU: reason = "SIGTTOU"; break; case MD_EXCEPTION_CODE_LIN_SIGURG: reason = "SIGURG"; break; case MD_EXCEPTION_CODE_LIN_SIGXCPU: reason = "SIGXCPU"; break; case MD_EXCEPTION_CODE_LIN_SIGXFSZ: reason = "SIGXFSZ"; break; case MD_EXCEPTION_CODE_LIN_SIGVTALRM: reason = "SIGVTALRM"; break; case MD_EXCEPTION_CODE_LIN_SIGPROF: reason = "SIGPROF"; break; case MD_EXCEPTION_CODE_LIN_SIGWINCH: reason = "SIGWINCH"; break; case MD_EXCEPTION_CODE_LIN_SIGIO: reason = "SIGIO"; break; case MD_EXCEPTION_CODE_LIN_SIGPWR: reason = "SIGPWR"; break; case MD_EXCEPTION_CODE_LIN_SIGSYS: reason = "SIGSYS"; break; case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: reason = "DUMP_REQUESTED"; break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_OS_SOLARIS: { switch (exception_code) { case MD_EXCEPTION_CODE_SOL_SIGHUP: reason = "SIGHUP"; break; case MD_EXCEPTION_CODE_SOL_SIGINT: reason = "SIGINT"; break; case MD_EXCEPTION_CODE_SOL_SIGQUIT: reason = "SIGQUIT"; break; case MD_EXCEPTION_CODE_SOL_SIGILL: reason = "SIGILL"; break; case MD_EXCEPTION_CODE_SOL_SIGTRAP: reason = "SIGTRAP"; break; case MD_EXCEPTION_CODE_SOL_SIGIOT: reason = "SIGIOT | SIGABRT"; break; case MD_EXCEPTION_CODE_SOL_SIGEMT: reason = "SIGEMT"; break; case MD_EXCEPTION_CODE_SOL_SIGFPE: reason = "SIGFPE"; break; case MD_EXCEPTION_CODE_SOL_SIGKILL: reason = "SIGKILL"; break; case MD_EXCEPTION_CODE_SOL_SIGBUS: reason = "SIGBUS"; break; case MD_EXCEPTION_CODE_SOL_SIGSEGV: reason = "SIGSEGV"; break; case MD_EXCEPTION_CODE_SOL_SIGSYS: reason = "SIGSYS"; break; case MD_EXCEPTION_CODE_SOL_SIGPIPE: reason = "SIGPIPE"; break; case MD_EXCEPTION_CODE_SOL_SIGALRM: reason = "SIGALRM"; break; case MD_EXCEPTION_CODE_SOL_SIGTERM: reason = "SIGTERM"; break; case MD_EXCEPTION_CODE_SOL_SIGUSR1: reason = "SIGUSR1"; break; case MD_EXCEPTION_CODE_SOL_SIGUSR2: reason = "SIGUSR2"; break; case MD_EXCEPTION_CODE_SOL_SIGCLD: reason = "SIGCLD | SIGCHLD"; break; case MD_EXCEPTION_CODE_SOL_SIGPWR: reason = "SIGPWR"; break; case MD_EXCEPTION_CODE_SOL_SIGWINCH: reason = "SIGWINCH"; break; case MD_EXCEPTION_CODE_SOL_SIGURG: reason = "SIGURG"; break; case MD_EXCEPTION_CODE_SOL_SIGPOLL: reason = "SIGPOLL | SIGIO"; break; case MD_EXCEPTION_CODE_SOL_SIGSTOP: reason = "SIGSTOP"; break; case MD_EXCEPTION_CODE_SOL_SIGTSTP: reason = "SIGTSTP"; break; case MD_EXCEPTION_CODE_SOL_SIGCONT: reason = "SIGCONT"; break; case MD_EXCEPTION_CODE_SOL_SIGTTIN: reason = "SIGTTIN"; break; case MD_EXCEPTION_CODE_SOL_SIGTTOU: reason = "SIGTTOU"; break; case MD_EXCEPTION_CODE_SOL_SIGVTALRM: reason = "SIGVTALRM"; break; case MD_EXCEPTION_CODE_SOL_SIGPROF: reason = "SIGPROF"; break; case MD_EXCEPTION_CODE_SOL_SIGXCPU: reason = "SIGXCPU"; break; case MD_EXCEPTION_CODE_SOL_SIGXFSZ: reason = "SIGXFSZ"; break; case MD_EXCEPTION_CODE_SOL_SIGWAITING: reason = "SIGWAITING"; break; case MD_EXCEPTION_CODE_SOL_SIGLWP: reason = "SIGLWP"; break; case MD_EXCEPTION_CODE_SOL_SIGFREEZE: reason = "SIGFREEZE"; break; case MD_EXCEPTION_CODE_SOL_SIGTHAW: reason = "SIGTHAW"; break; case MD_EXCEPTION_CODE_SOL_SIGCANCEL: reason = "SIGCANCEL"; break; case MD_EXCEPTION_CODE_SOL_SIGLOST: reason = "SIGLOST"; break; case MD_EXCEPTION_CODE_SOL_SIGXRES: reason = "SIGXRES"; break; case MD_EXCEPTION_CODE_SOL_SIGJVM1: reason = "SIGJVM1"; break; case MD_EXCEPTION_CODE_SOL_SIGJVM2: reason = "SIGJVM2"; break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; } break; } case MD_OS_PS3: { switch (exception_code) { case MD_EXCEPTION_CODE_PS3_UNKNOWN: reason = "UNKNOWN"; break; case MD_EXCEPTION_CODE_PS3_TRAP_EXCEP: reason = "TRAP_EXCEP"; break; case MD_EXCEPTION_CODE_PS3_PRIV_INSTR: reason = "PRIV_INSTR"; break; case MD_EXCEPTION_CODE_PS3_ILLEGAL_INSTR: reason = "ILLEGAL_INSTR"; break; case MD_EXCEPTION_CODE_PS3_INSTR_STORAGE: reason = "INSTR_STORAGE"; break; case MD_EXCEPTION_CODE_PS3_INSTR_SEGMENT: reason = "INSTR_SEGMENT"; break; case MD_EXCEPTION_CODE_PS3_DATA_STORAGE: reason = "DATA_STORAGE"; break; case MD_EXCEPTION_CODE_PS3_DATA_SEGMENT: reason = "DATA_SEGMENT"; break; case MD_EXCEPTION_CODE_PS3_FLOAT_POINT: reason = "FLOAT_POINT"; break; case MD_EXCEPTION_CODE_PS3_DABR_MATCH: reason = "DABR_MATCH"; break; case MD_EXCEPTION_CODE_PS3_ALIGN_EXCEP: reason = "ALIGN_EXCEP"; break; case MD_EXCEPTION_CODE_PS3_MEMORY_ACCESS: reason = "MEMORY_ACCESS"; break; case MD_EXCEPTION_CODE_PS3_COPRO_ALIGN: reason = "COPRO_ALIGN"; break; case MD_EXCEPTION_CODE_PS3_COPRO_INVALID_COM: reason = "COPRO_INVALID_COM"; break; case MD_EXCEPTION_CODE_PS3_COPRO_ERR: reason = "COPRO_ERR"; break; case MD_EXCEPTION_CODE_PS3_COPRO_FIR: reason = "COPRO_FIR"; break; case MD_EXCEPTION_CODE_PS3_COPRO_DATA_SEGMENT: reason = "COPRO_DATA_SEGMENT"; break; case MD_EXCEPTION_CODE_PS3_COPRO_DATA_STORAGE: reason = "COPRO_DATA_STORAGE"; break; case MD_EXCEPTION_CODE_PS3_COPRO_STOP_INSTR: reason = "COPRO_STOP_INSTR"; break; case MD_EXCEPTION_CODE_PS3_COPRO_HALT_INSTR: reason = "COPRO_HALT_INSTR"; break; case MD_EXCEPTION_CODE_PS3_COPRO_HALTINST_UNKNOWN: reason = "COPRO_HALTINSTR_UNKNOWN"; break; case MD_EXCEPTION_CODE_PS3_COPRO_MEMORY_ACCESS: reason = "COPRO_MEMORY_ACCESS"; break; case MD_EXCEPTION_CODE_PS3_GRAPHIC: reason = "GRAPHIC"; break; default: BPLOG(INFO) << "Unknown exception reason "<< reason; break; } break; } default: { BPLOG(INFO) << "Unknown exception reason " << reason; break; } } if (address) { *address = GetAddressForArchitecture( static_cast(raw_system_info->processor_architecture), *address); } return reason; } // static string MinidumpProcessor::GetAssertion(Minidump *dump) { MinidumpAssertion *assertion = dump->GetAssertion(); if (!assertion) return ""; const MDRawAssertionInfo *raw_assertion = assertion->assertion(); if (!raw_assertion) return ""; string assertion_string; switch (raw_assertion->type) { case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER: assertion_string = "Invalid parameter passed to library function"; break; case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL: assertion_string = "Pure virtual function called"; break; default: { char assertion_type[32]; snprintf(assertion_type, sizeof(assertion_type), "0x%08x", raw_assertion->type); assertion_string = "Unknown assertion type "; assertion_string += assertion_type; break; } } string expression = assertion->expression(); if (!expression.empty()) { assertion_string.append(" " + expression); } string function = assertion->function(); if (!function.empty()) { assertion_string.append(" in function " + function); } string file = assertion->file(); if (!file.empty()) { assertion_string.append(", in file " + file); } if (raw_assertion->line != 0) { char assertion_line[32]; snprintf(assertion_line, sizeof(assertion_line), "%u", raw_assertion->line); assertion_string.append(" at line "); assertion_string.append(assertion_line); } return assertion_string; } } // namespace google_breakpad