aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authormmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2007-09-26 18:28:05 +0000
committermmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2007-09-26 18:28:05 +0000
commitea2bba970675e01f9964f82d3f44960c1aad05dc (patch)
treed3d6744bf2ef136f9a89748c96ab4e5c465eb7ff /src/processor
parentFix crash reason switching to allow proper behavior for Linux. Spotted by (diff)
downloadbreakpad-ea2bba970675e01f9964f82d3f44960c1aad05dc.tar.xz
Add SPARC/Solaris support to client handler and processor (#201, 200).
Patch by Michael shang <satisfy123>. r=me, r=Alfred Peng. http://groups.google.com/group/google-breakpad-discuss/browse_thread/thread/2fba07577f1fa35e git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@215 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/minidump.cc163
-rw-r--r--src/processor/minidump_processor.cc139
-rw-r--r--src/processor/minidump_stackwalk.cc11
-rw-r--r--src/processor/stackwalker.cc8
-rw-r--r--src/processor/stackwalker_selftest.cc128
-rw-r--r--src/processor/stackwalker_selftest_sol.s111
-rw-r--r--src/processor/stackwalker_sparc.cc139
-rw-r--r--src/processor/stackwalker_sparc.h86
8 files changed, 764 insertions, 21 deletions
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__