| // Copyright (c) 2005, 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. |
| |
| // --- |
| // Author: Maxim Lifantsev |
| // |
| // Running: |
| // ./heap-checker_unittest |
| // |
| // If the unittest crashes because it can't find pprof, try: |
| // PPROF_PATH=/usr/local/someplace/bin/pprof ./heap-checker_unittest |
| // |
| // To test that the whole-program heap checker will actually cause a leak, try: |
| // HEAPCHECK_TEST_LEAK= ./heap-checker_unittest |
| // HEAPCHECK_TEST_LOOP_LEAK= ./heap-checker_unittest |
| // |
| // Note: Both of the above commands *should* abort with an error message. |
| |
| // CAVEAT: Do not use vector<> and string on-heap objects in this test, |
| // otherwise the test can sometimes fail for tricky leak checks |
| // when we want some allocated object not to be found live by the heap checker. |
| // This can happen with memory allocators like tcmalloc that can allocate |
| // heap objects back to back without any book-keeping data in between. |
| // What happens is that end-of-storage pointers of a live vector |
| // (or a string depending on the STL implementation used) |
| // can happen to point to that other heap-allocated |
| // object that is not reachable otherwise and that |
| // we don't want to be reachable. |
| // |
| // The implication of this for real leak checking |
| // is just one more chance for the liveness flood to be inexact |
| // (see the comment in our .h file). |
| |
| #include "config_for_unittests.h" |
| #ifdef HAVE_POLL_H |
| #include <poll.h> |
| #endif |
| #if defined HAVE_STDINT_H |
| #include <stdint.h> // to get uint16_t (ISO naming madness) |
| #elif defined HAVE_INTTYPES_H |
| #include <inttypes.h> // another place uint16_t might be defined |
| #endif |
| #include <sys/types.h> |
| #include <stdlib.h> |
| #include <errno.h> // errno |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> // for sleep(), geteuid() |
| #endif |
| #ifdef HAVE_MMAP |
| #include <sys/mman.h> |
| #endif |
| #include <fcntl.h> // for open(), close() |
| #ifdef HAVE_EXECINFO_H |
| #include <execinfo.h> // backtrace |
| #endif |
| #ifdef HAVE_GRP_H |
| #include <grp.h> // getgrent, getgrnam |
| #endif |
| #ifdef HAVE_PWD_H |
| #include <pwd.h> |
| #endif |
| |
| #include <algorithm> |
| #include <iostream> // for cout |
| #include <iomanip> // for hex |
| #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> |
| |
| // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old |
| // form of the name instead. |
| #ifndef MAP_ANONYMOUS |
| # define MAP_ANONYMOUS MAP_ANON |
| #endif |
| |
| using namespace std; |
| |
| // ========================================================================= // |
| |
| // TODO(maxim): write a shell script to test that these indeed crash us |
| // (i.e. we do detect leaks) |
| // Maybe add more such crash tests. |
| |
| 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"); |
| // TODO(maxim): Chage the default to false |
| // when the standard environment used NTPL threads: |
| // they do not seem to have this problem. |
| DEFINE_bool(no_threads, |
| EnvToBool("HEAP_CHECKER_TEST_NO_THREADS", false), |
| "If we should not use any threads"); |
| // This is used so we can make can_create_leaks_reliably true |
| // for any pthread implementation and test with that. |
| |
| DECLARE_int64(heap_check_max_pointer_offset); // heap-checker.cc |
| DECLARE_string(heap_check); // in heap-checker.cc |
| |
| #define WARN_IF(cond, msg) LOG_IF(WARNING, cond, msg) |
| |
| // This is an evil macro! Be very careful using it... |
| #undef VLOG // and we start by evilling overriding logging.h VLOG |
| #define VLOG(lvl) if (FLAGS_verbose >= (lvl)) cout << "\n" |
| // This is, likewise, evil |
| #define LOGF VLOG(INFO) |
| |
| static void RunHeapBusyThreads(); // below |
| |
| |
| 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); |
| } |
| |
| |
| // Set to true at end of main, so threads know. Not entirely thread-safe!, |
| // but probably good enough. |
| static bool g_have_exited_main = false; |
| |
| // If we can reliably create leaks (i.e. make leaked object |
| // really unreachable from any global data). |
| static bool can_create_leaks_reliably = false; |
| |
| // We use a simple allocation wrapper |
| // to make sure we wipe out the newly allocated objects |
| // in case they still happened to contain some pointer data |
| // accidentally left by the memory allocator. |
| struct Initialized { }; |
| static Initialized initialized; |
| void* operator new(size_t size, const Initialized&) { |
| // Below we use "p = new(initialized) Foo[1];" and "delete[] p;" |
| // instead of "p = new(initialized) Foo;" |
| // when we need to delete an allocated object. |
| 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); // defined below |
| static void WipeStack() { DoWipeStack(20); } |
| |
| static void Pause() { |
| poll(NULL, 0, 77); // time for thread activity in HeapBusyThreadBody |
| |
| // Indirectly test malloc_extension.*: |
| 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(); // e.g. MallocExtension::VerifyAllMemory |
| // can leave pointers to heap objects on stack |
| } |
| |
| // Make gcc think a pointer is "used" |
| template <class T> |
| static void Use(T** foo) { |
| VLOG(2) << "Dummy-using " << static_cast<void*>(*foo) << " at " << foo; |
| } |
| |
| // Arbitrary value, but not such that xor'ing with it is likely |
| // to map one valid pointer to another valid pointer: |
| static const uintptr_t kHideMask = |
| static_cast<uintptr_t>(0xF03A5F7BF03A5F7BLL); |
| |
| // Helpers to hide a pointer from live data traversal. |
| // We just xor the pointer so that (with high probability) |
| // it's not a valid address of a heap object anymore. |
| // Both Hide and UnHide must be executed within RunHidden() below |
| // to prevent leaving stale data on active stack that can be a pointer |
| // to a heap object that is not actually reachable via live variables. |
| // (UnHide might leave heap pointer value for an object |
| // that will be deallocated but later another object |
| // can be allocated at the same heap address.) |
| template <class T> |
| static void Hide(T** ptr) { |
| // we cast values, not dereferenced pointers, so no aliasing issues: |
| *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); |
| // we cast values, not dereferenced pointers, so no aliasing issues: |
| *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; |
| } |
| |
| // volatile to fool the compiler against inlining the calls to these |
| 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); // undo -foptimize-sibling-calls |
| } else { |
| c->Run(); |
| } |
| } |
| |
| /*static*/ 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; |
| (void)arr; |
| (*wipe_stack_ptr)(n-1); |
| sleep(0); // undo -foptimize-sibling-calls |
| } |
| } |
| |
| // This executes closure c several stack frames down from the current one |
| // and then makes an effort to also wipe out the stack data that was used by |
| // the closure. |
| // This way we prevent leak checker from finding any temporary pointers |
| // of the closure execution on the stack and deciding that |
| // these pointers (and the pointed objects) are still live. |
| 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); // use only hidden versions |
| VLOG(2) << "Allocated hidden " << p << " at " << &p; |
| *ptr = p; // assign the hidden versions |
| } |
| |
| static void* AllocHidden(size_t size) { |
| void* r; |
| RunHidden(NewCallback(DoAllocHidden, size, &r)); |
| return r; |
| } |
| |
| static void DoDeAllocHidden(void** ptr) { |
| Use(ptr); // use only hidden versions |
| 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*)); |
| // We can't use malloc_reclaim_memory flag in opt mode as debugallocation.cc |
| // is not used. Instead we allocate a bunch of heap objects that are |
| // of the same size as what we are going to leak to ensure that the object |
| // we are about to leak is not at the same address as some old allocated |
| // and freed object that might still have pointers leading to it. |
| 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)()) { |
| // By default, don't print the 'we detected a leak' message in the |
| // cases we're expecting a leak (we still print when --v is >= 1). |
| // This way, the logging output is less confusing: we only print |
| // "we detected a leak", and how to diagnose it, for *unexpected* leaks. |
| int32 old_FLAGS_verbose = FLAGS_verbose; |
| if (!VLOG_IS_ON(1)) // not on a verbose setting |
| FLAGS_verbose = FATAL; // only log fatal errors |
| 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(); // to help with can_create_leaks_reliably |
| const bool no_leaks = |
| type == NO_LEAKS ? RUN_SILENT(*check, BriefNoLeaks) |
| : RUN_SILENT(*check, BriefSameHeap); |
| if (can_create_leaks_reliably) { |
| // these might still fail occasionally, but it should be very rare |
| 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"); |
| } |
| } |
| |
| // not deallocates |
| 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; |
| } |
| |
| // not deallocates two objects linked together |
| 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); |
| } |
| |
| // deallocates more than allocates |
| 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); |
| } |
| |
| // deallocates more than allocates |
| 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); |
| } |
| |
| // have less objecs |
| 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); |
| } |
| |
| // have more objecs |
| 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()); |
| |
| // Confirm pointer doesn't appear to contain a byte sequence |
| // that == the pointer. We don't really need to test that |
| // the xor trick itself works, as without it nothing in this |
| // test suite would work. See the Hide/Unhide/*Hidden* set |
| // of helper methods. |
| void** rp = reinterpret_cast<void**>(&p); |
| CHECK_NE(foo, *rp); |
| } |
| |
| // simple tests that deallocate what they allocated |
| 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()); |
| } |
| } |
| |
| // no false positives |
| 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()); |
| } |
| } |
| |
| // test that we detect leaks when we have same total # of bytes and |
| // objects, but different individual object sizes |
| 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(); |
| |
| // foo1 and foo2 leaked |
| VerifyLeaks(&check, NO_LEAKS, (280+120)*sizeof(int), 2); |
| |
| DeAllocHidden(&foo1); |
| DeAllocHidden(&foo2); |
| } |
| |
| // no false negatives from pprof |
| 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); |
| // TODO(maxim): use the above if we make pprof work in automated test runs |
| if (!FLAGS_maybe_stripped) { |
| CHECK_EQ(RUN_SILENT(check, SameHeap), false); |
| // pprof checking should catch the leak |
| } 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); |
| } |
| |
| // simple leak |
| static void TransLeaks() { |
| AllocHidden(1 * sizeof(char)); |
| } |
| |
| // range-based disabling using Disabler |
| static void ScopedDisabledLeaks() { |
| HeapLeakChecker::Disabler disabler; |
| AllocHidden(3 * sizeof(int)); |
| TransLeaks(); |
| (void)malloc(10); // Direct leak |
| } |
| |
| // have different disabled leaks |
| static void* RunDisabledLeaks(void* a) { |
| ScopedDisabledLeaks(); |
| return a; |
| } |
| |
| // have different disabled leaks inside of a thread |
| 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); |
| } |
| |
| // different disabled leaks (some in threads) |
| 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; |
| } |
| |
| // Check that normal STL usage does not result in a leak report. |
| // (In particular we test that there's no complex STL's own allocator |
| // running on top of our allocator with hooks to heap profiler |
| // that can result in false leak report in this case.) |
| 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; |
| } |
| |
| // Check that normal leaked STL usage *does* result in a leak report. |
| // (In particular we test that there's no complex STL's own allocator |
| // running on top of our allocator with hooks to heap profiler |
| // that can result in false absence of leak report in this case.) |
| static void TestSTLAllocInverse() { |
| HeapLeakChecker check("death_inverse_stl"); |
| IntSet* x; |
| RunHidden(NewCallback(DoTestSTLAllocInverse, &x)); |
| LogHidden("Leaking", x); |
| if (can_create_leaks_reliably) { |
| WipeStack(); // to help with can_create_leaks_reliably |
| // these might still fail occasionally, but it should be very rare |
| CHECK_EQ(RUN_SILENT(check, BriefNoLeaks), false); |
| CHECK_GE(check.BytesLeaked(), 100 * sizeof(int)); |
| CHECK_GE(check.ObjectsLeaked(), 100); |
| // assumes set<>s are represented by some kind of binary tree |
| // or something else allocating >=1 heap object per set object |
| } 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); |
| // This will crash if p is not known to heap profiler: |
| // (i.e. STL's "allocator" does not have a direct hook to heap profiler) |
| 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()); // just in case |
| } |
| |
| 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; // needed for a sanity-check |
| } |
| |
| // force various C library static and thread-specific allocations |
| 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) { |
| // Test-logging inside threads which (potentially) creates and uses |
| // thread-local data inside standard C++ library: |
| 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(); // a race condition here is okay |
| getgrnam(grp->gr_name); |
| #endif |
| #ifdef HAVE_PWD_H |
| getpwuid(geteuid()); |
| #endif |
| } |
| |
| // Continuous random heap memory activity to try to disrupt heap checking. |
| 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; |
| // Try to hide ptr from heap checker in a CPU register: |
| // Here we are just making a best effort to put the only pointer |
| // to a heap object into a thread register to test |
| // the thread-register finding machinery in the heap checker. |
| #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) { |
| // TestLibCAllocate() calls libc functions that don't work so well |
| // after main() has exited. So we just don't do the test then. |
| 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) { |
| // Hide the register "ptr" value with an xor mask. |
| // If one provides --test_register_leak flag, the test should |
| // (with very high probability) crash on some leak check |
| // with a leak report (of some x * sizeof(int) + y * sizeof(int*) bytes) |
| // pointing at the two lines above in this function |
| // with "new(initialized) int" in them as the allocators |
| // of the leaked objects. |
| // CAVEAT: We can't really prevent a compiler to save some |
| // temporary values of "ptr" on the stack and thus let us find |
| // the heap objects not via the register. |
| // Hence it's normal if for certain compilers or optimization modes |
| // --test_register_leak does not cause a leak crash of the above form |
| // (this happens e.g. for gcc 4.0.1 in opt mode). |
| ptr = reinterpret_cast<int **>( |
| reinterpret_cast<uintptr_t>(ptr) ^ kHideMask); |
| // busy loop to get the thread interrupted at: |
| 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; // make many threads |
| |
| pthread_t tid; |
| pthread_attr_t attr; |
| CHECK_EQ(pthread_attr_init(&attr), 0); |
| // make them and let them run |
| 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(); |
| } |
| |
| // ========================================================================= // |
| |
| // This code section is to test that objects that are reachable from global |
| // variables are not reported as leaks |
| // as well as that (Un)IgnoreObject work for such objects fine. |
| |
| // An object making functions: |
| // returns a "weird" pointer to a new object for which |
| // it's worth checking that the object is reachable via that pointer. |
| typedef void* (*ObjMakerFunc)(); |
| static list<ObjMakerFunc> obj_makers; // list of registered object makers |
| |
| // Helper macro to register an object making function |
| // 'name' is an identifier of this object maker, |
| // 'body' is its function body that must declare |
| // pointer 'p' to the nex object to return. |
| // Usage example: |
| // REGISTER_OBJ_MAKER(trivial, int* p = new(initialized) int;) |
| #define REGISTER_OBJ_MAKER(name, body) \ |
| void* ObjMaker_##name##_() { \ |
| VLOG(1) << "Obj making " << #name; \ |
| body; \ |
| return p; \ |
| } \ |
| static ObjMakerRegistrar maker_reg_##name##__(&ObjMaker_##name##_); |
| // helper class for REGISTER_OBJ_MAKER |
| struct ObjMakerRegistrar { |
| ObjMakerRegistrar(ObjMakerFunc obj_maker) { obj_makers.push_back(obj_maker); } |
| }; |
| |
| // List of the objects/pointers made with all the obj_makers |
| // to test reachability via global data pointers during leak checks. |
| static list<void*>* live_objects = new list<void*>; |
| // pointer so that it does not get destructed on exit |
| |
| // Exerciser for one ObjMakerFunc. |
| static void TestPointerReach(ObjMakerFunc obj_maker) { |
| HeapLeakChecker::IgnoreObject(obj_maker()); // test IgnoreObject |
| |
| void* obj = obj_maker(); |
| HeapLeakChecker::IgnoreObject(obj); |
| HeapLeakChecker::UnIgnoreObject(obj); // test UnIgnoreObject |
| HeapLeakChecker::IgnoreObject(obj); // not to need deletion for obj |
| |
| live_objects->push_back(obj_maker()); // test reachability at leak check |
| } |
| |
| // Test all ObjMakerFunc registred via REGISTER_OBJ_MAKER. |
| static void TestObjMakers() { |
| for (list<ObjMakerFunc>::const_iterator i = obj_makers.begin(); |
| i != obj_makers.end(); ++i) { |
| TestPointerReach(*i); |
| TestPointerReach(*i); // a couple more times would not hurt |
| TestPointerReach(*i); |
| } |
| } |
| |
| // A dummy class to mimic allocation behavior of string-s. |
| 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; |
| }; |
| |
| // to test pointers to objects, built-in arrays, string, etc: |
| 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; |
| ) |
| |
| // small objects: |
| REGISTER_OBJ_MAKER(0_sized, void* p = malloc(0);) // 0-sized object (important) |
| 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() { } |
| }; |
| |
| // to test pointers to objects of base subclasses: |
| |
| 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;) |
| // inside-object pointers: |
| 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() { } |
| }; |
| |
| // to specifically test heap reachability under |
| // inerface-only multiple inheritance (some use inside-object pointers): |
| 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;) |
| |
| // to mimic UnicodeString defined in third_party/icu, |
| // which store a platform-independent-sized refcount in the first |
| // few bytes and keeps a pointer pointing behind the refcount. |
| REGISTER_OBJ_MAKER(unicode_string, |
| char* p = new char[sizeof(uint32) * 10]; |
| p += sizeof(uint32); |
| ) |
| // similar, but for platform-dependent-sized refcount |
| 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) {} |
| }; |
| |
| // to test inside-object pointers pointing at objects nested into heap objects: |
| 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);) |
| |
| // allocate many objects reachable from global data |
| 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(); |
| } |
| |
| // ========================================================================= // |
| |
| // Get address (PC value) following the mmap call into addr_after_mmap_call |
| static void* Mmapper(uintptr_t* addr_after_mmap_call) { |
| void* r = mmap(NULL, 100, PROT_READ|PROT_WRITE, |
| MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); |
| // Get current PC value into addr_after_mmap_call |
| void* stack[1]; |
| CHECK_EQ(GetStackTrace(stack, 1, 0), 1); |
| *addr_after_mmap_call = reinterpret_cast<uintptr_t>(stack[0]); |
| sleep(0); // undo -foptimize-sibling-calls |
| return r; |
| } |
| |
| // to trick complier into preventing inlining |
| static void* (*mmapper_addr)(uintptr_t* addr) = &Mmapper; |
| |
| // TODO(maxim): copy/move this to memory_region_map_unittest |
| // TODO(maxim): expand this test to include mmap64, mremap and sbrk calls. |
| 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(); |
| } |
| } |
| } |
| // caller must point into Mmapper function: |
| 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); // undo -foptimize-sibling-calls |
| // Get current PC value into addr_after_malloc_call |
| void* stack[1]; |
| CHECK_EQ(GetStackTrace(stack, 1, 0), 1); |
| *addr_after_malloc_call = reinterpret_cast<uintptr_t>(stack[0]); |
| return r; |
| } |
| |
| // to trick complier into preventing inlining |
| static void* (*mallocer_addr)(uintptr_t* addr) = &Mallocer; |
| |
| // non-static for friendship with HeapProfiler |
| // TODO(maxim): expand this test to include |
| // realloc, calloc, memalign, valloc, pvalloc, new, and new[]. |
| 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)); |
| // caller must point into Mallocer function: |
| 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; |
| } |
| |
| // Helper to do 'return 0;' inside main(): insted we do 'return Pass();' |
| 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(); |
| |
| // glibc 2.4, on x86_64 at least, has a lock-ordering bug, which |
| // means deadlock is possible when one thread calls dl_open at the |
| // same time another thread is calling dl_iterate_phdr. libunwind |
| // calls dl_iterate_phdr, and TestLibCAllocate calls dl_open (or the |
| // various syscalls in it do), at least the first time it's run. |
| // To avoid the deadlock, we run TestLibCAllocate once before getting |
| // multi-threaded. |
| // TODO(csilvers): once libc is fixed, or libunwind can work around it, |
| // get rid of this early call. We *want* our test to |
| // find potential problems like this one! |
| TestLibCAllocate(); |
| |
| if (FLAGS_interfering_threads) { |
| RunHeapBusyThreads(); // add interference early |
| } |
| TestLibCAllocate(); |
| |
| LOGF << "In main(): heap_check=" << FLAGS_heap_check << endl; |
| |
| CHECK(HeapLeakChecker::NoGlobalLeaks()); // so far, so good |
| |
| if (FLAGS_test_leak) { |
| void* arr; |
| RunHidden(NewCallback(MakeALeak, &arr)); |
| Use(&arr); |
| LogHidden("Leaking", arr); |
| if (FLAGS_test_cancel_global_check) { |
| HeapLeakChecker::CancelGlobalCheck(); |
| } else { |
| // Verify we can call NoGlobalLeaks repeatedly without deadlocking |
| HeapLeakChecker::NoGlobalLeaks(); |
| HeapLeakChecker::NoGlobalLeaks(); |
| } |
| return Pass(); |
| // whole-program leak-check should (with very high probability) |
| // catch the leak of arr (10 * sizeof(int) bytes) |
| // (when !FLAGS_test_cancel_global_check) |
| } |
| |
| 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 { |
| // Verify we can call NoGlobalLeaks repeatedly without deadlocking |
| HeapLeakChecker::NoGlobalLeaks(); |
| HeapLeakChecker::NoGlobalLeaks(); |
| } |
| return Pass(); |
| // whole-program leak-check should (with very high probability) |
| // catch the leak of arr1 and arr2 (4 * sizeof(void*) bytes) |
| // (when !FLAGS_test_cancel_global_check) |
| } |
| |
| if (FLAGS_test_register_leak) { |
| // make us fail only where the .sh test expects: |
| Pause(); |
| for (int i = 0; i < 100; ++i) { // give it some time to crash |
| 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()); // so far, so good |
| |
| TestHeapLeakCheckerNoFalsePositives(); |
| Pause(); |
| |
| TestHeapLeakCheckerDisabling(); |
| Pause(); |
| |
| TestSTLAlloc(); |
| Pause(); |
| TestSTLAllocInverse(); |
| Pause(); |
| |
| // Test that various STL allocators work. Some of these are redundant, but |
| // we don't know how STL might change in the future. For example, |
| // http://wiki/Main/StringNeStdString. |
| #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()); // so far, so good |
| |
| 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()); // so far, so good |
| |
| return Pass(); |
| } |