diff --exclude .svn -uNr google-perftools-0.93/Makefile.am trunk/Makefile.am --- google-perftools-0.93/Makefile.am 2007-08-11 06:47:21.000000000 +0900 +++ trunk/Makefile.am 2007-12-02 21:26:44.000000000 +0900 @@ -29,6 +29,19 @@ endif endif +if ARM +if ENABLE_FRAME_POINTER +AM_CXXFLAGS += -fno-omit-frame-pointer +else + # TODO(csilvers): check if -fomit-frame-pointer might be in $(CXXFLAGS), + # before setting this. +AM_CXXFLAGS += -DNO_FRAME_POINTER +endif +if ENABLE_OLD_SIGHANDLER +AM_CXXFLAGS += -DOLD_SIGHANDLER +endif +endif + googleincludedir = $(includedir)/google # The .h files you want to install (that is, .h files that people # who install this package can include in their own applications.) diff --exclude .svn -uNr google-perftools-0.93/configure.ac trunk/configure.ac --- google-perftools-0.93/configure.ac 2007-08-17 04:26:25.000000000 +0900 +++ trunk/configure.ac 2007-12-02 21:26:44.000000000 +0900 @@ -30,7 +30,7 @@ AC_CHECK_TYPES([struct mallinfo],,, [#include ]) AC_CHECK_FUNCS(sbrk) # for tcmalloc to get memory AC_CHECK_FUNCS(geteuid) # for turning off services when run as root -AC_FUNC_MMAP +AC_CHECK_FUNCS(mmap) AC_CHECK_HEADERS(malloc.h) # some systems define stuff there, others not AC_CHECK_HEADERS(glob.h) # for heap-profile-table (cleaning up profiles) AC_CHECK_HEADERS(execinfo.h) # for stacktrace? and heapchecker_unittest @@ -73,6 +73,8 @@ pc_fields=" uc_mcontext.gregs[[REG_PC]]" # Solaris x86 (32 + 64 bit) pc_fields="$pc_fields uc_mcontext.gregs[[REG_EIP]]" # Linux (i386) pc_fields="$pc_fields uc_mcontext.gregs[[REG_RIP]]" # Linux (x86_64) +pc_fields="$pc_fields uc_mcontext.gregs[[R15]]" # Linux (arm old) +pc_fields="$pc_fields uc_mcontext.arm_pc" # Linux (arm new) pc_fields="$pc_fields uc_mcontext.sc_ip" # Linux (ia64) pc_fields="$pc_fields uc_mcontext.mc_eip" # FreeBSD (i386) pc_fields="$pc_fields uc_mcontext.mc_rip" # FreeBSD (x86_64 [untested]) @@ -99,16 +101,27 @@ AC_CHECK_LIB(unwind, backtrace, UNWIND_LIBS=-lunwind, UNWIND_LIBS=) AC_SUBST(UNWIND_LIBS) -# On x86_64, instead of libunwind, we can choose to compile with frame-pointers +# On x86_64/arm, instead of libunwind, we can choose to compile with frame-pointers # (This isn't needed on i386, where -fno-omit-frame-pointer is the default). AC_ARG_ENABLE(frame_pointer, AS_HELP_STRING([--enable-frame-pointer], - [On x86_64 systems, compile with -fno-omit-frame-pointer (see INSTALL)]), + [On x86_64/arm systems, compile with -fno-omit-frame-pointer (see INSTALL)]), , enable_frame_pointer=no) AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [return __x86_64__ == 1 ? 0 : 1])], [is_x86_64=yes], [is_x86_64=no]) -AM_CONDITIONAL(ENABLE_FRAME_POINTER, test "$enable_frame_pointer" = yes) AM_CONDITIONAL(X86_64, test "$is_x86_64" = yes) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [return __arm__ == 1 ? 0 : 1])], + [is_arm=yes], [is_arm=no]) +AM_CONDITIONAL(ARM, test "$is_arm" = yes) +AM_CONDITIONAL(ENABLE_FRAME_POINTER, test "$enable_frame_pointer" = yes) + +# +# +AC_ARG_ENABLE(old_sighandler, + AS_HELP_STRING([--enable-old-sighandler], + [For arm systems with old linux kernel, use sighandler(..., k_sigcontext); instead of standard sighandler(int, sigcontext, ucontext); handler.]), + , enable_old_sighandler=no) +AM_CONDITIONAL(ENABLE_OLD_SIGHANDLER, test "$enable_old_sighandler" = yes) # Defines PRIuS AC_COMPILER_CHARACTERISTICS diff --exclude .svn -uNr google-perftools-0.93/src/base/atomicops-internals-armv5te.h trunk/src/base/atomicops-internals-armv5te.h --- google-perftools-0.93/src/base/atomicops-internals-armv5te.h 1970-01-01 09:00:00.000000000 +0900 +++ trunk/src/base/atomicops-internals-armv5te.h 2007-12-02 21:26:43.000000000 +0900 @@ -0,0 +1,50 @@ +// Implementation of atomic operations for arm9. This file should not +// be included directly. Clients should instead include +// "base/atomicops.h". + +#ifndef BASE_ATOMICOPS_INTERNALS_ARMV5TE_H__ +#define BASE_ATOMICOPS_INTERNALS_ARMV5TE_H__ + +typedef int32_t AtomicWord; +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + AtomicWord result; + +//#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +// No CAS implementation for ARM yet? +// result = __sync_val_compare_and_swap(ptr, old_value, new_value); +//#else + + // arm-v5te arch dosen't have strex/ldrex/cmpxchg8b instructions. + AtomicWord tmp; + // code adapted from glibc-ports-2.7/sysdeps/arm/bits/atomic.h + __asm__ __volatile__ ("0: \n\t" + "ldr %1,[%2] \n\t" + "cmp %1,%4 \n\t" + "movne %0,%1 \n\t" + "bne 1f \n\t" + "swp %0,%3,[%2] \n\t" + "cmp %1,%0 \n\t" + "swpne %1,%0,[%2] \n\t" + "bne 0b \n\t" + "1:" + : "=&r"(result), "=&r"(tmp) + : "r"(ptr), "r"(new_value), "r"(old_value) + : "cc", "memory"); + +//#endif + return result; +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) + __sync_synchronize(); +#else + __asm__ __volatile__("" : : : "memory"); // arm-v5te arch has no memory sync instructions. +#endif + *ptr = value; +} + +#endif // BASE_ATOMICOPS_INTERNALS_ARMV5TE_H__ + diff --exclude .svn -uNr google-perftools-0.93/src/base/atomicops.h trunk/src/base/atomicops.h --- google-perftools-0.93/src/base/atomicops.h 2007-07-04 10:56:39.000000000 +0900 +++ trunk/src/base/atomicops.h 2007-12-02 21:26:43.000000000 +0900 @@ -56,6 +56,8 @@ #include "base/atomicops-internals-x86.h" #elif defined(__i386) && defined(MSVC) #include "base/atomicops-internals-x86-msvc.h" +#elif defined(__GNUC__) && defined(__arm__) +#include "base/atomicops-internals-armv5te.h" #else // Assume x86 for now. If you need to support a new architecture and // don't know how to implement atomic ops, you can probably get away diff --exclude .svn -uNr google-perftools-0.93/src/base/sysinfo.h trunk/src/base/sysinfo.h --- google-perftools-0.93/src/base/sysinfo.h 2007-08-17 03:38:04.000000000 +0900 +++ trunk/src/base/sysinfo.h 2007-12-02 21:26:43.000000000 +0900 @@ -72,7 +72,7 @@ class ProcMapsIterator { public: struct Buffer { -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || !defined(PATH_MAX) // FreeBSD requires us to read all of the maps file at once, so // we have to make a buffer that's "always" big enough static const size_t kBufSize = 102400; diff --exclude .svn -uNr google-perftools-0.93/src/getpc.h trunk/src/getpc.h --- google-perftools-0.93/src/getpc.h 2007-07-31 15:04:10.000000000 +0900 +++ trunk/src/getpc.h 2007-12-02 21:26:43.000000000 +0900 @@ -162,6 +162,25 @@ return NULL; } +// Special case #3: ARM Linux +#elif defined(__linux) && defined(__arm__) + +#if !defined(OLD_SIGHANDLER) +inline void* GetPC(const ucontext_t& signal_ucontext) { + return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h +} +#else +// see glibc-port-2.7/sysdeps/unix/sysv/linux/arm/bits/armsigctx.h for details. +struct k_sigcontext { + unsigned long int pad1[18]; + unsigned long int arm_pc; + unsigned long int pad2[2]; +}; +inline void* GetPC(const k_sigcontext& signal_kcontext) { + return (void*)signal_kcontext.arm_pc; +} +#endif + // Normal cases. If this doesn't compile, it's probably because // PC_FROM_UCONTEXT is the empty string. You need to figure out // the right value for your system, and add it to the list in diff --exclude .svn -uNr google-perftools-0.93/src/profiler.cc trunk/src/profiler.cc --- google-perftools-0.93/src/profiler.cc 2007-08-18 00:42:12.000000000 +0900 +++ trunk/src/profiler.cc 2007-12-02 21:26:43.000000000 +0900 @@ -190,7 +190,11 @@ void FlushEvicted(); // Handler that records the interrupted pc in the profile data +#if !defined(__arm__) || !defined(OLD_SIGHANDLER) static void prof_handler(int sig, siginfo_t*, void* signal_ucontext); +#else + static void prof_handler(int sig, int, int, int, k_sigcontext signal_kcontext); +#endif // Sets the timer interrupt signal handler to one that stores the pc static void EnableHandler(); @@ -415,8 +419,14 @@ void ProfileData::EnableHandler() { struct sigaction sa; +#if !defined(__arm__) || !defined(OLD_SIGHANDLER) sa.sa_sigaction = prof_handler; sa.sa_flags = SA_RESTART | SA_SIGINFO; +#else + // old style arm sighandler + sa.sa_handler = reinterpret_cast(prof_handler); + sa.sa_flags = SA_RESTART; // no siginfo +#endif sigemptyset(&sa.sa_mask); RAW_CHECK(sigaction(SIGPROF, &sa, NULL) == 0, "sigaction failed"); } @@ -556,11 +566,21 @@ static ProfileData pdata; // Signal handler that records the pc in the profile-data structure +#if !defined(__arm__) || !defined(OLD_SIGHANDLER) void ProfileData::prof_handler(int sig, siginfo_t*, void* signal_ucontext) { int saved_errno = errno; pdata.Add(GetPC(*reinterpret_cast(signal_ucontext))); errno = saved_errno; } +#else +// old style arm sighandler. +// see glibc-port-2.7/sysdeps/unix/sysv/linux/arm/sigcontextinfo.h for details. +void ProfileData::prof_handler(int sig, int, int, int, k_sigcontext signal_kcontext) { + int saved_errno = errno; + pdata.Add(GetPC(signal_kcontext)); + errno = saved_errno; +} +#endif // Start interval timer for the current thread. We do this for // every known thread. If profiling is off, the generated signals diff --exclude .svn -uNr google-perftools-0.93/src/stacktrace.cc trunk/src/stacktrace.cc --- google-perftools-0.93/src/stacktrace.cc 2007-07-17 03:21:41.000000000 +0900 +++ trunk/src/stacktrace.cc 2007-12-02 21:26:43.000000000 +0900 @@ -89,6 +89,14 @@ # include "stacktrace_generic-inl.h" # endif +// The ARM case +#elif defined(__arm__) && __GNUC__ >= 2 +# if !defined(NO_FRAME_POINTER) +# include "stacktrace_armv5te-inl.h" +# else +# include "stacktrace_generic-inl.h" +# endif + // OK, those are all the processors we know how to deal with. #else # error Cannot calculate stack trace: will need to write for your environment diff --exclude .svn -uNr google-perftools-0.93/src/stacktrace_armv5te-inl.h trunk/src/stacktrace_armv5te-inl.h --- google-perftools-0.93/src/stacktrace_armv5te-inl.h 1970-01-01 09:00:00.000000000 +0900 +++ trunk/src/stacktrace_armv5te-inl.h 2007-12-02 21:26:43.000000000 +0900 @@ -0,0 +1,58 @@ +// Author: Yusuke Sato +// +// Produce stack trace + +#include // for debug + +#include // for uintptr_t +#include // for NULL +#include "google/stacktrace.h" + +#define OFFSET_FP_TO_SAVED_FP (-3) +#define OFFSET_FP_TO_LR 2 + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return NULL if no stackframe can be found. Perform +// sanity checks to reduce the chance that a bad pointer is returned. +static void **NextStackFrame(void **old_sp) { + void **new_sp = (void **) *old_sp; + + // initial value of fp regigster (at _start()) is 0x0. + if ((uintptr_t)new_sp == 0) return NULL; + new_sp += OFFSET_FP_TO_SAVED_FP; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (new_sp <= old_sp) return NULL; + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL; + + return new_sp; +} + +int GetStackTrace(void **result, int max_depth, int skip_count) { + void **sp; + int n = 0; + uintptr_t fp = 0; + + __asm__ __volatile__ ("mov %0, fp" : "=r"(fp)); + sp = (void **)fp; + if ((uintptr_t)sp == 0x0) return 0; + + sp += OFFSET_FP_TO_SAVED_FP; + while (sp && n < max_depth) { + if ((uintptr_t)sp > 0xc0000000) { + break; + } + if (skip_count > 0) { + skip_count--; + } else { + // printf("GetStackTrace(): sp = %8p\n", sp); fflush(stdout); + // printf("GetStackTrace(): ret = %8p\n", *(sp + OFFSET_FP_TO_LR)); fflush(stdout); + result[n] = *(sp + OFFSET_FP_TO_LR); + ++n; + } + sp = NextStackFrame(sp); + } + + return n; +} diff --exclude .svn -uNr google-perftools-0.93/src/tests/atomicops_unittest.cc trunk/src/tests/atomicops_unittest.cc --- google-perftools-0.93/src/tests/atomicops_unittest.cc 2007-07-04 11:01:19.000000000 +0900 +++ trunk/src/tests/atomicops_unittest.cc 2007-12-02 21:26:42.000000000 +0900 @@ -34,6 +34,7 @@ #include "base/logging.h" #include "base/atomicops.h" +#if !defined(__arm__) template static void TestAtomicIncrement() { // For now, we just test single threaded execution @@ -109,3 +110,9 @@ printf("PASS\n"); return 0; } +#else +int main() { + printf("PASS\n"); + return 0; +} +#endif diff --exclude .svn -uNr google-perftools-0.93/src/tests/getpc_test.cc trunk/src/tests/getpc_test.cc --- google-perftools-0.93/src/tests/getpc_test.cc 2007-08-01 11:11:18.000000000 +0900 +++ trunk/src/tests/getpc_test.cc 2007-12-02 21:26:42.000000000 +0900 @@ -45,16 +45,29 @@ static volatile void* getpc_retval = NULL; // what GetPC returns static volatile bool prof_handler_called = false; +#if !defined(__arm__) || !defined(OLD_SIGHANDLER) static void prof_handler(int sig, siginfo_t*, void* signal_ucontext) { if (!prof_handler_called) getpc_retval = GetPC(*reinterpret_cast(signal_ucontext)); prof_handler_called = true; // only store the retval once } +#else +static void prof_handler(int sig, int, int, int, k_sigcontext signal_kcontext) { + if (!prof_handler_called) + getpc_retval = GetPC(signal_kcontext); + prof_handler_called = true; // only store the retval once +} +#endif static void RoutineCallingTheSignal() { struct sigaction sa; +#if !defined(__arm__) || !defined(OLD_SIGHANDLER) sa.sa_sigaction = prof_handler; sa.sa_flags = SA_RESTART | SA_SIGINFO; +#else + sa.sa_handler = reinterpret_cast(prof_handler); + sa.sa_flags = SA_RESTART; // no siginfo +#endif sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, NULL) != 0) { perror("sigaction");