From dd2ff4a21c57672170eb14ccc5142efd7d92f3f1 Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Tue, 18 Mar 2008 16:10:10 +0000 Subject: issue 223 - Fixes for SOlaris handler during integration with Firefox. patch by Alfred Peng, r=mento,me git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@250 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/solaris/handler/exception_handler.cc | 86 ++-- src/client/solaris/handler/exception_handler.h | 42 +- src/client/solaris/handler/minidump_generator.cc | 488 +++++++++++++++++------ src/client/solaris/handler/minidump_generator.h | 64 +-- src/client/solaris/handler/minidump_test.cc | 2 +- src/client/solaris/handler/solaris_lwp.cc | 125 +++++- src/client/solaris/handler/solaris_lwp.h | 16 + src/common/solaris/dump_symbols.cc | 187 ++++++--- src/google_breakpad/processor/minidump.h | 1 + src/google_breakpad/processor/stack_frame_cpu.h | 6 +- src/processor/minidump.cc | 4 + 11 files changed, 687 insertions(+), 334 deletions(-) diff --git a/src/client/solaris/handler/exception_handler.cc b/src/client/solaris/handler/exception_handler.cc index 3d35b8eb..7fc8d255 100644 --- a/src/client/solaris/handler/exception_handler.cc +++ b/src/client/solaris/handler/exception_handler.cc @@ -42,12 +42,9 @@ #include "common/solaris/guid_creator.h" #include "common/solaris/message_output.h" #include "google_breakpad/common/minidump_format.h" -#include "processor/scoped_ptr.h" namespace google_breakpad { -static const int kStackSize = 1024 * 1024; - // Signals that we are interested. static const int kSigTable[] = { SIGSEGV, @@ -68,10 +65,9 @@ ExceptionHandler::ExceptionHandler(const string &dump_path, void *callback_context, bool install_handler) : filter_(filter), - handler_thread_(0), - handler_return_value_(false), callback_(callback), callback_context_(callback_context), + dump_path_(), installed_handler_(install_handler) { set_dump_path(dump_path); @@ -79,19 +75,6 @@ ExceptionHandler::ExceptionHandler(const string &dump_path, SetupHandler(); } - sem_init(&handler_start_semaphore_, 0, 0); - sem_init(&handler_finish_semaphore_, 0, 0); - pthread_attr_t attr; - scoped_array thread_stack; - - pthread_attr_init(&attr); - thread_stack.reset(new char[kStackSize]); - pthread_attr_setstackaddr(&attr, thread_stack.get()); - pthread_attr_setstacksize(&attr, kStackSize); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&handler_thread_, &attr, ExceptionHandlerThreadMain, this); - pthread_attr_destroy(&attr); - if (install_handler) { pthread_mutex_lock(&handler_stack_mutex_); @@ -125,34 +108,11 @@ ExceptionHandler::~ExceptionHandler() { delete handler_stack_; handler_stack_ = NULL; } - pthread_exit((void *)handler_thread_); - sem_destroy(&handler_start_semaphore_); - sem_destroy(&handler_finish_semaphore_); pthread_mutex_unlock(&handler_stack_mutex_); } -// static -void* ExceptionHandler::ExceptionHandlerThreadMain(void *lpParameter) { - ExceptionHandler *self = reinterpret_cast(lpParameter); - assert(self); - - while (true) { - if (!sem_wait(&(self->handler_start_semaphore_))) { - // Perform the requested action. - self->handler_return_value_ = self->InternalWriteMinidump(); - - // Allow the requesting thread to proceed. - sem_post(&(self->handler_finish_semaphore_)); - } - } - - // Not reached. This thread will be terminated by ExceptionHandler's - // destructor. - return 0; -} - bool ExceptionHandler::WriteMinidump() { - return WriteMinidumpOnHandlerThread(0); + return InternalWriteMinidump(0, 0, NULL); } // static @@ -161,7 +121,7 @@ bool ExceptionHandler::WriteMinidump(const string &dump_path, void *callback_context) { ExceptionHandler handler(dump_path, NULL, callback, callback_context, false); - return handler.WriteMinidumpOnHandlerThread(0); + return handler.InternalWriteMinidump(0, 0, NULL); } void ExceptionHandler::SetupHandler() { @@ -204,22 +164,22 @@ void ExceptionHandler::TeardownAllHandlers() { } } -bool ExceptionHandler::WriteMinidumpOnHandlerThread(int signo) { - // Set up data to be passed in to the handler thread. - signo_ = signo; - - // This causes the handler thread to call InternalWriteMinidump. - sem_post(&handler_start_semaphore_); - - // Wait until InternalWriteMinidump is done and collect its return value. - sem_wait(&handler_finish_semaphore_); - bool status = handler_return_value_; - - return status; -} - // static void ExceptionHandler::HandleException(int signo) { +//void ExceptionHandler::HandleException(int signo, siginfo_t *sip, ucontext_t *sig_ctx) { + // The context information about the signal is put on the stack of + // the signal handler frame as value parameter. For some reasons, the + // prototype of the handler doesn't declare this information as parameter, we + // will do it by hand. The stack layout for a signal handler frame is here: + // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 + // + // However, if we are being called by another signal handler passing the + // signal up the chain, then we may not have this random extra parameter, + // so we may have to walk the stack to find it. We do the actual work + // on another thread, where it's a little safer, but we want the ebp + // from this frame to find it. + uintptr_t current_ebp = (uintptr_t)_getfp(); + pthread_mutex_lock(&handler_stack_mutex_); ExceptionHandler *current_handler = handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); @@ -227,7 +187,10 @@ void ExceptionHandler::HandleException(int signo) { // Restore original handler. current_handler->TeardownHandler(signo); - if (current_handler->WriteMinidumpOnHandlerThread(signo)) { + + ucontext_t *sig_ctx = NULL; + if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { +// if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) { // Fully handled this exception, safe to exit. exit(EXIT_FAILURE); } else { @@ -253,7 +216,9 @@ void ExceptionHandler::HandleException(int signo) { pthread_mutex_unlock(&handler_stack_mutex_); } -bool ExceptionHandler::InternalWriteMinidump() { +bool ExceptionHandler::InternalWriteMinidump(int signo, + uintptr_t sighandler_ebp, + ucontext_t **sig_ctx) { if (filter_ && !filter_(callback_context_)) return false; @@ -277,7 +242,8 @@ bool ExceptionHandler::InternalWriteMinidump() { print_message1(2, "HandleException: failed to block signals.\n"); } - success = minidump_generator_.WriteMinidumpToFile(minidump_path, signo_); + success = minidump_generator_.WriteMinidumpToFile( + minidump_path, signo, sighandler_ebp, sig_ctx); // Unblock the signals. if (blocked) diff --git a/src/client/solaris/handler/exception_handler.h b/src/client/solaris/handler/exception_handler.h index ab035905..4d72485f 100644 --- a/src/client/solaris/handler/exception_handler.h +++ b/src/client/solaris/handler/exception_handler.h @@ -32,9 +32,6 @@ #ifndef CLIENT_SOLARIS_HANDLER_EXCEPTION_HANDLER_H__ #define CLIENT_SOLARIS_HANDLER_EXCEPTION_HANDLER_H__ -#include -#include - #include #include #include @@ -119,9 +116,11 @@ class ExceptionHandler { bool install_handler); ~ExceptionHandler(); - // Set the minidump path. + // Get and Set the minidump path. + string dump_path() const { return dump_path_; } void set_dump_path(const string &dump_path) { - dump_path_c_ = dump_path.c_str(); + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); } // Writes a minidump immediately. This can be used to capture the @@ -150,36 +149,25 @@ class ExceptionHandler { // Signal handler. static void HandleException(int signo); - // Trigger the call to InternalWriteMinidump and wait for the return value. - bool WriteMinidumpOnHandlerThread(int signo); - // Write all the information to the dump file. - bool InternalWriteMinidump(); + // If called from a signal handler, sighandler_ebp is the ebp of + // that signal handler's frame, and sig_ctx is an out parameter + // that will be set to point at the ucontext_t that was placed + // on the stack by the kernel. You can pass zero and NULL + // for the second and third parameters if you are not calling + // this from a signal handler. + bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp, + ucontext_t **sig_ctx); private: - // Signal number when crash happed. Can be 0 if this is a requested dump. - int signo_; - - // The exception handler thread. - pthread_t handler_thread_; - - // Semaphores used to move exception handling between the exception thread - // and the handler thread. handler_start_semaphore_ is signalled by the - // exception thread to wake up the handler thread when an exception occurs. - // handler_finish_semaphore_ is signalled by the handler thread to wake up - // the exception thread when handling is complete. - sem_t handler_start_semaphore_; - sem_t handler_finish_semaphore_; - - // The return value of the handler, passed from the handler thread back to - // the requesting thread. - bool handler_return_value_; - // The callbacks before and after writing the dump file. FilterCallback filter_; MinidumpCallback callback_; void *callback_context_; + // The directory in which a minidump will be written, set by the dump_path + // argument to the constructor, or set_dump_path. + string dump_path_; // C style dump path. Keep this when setting dump path, since calling // c_str() of std::string when crashing may not be safe. const char *dump_path_c_; diff --git a/src/client/solaris/handler/minidump_generator.cc b/src/client/solaris/handler/minidump_generator.cc index 25e7982f..ea046cff 100644 --- a/src/client/solaris/handler/minidump_generator.cc +++ b/src/client/solaris/handler/minidump_generator.cc @@ -30,12 +30,11 @@ // Author: Alfred Peng #include -#include +#include #include #include #include #include -#include #include #include @@ -45,21 +44,98 @@ #include "client/minidump_file_writer-inl.h" #include "common/solaris/file_id.h" -namespace google_breakpad { +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; -MinidumpGenerator::MinidumpGenerator() - : requester_pid_(0), - signo_(0), - lwp_lister_(NULL) { + 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(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; } -MinidumpGenerator::~MinidumpGenerator() { +// 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 callback_param(IsLwpCrashedCallback, + &context); + lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); + return context.crashing_lwpid; } -bool MinidumpGenerator::WriteLwpStack(uintptr_t last_esp, - UntypedMDRVA *memory, - MDMemoryDescriptor *loc) { - uintptr_t stack_bottom = lwp_lister_->GetLwpStackBottom(last_esp); +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) { @@ -75,29 +151,52 @@ bool MinidumpGenerator::WriteLwpStack(uintptr_t last_esp, } #if TARGET_CPU_SPARC -bool MinidumpGenerator::WriteContext(MDRawContextSPARC *context, prgregset_t regs, - prfpregset_t *fp_regs) { +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 = (unsigned int)(regs[32]); - context->pc = (unsigned int)(regs[R_PC]); - context->npc = (unsigned int)(regs[R_nPC]); - context->y = (unsigned int)(regs[R_Y]); - context->asi = (unsigned int)(regs[36]); - context->fprs = (unsigned int)(regs[37]); - + 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] = (unsigned int)(regs[i]); + context->g_r[i] = (uintptr_t)(regs[i]); } return true; } #elif TARGET_CPU_X86 -bool MinidumpGenerator::WriteContext(MDRawContextX86 *context, prgregset_t regs, - prfpregset_t *fp_regs) { +bool WriteContext(MDRawContextX86 *context, prgregset_t regs, + prfpregset_t *fp_regs) { if (!context || !regs) return false; @@ -124,18 +223,67 @@ bool MinidumpGenerator::WriteContext(MDRawContextX86 *context, prgregset_t regs, } #endif /* TARGET_CPU_XXX */ -bool MinidumpGenerator::WriteLwpStream(lwpstatus_t *lsp, MDRawThread *lwp) { +// 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 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 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; - prgregset_t *gregs = &(lsp->pr_reg); - UntypedMDRVA memory(&writer_); + const prgregset_t *gregs = &(lsp->pr_reg); + UntypedMDRVA memory(minidump_writer); #if TARGET_CPU_SPARC - if (!WriteLwpStack((*gregs)[R_SP], + if (!WriteLwpStack(lwp_lister, + (*gregs)[R_SP], &memory, &lwp->stack)) return false; // Write context - TypedMDRVA context(&writer_); + TypedMDRVA context(minidump_writer); if (!context.Allocate()) return false; // should be the thread_id @@ -143,13 +291,14 @@ bool MinidumpGenerator::WriteLwpStream(lwpstatus_t *lsp, MDRawThread *lwp) { lwp->thread_context = context.location(); memset(context.get(), 0, sizeof(MDRawContextSPARC)); #elif TARGET_CPU_X86 - if (!WriteLwpStack((*gregs)[UESP], + if (!WriteLwpStack(lwp_lister, + (*gregs)[UESP], &memory, &lwp->stack)) return false; // Write context - TypedMDRVA context(&writer_); + TypedMDRVA context(minidump_writer); if (!context.Allocate()) return false; // should be the thread_id @@ -160,7 +309,7 @@ bool MinidumpGenerator::WriteLwpStream(lwpstatus_t *lsp, MDRawThread *lwp) { return WriteContext(context.get(), (int *)gregs, &fp_regs); } -bool MinidumpGenerator::WriteCPUInformation(MDRawSystemInfo *sys_info) { +bool WriteCPUInformation(MDRawSystemInfo *sys_info) { struct utsname uts; char *major, *minor, *build; @@ -188,7 +337,8 @@ bool MinidumpGenerator::WriteCPUInformation(MDRawSystemInfo *sys_info) { return true; } -bool MinidumpGenerator::WriteOSInformation(MDRawSystemInfo *sys_info) { +bool WriteOSInformation(MinidumpFileWriter *minidump_writer, + MDRawSystemInfo *sys_info) { sys_info->platform_id = MD_OS_SOLARIS; struct utsname uts; @@ -220,7 +370,7 @@ bool MinidumpGenerator::WriteOSInformation(MDRawSystemInfo *sys_info) { } MDLocationDescriptor location; - if (!writer_.WriteString(os_version, 0, &location)) + if (!minidump_writer->WriteString(os_version, 0, &location)) return false; sys_info->csd_version_rva = location.rva; } @@ -229,21 +379,34 @@ bool MinidumpGenerator::WriteOSInformation(MDRawSystemInfo *sys_info) { // Callback context for get writting lwp information. struct LwpInfoCallbackCtx { - MinidumpGenerator *generator; + MinidumpFileWriter *minidump_writer; + const WriterArgument *writer_args; TypedMDRVA *list; int lwp_index; }; bool LwpInformationCallback(lwpstatus_t *lsp, void *context) { bool success = true; - // The current thread is the one to handle the crash. Ignore it. + LwpInfoCallbackCtx *callback_context = + static_cast(context); + + // The current lwp is the one to handle the crash. Ignore it. if (lsp->pr_lwpid != pthread_self()) { LwpInfoCallbackCtx *callback_context = static_cast(context); MDRawThread lwp; memset(&lwp, 0, sizeof(MDRawThread)); - success = callback_context->generator->WriteLwpStream(lsp, &lwp); + 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++, @@ -254,12 +417,15 @@ bool LwpInformationCallback(lwpstatus_t *lsp, void *context) { return success; } -bool MinidumpGenerator::WriteLwpListStream(MDRawDirectory *dir) { +bool WriteLwpListStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { // Get the lwp information. - int lwp_count = lwp_lister_->GetLwpCount(); + const SolarisLwp *lwp_lister = writer_args->lwp_lister; + int lwp_count = lwp_lister->GetLwpCount(); if (lwp_count < 0) return false; - TypedMDRVA list(&writer_); + TypedMDRVA list(minidump_writer); if (!list.AllocateObjectAndArray(lwp_count - 1, sizeof(MDRawThread))) return false; dir->stream_type = MD_THREAD_LIST_STREAM; @@ -267,31 +433,32 @@ bool MinidumpGenerator::WriteLwpListStream(MDRawDirectory *dir) { list.get()->number_of_threads = lwp_count - 1; LwpInfoCallbackCtx context; - context.generator = this; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; context.list = &list; context.lwp_index = 0; CallbackParam callback_param(LwpInformationCallback, &context); int written = - lwp_lister_->Lwp_iter_all(lwp_lister_->getpid(), &callback_param); + lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); return written == lwp_count; } -bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, - const char *module_path) { - TypedMDRVA cv(&writer_); +bool WriteCVRecord(MinidumpFileWriter *minidump_writer, + MDRawModule *module, + const char *module_path, + char *realname) { + TypedMDRVA cv(minidump_writer); char path[PATH_MAX]; const char *module_name = module_path ? module_path : ""; snprintf(path, sizeof(path), "/proc/self/object/%s", module_name); - size_t module_name_length = strlen(module_name); + size_t module_name_length = strlen(realname); if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) return false; - if (!cv.CopyIndexAfterObject(0, const_cast(module_name), - module_name_length)) { + if (!cv.CopyIndexAfterObject(0, realname, module_name_length)) return false; - } module->cv_record = cv.location(); MDCVInfoPDB70 *cv_ptr = cv.get(); @@ -322,8 +489,8 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, } struct ModuleInfoCallbackCtx { - MinidumpGenerator *generator; MinidumpFileWriter *minidump_writer; + const WriterArgument *writer_args; TypedMDRVA *list; int module_index; }; @@ -338,16 +505,29 @@ bool ModuleInfoCallback(const ModuleInfo &module_info, void *context) { MDRawModule module; memset(&module, 0, sizeof(module)); MDLocationDescriptor loc; - if (!callback_context->minidump_writer->WriteString(module_info.name, - 0, &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 = (u_int64_t)module_info.start_addr; module.size_of_image = module_info.size; module.module_name_rva = loc.rva; - if (!callback_context->generator->WriteCVRecord(&module, module_info.name)) + if (!WriteCVRecord(callback_context->minidump_writer, &module, + module_info.name, realname)) return false; callback_context->list->CopyIndexAfterObject( @@ -355,9 +535,11 @@ bool ModuleInfoCallback(const ModuleInfo &module_info, void *context) { return true; } -bool MinidumpGenerator::WriteModuleListStream(MDRawDirectory *dir) { - TypedMDRVA list(&writer_); - int module_count = lwp_lister_->GetModuleCount(); +bool WriteModuleListStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA list(minidump_writer); + int module_count = writer_args->lwp_lister->GetModuleCount(); if (module_count <= 0 || !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) { @@ -368,16 +550,18 @@ bool MinidumpGenerator::WriteModuleListStream(MDRawDirectory *dir) { dir->location = list.location(); list.get()->number_of_modules = module_count; ModuleInfoCallbackCtx context; - context.generator = this; - context.minidump_writer = &writer_; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; context.list = &list; context.module_index = 0; CallbackParam callback(ModuleInfoCallback, &context); - return lwp_lister_->ListModules(&callback) == module_count; + return writer_args->lwp_lister->ListModules(&callback) == module_count; } -bool MinidumpGenerator::WriteSystemInfoStream(MDRawDirectory *dir) { - TypedMDRVA sys_info(&writer_); +bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA sys_info(minidump_writer); if (!sys_info.Allocate()) return false; @@ -386,69 +570,65 @@ bool MinidumpGenerator::WriteSystemInfoStream(MDRawDirectory *dir) { dir->location = sys_info.location(); return WriteCPUInformation(sys_info.get()) && - WriteOSInformation(sys_info.get()); + WriteOSInformation(minidump_writer, sys_info.get()); } -bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *dir) { - ucontext_t uc; - gregset_t *gregs; - fpregset_t fp_regs; - - if (getcontext(&uc) != 0) +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 exception(&writer_); + TypedMDRVA exception(minidump_writer); if (!exception.Allocate()) return false; dir->stream_type = MD_EXCEPTION_STREAM; dir->location = exception.location(); - exception.get()->thread_id = requester_pid_; - exception.get()->exception_record.exception_code = signo_; + exception.get()->thread_id = writer_args->crashed_lwpid; + exception.get()->exception_record.exception_code = writer_args->signo; exception.get()->exception_record.exception_flags = 0; - gregs = &(uc.uc_mcontext.gregs); - fp_regs = uc.uc_mcontext.fpregs; #if TARGET_CPU_SPARC - exception.get()->exception_record.exception_address = ((unsigned int *)gregs)[1]; + 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 context(&writer_); + TypedMDRVA context(minidump_writer); if (!context.Allocate()) return false; exception.get()->thread_context = context.location(); memset(context.get(), 0, sizeof(MDRawContextSPARC)); - - // On Solaris i386, gregset_t = prgregset_t, fpregset_t = prfpregset_t - // But on Solaris Sparc are diffrent, see sys/regset.h and sys/procfs_isa.h - context.get()->context_flags = MD_CONTEXT_SPARC_FULL; - context.get()->ccr = ((unsigned int *)gregs)[0]; - context.get()->pc = ((unsigned int *)gregs)[1]; - context.get()->npc = ((unsigned int *)gregs)[2]; - context.get()->y = ((unsigned int *)gregs)[3]; - context.get()->asi = ((unsigned int *)gregs)[19]; - context.get()->fprs = ((unsigned int *)gregs)[20]; - for (int i = 0; i < 32; ++i) { - context.get()->g_r[i] = 0; - } - for (int i = 1; i < 16; ++i) { - context.get()->g_r[i] = ((unsigned int *)gregs)[i + 3]; - } - - return true; + return WriteContext(context.get(), writer_args->sig_ctx); #elif TARGET_CPU_X86 - exception.get()->exception_record.exception_address = (*gregs)[EIP]; + 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 context(&writer_); + TypedMDRVA 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 *)gregs, &fp_regs); -#endif /* TARGET_CPU_XXX */ + return WriteContext(context.get(), + (int *)&writer_args->sig_ctx->uc_mcontext.gregs, + NULL); +#endif } -bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *dir) { - TypedMDRVA info(&writer_); +bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA info(minidump_writer); if (!info.Allocate()) return false; @@ -457,13 +637,15 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *dir) { dir->location = info.location(); info.get()->size_of_info = sizeof(MDRawMiscInfo); info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; - info.get()->process_id = requester_pid_; + info.get()->process_id = writer_args->requester_pid; return true; } -bool MinidumpGenerator::WriteBreakpadInfoStream(MDRawDirectory *dir) { - TypedMDRVA info(&writer_); +bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA info(minidump_writer); if (!info.Allocate()) return false; @@ -474,7 +656,7 @@ bool MinidumpGenerator::WriteBreakpadInfoStream(MDRawDirectory *dir) { 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 = requester_pid_; + info.get()->requesting_thread_id = writer_args->requester_pid; return true; } @@ -486,25 +668,53 @@ class AutoLwpResumer { 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() { - // Function table to writer a full minidump. - const WriteStreamFN writers[] = { - &MinidumpGenerator::WriteLwpListStream, - &MinidumpGenerator::WriteModuleListStream, - &MinidumpGenerator::WriteSystemInfoStream, - &MinidumpGenerator::WriteExceptionStream, - &MinidumpGenerator::WriteMiscInfoStream, - &MinidumpGenerator::WriteBreakpadInfoStream, - }; - - if (!lwp_lister_->ControlAllLwps(true)) +//void* MinidumpGenerator::Write(void *argument) { +void* Write(void *argument) { + WriterArgument *writer_args = static_cast(argument); + + if (!writer_args->lwp_lister->ControlAllLwps(true)) return NULL; - AutoLwpResumer lwpResumer(lwp_lister_); + 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; + } - TypedMDRVA header(&writer_); - TypedMDRVA dir(&writer_); + MinidumpFileWriter *minidump_writer = writer_args->minidump_writer; + TypedMDRVA header(minidump_writer); + TypedMDRVA dir(minidump_writer); if (!header.Allocate()) return 0; @@ -521,29 +731,53 @@ void* MinidumpGenerator::Write() { int dir_index = 0; MDRawDirectory local_dir; for (int i = 0; i < writer_count; ++i) { - if ((this->*writers[i])(&local_dir)) + 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) { + 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; - if (writer_.Open(file_pathname)) { + MinidumpFileWriter minidump_writer; + if (minidump_writer.Open(file_pathname)) { + WriterArgument argument; + memset(&argument, 0, sizeof(argument)); SolarisLwp lwp_lister(getpid()); - lwp_lister_ = &lwp_lister; - requester_pid_ = getpid(); - signo_ = signo; - if (Write()) - return true; + 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; diff --git a/src/client/solaris/handler/minidump_generator.h b/src/client/solaris/handler/minidump_generator.h index 82309936..882f9e1d 100644 --- a/src/client/solaris/handler/minidump_generator.h +++ b/src/client/solaris/handler/minidump_generator.h @@ -32,13 +32,7 @@ #ifndef CLIENT_SOLARIS_HANDLER_MINIDUMP_GENERATOR_H__ #define CLIENT_SOLARIS_HANDLER_MINIDUMP_GENERATOR_H__ -#if defined(sparc) || defined(__sparc__) -#define TARGET_CPU_SPARC 1 -#elif defined(i386) || defined(__i386__) -#define TARGET_CPU_X86 1 -#else -#error "cannot determine cpu type" -#endif +#include #include "client/minidump_file_writer.h" #include "client/solaris/handler/solaris_lwp.h" @@ -66,59 +60,9 @@ class MinidumpGenerator { // Write minidump. bool WriteMinidumpToFile(const char *file_pathname, - int signo); - - private: - // Helpers - bool WriteCVRecord(MDRawModule *module, const char *module_path); - - // Write the lwp stack information to dump file. - bool WriteLwpStack(uintptr_t last_esp, UntypedMDRVA *memory, - MDMemoryDescriptor *loc); - - // Write CPU context based on provided registers. -#if TARGET_CPU_SPARC - bool WriteContext(MDRawContextSPARC *context, prgregset_t regs, - prfpregset_t *fp_regs); -#elif TARGET_CPU_X86 - bool WriteContext(MDRawContextX86 *context, prgregset_t regs, - prfpregset_t *fp_regs); -#endif /* TARGET_CPU_XXX */ - - // Write information about a lwp. - // Only processes lwp running normally at the crash. - bool WriteLwpStream(lwpstatus_t *lsp, MDRawThread *lwp); - - // Write the CPU information to the dump file. - bool WriteCPUInformation(MDRawSystemInfo *sys_info); - - //Write the OS information to the dump file. - bool WriteOSInformation(MDRawSystemInfo *sys_info); - - typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *); - - // Write all the information to the dump file. - void *Write(); - - // Stream writers - bool WriteLwpListStream(MDRawDirectory *dir); - bool WriteModuleListStream(MDRawDirectory *dir); - bool WriteSystemInfoStream(MDRawDirectory *dir); - bool WriteExceptionStream(MDRawDirectory *dir); - bool WriteMiscInfoStream(MDRawDirectory *dir); - bool WriteBreakpadInfoStream(MDRawDirectory *dir); - - private: - MinidumpFileWriter writer_; - - // Pid of the lwp who called WriteMinidumpToFile - int requester_pid_; - - // Signal number when crash happed. Can be 0 if this is a requested dump. - int signo_; - - // Used to get information about the lwps. - SolarisLwp *lwp_lister_; + int signo, + uintptr_t sighandler_ebp, + ucontext_t **sig_ctx) const; }; } // namespace google_breakpad diff --git a/src/client/solaris/handler/minidump_test.cc b/src/client/solaris/handler/minidump_test.cc index 03b7148c..33302d86 100644 --- a/src/client/solaris/handler/minidump_test.cc +++ b/src/client/solaris/handler/minidump_test.cc @@ -48,7 +48,7 @@ static void *Reporter(void *) { snprintf(buffer, sizeof(buffer), "./minidump_test.out"); fprintf(stdout, "Writing %s\n", buffer); - md.WriteMinidumpToFile(buffer, 0); + md.WriteMinidumpToFile(buffer, 0, 0, NULL); doneWritingReport = true; return NULL; diff --git a/src/client/solaris/handler/solaris_lwp.cc b/src/client/solaris/handler/solaris_lwp.cc index 9ce7b4fd..0148997a 100644 --- a/src/client/solaris/handler/solaris_lwp.cc +++ b/src/client/solaris/handler/solaris_lwp.cc @@ -30,9 +30,11 @@ // Author: Alfred Peng #include +#include #include #include #include +#include #include #include #include @@ -56,6 +58,16 @@ uintptr_t stack_base_address = 0; static const int HEADER_MAX = 2000; static const int MAP_MAX = 1000; +// Context information for the callbacks when validating address by listing +// modules. +struct AddressValidatingContext { + uintptr_t address; + bool is_mapped; + + AddressValidatingContext() : address(0UL), is_mapped(false) { + } +}; + // Convert from string to int. static bool LocalAtoi(char *s, int *r) { assert(s != NULL); @@ -69,18 +81,19 @@ static bool LocalAtoi(char *s, int *r) { } // Callback invoked for each mapped module. -// It use the module's adderss range to validate the address. +// It uses the module's adderss range to validate the address. static bool AddressNotInModuleCallback(const ModuleInfo &module_info, void *context) { - uintptr_t addr = reinterpret_cast(context); - if ((module_info.start_addr > 0) && - (addr >= module_info.start_addr) && - (addr <= module_info.start_addr + module_info.size)) { + AddressValidatingContext *addr = + reinterpret_cast(context); + if (addr->is_mapped = ((module_info.start_addr > 0) && + (addr->address >= module_info.start_addr) && + (addr->address <= module_info.start_addr + + module_info.size))) { stack_base_address = module_info.start_addr + module_info.size; - return false; } - return true; + return !addr->is_mapped; } static int IterateLwpAll(int pid, @@ -114,6 +127,28 @@ static int IterateLwpAll(int pid, return count; } +#if defined(__i386) && !defined(NO_FRAME_POINTER) +void *GetNextFrame(void **last_ebp) { + void *sp = *last_ebp; + if ((unsigned long)sp == (unsigned long)last_ebp) + return NULL; + if ((unsigned long)sp & (sizeof(void *) - 1)) + return NULL; + if ((unsigned long)sp - (unsigned long)last_ebp > 100000) + return NULL; + return sp; +} +#elif defined(__sparc) +void *GetNextFrame(void *last_ebp) { + return reinterpret_cast(last_ebp)->fr_savfp; +} +#else +void *GetNextFrame(void **last_ebp) { + return reinterpret_cast(last_ebp); +} +#endif + + class AutoCloser { public: AutoCloser(int fd) : fd_(fd) {} @@ -250,8 +285,10 @@ int SolarisLwp::Lwp_iter_all(int pid, } uintptr_t SolarisLwp::GetLwpStackBottom(uintptr_t current_esp) const { + AddressValidatingContext addr; + addr.address = current_esp; CallbackParam callback_param(AddressNotInModuleCallback, - (void *)current_esp); + &addr); ListModules(&callback_param); return stack_base_address; } @@ -313,7 +350,28 @@ int SolarisLwp::ListModules( memset(&module, 0, sizeof (module)); module.start_addr = _maps->pr_vaddr; module.size = _maps->pr_size; - if ((strlen(name) > 0) && (strcmp(name, "a.out") != 0)) { + if (strlen(name) > 0) { + int objectfd = 0; + char path[PATH_MAX]; + char buf[SELFMAG]; + + snprintf(path, sizeof (path), "/proc/self/object/%s", name); + if ((objectfd = open(path, O_RDONLY)) < 0) { + print_message1(2, "can't open module file\n"); + continue; + } + + AutoCloser autocloser(objectfd); + + if (read(objectfd, buf, SELFMAG) != SELFMAG) { + print_message1(2, "can't read module file\n"); + continue; + } + if (buf[0] != ELFMAG0 || buf[1] != ELFMAG1 || + buf[2] != ELFMAG2 || buf[3] != ELFMAG3) { + continue; + } + strncpy(module.name, name, sizeof (module.name) - 1); ++module_count; } @@ -326,4 +384,53 @@ int SolarisLwp::ListModules( return module_count; } +// Check if the address is a valid virtual address. +// If the address is in any of the mapped modules, we take it as valid. +// Otherwise it is invalid. +bool SolarisLwp::IsAddressMapped(uintptr_t address) const { + AddressValidatingContext addr; + addr.address = address; + CallbackParam callback_param(AddressNotInModuleCallback, + &addr); + ListModules(&callback_param); + return addr.is_mapped; +} + +// We're looking for a ucontext_t as the second parameter +// to a signal handler function call. Luckily, the ucontext_t +// has an ebp(fp on SPARC) member which should match the ebp(fp) +// pointed to by the ebp(fp) of the signal handler frame. +// The Solaris stack looks like this: +// http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 +bool SolarisLwp::FindSigContext(uintptr_t sighandler_ebp, + ucontext_t **sig_ctx) { + uintptr_t previous_ebp; + uintptr_t sig_ebp; + const int MAX_STACK_DEPTH = 50; + int depth_counter = 0; + + do { +#if TARGET_CPU_SPARC + previous_ebp = reinterpret_cast(GetNextFrame( + reinterpret_cast(sighandler_ebp))); + *sig_ctx = reinterpret_cast(sighandler_ebp + sizeof (struct frame)); + uintptr_t sig_esp = (*sig_ctx)->uc_mcontext.gregs[REG_O6]; + if (sig_esp < previous_ebp && sig_esp > sighandler_ebp) + sig_ebp = (uintptr_t)(((struct frame *)sig_esp)->fr_savfp); + +#elif TARGET_CPU_X86 + previous_ebp = reinterpret_cast(GetNextFrame( + reinterpret_cast(sighandler_ebp))); + *sig_ctx = reinterpret_cast(sighandler_ebp + sizeof (struct frame) + + 3 * sizeof(uintptr_t)); + sig_ebp = (*sig_ctx)->uc_mcontext.gregs[EBP]; +#endif + sighandler_ebp = previous_ebp; + depth_counter++; + } while(previous_ebp != sig_ebp && sighandler_ebp != 0 && + IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); + + return previous_ebp == sig_ebp && previous_ebp != 0; +} + } // namespace google_breakpad diff --git a/src/client/solaris/handler/solaris_lwp.h b/src/client/solaris/handler/solaris_lwp.h index d859111b..0914cfcd 100644 --- a/src/client/solaris/handler/solaris_lwp.h +++ b/src/client/solaris/handler/solaris_lwp.h @@ -32,9 +32,18 @@ #ifndef CLIENT_SOLARIS_HANDLER_SOLARIS_LWP_H__ #define CLIENT_SOLARIS_HANDLER_SOLARIS_LWP_H__ +#if defined(sparc) || defined(__sparc) +#define TARGET_CPU_SPARC 1 +#elif defined(i386) || defined(__i386) +#define TARGET_CPU_X86 1 +#else +#error "cannot determine cpu type" +#endif + #include #include #include +#include #ifndef _KERNEL #define _KERNEL @@ -134,6 +143,13 @@ class SolarisLwp { // Get the bottom of the stack from esp. uintptr_t GetLwpStackBottom(uintptr_t current_esp) const; + // Finds a signal context on the stack given the ebp of our signal handler. + bool FindSigContext(uintptr_t sighandler_ebp, ucontext_t **sig_ctx); + + private: + // Check if the address is a valid virtual address. + bool IsAddressMapped(uintptr_t address) const; + private: // The pid of the process we are listing lwps. int pid_; diff --git a/src/common/solaris/dump_symbols.cc b/src/common/solaris/dump_symbols.cc index b9fc74f6..df1c5b09 100644 --- a/src/common/solaris/dump_symbols.cc +++ b/src/common/solaris/dump_symbols.cc @@ -40,6 +40,7 @@ #include #include +#include #include #include "common/solaris/dump_symbols.h" @@ -50,7 +51,15 @@ // This namespace contains helper functions. namespace { -// Symbol table entry for stabs. Sun CC specific. +using std::make_pair; + +#if defined(_LP64) +typedef Elf64_Sym Elf_Sym; +#else +typedef Elf32_Sym Elf_Sym; +#endif + +// Symbol table entry from stabs. Sun CC specific. struct slist { // String table index. unsigned int n_strx; @@ -61,6 +70,14 @@ struct slist { unsigned long n_value; }; +// Symbol table entry +struct SymbolEntry { + // Offset from the start of the file. + GElf_Addr offset; + // Function size. + GElf_Word size; +}; + // Infomation of a line. struct LineInfo { // Offset from start of the function. @@ -107,10 +124,20 @@ struct SourceFileInfo { std::vector func_info; }; +struct CompareString { + bool operator()(const char *s1, const char *s2) const { + return strcmp(s1, s2) < 0; + } +}; + +typedef std::map SymbolMap; + // Information of a symbol table. // This is the root of all types of symbol. struct SymbolInfo { std::vector source_file_info; + // Symbols information. + SymbolMap symbol_entries; }; // Stab section name. @@ -119,8 +146,32 @@ const char *kStabName = ".stab"; // Stab str section name. const char *kStabStrName = ".stabstr"; +// Symtab section name. +const char *kSymtabName = ".symtab"; + +// Strtab section name. +const char *kStrtabName = ".strtab"; + // Default buffer lenght for demangle. -const int demangleLen = 2000; +const int demangleLen = 20000; + +// Offset to the string table. +u_int64_t stringOffset = 0; + +// Update the offset to the start of the string index of the next +// object module for every N_ENDM stabs. +inline void RecalculateOffset(struct slist* cur_list, char *stabstr) { + while ((--cur_list)->n_strx == 0) ; + stringOffset += cur_list->n_strx; + + char *temp = stabstr + stringOffset; + while (*temp != '\0') { + ++stringOffset; + ++temp; + } + // Skip the extra '\0' + ++stringOffset; +} // Demangle using demangle library on Solaris. std::string Demangle(const char *mangled) { @@ -145,18 +196,6 @@ out: return std::string(mangled); } -// Find the prefered loading address of the binary. -GElf_Addr GetLoadingAddress(const GElf_Phdr *program_headers, int nheader) { - for (int i = 0; i < nheader; ++i) { - const GElf_Phdr &header = program_headers[i]; - // For executable, it is the PT_LOAD segment with offset to zero. - if (header.p_type == PT_LOAD && header.p_offset == 0) - return header.p_vaddr; - } - // For other types of ELF, return 0. - return 0; -} - bool WriteFormat(int fd, const char *fmt, ...) { va_list list; char buffer[4096]; @@ -226,9 +265,11 @@ int LoadLineInfo(struct slist *list, do { // Skip non line information. while (cur_list < list_end && cur_list->n_type != N_SLINE) { - // Only exit when got another function, or source file. - if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) + // Only exit when got another function, or source file, or end stab. + if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO || + cur_list->n_type == N_ENDM) { return cur_list - list; + } ++cur_list; } struct LineInfo line; @@ -248,7 +289,7 @@ int LoadLineInfo(struct slist *list, int LoadFuncSymbols(struct slist *list, struct slist *list_end, - const GElf_Shdr *stabstr_section, + char *stabstr, GElf_Word base, struct SourceFileInfo *source_file_info) { struct slist *cur_list = list; @@ -263,18 +304,20 @@ int LoadFuncSymbols(struct slist *list, return cur_list - list; } ++cur_list; + if (cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); continue; } while (cur_list->n_type == N_FUN) { struct FuncInfo func_info; memset(&func_info, 0, sizeof(func_info)); - func_info.name = - reinterpret_cast(cur_list->n_strx + - stabstr_section->sh_offset + base); + func_info.name = stabstr + cur_list->n_strx + stringOffset; // The n_value field is always 0 from stab generated by Sun CC. // TODO(Alfred): Find the correct value. func_info.addr = cur_list->n_value; ++cur_list; + if (cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); if (cur_list->n_type != N_ESYM && cur_list->n_type != N_ISYM && cur_list->n_type != N_FUN) { // Stack parameter size. @@ -282,6 +325,8 @@ int LoadFuncSymbols(struct slist *list, // Line info. cur_list += LoadLineInfo(cur_list, list_end, &func_info); } + if (cur_list < list_end && cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); // Functions in this module should have address bigger than the module // starting address. // @@ -296,48 +341,70 @@ int LoadFuncSymbols(struct slist *list, } // Compute size and rva information based on symbols loaded from stab section. -bool ComputeSizeAndRVA(GElf_Addr loading_addr, struct SymbolInfo *symbols) { +bool ComputeSizeAndRVA(struct SymbolInfo *symbols) { std::vector *sorted_files = &(symbols->source_file_info); + SymbolMap *symbol_entries = &(symbols->symbol_entries); for (size_t i = 0; i < sorted_files->size(); ++i) { struct SourceFileInfo &source_file = (*sorted_files)[i]; std::vector *sorted_functions = &(source_file.func_info); - for (size_t j = 0; j < sorted_functions->size(); ++j) { + int func_size = sorted_functions->size(); + + for (size_t j = 0; j < func_size; ++j) { struct FuncInfo &func_info = (*sorted_functions)[j]; - assert(func_info.addr >= loading_addr); - func_info.rva_to_base = func_info.addr - loading_addr; int line_count = func_info.line_info.size(); - func_info.size = - (line_count == 0) ? 0 : - func_info.line_info[line_count - 1].rva_to_func; + + // Discard the ending part of the name. + std::string func_name(func_info.name); + std::string::size_type last_colon = func_name.find_first_of(':'); + if (last_colon != std::string::npos) + func_name = func_name.substr(0, last_colon); + + // Fine the symbol offset from the loading address and size by name. + SymbolMap::const_iterator it = symbol_entries->find(func_name.c_str()); + if (it->second) { + func_info.rva_to_base = it->second->offset; + func_info.size = (line_count == 0) ? 0 : it->second->size; + } else { + func_info.rva_to_base = 0; + func_info.size = 0; + } + // Compute function and line size. for (size_t k = 0; k < line_count; ++k) { struct LineInfo &line_info = func_info.line_info[k]; - if (k == 0) { - line_info.size = line_info.rva_to_func; + + line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; + if (k == line_count - 1) { + line_info.size = func_info.size - line_info.rva_to_func; } else { - line_info.size = - line_info.rva_to_func - func_info.line_info[k - 1].rva_to_func; + struct LineInfo &next_line = func_info.line_info[k + 1]; + line_info.size = next_line.rva_to_func - line_info.rva_to_func; } - line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; } // for each line. } // for each function. } // for each source file. + for (SymbolMap::iterator it = symbol_entries->begin(); + it != symbol_entries->end(); ++it) { + free(it->second); + } return true; } bool LoadAllSymbols(const GElf_Shdr *stab_section, const GElf_Shdr *stabstr_section, - GElf_Addr loading_addr, GElf_Word base, struct SymbolInfo *symbols) { if (stab_section == NULL || stabstr_section == NULL) return false; + char *stabstr = + reinterpret_cast(stabstr_section->sh_offset + base); struct slist *lists = reinterpret_cast(stab_section->sh_offset + base); int nstab = stab_section->sh_size / sizeof(struct slist); int source_id = 0; + // First pass, load all symbols from the object file. for (int i = 0; i < nstab; ) { int step = 1; @@ -345,9 +412,7 @@ bool LoadAllSymbols(const GElf_Shdr *stab_section, if (cur_list->n_type == N_SO) { // FUNC
struct SourceFileInfo source_file_info; - source_file_info.name = - reinterpret_cast(cur_list->n_strx + - stabstr_section->sh_offset + base); + source_file_info.name = stabstr + cur_list->n_strx + stringOffset; // The n_value field is always 0 from stab generated by Sun CC. // TODO(Alfred): Find the correct value. source_file_info.addr = cur_list->n_value; @@ -355,22 +420,19 @@ bool LoadAllSymbols(const GElf_Shdr *stab_section, source_file_info.source_id = source_id++; else source_file_info.source_id = -1; - step = LoadFuncSymbols(cur_list, lists + nstab - 1, - stabstr_section, base, &source_file_info); + step = LoadFuncSymbols(cur_list, lists + nstab - 1, stabstr, + base, &source_file_info); symbols->source_file_info.push_back(source_file_info); } i += step; } // Second pass, compute the size of functions and lines. - return ComputeSizeAndRVA(loading_addr, symbols); + return ComputeSizeAndRVA(symbols); } bool LoadSymbols(Elf *elf, GElf_Ehdr *elf_header, struct SymbolInfo *symbols, void *obj_base) { GElf_Word base = reinterpret_cast(obj_base); - GElf_Addr loading_addr = GetLoadingAddress( - reinterpret_cast(elf_header->e_phoff + base), - elf_header->e_phnum); const GElf_Shdr *sections = reinterpret_cast(elf_header->e_shoff + base); @@ -386,9 +448,34 @@ bool LoadSymbols(Elf *elf, GElf_Ehdr *elf_header, struct SymbolInfo *symbols, fprintf(stderr, "Stabstr section not found.\n"); return false; } + GElf_Shdr symtab_section; + if (!FindSectionByName(elf, kSymtabName, elf_header->e_shstrndx, + &symtab_section)) { + fprintf(stderr, "Symtab section not found.\n"); + return false; + } + GElf_Shdr strtab_section; + if (!FindSectionByName(elf, kStrtabName, elf_header->e_shstrndx, + &strtab_section)) { + fprintf(stderr, "Strtab section not found.\n"); + return false; + } + + Elf_Sym *symbol = (Elf_Sym *)((char *)base + symtab_section.sh_offset); + for (int i = 0; i < symtab_section.sh_size/symtab_section.sh_entsize; ++i) { + struct SymbolEntry *symbol_entry = + (struct SymbolEntry *)malloc(sizeof(struct SymbolEntry)); + const char *name = reinterpret_cast( + strtab_section.sh_offset + (GElf_Word)base + symbol->st_name); + symbol_entry->offset = symbol->st_value; + symbol_entry->size = symbol->st_size; + symbols->symbol_entries.insert(make_pair(name, symbol_entry)); + ++symbol; + } + // Load symbols. - return LoadAllSymbols(&stab_section, &stabstr_section, loading_addr, base, symbols); + return LoadAllSymbols(&stab_section, &stabstr_section, base, symbols); } bool WriteModuleInfo(int fd, GElf_Half arch, const std::string &obj_file) { @@ -397,8 +484,12 @@ bool WriteModuleInfo(int fd, GElf_Half arch, const std::string &obj_file) { arch_name = "x86"; else if (arch == EM_X86_64) arch_name = "x86_64"; - else + else if (arch == EM_SPARC32PLUS) + arch_name = "SPARC_32+"; + else { + printf("Please add more ARCH support\n"); return false; + } unsigned char identifier[16]; google_breakpad::FileID file_id(obj_file.c_str()); @@ -437,18 +528,20 @@ bool WriteOneFunction(int fd, int source_id, func_name = func_name.substr(0, last_colon); func_name = Demangle(func_name.c_str()); - if (func_info.size < 0) + if (func_info.size <= 0) return true; // rva_to_base could be unsigned long(32 bit) or unsigned long long(64 bit). - if (WriteFormat(fd, "FUNC %llx %d %d %s\n", + if (WriteFormat(fd, "FUNC %llx %x %d %s\n", (long long)func_info.rva_to_base, func_info.size, func_info.stack_param_size, func_name.c_str())) { for (size_t i = 0; i < func_info.line_info.size(); ++i) { const struct LineInfo &line_info = func_info.line_info[i]; - if (!WriteFormat(fd, "%llx %d %d %d\n", + if (line_info.line_num == 0) + return true; + if (!WriteFormat(fd, "%llx %x %d %d\n", (long long)line_info.rva_to_base, line_info.size, line_info.line_num, diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index 8c149337..9a135d2d 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -79,6 +79,7 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ #define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ +#include #include #include diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h index a8840278..70823b9c 100644 --- a/src/google_breakpad/processor/stack_frame_cpu.h +++ b/src/google_breakpad/processor/stack_frame_cpu.h @@ -128,9 +128,9 @@ struct StackFrameSPARC : public StackFrame { // to be confirmed enum ContextValidity { CONTEXT_VALID_NONE = 0, - CONTEXT_VALID_PC = 0 << 0, - CONTEXT_VALID_SP = 0 << 1, - CONTEXT_VALID_FP = 0 << 2, + CONTEXT_VALID_PC = 1 << 0, + CONTEXT_VALID_SP = 1 << 1, + CONTEXT_VALID_FP = 1 << 2, CONTEXT_VALID_ALL = -1 }; diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index f7bc7f31..d91332c5 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -2885,6 +2885,10 @@ string MinidumpSystemInfo::GetCPU() { cpu = "ppc"; break; + case MD_CPU_ARCHITECTURE_SPARC: + cpu = "sparc"; + break; + default: BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << HexString(system_info_.processor_architecture); -- cgit v1.2.1