This source file includes following definitions.
- Run
- p1_
- Run
- p2_
- Run
- NewCallback
- NewCallback
- NewCallback
- WipeStack
- Pause
- Use
- Hide
- UnHide
- LogHidden
- DoRunHidden
- DoWipeStack
- RunHidden
- DoAllocHidden
- AllocHidden
- DoDeAllocHidden
- DeAllocHidden
- PreventHeapReclaiming
- RunSilent
- VerifyLeaks
- TestHeapLeakCheckerDeathSimple
- MakeDeathLoop
- TestHeapLeakCheckerDeathLoop
- TestHeapLeakCheckerDeathInverse
- TestHeapLeakCheckerDeathNoLeaks
- TestHeapLeakCheckerDeathCountLess
- TestHeapLeakCheckerDeathCountMore
- TestHiddenPointer
- TestHeapLeakChecker
- TestHeapLeakCheckerNoFalsePositives
- TestLeakButTotalsMatch
- TestHeapLeakCheckerDeathTrick
- TransLeaks
- ScopedDisabledLeaks
- RunDisabledLeaks
- ThreadDisabledLeaks
- TestHeapLeakCheckerDisabling
- DoTestSTLAlloc
- TestSTLAlloc
- DoTestSTLAllocInverse
- FreeTestSTLAllocInverse
- TestSTLAllocInverse
- DirectTestSTLAlloc
- KeyFree
- KeyInit
- TestLibCAllocate
- HeapBusyThreadBody
- RunHeapBusyThreads
- TestPointerReach
- TestObjMakers
- REGISTER_OBJ_MAKER
- f
- f2
- f
- f2
- f
- f2
- f
- A
- B
- C
- f
- A
- B
- REGISTER_OBJ_MAKER
- TestHeapLeakCheckerLiveness
- Mmapper
- VerifyMemoryRegionMapStackGet
- Mallocer
- VerifyHeapProfileTableStackGet
- MakeALeak
- Pass
- main
#include "config_for_unittests.h"
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#if defined HAVE_STDINT_H
#include <stdint.h>
#elif defined HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#include <fcntl.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/commandlineflags.h"
#include "base/googleinit.h"
#include "base/logging.h"
#include "base/commandlineflags.h"
#include "base/thread_lister.h"
#include <gperftools/heap-checker.h>
#include "memory_region_map.h"
#include <gperftools/malloc_extension.h>
#include <gperftools/stacktrace.h>
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
using namespace std;
DEFINE_bool(test_leak,
EnvToBool("HEAP_CHECKER_TEST_TEST_LEAK", false),
"If should cause a leak crash");
DEFINE_bool(test_loop_leak,
EnvToBool("HEAP_CHECKER_TEST_TEST_LOOP_LEAK", false),
"If should cause a looped leak crash");
DEFINE_bool(test_register_leak,
EnvToBool("HEAP_CHECKER_TEST_TEST_REGISTER_LEAK", false),
"If should cause a leak crash by hiding a pointer "
"that is only in a register");
DEFINE_bool(test_cancel_global_check,
EnvToBool("HEAP_CHECKER_TEST_TEST_CANCEL_GLOBAL_CHECK", false),
"If should test HeapLeakChecker::CancelGlobalCheck "
"when --test_leak or --test_loop_leak are given; "
"the test should not fail then");
DEFINE_bool(maybe_stripped,
EnvToBool("HEAP_CHECKER_TEST_MAYBE_STRIPPED", true),
"If we think we can be a stripped binary");
DEFINE_bool(interfering_threads,
EnvToBool("HEAP_CHECKER_TEST_INTERFERING_THREADS", true),
"If we should use threads trying "
"to interfere with leak checking");
DEFINE_bool(hoarding_threads,
EnvToBool("HEAP_CHECKER_TEST_HOARDING_THREADS", true),
"If threads (usually the manager thread) are known "
"to retain some old state in their global buffers, "
"so that it's hard to force leaks when threads are around");
DEFINE_bool(no_threads,
EnvToBool("HEAP_CHECKER_TEST_NO_THREADS", false),
"If we should not use any threads");
DECLARE_int64(heap_check_max_pointer_offset);
DECLARE_string(heap_check);
#define WARN_IF(cond, msg) LOG_IF(WARNING, cond, msg)
#undef VLOG
#define VLOG(lvl) if (FLAGS_verbose >= (lvl)) cout << "\n"
#define LOGF VLOG(INFO)
static void RunHeapBusyThreads();
class Closure {
public:
virtual ~Closure() { }
virtual void Run() = 0;
};
class Callback0 : public Closure {
public:
typedef void (*FunctionSignature)();
inline Callback0(FunctionSignature f) : f_(f) {}
virtual void Run() { (*f_)(); delete this; }
private:
FunctionSignature f_;
};
template <class P1> class Callback1 : public Closure {
public:
typedef void (*FunctionSignature)(P1);
inline Callback1<P1>(FunctionSignature f, P1 p1) : f_(f), p1_(p1) {}
virtual void Run() { (*f_)(p1_); delete this; }
private:
FunctionSignature f_;
P1 p1_;
};
template <class P1, class P2> class Callback2 : public Closure {
public:
typedef void (*FunctionSignature)(P1,P2);
inline Callback2<P1,P2>(FunctionSignature f, P1 p1, P2 p2) : f_(f), p1_(p1), p2_(p2) {}
virtual void Run() { (*f_)(p1_, p2_); delete this; }
private:
FunctionSignature f_;
P1 p1_;
P2 p2_;
};
inline Callback0* NewCallback(void (*function)()) {
return new Callback0(function);
}
template <class P1>
inline Callback1<P1>* NewCallback(void (*function)(P1), P1 p1) {
return new Callback1<P1>(function, p1);
}
template <class P1, class P2>
inline Callback2<P1,P2>* NewCallback(void (*function)(P1,P2), P1 p1, P2 p2) {
return new Callback2<P1,P2>(function, p1, p2);
}
static bool g_have_exited_main = false;
static bool can_create_leaks_reliably = false;
struct Initialized { };
static Initialized initialized;
void* operator new(size_t size, const Initialized&) {
void* p = malloc(size);
memset(p, 0, size);
return p;
}
void* operator new[](size_t size, const Initialized&) {
char* p = new char[size];
memset(p, 0, size);
return p;
}
static void DoWipeStack(int n);
static void WipeStack() { DoWipeStack(20); }
static void Pause() {
poll(NULL, 0, 77);
CHECK(MallocExtension::instance()->VerifyAllMemory());
int blocks;
size_t total;
int histogram[kMallocHistogramSize];
if (MallocExtension::instance()
->MallocMemoryStats(&blocks, &total, histogram) && total != 0) {
VLOG(3) << "Malloc stats: " << blocks << " blocks of "
<< total << " bytes";
for (int i = 0; i < kMallocHistogramSize; ++i) {
if (histogram[i]) {
VLOG(3) << " Malloc histogram at " << i << " : " << histogram[i];
}
}
}
WipeStack();
}
template <class T>
static void Use(T** foo) {
VLOG(2) << "Dummy-using " << static_cast<void*>(*foo) << " at " << foo;
}
static const uintptr_t kHideMask =
static_cast<uintptr_t>(0xF03A5F7BF03A5F7BLL);
template <class T>
static void Hide(T** ptr) {
*ptr = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(*ptr) ^ kHideMask);
VLOG(2) << "hid: " << static_cast<void*>(*ptr);
}
template <class T>
static void UnHide(T** ptr) {
VLOG(2) << "unhiding: " << static_cast<void*>(*ptr);
*ptr = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(*ptr) ^ kHideMask);
}
static void LogHidden(const char* message, const void* ptr) {
LOGF << message << " : "
<< ptr << " ^ " << reinterpret_cast<void*>(kHideMask) << endl;
}
void (*volatile run_hidden_ptr)(Closure* c, int n);
void (*volatile wipe_stack_ptr)(int n);
static void DoRunHidden(Closure* c, int n) {
if (n) {
VLOG(10) << "Level " << n << " at " << &n;
(*run_hidden_ptr)(c, n-1);
(*wipe_stack_ptr)(n);
sleep(0);
} else {
c->Run();
}
}
void DoWipeStack(int n) {
VLOG(10) << "Wipe level " << n << " at " << &n;
if (n) {
const int sz = 30;
volatile int arr[sz];
for (int i = 0; i < sz; ++i) arr[i] = 0;
(*wipe_stack_ptr)(n-1);
sleep(0);
}
}
static void RunHidden(Closure* c) {
DoRunHidden(c, 15);
DoWipeStack(20);
}
static void DoAllocHidden(size_t size, void** ptr) {
void* p = new(initialized) char[size];
Hide(&p);
Use(&p);
VLOG(2) << "Allocated hidden " << p << " at " << &p;
*ptr = p;
}
static void* AllocHidden(size_t size) {
void* r;
RunHidden(NewCallback(DoAllocHidden, size, &r));
return r;
}
static void DoDeAllocHidden(void** ptr) {
Use(ptr);
void* p = *ptr;
VLOG(2) << "Deallocating hidden " << p;
UnHide(&p);
delete [] reinterpret_cast<char*>(p);
}
static void DeAllocHidden(void** ptr) {
RunHidden(NewCallback(DoDeAllocHidden, ptr));
*ptr = NULL;
Use(ptr);
}
void PreventHeapReclaiming(size_t size) {
#ifdef NDEBUG
if (true) {
static void** no_reclaim_list = NULL;
CHECK(size >= sizeof(void*));
for (int i = 0; i < 100; ++i) {
void** p = reinterpret_cast<void**>(new(initialized) char[size]);
p[0] = no_reclaim_list;
no_reclaim_list = p;
}
}
#endif
}
static bool RunSilent(HeapLeakChecker* check,
bool (HeapLeakChecker::* func)()) {
int32 old_FLAGS_verbose = FLAGS_verbose;
if (!VLOG_IS_ON(1))
FLAGS_verbose = FATAL;
const bool retval = (check->*func)();
FLAGS_verbose = old_FLAGS_verbose;
return retval;
}
#define RUN_SILENT(check, func) RunSilent(&(check), &HeapLeakChecker::func)
enum CheckType { SAME_HEAP, NO_LEAKS };
static void VerifyLeaks(HeapLeakChecker* check, CheckType type,
int leaked_bytes, int leaked_objects) {
WipeStack();
const bool no_leaks =
type == NO_LEAKS ? RUN_SILENT(*check, BriefNoLeaks)
: RUN_SILENT(*check, BriefSameHeap);
if (can_create_leaks_reliably) {
CHECK_EQ(no_leaks, false);
CHECK_EQ(check->BytesLeaked(), leaked_bytes);
CHECK_EQ(check->ObjectsLeaked(), leaked_objects);
} else {
WARN_IF(no_leaks != false,
"Expected leaks not found: "
"Some liveness flood must be too optimistic");
}
}
static void TestHeapLeakCheckerDeathSimple() {
HeapLeakChecker check("death_simple");
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
void* bar = AllocHidden(300);
Use(&bar);
LogHidden("Leaking", foo);
LogHidden("Leaking", bar);
Pause();
VerifyLeaks(&check, NO_LEAKS, 300 + 100 * sizeof(int), 2);
DeAllocHidden(&foo);
DeAllocHidden(&bar);
}
static void MakeDeathLoop(void** arr1, void** arr2) {
PreventHeapReclaiming(2 * sizeof(void*));
void** a1 = new(initialized) void*[2];
void** a2 = new(initialized) void*[2];
a1[1] = reinterpret_cast<void*>(a2);
a2[1] = reinterpret_cast<void*>(a1);
Hide(&a1);
Hide(&a2);
Use(&a1);
Use(&a2);
VLOG(2) << "Made hidden loop at " << &a1 << " to " << arr1;
*arr1 = a1;
*arr2 = a2;
}
static void TestHeapLeakCheckerDeathLoop() {
HeapLeakChecker check("death_loop");
void* arr1;
void* arr2;
RunHidden(NewCallback(MakeDeathLoop, &arr1, &arr2));
Use(&arr1);
Use(&arr2);
LogHidden("Leaking", arr1);
LogHidden("Leaking", arr2);
Pause();
VerifyLeaks(&check, NO_LEAKS, 4 * sizeof(void*), 2);
DeAllocHidden(&arr1);
DeAllocHidden(&arr2);
}
static void TestHeapLeakCheckerDeathInverse() {
void* bar = AllocHidden(250 * sizeof(int));
Use(&bar);
LogHidden("Pre leaking", bar);
Pause();
HeapLeakChecker check("death_inverse");
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
LogHidden("Leaking", foo);
DeAllocHidden(&bar);
Pause();
VerifyLeaks(&check, SAME_HEAP,
100 * static_cast<int64>(sizeof(int)),
1);
DeAllocHidden(&foo);
}
static void TestHeapLeakCheckerDeathNoLeaks() {
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
void* bar = AllocHidden(250 * sizeof(int));
Use(&bar);
HeapLeakChecker check("death_noleaks");
DeAllocHidden(&bar);
CHECK_EQ(check.BriefNoLeaks(), true);
DeAllocHidden(&foo);
}
static void TestHeapLeakCheckerDeathCountLess() {
void* bar1 = AllocHidden(50 * sizeof(int));
Use(&bar1);
void* bar2 = AllocHidden(50 * sizeof(int));
Use(&bar2);
LogHidden("Pre leaking", bar1);
LogHidden("Pre leaking", bar2);
Pause();
HeapLeakChecker check("death_count_less");
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
LogHidden("Leaking", foo);
DeAllocHidden(&bar1);
DeAllocHidden(&bar2);
Pause();
VerifyLeaks(&check, SAME_HEAP,
100 * sizeof(int),
1);
DeAllocHidden(&foo);
}
static void TestHeapLeakCheckerDeathCountMore() {
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
LogHidden("Pre leaking", foo);
Pause();
HeapLeakChecker check("death_count_more");
void* bar1 = AllocHidden(50 * sizeof(int));
Use(&bar1);
void* bar2 = AllocHidden(50 * sizeof(int));
Use(&bar2);
LogHidden("Leaking", bar1);
LogHidden("Leaking", bar2);
DeAllocHidden(&foo);
Pause();
VerifyLeaks(&check, SAME_HEAP,
100 * sizeof(int),
2);
DeAllocHidden(&bar1);
DeAllocHidden(&bar2);
}
static void TestHiddenPointer() {
int i;
void* foo = &i;
HiddenPointer<void> p(foo);
CHECK_EQ(foo, p.get());
CHECK_NE(foo, *reinterpret_cast<void**>(&p));
}
static void TestHeapLeakChecker() {
{ HeapLeakChecker check("trivial");
int foo = 5;
int* p = &foo;
Use(&p);
Pause();
CHECK(check.BriefSameHeap());
}
Pause();
{ HeapLeakChecker check("simple");
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
void* bar = AllocHidden(200 * sizeof(int));
Use(&bar);
DeAllocHidden(&foo);
DeAllocHidden(&bar);
Pause();
CHECK(check.BriefSameHeap());
}
}
static void TestHeapLeakCheckerNoFalsePositives() {
{ HeapLeakChecker check("trivial_p");
int foo = 5;
int* p = &foo;
Use(&p);
Pause();
CHECK(check.BriefSameHeap());
}
Pause();
{ HeapLeakChecker check("simple_p");
void* foo = AllocHidden(100 * sizeof(int));
Use(&foo);
void* bar = AllocHidden(200 * sizeof(int));
Use(&bar);
DeAllocHidden(&foo);
DeAllocHidden(&bar);
Pause();
CHECK(check.SameHeap());
}
}
static void TestLeakButTotalsMatch() {
void* bar1 = AllocHidden(240 * sizeof(int));
Use(&bar1);
void* bar2 = AllocHidden(160 * sizeof(int));
Use(&bar2);
LogHidden("Pre leaking", bar1);
LogHidden("Pre leaking", bar2);
Pause();
HeapLeakChecker check("trick");
void* foo1 = AllocHidden(280 * sizeof(int));
Use(&foo1);
void* foo2 = AllocHidden(120 * sizeof(int));
Use(&foo2);
LogHidden("Leaking", foo1);
LogHidden("Leaking", foo2);
DeAllocHidden(&bar1);
DeAllocHidden(&bar2);
Pause();
VerifyLeaks(&check, NO_LEAKS, (280+120)*sizeof(int), 2);
DeAllocHidden(&foo1);
DeAllocHidden(&foo2);
}
static void TestHeapLeakCheckerDeathTrick() {
void* bar1 = AllocHidden(240 * sizeof(int));
Use(&bar1);
void* bar2 = AllocHidden(160 * sizeof(int));
Use(&bar2);
HeapLeakChecker check("death_trick");
DeAllocHidden(&bar1);
DeAllocHidden(&bar2);
void* foo1 = AllocHidden(280 * sizeof(int));
Use(&foo1);
void* foo2 = AllocHidden(120 * sizeof(int));
Use(&foo2);
if (!FLAGS_maybe_stripped) {
CHECK_EQ(RUN_SILENT(check, SameHeap), false);
} else {
WARN_IF(RUN_SILENT(check, SameHeap) != false,
"death_trick leak is not caught; "
"we must be using a stripped binary");
}
DeAllocHidden(&foo1);
DeAllocHidden(&foo2);
}
static void TransLeaks() {
AllocHidden(1 * sizeof(char));
}
static void ScopedDisabledLeaks() {
HeapLeakChecker::Disabler disabler;
AllocHidden(3 * sizeof(int));
TransLeaks();
(void)malloc(10);
}
static void* RunDisabledLeaks(void* a) {
ScopedDisabledLeaks();
return a;
}
static void ThreadDisabledLeaks() {
if (FLAGS_no_threads) return;
pthread_t tid;
pthread_attr_t attr;
CHECK_EQ(pthread_attr_init(&attr), 0);
CHECK_EQ(pthread_create(&tid, &attr, RunDisabledLeaks, NULL), 0);
void* res;
CHECK_EQ(pthread_join(tid, &res), 0);
}
static void TestHeapLeakCheckerDisabling() {
HeapLeakChecker check("disabling");
RunDisabledLeaks(NULL);
RunDisabledLeaks(NULL);
ThreadDisabledLeaks();
RunDisabledLeaks(NULL);
ThreadDisabledLeaks();
ThreadDisabledLeaks();
Pause();
CHECK(check.SameHeap());
}
typedef set<int> IntSet;
static int some_ints[] = { 1, 2, 3, 21, 22, 23, 24, 25 };
static void DoTestSTLAlloc() {
IntSet* x = new(initialized) IntSet[1];
*x = IntSet(some_ints, some_ints + 6);
for (int i = 0; i < 1000; i++) {
x->insert(i*3);
}
delete [] x;
}
static void TestSTLAlloc() {
HeapLeakChecker check("stl");
RunHidden(NewCallback(DoTestSTLAlloc));
CHECK_EQ(check.BriefSameHeap(), true);
}
static void DoTestSTLAllocInverse(IntSet** setx) {
IntSet* x = new(initialized) IntSet[1];
*x = IntSet(some_ints, some_ints + 3);
for (int i = 0; i < 100; i++) {
x->insert(i*2);
}
Hide(&x);
*setx = x;
}
static void FreeTestSTLAllocInverse(IntSet** setx) {
IntSet* x = *setx;
UnHide(&x);
delete [] x;
}
static void TestSTLAllocInverse() {
HeapLeakChecker check("death_inverse_stl");
IntSet* x;
RunHidden(NewCallback(DoTestSTLAllocInverse, &x));
LogHidden("Leaking", x);
if (can_create_leaks_reliably) {
WipeStack();
CHECK_EQ(RUN_SILENT(check, BriefNoLeaks), false);
CHECK_GE(check.BytesLeaked(), 100 * sizeof(int));
CHECK_GE(check.ObjectsLeaked(), 100);
} else {
WARN_IF(RUN_SILENT(check, BriefNoLeaks) != false,
"Expected leaks not found: "
"Some liveness flood must be too optimistic");
}
RunHidden(NewCallback(FreeTestSTLAllocInverse, &x));
}
template<class Alloc>
static void DirectTestSTLAlloc(Alloc allocator, const char* name) {
HeapLeakChecker check((string("direct_stl-") + name).c_str());
static const int kSize = 1000;
typename Alloc::pointer ptrs[kSize];
for (int i = 0; i < kSize; ++i) {
typename Alloc::pointer p = allocator.allocate(i*3+1);
HeapLeakChecker::IgnoreObject(p);
HeapLeakChecker::UnIgnoreObject(p);
ptrs[i] = p;
}
for (int i = 0; i < kSize; ++i) {
allocator.deallocate(ptrs[i], i*3+1);
ptrs[i] = NULL;
}
CHECK(check.BriefSameHeap());
}
static struct group* grp = NULL;
static const int kKeys = 50;
static pthread_key_t key[kKeys];
static void KeyFree(void* ptr) {
delete [] reinterpret_cast<char*>(ptr);
}
static bool key_init_has_run = false;
static void KeyInit() {
for (int i = 0; i < kKeys; ++i) {
CHECK_EQ(pthread_key_create(&key[i], KeyFree), 0);
VLOG(2) << "pthread key " << i << " : " << key[i];
}
key_init_has_run = true;
}
static void TestLibCAllocate() {
CHECK(key_init_has_run);
for (int i = 0; i < kKeys; ++i) {
void* p = pthread_getspecific(key[i]);
if (NULL == p) {
if (i == 0) {
VLOG(0) << "Adding pthread-specifics for thread " << pthread_self()
<< " pid " << getpid();
}
p = new(initialized) char[77 + i];
VLOG(2) << "pthread specific " << i << " : " << p;
pthread_setspecific(key[i], p);
}
}
strerror(errno);
const time_t now = time(NULL);
ctime(&now);
#ifdef HAVE_EXECINFO_H
void *stack[1];
backtrace(stack, 1);
#endif
#ifdef HAVE_GRP_H
gid_t gid = getgid();
getgrgid(gid);
if (grp == NULL) grp = getgrent();
getgrnam(grp->gr_name);
#endif
#ifdef HAVE_PWD_H
getpwuid(geteuid());
#endif
}
static void* HeapBusyThreadBody(void* a) {
const int thread_num = reinterpret_cast<intptr_t>(a);
VLOG(0) << "A new HeapBusyThread " << thread_num;
TestLibCAllocate();
int user = 0;
#if defined(__i386__) && defined(__GNUC__)
register int** ptr asm("esi");
#elif defined(__x86_64__) && defined(__GNUC__)
register int** ptr asm("r15");
#else
register int** ptr;
#endif
ptr = NULL;
typedef set<int> Set;
Set s1;
while (1) {
if (!g_have_exited_main)
TestLibCAllocate();
if (ptr == NULL) {
ptr = new(initialized) int*[1];
*ptr = new(initialized) int[1];
}
set<int>* s2 = new(initialized) set<int>[1];
s1.insert(random());
s2->insert(*s1.begin());
user += *s2->begin();
**ptr += user;
if (random() % 51 == 0) {
s1.clear();
if (random() % 2 == 0) {
s1.~Set();
new(&s1) Set;
}
}
VLOG(3) << pthread_self() << " (" << getpid() << "): in wait: "
<< ptr << ", " << *ptr << "; " << s1.size();
VLOG(2) << pthread_self() << " (" << getpid() << "): in wait, ptr = "
<< reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(ptr) ^ kHideMask)
<< "^" << reinterpret_cast<void*>(kHideMask);
if (FLAGS_test_register_leak && thread_num % 5 == 0) {
ptr = reinterpret_cast<int **>(
reinterpret_cast<uintptr_t>(ptr) ^ kHideMask);
for (int i = 1; i < 10000000; ++i) user += (1 + user * user * 5) / i;
ptr = reinterpret_cast<int **>(
reinterpret_cast<uintptr_t>(ptr) ^ kHideMask);
} else {
poll(NULL, 0, random() % 100);
}
VLOG(2) << pthread_self() << ": continuing";
if (random() % 3 == 0) {
delete [] *ptr;
delete [] ptr;
ptr = NULL;
}
delete [] s2;
}
return a;
}
static void RunHeapBusyThreads() {
KeyInit();
if (!FLAGS_interfering_threads || FLAGS_no_threads) return;
const int n = 17;
pthread_t tid;
pthread_attr_t attr;
CHECK_EQ(pthread_attr_init(&attr), 0);
for (int i = 0; i < n; ++i) {
VLOG(0) << "Creating extra thread " << i + 1;
CHECK(pthread_create(&tid, &attr, HeapBusyThreadBody,
reinterpret_cast<void*>(i)) == 0);
}
Pause();
Pause();
}
typedef void* (*ObjMakerFunc)();
static list<ObjMakerFunc> obj_makers;
#define REGISTER_OBJ_MAKER(name, body) \
void* ObjMaker_##name##_() { \
VLOG(1) << "Obj making " << #name; \
body; \
return p; \
} \
static ObjMakerRegistrar maker_reg_##name##__(&ObjMaker_##name##_);
struct ObjMakerRegistrar {
ObjMakerRegistrar(ObjMakerFunc obj_maker) { obj_makers.push_back(obj_maker); }
};
static list<void*>* live_objects = new list<void*>;
static void TestPointerReach(ObjMakerFunc obj_maker) {
HeapLeakChecker::IgnoreObject(obj_maker());
void* obj = obj_maker();
HeapLeakChecker::IgnoreObject(obj);
HeapLeakChecker::UnIgnoreObject(obj);
HeapLeakChecker::IgnoreObject(obj);
live_objects->push_back(obj_maker());
}
static void TestObjMakers() {
for (list<ObjMakerFunc>::const_iterator i = obj_makers.begin();
i != obj_makers.end(); ++i) {
TestPointerReach(*i);
TestPointerReach(*i);
TestPointerReach(*i);
}
}
template<class T>
struct Array {
Array() {
size = 3 + random() % 30;
ptr = new(initialized) T[size];
}
~Array() { delete [] ptr; }
Array(const Array& x) {
size = x.size;
ptr = new(initialized) T[size];
for (size_t i = 0; i < size; ++i) {
ptr[i] = x.ptr[i];
}
}
void operator=(const Array& x) {
delete [] ptr;
size = x.size;
ptr = new(initialized) T[size];
for (size_t i = 0; i < size; ++i) {
ptr[i] = x.ptr[i];
}
}
void append(const Array& x) {
T* p = new(initialized) T[size + x.size];
for (size_t i = 0; i < size; ++i) {
p[i] = ptr[i];
}
for (size_t i = 0; i < x.size; ++i) {
p[size+i] = x.ptr[i];
}
size += x.size;
delete [] ptr;
ptr = p;
}
private:
size_t size;
T* ptr;
};
REGISTER_OBJ_MAKER(plain, int* p = new(initialized) int;)
REGISTER_OBJ_MAKER(int_array_1, int* p = new(initialized) int[1];)
REGISTER_OBJ_MAKER(int_array, int* p = new(initialized) int[10];)
REGISTER_OBJ_MAKER(string, Array<char>* p = new(initialized) Array<char>();)
REGISTER_OBJ_MAKER(string_array,
Array<char>* p = new(initialized) Array<char>[5];)
REGISTER_OBJ_MAKER(char_array, char* p = new(initialized) char[5];)
REGISTER_OBJ_MAKER(appended_string,
Array<char>* p = new Array<char>();
p->append(Array<char>());
)
REGISTER_OBJ_MAKER(plain_ptr, int** p = new(initialized) int*;)
REGISTER_OBJ_MAKER(linking_ptr,
int** p = new(initialized) int*;
*p = new(initialized) int;
)
REGISTER_OBJ_MAKER(0_sized, void* p = malloc(0);)
REGISTER_OBJ_MAKER(1_sized, void* p = malloc(1);)
REGISTER_OBJ_MAKER(2_sized, void* p = malloc(2);)
REGISTER_OBJ_MAKER(3_sized, void* p = malloc(3);)
REGISTER_OBJ_MAKER(4_sized, void* p = malloc(4);)
static int set_data[] = { 1, 2, 3, 4, 5, 6, 7, 21, 22, 23, 24, 25, 26, 27 };
static set<int> live_leak_set(set_data, set_data+7);
static const set<int> live_leak_const_set(set_data, set_data+14);
REGISTER_OBJ_MAKER(set,
set<int>* p = new(initialized) set<int>(set_data, set_data + 13);
)
class ClassA {
public:
explicit ClassA(int a) : ptr(NULL) { }
mutable char* ptr;
};
static const ClassA live_leak_mutable(1);
template<class C>
class TClass {
public:
explicit TClass(int a) : ptr(NULL) { }
mutable C val;
mutable C* ptr;
};
static const TClass<Array<char> > live_leak_templ_mutable(1);
class ClassB {
public:
ClassB() { }
char b[7];
virtual void f() { }
virtual ~ClassB() { }
};
class ClassB2 {
public:
ClassB2() { }
char b2[11];
virtual void f2() { }
virtual ~ClassB2() { }
};
class ClassD1 : public ClassB {
char d1[15];
virtual void f() { }
};
class ClassD2 : public ClassB2 {
char d2[19];
virtual void f2() { }
};
class ClassD : public ClassD1, public ClassD2 {
char d[3];
virtual void f() { }
virtual void f2() { }
};
REGISTER_OBJ_MAKER(B, ClassB* p = new(initialized) ClassB;)
REGISTER_OBJ_MAKER(D1, ClassD1* p = new(initialized) ClassD1;)
REGISTER_OBJ_MAKER(D2, ClassD2* p = new(initialized) ClassD2;)
REGISTER_OBJ_MAKER(D, ClassD* p = new(initialized) ClassD;)
REGISTER_OBJ_MAKER(D1_as_B, ClassB* p = new(initialized) ClassD1;)
REGISTER_OBJ_MAKER(D2_as_B2, ClassB2* p = new(initialized) ClassD2;)
REGISTER_OBJ_MAKER(D_as_B, ClassB* p = new(initialized) ClassD;)
REGISTER_OBJ_MAKER(D_as_D1, ClassD1* p = new(initialized) ClassD;)
REGISTER_OBJ_MAKER(D_as_B2, ClassB2* p = new(initialized) ClassD;)
REGISTER_OBJ_MAKER(D_as_D2, ClassD2* p = new(initialized) ClassD;)
class InterfaceA {
public:
virtual void A() = 0;
virtual ~InterfaceA() { }
protected:
InterfaceA() { }
};
class InterfaceB {
public:
virtual void B() = 0;
virtual ~InterfaceB() { }
protected:
InterfaceB() { }
};
class InterfaceC : public InterfaceA {
public:
virtual void C() = 0;
virtual ~InterfaceC() { }
protected:
InterfaceC() { }
};
class ClassMltD1 : public ClassB, public InterfaceB, public InterfaceC {
public:
char d1[11];
virtual void f() { }
virtual void A() { }
virtual void B() { }
virtual void C() { }
};
class ClassMltD2 : public InterfaceA, public InterfaceB, public ClassB {
public:
char d2[15];
virtual void f() { }
virtual void A() { }
virtual void B() { }
};
REGISTER_OBJ_MAKER(MltD1, ClassMltD1* p = new(initialized) ClassMltD1;)
REGISTER_OBJ_MAKER(MltD1_as_B, ClassB* p = new(initialized) ClassMltD1;)
REGISTER_OBJ_MAKER(MltD1_as_IA, InterfaceA* p = new(initialized) ClassMltD1;)
REGISTER_OBJ_MAKER(MltD1_as_IB, InterfaceB* p = new(initialized) ClassMltD1;)
REGISTER_OBJ_MAKER(MltD1_as_IC, InterfaceC* p = new(initialized) ClassMltD1;)
REGISTER_OBJ_MAKER(MltD2, ClassMltD2* p = new(initialized) ClassMltD2;)
REGISTER_OBJ_MAKER(MltD2_as_B, ClassB* p = new(initialized) ClassMltD2;)
REGISTER_OBJ_MAKER(MltD2_as_IA, InterfaceA* p = new(initialized) ClassMltD2;)
REGISTER_OBJ_MAKER(MltD2_as_IB, InterfaceB* p = new(initialized) ClassMltD2;)
REGISTER_OBJ_MAKER(unicode_string,
char* p = new char[sizeof(uint32) * 10];
p += sizeof(uint32);
)
REGISTER_OBJ_MAKER(ref_counted,
char* p = new char[sizeof(int) * 20];
p += sizeof(int);
)
struct Nesting {
struct Inner {
Nesting* parent;
Inner(Nesting* p) : parent(p) {}
};
Inner i0;
char n1[5];
Inner i1;
char n2[11];
Inner i2;
char n3[27];
Inner i3;
Nesting() : i0(this), i1(this), i2(this), i3(this) {}
};
REGISTER_OBJ_MAKER(nesting_i0, Nesting::Inner* p = &((new Nesting())->i0);)
REGISTER_OBJ_MAKER(nesting_i1, Nesting::Inner* p = &((new Nesting())->i1);)
REGISTER_OBJ_MAKER(nesting_i2, Nesting::Inner* p = &((new Nesting())->i2);)
REGISTER_OBJ_MAKER(nesting_i3, Nesting::Inner* p = &((new Nesting())->i3);)
static void TestHeapLeakCheckerLiveness() {
live_leak_mutable.ptr = new(initialized) char[77];
live_leak_templ_mutable.ptr = new(initialized) Array<char>();
live_leak_templ_mutable.val = Array<char>();
TestObjMakers();
}
static void* Mmapper(uintptr_t* addr_after_mmap_call) {
void* r = mmap(NULL, 100, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
void* stack[1];
CHECK_EQ(GetStackTrace(stack, 1, 0), 1);
*addr_after_mmap_call = reinterpret_cast<uintptr_t>(stack[0]);
sleep(0);
return r;
}
static void* (*mmapper_addr)(uintptr_t* addr) = &Mmapper;
static void VerifyMemoryRegionMapStackGet() {
uintptr_t caller_addr_limit;
void* addr = (*mmapper_addr)(&caller_addr_limit);
uintptr_t caller = 0;
{ MemoryRegionMap::LockHolder l;
for (MemoryRegionMap::RegionIterator
i = MemoryRegionMap::BeginRegionLocked();
i != MemoryRegionMap::EndRegionLocked(); ++i) {
if (i->start_addr == reinterpret_cast<uintptr_t>(addr)) {
CHECK_EQ(caller, 0);
caller = i->caller();
}
}
}
if (!(reinterpret_cast<uintptr_t>(mmapper_addr) <= caller &&
caller < caller_addr_limit)) {
LOGF << std::hex << "0x" << caller
<< " does not seem to point into code of function Mmapper at "
<< "0x" << reinterpret_cast<uintptr_t>(mmapper_addr)
<< "! Stack frame collection must be off in MemoryRegionMap!";
LOG(FATAL, "\n");
}
munmap(addr, 100);
}
static void* Mallocer(uintptr_t* addr_after_malloc_call) {
void* r = malloc(100);
sleep(0);
void* stack[1];
CHECK_EQ(GetStackTrace(stack, 1, 0), 1);
*addr_after_malloc_call = reinterpret_cast<uintptr_t>(stack[0]);
return r;
}
static void* (*mallocer_addr)(uintptr_t* addr) = &Mallocer;
extern void VerifyHeapProfileTableStackGet() {
uintptr_t caller_addr_limit;
void* addr = (*mallocer_addr)(&caller_addr_limit);
uintptr_t caller =
reinterpret_cast<uintptr_t>(HeapLeakChecker::GetAllocCaller(addr));
if (!(reinterpret_cast<uintptr_t>(mallocer_addr) <= caller &&
caller < caller_addr_limit)) {
LOGF << std::hex << "0x" << caller
<< " does not seem to point into code of function Mallocer at "
<< "0x" << reinterpret_cast<uintptr_t>(mallocer_addr)
<< "! Stack frame collection must be off in heap profiler!";
LOG(FATAL, "\n");
}
free(addr);
}
static void MakeALeak(void** arr) {
PreventHeapReclaiming(10 * sizeof(int));
void* a = new(initialized) int[10];
Hide(&a);
*arr = a;
}
static int Pass() {
fprintf(stdout, "PASS\n");
g_have_exited_main = true;
return 0;
}
int main(int argc, char** argv) {
run_hidden_ptr = DoRunHidden;
wipe_stack_ptr = DoWipeStack;
if (!HeapLeakChecker::IsActive()) {
CHECK_EQ(FLAGS_heap_check, "");
LOG(WARNING, "HeapLeakChecker got turned off; we won't test much...");
} else {
VerifyMemoryRegionMapStackGet();
VerifyHeapProfileTableStackGet();
}
KeyInit();
TestLibCAllocate();
if (FLAGS_interfering_threads) {
RunHeapBusyThreads();
}
TestLibCAllocate();
LOGF << "In main(): heap_check=" << FLAGS_heap_check << endl;
CHECK(HeapLeakChecker::NoGlobalLeaks());
if (FLAGS_test_leak) {
void* arr;
RunHidden(NewCallback(MakeALeak, &arr));
Use(&arr);
LogHidden("Leaking", arr);
if (FLAGS_test_cancel_global_check) {
HeapLeakChecker::CancelGlobalCheck();
} else {
HeapLeakChecker::NoGlobalLeaks();
HeapLeakChecker::NoGlobalLeaks();
}
return Pass();
}
if (FLAGS_test_loop_leak) {
void* arr1;
void* arr2;
RunHidden(NewCallback(MakeDeathLoop, &arr1, &arr2));
Use(&arr1);
Use(&arr2);
LogHidden("Loop leaking", arr1);
LogHidden("Loop leaking", arr2);
if (FLAGS_test_cancel_global_check) {
HeapLeakChecker::CancelGlobalCheck();
} else {
HeapLeakChecker::NoGlobalLeaks();
HeapLeakChecker::NoGlobalLeaks();
}
return Pass();
}
if (FLAGS_test_register_leak) {
Pause();
for (int i = 0; i < 100; ++i) {
CHECK(HeapLeakChecker::NoGlobalLeaks());
Pause();
}
return Pass();
}
TestHeapLeakCheckerLiveness();
HeapLeakChecker heap_check("all");
TestHiddenPointer();
TestHeapLeakChecker();
Pause();
TestLeakButTotalsMatch();
Pause();
TestHeapLeakCheckerDeathSimple();
Pause();
TestHeapLeakCheckerDeathLoop();
Pause();
TestHeapLeakCheckerDeathInverse();
Pause();
TestHeapLeakCheckerDeathNoLeaks();
Pause();
TestHeapLeakCheckerDeathCountLess();
Pause();
TestHeapLeakCheckerDeathCountMore();
Pause();
TestHeapLeakCheckerDeathTrick();
Pause();
CHECK(HeapLeakChecker::NoGlobalLeaks());
TestHeapLeakCheckerNoFalsePositives();
Pause();
TestHeapLeakCheckerDisabling();
Pause();
TestSTLAlloc();
Pause();
TestSTLAllocInverse();
Pause();
#define DTSL(a) { DirectTestSTLAlloc(a, #a); \
Pause(); }
DTSL(std::allocator<char>());
DTSL(std::allocator<int>());
DTSL(std::string().get_allocator());
DTSL(string().get_allocator());
DTSL(vector<int>().get_allocator());
DTSL(vector<double>().get_allocator());
DTSL(vector<vector<int> >().get_allocator());
DTSL(vector<string>().get_allocator());
DTSL((map<string, string>().get_allocator()));
DTSL((map<string, int>().get_allocator()));
DTSL(set<char>().get_allocator());
#undef DTSL
TestLibCAllocate();
Pause();
CHECK(HeapLeakChecker::NoGlobalLeaks());
Pause();
if (!FLAGS_maybe_stripped) {
CHECK(heap_check.SameHeap());
} else {
WARN_IF(heap_check.SameHeap() != true,
"overall leaks are caught; we must be using a stripped binary");
}
CHECK(HeapLeakChecker::NoGlobalLeaks());
return Pass();
}