| // Copyright (c) 2007, 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. |
| |
| // Author: Alfred Peng |
| |
| #include <fcntl.h> |
| #include <sys/frame.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/utsname.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| |
| #include <cstdlib> |
| #include <ctime> |
| |
| #include "client/solaris/handler/minidump_generator.h" |
| #include "client/minidump_file_writer-inl.h" |
| #include "common/solaris/file_id.h" |
| |
| namespace { |
| |
| using namespace google_breakpad; |
| |
| // Argument for the writer function. |
| struct WriterArgument { |
| MinidumpFileWriter *minidump_writer; |
| |
| // Pid of the lwp who called WriteMinidumpToFile |
| int requester_pid; |
| |
| // The stack bottom of the lwp which caused the dump. |
| // Mainly used to find the lwp id of the crashed lwp since signal |
| // handler may not be called in the lwp who caused it. |
| uintptr_t crashed_stack_bottom; |
| |
| // Id of the crashing lwp. |
| int crashed_lwpid; |
| |
| // Signal number when crash happened. Can be 0 if this is a requested dump. |
| int signo; |
| |
| // The ebp of the signal handler frame on x86. Can be 0 if this is a |
| // requested dump. |
| uintptr_t sighandler_ebp; |
| |
| // User context when crash happens. Can be NULL if this is a requested dump. |
| // This is actually an out parameter, but it will be filled in at the start |
| // of the writer LWP. |
| ucontext_t *sig_ctx; |
| |
| // Used to get information about the lwps. |
| SolarisLwp *lwp_lister; |
| }; |
| |
| // Holding context information for the callback of finding the crashing lwp. |
| struct FindCrashLwpContext { |
| const SolarisLwp *lwp_lister; |
| uintptr_t crashing_stack_bottom; |
| int crashing_lwpid; |
| |
| FindCrashLwpContext() : |
| lwp_lister(NULL), |
| crashing_stack_bottom(0UL), |
| crashing_lwpid(-1) { |
| } |
| }; |
| |
| // Callback for list lwps. |
| // It will compare the stack bottom of the provided lwp with the stack |
| // bottom of the crashed lwp, it they are eqaul, this lwp is the one |
| // who crashed. |
| bool IsLwpCrashedCallback(lwpstatus_t *lsp, void *context) { |
| FindCrashLwpContext *crashing_context = |
| static_cast<FindCrashLwpContext *>(context); |
| const SolarisLwp *lwp_lister = crashing_context->lwp_lister; |
| const prgregset_t *gregs = &(lsp->pr_reg); |
| #if TARGET_CPU_SPARC |
| uintptr_t last_ebp = (*gregs)[R_FP]; |
| #elif TARGET_CPU_X86 |
| uintptr_t last_ebp = (*gregs)[EBP]; |
| #endif |
| uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_ebp); |
| if (stack_bottom > last_ebp && |
| stack_bottom == crashing_context->crashing_stack_bottom) { |
| // Got it. Stop iteration. |
| crashing_context->crashing_lwpid = lsp->pr_lwpid; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Find the crashing lwpid. |
| // This is done based on stack bottom comparing. |
| int FindCrashingLwp(uintptr_t crashing_stack_bottom, |
| int requester_pid, |
| const SolarisLwp *lwp_lister) { |
| FindCrashLwpContext context; |
| context.lwp_lister = lwp_lister; |
| context.crashing_stack_bottom = crashing_stack_bottom; |
| CallbackParam<LwpCallback> callback_param(IsLwpCrashedCallback, |
| &context); |
| lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); |
| return context.crashing_lwpid; |
| } |
| |
| bool WriteLwpStack(const SolarisLwp *lwp_lister, |
| uintptr_t last_esp, |
| UntypedMDRVA *memory, |
| MDMemoryDescriptor *loc) { |
| uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_esp); |
| if (stack_bottom >= last_esp) { |
| int size = stack_bottom - last_esp; |
| if (size > 0) { |
| if (!memory->Allocate(size)) |
| return false; |
| memory->Copy(reinterpret_cast<void *>(last_esp), size); |
| loc->start_of_memory_range = last_esp; |
| loc->memory = memory->location(); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| #if TARGET_CPU_SPARC |
| bool WriteContext(MDRawContextSPARC *context, ucontext_t *sig_ctx) { |
| assert(sig_ctx != NULL); |
| int* regs = sig_ctx->uc_mcontext.gregs; |
| context->context_flags = MD_CONTEXT_SPARC_FULL; |
| |
| context->ccr = (unsigned int)(regs[0]); |
| context->pc = (unsigned int)(regs[REG_PC]); |
| context->npc = (unsigned int)(regs[REG_nPC]); |
| context->y = (unsigned int)(regs[REG_Y]); |
| context->asi = (unsigned int)(regs[19]); |
| context->fprs = (unsigned int)(regs[20]); |
| |
| for ( int i = 0 ; i < 32; ++i ) { |
| context->g_r[i] = 0; |
| } |
| |
| for ( int i = 1 ; i < 16; ++i ) { |
| context->g_r[i] = (uintptr_t)(sig_ctx->uc_mcontext.gregs[i + 3]); |
| } |
| context->g_r[30] = (uintptr_t)(((struct frame *)context->g_r[14])->fr_savfp); |
| |
| return true; |
| } |
| |
| bool WriteContext(MDRawContextSPARC *context, prgregset_t regs, |
| prfpregset_t *fp_regs) { |
| if (!context || !regs) |
| return false; |
| |
| context->context_flags = MD_CONTEXT_SPARC_FULL; |
| |
| context->ccr = (uintptr_t)(regs[32]); |
| context->pc = (uintptr_t)(regs[R_PC]); |
| context->npc = (uintptr_t)(regs[R_nPC]); |
| context->y = (uintptr_t)(regs[R_Y]); |
| context->asi = (uintptr_t)(regs[36]); |
| context->fprs = (uintptr_t)(regs[37]); |
| for ( int i = 0 ; i < 32 ; ++i ){ |
| context->g_r[i] = (uintptr_t)(regs[i]); |
| } |
| |
| return true; |
| } |
| #elif TARGET_CPU_X86 |
| bool WriteContext(MDRawContextX86 *context, prgregset_t regs, |
| prfpregset_t *fp_regs) { |
| if (!context || !regs) |
| return false; |
| |
| context->context_flags = MD_CONTEXT_X86_FULL; |
| |
| context->cs = regs[CS]; |
| context->ds = regs[DS]; |
| context->es = regs[ES]; |
| context->fs = regs[FS]; |
| context->gs = regs[GS]; |
| context->ss = regs[SS]; |
| context->edi = regs[EDI]; |
| context->esi = regs[ESI]; |
| context->ebx = regs[EBX]; |
| context->edx = regs[EDX]; |
| context->ecx = regs[ECX]; |
| context->eax = regs[EAX]; |
| context->ebp = regs[EBP]; |
| context->eip = regs[EIP]; |
| context->esp = regs[UESP]; |
| context->eflags = regs[EFL]; |
| |
| return true; |
| } |
| #endif /* TARGET_CPU_XXX */ |
| |
| // Write information about a crashed Lwp. |
| // When a lwp crash, kernel will write something on the stack for processing |
| // signal. This makes the current stack not reliable, and our stack walker |
| // won't figure out the whole call stack for this. So we write the stack at the |
| // time of the crash into the minidump file, not the current stack. |
| bool WriteCrashedLwpStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| const lwpstatus_t *lsp, |
| MDRawThread *lwp) { |
| assert(writer_args->sig_ctx != NULL); |
| |
| lwp->thread_id = lsp->pr_lwpid; |
| |
| #if TARGET_CPU_SPARC |
| UntypedMDRVA memory(minidump_writer); |
| if (!WriteLwpStack(writer_args->lwp_lister, |
| writer_args->sig_ctx->uc_mcontext.gregs[REG_O6], |
| &memory, |
| &lwp->stack)) |
| return false; |
| |
| TypedMDRVA<MDRawContextSPARC> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| lwp->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextSPARC)); |
| return WriteContext(context.get(), writer_args->sig_ctx); |
| #elif TARGET_CPU_X86 |
| UntypedMDRVA memory(minidump_writer); |
| if (!WriteLwpStack(writer_args->lwp_lister, |
| writer_args->sig_ctx->uc_mcontext.gregs[UESP], |
| &memory, |
| &lwp->stack)) |
| return false; |
| |
| TypedMDRVA<MDRawContextX86> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| lwp->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextX86)); |
| return WriteContext(context.get(), |
| (int *)&writer_args->sig_ctx->uc_mcontext.gregs, |
| &writer_args->sig_ctx->uc_mcontext.fpregs); |
| #endif |
| } |
| |
| bool WriteLwpStream(MinidumpFileWriter *minidump_writer, |
| const SolarisLwp *lwp_lister, |
| const lwpstatus_t *lsp, MDRawThread *lwp) { |
| prfpregset_t fp_regs = lsp->pr_fpreg; |
| const prgregset_t *gregs = &(lsp->pr_reg); |
| UntypedMDRVA memory(minidump_writer); |
| #if TARGET_CPU_SPARC |
| if (!WriteLwpStack(lwp_lister, |
| (*gregs)[R_SP], |
| &memory, |
| &lwp->stack)) |
| return false; |
| |
| // Write context |
| TypedMDRVA<MDRawContextSPARC> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| // should be the thread_id |
| lwp->thread_id = lsp->pr_lwpid; |
| lwp->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextSPARC)); |
| #elif TARGET_CPU_X86 |
| if (!WriteLwpStack(lwp_lister, |
| (*gregs)[UESP], |
| &memory, |
| &lwp->stack)) |
| return false; |
| |
| // Write context |
| TypedMDRVA<MDRawContextX86> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| // should be the thread_id |
| lwp->thread_id = lsp->pr_lwpid; |
| lwp->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextX86)); |
| #endif /* TARGET_CPU_XXX */ |
| return WriteContext(context.get(), (int *)gregs, &fp_regs); |
| } |
| |
| bool WriteCPUInformation(MDRawSystemInfo *sys_info) { |
| struct utsname uts; |
| char *major, *minor, *build; |
| |
| sys_info->number_of_processors = sysconf(_SC_NPROCESSORS_CONF); |
| sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; |
| if (uname(&uts) != -1) { |
| // Match "i86pc" as X86 architecture. |
| if (strcmp(uts.machine, "i86pc") == 0) |
| sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86; |
| else if (strcmp(uts.machine, "sun4u") == 0) |
| sys_info->processor_architecture = MD_CPU_ARCHITECTURE_SPARC; |
| } |
| |
| major = uts.release; |
| minor = strchr(major, '.'); |
| *minor = '\0'; |
| ++minor; |
| sys_info->major_version = atoi(major); |
| sys_info->minor_version = atoi(minor); |
| |
| build = strchr(uts.version, '_'); |
| ++build; |
| sys_info->build_number = atoi(build); |
| |
| return true; |
| } |
| |
| bool WriteOSInformation(MinidumpFileWriter *minidump_writer, |
| MDRawSystemInfo *sys_info) { |
| sys_info->platform_id = MD_OS_SOLARIS; |
| |
| struct utsname uts; |
| if (uname(&uts) != -1) { |
| char os_version[512]; |
| size_t space_left = sizeof(os_version); |
| memset(os_version, 0, space_left); |
| const char *os_info_table[] = { |
| uts.sysname, |
| uts.release, |
| uts.version, |
| uts.machine, |
| "OpenSolaris", |
| NULL |
| }; |
| for (const char **cur_os_info = os_info_table; |
| *cur_os_info != NULL; |
| ++cur_os_info) { |
| if (cur_os_info != os_info_table && space_left > 1) { |
| strcat(os_version, " "); |
| --space_left; |
| } |
| if (space_left > strlen(*cur_os_info)) { |
| strcat(os_version, *cur_os_info); |
| space_left -= strlen(*cur_os_info); |
| } else { |
| break; |
| } |
| } |
| |
| MDLocationDescriptor location; |
| if (!minidump_writer->WriteString(os_version, 0, &location)) |
| return false; |
| sys_info->csd_version_rva = location.rva; |
| } |
| return true; |
| } |
| |
| // Callback context for get writting lwp information. |
| struct LwpInfoCallbackCtx { |
| MinidumpFileWriter *minidump_writer; |
| const WriterArgument *writer_args; |
| TypedMDRVA<MDRawThreadList> *list; |
| int lwp_index; |
| }; |
| |
| bool LwpInformationCallback(lwpstatus_t *lsp, void *context) { |
| bool success = true; |
| LwpInfoCallbackCtx *callback_context = |
| static_cast<LwpInfoCallbackCtx *>(context); |
| |
| // The current lwp is the one to handle the crash. Ignore it. |
| if (lsp->pr_lwpid != pthread_self()) { |
| LwpInfoCallbackCtx *callback_context = |
| static_cast<LwpInfoCallbackCtx *>(context); |
| MDRawThread lwp; |
| memset(&lwp, 0, sizeof(MDRawThread)); |
| |
| if (lsp->pr_lwpid != callback_context->writer_args->crashed_lwpid || |
| callback_context->writer_args->sig_ctx == NULL) { |
| success = WriteLwpStream(callback_context->minidump_writer, |
| callback_context->writer_args->lwp_lister, |
| lsp, &lwp); |
| } else { |
| success = WriteCrashedLwpStream(callback_context->minidump_writer, |
| callback_context->writer_args, |
| lsp, &lwp); |
| } |
| if (success) { |
| callback_context->list->CopyIndexAfterObject( |
| callback_context->lwp_index++, |
| &lwp, sizeof(MDRawThread)); |
| } |
| } |
| |
| return success; |
| } |
| |
| bool WriteLwpListStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| // Get the lwp information. |
| const SolarisLwp *lwp_lister = writer_args->lwp_lister; |
| int lwp_count = lwp_lister->GetLwpCount(); |
| if (lwp_count < 0) |
| return false; |
| TypedMDRVA<MDRawThreadList> list(minidump_writer); |
| if (!list.AllocateObjectAndArray(lwp_count - 1, sizeof(MDRawThread))) |
| return false; |
| dir->stream_type = MD_THREAD_LIST_STREAM; |
| dir->location = list.location(); |
| list.get()->number_of_threads = lwp_count - 1; |
| |
| LwpInfoCallbackCtx context; |
| context.minidump_writer = minidump_writer; |
| context.writer_args = writer_args; |
| context.list = &list; |
| context.lwp_index = 0; |
| CallbackParam<LwpCallback> callback_param(LwpInformationCallback, |
| &context); |
| int written = |
| lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); |
| return written == lwp_count; |
| } |
| |
| bool WriteCVRecord(MinidumpFileWriter *minidump_writer, |
| MDRawModule *module, |
| const char *module_path, |
| char *realname) { |
| TypedMDRVA<MDCVInfoPDB70> cv(minidump_writer); |
| |
| char path[PATH_MAX]; |
| const char *module_name = module_path ? module_path : "<Unknown>"; |
| snprintf(path, sizeof(path), "/proc/self/object/%s", module_name); |
| |
| size_t module_name_length = strlen(realname); |
| if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t))) |
| return false; |
| if (!cv.CopyIndexAfterObject(0, realname, module_name_length)) |
| return false; |
| |
| module->cv_record = cv.location(); |
| MDCVInfoPDB70 *cv_ptr = cv.get(); |
| memset(cv_ptr, 0, sizeof(MDCVInfoPDB70)); |
| cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; |
| cv_ptr->age = 0; |
| |
| // Get the module identifier |
| FileID file_id(path); |
| unsigned char identifier[16]; |
| |
| if (file_id.ElfFileIdentifier(identifier)) { |
| cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | |
| (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | |
| (uint32_t)identifier[3]; |
| cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; |
| cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; |
| cv_ptr->signature.data4[0] = identifier[8]; |
| cv_ptr->signature.data4[1] = identifier[9]; |
| cv_ptr->signature.data4[2] = identifier[10]; |
| cv_ptr->signature.data4[3] = identifier[11]; |
| cv_ptr->signature.data4[4] = identifier[12]; |
| cv_ptr->signature.data4[5] = identifier[13]; |
| cv_ptr->signature.data4[6] = identifier[14]; |
| cv_ptr->signature.data4[7] = identifier[15]; |
| } |
| return true; |
| } |
| |
| struct ModuleInfoCallbackCtx { |
| MinidumpFileWriter *minidump_writer; |
| const WriterArgument *writer_args; |
| TypedMDRVA<MDRawModuleList> *list; |
| int module_index; |
| }; |
| |
| bool ModuleInfoCallback(const ModuleInfo &module_info, void *context) { |
| ModuleInfoCallbackCtx *callback_context = |
| static_cast<ModuleInfoCallbackCtx *>(context); |
| // Skip those modules without name, or those that are not modules. |
| if (strlen(module_info.name) == 0) |
| return true; |
| |
| MDRawModule module; |
| memset(&module, 0, sizeof(module)); |
| MDLocationDescriptor loc; |
| char path[PATH_MAX]; |
| char buf[PATH_MAX]; |
| char *realname; |
| int count; |
| |
| snprintf(path, sizeof (path), "/proc/self/path/%s", module_info.name); |
| if ((count = readlink(path, buf, PATH_MAX)) < 0) |
| return false; |
| buf[count] = '\0'; |
| |
| if ((realname = strrchr(buf, '/')) == NULL) |
| return false; |
| realname++; |
| |
| if (!callback_context->minidump_writer->WriteString(realname, 0, &loc)) |
| return false; |
| |
| module.base_of_image = (uint64_t)module_info.start_addr; |
| module.size_of_image = module_info.size; |
| module.module_name_rva = loc.rva; |
| |
| if (!WriteCVRecord(callback_context->minidump_writer, &module, |
| module_info.name, realname)) |
| return false; |
| |
| callback_context->list->CopyIndexAfterObject( |
| callback_context->module_index++, &module, MD_MODULE_SIZE); |
| return true; |
| } |
| |
| bool WriteModuleListStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| TypedMDRVA<MDRawModuleList> list(minidump_writer); |
| int module_count = writer_args->lwp_lister->GetModuleCount(); |
| |
| if (module_count <= 0 || |
| !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) { |
| return false; |
| } |
| |
| dir->stream_type = MD_MODULE_LIST_STREAM; |
| dir->location = list.location(); |
| list.get()->number_of_modules = module_count; |
| ModuleInfoCallbackCtx context; |
| context.minidump_writer = minidump_writer; |
| context.writer_args = writer_args; |
| context.list = &list; |
| context.module_index = 0; |
| CallbackParam<ModuleCallback> callback(ModuleInfoCallback, &context); |
| return writer_args->lwp_lister->ListModules(&callback) == module_count; |
| } |
| |
| bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| TypedMDRVA<MDRawSystemInfo> sys_info(minidump_writer); |
| |
| if (!sys_info.Allocate()) |
| return false; |
| |
| dir->stream_type = MD_SYSTEM_INFO_STREAM; |
| dir->location = sys_info.location(); |
| |
| return WriteCPUInformation(sys_info.get()) && |
| WriteOSInformation(minidump_writer, sys_info.get()); |
| } |
| |
| bool WriteExceptionStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| // This happenes when this is not a crash, but a requested dump. |
| if (writer_args->sig_ctx == NULL) |
| return false; |
| |
| TypedMDRVA<MDRawExceptionStream> exception(minidump_writer); |
| if (!exception.Allocate()) |
| return false; |
| |
| dir->stream_type = MD_EXCEPTION_STREAM; |
| dir->location = exception.location(); |
| exception.get()->thread_id = writer_args->crashed_lwpid; |
| exception.get()->exception_record.exception_code = writer_args->signo; |
| exception.get()->exception_record.exception_flags = 0; |
| |
| #if TARGET_CPU_SPARC |
| if (writer_args->sig_ctx != NULL) { |
| exception.get()->exception_record.exception_address = |
| writer_args->sig_ctx->uc_mcontext.gregs[REG_PC]; |
| } else { |
| return true; |
| } |
| |
| // Write context of the exception. |
| TypedMDRVA<MDRawContextSPARC> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| exception.get()->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextSPARC)); |
| return WriteContext(context.get(), writer_args->sig_ctx); |
| #elif TARGET_CPU_X86 |
| if (writer_args->sig_ctx != NULL) { |
| exception.get()->exception_record.exception_address = |
| writer_args->sig_ctx->uc_mcontext.gregs[EIP]; |
| } else { |
| return true; |
| } |
| |
| // Write context of the exception. |
| TypedMDRVA<MDRawContextX86> context(minidump_writer); |
| if (!context.Allocate()) |
| return false; |
| exception.get()->thread_context = context.location(); |
| memset(context.get(), 0, sizeof(MDRawContextX86)); |
| return WriteContext(context.get(), |
| (int *)&writer_args->sig_ctx->uc_mcontext.gregs, |
| NULL); |
| #endif |
| } |
| |
| bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| TypedMDRVA<MDRawMiscInfo> info(minidump_writer); |
| |
| if (!info.Allocate()) |
| return false; |
| |
| dir->stream_type = MD_MISC_INFO_STREAM; |
| dir->location = info.location(); |
| info.get()->size_of_info = sizeof(MDRawMiscInfo); |
| info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; |
| info.get()->process_id = writer_args->requester_pid; |
| |
| return true; |
| } |
| |
| bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer, |
| const WriterArgument *writer_args, |
| MDRawDirectory *dir) { |
| TypedMDRVA<MDRawBreakpadInfo> info(minidump_writer); |
| |
| if (!info.Allocate()) |
| return false; |
| |
| dir->stream_type = MD_BREAKPAD_INFO_STREAM; |
| dir->location = info.location(); |
| |
| info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | |
| MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; |
| info.get()->dump_thread_id = getpid(); |
| info.get()->requesting_thread_id = writer_args->requester_pid; |
| return true; |
| } |
| |
| class AutoLwpResumer { |
| public: |
| AutoLwpResumer(SolarisLwp *lwp) : lwp_(lwp) {} |
| ~AutoLwpResumer() { lwp_->ControlAllLwps(false); } |
| private: |
| SolarisLwp *lwp_; |
| }; |
| |
| // Prototype of writer functions. |
| typedef bool (*WriteStreamFN)(MinidumpFileWriter *, |
| const WriterArgument *, |
| MDRawDirectory *); |
| |
| // Function table to writer a full minidump. |
| const WriteStreamFN writers[] = { |
| WriteLwpListStream, |
| WriteModuleListStream, |
| WriteSystemInfoStream, |
| WriteExceptionStream, |
| WriteMiscInfoStream, |
| WriteBreakpadInfoStream, |
| }; |
| |
| // Will call each writer function in the writers table. |
| //void* MinidumpGenerator::Write(void *argument) { |
| void* Write(void *argument) { |
| WriterArgument *writer_args = static_cast<WriterArgument *>(argument); |
| |
| if (!writer_args->lwp_lister->ControlAllLwps(true)) |
| return NULL; |
| |
| AutoLwpResumer lwpResumer(writer_args->lwp_lister); |
| |
| if (writer_args->sighandler_ebp != 0 && |
| writer_args->lwp_lister->FindSigContext(writer_args->sighandler_ebp, |
| &writer_args->sig_ctx)) { |
| writer_args->crashed_stack_bottom = |
| writer_args->lwp_lister->GetLwpStackBottom( |
| #if TARGET_CPU_SPARC |
| writer_args->sig_ctx->uc_mcontext.gregs[REG_O6] |
| #elif TARGET_CPU_X86 |
| writer_args->sig_ctx->uc_mcontext.gregs[UESP] |
| #endif |
| ); |
| |
| int crashed_lwpid = FindCrashingLwp(writer_args->crashed_stack_bottom, |
| writer_args->requester_pid, |
| writer_args->lwp_lister); |
| if (crashed_lwpid > 0) |
| writer_args->crashed_lwpid = crashed_lwpid; |
| } |
| |
| MinidumpFileWriter *minidump_writer = writer_args->minidump_writer; |
| TypedMDRVA<MDRawHeader> header(minidump_writer); |
| TypedMDRVA<MDRawDirectory> dir(minidump_writer); |
| if (!header.Allocate()) |
| return 0; |
| |
| int writer_count = sizeof(writers) / sizeof(writers[0]); |
| // Need directory space for all writers. |
| if (!dir.AllocateArray(writer_count)) |
| return 0; |
| header.get()->signature = MD_HEADER_SIGNATURE; |
| header.get()->version = MD_HEADER_VERSION; |
| header.get()->time_date_stamp = time(NULL); |
| header.get()->stream_count = writer_count; |
| header.get()->stream_directory_rva = dir.position(); |
| |
| int dir_index = 0; |
| MDRawDirectory local_dir; |
| for (int i = 0; i < writer_count; ++i) { |
| if ((*writers[i])(minidump_writer, writer_args, &local_dir)) |
| dir.CopyIndex(dir_index++, &local_dir); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace |
| |
| namespace google_breakpad { |
| |
| MinidumpGenerator::MinidumpGenerator() { |
| } |
| |
| MinidumpGenerator::~MinidumpGenerator() { |
| } |
| |
| // Write minidump into file. |
| // It runs in a different thread from the crashing thread. |
| bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname, |
| int signo, |
| uintptr_t sighandler_ebp, |
| ucontext_t **sig_ctx) const { |
| // The exception handler thread. |
| pthread_t handler_thread; |
| |
| assert(file_pathname != NULL); |
| |
| if (file_pathname == NULL) |
| return false; |
| |
| MinidumpFileWriter minidump_writer; |
| if (minidump_writer.Open(file_pathname)) { |
| WriterArgument argument; |
| memset(&argument, 0, sizeof(argument)); |
| SolarisLwp lwp_lister(getpid()); |
| argument.lwp_lister = &lwp_lister; |
| argument.minidump_writer = &minidump_writer; |
| argument.requester_pid = getpid(); |
| argument.crashed_lwpid = pthread_self(); |
| argument.signo = signo; |
| argument.sighandler_ebp = sighandler_ebp; |
| argument.sig_ctx = NULL; |
| |
| pthread_create(&handler_thread, NULL, Write, (void *)&argument); |
| pthread_join(handler_thread, NULL); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace google_breakpad |