1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432 |
- // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
- // 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.
- // ---
- // Unittest for the TCMalloc implementation.
- //
- // * The test consists of a set of threads.
- // * Each thread maintains a set of allocated objects, with
- // a bound on the total amount of data in the set.
- // * Each allocated object's contents are generated by
- // hashing the object pointer, and a generation count
- // in the object. This allows us to easily check for
- // data corruption.
- // * At any given step, the thread can do any of the following:
- // a. Allocate an object
- // b. Increment an object's generation count and update
- // its contents.
- // c. Pass the object to another thread
- // d. Free an object
- // Also, at the end of every step, object(s) are freed to maintain
- // the memory upper-bound.
- //
- // If this test is compiled with -DDEBUGALLOCATION, then we don't
- // run some tests that test the inner workings of tcmalloc and
- // break on debugallocation: that certain allocations are aligned
- // in a certain way (even though no standard requires it), and that
- // realloc() tries to minimize copying (which debug allocators don't
- // care about).
- #include "config_for_unittests.h"
- // Complicated ordering requirements. tcmalloc.h defines (indirectly)
- // _POSIX_C_SOURCE, which it needs so stdlib.h defines posix_memalign.
- // unistd.h, on the other hand, requires _POSIX_C_SOURCE to be unset,
- // at least on FreeBSD, in order to define sbrk. The solution
- // is to #include unistd.h first. This is safe because unistd.h
- // doesn't sub-include stdlib.h, so we'll still get posix_memalign
- // when we #include stdlib.h. Blah.
- #ifdef HAVE_UNISTD_H
- #include <unistd.h> // for testing sbrk hooks
- #endif
- #include "tcmalloc.h" // must come early, to pick up posix_memalign
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #if defined HAVE_STDINT_H
- #include <stdint.h> // for intptr_t
- #endif
- #include <sys/types.h> // for size_t
- #ifdef HAVE_FCNTL_H
- #include <fcntl.h> // for open; used with mmap-hook test
- #endif
- #ifdef HAVE_MMAP
- #include <sys/mman.h> // for testing mmap hooks
- #endif
- #ifdef HAVE_MALLOC_H
- #include <malloc.h> // defines pvalloc/etc on cygwin
- #endif
- #include <assert.h>
- #include <vector>
- #include <algorithm>
- #include <string>
- #include <new>
- #include "base/logging.h"
- #include "base/simple_mutex.h"
- #include "gperftools/malloc_hook.h"
- #include "gperftools/malloc_extension.h"
- #include "gperftools/tcmalloc.h"
- #include "thread_cache.h"
- #include "system-alloc.h"
- #include "tests/testutil.h"
- // Windows doesn't define pvalloc and a few other obsolete unix
- // functions; nor does it define posix_memalign (which is not obsolete).
- #if defined(_WIN32)
- # define cfree free // don't bother to try to test these obsolete fns
- # define valloc malloc
- # define pvalloc malloc
- // I'd like to map posix_memalign to _aligned_malloc, but _aligned_malloc
- // must be paired with _aligned_free (not normal free), which is too
- // invasive a change to how we allocate memory here. So just bail
- static bool kOSSupportsMemalign = false;
- static inline void* Memalign(size_t align, size_t size) {
- //LOG(FATAL) << "memalign not supported on windows";
- exit(1);
- return NULL;
- }
- static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
- //LOG(FATAL) << "posix_memalign not supported on windows";
- exit(1);
- return -1;
- }
- // OS X defines posix_memalign in some OS versions but not others;
- // it's confusing enough to check that it's easiest to just not to test.
- #elif defined(__APPLE__)
- static bool kOSSupportsMemalign = false;
- static inline void* Memalign(size_t align, size_t size) {
- //LOG(FATAL) << "memalign not supported on OS X";
- exit(1);
- return NULL;
- }
- static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
- //LOG(FATAL) << "posix_memalign not supported on OS X";
- exit(1);
- return -1;
- }
- #else
- static bool kOSSupportsMemalign = true;
- static inline void* Memalign(size_t align, size_t size) {
- return memalign(align, size);
- }
- static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
- return posix_memalign(ptr, align, size);
- }
- #endif
- // 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
- #define LOGSTREAM stdout
- using std::vector;
- using std::string;
- DECLARE_double(tcmalloc_release_rate);
- DECLARE_int32(max_free_queue_size); // in debugallocation.cc
- DECLARE_int64(tcmalloc_sample_parameter);
- namespace testing {
- static const int FLAGS_numtests = 50000;
- static const int FLAGS_log_every_n_tests = 50000; // log exactly once
- // Testing parameters
- static const int FLAGS_lgmaxsize = 16; // lg() of the max size object to alloc
- static const int FLAGS_numthreads = 10; // Number of threads
- static const int FLAGS_threadmb = 4; // Max memory size allocated by thread
- static const int FLAGS_lg_max_memalign = 18; // lg of max alignment for memalign
- static const double FLAGS_memalign_min_fraction = 0; // min expected%
- static const double FLAGS_memalign_max_fraction = 0.4; // max expected%
- static const double FLAGS_memalign_max_alignment_ratio = 6; // alignment/size
- // Weights of different operations
- static const int FLAGS_allocweight = 50; // Weight for picking allocation
- static const int FLAGS_freeweight = 50; // Weight for picking free
- static const int FLAGS_updateweight = 10; // Weight for picking update
- static const int FLAGS_passweight = 1; // Weight for passing object
- static const int kSizeBits = 8 * sizeof(size_t);
- static const size_t kMaxSize = ~static_cast<size_t>(0);
- static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1);
- static const size_t kNotTooBig = 100000;
- // We want an allocation that is definitely more than main memory. OS
- // X has special logic to discard very big allocs before even passing
- // the request along to the user-defined memory allocator; we're not
- // interested in testing their logic, so we have to make sure we're
- // not *too* big.
- static const size_t kTooBig = kMaxSize - 100000;
- static int news_handled = 0;
- // Global array of threads
- class TesterThread;
- static TesterThread** threads;
- // To help with generating random numbers
- class TestHarness {
- private:
- // Information kept per type
- struct Type {
- string name;
- int type;
- int weight;
- };
- public:
- TestHarness(int seed)
- : types_(new vector<Type>), total_weight_(0), num_tests_(0) {
- srandom(seed);
- }
- ~TestHarness() {
- delete types_;
- }
- // Add operation type with specified weight. When starting a new
- // iteration, an operation type is picked with probability
- // proportional to its weight.
- //
- // "type" must be non-negative.
- // "weight" must be non-negative.
- void AddType(int type, int weight, const char* name);
- // Call this to get the type of operation for the next iteration.
- // It returns a random operation type from the set of registered
- // operations. Returns -1 if tests should finish.
- int PickType();
- // If n == 0, returns the next pseudo-random number in the range [0 .. 0]
- // If n != 0, returns the next pseudo-random number in the range [0 .. n)
- int Uniform(int n) {
- if (n == 0) {
- return random() * 0;
- } else {
- return random() % n;
- }
- }
- // Pick "base" uniformly from range [0,max_log] and then return
- // "base" random bits. The effect is to pick a number in the range
- // [0,2^max_log-1] with bias towards smaller numbers.
- int Skewed(int max_log) {
- const int base = random() % (max_log+1);
- return random() % (1 << base);
- }
- private:
- vector<Type>* types_; // Registered types
- int total_weight_; // Total weight of all types
- int num_tests_; // Num tests run so far
- };
- void TestHarness::AddType(int type, int weight, const char* name) {
- Type t;
- t.name = name;
- t.type = type;
- t.weight = weight;
- types_->push_back(t);
- total_weight_ += weight;
- }
- int TestHarness::PickType() {
- if (num_tests_ >= FLAGS_numtests) return -1;
- num_tests_++;
- assert(total_weight_ > 0);
- // This is a little skewed if total_weight_ doesn't divide 2^31, but it's close
- int v = Uniform(total_weight_);
- int i;
- for (i = 0; i < types_->size(); i++) {
- v -= (*types_)[i].weight;
- if (v < 0) {
- break;
- }
- }
- assert(i < types_->size());
- if ((num_tests_ % FLAGS_log_every_n_tests) == 0) {
- fprintf(LOGSTREAM, " Test %d out of %d: %s\n",
- num_tests_, FLAGS_numtests, (*types_)[i].name.c_str());
- }
- return (*types_)[i].type;
- }
- class AllocatorState : public TestHarness {
- public:
- explicit AllocatorState(int seed) : TestHarness(seed), memalign_fraction_(0) {
- if (kOSSupportsMemalign) {
- CHECK_GE(FLAGS_memalign_max_fraction, 0);
- CHECK_LE(FLAGS_memalign_max_fraction, 1);
- CHECK_GE(FLAGS_memalign_min_fraction, 0);
- CHECK_LE(FLAGS_memalign_min_fraction, 1);
- double delta = FLAGS_memalign_max_fraction - FLAGS_memalign_min_fraction;
- CHECK_GE(delta, 0);
- memalign_fraction_ = (Uniform(10000)/10000.0 * delta +
- FLAGS_memalign_min_fraction);
- //fprintf(LOGSTREAM, "memalign fraction: %f\n", memalign_fraction_);
- }
- }
- virtual ~AllocatorState() {}
- // Allocate memory. Randomly choose between malloc() or posix_memalign().
- void* alloc(size_t size) {
- if (Uniform(100) < memalign_fraction_ * 100) {
- // Try a few times to find a reasonable alignment, or fall back on malloc.
- for (int i = 0; i < 5; i++) {
- size_t alignment = 1 << Uniform(FLAGS_lg_max_memalign);
- if (alignment >= sizeof(intptr_t) &&
- (size < sizeof(intptr_t) ||
- alignment < FLAGS_memalign_max_alignment_ratio * size)) {
- void *result = reinterpret_cast<void*>(static_cast<intptr_t>(0x1234));
- int err = PosixMemalign(&result, alignment, size);
- if (err != 0) {
- CHECK_EQ(err, ENOMEM);
- }
- return err == 0 ? result : NULL;
- }
- }
- }
- return malloc(size);
- }
- private:
- double memalign_fraction_;
- };
- // Info kept per thread
- class TesterThread {
- private:
- // Info kept per allocated object
- struct Object {
- char* ptr; // Allocated pointer
- int size; // Allocated size
- int generation; // Generation counter of object contents
- };
- Mutex lock_; // For passing in another thread's obj
- int id_; // My thread id
- AllocatorState rnd_; // For generating random numbers
- vector<Object> heap_; // This thread's heap
- vector<Object> passed_; // Pending objects passed from others
- size_t heap_size_; // Current heap size
- int locks_ok_; // Number of OK TryLock() ops
- int locks_failed_; // Number of failed TryLock() ops
- // Type of operations
- enum Type { ALLOC, FREE, UPDATE, PASS };
- // ACM minimal standard random number generator. (re-entrant.)
- class ACMRandom {
- int32 seed_;
- public:
- explicit ACMRandom(int32 seed) { seed_ = seed; }
- int32 Next() {
- const int32 M = 2147483647L; // 2^31-1
- const int32 A = 16807;
- // In effect, we are computing seed_ = (seed_ * A) % M, where M = 2^31-1
- uint32 lo = A * (int32)(seed_ & 0xFFFF);
- uint32 hi = A * (int32)((uint32)seed_ >> 16);
- lo += (hi & 0x7FFF) << 16;
- if (lo > M) {
- lo &= M;
- ++lo;
- }
- lo += hi >> 15;
- if (lo > M) {
- lo &= M;
- ++lo;
- }
- return (seed_ = (int32) lo);
- }
- };
- public:
- TesterThread(int id)
- : id_(id),
- rnd_(id+1),
- heap_size_(0),
- locks_ok_(0),
- locks_failed_(0) {
- }
- virtual ~TesterThread() {
- if (FLAGS_verbose)
- fprintf(LOGSTREAM, "Thread %2d: locks %6d ok; %6d trylocks failed\n",
- id_, locks_ok_, locks_failed_);
- if (locks_ok_ + locks_failed_ >= 1000) {
- CHECK_LE(locks_failed_, locks_ok_ / 2);
- }
- }
- virtual void Run() {
- rnd_.AddType(ALLOC, FLAGS_allocweight, "allocate");
- rnd_.AddType(FREE, FLAGS_freeweight, "free");
- rnd_.AddType(UPDATE, FLAGS_updateweight, "update");
- rnd_.AddType(PASS, FLAGS_passweight, "pass");
- while (true) {
- AcquirePassedObjects();
- switch (rnd_.PickType()) {
- case ALLOC: AllocateObject(); break;
- case FREE: FreeObject(); break;
- case UPDATE: UpdateObject(); break;
- case PASS: PassObject(); break;
- case -1: goto done;
- default: assert(NULL == "Unknown type");
- }
- ShrinkHeap();
- }
- done:
- DeleteHeap();
- }
- // Allocate a new object
- void AllocateObject() {
- Object object;
- object.size = rnd_.Skewed(FLAGS_lgmaxsize);
- object.ptr = static_cast<char*>(rnd_.alloc(object.size));
- CHECK(object.ptr);
- object.generation = 0;
- FillContents(&object);
- heap_.push_back(object);
- heap_size_ += object.size;
- }
- // Mutate a random object
- void UpdateObject() {
- if (heap_.empty()) return;
- const int index = rnd_.Uniform(heap_.size());
- CheckContents(heap_[index]);
- heap_[index].generation++;
- FillContents(&heap_[index]);
- }
- // Free a random object
- void FreeObject() {
- if (heap_.empty()) return;
- const int index = rnd_.Uniform(heap_.size());
- Object object = heap_[index];
- CheckContents(object);
- free(object.ptr);
- heap_size_ -= object.size;
- heap_[index] = heap_[heap_.size()-1];
- heap_.pop_back();
- }
- // Delete all objects in the heap
- void DeleteHeap() {
- while (!heap_.empty()) {
- FreeObject();
- }
- }
- // Free objects until our heap is small enough
- void ShrinkHeap() {
- while (heap_size_ > FLAGS_threadmb << 20) {
- assert(!heap_.empty());
- FreeObject();
- }
- }
- // Pass a random object to another thread
- void PassObject() {
- // Pick object to pass
- if (heap_.empty()) return;
- const int index = rnd_.Uniform(heap_.size());
- Object object = heap_[index];
- CheckContents(object);
- // Pick thread to pass
- const int tid = rnd_.Uniform(FLAGS_numthreads);
- TesterThread* thread = threads[tid];
- if (thread->lock_.TryLock()) {
- // Pass the object
- locks_ok_++;
- thread->passed_.push_back(object);
- thread->lock_.Unlock();
- heap_size_ -= object.size;
- heap_[index] = heap_[heap_.size()-1];
- heap_.pop_back();
- } else {
- locks_failed_++;
- }
- }
- // Grab any objects passed to this thread by another thread
- void AcquirePassedObjects() {
- // We do not create unnecessary contention by always using
- // TryLock(). Plus we unlock immediately after swapping passed
- // objects into a local vector.
- vector<Object> copy;
- { // Locking scope
- if (!lock_.TryLock()) {
- locks_failed_++;
- return;
- }
- locks_ok_++;
- swap(copy, passed_);
- lock_.Unlock();
- }
- for (int i = 0; i < copy.size(); ++i) {
- const Object& object = copy[i];
- CheckContents(object);
- heap_.push_back(object);
- heap_size_ += object.size;
- }
- }
- // Fill object contents according to ptr/generation
- void FillContents(Object* object) {
- ACMRandom r(reinterpret_cast<intptr_t>(object->ptr) & 0x7fffffff);
- for (int i = 0; i < object->generation; ++i) {
- r.Next();
- }
- const char c = static_cast<char>(r.Next());
- memset(object->ptr, c, object->size);
- }
- // Check object contents
- void CheckContents(const Object& object) {
- ACMRandom r(reinterpret_cast<intptr_t>(object.ptr) & 0x7fffffff);
- for (int i = 0; i < object.generation; ++i) {
- r.Next();
- }
- // For large objects, we just check a prefix/suffix
- const char expected = static_cast<char>(r.Next());
- const int limit1 = object.size < 32 ? object.size : 32;
- const int start2 = limit1 > object.size - 32 ? limit1 : object.size - 32;
- for (int i = 0; i < limit1; ++i) {
- CHECK_EQ(object.ptr[i], expected);
- }
- for (int i = start2; i < object.size; ++i) {
- CHECK_EQ(object.ptr[i], expected);
- }
- }
- };
- static void RunThread(int thread_id) {
- threads[thread_id]->Run();
- }
- static void TryHugeAllocation(size_t s, AllocatorState* rnd) {
- void* p = rnd->alloc(s);
- CHECK(p == NULL); // huge allocation s should fail!
- }
- static void TestHugeAllocations(AllocatorState* rnd) {
- // Check that asking for stuff tiny bit smaller than largest possible
- // size returns NULL.
- for (size_t i = 0; i < 70000; i += rnd->Uniform(20)) {
- TryHugeAllocation(kMaxSize - i, rnd);
- }
- // Asking for memory sizes near signed/unsigned boundary (kMaxSignedSize)
- // might work or not, depending on the amount of virtual memory.
- #ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs
- for (size_t i = 0; i < 100; i++) {
- void* p = NULL;
- p = rnd->alloc(kMaxSignedSize + i);
- if (p) free(p); // if: free(NULL) is not necessarily defined
- p = rnd->alloc(kMaxSignedSize - i);
- if (p) free(p);
- }
- #endif
- // Check that ReleaseFreeMemory has no visible effect (aka, does not
- // crash the test):
- MallocExtension* inst = MallocExtension::instance();
- CHECK(inst);
- inst->ReleaseFreeMemory();
- }
- static void TestCalloc(size_t n, size_t s, bool ok) {
- char* p = reinterpret_cast<char*>(calloc(n, s));
- if (FLAGS_verbose)
- fprintf(LOGSTREAM, "calloc(%" PRIxS ", %" PRIxS "): %p\n", n, s, p);
- if (!ok) {
- CHECK(p == NULL); // calloc(n, s) should not succeed
- } else {
- CHECK(p != NULL); // calloc(n, s) should succeed
- for (int i = 0; i < n*s; i++) {
- CHECK(p[i] == '\0');
- }
- free(p);
- }
- }
- // This makes sure that reallocing a small number of bytes in either
- // direction doesn't cause us to allocate new memory.
- static void TestRealloc() {
- #ifndef DEBUGALLOCATION // debug alloc doesn't try to minimize reallocs
- // When sampling, we always allocate in units of page-size, which
- // makes reallocs of small sizes do extra work (thus, failing these
- // checks). Since sampling is random, we turn off sampling to make
- // sure that doesn't happen to us here.
- const int64 old_sample_parameter = FLAGS_tcmalloc_sample_parameter;
- FLAGS_tcmalloc_sample_parameter = 0; // turn off sampling
- int start_sizes[] = { 100, 1000, 10000, 100000 };
- int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 };
- for (int s = 0; s < sizeof(start_sizes)/sizeof(*start_sizes); ++s) {
- void* p = malloc(start_sizes[s]);
- CHECK(p);
- // The larger the start-size, the larger the non-reallocing delta.
- for (int d = 0; d < (s+1) * 2; ++d) {
- void* new_p = realloc(p, start_sizes[s] + deltas[d]);
- CHECK(p == new_p); // realloc should not allocate new memory
- }
- // Test again, but this time reallocing smaller first.
- for (int d = 0; d < s*2; ++d) {
- void* new_p = realloc(p, start_sizes[s] - deltas[d]);
- CHECK(p == new_p); // realloc should not allocate new memory
- }
- free(p);
- }
- FLAGS_tcmalloc_sample_parameter = old_sample_parameter;
- #endif
- }
- static void TestNewHandler() throw (std::bad_alloc) {
- ++news_handled;
- throw std::bad_alloc();
- }
- static void TestOneNew(void* (*func)(size_t)) {
- // success test
- try {
- void* ptr = (*func)(kNotTooBig);
- if (0 == ptr) {
- fprintf(LOGSTREAM, "allocation should not have failed.\n");
- abort();
- }
- } catch (...) {
- fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
- abort();
- }
- // failure test
- // we should always receive a bad_alloc exception
- try {
- (*func)(kTooBig);
- fprintf(LOGSTREAM, "allocation should have failed.\n");
- abort();
- } catch (const std::bad_alloc&) {
- // correct
- } catch (...) {
- fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
- abort();
- }
- }
- static void TestNew(void* (*func)(size_t)) {
- news_handled = 0;
- // test without new_handler:
- std::new_handler saved_handler = std::set_new_handler(0);
- TestOneNew(func);
- // test with new_handler:
- std::set_new_handler(TestNewHandler);
- TestOneNew(func);
- if (news_handled != 1) {
- fprintf(LOGSTREAM, "new_handler was not called.\n");
- abort();
- }
- std::set_new_handler(saved_handler);
- }
- static void TestOneNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) {
- // success test
- try {
- void* ptr = (*func)(kNotTooBig, std::nothrow);
- if (0 == ptr) {
- fprintf(LOGSTREAM, "allocation should not have failed.\n");
- abort();
- }
- } catch (...) {
- fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
- abort();
- }
- // failure test
- // we should always receive a bad_alloc exception
- try {
- if ((*func)(kTooBig, std::nothrow) != 0) {
- fprintf(LOGSTREAM, "allocation should have failed.\n");
- abort();
- }
- } catch (...) {
- fprintf(LOGSTREAM, "nothrow allocation threw unexpected exception.\n");
- abort();
- }
- }
- static void TestNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) {
- news_handled = 0;
- // test without new_handler:
- std::new_handler saved_handler = std::set_new_handler(0);
- TestOneNothrowNew(func);
- // test with new_handler:
- std::set_new_handler(TestNewHandler);
- TestOneNothrowNew(func);
- if (news_handled != 1) {
- fprintf(LOGSTREAM, "nothrow new_handler was not called.\n");
- abort();
- }
- std::set_new_handler(saved_handler);
- }
- // These are used as callbacks by the sanity-check. Set* and Reset*
- // register the hook that counts how many times the associated memory
- // function is called. After each such call, call Verify* to verify
- // that we used the tcmalloc version of the call, and not the libc.
- // Note the ... in the hook signature: we don't care what arguments
- // the hook takes.
- #define MAKE_HOOK_CALLBACK(hook_type, ...) \
- static volatile int g_##hook_type##_calls = 0; \
- static void IncrementCallsTo##hook_type(__VA_ARGS__) { \
- g_##hook_type##_calls++; \
- } \
- static void Verify##hook_type##WasCalled() { \
- CHECK_GT(g_##hook_type##_calls, 0); \
- g_##hook_type##_calls = 0; /* reset for next call */ \
- } \
- static void Set##hook_type() { \
- CHECK(MallocHook::Add##hook_type( \
- (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
- } \
- static void Reset##hook_type() { \
- CHECK(MallocHook::Remove##hook_type( \
- (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
- }
- // We do one for each hook typedef in malloc_hook.h
- MAKE_HOOK_CALLBACK(NewHook, const void*, size_t);
- MAKE_HOOK_CALLBACK(DeleteHook, const void*);
- MAKE_HOOK_CALLBACK(MmapHook, const void*, const void*, size_t, int, int, int,
- off_t);
- MAKE_HOOK_CALLBACK(MremapHook, const void*, const void*, size_t, size_t, int,
- const void*);
- MAKE_HOOK_CALLBACK(MunmapHook, const void *, size_t);
- MAKE_HOOK_CALLBACK(SbrkHook, const void *, ptrdiff_t);
- static void TestAlignmentForSize(int size) {
- fprintf(LOGSTREAM, "Testing alignment of malloc(%d)\n", size);
- static const int kNum = 100;
- void* ptrs[kNum];
- for (int i = 0; i < kNum; i++) {
- ptrs[i] = malloc(size);
- uintptr_t p = reinterpret_cast<uintptr_t>(ptrs[i]);
- CHECK((p % sizeof(void*)) == 0);
- CHECK((p % sizeof(double)) == 0);
- // Must have 16-byte (or 8-byte in case of -DTCMALLOC_ALIGN_8BYTES)
- // alignment for large enough objects
- if (size >= kMinAlign) {
- CHECK((p % kMinAlign) == 0);
- }
- }
- for (int i = 0; i < kNum; i++) {
- free(ptrs[i]);
- }
- }
- static void TestMallocAlignment() {
- for (int lg = 0; lg < 16; lg++) {
- TestAlignmentForSize((1<<lg) - 1);
- TestAlignmentForSize((1<<lg) + 0);
- TestAlignmentForSize((1<<lg) + 1);
- }
- }
- static void TestHugeThreadCache() {
- fprintf(LOGSTREAM, "==== Testing huge thread cache\n");
- // More than 2^16 to cause integer overflow of 16 bit counters.
- static const int kNum = 70000;
- char** array = new char*[kNum];
- for (int i = 0; i < kNum; ++i) {
- array[i] = new char[10];
- }
- for (int i = 0; i < kNum; ++i) {
- delete[] array[i];
- }
- delete[] array;
- }
- namespace {
- struct RangeCallbackState {
- uintptr_t ptr;
- base::MallocRange::Type expected_type;
- size_t min_size;
- bool matched;
- };
- static void RangeCallback(void* arg, const base::MallocRange* r) {
- RangeCallbackState* state = reinterpret_cast<RangeCallbackState*>(arg);
- if (state->ptr >= r->address &&
- state->ptr < r->address + r->length) {
- if (state->expected_type == base::MallocRange::FREE) {
- // We are expecting r->type == FREE, but ReleaseMemory
- // may have already moved us to UNMAPPED state instead (this happens in
- // approximately 0.1% of executions). Accept either state.
- CHECK(r->type == base::MallocRange::FREE ||
- r->type == base::MallocRange::UNMAPPED);
- } else {
- CHECK_EQ(r->type, state->expected_type);
- }
- CHECK_GE(r->length, state->min_size);
- state->matched = true;
- }
- }
- // Check that at least one of the callbacks from Ranges() contains
- // the specified address with the specified type, and has size
- // >= min_size.
- static void CheckRangeCallback(void* ptr, base::MallocRange::Type type,
- size_t min_size) {
- RangeCallbackState state;
- state.ptr = reinterpret_cast<uintptr_t>(ptr);
- state.expected_type = type;
- state.min_size = min_size;
- state.matched = false;
- MallocExtension::instance()->Ranges(&state, RangeCallback);
- CHECK(state.matched);
- }
- }
- static bool HaveSystemRelease =
- TCMalloc_SystemRelease(TCMalloc_SystemAlloc(kPageSize, NULL, 0), kPageSize);
- static void TestRanges() {
- static const int MB = 1048576;
- void* a = malloc(MB);
- void* b = malloc(MB);
- base::MallocRange::Type releasedType =
- HaveSystemRelease ? base::MallocRange::UNMAPPED : base::MallocRange::FREE;
- CheckRangeCallback(a, base::MallocRange::INUSE, MB);
- CheckRangeCallback(b, base::MallocRange::INUSE, MB);
- free(a);
- CheckRangeCallback(a, base::MallocRange::FREE, MB);
- CheckRangeCallback(b, base::MallocRange::INUSE, MB);
- MallocExtension::instance()->ReleaseFreeMemory();
- CheckRangeCallback(a, releasedType, MB);
- CheckRangeCallback(b, base::MallocRange::INUSE, MB);
- free(b);
- CheckRangeCallback(a, releasedType, MB);
- CheckRangeCallback(b, base::MallocRange::FREE, MB);
- }
- #ifndef DEBUGALLOCATION
- static size_t GetUnmappedBytes() {
- size_t bytes;
- CHECK(MallocExtension::instance()->GetNumericProperty(
- "tcmalloc.pageheap_unmapped_bytes", &bytes));
- return bytes;
- }
- #endif
- class AggressiveDecommitChanger {
- size_t old_value_;
- public:
- AggressiveDecommitChanger(size_t new_value) {
- MallocExtension *inst = MallocExtension::instance();
- bool rv = inst->GetNumericProperty("tcmalloc.aggressive_memory_decommit", &old_value_);
- CHECK_CONDITION(rv);
- rv = inst->SetNumericProperty("tcmalloc.aggressive_memory_decommit", new_value);
- CHECK_CONDITION(rv);
- }
- ~AggressiveDecommitChanger() {
- MallocExtension *inst = MallocExtension::instance();
- bool rv = inst->SetNumericProperty("tcmalloc.aggressive_memory_decommit", old_value_);
- CHECK_CONDITION(rv);
- }
- };
- static void TestReleaseToSystem() {
- // Debug allocation mode adds overhead to each allocation which
- // messes up all the equality tests here. I just disable the
- // teset in this mode. TODO(csilvers): get it to work for debugalloc?
- #ifndef DEBUGALLOCATION
- if(!HaveSystemRelease) return;
- const double old_tcmalloc_release_rate = FLAGS_tcmalloc_release_rate;
- FLAGS_tcmalloc_release_rate = 0;
- AggressiveDecommitChanger disabler(0);
- static const int MB = 1048576;
- void* a = malloc(MB);
- void* b = malloc(MB);
- MallocExtension::instance()->ReleaseFreeMemory();
- size_t starting_bytes = GetUnmappedBytes();
- // Calling ReleaseFreeMemory() a second time shouldn't do anything.
- MallocExtension::instance()->ReleaseFreeMemory();
- EXPECT_EQ(starting_bytes, GetUnmappedBytes());
- // ReleaseToSystem shouldn't do anything either.
- MallocExtension::instance()->ReleaseToSystem(MB);
- EXPECT_EQ(starting_bytes, GetUnmappedBytes());
- free(a);
- // The span to release should be 1MB.
- MallocExtension::instance()->ReleaseToSystem(MB/2);
- EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
- // Should do nothing since the previous call released too much.
- MallocExtension::instance()->ReleaseToSystem(MB/4);
- EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
- free(b);
- // Use up the extra MB/4 bytes from 'a' and also release 'b'.
- MallocExtension::instance()->ReleaseToSystem(MB/2);
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- // Should do nothing since the previous call released too much.
- MallocExtension::instance()->ReleaseToSystem(MB/2);
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- // Nothing else to release.
- MallocExtension::instance()->ReleaseFreeMemory();
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- a = malloc(MB);
- free(a);
- EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
- // Releasing less than a page should still trigger a release.
- MallocExtension::instance()->ReleaseToSystem(1);
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- FLAGS_tcmalloc_release_rate = old_tcmalloc_release_rate;
- #endif // #ifndef DEBUGALLOCATION
- }
- static void TestAggressiveDecommit() {
- // Debug allocation mode adds overhead to each allocation which
- // messes up all the equality tests here. I just disable the
- // teset in this mode.
- #ifndef DEBUGALLOCATION
- if(!HaveSystemRelease) return;
- fprintf(LOGSTREAM, "Testing aggressive de-commit\n");
- AggressiveDecommitChanger enabler(1);
- static const int MB = 1048576;
- void* a = malloc(MB);
- void* b = malloc(MB);
- size_t starting_bytes = GetUnmappedBytes();
- // ReleaseToSystem shouldn't do anything either.
- MallocExtension::instance()->ReleaseToSystem(MB);
- EXPECT_EQ(starting_bytes, GetUnmappedBytes());
- free(a);
- // The span to release should be 1MB.
- EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
- free(b);
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- // Nothing else to release.
- MallocExtension::instance()->ReleaseFreeMemory();
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- a = malloc(MB);
- free(a);
- EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
- fprintf(LOGSTREAM, "Done testing aggressive de-commit\n");
- #endif // #ifndef DEBUGALLOCATION
- }
- // On MSVC10, in release mode, the optimizer convinces itself
- // g_no_memory is never changed (I guess it doesn't realize OnNoMemory
- // might be called). Work around this by setting the var volatile.
- volatile bool g_no_memory = false;
- std::new_handler g_old_handler = NULL;
- static void OnNoMemory() {
- g_no_memory = true;
- std::set_new_handler(g_old_handler);
- }
- static void TestSetNewMode() {
- int old_mode = tc_set_new_mode(1);
- g_old_handler = std::set_new_handler(&OnNoMemory);
- g_no_memory = false;
- void* ret = malloc(kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_TRUE(g_no_memory);
- g_old_handler = std::set_new_handler(&OnNoMemory);
- g_no_memory = false;
- ret = calloc(1, kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_TRUE(g_no_memory);
- g_old_handler = std::set_new_handler(&OnNoMemory);
- g_no_memory = false;
- ret = realloc(NULL, kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_TRUE(g_no_memory);
- if (kOSSupportsMemalign) {
- // Not really important, but must be small enough such that
- // kAlignment + kTooBig does not overflow.
- const int kAlignment = 1 << 5;
- g_old_handler = std::set_new_handler(&OnNoMemory);
- g_no_memory = false;
- ret = Memalign(kAlignment, kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_TRUE(g_no_memory);
- g_old_handler = std::set_new_handler(&OnNoMemory);
- g_no_memory = false;
- EXPECT_EQ(ENOMEM,
- PosixMemalign(&ret, kAlignment, kTooBig));
- EXPECT_EQ(NULL, ret);
- EXPECT_TRUE(g_no_memory);
- }
- tc_set_new_mode(old_mode);
- }
- static void TestErrno(void) {
- void* ret;
- if (kOSSupportsMemalign) {
- errno = 0;
- ret = Memalign(128, kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_EQ(ENOMEM, errno);
- }
- errno = 0;
- ret = malloc(kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_EQ(ENOMEM, errno);
- errno = 0;
- ret = tc_malloc_skip_new_handler(kTooBig);
- EXPECT_EQ(NULL, ret);
- EXPECT_EQ(ENOMEM, errno);
- }
- static int RunAllTests(int argc, char** argv) {
- // Optional argv[1] is the seed
- AllocatorState rnd(argc > 1 ? atoi(argv[1]) : 100);
- SetTestResourceLimit();
- // TODO(odo): This test has been disabled because it is only by luck that it
- // does not result in fragmentation. When tcmalloc makes an allocation which
- // spans previously unused leaves of the pagemap it will allocate and fill in
- // the leaves to cover the new allocation. The leaves happen to be 256MiB in
- // the 64-bit build, and with the sbrk allocator these allocations just
- // happen to fit in one leaf by luck. With other allocators (mmap,
- // memfs_malloc when used with small pages) the allocations generally span
- // two leaves and this results in a very bad fragmentation pattern with this
- // code. The same failure can be forced with the sbrk allocator just by
- // allocating something on the order of 128MiB prior to starting this test so
- // that the test allocations straddle a 256MiB boundary.
- // TODO(csilvers): port MemoryUsage() over so the test can use that
- #if 0
- # include <unistd.h> // for getpid()
- // Allocate and deallocate blocks of increasing sizes to check if the alloc
- // metadata fragments the memory. (Do not put other allocations/deallocations
- // before this test, it may break).
- {
- size_t memory_usage = MemoryUsage(getpid());
- fprintf(LOGSTREAM, "Testing fragmentation\n");
- for ( int i = 200; i < 240; ++i ) {
- int size = i << 20;
- void *test1 = rnd.alloc(size);
- CHECK(test1);
- for ( int j = 0; j < size; j += (1 << 12) ) {
- static_cast<char*>(test1)[j] = 1;
- }
- free(test1);
- }
- // There may still be a bit of fragmentation at the beginning, until we
- // reach kPageMapBigAllocationThreshold bytes so we check for
- // 200 + 240 + margin.
- CHECK_LT(MemoryUsage(getpid()), memory_usage + (450 << 20) );
- }
- #endif
- // Check that empty allocation works
- fprintf(LOGSTREAM, "Testing empty allocation\n");
- {
- void* p1 = rnd.alloc(0);
- CHECK(p1 != NULL);
- void* p2 = rnd.alloc(0);
- CHECK(p2 != NULL);
- CHECK(p1 != p2);
- free(p1);
- free(p2);
- }
- // This code stresses some of the memory allocation via STL.
- // It may call operator delete(void*, nothrow_t).
- fprintf(LOGSTREAM, "Testing STL use\n");
- {
- std::vector<int> v;
- v.push_back(1);
- v.push_back(2);
- v.push_back(3);
- v.push_back(0);
- std::stable_sort(v.begin(), v.end());
- }
- // Test each of the memory-allocation functions once, just as a sanity-check
- fprintf(LOGSTREAM, "Sanity-testing all the memory allocation functions\n");
- {
- // We use new-hook and delete-hook to verify we actually called the
- // tcmalloc version of these routines, and not the libc version.
- SetNewHook(); // defined as part of MAKE_HOOK_CALLBACK, above
- SetDeleteHook(); // ditto
- void* p1 = malloc(10);
- CHECK(p1 != NULL); // force use of this variable
- VerifyNewHookWasCalled();
- // Also test the non-standard tc_malloc_size
- size_t actual_p1_size = tc_malloc_size(p1);
- CHECK_GE(actual_p1_size, 10);
- CHECK_LT(actual_p1_size, 100000); // a reasonable upper-bound, I think
- free(p1);
- VerifyDeleteHookWasCalled();
- p1 = tc_malloc_skip_new_handler(10);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- free(p1);
- VerifyDeleteHookWasCalled();
- p1 = calloc(10, 2);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- // We make sure we realloc to a big size, since some systems (OS
- // X) will notice if the realloced size continues to fit into the
- // malloc-block and make this a noop if so.
- p1 = realloc(p1, 30000);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- VerifyDeleteHookWasCalled();
- cfree(p1); // synonym for free
- VerifyDeleteHookWasCalled();
- if (kOSSupportsMemalign) {
- CHECK_EQ(PosixMemalign(&p1, sizeof(p1), 40), 0);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- free(p1);
- VerifyDeleteHookWasCalled();
- p1 = Memalign(sizeof(p1) * 2, 50);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- free(p1);
- VerifyDeleteHookWasCalled();
- }
- // Windows has _aligned_malloc. Let's test that that's captured too.
- #if (defined(_MSC_VER) || defined(__MINGW32__)) && !defined(PERFTOOLS_NO_ALIGNED_MALLOC)
- p1 = _aligned_malloc(sizeof(p1) * 2, 64);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- _aligned_free(p1);
- VerifyDeleteHookWasCalled();
- #endif
- p1 = valloc(60);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- free(p1);
- VerifyDeleteHookWasCalled();
- p1 = pvalloc(70);
- CHECK(p1 != NULL);
- VerifyNewHookWasCalled();
- free(p1);
- VerifyDeleteHookWasCalled();
- char* p2 = new char;
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- delete p2;
- VerifyDeleteHookWasCalled();
- p2 = new char[100];
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- delete[] p2;
- VerifyDeleteHookWasCalled();
- p2 = new(std::nothrow) char;
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- delete p2;
- VerifyDeleteHookWasCalled();
- p2 = new(std::nothrow) char[100];
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- delete[] p2;
- VerifyDeleteHookWasCalled();
- // Another way of calling operator new
- p2 = static_cast<char*>(::operator new(100));
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- ::operator delete(p2);
- VerifyDeleteHookWasCalled();
- // Try to call nothrow's delete too. Compilers use this.
- p2 = static_cast<char*>(::operator new(100, std::nothrow));
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- ::operator delete(p2, std::nothrow);
- VerifyDeleteHookWasCalled();
- // Try strdup(), which the system allocates but we must free. If
- // all goes well, libc will use our malloc!
- p2 = strdup("test");
- CHECK(p2 != NULL);
- VerifyNewHookWasCalled();
- free(p2);
- VerifyDeleteHookWasCalled();
- // Test mmap too: both anonymous mmap and mmap of a file
- // Note that for right now we only override mmap on linux
- // systems, so those are the only ones for which we check.
- SetMmapHook();
- SetMremapHook();
- SetMunmapHook();
- #if defined(HAVE_MMAP) && defined(__linux) && \
- (defined(__i386__) || defined(__x86_64__))
- int size = 8192*2;
- p1 = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE,
- -1, 0);
- CHECK(p1 != NULL);
- VerifyMmapHookWasCalled();
- p1 = mremap(p1, size, size/2, 0);
- CHECK(p1 != NULL);
- VerifyMremapHookWasCalled();
- size /= 2;
- munmap(p1, size);
- VerifyMunmapHookWasCalled();
- int fd = open("/dev/zero", O_RDONLY);
- CHECK_GE(fd, 0); // make sure the open succeeded
- p1 = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
- CHECK(p1 != NULL);
- VerifyMmapHookWasCalled();
- munmap(p1, 8192);
- VerifyMunmapHookWasCalled();
- close(fd);
- #else // this is just to quiet the compiler: make sure all fns are called
- IncrementCallsToMmapHook(NULL, NULL, 0, 0, 0, 0, 0);
- IncrementCallsToMunmapHook(NULL, 0);
- IncrementCallsToMremapHook(NULL, NULL, 0, 0, 0, NULL);
- VerifyMmapHookWasCalled();
- VerifyMremapHookWasCalled();
- VerifyMunmapHookWasCalled();
- #endif
- // Test sbrk
- SetSbrkHook();
- #if defined(HAVE_SBRK) && defined(__linux) && \
- (defined(__i386__) || defined(__x86_64__))
- p1 = sbrk(8192);
- CHECK(p1 != NULL);
- VerifySbrkHookWasCalled();
- p1 = sbrk(-8192);
- CHECK(p1 != NULL);
- VerifySbrkHookWasCalled();
- // However, sbrk hook should *not* be called with sbrk(0)
- p1 = sbrk(0);
- CHECK(p1 != NULL);
- CHECK_EQ(g_SbrkHook_calls, 0);
- #else // this is just to quiet the compiler: make sure all fns are called
- IncrementCallsToSbrkHook(NULL, 0);
- VerifySbrkHookWasCalled();
- #endif
- // Reset the hooks to what they used to be. These are all
- // defined as part of MAKE_HOOK_CALLBACK, above.
- ResetNewHook();
- ResetDeleteHook();
- ResetMmapHook();
- ResetMremapHook();
- ResetMunmapHook();
- ResetSbrkHook();
- }
- // Check that "lots" of memory can be allocated
- fprintf(LOGSTREAM, "Testing large allocation\n");
- {
- const int mb_to_allocate = 100;
- void* p = rnd.alloc(mb_to_allocate << 20);
- CHECK(p != NULL); // could not allocate
- free(p);
- }
- TestMallocAlignment();
- // Check calloc() with various arguments
- fprintf(LOGSTREAM, "Testing calloc\n");
- TestCalloc(0, 0, true);
- TestCalloc(0, 1, true);
- TestCalloc(1, 1, true);
- TestCalloc(1<<10, 0, true);
- TestCalloc(1<<20, 0, true);
- TestCalloc(0, 1<<10, true);
- TestCalloc(0, 1<<20, true);
- TestCalloc(1<<20, 2, true);
- TestCalloc(2, 1<<20, true);
- TestCalloc(1000, 1000, true);
- TestCalloc(kMaxSize, 2, false);
- TestCalloc(2, kMaxSize, false);
- TestCalloc(kMaxSize, kMaxSize, false);
- TestCalloc(kMaxSignedSize, 3, false);
- TestCalloc(3, kMaxSignedSize, false);
- TestCalloc(kMaxSignedSize, kMaxSignedSize, false);
- // Test that realloc doesn't always reallocate and copy memory.
- fprintf(LOGSTREAM, "Testing realloc\n");
- TestRealloc();
- fprintf(LOGSTREAM, "Testing operator new(nothrow).\n");
- TestNothrowNew(&::operator new);
- fprintf(LOGSTREAM, "Testing operator new[](nothrow).\n");
- TestNothrowNew(&::operator new[]);
- fprintf(LOGSTREAM, "Testing operator new.\n");
- TestNew(&::operator new);
- fprintf(LOGSTREAM, "Testing operator new[].\n");
- TestNew(&::operator new[]);
- // Create threads
- fprintf(LOGSTREAM, "Testing threaded allocation/deallocation (%d threads)\n",
- FLAGS_numthreads);
- threads = new TesterThread*[FLAGS_numthreads];
- for (int i = 0; i < FLAGS_numthreads; ++i) {
- threads[i] = new TesterThread(i);
- }
- // This runs all the tests at the same time, with a 1M stack size each
- RunManyThreadsWithId(RunThread, FLAGS_numthreads, 1<<20);
- for (int i = 0; i < FLAGS_numthreads; ++i) delete threads[i]; // Cleanup
- // Do the memory intensive tests after threads are done, since exhausting
- // the available address space can make pthread_create to fail.
- // Check that huge allocations fail with NULL instead of crashing
- fprintf(LOGSTREAM, "Testing huge allocations\n");
- TestHugeAllocations(&rnd);
- // Check that large allocations fail with NULL instead of crashing
- #ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs
- fprintf(LOGSTREAM, "Testing out of memory\n");
- for (int s = 0; ; s += (10<<20)) {
- void* large_object = rnd.alloc(s);
- if (large_object == NULL) break;
- free(large_object);
- }
- #endif
- TestHugeThreadCache();
- TestRanges();
- TestReleaseToSystem();
- TestAggressiveDecommit();
- TestSetNewMode();
- TestErrno();
- return 0;
- }
- }
- using testing::RunAllTests;
- int main(int argc, char** argv) {
- #ifdef DEBUGALLOCATION // debug allocation takes forever for huge allocs
- FLAGS_max_free_queue_size = 0; // return freed blocks to tcmalloc immediately
- #endif
- RunAllTests(argc, argv);
- // Test tc_version()
- fprintf(LOGSTREAM, "Testing tc_version()\n");
- int major;
- int minor;
- const char* patch;
- char mmp[64];
- const char* human_version = tc_version(&major, &minor, &patch);
- snprintf(mmp, sizeof(mmp), "%d.%d%s", major, minor, patch);
- CHECK(!strcmp(PACKAGE_STRING, human_version));
- CHECK(!strcmp(PACKAGE_VERSION, mmp));
- fprintf(LOGSTREAM, "PASS\n");
- }
|