This source file includes following definitions.
- Memalign
- PosixMemalign
- Memalign
- PosixMemalign
- Memalign
- PosixMemalign
- num_tests_
- Uniform
- Skewed
- AddType
- PickType
- memalign_fraction_
- alloc
- Next
- locks_failed_
- Run
- AllocateObject
- UpdateObject
- FreeObject
- DeleteHeap
- ShrinkHeap
- PassObject
- AcquirePassedObjects
- FillContents
- CheckContents
- RunThread
- TryHugeAllocation
- TestHugeAllocations
- TestCalloc
- TestRealloc
- TestNewHandler
- TestOneNew
- TestNew
- TestOneNothrowNew
- TestNothrowNew
- TestAlignmentForSize
- TestMallocAlignment
- TestHugeThreadCache
- RangeCallback
- CheckRangeCallback
- TestRanges
- GetUnmappedBytes
- TestReleaseToSystem
- OnNoMemory
- TestSetNewMode
- RunAllTests
- main
#include "config_for_unittests.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "tcmalloc.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#if defined HAVE_STDINT_H
#include <stdint.h>
#endif
#include <sys/types.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#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 "tests/testutil.h"
#if defined(_WIN32)
# define cfree free
# define valloc malloc
# define pvalloc malloc
static bool kOSSupportsMemalign = false;
static inline void* Memalign(size_t align, size_t size) {
exit(1);
return NULL;
}
static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
exit(1);
return -1;
}
#elif defined(__APPLE__)
static bool kOSSupportsMemalign = false;
static inline void* Memalign(size_t align, size_t size) {
exit(1);
return NULL;
}
static inline int PosixMemalign(void** ptr, size_t align, size_t size) {
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
#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);
DECLARE_int64(tcmalloc_sample_parameter);
namespace testing {
static const int FLAGS_numtests = 50000;
static const int FLAGS_log_every_n_tests = 50000;
static const int FLAGS_lgmaxsize = 16;
static const int FLAGS_numthreads = 10;
static const int FLAGS_threadmb = 4;
static const int FLAGS_lg_max_memalign = 18;
static const double FLAGS_memalign_min_fraction = 0;
static const double FLAGS_memalign_max_fraction = 0.4;
static const double FLAGS_memalign_max_alignment_ratio = 6;
static const int FLAGS_allocweight = 50;
static const int FLAGS_freeweight = 50;
static const int FLAGS_updateweight = 10;
static const int FLAGS_passweight = 1;
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;
static const size_t kTooBig = kMaxSize - 100000;
static int news_handled = 0;
class TesterThread;
static TesterThread** threads;
class TestHarness {
private:
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_;
}
void AddType(int type, int weight, const char* name);
int PickType();
int Uniform(int n) {
if (n == 0) {
return random() * 0;
} else {
return random() % n;
}
}
int Skewed(int max_log) {
const int base = random() % (max_log+1);
return random() % (1 << base);
}
private:
vector<Type>* types_;
int total_weight_;
int num_tests_;
};
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);
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);
}
}
virtual ~AllocatorState() {}
void* alloc(size_t size) {
if (Uniform(100) < memalign_fraction_ * 100) {
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_;
};
class TesterThread {
private:
struct Object {
char* ptr;
int size;
int generation;
};
Mutex lock_;
int id_;
AllocatorState rnd_;
vector<Object> heap_;
vector<Object> passed_;
size_t heap_size_;
int locks_ok_;
int locks_failed_;
enum Type { ALLOC, FREE, UPDATE, PASS };
class ACMRandom {
int32 seed_;
public:
explicit ACMRandom(int32 seed) { seed_ = seed; }
int32 Next() {
const int32 M = 2147483647L;
const int32 A = 16807;
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();
}
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;
}
void UpdateObject() {
if (heap_.empty()) return;
const int index = rnd_.Uniform(heap_.size());
CheckContents(heap_[index]);
heap_[index].generation++;
FillContents(&heap_[index]);
}
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();
}
void DeleteHeap() {
while (!heap_.empty()) {
FreeObject();
}
}
void ShrinkHeap() {
while (heap_size_ > FLAGS_threadmb << 20) {
assert(!heap_.empty());
FreeObject();
}
}
void PassObject() {
if (heap_.empty()) return;
const int index = rnd_.Uniform(heap_.size());
Object object = heap_[index];
CheckContents(object);
const int tid = rnd_.Uniform(FLAGS_numthreads);
TesterThread* thread = threads[tid];
if (thread->lock_.TryLock()) {
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_++;
}
}
void AcquirePassedObjects() {
vector<Object> copy;
{
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;
}
}
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);
}
void CheckContents(const Object& object) {
ACMRandom r(reinterpret_cast<intptr_t>(object.ptr) & 0x7fffffff);
for (int i = 0; i < object.generation; ++i) {
r.Next();
}
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);
}
static void TestHugeAllocations(AllocatorState* rnd) {
for (size_t i = 0; i < 70000; i += rnd->Uniform(20)) {
TryHugeAllocation(kMaxSize - i, rnd);
}
#ifndef DEBUGALLOCATION
for (size_t i = 0; i < 100; i++) {
void* p = NULL;
p = rnd->alloc(kMaxSignedSize + i);
if (p) free(p);
p = rnd->alloc(kMaxSignedSize - i);
if (p) free(p);
}
#endif
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);
} else {
CHECK(p != NULL);
for (int i = 0; i < n*s; i++) {
CHECK(p[i] == '\0');
}
free(p);
}
}
static void TestRealloc() {
#ifndef DEBUGALLOCATION
const int64 old_sample_parameter = FLAGS_tcmalloc_sample_parameter;
FLAGS_tcmalloc_sample_parameter = 0;
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);
for (int d = 0; d < (s+1) * 2; ++d) {
void* new_p = realloc(p, start_sizes[s] + deltas[d]);
CHECK(p == new_p);
}
for (int d = 0; d < s*2; ++d) {
void* new_p = realloc(p, start_sizes[s] - deltas[d]);
CHECK(p == new_p);
}
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)) {
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();
}
try {
(*func)(kTooBig);
fprintf(LOGSTREAM, "allocation should have failed.\n");
abort();
} catch (const std::bad_alloc&) {
} catch (...) {
fprintf(LOGSTREAM, "allocation threw unexpected exception.\n");
abort();
}
}
static void TestNew(void* (*func)(size_t)) {
news_handled = 0;
std::new_handler saved_handler = std::set_new_handler(0);
TestOneNew(func);
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&)) {
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();
}
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;
std::new_handler saved_handler = std::set_new_handler(0);
TestOneNothrowNew(func);
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);
}
#define MAKE_HOOK_CALLBACK(hook_type) \
static int g_##hook_type##_calls = 0; \
static void IncrementCallsTo##hook_type(...) { \
g_##hook_type##_calls++; \
} \
static void Verify##hook_type##WasCalled() { \
CHECK_GT(g_##hook_type##_calls, 0); \
g_##hook_type##_calls = 0; \
} \
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)); \
}
MAKE_HOOK_CALLBACK(NewHook);
MAKE_HOOK_CALLBACK(DeleteHook);
MAKE_HOOK_CALLBACK(MmapHook);
MAKE_HOOK_CALLBACK(MremapHook);
MAKE_HOOK_CALLBACK(MunmapHook);
MAKE_HOOK_CALLBACK(SbrkHook);
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);
if (size >= 16) {
CHECK((p % 16) == 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");
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) {
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;
}
}
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 void TestRanges() {
static const int MB = 1048576;
void* a = malloc(MB);
void* b = malloc(MB);
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, base::MallocRange::UNMAPPED, MB);
CheckRangeCallback(b, base::MallocRange::INUSE, MB);
free(b);
CheckRangeCallback(a, base::MallocRange::UNMAPPED, 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
static void TestReleaseToSystem() {
#ifndef DEBUGALLOCATION
const double old_tcmalloc_release_rate = FLAGS_tcmalloc_release_rate;
FLAGS_tcmalloc_release_rate = 0;
static const int MB = 1048576;
void* a = malloc(MB);
void* b = malloc(MB);
MallocExtension::instance()->ReleaseFreeMemory();
size_t starting_bytes = GetUnmappedBytes();
MallocExtension::instance()->ReleaseFreeMemory();
EXPECT_EQ(starting_bytes, GetUnmappedBytes());
MallocExtension::instance()->ReleaseToSystem(MB);
EXPECT_EQ(starting_bytes, GetUnmappedBytes());
free(a);
MallocExtension::instance()->ReleaseToSystem(MB/2);
EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
MallocExtension::instance()->ReleaseToSystem(MB/4);
EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
free(b);
MallocExtension::instance()->ReleaseToSystem(MB/2);
EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
MallocExtension::instance()->ReleaseToSystem(MB/2);
EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
MallocExtension::instance()->ReleaseFreeMemory();
EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
a = malloc(MB);
free(a);
EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
MallocExtension::instance()->ReleaseToSystem(1);
EXPECT_EQ(starting_bytes + 2*MB, GetUnmappedBytes());
FLAGS_tcmalloc_release_rate = old_tcmalloc_release_rate;
#endif
}
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) {
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 int RunAllTests(int argc, char** argv) {
AllocatorState rnd(argc > 1 ? atoi(argv[1]) : 100);
SetTestResourceLimit();
#if 0
# include <unistd.h>
{
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);
}
CHECK_LT(MemoryUsage(getpid()), memory_usage + (450 << 20) );
}
#endif
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);
}
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());
}
fprintf(LOGSTREAM, "Sanity-testing all the memory allocation functions\n");
{
SetNewHook();
SetDeleteHook();
void* p1 = malloc(10);
CHECK(p1 != NULL);
VerifyNewHookWasCalled();
size_t actual_p1_size = tc_malloc_size(p1);
CHECK_GE(actual_p1_size, 10);
CHECK_LT(actual_p1_size, 100000);
free(p1);
VerifyDeleteHookWasCalled();
p1 = calloc(10, 2);
CHECK(p1 != NULL);
VerifyNewHookWasCalled();
p1 = realloc(p1, 30000);
CHECK(p1 != NULL);
VerifyNewHookWasCalled();
VerifyDeleteHookWasCalled();
cfree(p1);
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();
}
#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();
p2 = static_cast<char*>(::operator new(100));
CHECK(p2 != NULL);
VerifyNewHookWasCalled();
::operator delete(p2);
VerifyDeleteHookWasCalled();
p2 = static_cast<char*>(::operator new(100, std::nothrow));
CHECK(p2 != NULL);
VerifyNewHookWasCalled();
::operator delete(p2, std::nothrow);
VerifyDeleteHookWasCalled();
p2 = strdup("test");
CHECK(p2 != NULL);
VerifyNewHookWasCalled();
free(p2);
VerifyDeleteHookWasCalled();
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);
p1 = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
CHECK(p1 != NULL);
VerifyMmapHookWasCalled();
munmap(p1, 8192);
VerifyMunmapHookWasCalled();
close(fd);
#else
IncrementCallsToMmapHook();
IncrementCallsToMunmapHook();
IncrementCallsToMremapHook();
VerifyMmapHookWasCalled();
VerifyMremapHookWasCalled();
VerifyMunmapHookWasCalled();
#endif
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();
p1 = sbrk(0);
CHECK(p1 != NULL);
CHECK_EQ(g_SbrkHook_calls, 0);
#else
IncrementCallsToSbrkHook();
VerifySbrkHookWasCalled();
#endif
ResetNewHook();
ResetDeleteHook();
ResetMmapHook();
ResetMremapHook();
ResetMunmapHook();
ResetSbrkHook();
}
fprintf(LOGSTREAM, "Testing large allocation\n");
{
const int mb_to_allocate = 100;
void* p = rnd.alloc(mb_to_allocate << 20);
CHECK(p != NULL);
free(p);
}
TestMallocAlignment();
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);
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[]);
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);
}
RunManyThreadsWithId(RunThread, FLAGS_numthreads, 1<<20);
for (int i = 0; i < FLAGS_numthreads; ++i) delete threads[i];
fprintf(LOGSTREAM, "Testing huge allocations\n");
TestHugeAllocations(&rnd);
#ifndef DEBUGALLOCATION
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();
TestSetNewMode();
return 0;
}
}
using testing::RunAllTests;
int main(int argc, char** argv) {
#ifdef DEBUGALLOCATION
FLAGS_max_free_queue_size = 0;
#endif
RunAllTests(argc, argv);
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");
}