summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralkondratenko@gmail.com <alkondratenko@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2013-07-26 18:04:03 +0000
committeralkondratenko@gmail.com <alkondratenko@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2013-07-26 18:04:03 +0000
commitf45133e75c09ca7d5e86bda2db16e30c6fa348c0 (patch)
tree0cbf4821329359104eb1f4f610b937b289744701
parentee2bf097133b115e3da249e43507e02645e46e59 (diff)
downloadgperftools-f45133e75c09ca7d5e86bda2db16e30c6fa348c0.tar.gz
issue-553: Fix syscall wrapper for PowerPC
This is patch by Adhemerval Zanella. * src/stacktrace_powerpc-inl.h: It is just a cleanup for the stacktrace functions for PowerPC. The idea is to simplify the code. * src/tests/heap-checker_unittest.cc: Handles the PPC64 function descriptor correctly in malloc tracers. Different from other architecture, for PPC64 the address returned in function pointers are the ODP entry, not the symbol address in .text segment. This leads the comparison bogus, since it will compare a ODP entry with a .text address. * src/heap-checker.cc: Add support for PPC in ptrace. * src/base/elfcore.h: Likewise. * src/base/linuxthreads.cc: Fix the thread creation using the clone wrapper. * src/base/linux_syscall_support.h: Various fixes for PPC32 and PPC64: fixes the kernel_stat[64] struct layout, and sys_clone and sys_socket implementation. git-svn-id: http://gperftools.googlecode.com/svn/trunk@227 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rw-r--r--src/base/elfcore.h19
-rw-r--r--src/base/linux_syscall_support.h199
-rw-r--r--src/base/linuxthreads.cc10
-rw-r--r--src/heap-checker.cc14
-rw-r--r--src/stacktrace_powerpc-inl.h139
-rw-r--r--src/tests/heap-checker_unittest.cc25
6 files changed, 234 insertions, 172 deletions
diff --git a/src/base/elfcore.h b/src/base/elfcore.h
index 34a96de..122fbb1 100644
--- a/src/base/elfcore.h
+++ b/src/base/elfcore.h
@@ -37,11 +37,11 @@
extern "C" {
#endif
-/* We currently only support x86-32, x86-64, ARM, and MIPS on Linux.
+/* We currently only support x86-32, x86-64, ARM, MIPS, PPC on Linux.
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
- defined(__mips__)) && defined(__linux)
+ defined(__mips__) || defined(__PPC__)) && defined(__linux)
#include <stdarg.h>
#include <stdint.h>
@@ -108,6 +108,21 @@ extern "C" {
unsigned long cp0_cause;
unsigned long unused;
} mips_regs;
+#elif defined (__PPC__)
+ typedef struct ppc_regs {
+ #define SP uregs[1] /* Stack pointer */
+ #define IP rip /* Program counter */
+ #define LR lr /* Link register */
+ unsigned long uregs[32]; /* General Purpose Registers - r0-r31. */
+ double fpr[32]; /* Floating-Point Registers - f0-f31. */
+ unsigned long rip; /* Program counter. */
+ unsigned long msr;
+ unsigned long ccr;
+ unsigned long lr;
+ unsigned long ctr;
+ unsigned long xeq;
+ unsigned long mq;
+ } ppc_regs;
#endif
#if defined(__i386__) && defined(__GNUC__)
diff --git a/src/base/linux_syscall_support.h b/src/base/linux_syscall_support.h
index d768471..b3fc8a3 100644
--- a/src/base/linux_syscall_support.h
+++ b/src/base/linux_syscall_support.h
@@ -130,7 +130,7 @@
#ifndef SYS_LINUX_SYSCALL_SUPPORT_H
#define SYS_LINUX_SYSCALL_SUPPORT_H
-/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux.
+/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC/PPC64 on Linux.
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
@@ -333,23 +333,21 @@ struct kernel_stat64 {
struct kernel_stat64 {
unsigned long long st_dev;
unsigned long long st_ino;
- unsigned st_mode;
unsigned st_nlink;
+ unsigned st_mode;
unsigned st_uid;
unsigned st_gid;
+ int __pad2;
unsigned long long st_rdev;
- unsigned short int __pad2;
long long st_size;
- long st_blksize;
+ long long st_blksize;
long long st_blocks;
- long st_atime_;
- unsigned long st_atime_nsec_;
- long st_mtime_;
- unsigned long st_mtime_nsec_;
- long st_ctime_;
- unsigned long st_ctime_nsec_;
+ kernel_timespec st_atim;
+ kernel_timespec st_mtim;
+ kernel_timespec st_ctim;
unsigned long __unused4;
unsigned long __unused5;
+ unsigned long __unused6;
};
#else
struct kernel_stat64 {
@@ -427,24 +425,23 @@ struct kernel_stat {
};
#elif defined(__PPC__)
struct kernel_stat {
- unsigned st_dev;
- unsigned long st_ino; // ino_t
- unsigned long st_mode; // mode_t
- unsigned short st_nlink; // nlink_t
- unsigned st_uid; // uid_t
- unsigned st_gid; // gid_t
- unsigned st_rdev;
- long st_size; // off_t
+ unsigned long long st_dev;
+ unsigned long st_ino;
+ unsigned long st_nlink;
+ unsigned long st_mode;
+ unsigned st_uid;
+ unsigned st_gid;
+ int __pad2;
+ unsigned long long st_rdev;
+ long st_size;
unsigned long st_blksize;
unsigned long st_blocks;
- unsigned long st_atime_;
- unsigned long st_atime_nsec_;
- unsigned long st_mtime_;
- unsigned long st_mtime_nsec_;
- unsigned long st_ctime_;
- unsigned long st_ctime_nsec_;
+ kernel_timespec st_atim;
+ kernel_timespec st_mtim;
+ kernel_timespec st_ctim;
unsigned long __unused4;
unsigned long __unused5;
+ unsigned long __unused6;
};
#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
struct kernel_stat {
@@ -659,6 +656,9 @@ struct kernel_stat {
#ifndef __NR_fstat64
#define __NR_fstat64 197
#endif
+#ifndef __NR_socket
+#define __NR_socket 198
+#endif
#ifndef __NR_getdents64
#define __NR_getdents64 202
#endif
@@ -1710,13 +1710,13 @@ struct kernel_stat {
#define LSS_BODY(nr, type, name, args...) \
long __sc_ret, __sc_err; \
{ \
- register unsigned long __sc_0 __asm__ ("r0"); \
- register unsigned long __sc_3 __asm__ ("r3"); \
- register unsigned long __sc_4 __asm__ ("r4"); \
- register unsigned long __sc_5 __asm__ ("r5"); \
- register unsigned long __sc_6 __asm__ ("r6"); \
- register unsigned long __sc_7 __asm__ ("r7"); \
- register unsigned long __sc_8 __asm__ ("r8"); \
+ register unsigned long __sc_0 __asm__ ("r0"); \
+ register unsigned long __sc_3 __asm__ ("r3"); \
+ register unsigned long __sc_4 __asm__ ("r4"); \
+ register unsigned long __sc_5 __asm__ ("r5"); \
+ register unsigned long __sc_6 __asm__ ("r6"); \
+ register unsigned long __sc_7 __asm__ ("r7"); \
+ register unsigned long __sc_8 __asm__ ("r8"); \
\
LSS_LOADARGS_##nr(name, args); \
__asm__ __volatile__ \
@@ -1773,15 +1773,82 @@ struct kernel_stat {
type5 arg5, type6 arg6) { \
LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \
}
- /* clone function adapted from glibc 2.3.6 clone.S */
- /* TODO(csilvers): consider wrapping some args up in a struct, like we
- * do for i386's _syscall6, so we can compile successfully on gcc 2.95
- */
+ /* clone function adapted from glibc 2.18 clone.S */
LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
int flags, void *arg, int *parent_tidptr,
void *newtls, int *child_tidptr) {
long __ret, __err;
{
+#if defined(__PPC64__)
+ register int (*__fn)(void *) __asm__ ("r3") = fn;
+ register void *__cstack __asm__ ("r4") = child_stack;
+ register int __flags __asm__ ("r5") = flags;
+ register void * __arg __asm__ ("r6") = arg;
+ register int * __ptidptr __asm__ ("r7") = parent_tidptr;
+ register void * __newtls __asm__ ("r8") = newtls;
+ register int * __ctidptr __asm__ ("r9") = child_tidptr;
+ __asm__ __volatile__(
+ /* check for fn == NULL
+ * and child_stack == NULL
+ */
+ "cmpdi cr0, %6, 0\n\t"
+ "cmpdi cr1, %7, 0\n\t"
+ "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t"
+ "beq- cr0, 1f\n\t"
+
+ /* set up stack frame for child */
+ "clrrdi %7, %7, 4\n\t"
+ "li 0, 0\n\t"
+ "stdu 0, -112(%7)\n\t"
+
+ /* fn, arg, child_stack are saved acrVoss the syscall */
+ "mr 28, %6\n\t"
+ "mr 29, %7\n\t"
+ "mr 27, %9\n\t"
+
+ /* syscall
+ r3 == flags
+ r4 == child_stack
+ r5 == parent_tidptr
+ r6 == newtls
+ r7 == child_tidptr */
+ "mr 3, %8\n\t"
+ "mr 5, %10\n\t"
+ "mr 6, %11\n\t"
+ "mr 7, %12\n\t"
+ "li 0, %4\n\t"
+ "sc\n\t"
+
+ /* Test if syscall was successful */
+ "cmpdi cr1, 3, 0\n\t"
+ "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
+ "bne- cr1, 1f\n\t"
+
+ /* Do the function call */
+ "std 2, 40(1)\n\t"
+ "ld 0, 0(28)\n\t"
+ "ld 2, 8(28)\n\t"
+ "mtctr 0\n\t"
+ "mr 3, 27\n\t"
+ "bctrl\n\t"
+ "ld 2, 40(1)\n\t"
+
+ /* Call _exit(r3) */
+ "li 0, %5\n\t"
+ "sc\n\t"
+
+ /* Return to parent */
+ "1:\n\t"
+ "mr %0, 3\n\t"
+ : "=r" (__ret), "=r" (__err)
+ : "0" (-1), "i" (EINVAL),
+ "i" (__NR_clone), "i" (__NR_exit),
+ "r" (__fn), "r" (__cstack), "r" (__flags),
+ "r" (__arg), "r" (__ptidptr), "r" (__newtls),
+ "r" (__ctidptr)
+ : "cr0", "cr1", "memory", "ctr",
+ "r0", "r29", "r27", "r28");
+#else
register int (*__fn)(void *) __asm__ ("r8") = fn;
register void *__cstack __asm__ ("r4") = child_stack;
register int __flags __asm__ ("r3") = flags;
@@ -1844,6 +1911,8 @@ struct kernel_stat {
"r" (__ctidptr)
: "cr0", "cr1", "memory", "ctr",
"r0", "r29", "r27", "r28");
+
+#endif
}
LSS_RETURN(int, __ret, __err);
}
@@ -2110,66 +2179,8 @@ struct kernel_stat {
return rc;
}
#endif
- #if defined(__PPC__)
- #undef LSS_SC_LOADARGS_0
- #define LSS_SC_LOADARGS_0(dummy...)
- #undef LSS_SC_LOADARGS_1
- #define LSS_SC_LOADARGS_1(arg1) \
- __sc_4 = (unsigned long) (arg1)
- #undef LSS_SC_LOADARGS_2
- #define LSS_SC_LOADARGS_2(arg1, arg2) \
- LSS_SC_LOADARGS_1(arg1); \
- __sc_5 = (unsigned long) (arg2)
- #undef LSS_SC_LOADARGS_3
- #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \
- LSS_SC_LOADARGS_2(arg1, arg2); \
- __sc_6 = (unsigned long) (arg3)
- #undef LSS_SC_LOADARGS_4
- #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \
- LSS_SC_LOADARGS_3(arg1, arg2, arg3); \
- __sc_7 = (unsigned long) (arg4)
- #undef LSS_SC_LOADARGS_5
- #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \
- LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \
- __sc_8 = (unsigned long) (arg5)
- #undef LSS_SC_BODY
- #define LSS_SC_BODY(nr, type, opt, args...) \
- long __sc_ret, __sc_err; \
- { \
- register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \
- register unsigned long __sc_3 __asm__ ("r3") = opt; \
- register unsigned long __sc_4 __asm__ ("r4"); \
- register unsigned long __sc_5 __asm__ ("r5"); \
- register unsigned long __sc_6 __asm__ ("r6"); \
- register unsigned long __sc_7 __asm__ ("r7"); \
- register unsigned long __sc_8 __asm__ ("r8"); \
- LSS_SC_LOADARGS_##nr(args); \
- __asm__ __volatile__ \
- ("stwu 1, -48(1)\n\t" \
- "stw 4, 20(1)\n\t" \
- "stw 5, 24(1)\n\t" \
- "stw 6, 28(1)\n\t" \
- "stw 7, 32(1)\n\t" \
- "stw 8, 36(1)\n\t" \
- "addi 4, 1, 20\n\t" \
- "sc\n\t" \
- "mfcr %0" \
- : "=&r" (__sc_0), \
- "=&r" (__sc_3), "=&r" (__sc_4), \
- "=&r" (__sc_5), "=&r" (__sc_6), \
- "=&r" (__sc_7), "=&r" (__sc_8) \
- : LSS_ASMINPUT_##nr \
- : "cr0", "ctr", "memory"); \
- __sc_ret = __sc_3; \
- __sc_err = __sc_0; \
- } \
- LSS_RETURN(type, __sc_ret, __sc_err)
-
- LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
- LSS_SC_BODY(3, int, 1, domain, type, protocol);
- }
- #endif
#if defined(__i386__) || \
+ defined(__PPC__) || \
(defined(__arm__) && !defined(__ARM_EABI__)) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
diff --git a/src/base/linuxthreads.cc b/src/base/linuxthreads.cc
index 73eef04..ba4d1c8 100644
--- a/src/base/linuxthreads.cc
+++ b/src/base/linuxthreads.cc
@@ -96,6 +96,14 @@ static int local_clone (int (*fn)(void *), void *arg, ...)
#endif
#endif
+/* To avoid the gap cross page boundaries, increase by the large parge
+ * size mostly PowerPC system uses. */
+#ifdef __PPC64__
+#define CLONE_STACK_SIZE 65536
+#else
+#define CLONE_STACK_SIZE 4096
+#endif
+
static int local_clone (int (*fn)(void *), void *arg, ...) {
/* Leave 4kB of gap between the callers stack and the new clone. This
* should be more than sufficient for the caller to call waitpid() until
@@ -111,7 +119,7 @@ static int local_clone (int (*fn)(void *), void *arg, ...) {
* is being debugged. This is OK and the error code will be reported
* correctly.
*/
- return sys_clone(fn, (char *)&arg - 4096,
+ return sys_clone(fn, (char *)&arg - CLONE_STACK_SIZE,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_UNTRACED, arg, 0, 0, 0);
}
diff --git a/src/heap-checker.cc b/src/heap-checker.cc
index bc9e490..c050866 100644
--- a/src/heap-checker.cc
+++ b/src/heap-checker.cc
@@ -1008,6 +1008,15 @@ static enum {
// due to reliance on locale functions (these are called through RAW_LOG
// and in other ways).
//
+
+#if defined(HAVE_LINUX_PTRACE_H) && defined(HAVE_SYS_SYSCALL_H) && defined(DUMPER)
+# if (defined(__i386__) || defined(__x86_64))
+# define THREAD_REGS i386_regs
+# elif defined(__PPC__)
+# define THREAD_REGS ppc_regs
+# endif
+#endif
+
/*static*/ int HeapLeakChecker::IgnoreLiveThreadsLocked(void* parameter,
int num_threads,
pid_t* thread_pids,
@@ -1030,9 +1039,8 @@ static enum {
// specially via self_thread_stack, not here:
if (thread_pids[i] == self_thread_pid) continue;
RAW_VLOG(11, "Handling thread with pid %d", thread_pids[i]);
-#if (defined(__i386__) || defined(__x86_64)) && \
- defined(HAVE_LINUX_PTRACE_H) && defined(HAVE_SYS_SYSCALL_H) && defined(DUMPER)
- i386_regs thread_regs;
+#ifdef THREAD_REGS
+ THREAD_REGS thread_regs;
#define sys_ptrace(r, p, a, d) syscall(SYS_ptrace, (r), (p), (a), (d))
// We use sys_ptrace to avoid thread locking
// because this is called from ListAllProcessThreads
diff --git a/src/stacktrace_powerpc-inl.h b/src/stacktrace_powerpc-inl.h
index acf2884..e1bf0bd 100644
--- a/src/stacktrace_powerpc-inl.h
+++ b/src/stacktrace_powerpc-inl.h
@@ -45,32 +45,45 @@
#include <stdlib.h> // for NULL
#include <gperftools/stacktrace.h>
+struct layout_ppc {
+ struct layout_ppc *next;
+#if defined(__APPLE__) || (defined(__linux) && defined(__PPC64__))
+ long condition_register;
+#endif
+ void *return_addr;
+};
+
// 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 (the strictness of which is controlled by the boolean parameter
// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
template<bool STRICT_UNWINDING>
-static void **NextStackFrame(void **old_sp) {
- void **new_sp = (void **) *old_sp;
+static layout_ppc *NextStackFrame(layout_ppc *current) {
+ uintptr_t old_sp = (uintptr_t)(current);
+ uintptr_t new_sp = (uintptr_t)(current->next);
// Check that the transition from frame pointer old_sp to frame
// pointer new_sp isn't clearly bogus
if (STRICT_UNWINDING) {
// With the stack growing downwards, older stack frame must be
// at a greater address that the current one.
- if (new_sp <= old_sp) return NULL;
+ if (new_sp <= old_sp)
+ return NULL;
// Assume stack frames larger than 100,000 bytes are bogus.
- if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL;
+ if (new_sp - old_sp > 100000)
+ return NULL;
} else {
// In the non-strict mode, allow discontiguous stack frames.
// (alternate-signal-stacks for example).
- if (new_sp == old_sp) return NULL;
+ if (new_sp == old_sp)
+ return NULL;
// And allow frames upto about 1MB.
- if ((new_sp > old_sp)
- && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL;
+ if ((new_sp > old_sp) && (new_sp - old_sp > 1000000))
+ return NULL;
}
- if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL;
- return new_sp;
+ if (new_sp & (sizeof(void *) - 1))
+ return NULL;
+ return current->next;
}
// This ensures that GetStackTrace stes up the Link Register properly.
@@ -81,6 +94,26 @@ void StacktracePowerPCDummyFunction() { __asm__ volatile(""); }
// Note: this part of the file is included several times.
// Do not put globals below.
+// Load instruction used on top-of-stack get.
+#if defined(__PPC64__) || defined(__LP64__)
+# define LOAD "ld"
+#else
+# define LOAD "lwz"
+#endif
+
+#if defined(__linux__) && defined(__PPC__)
+# define TOP_STACK "%0,0(1)"
+#elif defined(__MACH__) && defined(__APPLE__)
+// Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
+// and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a
+// different asm syntax. I don't know quite the best way to discriminate
+// systems using the old as from the new one; I've gone with __APPLE__.
+// TODO(csilvers): use autoconf instead, to look for 'as --version' == 1 or 2
+# define TOP_STACK "%0,0(r1)"
+#endif
+
+
+
// The following 4 functions are generated from the code below:
// GetStack{Trace,Frames}()
// GetStack{Trace,Frames}WithContext()
@@ -93,78 +126,44 @@ void StacktracePowerPCDummyFunction() { __asm__ volatile(""); }
// int skip_count: how many stack pointers to skip before storing in result
// void* ucp: a ucontext_t* (GetStack{Trace,Frames}WithContext only)
int GET_STACK_TRACE_OR_FRAMES {
- void **sp;
- // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
- // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a
- // different asm syntax. I don't know quite the best way to discriminate
- // systems using the old as from the new one; I've gone with __APPLE__.
- // TODO(csilvers): use autoconf instead, to look for 'as --version' == 1 or 2
-#ifdef __APPLE__
- __asm__ volatile ("mr %0,r1" : "=r" (sp));
-#else
- __asm__ volatile ("mr %0,1" : "=r" (sp));
-#endif
+ layout_ppc *current;
+ int n;
+
+ // Force GCC to spill LR.
+ asm volatile ("" : "=l"(current));
+
+ // Get the address on top-of-stack
+ asm volatile (LOAD " " TOP_STACK : "=r"(current));
- // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack
- // entry that holds the return address of the subroutine call (what
- // instruction we run after our function finishes). This is the
- // same as the stack-pointer of our parent routine, which is what we
- // want here. While the compiler will always(?) set up LR for
- // subroutine calls, it may not for leaf functions (such as this one).
- // This routine forces the compiler (at least gcc) to push it anyway.
StacktracePowerPCDummyFunction();
-#if IS_STACK_FRAMES
- // Note we do *not* increment skip_count here for the SYSV ABI. If
- // we did, the list of stack frames wouldn't properly match up with
- // the list of return addresses. Note this means the top pc entry
- // is probably bogus for linux/ppc (and other SYSV-ABI systems).
-#else
- // The LR save area is used by the callee, so the top entry is bogus.
- skip_count++;
-#endif
+ n = 0;
+ while (current && n < max_depth) {
+ result[n] = current->return_addr;
- int n = 0;
- while (sp && n < max_depth) {
// The GetStackFrames routine is called when we are in some
// informational context (the failure signal handler for example).
// Use the non-strict unwinding rules to produce a stack trace
// that is as complete as possible (even if it contains a few
// bogus entries in some rare cases).
- void **next_sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
-
- if (skip_count > 0) {
- skip_count--;
- } else {
- // PowerPC has 3 main ABIs, which say where in the stack the
- // Link Register is. For DARWIN and AIX (used by apple and
- // linux ppc64), it's in sp[2]. For SYSV (used by linux ppc),
- // it's in sp[1].
-#if defined(_CALL_AIX) || defined(_CALL_DARWIN)
- result[n] = *(sp+2);
-#elif defined(_CALL_SYSV)
- result[n] = *(sp+1);
-#elif defined(__APPLE__) || (defined(__linux) && defined(__PPC64__))
- // This check is in case the compiler doesn't define _CALL_AIX/etc.
- result[n] = *(sp+2);
-#elif defined(__linux)
- // This check is in case the compiler doesn't define _CALL_SYSV.
- result[n] = *(sp+1);
-#else
-#error Need to specify the PPC ABI for your archiecture.
-#endif
-
+ layout_ppc *next = NextStackFrame<!IS_STACK_FRAMES>(current);
#if IS_STACK_FRAMES
- if (next_sp > sp) {
- sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
- } else {
- // A frame-size of 0 is used to indicate unknown frame size.
- sizes[n] = 0;
- }
-#endif
- n++;
+ if (next > current) {
+ sizes[n] = (uintptr_t)next - (uintptr_t)current;
+ } else {
+ // A frame-size of 0 is used to indicate unknown frame size.
+ sizes[n] = 0;
}
- sp = next_sp;
+#endif
+ current = next;
+ n++;
}
+
+ // It's possible the second-last stack frame can't return
+ // (that is, it's __libc_start_main), in which case
+ // the CRT startup code will have set its LR to 'NULL'.
+ if (n > 0 && result[n-1] == NULL)
+ n--;
+
return n;
}
diff --git a/src/tests/heap-checker_unittest.cc b/src/tests/heap-checker_unittest.cc
index 75c05f8..482f51f 100644
--- a/src/tests/heap-checker_unittest.cc
+++ b/src/tests/heap-checker_unittest.cc
@@ -1263,6 +1263,27 @@ static void* Mmapper(uintptr_t* addr_after_mmap_call) {
return r;
}
+// On PPC64 the stacktrace returned by GetStatcTrace contains the function
+// address from .text segment while function pointers points to ODP entries.
+// The following code decodes the ODP to get the actual symbol address.
+#if defined(__linux) && defined(__PPC64__)
+static inline uintptr_t GetFunctionAddress (void* (*func)(uintptr_t*))
+{
+ struct odp_entry_t {
+ unsigned long int symbol;
+ unsigned long int toc;
+ unsigned long int env;
+ } *odp_entry = reinterpret_cast<odp_entry_t*>(func);
+
+ return static_cast<uintptr_t>(odp_entry->symbol);
+}
+#else
+static inline uintptr_t GetFunctionAddress (void* (*func)(uintptr_t*))
+{
+ return reinterpret_cast<uintptr_t>(func);
+}
+#endif
+
// to trick complier into preventing inlining
static void* (*mmapper_addr)(uintptr_t* addr) = &Mmapper;
@@ -1283,7 +1304,7 @@ static void VerifyMemoryRegionMapStackGet() {
}
}
// caller must point into Mmapper function:
- if (!(reinterpret_cast<uintptr_t>(mmapper_addr) <= caller &&
+ if (!(GetFunctionAddress(mmapper_addr) <= caller &&
caller < caller_addr_limit)) {
LOGF << std::hex << "0x" << caller
<< " does not seem to point into code of function Mmapper at "
@@ -1316,7 +1337,7 @@ extern void VerifyHeapProfileTableStackGet() {
uintptr_t caller =
reinterpret_cast<uintptr_t>(HeapLeakChecker::GetAllocCaller(addr));
// caller must point into Mallocer function:
- if (!(reinterpret_cast<uintptr_t>(mallocer_addr) <= caller &&
+ if (!(GetFunctionAddress(mallocer_addr) <= caller &&
caller < caller_addr_limit)) {
LOGF << std::hex << "0x" << caller
<< " does not seem to point into code of function Mallocer at "