diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/solaris/handler/Makefile | 3 | ||||
-rw-r--r-- | src/client/solaris/handler/minidump_generator.cc | 83 | ||||
-rw-r--r-- | src/client/solaris/handler/minidump_generator.h | 13 | ||||
-rw-r--r-- | src/client/solaris/handler/solaris_lwp.cc | 20 | ||||
-rw-r--r-- | src/google_breakpad/common/minidump_format.h | 129 | ||||
-rw-r--r-- | src/google_breakpad/processor/minidump.h | 6 | ||||
-rw-r--r-- | src/google_breakpad/processor/stack_frame_cpu.h | 24 | ||||
-rw-r--r-- | src/processor/minidump.cc | 163 | ||||
-rw-r--r-- | src/processor/minidump_processor.cc | 139 | ||||
-rw-r--r-- | src/processor/minidump_stackwalk.cc | 11 | ||||
-rw-r--r-- | src/processor/stackwalker.cc | 8 | ||||
-rw-r--r-- | src/processor/stackwalker_selftest.cc | 128 | ||||
-rw-r--r-- | src/processor/stackwalker_selftest_sol.s | 111 | ||||
-rw-r--r-- | src/processor/stackwalker_sparc.cc | 139 | ||||
-rw-r--r-- | src/processor/stackwalker_sparc.h | 86 |
15 files changed, 1023 insertions, 40 deletions
diff --git a/src/client/solaris/handler/Makefile b/src/client/solaris/handler/Makefile index 425d66f2..318ac8e5 100644 --- a/src/client/solaris/handler/Makefile +++ b/src/client/solaris/handler/Makefile @@ -73,4 +73,5 @@ $(BIN_DIR)/exception_handler_test:$(EXCEPTION_TEST_OBJ) $(CXX) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ clean: - rm -f $(BIN) *.o *.out *.dmp core + rm -f $(BIN) *.o *.out *.dmp core ../../minidump_file_writer.o\ + ../../../common/*.o ../../../common/solaris/*.o diff --git a/src/client/solaris/handler/minidump_generator.cc b/src/client/solaris/handler/minidump_generator.cc index bcb9d88b..f86e2df1 100644 --- a/src/client/solaris/handler/minidump_generator.cc +++ b/src/client/solaris/handler/minidump_generator.cc @@ -60,7 +60,7 @@ bool MinidumpGenerator::WriteLwpStack(uintptr_t last_esp, UntypedMDRVA *memory, MDMemoryDescriptor *loc) { uintptr_t stack_bottom = lwp_lister_->GetLwpStackBottom(last_esp); - if (stack_bottom > last_esp) { + if (stack_bottom >= last_esp) { int size = stack_bottom - last_esp; if (size > 0) { if (!memory->Allocate(size)) @@ -74,6 +74,28 @@ bool MinidumpGenerator::WriteLwpStack(uintptr_t last_esp, return false; } +#if TARGET_CPU_SPARC +bool MinidumpGenerator::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]); + + for ( int i = 0 ; i < 32 ; ++i ){ + context->g_r[i] = (unsigned int)(regs[i]); + } + + return true; +} +#elif TARGET_CPU_X86 bool MinidumpGenerator::WriteContext(MDRawContextX86 *context, prgregset_t regs, prfpregset_t *fp_regs) { if (!context || !regs) @@ -100,23 +122,41 @@ bool MinidumpGenerator::WriteContext(MDRawContextX86 *context, prgregset_t regs, return true; } +#endif /* TARGET_CPU_XXX */ bool MinidumpGenerator::WriteLwpStream(lwpstatus_t *lsp, MDRawThread *lwp) { prfpregset_t fp_regs = lsp->pr_fpreg; prgregset_t *gregs = &(lsp->pr_reg); UntypedMDRVA memory(&writer_); - if (!WriteLwpStack((*gregs)[UESP], +#if TARGET_CPU_SPARC + if (!WriteLwpStack((*gregs)[R_SP], &memory, &lwp->stack)) return false; // Write context + TypedMDRVA<MDRawContextSPARC> context(&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((*gregs)[UESP], + &memory, + &lwp->stack)) + return false; + + // Write context TypedMDRVA<MDRawContextX86> context(&writer_); if (!context.Allocate()) return false; - lwp->thread_id = lwp_lister_->getpid(); + // 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); } @@ -220,11 +260,11 @@ bool MinidumpGenerator::WriteLwpListStream(MDRawDirectory *dir) { if (lwp_count < 0) return false; TypedMDRVA<MDRawThreadList> list(&writer_); - if (!list.AllocateObjectAndArray(lwp_count, sizeof(MDRawThread))) + 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; + list.get()->number_of_threads = lwp_count - 1; LwpInfoCallbackCtx context; context.generator = this; @@ -351,8 +391,8 @@ bool MinidumpGenerator::WriteSystemInfoStream(MDRawDirectory *dir) { bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *dir) { ucontext_t uc; - prgregset_t *gregs; - prfpregset_t fp_regs; + gregset_t *gregs; + fpregset_t fp_regs; if (getcontext(&uc) != 0) return false; @@ -369,8 +409,34 @@ bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *dir) { 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]; + // Write context of the exception. + TypedMDRVA<MDRawContextSPARC> context(&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; +#elif TARGET_CPU_X86 exception.get()->exception_record.exception_address = (*gregs)[EIP]; - // Write context of the exception. TypedMDRVA<MDRawContextX86> context(&writer_); if (!context.Allocate()) @@ -378,6 +444,7 @@ bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *dir) { exception.get()->thread_context = context.location(); memset(context.get(), 0, sizeof(MDRawContextX86)); return WriteContext(context.get(), (int *)gregs, &fp_regs); +#endif /* TARGET_CPU_XXX */ } bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *dir) { diff --git a/src/client/solaris/handler/minidump_generator.h b/src/client/solaris/handler/minidump_generator.h index 36b92716..82309936 100644 --- a/src/client/solaris/handler/minidump_generator.h +++ b/src/client/solaris/handler/minidump_generator.h @@ -32,6 +32,14 @@ #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 "client/minidump_file_writer.h" #include "client/solaris/handler/solaris_lwp.h" #include "google_breakpad/common/breakpad_types.h" @@ -69,8 +77,13 @@ class MinidumpGenerator { 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. diff --git a/src/client/solaris/handler/solaris_lwp.cc b/src/client/solaris/handler/solaris_lwp.cc index 763e1977..9ce7b4fd 100644 --- a/src/client/solaris/handler/solaris_lwp.cc +++ b/src/client/solaris/handler/solaris_lwp.cc @@ -217,7 +217,7 @@ int SolarisLwp::Lwp_iter_all(int pid, lwpsinfo_t *Lpsp; long nstat; long ninfo; - int rv; + int rv = 0; /* * The /proc/pid/lstatus file has the array of lwpstatus_t's and the @@ -240,8 +240,9 @@ int SolarisLwp::Lwp_iter_all(int pid, sp = NULL; } if (callback_param && - !(rv = (callback_param->call_back)(sp, callback_param->context))) + !(callback_param->call_back)(sp, callback_param->context)) break; + ++rv; Lpsp = (lwpsinfo_t *)((uintptr_t)Lpsp + Lphp->pr_entsize); } @@ -279,7 +280,8 @@ int SolarisLwp::ListModules( return -1; /* - * Determine number of mappings. + * Determine number of mappings, this value must be + * larger than the actual module count */ size = status.st_size; if ((num = (int)(size / sizeof (prmap_t))) > MAP_MAX) { @@ -287,9 +289,6 @@ int SolarisLwp::ListModules( return -1; } - if (!callback_param) - return num; // return the Module count - if (read(fd, (void *)maps, size) < 0) { print_message2(2, "failed to read %d\n", fd); return -1; @@ -297,7 +296,8 @@ int SolarisLwp::ListModules( prmap_t *_maps; int _num; - + int module_count = 0; + /* * Scan each mapping - note it is assummed that the mappings are * presented in order. We fill holes between mappings. On intel @@ -313,15 +313,17 @@ int SolarisLwp::ListModules( memset(&module, 0, sizeof (module)); module.start_addr = _maps->pr_vaddr; module.size = _maps->pr_size; - if (name && (strcmp(name, "a.out") != 0)) + if ((strlen(name) > 0) && (strcmp(name, "a.out") != 0)) { strncpy(module.name, name, sizeof (module.name) - 1); + ++module_count; + } if (callback_param && (!callback_param->call_back(module, callback_param->context))) { break; } } - return num; + return module_count; } } // namespace google_breakpad diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h index b70b6aff..cb6811e0 100644 --- a/src/google_breakpad/common/minidump_format.h +++ b/src/google_breakpad/common/minidump_format.h @@ -215,6 +215,87 @@ typedef struct { #define MD_CONTEXT_CPU_MASK 0xffffffc0 +/* + * SPARC support, see (solaris)sys/procfs_isa.h also + */ + +#define MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT 32 + +typedef struct { + + /* FPU floating point regs */ + u_int64_t regs[MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT]; + + u_int64_t filler; + u_int64_t fsr; /* FPU status register */ +} MDFloatingSaveAreaSPARC; /* FLOATING_SAVE_AREA */ + +#define MD_CONTEXT_SPARC_GPR_COUNT 32 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + u_int32_t context_flags; + u_int32_t flag_pad; + /* + * General register access (SPARC). + * Don't confuse definitions here with definitions in <sys/regset.h>. + * Registers are 32 bits for ILP32, 64 bits for LP64. + * SPARC V7/V8 is for 32bit, SPARC V9 is for 64bit + */ + + /* 32 Integer working registers */ + + /* g_r[0-7] global registers(g0-g7) + * g_r[8-15] out registers(o0-o7) + * g_r[16-23] local registers(l0-l7) + * g_r[24-31] in registers(i0-i7) + */ + u_int64_t g_r[MD_CONTEXT_SPARC_GPR_COUNT]; + + /* several control registers */ + + /* Processor State register(PSR) for SPARC V7/V8 + * Condition Code register (CCR) for SPARC V9 + */ + u_int64_t ccr; + + u_int64_t pc; /* Program Counter register (PC) */ + u_int64_t npc; /* Next Program Counter register (nPC) */ + u_int64_t y; /* Y register (Y) */ + + /* Address Space Identifier register (ASI) for SPARC V9 + * WIM for SPARC V7/V8 + */ + u_int64_t asi; + + /* Floating-Point Registers State register (FPRS) for SPARC V9 + * TBR for for SPARC V7/V8 + */ + u_int64_t fprs; + + /* The next field is included with MD_CONTEXT_SPARC_FLOATING_POINT */ + MDFloatingSaveAreaSPARC float_save; + +} MDRawContextSPARC; /* CONTEXT_SPARC */ + +/* For (MDRawContextSPARC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_SPARC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_SPARC 0x10000000 +#define MD_CONTEXT_SPARC_CONTROL (MD_CONTEXT_SPARC | 0x00000001) +#define MD_CONTEXT_SPARC_INTEGER (MD_CONTEXT_SPARC | 0x00000002) +#define MD_CONTEXT_SAPARC_FLOATING_POINT (MD_CONTEXT_SPARC | 0x00000004) +#define MD_CONTEXT_SAPARC_EXTRA (MD_CONTEXT_SPARC | 0x00000008) + +#define MD_CONTEXT_SPARC_FULL (MD_CONTEXT_SPARC_CONTROL | \ + MD_CONTEXT_SPARC_INTEGER) + +#define MD_CONTEXT_SPARC_ALL (MD_CONTEXT_SPARC_FULL | \ + MD_CONTEXT_SAPARC_FLOATING_POINT | \ + MD_CONTEXT_SAPARC_EXTRA) /* * Breakpad minidump extension for PowerPC support. Based on Darwin/Mac OS X' @@ -929,6 +1010,54 @@ typedef enum { MD_EXCEPTION_CODE_LIN_SIGSYS = 31 /* Bad system call */ } MDExceptionCodeLinux; +/* For (MDException).exception_code. These values come from sys/iso/signal_iso.h + */ +typedef enum { + MD_EXCEPTION_CODE_SOL_SIGHUP = 1, /* Hangup */ + MD_EXCEPTION_CODE_SOL_SIGINT = 2, /* interrupt (rubout) */ + MD_EXCEPTION_CODE_SOL_SIGQUIT = 3, /* quit (ASCII FS) */ + MD_EXCEPTION_CODE_SOL_SIGILL = 4, /* illegal instruction (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGTRAP = 5, /* trace trap (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGIOT = 6, /* IOT instruction */ + MD_EXCEPTION_CODE_SOL_SIGABRT = 6, /* used by abort, replace SIGIOT in the future */ + MD_EXCEPTION_CODE_SOL_SIGEMT = 7, /* EMT instruction */ + MD_EXCEPTION_CODE_SOL_SIGFPE = 8, /* floating point exception */ + MD_EXCEPTION_CODE_SOL_SIGKILL = 9, /* kill (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGBUS = 10, /* bus error */ + MD_EXCEPTION_CODE_SOL_SIGSEGV = 11, /* segmentation violation */ + MD_EXCEPTION_CODE_SOL_SIGSYS = 12, /* bad argument to system call */ + MD_EXCEPTION_CODE_SOL_SIGPIPE = 13, /* write on a pipe with no one to read it */ + MD_EXCEPTION_CODE_SOL_SIGALRM = 14, /* alarm clock */ + MD_EXCEPTION_CODE_SOL_SIGTERM = 15, /* software termination signal from kill */ + MD_EXCEPTION_CODE_SOL_SIGUSR1 = 16, /* user defined signal 1 */ + MD_EXCEPTION_CODE_SOL_SIGUSR2 = 17, /* user defined signal 2 */ + MD_EXCEPTION_CODE_SOL_SIGCLD = 18, /* child status change */ + MD_EXCEPTION_CODE_SOL_SIGCHLD = 18, /* child status change alias (POSIX) */ + MD_EXCEPTION_CODE_SOL_SIGPWR = 19, /* power-fail restart */ + MD_EXCEPTION_CODE_SOL_SIGWINCH = 20, /* window size change */ + MD_EXCEPTION_CODE_SOL_SIGURG = 21, /* urgent socket condition */ + MD_EXCEPTION_CODE_SOL_SIGPOLL = 22, /* pollable event occured */ + MD_EXCEPTION_CODE_SOL_SIGIO = 22, /* socket I/O possible (SIGPOLL alias) */ + MD_EXCEPTION_CODE_SOL_SIGSTOP = 23, /* stop (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGTSTP = 24, /* user stop requested from tty */ + MD_EXCEPTION_CODE_SOL_SIGCONT = 25, /* stopped process has been continued */ + MD_EXCEPTION_CODE_SOL_SIGTTIN = 26, /* background tty read attempted */ + MD_EXCEPTION_CODE_SOL_SIGTTOU = 27, /* background tty write attempted */ + MD_EXCEPTION_CODE_SOL_SIGVTALRM = 28, /* virtual timer expired */ + MD_EXCEPTION_CODE_SOL_SIGPROF = 29, /* profiling timer expired */ + MD_EXCEPTION_CODE_SOL_SIGXCPU = 30, /* exceeded cpu limit */ + MD_EXCEPTION_CODE_SOL_SIGXFSZ = 31, /* exceeded file size limit */ + MD_EXCEPTION_CODE_SOL_SIGWAITING = 32, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGLWP = 33, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGFREEZE = 34, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGTHAW = 35, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGCANCEL = 36, /* reserved signal for thread cancellation */ + MD_EXCEPTION_CODE_SOL_SIGLOST = 37, /* resource lost (eg, record-lock lost) */ + MD_EXCEPTION_CODE_SOL_SIGXRES = 38, /* resource control exceeded */ + MD_EXCEPTION_CODE_SOL_SIGJVM1 = 39, /* reserved signal for Java Virtual Machine */ + MD_EXCEPTION_CODE_SOL_SIGJVM2 = 40 /* reserved signal for Java Virtual Machine */ +} MDExceptionCodeSolaris; + typedef struct { u_int32_t thread_id; /* Thread in which the exception * occurred. Corresponds to diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index f40f4c06..55b09533 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -177,7 +177,8 @@ class MinidumpContext : public MinidumpStream { // NULL. const MDRawContextX86* GetContextX86() const; const MDRawContextPPC* GetContextPPC() const; - + const MDRawContextSPARC* GetContextSPARC() const; + // Print a human-readable representation of the object to stdout. void Print(); @@ -204,6 +205,9 @@ class MinidumpContext : public MinidumpStream { MDRawContextBase* base; MDRawContextX86* x86; MDRawContextPPC* ppc; + // on Solaris SPARC, sparc is defined as a numeric constant, + // so variables can NOT be named as sparc + MDRawContextSPARC* ctx_sparc; } context_; }; diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h index 8af5ffee..567c1b7e 100644 --- a/src/google_breakpad/processor/stack_frame_cpu.h +++ b/src/google_breakpad/processor/stack_frame_cpu.h @@ -98,6 +98,30 @@ struct StackFramePPC : public StackFrame { int context_validity; }; +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_ALL = -1 + }; + + StackFrameSPARC() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextSPARC context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + } // namespace google_breakpad #endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 93d4eb05..f4817044 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -450,6 +450,62 @@ bool MinidumpContext::Read(u_int32_t expected_size) { break; } + case MD_CONTEXT_SPARC: { + if (expected_size != sizeof(MDRawContextSPARC)) { + BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << + expected_size << " != " << sizeof(MDRawContextSPARC); + return false; + } + + scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_sparc->context_flags = context_flags; + + size_t flags_size = sizeof(context_sparc->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast<u_int8_t*>(context_sparc.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextSPARC) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read sparc context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_sparc->context_flags was already swapped. + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; + ++gpr_index) { + Swap(&context_sparc->g_r[gpr_index]); + } + Swap(&context_sparc->ccr); + Swap(&context_sparc->pc); + Swap(&context_sparc->npc); + Swap(&context_sparc->y); + Swap(&context_sparc->asi); + Swap(&context_sparc->fprs); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; + ++fpr_index) { + Swap(&context_sparc->float_save.regs[fpr_index]); + } + Swap(&context_sparc->float_save.filler); + Swap(&context_sparc->float_save.fsr); + } + context_.ctx_sparc = context_sparc.release(); + + break; + } + default: { // Unknown context type BPLOG(ERROR) << "MinidumpContext unknown context type " << @@ -494,6 +550,14 @@ const MDRawContextPPC* MinidumpContext::GetContextPPC() const { return context_.ppc; } +const MDRawContextSPARC* MinidumpContext::GetContextSPARC() const { + if (GetContextCPU() != MD_CONTEXT_SPARC) { + BPLOG(ERROR) << "MinidumpContext cannot get sparc context"; + return NULL; + } + + return context_.ctx_sparc; +} void MinidumpContext::FreeContext() { switch (GetContextCPU()) { @@ -505,6 +569,10 @@ void MinidumpContext::FreeContext() { delete context_.ppc; break; + case MD_CONTEXT_SPARC: + delete context_.ctx_sparc; + break; + default: // There is no context record (valid_ is false) or there's a // context record for an unknown CPU (shouldn't happen, only known @@ -552,6 +620,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) { if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC) return_value = true; break; + + case MD_CONTEXT_SPARC: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) + return_value = true; + break; } BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << @@ -672,6 +745,37 @@ void MinidumpContext::Print() { break; } + case MD_CONTEXT_SPARC: { + const MDRawContextSPARC* context_sparc = GetContextSPARC(); + printf("MDRawContextSPARC\n"); + printf(" context_flags = 0x%x\n", + context_sparc->context_flags); + for (unsigned int g_r_index = 0; + g_r_index < MD_CONTEXT_SPARC_GPR_COUNT; + ++g_r_index) { + printf(" g_r[%2d] = 0x%llx\n", + g_r_index, context_sparc->g_r[g_r_index]); + } + printf(" ccr = 0x%llx\n", context_sparc->ccr); + printf(" pc = 0x%llx\n", context_sparc->pc); + printf(" npc = 0x%llx\n", context_sparc->npc); + printf(" y = 0x%llx\n", context_sparc->y); + printf(" asi = 0x%llx\n", context_sparc->asi); + printf(" fprs = 0x%llx\n", context_sparc->fprs); + + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; + ++fpr_index) { + printf(" float_save.regs[%2d] = 0x%llx\n", + fpr_index, context_sparc->float_save.regs[fpr_index]); + } + printf(" float_save.filler = 0x%llx\n", + context_sparc->float_save.filler); + printf(" float_save.fsr = 0x%llx\n", + context_sparc->float_save.fsr); + break; + } + default: { break; } @@ -1068,12 +1172,23 @@ bool MinidumpThreadList::Read(u_int32_t expected_size) { if (expected_size != sizeof(thread_count) + thread_count * sizeof(MDRawThread)) { - BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << - " != " << - sizeof(thread_count) + thread_count * sizeof(MDRawThread); - return false; + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(thread_count) + 4 + + thread_count * sizeof(MDRawThread)) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << + " != " << sizeof(thread_count) + + thread_count * sizeof(MDRawThread); + return false; + } } + if (thread_count > max_threads_) { BPLOG(ERROR) << "MinidumpThreadList count " << thread_count << " exceeds maximum " << max_threads_; @@ -1328,6 +1443,7 @@ string MinidumpModule::code_identifier() const { } case MD_OS_MAC_OS_X: + case MD_OS_SOLARIS: case MD_OS_LINUX: { // TODO(mmentovai): support uuid extension if present, otherwise fall // back to version (from LC_ID_DYLIB?), otherwise fall back to something @@ -1924,10 +2040,20 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) { if (expected_size != sizeof(module_count) + module_count * MD_MODULE_SIZE) { - BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << - " != " << - sizeof(module_count) + module_count * MD_MODULE_SIZE; - return false; + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(module_count) + 4 + + module_count * MD_MODULE_SIZE) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << + " != " << sizeof(module_count) + + module_count * MD_MODULE_SIZE; + return false; + } } if (module_count > max_modules_) { @@ -2155,9 +2281,20 @@ bool MinidumpMemoryList::Read(u_int32_t expected_size) { if (expected_size != sizeof(region_count) + region_count * sizeof(MDMemoryDescriptor)) { - BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << - " != " << region_count * sizeof(MDMemoryDescriptor); - return false; + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(region_count) + 4 + + region_count * sizeof(MDMemoryDescriptor)) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << + " != " << sizeof(region_count) + + region_count * sizeof(MDMemoryDescriptor); + return false; + } } if (region_count > max_regions_) { @@ -2526,6 +2663,10 @@ string MinidumpSystemInfo::GetOS() { os = "linux"; break; + case MD_OS_SOLARIS: + os = "solaris"; + break; + default: BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " << HexString(system_info_.platform_id); diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index e7f4b0e6..54e6b545 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -280,6 +280,11 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) { break; } + case MD_CPU_ARCHITECTURE_SPARC: { + info->cpu = "sparc"; + break; + } + default: { // Assign the numeric architecture ID into the CPU string. char cpu_string[7]; @@ -332,6 +337,11 @@ bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) { break; } + case MD_OS_SOLARIS: { + info->os = "Solaris"; + break; + } + default: { // Assign the numeric platform ID into the OS string. char os_string[11]; @@ -835,6 +845,135 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { 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; + } + default: { BPLOG(INFO) << "Unknown exception reason " << reason; break; diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 35f0aa27..547914db 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -66,6 +66,7 @@ using google_breakpad::scoped_ptr; using google_breakpad::SimpleSymbolSupplier; using google_breakpad::StackFrame; using google_breakpad::StackFramePPC; +using google_breakpad::StackFrameSPARC; using google_breakpad::StackFrameX86; // Separator character for machine readable output. @@ -164,6 +165,16 @@ static void PrintStack(const CallStack *stack, const string &cpu) { sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); + } else if (cpu == "sparc") { + const StackFrameSPARC *frame_sparc = + reinterpret_cast<const StackFrameSPARC*>(frame); + + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) + sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) + sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) + sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); } printf("\n"); diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 46d8384b..502f07d4 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -49,6 +49,7 @@ #include "processor/scoped_ptr.h" #include "processor/stack_frame_info.h" #include "processor/stackwalker_ppc.h" +#include "processor/stackwalker_sparc.h" #include "processor/stackwalker_x86.h" namespace google_breakpad { @@ -163,6 +164,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU( memory, modules, supplier, resolver); break; + + case MD_CONTEXT_SPARC: + cpu_stackwalker = new StackwalkerSPARC(system_info, + context->GetContextSPARC(), + memory, modules, supplier, + resolver); + break; } BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) << diff --git a/src/processor/stackwalker_selftest.cc b/src/processor/stackwalker_selftest.cc index 913bb0ab..247b37ce 100644 --- a/src/processor/stackwalker_selftest.cc +++ b/src/processor/stackwalker_selftest.cc @@ -49,7 +49,17 @@ // // Author: Mark Mentovai -#if defined(__GNUC__) && (defined(__i386__) || defined(__ppc__)) +#include "processor/logging.h" + +#if defined(__i386) && !defined(__i386__) +#define __i386__ +#endif +#if defined(__sparc) && !defined(__sparc__) +#define __sparc__ +#endif + +#if (defined(__SUNPRO_CC) || defined(__GNUC__)) && \ + (defined(__i386__) || defined(__ppc__) || defined(__sparc__)) #include <cstdio> @@ -61,7 +71,6 @@ #include "google_breakpad/processor/memory_region.h" #include "google_breakpad/processor/stack_frame.h" #include "google_breakpad/processor/stack_frame_cpu.h" -#include "processor/logging.h" #include "processor/scoped_ptr.h" using google_breakpad::BasicSourceLineResolver; @@ -71,6 +80,7 @@ using google_breakpad::scoped_ptr; using google_breakpad::StackFrame; using google_breakpad::StackFramePPC; using google_breakpad::StackFrameX86; +using google_breakpad::StackFrameSPARC; #if defined(__i386__) #include "processor/stackwalker_x86.h" @@ -78,7 +88,10 @@ using google_breakpad::StackwalkerX86; #elif defined(__ppc__) #include "processor/stackwalker_ppc.h" using google_breakpad::StackwalkerPPC; -#endif // __i386__ || __ppc__ +#elif defined(__sparc__) +#include "processor/stackwalker_sparc.h" +using google_breakpad::StackwalkerSPARC; +#endif // __i386__ || __ppc__ || __sparc__ #define RECURSION_DEPTH 100 @@ -117,6 +130,9 @@ class SelfMemoryRegion : public MemoryRegion { }; +#if defined(__GNUC__) + + #if defined(__i386__) // GetEBP returns the current value of the %ebp register. Because it's @@ -210,14 +226,87 @@ static u_int32_t GetPC() { } -#endif // __i386__ || __ppc__ +#elif defined(__sparc__) +// GetSP returns the current value of the %sp/%o6/%g_r[14] register, which +// by convention, is the stack pointer on sparc. Because it's implemented +// as a function, %sp itself contains GetSP's own stack pointer and not +// the caller's stack pointer. Dereference to obtain the caller's stack +// pointer, which the compiler-generated prolog stored on the stack. +// Because this function depends on the compiler-generated prolog, inlining +// is disabled. +static u_int32_t GetSP() __attribute__((noinline)); +static u_int32_t GetSP() { + u_int32_t sp; + __asm__ __volatile__( + "mov %%fp, %0" + : "=r" (sp) + ); + return sp; +} + +// GetFP returns the current value of the %fp register. Because it's +// implemented as a function, %fp itself contains GetFP's frame pointer +// and not the caller's frame pointer. Dereference %fp to obtain the +// caller's frame pointer, which the compiler-generated preamble stored +// on the stack (provided frame pointers are not being omitted.) Because +// this function depends on the compiler-generated preamble, inlining is +// disabled. +static u_int32_t GetFP() __attribute__((noinline)); +static u_int32_t GetFP() { + u_int32_t fp; + __asm__ __volatile__( + "ld [%%fp+56], %0" + : "=r" (fp) + ); + return fp; +} + +// GetPC returns the program counter identifying the next instruction to +// execute after GetPC returns. It obtains this information from the +// link register, where it was placed by the branch instruction that called +// GetPC. Because this function depends on the caller's use of a branch +// instruction, inlining is disabled. +static u_int32_t GetPC() __attribute__((noinline)); +static u_int32_t GetPC() { + u_int32_t pc; + __asm__ __volatile__( + "mov %%i7, %0" + : "=r" (pc) + ); + return pc + 8; +} + +#endif // __i386__ || __ppc__ || __sparc__ + +#elif defined(__SUNPRO_CC) + +#if defined(__i386__) +extern "C" { +extern u_int32_t GetEIP(); +extern u_int32_t GetEBP(); +extern u_int32_t GetESP(); +} +#elif defined(__sparc__) +extern "C" { +extern u_int32_t GetPC(); +extern u_int32_t GetFP(); +extern u_int32_t GetSP(); +} +#endif // __i386__ || __sparc__ + +#endif // __GNUC__ || __SUNPRO_CC + // CountCallerFrames returns the number of stack frames beneath the function // that called CountCallerFrames. Because this function's return value // is dependent on the size of the stack beneath it, inlining is disabled, // and any function that calls this should not be inlined either. +#if defined(__GNUC__) static unsigned int CountCallerFrames() __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +static unsigned int CountCallerFrames(); +#endif static unsigned int CountCallerFrames() { SelfMemoryRegion memory; BasicSourceLineResolver resolver; @@ -237,7 +326,15 @@ static unsigned int CountCallerFrames() { StackwalkerPPC stackwalker = StackwalkerPPC(NULL, &context, &memory, NULL, NULL, &resolver); -#endif // __i386__ || __ppc__ +#elif defined(__sparc__) + MDRawContextSPARC context = MDRawContextSPARC(); + context.pc = GetPC(); + context.g_r[14] = GetSP(); + context.g_r[30] = GetFP(); + + StackwalkerSPARC stackwalker = StackwalkerSPARC(NULL, &context, &memory, + NULL, NULL, &resolver); +#endif // __i386__ || __ppc__ || __sparc__ CallStack stack; stackwalker.Walk(&stack); @@ -257,7 +354,11 @@ static unsigned int CountCallerFrames() { #elif defined(__ppc__) StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame); printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]); -#endif // __i386__ || __ppc__ +#elif defined(__sparc__) + StackFrameSPARC *frame_sparc = reinterpret_cast<StackFrameSPARC*>(frame); + printf(" sp = 0x%08x fp = 0x%08x\n", + frame_sparc->context.g_r[14], frame_sparc->context.g_r[30]); +#endif // __i386__ || __ppc__ || __sparc__ } #endif // PRINT_STACKS @@ -273,8 +374,12 @@ static unsigned int CountCallerFrames() { // have been reached, Recursor stops checking and returns success. If the // frame count check fails at any depth, Recursor will stop and return false. // Because this calls CountCallerFrames, inlining is disabled. +#if defined(__GNUC__) static bool Recursor(unsigned int depth, unsigned int parent_callers) __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +static bool Recursor(unsigned int depth, unsigned int parent_callers); +#endif static bool Recursor(unsigned int depth, unsigned int parent_callers) { unsigned int callers = CountCallerFrames(); if (callers != parent_callers + 1) @@ -291,7 +396,11 @@ static bool Recursor(unsigned int depth, unsigned int parent_callers) { // Because this calls CountCallerFrames, inlining is disabled - but because // it's main (and nobody calls it other than the entry point), it wouldn't // be inlined anyway. +#if defined(__GNUC__) int main(int argc, char** argv) __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +int main(int argc, char** argv); +#endif int main(int argc, char** argv) { BPLOG_INIT(&argc, &argv); @@ -299,9 +408,8 @@ int main(int argc, char** argv) { } -#else // __GNUC__ && (__i386__ || __ppc__) -// Not gcc? We use gcc's __asm__. -// Not i386 or ppc? We can only test stacks we know how to walk. +#else +// Not i386 or ppc or sparc? We can only test stacks we know how to walk. int main(int argc, char **argv) { @@ -314,4 +422,4 @@ int main(int argc, char **argv) { } -#endif // __GNUC__ && (__i386__ || __ppc__) +#endif // (__GNUC__ || __SUNPRO_CC) && (__i386__ || __ppc__ || __sparc__) diff --git a/src/processor/stackwalker_selftest_sol.s b/src/processor/stackwalker_selftest_sol.s new file mode 100644 index 00000000..648b0499 --- /dev/null +++ b/src/processor/stackwalker_selftest_sol.s @@ -0,0 +1,111 @@ +/* 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. + */ + +/* stackwalker_selftest_sol.s + * On Solaris, the recommeded compiler is CC, so we can not use gcc inline + * asm, use this method instead. + * + * How to compile: as -P -L -D_ASM -D_STDC -K PIC -o \ + * src/processor/stackwalker_selftest_sol.o \ + * src/processor/stackwalker_selftest_sol.s + * + * Author: Michael Shang + */ + +#include <sys/asm_linkage.h> + +#if defined(__i386) + + +ENTRY(GetEBP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl 0x00000000(%ebp),%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + leave + ret +SET_SIZE(GetEBP) + +ENTRY(GetEIP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl 0x00000004(%ebp),%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + leave + ret +SET_SIZE(GetEIP) + +ENTRY(GetESP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl %ebp,%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + addl $0x00000008,%eax + leave + ret +SET_SIZE(GetESP) + + +#elif defined(__sparc) + + +ENTRY(GetPC) + save %sp, -120, %sp + mov %i7, %i4 + inccc 8, %i4 + mov %i4, %i0 + ret + restore +SET_SIZE(GetPC) + +ENTRY(GetSP) + save %sp, -120, %sp + mov %fp, %i4 + mov %i4, %i0 + ret + restore +SET_SIZE(GetSP) + +ENTRY(GetFP) + save %sp, -120, %sp + ld [%fp + 56], %g1 + mov %g1, %i0 + ret + restore +SET_SIZE(GetFP) + + +#endif // __i386 || __sparc diff --git a/src/processor/stackwalker_sparc.cc b/src/processor/stackwalker_sparc.cc new file mode 100644 index 00000000..b7b6f005 --- /dev/null +++ b/src/processor/stackwalker_sparc.cc @@ -0,0 +1,139 @@ +// 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. + +// stackwalker_sparc.cc: sparc-specific stackwalker. +// +// See stackwalker_sparc.h for documentation. +// +// Author: Michael Shang + + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" +#include "processor/stackwalker_sparc.h" + +namespace google_breakpad { + + +StackwalkerSPARC::StackwalkerSPARC(const SystemInfo *system_info, + const MDRawContextSPARC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context) { +} + + +StackFrame* StackwalkerSPARC::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameSPARC *frame = new StackFrameSPARC(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFrameSPARC::CONTEXT_VALID_ALL; + frame->instruction = frame->context.pc; + + return frame; +} + + +StackFrame* StackwalkerSPARC::GetCallerFrame( + const CallStack *stack, + const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + StackFrameSPARC *last_frame = static_cast<StackFrameSPARC*>( + stack->frames()->back()); + + // new: caller + // old: callee + // %fp, %i6 and g_r[30] is the same, see minidump_format.h + // %sp, %o6 and g_r[14] is the same, see minidump_format.h + // %sp_new = %fp_old + // %fp_new = *(%fp_old + 32 + 32 - 8), where the callee's %i6 + // %pc_new = *(%fp_old + 32 + 32 - 4) + 8 + // which is callee's %i7 plus 8 + + // A caller frame must reside higher in memory than its callee frames. + // Anything else is an error, or an indication that we've reached the + // end of the stack. + u_int32_t stack_pointer = last_frame->context.g_r[30]; + if (stack_pointer <= last_frame->context.g_r[14]) { + return NULL; + } + + u_int32_t instruction; + if (!memory_->GetMemoryAtAddress(stack_pointer + 60, + &instruction) || instruction <= 1) { + return NULL; + } + + u_int32_t stack_base; + if (!memory_->GetMemoryAtAddress(stack_pointer + 56, + &stack_base) || stack_base <= 1) { + return NULL; + } + + StackFrameSPARC *frame = new StackFrameSPARC(); + + frame->context = last_frame->context; + frame->context.g_r[14] = stack_pointer; + frame->context.g_r[30] = stack_base; + + // frame->context.pc is the return address, which is 2 instruction + // past the branch that caused us to arrive at the callee, which are + // a CALL instruction then a NOP instruction. + // frame_ppc->instruction to 8 less than that. Since all sparc + // instructions are 4 bytes wide, this is the address of the branch + // instruction. This allows source line information to match up with the + // line that contains a function call. Callers that require the exact + // return address value may access the %i7/g_r[31] field of StackFrameSPARC. + frame->context.pc = instruction + 8; + frame->instruction = instruction; + frame->context_validity = StackFrameSPARC::CONTEXT_VALID_PC | + StackFrameSPARC::CONTEXT_VALID_SP | + StackFrameSPARC::CONTEXT_VALID_FP; + + return frame; +} + + +} // namespace google_breakpad diff --git a/src/processor/stackwalker_sparc.h b/src/processor/stackwalker_sparc.h new file mode 100644 index 00000000..f051e5bb --- /dev/null +++ b/src/processor/stackwalker_sparc.h @@ -0,0 +1,86 @@ +// 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. + +// stackwalker_sparc.h: sparc-specific stackwalker. +// +// Provides stack frames given sparc register context and a memory region +// corresponding to an sparc stack. +// +// Author: Michael Shang + + +#ifndef PROCESSOR_STACKWALKER_SPARC_H__ +#define PROCESSOR_STACKWALKER_SPARC_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerSPARC : public Stackwalker { + public: + // context is a sparc context object that gives access to sparc-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerSPARC(const SystemInfo *system_info, + const MDRawContextSPARC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and + // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or + // alternate conventions as guided by stack_frame_info_). + // Implementation of Stackwalker, using ppc context (stack pointer in %r1, + // saved program counter in %srr0) and stack conventions (saved stack + // pointer at 0(%r1), return address at 8(0(%r1)). + // Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and + // stack conventions (saved %sp at) + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame( + const CallStack *stack, + const vector< linked_ptr<StackFrameInfo> > &stack_frame_info); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextSPARC *context_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_SPARC_H__ |