diff options
-rw-r--r-- | src/base/elfcore.h | 19 | ||||
-rw-r--r-- | src/base/linux_syscall_support.h | 199 | ||||
-rw-r--r-- | src/base/linuxthreads.cc | 10 | ||||
-rw-r--r-- | src/heap-checker.cc | 14 | ||||
-rw-r--r-- | src/stacktrace_powerpc-inl.h | 139 | ||||
-rw-r--r-- | src/tests/heap-checker_unittest.cc | 25 |
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 " |