1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Upgrade gperftools to 2.9 for GCP/GMC/GMP/GMD. (#2247)

This commit is contained in:
winlin 2021-12-12 15:38:30 +08:00
parent 63da0dca92
commit 44e9dc83e9
346 changed files with 169666 additions and 78 deletions

View file

@ -0,0 +1,174 @@
// -*- 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.
// ---
// Author: Sanjay Ghemawat
#include <stdlib.h> // for rand()
#include <vector>
#include <set>
#include <random>
#include <algorithm>
#include <utility>
#include "addressmap-inl.h"
#include "base/logging.h"
#include "base/commandlineflags.h"
DEFINE_int32(iters, 20, "Number of test iterations");
DEFINE_int32(N, 100000, "Number of elements to test per iteration");
using std::pair;
using std::make_pair;
using std::vector;
using std::set;
using std::shuffle;
struct UniformRandomNumberGenerator {
size_t Uniform(size_t max_size) {
if (max_size == 0)
return 0;
return rand() % max_size; // not a great random-number fn, but portable
}
};
static UniformRandomNumberGenerator rnd;
// pair of associated value and object size
typedef pair<int, size_t> ValueT;
struct PtrAndSize {
char* ptr;
size_t size;
PtrAndSize(char* p, size_t s) : ptr(p), size(s) {}
};
size_t SizeFunc(const ValueT& v) { return v.second; }
static void SetCheckCallback(const void* ptr, ValueT* val,
set<pair<const void*, int> >* check_set) {
check_set->insert(make_pair(ptr, val->first));
}
int main(int argc, char** argv) {
// Get a bunch of pointers
const int N = FLAGS_N;
static const int kMaxRealSize = 49;
// 100Mb to stress not finding previous object (AddressMap's cluster is 1Mb):
static const size_t kMaxSize = 100*1000*1000;
vector<PtrAndSize> ptrs_and_sizes;
for (int i = 0; i < N; ++i) {
size_t s = rnd.Uniform(kMaxRealSize);
ptrs_and_sizes.push_back(PtrAndSize(new char[s], s));
}
for (int x = 0; x < FLAGS_iters; ++x) {
RAW_LOG(INFO, "Iteration %d/%d...\n", x, FLAGS_iters);
// Permute pointers to get rid of allocation order issues
std::random_device rd;
std::mt19937 g(rd());
shuffle(ptrs_and_sizes.begin(), ptrs_and_sizes.end(), g);
AddressMap<ValueT> map(malloc, free);
const ValueT* result;
const void* res_p;
// Insert a bunch of entries
for (int i = 0; i < N; ++i) {
char* p = ptrs_and_sizes[i].ptr;
CHECK(!map.Find(p));
int offs = rnd.Uniform(ptrs_and_sizes[i].size);
CHECK(!map.FindInside(&SizeFunc, kMaxSize, p + offs, &res_p));
map.Insert(p, make_pair(i, ptrs_and_sizes[i].size));
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i);
CHECK(result = map.FindInside(&SizeFunc, kMaxRealSize, p + offs, &res_p));
CHECK_EQ(res_p, p);
CHECK_EQ(result->first, i);
map.Insert(p, make_pair(i + N, ptrs_and_sizes[i].size));
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i + N);
}
// Delete the even entries
for (int i = 0; i < N; i += 2) {
void* p = ptrs_and_sizes[i].ptr;
ValueT removed;
CHECK(map.FindAndRemove(p, &removed));
CHECK_EQ(removed.first, i + N);
}
// Lookup the odd entries and adjust them
for (int i = 1; i < N; i += 2) {
char* p = ptrs_and_sizes[i].ptr;
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i + N);
int offs = rnd.Uniform(ptrs_and_sizes[i].size);
CHECK(result = map.FindInside(&SizeFunc, kMaxRealSize, p + offs, &res_p));
CHECK_EQ(res_p, p);
CHECK_EQ(result->first, i + N);
map.Insert(p, make_pair(i + 2*N, ptrs_and_sizes[i].size));
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i + 2*N);
}
// Insert even entries back
for (int i = 0; i < N; i += 2) {
char* p = ptrs_and_sizes[i].ptr;
int offs = rnd.Uniform(ptrs_and_sizes[i].size);
CHECK(!map.FindInside(&SizeFunc, kMaxSize, p + offs, &res_p));
map.Insert(p, make_pair(i + 2*N, ptrs_and_sizes[i].size));
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i + 2*N);
CHECK(result = map.FindInside(&SizeFunc, kMaxRealSize, p + offs, &res_p));
CHECK_EQ(res_p, p);
CHECK_EQ(result->first, i + 2*N);
}
// Check all entries
set<pair<const void*, int> > check_set;
map.Iterate(SetCheckCallback, &check_set);
CHECK_EQ(check_set.size(), N);
for (int i = 0; i < N; ++i) {
void* p = ptrs_and_sizes[i].ptr;
check_set.erase(make_pair(p, i + 2*N));
CHECK(result = map.Find(p));
CHECK_EQ(result->first, i + 2*N);
}
CHECK_EQ(check_set.size(), 0);
}
for (int i = 0; i < N; ++i) {
delete[] ptrs_and_sizes[i].ptr;
}
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,152 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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: Sanjay Ghemawat
*/
#include <stdio.h>
#include "base/logging.h"
#include "base/atomicops.h"
#define GG_ULONGLONG(x) static_cast<uint64>(x)
#define NUM_BITS(T) (sizeof(T) * 8)
template <class AtomicType>
static void TestCompareAndSwap(AtomicType (*compare_and_swap_func)
(volatile AtomicType*, AtomicType, AtomicType)) {
AtomicType value = 0;
AtomicType prev = (*compare_and_swap_func)(&value, 0, 1);
ASSERT_EQ(1, value);
ASSERT_EQ(0, prev);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = (*compare_and_swap_func)(&value, 0, 5);
ASSERT_EQ(k_test_val, value);
ASSERT_EQ(k_test_val, prev);
value = k_test_val;
prev = (*compare_and_swap_func)(&value, k_test_val, 5);
ASSERT_EQ(5, value);
ASSERT_EQ(k_test_val, prev);
}
template <class AtomicType>
static void TestAtomicExchange(AtomicType (*atomic_exchange_func)
(volatile AtomicType*, AtomicType)) {
AtomicType value = 0;
AtomicType new_value = (*atomic_exchange_func)(&value, 1);
ASSERT_EQ(1, value);
ASSERT_EQ(0, new_value);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = (*atomic_exchange_func)(&value, k_test_val);
ASSERT_EQ(k_test_val, value);
ASSERT_EQ(k_test_val, new_value);
value = k_test_val;
new_value = (*atomic_exchange_func)(&value, 5);
ASSERT_EQ(5, value);
ASSERT_EQ(k_test_val, new_value);
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestStore() {
const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
base::subtle::NoBarrier_Store(&value, kVal1);
ASSERT_EQ(kVal1, value);
base::subtle::NoBarrier_Store(&value, kVal2);
ASSERT_EQ(kVal2, value);
base::subtle::Release_Store(&value, kVal1);
ASSERT_EQ(kVal1, value);
base::subtle::Release_Store(&value, kVal2);
ASSERT_EQ(kVal2, value);
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestLoad() {
const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
value = kVal1;
ASSERT_EQ(kVal1, base::subtle::NoBarrier_Load(&value));
value = kVal2;
ASSERT_EQ(kVal2, base::subtle::NoBarrier_Load(&value));
value = kVal1;
ASSERT_EQ(kVal1, base::subtle::Acquire_Load(&value));
value = kVal2;
ASSERT_EQ(kVal2, base::subtle::Acquire_Load(&value));
}
template <class AtomicType>
static void TestAtomicOps() {
TestCompareAndSwap<AtomicType>(base::subtle::NoBarrier_CompareAndSwap);
TestCompareAndSwap<AtomicType>(base::subtle::Acquire_CompareAndSwap);
TestCompareAndSwap<AtomicType>(base::subtle::Release_CompareAndSwap);
TestAtomicExchange<AtomicType>(base::subtle::NoBarrier_AtomicExchange);
TestAtomicExchange<AtomicType>(base::subtle::Acquire_AtomicExchange);
TestAtomicExchange<AtomicType>(base::subtle::Release_AtomicExchange);
TestStore<AtomicType>();
TestLoad<AtomicType>();
}
int main(int argc, char** argv) {
TestAtomicOps<AtomicWord>();
TestAtomicOps<Atomic32>();
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,64 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein
// This tests the accounting done by tcmalloc. When we allocate and
// free a small buffer, the number of bytes used by the application
// before the alloc+free should match the number of bytes used after.
// However, the internal data structures used by tcmalloc will be
// quite different -- new spans will have been allocated, etc. This
// is, thus, a simple test that we account properly for the internal
// data structures, so that we report the actual application-used
// bytes properly.
#include "config_for_unittests.h"
#include <stdlib.h>
#include <stdio.h>
#include <gperftools/malloc_extension.h>
#include "base/logging.h"
int main() {
// We don't do accounting right when using debugallocation.cc, so
// turn off the test then. TODO(csilvers): get this working too.
#ifdef NDEBUG
static const char kCurrent[] = "generic.current_allocated_bytes";
size_t before_bytes, after_bytes;
MallocExtension::instance()->GetNumericProperty(kCurrent, &before_bytes);
free(malloc(200));
MallocExtension::instance()->GetNumericProperty(kCurrent, &after_bytes);
CHECK_EQ(before_bytes, after_bytes);
#endif
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,333 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Fred Akalin
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for memcmp
#include <vector>
#include "gperftools/malloc_extension.h"
#include "gperftools/tcmalloc.h"
#include "base/logging.h"
#include "tests/testutil.h"
using std::vector;
vector<void (*)()> g_testlist; // the tests to run
#define TEST(a, b) \
struct Test_##a##_##b { \
Test_##a##_##b() { g_testlist.push_back(&Run); } \
static void Run(); \
}; \
static Test_##a##_##b g_test_##a##_##b; \
void Test_##a##_##b::Run()
static int RUN_ALL_TESTS() {
vector<void (*)()>::const_iterator it;
for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
(*it)(); // The test will error-exit if there's a problem.
}
fprintf(stderr, "\nPassed %d tests\n\nPASS\n",
static_cast<int>(g_testlist.size()));
return 0;
}
// The death tests are meant to be run from a shell-script driver, which
// passes in an integer saying which death test to run. We store that
// test-to-run here, and in the macro use a counter to see when we get
// to that test, so we can run it.
static int test_to_run = 0; // set in main() based on argv
static int test_counter = 0; // incremented every time the macro is called
#define IF_DEBUG_EXPECT_DEATH(statement, regex) do { \
if (test_counter++ == test_to_run) { \
fprintf(stderr, "Expected regex:%s\n", regex); \
statement; \
} \
} while (false)
// This flag won't be compiled in in opt mode.
DECLARE_int32(max_free_queue_size);
// Test match as well as mismatch rules. But do not test on OS X; on
// OS X the OS converts new/new[] to malloc before it gets to us, so
// we are unable to catch these mismatch errors.
#ifndef __APPLE__
TEST(DebugAllocationTest, DeallocMismatch) {
// malloc can be matched only by free
// new can be matched only by delete and delete(nothrow)
// new[] can be matched only by delete[] and delete[](nothrow)
// new(nothrow) can be matched only by delete and delete(nothrow)
// new(nothrow)[] can be matched only by delete[] and delete[](nothrow)
// Allocate with malloc.
{
int* x = static_cast<int*>(noopt(malloc(sizeof(*x))));
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
// Should work fine.
free(x);
}
// Allocate with new.
{
int* x = noopt(new int);
int* y = noopt(new int);
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
delete x;
::operator delete(y, std::nothrow);
}
// Allocate with new[].
{
int* x = noopt(new int[1]);
int* y = noopt(new int[1]);
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
delete [] x;
::operator delete[](y, std::nothrow);
}
// Allocate with new(nothrow).
{
int* x = noopt(new (std::nothrow) int);
int* y = noopt(new (std::nothrow) int);
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
delete x;
::operator delete(y, std::nothrow);
}
// Allocate with new(nothrow)[].
{
int* x = noopt(new (std::nothrow) int[1]);
int* y = noopt(new (std::nothrow) int[1]);
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
delete [] x;
::operator delete[](y, std::nothrow);
}
}
#endif // #ifdef OS_MACOSX
TEST(DebugAllocationTest, DoubleFree) {
int* pint = noopt(new int);
delete pint;
IF_DEBUG_EXPECT_DEATH(delete pint, "has been already deallocated");
}
TEST(DebugAllocationTest, StompBefore) {
int* pint = noopt(new int);
#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
pint[-1] = 5;
IF_DEBUG_EXPECT_DEATH(delete pint, "a word before object");
#endif
}
TEST(DebugAllocationTest, StompAfter) {
int* pint = noopt(new int);
#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
pint[1] = 5;
IF_DEBUG_EXPECT_DEATH(delete pint, "a word after object");
#endif
}
TEST(DebugAllocationTest, FreeQueueTest) {
// Verify that the allocator doesn't return blocks that were recently freed.
int* x = noopt(new int);
int* old_x = x;
delete x;
x = noopt(new int);
#if 1
// This check should not be read as a universal guarantee of behavior. If
// other threads are executing, it would be theoretically possible for this
// check to fail despite the efforts of debugallocation.cc to the contrary.
// It should always hold under the controlled conditions of this unittest,
// however.
EXPECT_NE(x, old_x); // Allocator shouldn't return recently freed blocks
#else
// The below check passes, but since it isn't *required* to pass, I've left
// it commented out.
// EXPECT_EQ(x, old_x);
#endif
old_x = NULL; // avoid breaking opt build with an unused variable warning.
delete x;
}
TEST(DebugAllocationTest, DanglingPointerWriteTest) {
// This test can only be run if debugging.
//
// If not debugging, the 'new' following the dangling write might not be
// safe. When debugging, we expect the (trashed) deleted block to be on the
// list of recently-freed blocks, so the following 'new' will be safe.
#if 1
int* x = noopt(new int);
delete x;
int poisoned_x_value = *x;
*x = 1; // a dangling write.
char* s = noopt(new char[FLAGS_max_free_queue_size]);
// When we delete s, we push the storage that was previously allocated to x
// off the end of the free queue. At that point, the write to that memory
// will be detected.
IF_DEBUG_EXPECT_DEATH(delete [] s, "Memory was written to after being freed.");
// restore the poisoned value of x so that we can delete s without causing a
// crash.
*x = poisoned_x_value;
delete [] s;
#endif
}
TEST(DebugAllocationTest, DanglingWriteAtExitTest) {
int *x = noopt(new int);
delete x;
int old_x_value = *x;
*x = 1;
// verify that dangling writes are caught at program termination if the
// corrupted block never got pushed off of the end of the free queue.
IF_DEBUG_EXPECT_DEATH(exit(0), "Memory was written to after being freed.");
*x = old_x_value; // restore x so that the test can exit successfully.
}
TEST(DebugAllocationTest, StackTraceWithDanglingWriteAtExitTest) {
int *x = noopt(new int);
delete x;
int old_x_value = *x;
*x = 1;
// verify that we also get a stack trace when we have a dangling write.
// The " @ " is part of the stack trace output.
IF_DEBUG_EXPECT_DEATH(exit(0), " @ .*main");
*x = old_x_value; // restore x so that the test can exit successfully.
}
static size_t CurrentlyAllocatedBytes() {
size_t value;
CHECK(MallocExtension::instance()->GetNumericProperty(
"generic.current_allocated_bytes", &value));
return value;
}
TEST(DebugAllocationTest, CurrentlyAllocated) {
// Clear the free queue
#if 1
FLAGS_max_free_queue_size = 0;
// Force a round-trip through the queue management code so that the
// new size is seen and the queue of recently-freed blocks is flushed.
free(noopt(malloc(1)));
FLAGS_max_free_queue_size = 1048576;
#endif
// Free something and check that it disappears from allocated bytes
// immediately.
char* p = noopt(new char[1000]);
size_t after_malloc = CurrentlyAllocatedBytes();
delete[] p;
size_t after_free = CurrentlyAllocatedBytes();
EXPECT_LE(after_free, after_malloc - 1000);
}
TEST(DebugAllocationTest, GetAllocatedSizeTest) {
#if 1
// When debug_allocation is in effect, GetAllocatedSize should return
// exactly requested size, since debug_allocation doesn't allow users
// to write more than that.
for (int i = 0; i < 10; ++i) {
void *p = noopt(malloc(i));
EXPECT_EQ(i, MallocExtension::instance()->GetAllocatedSize(p));
free(p);
}
#endif
void* a = noopt(malloc(1000));
EXPECT_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000);
// This is just a sanity check. If we allocated too much, alloc is broken
EXPECT_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000);
EXPECT_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000);
free(a);
}
TEST(DebugAllocationTest, HugeAlloc) {
// This must not be a const variable so it doesn't form an
// integral-constant-expression which can be *statically* rejected by the
// compiler as too large for the allocation.
size_t kTooBig = ~static_cast<size_t>(0);
void* a = NULL;
#ifndef NDEBUG
a = noopt(malloc(noopt(kTooBig)));
EXPECT_EQ(NULL, a);
// kAlsoTooBig is small enough not to get caught by debugallocation's check,
// but will still fall through to tcmalloc's check. This must also be
// a non-const variable. See kTooBig for more details.
size_t kAlsoTooBig = kTooBig - 1024;
a = noopt(malloc(noopt(kAlsoTooBig)));
EXPECT_EQ(NULL, a);
#endif
}
// based on test program contributed by mikesart@gmail.com aka
// mikesart@valvesoftware.com. See issue-464.
TEST(DebugAllocationTest, ReallocAfterMemalign) {
char stuff[50];
memset(stuff, 0x11, sizeof(stuff));
void *p = tc_memalign(16, sizeof(stuff));
EXPECT_NE(p, NULL);
memcpy(stuff, p, sizeof(stuff));
p = noopt(realloc(p, sizeof(stuff) + 10));
EXPECT_NE(p, NULL);
int rv = memcmp(stuff, p, sizeof(stuff));
EXPECT_EQ(rv, 0);
}
int main(int argc, char** argv) {
// If you run without args, we run the non-death parts of the test.
// Otherwise, argv[1] should be a number saying which death-test
// to run. We will output a regexp we expect the death-message
// to include, and then run the given death test (which hopefully
// will produce that error message). If argv[1] > the number of
// death tests, we will run only the non-death parts. One way to
// tell when you are done with all tests is when no 'expected
// regexp' message is printed for a given argv[1].
if (argc < 2) {
test_to_run = -1; // will never match
} else {
test_to_run = atoi(argv[1]);
}
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,98 @@
#!/bin/sh
# Copyright (c) 2009, 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: Craig Silverstein
BINDIR="${BINDIR:-.}"
# We expect PPROF_PATH to be set in the environment.
# If not, we set it to some reasonable value
export PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir]"
echo " By default, unittest_dir=$BINDIR"
exit 1
fi
DEBUGALLOCATION_TEST="${1:-$BINDIR/debugallocation_test}"
num_failures=0
# Run the i-th death test and make sure the test has the expected
# regexp. We can depend on the first line of the output being
# Expected regex:<regex>
# Evaluates to "done" if we are not actually a death-test (so $1 is
# too big a number, and we can stop). Evaluates to "" otherwise.
# Increments num_failures if the death test does not succeed.
OneDeathTest() {
"$DEBUGALLOCATION_TEST" "$1" 2>&1 | {
regex_line='dummy'
# Normally the regex_line is the first line of output, but not
# always (if tcmalloc itself does any logging to stderr).
while test -n "$regex_line"; do
read regex_line
regex=`expr "$regex_line" : "Expected regex:\(.*\)"`
test -n "$regex" && break # found the regex line
done
test -z "$regex" && echo "done" || grep "$regex" 2>&1
}
}
death_test_num=0 # which death test to run
while :; do # same as 'while true', but more portable
echo -n "Running death test $death_test_num..."
output="`OneDeathTest $death_test_num`"
case $output in
# Empty string means grep didn't find anything.
"") echo "FAILED"; num_failures=`expr $num_failures + 1`;;
"done"*) echo "done with death tests"; break;;
# Any other string means grep found something, like it ought to.
*) echo "OK";;
esac
death_test_num=`expr $death_test_num + 1`
done
# Test the non-death parts of the test too
echo -n "Running non-death tests..."
if "$DEBUGALLOCATION_TEST"; then
echo "OK"
else
echo "FAILED"
num_failures=`expr $num_failures + 1`
fi
if [ "$num_failures" = 0 ]; then
echo "PASS"
else
echo "Failed with $num_failures failures"
fi
exit $num_failures

View file

@ -0,0 +1,133 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2003, 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: Sanjay Ghemawat
//
// Test speed of handling fragmented heap
#include "config_for_unittests.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/time.h> // for struct timeval
#include <sys/resource.h> // for getrusage
#endif
#ifdef _WIN32
#include <windows.h> // for GetTickCount()
#endif
#include <vector>
#include "base/logging.h"
#include "common.h"
#include <gperftools/malloc_extension.h>
using std::vector;
int main(int argc, char** argv) {
// Make kAllocSize one page larger than the maximum small object size.
static const int kAllocSize = kMaxSize + kPageSize;
// Allocate 400MB in total.
static const int kTotalAlloc = 400 << 20;
static const int kAllocIterations = kTotalAlloc / kAllocSize;
// Allocate lots of objects
vector<char*> saved(kAllocIterations);
for (int i = 0; i < kAllocIterations; i++) {
saved[i] = new char[kAllocSize];
}
// Check the current "slack".
size_t slack_before;
MallocExtension::instance()->GetNumericProperty("tcmalloc.slack_bytes",
&slack_before);
// Free alternating ones to fragment heap
size_t free_bytes = 0;
for (int i = 0; i < saved.size(); i += 2) {
delete[] saved[i];
free_bytes += kAllocSize;
}
// Check that slack delta is within 10% of expected.
size_t slack_after;
MallocExtension::instance()->GetNumericProperty("tcmalloc.slack_bytes",
&slack_after);
CHECK_GE(slack_after, slack_before);
size_t slack = slack_after - slack_before;
CHECK_GT(double(slack), 0.9*free_bytes);
CHECK_LT(double(slack), 1.1*free_bytes);
// Dump malloc stats
static const int kBufSize = 1<<20;
char* buffer = new char[kBufSize];
MallocExtension::instance()->GetStats(buffer, kBufSize);
VLOG(1, "%s", buffer);
delete[] buffer;
// Now do timing tests
for (int i = 0; i < 5; i++) {
static const int kIterations = 100000;
#ifdef HAVE_SYS_RESOURCE_H
struct rusage r;
getrusage(RUSAGE_SELF, &r); // figure out user-time spent on this
struct timeval tv_start = r.ru_utime;
#elif defined(_WIN32)
long long int tv_start = GetTickCount();
#else
# error No way to calculate time on your system
#endif
for (int i = 0; i < kIterations; i++) {
size_t s;
MallocExtension::instance()->GetNumericProperty("tcmalloc.slack_bytes",
&s);
}
#ifdef HAVE_SYS_RESOURCE_H
getrusage(RUSAGE_SELF, &r);
struct timeval tv_end = r.ru_utime;
int64 sumsec = static_cast<int64>(tv_end.tv_sec) - tv_start.tv_sec;
int64 sumusec = static_cast<int64>(tv_end.tv_usec) - tv_start.tv_usec;
#elif defined(_WIN32)
long long int tv_end = GetTickCount();
int64 sumsec = (tv_end - tv_start) / 1000;
// Resolution in windows is only to the millisecond, alas
int64 sumusec = ((tv_end - tv_start) % 1000) * 1000;
#else
# error No way to calculate time on your system
#endif
fprintf(stderr, "getproperty: %6.1f ns/call\n",
(sumsec * 1e9 + sumusec * 1e3) / kIterations);
}
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,123 @@
// -*- 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.
// ---
// Author: Craig Silverstein
//
// This verifies that GetPC works correctly. This test uses a minimum
// of Google infrastructure, to make it very easy to port to various
// O/Ses and CPUs and test that GetPC is working.
#include "config.h"
#include "getpc.h" // should be first to get the _GNU_SOURCE dfn
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h> // for setitimer
// Needs to be volatile so compiler doesn't try to optimize it away
static volatile void* getpc_retval = NULL; // what GetPC returns
static volatile bool prof_handler_called = false;
static void prof_handler(int sig, siginfo_t*, void* signal_ucontext) {
if (!prof_handler_called)
getpc_retval = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));
prof_handler_called = true; // only store the retval once
}
static void RoutineCallingTheSignal() {
struct sigaction sa;
sa.sa_sigaction = prof_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGPROF, &sa, NULL) != 0) {
perror("sigaction");
exit(1);
}
struct itimerval timer;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 1000;
timer.it_value = timer.it_interval;
setitimer(ITIMER_PROF, &timer, 0);
// Now we need to do some work for a while, that doesn't call any
// other functions, so we can be guaranteed that when the SIGPROF
// fires, we're the routine executing.
int r = 0;
for (int i = 0; !prof_handler_called; ++i) {
for (int j = 0; j < i; j++) {
r ^= i;
r <<= 1;
r ^= j;
r >>= 1;
}
}
// Now make sure the above loop doesn't get optimized out
srand(r);
}
// This is an upper bound of how many bytes the instructions for
// RoutineCallingTheSignal might be. There's probably a more
// principled way to do this, but I don't know how portable it would be.
// (The function is 372 bytes when compiled with -g on Mac OS X 10.4.
// I can imagine it would be even bigger in 64-bit architectures.)
const int kRoutineSize = 512 * sizeof(void*)/4; // allow 1024 for 64-bit
int main(int argc, char** argv) {
RoutineCallingTheSignal();
// Annoyingly, C++ disallows casting pointer-to-function to
// pointer-to-object, so we use a C-style cast instead.
char* expected = (char*)&RoutineCallingTheSignal;
char* actual = (char*)getpc_retval;
// For ia64, ppc64v1, and parisc64, the function pointer is actually
// a struct. For instance, ia64's dl-fptr.h:
// struct fdesc { /* An FDESC is a function descriptor. */
// ElfW(Addr) ip; /* code entry point */
// ElfW(Addr) gp; /* global pointer */
// };
// We want the code entry point.
// NOTE: ppc64 ELFv2 (Little Endian) does not have function pointers
#if defined(__ia64) || \
(defined(__powerpc64__) && _CALL_ELF != 2)
expected = ((char**)expected)[0]; // this is "ip"
#endif
if (actual < expected || actual > expected + kRoutineSize) {
printf("Test FAILED: actual PC: %p, expected PC: %p\n", actual, expected);
return 1;
} else {
printf("PASS\n");
return 0;
}
}

View file

@ -0,0 +1,176 @@
#!/bin/sh
# 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
#
# Run the heap checker unittest in a mode where it is supposed to crash and
# return an error if it doesn't.
# We expect BINDIR to be set in the environment.
# If not, we set it to some reasonable value.
BINDIR="${BINDIR:-.}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir]"
echo " By default, unittest_dir=$BINDIR"
exit 1
fi
EXE="${1:-$BINDIR/heap-checker_unittest}"
TMPDIR="/tmp/heap_check_death_info"
ALARM() {
# You need perl to run pprof, so I assume it's installed
perl -e '
$timeout=$ARGV[0]; shift;
$retval = 255; # the default retval, for the case where we timed out
eval { # need to run in an eval-block to trigger during system()
local $SIG{ALRM} = sub { die "alarm\n" }; # \n is required!
alarm $timeout;
$retval = system(@ARGV);
# Make retval bash-style: exit status, or 128+n if terminated by signal n
$retval = ($retval & 127) ? (128 + $retval) : ($retval >> 8);
alarm 0;
};
exit $retval; # return system()-retval, or 255 if system() never returned
' "$@"
}
# $1: timeout for alarm;
# $2: regexp of expected exit code(s);
# $3: regexp to match a line in the output;
# $4: regexp to not match a line in the output;
# $5+ args to pass to $EXE
Test() {
# Note: make sure these varnames don't conflict with any vars outside Test()!
timeout="$1"
shift
expected_ec="$1"
shift
expected_regexp="$1"
shift
unexpected_regexp="$1"
shift
echo -n "Testing $EXE with $@ ... "
output="$TMPDIR/output"
ALARM $timeout env "$@" $EXE > "$output" 2>&1
actual_ec=$?
ec_ok=`expr "$actual_ec" : "$expected_ec$" >/dev/null || echo false`
matches_ok=`test -z "$expected_regexp" || \
grep "$expected_regexp" "$output" >/dev/null 2>&1 || echo false`
negmatches_ok=`test -z "$unexpected_regexp" || \
! grep "$unexpected_regexp" "$output" >/dev/null 2>&1 || echo false`
if $ec_ok && $matches_ok && $negmatches_ok; then
echo "PASS"
return 0 # 0: success
fi
# If we get here, we failed. Now we just need to report why
echo "FAIL"
if [ $actual_ec -eq 255 ]; then # 255 == SIGTERM due to $ALARM
echo "Test was taking unexpectedly long time to run and so we aborted it."
echo "Try the test case manually or raise the timeout from $timeout"
echo "to distinguish test slowness from a real problem."
else
$ec_ok || \
echo "Wrong exit code: expected: '$expected_ec'; actual: $actual_ec"
$matches_ok || \
echo "Output did not match '$expected_regexp'"
$negmatches_ok || \
echo "Output unexpectedly matched '$unexpected_regexp'"
fi
echo "Output from failed run:"
echo "---"
cat "$output"
echo "---"
return 1 # 1: failure
}
TMPDIR=/tmp/heap_check_death_info
rm -rf $TMPDIR || exit 1
mkdir $TMPDIR || exit 2
export HEAPCHECK=strict # default mode
# These invocations should pass (0 == PASS):
# This tests that turning leak-checker off dynamically works fine
Test 120 0 "^PASS$" "" HEAPCHECK="" || exit 1
# This disables threads so we can cause leaks reliably and test finding them
Test 120 0 "^PASS$" "" HEAP_CHECKER_TEST_NO_THREADS=1 || exit 2
# Test that --test_cancel_global_check works
Test 20 0 "Canceling .* whole-program .* leak check$" "" \
HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_TEST_CANCEL_GLOBAL_CHECK=1 || exit 3
Test 20 0 "Canceling .* whole-program .* leak check$" "" \
HEAP_CHECKER_TEST_TEST_LOOP_LEAK=1 HEAP_CHECKER_TEST_TEST_CANCEL_GLOBAL_CHECK=1 || exit 4
# Test that very early log messages are present and controllable:
EARLY_MSG="Starting tracking the heap$"
Test 60 0 "$EARLY_MSG" "" \
HEAPCHECK="" HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 \
PERFTOOLS_VERBOSE=10 || exit 5
Test 60 0 "MemoryRegionMap Init$" "" \
HEAPCHECK="" HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 \
PERFTOOLS_VERBOSE=11 || exit 6
Test 60 0 "" "$EARLY_MSG" \
HEAPCHECK="" HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 \
PERFTOOLS_VERBOSE=-11 || exit 7
# These invocations should fail with very high probability,
# rather than return 0 or hang (1 == exit(1), 134 == abort(), 139 = SIGSEGV):
Test 60 1 "Exiting .* because of .* leaks$" "" \
HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 || exit 8
Test 60 1 "Exiting .* because of .* leaks$" "" \
HEAP_CHECKER_TEST_TEST_LOOP_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 || exit 9
# Test that we produce a reasonable textual leak report.
Test 60 1 "MakeALeak" "" \
HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 \
|| exit 10
# Test that very early log messages are present and controllable:
Test 60 1 "Starting tracking the heap$" "" \
HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 PERFTOOLS_VERBOSE=10 \
|| exit 11
Test 60 1 "" "Starting tracking the heap" \
HEAP_CHECKER_TEST_TEST_LEAK=1 HEAP_CHECKER_TEST_NO_THREADS=1 PERFTOOLS_VERBOSE=-10 \
|| exit 12
cd / # so we're not in TMPDIR when we delete it
rm -rf $TMPDIR
echo "PASS"
exit 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,89 @@
#!/bin/sh
# 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: Craig Silverstein
#
# Runs the heap-checker unittest with various environment variables.
# This is necessary because we turn on features like the heap profiler
# and heap checker via environment variables. This test makes sure
# they all play well together.
# We expect BINDIR and PPROF_PATH to be set in the environment.
# If not, we set them to some reasonable values
BINDIR="${BINDIR:-.}"
PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
if [ "x$1" = "x-h" -o "$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir] [path to pprof]"
echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH"
exit 1
fi
HEAP_CHECKER="${1:-$BINDIR/heap-checker_unittest}"
PPROF_PATH="${2:-$PPROF_PATH}"
TMPDIR=/tmp/heap_check_info
rm -rf $TMPDIR || exit 2
mkdir $TMPDIR || exit 3
# $1: value of heap-check env. var.
run_check() {
export PPROF_PATH="$PPROF_PATH"
[ -n "$1" ] && export HEAPCHECK="$1" || unset HEAPPROFILE
echo -n "Testing $HEAP_CHECKER with HEAPCHECK=$1 ... "
if $HEAP_CHECKER > $TMPDIR/output 2>&1; then
echo "OK"
else
echo "FAILED"
echo "Output from the failed run:"
echo "----"
cat $TMPDIR/output
echo "----"
exit 4
fi
# If we set HEAPPROFILE, then we expect it to actually have emitted
# a profile. Check that it did.
if [ -n "$HEAPPROFILE" ]; then
[ -e "$HEAPPROFILE.0001.heap" ] || exit 5
fi
}
run_check ""
run_check "local"
run_check "normal"
run_check "strict"
rm -rf $TMPDIR # clean up
echo "PASS"

View file

@ -0,0 +1,168 @@
// -*- 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.
// ---
// Author: Craig Silverstein
//
// A small program that just exercises our heap profiler by allocating
// memory and letting the heap-profiler emit a profile. We don't test
// threads (TODO). By itself, this unittest tests that the heap-profiler
// doesn't crash on simple programs, but its output can be analyzed by
// another testing script to actually verify correctness. See, eg,
// heap-profiler_unittest.sh.
#include "config_for_unittests.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> // for mkdir()
#include <sys/stat.h> // for mkdir() on freebsd and os x
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for fork()
#endif
#include <sys/wait.h> // for wait()
#include <string>
#include "base/basictypes.h"
#include "base/logging.h"
#include <gperftools/heap-profiler.h>
using std::string;
static const int kMaxCount = 100000;
int* g_array[kMaxCount]; // an array of int-vectors
static ATTRIBUTE_NOINLINE void Allocate(int start, int end, int size) {
// NOTE: we're using this to prevent gcc 5 from merging otherwise
// identical Allocate & Allocate2 functions.
VLOG(10, "Allocate");
for (int i = start; i < end; ++i) {
if (i < kMaxCount)
g_array[i] = new int[size];
}
}
static ATTRIBUTE_NOINLINE void Allocate2(int start, int end, int size) {
VLOG(10, "Allocate2");
for (int i = start; i < end; ++i) {
if (i < kMaxCount)
g_array[i] = new int[size];
}
}
static void Deallocate(int start, int end) {
for (int i = start; i < end; ++i) {
delete[] g_array[i];
g_array[i] = 0;
}
}
static void TestHeapProfilerStartStopIsRunning() {
// If you run this with whole-program heap-profiling on, than
// IsHeapProfilerRunning should return true.
if (!IsHeapProfilerRunning()) {
const char* tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
mkdir(tmpdir, 0755); // if necessary
HeapProfilerStart((string(tmpdir) + "/start_stop").c_str());
CHECK(IsHeapProfilerRunning());
Allocate(0, 40, 100);
Deallocate(0, 40);
HeapProfilerStop();
CHECK(!IsHeapProfilerRunning());
}
}
static void TestDumpHeapProfiler() {
// If you run this with whole-program heap-profiling on, than
// IsHeapProfilerRunning should return true.
if (!IsHeapProfilerRunning()) {
const char* tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
mkdir(tmpdir, 0755); // if necessary
HeapProfilerStart((string(tmpdir) + "/dump").c_str());
CHECK(IsHeapProfilerRunning());
Allocate(0, 40, 100);
Deallocate(0, 40);
char* output = GetHeapProfile();
free(output);
HeapProfilerStop();
}
}
int main(int argc, char** argv) {
if (argc > 2 || (argc == 2 && argv[1][0] == '-')) {
printf("USAGE: %s [number of children to fork]\n", argv[0]);
exit(0);
}
int num_forks = 0;
if (argc == 2) {
num_forks = atoi(argv[1]);
}
TestHeapProfilerStartStopIsRunning();
TestDumpHeapProfiler();
Allocate(0, 40, 100);
Deallocate(0, 40);
Allocate(0, 40, 100);
Allocate(0, 40, 100);
Allocate2(40, 400, 1000);
Allocate2(400, 1000, 10000);
Deallocate(0, 1000);
Allocate(0, 100, 100000);
Deallocate(0, 10);
Deallocate(10, 20);
Deallocate(90, 100);
Deallocate(20, 90);
while (num_forks-- > 0) {
switch (fork()) {
case -1:
printf("FORK failed!\n");
return 1;
case 0: // child
return execl(argv[0], argv[0], NULL); // run child with no args
default:
wait(NULL); // we'll let the kids run one at a time
}
}
printf("DONE.\n");
return 0;
}

View file

@ -0,0 +1,147 @@
#!/bin/sh
# 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: Craig Silverstein
#
# Runs the heap-profiler unittest and makes sure the profile looks appropriate.
#
# We run under the assumption that if $HEAP_PROFILER is run with --help,
# it prints a usage line of the form
# USAGE: <actual executable being run> [...]
#
# This is because libtool sometimes turns the 'executable' into a
# shell script which runs an actual binary somewhere else.
# We expect BINDIR and PPROF_PATH to be set in the environment.
# If not, we set them to some reasonable values
BINDIR="${BINDIR:-.}"
PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir] [path to pprof]"
echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH"
exit 1
fi
HEAP_PROFILER="${1:-$BINDIR/heap-profiler_unittest}"
PPROF="${2:-$PPROF_PATH}"
TEST_TMPDIR=`mktemp -d /tmp/heap-profiler_unittest.XXXXXX`
# It's meaningful to the profiler, so make sure we know its state
unset HEAPPROFILE
num_failures=0
# Given one profile (to check the contents of that profile) or two
# profiles (to check the diff between the profiles), and a function
# name, verify that the function name takes up at least 90% of the
# allocated memory. The function name is actually specified first.
VerifyMemFunction() {
function="$1"
shift
# get program name. Note we have to unset HEAPPROFILE so running
# help doesn't overwrite existing profiles.
exec=`unset HEAPPROFILE; $HEAP_PROFILER --help | awk '{print $2; exit;}'`
if [ $# = 2 ]; then
[ -f "$1" ] || { echo "Profile not found: $1"; exit 1; }
[ -f "$2" ] || { echo "Profile not found: $2"; exit 1; }
$PPROF --base="$1" $exec "$2" >"$TEST_TMPDIR/output.pprof" 2>&1
else
[ -f "$1" ] || { echo "Profile not found: $1"; exit 1; }
$PPROF $exec "$1" >"$TEST_TMPDIR/output.pprof" 2>&1
fi
cat "$TEST_TMPDIR/output.pprof" \
| tr -d % | awk '$6 ~ /^'$function'$/ && $2 > 90 {exit 1;}'
if [ $? != 1 ]; then
echo
echo "--- Test failed for $function: didn't account for 90% of executable memory"
echo "--- Program output:"
cat "$TEST_TMPDIR/output"
echo "--- pprof output:"
cat "$TEST_TMPDIR/output.pprof"
echo "---"
num_failures=`expr $num_failures + 1`
fi
}
VerifyOutputContains() {
text="$1"
if ! grep "$text" "$TEST_TMPDIR/output" >/dev/null 2>&1; then
echo "--- Test failed: output does not contain '$text'"
echo "--- Program output:"
cat "$TEST_TMPDIR/output"
echo "---"
num_failures=`expr $num_failures + 1`
fi
}
HEAPPROFILE="$TEST_TMPDIR/test"
HEAP_PROFILE_INUSE_INTERVAL="10240" # need this to be 10Kb
HEAP_PROFILE_ALLOCATION_INTERVAL="$HEAP_PROFILE_INUSE_INTERVAL"
HEAP_PROFILE_DEALLOCATION_INTERVAL="$HEAP_PROFILE_INUSE_INTERVAL"
export HEAPPROFILE
export HEAP_PROFILE_INUSE_INTERVAL
export HEAP_PROFILE_ALLOCATION_INTERVAL
export HEAP_PROFILE_DEALLOCATION_INTERVAL
# We make the unittest run a child process, to test that the child
# process doesn't try to write a heap profile as well and step on the
# parent's toes. If it does, we expect the parent-test to fail.
$HEAP_PROFILER 1 >$TEST_TMPDIR/output 2>&1 # run program, with 1 child proc
VerifyMemFunction Allocate2 "$HEAPPROFILE.1329.heap"
VerifyMemFunction Allocate "$HEAPPROFILE.1448.heap" "$HEAPPROFILE.1548.heap"
# Check the child process got to emit its own profile as well.
VerifyMemFunction Allocate2 "$HEAPPROFILE"_*.1329.heap
VerifyMemFunction Allocate "$HEAPPROFILE"_*.1448.heap "$HEAPPROFILE"_*.1548.heap
# Make sure we logged both about allocating and deallocating memory
VerifyOutputContains "62 MB allocated"
VerifyOutputContains "62 MB freed"
# Now try running without --heap_profile specified, to allow
# testing of the HeapProfileStart/Stop functionality.
$HEAP_PROFILER >"$TEST_TMPDIR/output2" 2>&1
rm -rf $TEST_TMPDIR # clean up
if [ $num_failures = 0 ]; then
echo "PASS"
else
echo "Tests finished with $num_failures failures"
fi
exit $num_failures

View file

@ -0,0 +1,62 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// 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.
// This is a unit test for exercising fragmentation of large (over 1
// meg) page spans. It makes sure that allocations/releases of
// increasing memory chunks do not blowup memory
// usage. See also https://code.google.com/p/gperftools/issues/detail?id=368
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "base/logging.h"
#include "common.h"
#include <gperftools/malloc_extension.h>
int main (int argc, char** argv) {
for (int pass = 1; pass <= 3; pass++) {
size_t size = 100*1024*1024;
while (size < 500*1024*1024) {
void *ptr = malloc(size);
free(ptr);
size += 20000;
size_t heap_size = static_cast<size_t>(-1);
MallocExtension::instance()->GetNumericProperty("generic.heap_size",
&heap_size);
CHECK_LT(heap_size, 1*1024*1024*1024);
}
}
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,197 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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.
*/
// A test for low_level_alloc.cc
#include <stdio.h>
#include <map>
#include "base/low_level_alloc.h"
#include "base/logging.h"
#include <gperftools/malloc_hook.h>
using std::map;
// a block of memory obtained from the allocator
struct BlockDesc {
char *ptr; // pointer to memory
int len; // number of bytes
int fill; // filled with data starting with this
};
// Check that the pattern placed in the block d
// by RandomizeBlockDesc is still there.
static void CheckBlockDesc(const BlockDesc &d) {
for (int i = 0; i != d.len; i++) {
CHECK((d.ptr[i] & 0xff) == ((d.fill + i) & 0xff));
}
}
// Fill the block "*d" with a pattern
// starting with a random byte.
static void RandomizeBlockDesc(BlockDesc *d) {
d->fill = rand() & 0xff;
for (int i = 0; i != d->len; i++) {
d->ptr[i] = (d->fill + i) & 0xff;
}
}
// Use to indicate to the malloc hooks that
// this calls is from LowLevelAlloc.
static bool using_low_level_alloc = false;
// n times, toss a coin, and based on the outcome
// either allocate a new block or deallocate an old block.
// New blocks are placed in a map with a random key
// and initialized with RandomizeBlockDesc().
// If keys conflict, the older block is freed.
// Old blocks are always checked with CheckBlockDesc()
// before being freed. At the end of the run,
// all remaining allocated blocks are freed.
// If use_new_arena is true, use a fresh arena, and then delete it.
// If call_malloc_hook is true and user_arena is true,
// allocations and deallocations are reported via the MallocHook
// interface.
static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
typedef map<int, BlockDesc> AllocMap;
AllocMap allocated;
AllocMap::iterator it;
BlockDesc block_desc;
int rnd;
LowLevelAlloc::Arena *arena = 0;
if (use_new_arena) {
int32 flags = call_malloc_hook? LowLevelAlloc::kCallMallocHook : 0;
arena = LowLevelAlloc::NewArena(flags, LowLevelAlloc::DefaultArena());
}
for (int i = 0; i != n; i++) {
if (i != 0 && i % 10000 == 0) {
printf(".");
fflush(stdout);
}
switch(rand() & 1) { // toss a coin
case 0: // coin came up heads: add a block
using_low_level_alloc = true;
block_desc.len = rand() & 0x3fff;
block_desc.ptr =
reinterpret_cast<char *>(
arena == 0
? LowLevelAlloc::Alloc(block_desc.len)
: LowLevelAlloc::AllocWithArena(block_desc.len, arena));
using_low_level_alloc = false;
RandomizeBlockDesc(&block_desc);
rnd = rand();
it = allocated.find(rnd);
if (it != allocated.end()) {
CheckBlockDesc(it->second);
using_low_level_alloc = true;
LowLevelAlloc::Free(it->second.ptr);
using_low_level_alloc = false;
it->second = block_desc;
} else {
allocated[rnd] = block_desc;
}
break;
case 1: // coin came up tails: remove a block
it = allocated.begin();
if (it != allocated.end()) {
CheckBlockDesc(it->second);
using_low_level_alloc = true;
LowLevelAlloc::Free(it->second.ptr);
using_low_level_alloc = false;
allocated.erase(it);
}
break;
}
}
// remove all remaniing blocks
while ((it = allocated.begin()) != allocated.end()) {
CheckBlockDesc(it->second);
using_low_level_alloc = true;
LowLevelAlloc::Free(it->second.ptr);
using_low_level_alloc = false;
allocated.erase(it);
}
if (use_new_arena) {
CHECK(LowLevelAlloc::DeleteArena(arena));
}
}
// used for counting allocates and frees
static int32 allocates;
static int32 frees;
// called on each alloc if kCallMallocHook specified
static void AllocHook(const void *p, size_t size) {
if (using_low_level_alloc) {
allocates++;
}
}
// called on each free if kCallMallocHook specified
static void FreeHook(const void *p) {
if (using_low_level_alloc) {
frees++;
}
}
int main(int argc, char *argv[]) {
// This is needed by maybe_threads_unittest.sh, which parses argv[0]
// to figure out what directory low_level_alloc_unittest is in.
if (argc != 1) {
fprintf(stderr, "USAGE: %s\n", argv[0]);
return 1;
}
CHECK(MallocHook::AddNewHook(&AllocHook));
CHECK(MallocHook::AddDeleteHook(&FreeHook));
CHECK_EQ(allocates, 0);
CHECK_EQ(frees, 0);
Test(false, false, 50000);
CHECK_NE(allocates, 0); // default arena calls hooks
CHECK_NE(frees, 0);
for (int i = 0; i != 16; i++) {
bool call_hooks = ((i & 1) == 1);
allocates = 0;
frees = 0;
Test(true, call_hooks, 15000);
if (call_hooks) {
CHECK_GT(allocates, 5000); // arena calls hooks
CHECK_GT(frees, 5000);
} else {
CHECK_EQ(allocates, 0); // arena doesn't call hooks
CHECK_EQ(frees, 0);
}
}
printf("\nPASS\n");
CHECK(MallocHook::RemoveNewHook(&AllocHook));
CHECK(MallocHook::RemoveDeleteHook(&FreeHook));
return 0;
}

View file

@ -0,0 +1,182 @@
/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2009, 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: Craig Silverstein
*
* This tests the c shims: malloc_extension_c.h and malloc_hook_c.h.
* Mostly, we'll just care that these shims compile under gcc
* (*not* g++!)
*
* NOTE: this is C code, not C++ code!
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h> /* for size_t */
#include <gperftools/malloc_extension_c.h>
#include <gperftools/malloc_hook_c.h>
#define FAIL(msg) do { \
fprintf(stderr, "FATAL ERROR: %s\n", msg); \
exit(1); \
} while (0)
static int g_new_hook_calls = 0;
static int g_delete_hook_calls = 0;
void TestNewHook(const void* ptr, size_t size) {
g_new_hook_calls++;
}
void TestDeleteHook(const void* ptr) {
g_delete_hook_calls++;
}
static
void *forced_malloc(size_t size)
{
extern void *tc_malloc(size_t);
void *rv = tc_malloc(size);
if (!rv) {
FAIL("malloc is not supposed to fail here");
}
return rv;
}
void TestMallocHook(void) {
/* TODO(csilvers): figure out why we get:
* E0100 00:00:00.000000 7383 malloc_hook.cc:244] RAW: google_malloc section is missing, thus InHookCaller is broken!
*/
#if 0
void* result[5];
if (MallocHook_GetCallerStackTrace(result, sizeof(result)/sizeof(*result),
0) < 2) { /* should have this and main */
FAIL("GetCallerStackTrace failed");
}
#endif
if (!MallocHook_AddNewHook(&TestNewHook)) {
FAIL("Failed to add new hook");
}
if (!MallocHook_AddDeleteHook(&TestDeleteHook)) {
FAIL("Failed to add delete hook");
}
free(forced_malloc(10));
free(forced_malloc(20));
if (g_new_hook_calls != 2) {
FAIL("Wrong number of calls to the new hook");
}
if (g_delete_hook_calls != 2) {
FAIL("Wrong number of calls to the delete hook");
}
if (!MallocHook_RemoveNewHook(&TestNewHook)) {
FAIL("Failed to remove new hook");
}
if (!MallocHook_RemoveDeleteHook(&TestDeleteHook)) {
FAIL("Failed to remove delete hook");
}
free(forced_malloc(10));
free(forced_malloc(20));
if (g_new_hook_calls != 2) {
FAIL("Wrong number of calls to the new hook");
}
MallocHook_SetNewHook(&TestNewHook);
MallocHook_SetDeleteHook(&TestDeleteHook);
free(forced_malloc(10));
free(forced_malloc(20));
if (g_new_hook_calls != 4) {
FAIL("Wrong number of calls to the singular new hook");
}
if (MallocHook_SetNewHook(NULL) == NULL) {
FAIL("Failed to set new hook");
}
if (MallocHook_SetDeleteHook(NULL) == NULL) {
FAIL("Failed to set delete hook");
}
}
void TestMallocExtension(void) {
int blocks;
size_t total;
int hist[64];
char buffer[200];
char* x = (char*)malloc(10);
MallocExtension_VerifyAllMemory();
MallocExtension_VerifyMallocMemory(x);
MallocExtension_MallocMemoryStats(&blocks, &total, hist);
MallocExtension_GetStats(buffer, sizeof(buffer));
if (!MallocExtension_GetNumericProperty("generic.current_allocated_bytes",
&total)) {
FAIL("GetNumericProperty failed for generic.current_allocated_bytes");
}
if (total < 10) {
FAIL("GetNumericProperty had bad return for generic.current_allocated_bytes");
}
if (!MallocExtension_GetNumericProperty("generic.current_allocated_bytes",
&total)) {
FAIL("GetNumericProperty failed for generic.current_allocated_bytes");
}
MallocExtension_MarkThreadIdle();
MallocExtension_MarkThreadBusy();
MallocExtension_ReleaseToSystem(1);
MallocExtension_ReleaseFreeMemory();
if (MallocExtension_GetEstimatedAllocatedSize(10) < 10) {
FAIL("GetEstimatedAllocatedSize returned a bad value (too small)");
}
if (MallocExtension_GetAllocatedSize(x) < 10) {
FAIL("GetEstimatedAllocatedSize returned a bad value (too small)");
}
if (MallocExtension_GetOwnership(x) != MallocExtension_kOwned) {
FAIL("DidAllocatePtr returned a bad value (kNotOwned)");
}
/* TODO(csilvers): this relies on undocumented behavior that
GetOwnership works on stack-allocated variables. Use a better test. */
if (MallocExtension_GetOwnership(hist) != MallocExtension_kNotOwned) {
FAIL("DidAllocatePtr returned a bad value (kOwned)");
}
free(x);
}
int main(int argc, char** argv) {
TestMallocHook();
TestMallocExtension();
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,98 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Craig Silverstein
//
// Simple test of malloc_extension. Includes test of C shims.
#include "config_for_unittests.h"
#include <stdio.h>
#include <sys/types.h>
#include "base/logging.h"
#include <gperftools/malloc_extension.h>
#include <gperftools/malloc_extension_c.h>
int main(int argc, char** argv) {
void* a = malloc(1000);
size_t cxx_bytes_used, c_bytes_used;
ASSERT_TRUE(MallocExtension::instance()->GetNumericProperty(
"generic.current_allocated_bytes", &cxx_bytes_used));
ASSERT_TRUE(MallocExtension_GetNumericProperty(
"generic.current_allocated_bytes", &c_bytes_used));
ASSERT_GT(cxx_bytes_used, 1000);
ASSERT_EQ(cxx_bytes_used, c_bytes_used);
ASSERT_TRUE(MallocExtension::instance()->VerifyAllMemory());
ASSERT_TRUE(MallocExtension_VerifyAllMemory());
ASSERT_EQ(MallocExtension::kOwned,
MallocExtension::instance()->GetOwnership(a));
// TODO(csilvers): this relies on undocumented behavior that
// GetOwnership works on stack-allocated variables. Use a better test.
ASSERT_EQ(MallocExtension::kNotOwned,
MallocExtension::instance()->GetOwnership(&cxx_bytes_used));
ASSERT_EQ(MallocExtension::kNotOwned,
MallocExtension::instance()->GetOwnership(NULL));
ASSERT_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000);
// This is just a sanity check. If we allocated too much, tcmalloc is broken
ASSERT_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000);
ASSERT_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000);
for (int i = 0; i < 10; ++i) {
void *p = malloc(i);
ASSERT_GE(MallocExtension::instance()->GetAllocatedSize(p),
MallocExtension::instance()->GetEstimatedAllocatedSize(i));
free(p);
}
// Check the c-shim version too.
ASSERT_EQ(MallocExtension_kOwned, MallocExtension_GetOwnership(a));
ASSERT_EQ(MallocExtension_kNotOwned,
MallocExtension_GetOwnership(&cxx_bytes_used));
ASSERT_EQ(MallocExtension_kNotOwned, MallocExtension_GetOwnership(NULL));
ASSERT_GE(MallocExtension_GetAllocatedSize(a), 1000);
ASSERT_LE(MallocExtension_GetAllocatedSize(a), 5000);
ASSERT_GE(MallocExtension_GetEstimatedAllocatedSize(1000), 1000);
free(a);
// Verify that the .cc file and .h file have the same enum values.
ASSERT_EQ(static_cast<int>(MallocExtension::kUnknownOwnership),
static_cast<int>(MallocExtension_kUnknownOwnership));
ASSERT_EQ(static_cast<int>(MallocExtension::kOwned),
static_cast<int>(MallocExtension_kOwned));
ASSERT_EQ(static_cast<int>(MallocExtension::kNotOwned),
static_cast<int>(MallocExtension_kNotOwned));
printf("DONE\n");
return 0;
}

View file

@ -0,0 +1,367 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: llib@google.com (Bill Clarke)
#include "config_for_unittests.h"
#include <assert.h>
#include <stdio.h>
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for sleep()
#endif
#include <algorithm>
#include <string>
#include <vector>
#include <gperftools/malloc_hook.h>
#include "malloc_hook-inl.h"
#include "base/logging.h"
#include "base/simple_mutex.h"
#include "base/sysinfo.h"
#include "tests/testutil.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
namespace {
using std::string;
using std::vector;
vector<void (*)()> g_testlist; // the tests to run
#define TEST(a, b) \
struct Test_##a##_##b { \
Test_##a##_##b() { g_testlist.push_back(&Run); } \
static void Run(); \
}; \
static Test_##a##_##b g_test_##a##_##b; \
void Test_##a##_##b::Run()
static int RUN_ALL_TESTS() {
vector<void (*)()>::const_iterator it;
for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
(*it)(); // The test will error-exit if there's a problem.
}
fprintf(stderr, "\nPassed %d tests\n\nPASS\n",
static_cast<int>(g_testlist.size()));
return 0;
}
void Sleep(int seconds) {
#ifdef _MSC_VER
_sleep(seconds * 1000); // Windows's _sleep takes milliseconds argument
#else
sleep(seconds);
#endif
}
using std::min;
using base::internal::kHookListMaxValues;
// Since HookList is a template and is defined in malloc_hook.cc, we can only
// use an instantiation of it from malloc_hook.cc. We then reinterpret those
// values as integers for testing.
typedef base::internal::HookList<MallocHook::NewHook> TestHookList;
int TestHookList_Traverse(const TestHookList& list, uintptr_t* output_array, int n) {
MallocHook::NewHook values_as_hooks[kHookListMaxValues];
int result = list.Traverse(values_as_hooks, min(n, kHookListMaxValues));
for (int i = 0; i < result; ++i) {
output_array[i] = reinterpret_cast<const uintptr_t>(*values_as_hooks[i]);
}
return result;
}
bool TestHookList_Add(TestHookList* list, int val) {
return list->Add(reinterpret_cast<MallocHook::NewHook>(val));
}
bool TestHookList_Remove(TestHookList* list, int val) {
return list->Remove(reinterpret_cast<MallocHook::NewHook>(val));
}
// Note that this is almost the same as INIT_HOOK_LIST in malloc_hook.cc without
// the cast.
#define INIT_HOOK_LIST(initial_value) { 1, { initial_value } }
TEST(HookListTest, InitialValueExists) {
TestHookList list = INIT_HOOK_LIST(69);
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(1, TestHookList_Traverse(list, values, 2));
EXPECT_EQ(69, values[0]);
EXPECT_EQ(1, list.priv_end);
}
TEST(HookListTest, CanRemoveInitialValue) {
TestHookList list = INIT_HOOK_LIST(69);
ASSERT_TRUE(TestHookList_Remove(&list, 69));
EXPECT_EQ(0, list.priv_end);
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(0, TestHookList_Traverse(list, values, 2));
}
TEST(HookListTest, AddAppends) {
TestHookList list = INIT_HOOK_LIST(69);
ASSERT_TRUE(TestHookList_Add(&list, 42));
EXPECT_EQ(2, list.priv_end);
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(2, TestHookList_Traverse(list, values, 2));
EXPECT_EQ(69, values[0]);
EXPECT_EQ(42, values[1]);
}
TEST(HookListTest, RemoveWorksAndWillClearSize) {
TestHookList list = INIT_HOOK_LIST(69);
ASSERT_TRUE(TestHookList_Add(&list, 42));
ASSERT_TRUE(TestHookList_Remove(&list, 69));
EXPECT_EQ(2, list.priv_end);
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(1, TestHookList_Traverse(list, values, 2));
EXPECT_EQ(42, values[0]);
ASSERT_TRUE(TestHookList_Remove(&list, 42));
EXPECT_EQ(0, list.priv_end);
EXPECT_EQ(0, TestHookList_Traverse(list, values, 2));
}
TEST(HookListTest, AddPrependsAfterRemove) {
TestHookList list = INIT_HOOK_LIST(69);
ASSERT_TRUE(TestHookList_Add(&list, 42));
ASSERT_TRUE(TestHookList_Remove(&list, 69));
EXPECT_EQ(2, list.priv_end);
ASSERT_TRUE(TestHookList_Add(&list, 7));
EXPECT_EQ(2, list.priv_end);
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(2, TestHookList_Traverse(list, values, 2));
EXPECT_EQ(7, values[0]);
EXPECT_EQ(42, values[1]);
}
TEST(HookListTest, InvalidAddRejected) {
TestHookList list = INIT_HOOK_LIST(69);
EXPECT_FALSE(TestHookList_Add(&list, 0));
uintptr_t values[2] = { 0, 0 };
EXPECT_EQ(1, TestHookList_Traverse(list, values, 2));
EXPECT_EQ(69, values[0]);
EXPECT_EQ(1, list.priv_end);
}
TEST(HookListTest, FillUpTheList) {
TestHookList list = INIT_HOOK_LIST(69);
int num_inserts = 0;
while (TestHookList_Add(&list, ++num_inserts))
;
EXPECT_EQ(kHookListMaxValues, num_inserts);
EXPECT_EQ(kHookListMaxValues, list.priv_end);
uintptr_t values[kHookListMaxValues + 1];
EXPECT_EQ(kHookListMaxValues, TestHookList_Traverse(list, values,
kHookListMaxValues));
EXPECT_EQ(69, values[0]);
for (int i = 1; i < kHookListMaxValues; ++i) {
EXPECT_EQ(i, values[i]);
}
}
void MultithreadedTestThread(TestHookList* list, int shift,
int thread_num) {
string message;
char buf[64];
for (int i = 1; i < 1000; ++i) {
// In each loop, we insert a unique value, check it exists, remove it, and
// check it doesn't exist. We also record some stats to log at the end of
// each thread. Each insertion location and the length of the list is
// non-deterministic (except for the very first one, over all threads, and
// after the very last one the list should be empty).
int value = (i << shift) + thread_num;
EXPECT_TRUE(TestHookList_Add(list, value));
sched_yield(); // Ensure some more interleaving.
uintptr_t values[kHookListMaxValues + 1];
int num_values = TestHookList_Traverse(*list, values, kHookListMaxValues);
EXPECT_LT(0, num_values);
int value_index;
for (value_index = 0;
value_index < num_values && values[value_index] != value;
++value_index)
;
EXPECT_LT(value_index, num_values); // Should have found value.
snprintf(buf, sizeof(buf), "[%d/%d; ", value_index, num_values);
message += buf;
sched_yield();
EXPECT_TRUE(TestHookList_Remove(list, value));
sched_yield();
num_values = TestHookList_Traverse(*list, values, kHookListMaxValues);
for (value_index = 0;
value_index < num_values && values[value_index] != value;
++value_index)
;
EXPECT_EQ(value_index, num_values); // Should not have found value.
snprintf(buf, sizeof(buf), "%d]", num_values);
message += buf;
sched_yield();
}
fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str());
}
static volatile int num_threads_remaining;
static TestHookList list = INIT_HOOK_LIST(69);
static Mutex threadcount_lock;
void MultithreadedTestThreadRunner(int thread_num) {
// Wait for all threads to start running.
{
MutexLock ml(&threadcount_lock);
assert(num_threads_remaining > 0);
--num_threads_remaining;
// We should use condvars and the like, but for this test, we'll
// go simple and busy-wait.
while (num_threads_remaining > 0) {
threadcount_lock.Unlock();
Sleep(1);
threadcount_lock.Lock();
}
}
// shift is the smallest number such that (1<<shift) > kHookListMaxValues
int shift = 0;
for (int i = kHookListMaxValues; i > 0; i >>= 1)
shift += 1;
MultithreadedTestThread(&list, shift, thread_num);
}
TEST(HookListTest, MultithreadedTest) {
ASSERT_TRUE(TestHookList_Remove(&list, 69));
ASSERT_EQ(0, list.priv_end);
// Run kHookListMaxValues thread, each running MultithreadedTestThread.
// First, we need to set up the rest of the globals.
num_threads_remaining = kHookListMaxValues; // a global var
RunManyThreadsWithId(&MultithreadedTestThreadRunner, num_threads_remaining,
1 << 15);
uintptr_t values[kHookListMaxValues + 1];
EXPECT_EQ(0, TestHookList_Traverse(list, values, kHookListMaxValues));
EXPECT_EQ(0, list.priv_end);
}
// We only do mmap-hooking on (some) linux systems.
#if defined(HAVE_MMAP) && defined(__linux) && \
(defined(__i386__) || defined(__x86_64__) || defined(__PPC__))
int mmap_calls = 0;
int mmap_matching_calls = 0;
int munmap_calls = 0;
int munmap_matching_calls = 0;
const int kMmapMagicFd = 1;
void* const kMmapMagicPointer = reinterpret_cast<void*>(1);
int MmapReplacement(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result) {
++mmap_calls;
if (fd == kMmapMagicFd) {
++mmap_matching_calls;
*result = kMmapMagicPointer;
return true;
}
return false;
}
int MunmapReplacement(const void* ptr, size_t size, int* result) {
++munmap_calls;
if (ptr == kMmapMagicPointer) {
++munmap_matching_calls;
*result = 0;
return true;
}
return false;
}
TEST(MallocMookTest, MmapReplacements) {
mmap_calls = mmap_matching_calls = munmap_calls = munmap_matching_calls = 0;
MallocHook::SetMmapReplacement(&MmapReplacement);
MallocHook::SetMunmapReplacement(&MunmapReplacement);
EXPECT_EQ(kMmapMagicPointer, mmap(NULL, 1, PROT_READ, MAP_PRIVATE,
kMmapMagicFd, 0));
EXPECT_EQ(1, mmap_matching_calls);
char* ptr = reinterpret_cast<char*>(
mmap(NULL, 1, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
EXPECT_EQ(2, mmap_calls);
EXPECT_EQ(1, mmap_matching_calls);
ASSERT_NE(MAP_FAILED, ptr);
*ptr = 'a';
EXPECT_EQ(0, munmap(kMmapMagicPointer, 1));
EXPECT_EQ(1, munmap_calls);
EXPECT_EQ(1, munmap_matching_calls);
EXPECT_EQ(0, munmap(ptr, 1));
EXPECT_EQ(2, munmap_calls);
EXPECT_EQ(1, munmap_matching_calls);
// The DEATH test below is flaky, because we've just munmapped the memory,
// making it available for mmap()ing again. There is no guarantee that it
// will stay unmapped, and in fact it gets reused ~10% of the time.
// It the area is reused, then not only we don't die, but we also corrupt
// whoever owns that memory now.
// EXPECT_DEATH(*ptr = 'a', "SIGSEGV");
}
#endif // #ifdef HAVE_MMAP && linux && ...
} // namespace
int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,127 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2003, 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: Sanjay Ghemawat
//
// MallocExtension::MarkThreadIdle() testing
#include <stdio.h>
#include "config_for_unittests.h"
#include "base/logging.h"
#include <gperftools/malloc_extension.h>
#include "tests/testutil.h" // for RunThread()
// Helper routine to do lots of allocations
static void TestAllocation() {
static const int kNum = 100;
void* ptr[kNum];
for (int size = 8; size <= 65536; size*=2) {
for (int i = 0; i < kNum; i++) {
ptr[i] = malloc(size);
}
for (int i = 0; i < kNum; i++) {
free(ptr[i]);
}
}
}
// Routine that does a bunch of MarkThreadIdle() calls in sequence
// without any intervening allocations
static void MultipleIdleCalls() {
for (int i = 0; i < 4; i++) {
MallocExtension::instance()->MarkThreadIdle();
}
}
// Routine that does a bunch of MarkThreadIdle() calls in sequence
// with intervening allocations
static void MultipleIdleNonIdlePhases() {
for (int i = 0; i < 4; i++) {
TestAllocation();
MallocExtension::instance()->MarkThreadIdle();
}
}
// Get current thread cache usage
static size_t GetTotalThreadCacheSize() {
size_t result;
CHECK(MallocExtension::instance()->GetNumericProperty(
"tcmalloc.current_total_thread_cache_bytes",
&result));
return result;
}
// Check that MarkThreadIdle() actually reduces the amount
// of per-thread memory.
static void TestIdleUsage() {
const size_t original = GetTotalThreadCacheSize();
TestAllocation();
const size_t post_allocation = GetTotalThreadCacheSize();
CHECK_GT(post_allocation, original);
MallocExtension::instance()->MarkThreadIdle();
const size_t post_idle = GetTotalThreadCacheSize();
CHECK_LE(post_idle, original);
// Log after testing because logging can allocate heap memory.
VLOG(0, "Original usage: %zu\n", original);
VLOG(0, "Post allocation: %zu\n", post_allocation);
VLOG(0, "Post idle: %zu\n", post_idle);
}
static void TestTemporarilyIdleUsage() {
const size_t original = MallocExtension::instance()->GetThreadCacheSize();
TestAllocation();
const size_t post_allocation = MallocExtension::instance()->GetThreadCacheSize();
CHECK_GT(post_allocation, original);
MallocExtension::instance()->MarkThreadIdle();
const size_t post_idle = MallocExtension::instance()->GetThreadCacheSize();
CHECK_EQ(post_idle, 0);
// Log after testing because logging can allocate heap memory.
VLOG(0, "Original usage: %zu\n", original);
VLOG(0, "Post allocation: %zu\n", post_allocation);
VLOG(0, "Post idle: %zu\n", post_idle);
}
int main(int argc, char** argv) {
RunThread(&TestIdleUsage);
RunThread(&TestAllocation);
RunThread(&MultipleIdleCalls);
RunThread(&MultipleIdleNonIdlePhases);
RunThread(&TestTemporarilyIdleUsage);
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,79 @@
#!/bin/sh
# Copyright (c) 2007, 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: Craig Silverstein
#
# maybe_threads.cc was written to allow LD_PRELOAD=libtcmalloc.so to
# work even on binaries that were not linked with pthreads. This
# unittest tests that, by running low_level_alloc_unittest with an
# LD_PRELOAD. (low_level_alloc_unittest was chosen because it doesn't
# link in tcmalloc.)
#
# We assume all the .so files are in the same directory as both
# addressmap_unittest and profiler1_unittest. The reason we need
# profiler1_unittest is because it's instrumented to show the directory
# it's "really" in when run without any args. In practice this will either
# be BINDIR, or, when using libtool, BINDIR/.lib.
# We expect BINDIR to be set in the environment.
# If not, we set them to some reasonable values.
BINDIR="${BINDIR:-.}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir]"
echo " By default, unittest_dir=$BINDIR"
exit 1
fi
UNITTEST_DIR=${1:-$BINDIR}
# Figure out the "real" unittest directory. Also holds the .so files.
UNITTEST_DIR=`$UNITTEST_DIR/low_level_alloc_unittest --help 2>&1 \
| awk '{print $2; exit;}' \
| xargs dirname`
# Figure out where libtcmalloc lives. It should be in UNITTEST_DIR,
# but with libtool it might be in a subdir.
if [ -r "$UNITTEST_DIR/libtcmalloc_minimal.so" ]; then
LIB_PATH="$UNITTEST_DIR/libtcmalloc_minimal.so"
elif [ -r "$UNITTEST_DIR/.libs/libtcmalloc_minimal.so" ]; then
LIB_PATH="$UNITTEST_DIR/.libs/libtcmalloc_minimal.so"
elif [ -r "$UNITTEST_DIR/libtcmalloc_minimal.dylib" ]; then # for os x
LIB_PATH="$UNITTEST_DIR/libtcmalloc_minimal.dylib"
elif [ -r "$UNITTEST_DIR/.libs/libtcmalloc_minimal.dylib" ]; then
LIB_PATH="$UNITTEST_DIR/.libs/libtcmalloc_minimal.dylib"
else
echo "Cannot run $0: cannot find libtcmalloc_minimal.so"
exit 2
fi
LD_PRELOAD="$LIB_PATH" $UNITTEST_DIR/low_level_alloc_unittest

View file

@ -0,0 +1,221 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2004, 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: Sanjay Ghemawat
//
// Check memalign related routines.
//
// We can't really do a huge amount of checking, but at the very
// least, the following code checks that return values are properly
// aligned, and that writing into the objects works.
#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 Mac OS X, in order to define getpagesize. 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 getpagesize()
#endif
#include "tcmalloc.h" // must come early, to pick up posix_memalign
#include <assert.h>
#include <stdlib.h> // defines posix_memalign
#include <stdio.h> // for the printf at the end
#ifdef HAVE_STDINT_H
#include <stdint.h> // for uintptr_t
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for getpagesize()
#endif
// Malloc can be in several places on older versions of OS X.
#if defined(HAVE_MALLOC_H)
#include <malloc.h> // for memalign() and valloc()
#elif defined(HAVE_MALLOC_MALLOC_H)
#include <malloc/malloc.h>
#elif defined(HAVE_SYS_MALLOC_H)
#include <sys/malloc.h>
#endif
#include "base/basictypes.h"
#include "base/logging.h"
#include "tests/testutil.h"
// Return the next interesting size/delta to check. Returns -1 if no more.
static int NextSize(int size) {
if (size < 100) {
return size+1;
} else if (size < 1048576) {
// Find next power of two
int power = 1;
while (power < size) {
power <<= 1;
}
// Yield (power-1, power, power+1)
if (size < power-1) {
return power-1;
} else if (size == power-1) {
return power;
} else {
assert(size == power);
return power+1;
}
} else {
return -1;
}
}
// Shortform for cast
static uintptr_t Number(void* p) {
return reinterpret_cast<uintptr_t>(p);
}
// Check alignment
static void CheckAlignment(void* p, int align) {
if ((Number(p) & (align-1)) != 0)
LOG(FATAL, "wrong alignment; wanted 0x%x; got %p\n", align, p);
}
// Fill a buffer of the specified size with a predetermined pattern
static void Fill(void* p, int n, char seed) {
unsigned char* buffer = reinterpret_cast<unsigned char*>(p);
for (int i = 0; i < n; i++) {
buffer[i] = ((seed + i) & 0xff);
}
}
// Check that the specified buffer has the predetermined pattern
// generated by Fill()
static bool Valid(const void* p, int n, char seed) {
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(p);
for (int i = 0; i < n; i++) {
if (buffer[i] != ((seed + i) & 0xff)) {
return false;
}
}
return true;
}
int main(int argc, char** argv) {
SetTestResourceLimit();
// Try allocating data with a bunch of alignments and sizes
for (int a = 1; a < 1048576; a *= 2) {
for (int s = 0; s != -1; s = NextSize(s)) {
void* ptr = memalign(a, s);
CheckAlignment(ptr, a);
Fill(ptr, s, 'x');
CHECK(Valid(ptr, s, 'x'));
free(ptr);
if ((a >= sizeof(void*)) && ((a & (a-1)) == 0)) {
CHECK(posix_memalign(&ptr, a, s) == 0);
CheckAlignment(ptr, a);
Fill(ptr, s, 'y');
CHECK(Valid(ptr, s, 'y'));
free(ptr);
}
}
}
{
// Check various corner cases
void* p1 = memalign(1<<20, 1<<19);
void* p2 = memalign(1<<19, 1<<19);
void* p3 = memalign(1<<21, 1<<19);
CheckAlignment(p1, 1<<20);
CheckAlignment(p2, 1<<19);
CheckAlignment(p3, 1<<21);
Fill(p1, 1<<19, 'a');
Fill(p2, 1<<19, 'b');
Fill(p3, 1<<19, 'c');
CHECK(Valid(p1, 1<<19, 'a'));
CHECK(Valid(p2, 1<<19, 'b'));
CHECK(Valid(p3, 1<<19, 'c'));
free(p1);
free(p2);
free(p3);
}
{
// posix_memalign
void* ptr;
CHECK(posix_memalign(&ptr, 0, 1) == EINVAL);
CHECK(posix_memalign(&ptr, sizeof(void*)/2, 1) == EINVAL);
CHECK(posix_memalign(&ptr, sizeof(void*)+1, 1) == EINVAL);
CHECK(posix_memalign(&ptr, 4097, 1) == EINVAL);
// Grab some memory so that the big allocation below will definitely fail.
void* p_small = malloc(4*1048576);
CHECK(p_small != NULL);
// Make sure overflow is returned as ENOMEM
const size_t zero = 0;
static const size_t kMinusNTimes = 10;
for ( size_t i = 1; i < kMinusNTimes; ++i ) {
int r = posix_memalign(&ptr, 1024, zero - i);
CHECK(r == ENOMEM);
}
free(p_small);
}
const int pagesize = getpagesize();
{
// valloc
for (int s = 0; s != -1; s = NextSize(s)) {
void* p = valloc(s);
CheckAlignment(p, pagesize);
Fill(p, s, 'v');
CHECK(Valid(p, s, 'v'));
free(p);
}
}
{
// pvalloc
for (int s = 0; s != -1; s = NextSize(s)) {
void* p = pvalloc(s);
CheckAlignment(p, pagesize);
int alloc_needed = ((s + pagesize - 1) / pagesize) * pagesize;
Fill(p, alloc_needed, 'x');
CHECK(Valid(p, alloc_needed, 'x'));
free(p);
}
}
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,82 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Geoff Pike
#include <stdio.h>
#include "base/logging.h"
#include "packed-cache-inl.h"
static const int kHashbits = PackedCache<20>::kHashbits;
template <int kKeybits>
static size_t MustGet(const PackedCache<kKeybits>& cache, uintptr_t key) {
uint32 rv;
CHECK(cache.TryGet(key, &rv));
return rv;
}
template <int kKeybits>
static size_t Has(const PackedCache<kKeybits>& cache, uintptr_t key) {
uint32 dummy;
return cache.TryGet(key, &dummy);
}
// A basic sanity test.
void PackedCacheTest_basic() {
PackedCache<20> cache;
CHECK(!Has(cache, 0));
cache.Put(0, 17);
CHECK(Has(cache, 0));
CHECK_EQ(MustGet(cache, 0), 17);
cache.Put(19, 99);
CHECK_EQ(MustGet(cache, 0), 17);
CHECK_EQ(MustGet(cache, 19), 99);
// Knock <0, 17> out by using a conflicting key.
cache.Put(1 << kHashbits, 22);
CHECK(!Has(cache, 0));
CHECK_EQ(MustGet(cache, 1 << kHashbits), 22);
cache.Invalidate(19);
CHECK(!Has(cache, 19));
CHECK(!Has(cache, 0));
CHECK(Has(cache, 1 << kHashbits));
}
int main(int argc, char **argv) {
PackedCacheTest_basic();
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,202 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright 2009 Google Inc. All Rights Reserved.
// Author: fikes@google.com (Andrew Fikes)
//
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "config_for_unittests.h"
#include <stdio.h>
#include <memory>
#include "page_heap.h"
#include "system-alloc.h"
#include "base/logging.h"
#include "common.h"
DECLARE_int64(tcmalloc_heap_limit_mb);
namespace {
// The system will only release memory if the block size is equal or hight than
// system page size.
static bool HaveSystemRelease =
TCMalloc_SystemRelease(
TCMalloc_SystemAlloc(getpagesize(), NULL, 0), getpagesize());
static void CheckStats(const tcmalloc::PageHeap* ph,
uint64_t system_pages,
uint64_t free_pages,
uint64_t unmapped_pages) {
tcmalloc::PageHeap::Stats stats = ph->stats();
if (!HaveSystemRelease) {
free_pages += unmapped_pages;
unmapped_pages = 0;
}
EXPECT_EQ(system_pages, stats.system_bytes >> kPageShift);
EXPECT_EQ(free_pages, stats.free_bytes >> kPageShift);
EXPECT_EQ(unmapped_pages, stats.unmapped_bytes >> kPageShift);
}
static void TestPageHeap_Stats() {
std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
// Empty page heap
CheckStats(ph.get(), 0, 0, 0);
// Allocate a span 's1'
tcmalloc::Span* s1 = ph->New(256);
CheckStats(ph.get(), 256, 0, 0);
// Split span 's1' into 's1', 's2'. Delete 's2'
tcmalloc::Span* s2 = ph->Split(s1, 128);
ph->Delete(s2);
CheckStats(ph.get(), 256, 128, 0);
// Unmap deleted span 's2'
ph->ReleaseAtLeastNPages(1);
CheckStats(ph.get(), 256, 0, 128);
// Delete span 's1'
ph->Delete(s1);
CheckStats(ph.get(), 256, 128, 128);
}
// The number of kMaxPages-sized Spans we will allocate and free during the
// tests.
// We will also do twice this many kMaxPages/2-sized ones.
static constexpr int kNumberMaxPagesSpans = 10;
// Allocates all the last-level page tables we will need. Doing this before
// calculating the base heap usage is necessary, because otherwise if any of
// these are allocated during the main test it will throw the heap usage
// calculations off and cause the test to fail.
static void AllocateAllPageTables() {
// Make a separate PageHeap from the main test so the test can start without
// any pages in the lists.
std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
tcmalloc::Span *spans[kNumberMaxPagesSpans * 2];
for (int i = 0; i < kNumberMaxPagesSpans; ++i) {
spans[i] = ph->New(kMaxPages);
EXPECT_NE(spans[i], NULL);
}
for (int i = 0; i < kNumberMaxPagesSpans; ++i) {
ph->Delete(spans[i]);
}
for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) {
spans[i] = ph->New(kMaxPages >> 1);
EXPECT_NE(spans[i], NULL);
}
for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) {
ph->Delete(spans[i]);
}
}
static void TestPageHeap_Limit() {
AllocateAllPageTables();
std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
CHECK_EQ(kMaxPages, 1 << (20 - kPageShift));
// We do not know much is taken from the system for other purposes,
// so we detect the proper limit:
{
FLAGS_tcmalloc_heap_limit_mb = 1;
tcmalloc::Span* s = NULL;
while((s = ph->New(kMaxPages)) == NULL) {
FLAGS_tcmalloc_heap_limit_mb++;
}
FLAGS_tcmalloc_heap_limit_mb += kNumberMaxPagesSpans - 1;
ph->Delete(s);
// We are [10, 11) mb from the limit now.
}
// Test AllocLarge and GrowHeap first:
{
tcmalloc::Span * spans[kNumberMaxPagesSpans];
for (int i=0; i<kNumberMaxPagesSpans; ++i) {
spans[i] = ph->New(kMaxPages);
EXPECT_NE(spans[i], NULL);
}
EXPECT_EQ(ph->New(kMaxPages), NULL);
for (int i=0; i<kNumberMaxPagesSpans; i += 2) {
ph->Delete(spans[i]);
}
tcmalloc::Span *defragmented =
ph->New(kNumberMaxPagesSpans / 2 * kMaxPages);
if (HaveSystemRelease) {
// EnsureLimit should release deleted normal spans
EXPECT_NE(defragmented, NULL);
EXPECT_TRUE(ph->CheckExpensive());
ph->Delete(defragmented);
}
else
{
EXPECT_EQ(defragmented, NULL);
EXPECT_TRUE(ph->CheckExpensive());
}
for (int i=1; i<kNumberMaxPagesSpans; i += 2) {
ph->Delete(spans[i]);
}
}
// Once again, testing small lists this time (twice smaller spans):
{
tcmalloc::Span * spans[kNumberMaxPagesSpans * 2];
for (int i=0; i<kNumberMaxPagesSpans * 2; ++i) {
spans[i] = ph->New(kMaxPages >> 1);
EXPECT_NE(spans[i], NULL);
}
// one more half size allocation may be possible:
tcmalloc::Span * lastHalf = ph->New(kMaxPages >> 1);
EXPECT_EQ(ph->New(kMaxPages >> 1), NULL);
for (int i=0; i<kNumberMaxPagesSpans * 2; i += 2) {
ph->Delete(spans[i]);
}
for (Length len = kMaxPages >> 2;
len < kNumberMaxPagesSpans / 2 * kMaxPages; len = len << 1) {
if(len <= kMaxPages >> 1 || HaveSystemRelease) {
tcmalloc::Span *s = ph->New(len);
EXPECT_NE(s, NULL);
ph->Delete(s);
}
}
EXPECT_TRUE(ph->CheckExpensive());
for (int i=1; i<kNumberMaxPagesSpans * 2; i += 2) {
ph->Delete(spans[i]);
}
if (lastHalf != NULL) {
ph->Delete(lastHalf);
}
}
}
} // namespace
int main(int argc, char **argv) {
TestPageHeap_Stats();
TestPageHeap_Limit();
printf("PASS\n");
// on windows as part of library destructors we call getenv which
// calls malloc which fails due to our exhausted heap limit. It then
// causes fancy stack overflow because log message we're printing
// for failed allocation somehow cause malloc calls too
//
// To keep us out of trouble we just drop malloc limit
FLAGS_tcmalloc_heap_limit_mb = 0;
return 0;
}

View file

@ -0,0 +1,178 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2003, 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: Sanjay Ghemawat
#include "config_for_unittests.h"
#include <stdio.h>
#include <stdlib.h>
#if defined HAVE_STDINT_H
#include <stdint.h> // to get intptr_t
#elif defined HAVE_INTTYPES_H
#include <inttypes.h> // another place intptr_t might be defined
#endif
#include <sys/types.h>
#include <vector>
#include "base/logging.h"
#include "pagemap.h"
using std::vector;
static void Permute(vector<intptr_t>* elements) {
if (elements->empty())
return;
const size_t num_elements = elements->size();
for (size_t i = num_elements - 1; i > 0; --i) {
const size_t newpos = rand() % (i + 1);
const intptr_t tmp = (*elements)[i]; // swap
(*elements)[i] = (*elements)[newpos];
(*elements)[newpos] = tmp;
}
}
// Note: we leak memory every time a map is constructed, so do not
// create too many maps.
// Test specified map type
template <class Type>
void TestMap(int limit, bool limit_is_below_the_overflow_boundary) {
RAW_LOG(INFO, "Running test with %d iterations...\n", limit);
{ // Test sequential ensure/assignment
Type map(malloc);
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
map.Ensure(i, 1);
map.set(i, (void*)(i+1));
CHECK_EQ(map.get(i), (void*)(i+1));
}
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
CHECK_EQ(map.get(i), (void*)(i+1));
}
}
{ // Test bulk Ensure
Type map(malloc);
map.Ensure(0, limit);
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
map.set(i, (void*)(i+1));
CHECK_EQ(map.get(i), (void*)(i+1));
}
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
CHECK_EQ(map.get(i), (void*)(i+1));
}
}
// Test that we correctly notice overflow
{
Type map(malloc);
CHECK_EQ(map.Ensure(limit, limit+1), limit_is_below_the_overflow_boundary);
}
{ // Test randomized accesses
srand(301); // srand isn't great, but it's portable
vector<intptr_t> elements;
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) elements.push_back(i);
Permute(&elements);
Type map(malloc);
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
map.Ensure(elements[i], 1);
map.set(elements[i], (void*)(elements[i]+1));
CHECK_EQ(map.get(elements[i]), (void*)(elements[i]+1));
}
for (intptr_t i = 0; i < static_cast<intptr_t>(limit); i++) {
CHECK_EQ(map.get(i), (void*)(i+1));
}
}
}
// REQUIRES: BITS==10, i.e., valid range is [0,1023].
// Representations for different types will end up being:
// PageMap1: array[1024]
// PageMap2: array[32][32]
// PageMap3: array[16][16][4]
template <class Type>
void TestNext(const char* name) {
RAW_LOG(ERROR, "Running NextTest %s\n", name);
Type map(malloc);
char a, b, c, d, e;
// When map is empty
CHECK(map.Next(0) == NULL);
CHECK(map.Next(5) == NULL);
CHECK(map.Next(1<<30) == NULL);
// Add a single value
map.Ensure(40, 1);
map.set(40, &a);
CHECK(map.Next(0) == &a);
CHECK(map.Next(39) == &a);
CHECK(map.Next(40) == &a);
CHECK(map.Next(41) == NULL);
CHECK(map.Next(1<<30) == NULL);
// Add a few values
map.Ensure(41, 1);
map.Ensure(100, 3);
map.set(41, &b);
map.set(100, &c);
map.set(101, &d);
map.set(102, &e);
CHECK(map.Next(0) == &a);
CHECK(map.Next(39) == &a);
CHECK(map.Next(40) == &a);
CHECK(map.Next(41) == &b);
CHECK(map.Next(42) == &c);
CHECK(map.Next(63) == &c);
CHECK(map.Next(64) == &c);
CHECK(map.Next(65) == &c);
CHECK(map.Next(99) == &c);
CHECK(map.Next(100) == &c);
CHECK(map.Next(101) == &d);
CHECK(map.Next(102) == &e);
CHECK(map.Next(103) == NULL);
}
int main(int argc, char** argv) {
TestMap< TCMalloc_PageMap1<10> > (100, true);
TestMap< TCMalloc_PageMap1<10> > (1 << 10, false);
TestMap< TCMalloc_PageMap2<20> > (100, true);
TestMap< TCMalloc_PageMap2<20> > (1 << 20, false);
TestMap< TCMalloc_PageMap3<20> > (100, true);
TestMap< TCMalloc_PageMap3<20> > (1 << 20, false);
TestNext< TCMalloc_PageMap1<10> >("PageMap1");
TestNext< TCMalloc_PageMap2<10> >("PageMap2");
TestNext< TCMalloc_PageMap3<10> >("PageMap3");
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,398 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright 2009 Google Inc. All Rights Reserved.
// Author: Nabeel Mian (nabeelmian@google.com)
// Chris Demetriou (cgd@google.com)
//
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
//
//
// This file contains the unit tests for profile-handler.h interface.
#include "config.h"
#include "profile-handler.h"
#include <assert.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include "base/logging.h"
#include "base/simple_mutex.h"
// Some helpful macros for the test class
#define TEST_F(cls, fn) void cls :: fn()
// Do we expect the profiler to be enabled?
DEFINE_bool(test_profiler_enabled, true,
"expect profiler to be enabled during tests");
namespace {
// TODO(csilvers): error-checking on the pthreads routines
class Thread {
public:
Thread() : joinable_(false) { }
virtual ~Thread() { }
void SetJoinable(bool value) { joinable_ = value; }
void Start() {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, joinable_ ? PTHREAD_CREATE_JOINABLE
: PTHREAD_CREATE_DETACHED);
pthread_create(&thread_, &attr, &DoRun, this);
pthread_attr_destroy(&attr);
}
void Join() {
assert(joinable_);
pthread_join(thread_, NULL);
}
virtual void Run() = 0;
private:
static void* DoRun(void* cls) {
ProfileHandlerRegisterThread();
reinterpret_cast<Thread*>(cls)->Run();
return NULL;
}
pthread_t thread_;
bool joinable_;
};
// Sleep interval in nano secs. ITIMER_PROF goes off only afer the specified CPU
// time is consumed. Under heavy load this process may no get scheduled in a
// timely fashion. Therefore, give enough time (20x of ProfileHandle timer
// interval 10ms (100Hz)) for this process to accumulate enought CPU time to get
// a profile tick.
int kSleepInterval = 200000000;
// Sleep interval in nano secs. To ensure that if the timer has expired it is
// reset.
int kTimerResetInterval = 5000000;
static bool linux_per_thread_timers_mode_ = false;
static int timer_type_ = ITIMER_PROF;
// Delays processing by the specified number of nano seconds. 'delay_ns'
// must be less than the number of nano seconds in a second (1000000000).
void Delay(int delay_ns) {
static const int kNumNSecInSecond = 1000000000;
EXPECT_LT(delay_ns, kNumNSecInSecond);
struct timespec delay = { 0, delay_ns };
nanosleep(&delay, 0);
}
// Checks whether the profile timer is enabled for the current thread.
bool IsTimerEnabled() {
itimerval current_timer;
EXPECT_EQ(0, getitimer(timer_type_, &current_timer));
if ((current_timer.it_value.tv_sec == 0) &&
(current_timer.it_value.tv_usec != 0)) {
// May be the timer has expired. Sleep for a bit and check again.
Delay(kTimerResetInterval);
EXPECT_EQ(0, getitimer(timer_type_, &current_timer));
}
return (current_timer.it_value.tv_sec != 0 ||
current_timer.it_value.tv_usec != 0);
}
// Dummy worker thread to accumulate cpu time.
class BusyThread : public Thread {
public:
BusyThread() : stop_work_(false) {
}
// Setter/Getters
bool stop_work() {
MutexLock lock(&mu_);
return stop_work_;
}
void set_stop_work(bool stop_work) {
MutexLock lock(&mu_);
stop_work_ = stop_work;
}
private:
// Protects stop_work_ below.
Mutex mu_;
// Whether to stop work?
bool stop_work_;
// Do work until asked to stop.
void Run() {
while (!stop_work()) {
}
}
};
class NullThread : public Thread {
private:
void Run() {
}
};
// Signal handler which tracks the profile timer ticks.
static void TickCounter(int sig, siginfo_t* sig_info, void *vuc,
void* tick_counter) {
int* counter = static_cast<int*>(tick_counter);
++(*counter);
}
// This class tests the profile-handler.h interface.
class ProfileHandlerTest {
protected:
// Determines the timer type.
static void SetUpTestCase() {
timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
#if HAVE_LINUX_SIGEV_THREAD_ID
linux_per_thread_timers_mode_ = (getenv("CPUPROFILE_PER_THREAD_TIMERS") != NULL);
const char *signal_number = getenv("CPUPROFILE_TIMER_SIGNAL");
if (signal_number) {
//signal_number_ = strtol(signal_number, NULL, 0);
linux_per_thread_timers_mode_ = true;
Delay(kTimerResetInterval);
}
#endif
}
// Sets up the profile timers and SIGPROF/SIGALRM handler in a known state.
// It does the following:
// 1. Unregisters all the callbacks, stops the timer and clears out
// timer_sharing state in the ProfileHandler. This clears out any state
// left behind by the previous test or during module initialization when
// the test program was started.
// 3. Starts a busy worker thread to accumulate CPU usage.
virtual void SetUp() {
// Reset the state of ProfileHandler between each test. This unregisters
// all callbacks and stops the timer.
ProfileHandlerReset();
EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
// Start worker to accumulate cpu usage.
StartWorker();
}
virtual void TearDown() {
ProfileHandlerReset();
// Stops the worker thread.
StopWorker();
}
// Starts a busy worker thread to accumulate cpu time. There should be only
// one busy worker running. This is required for the case where there are
// separate timers for each thread.
void StartWorker() {
busy_worker_ = new BusyThread();
busy_worker_->SetJoinable(true);
busy_worker_->Start();
// Wait for worker to start up and register with the ProfileHandler.
// TODO(nabeelmian) This may not work under very heavy load.
Delay(kSleepInterval);
}
// Stops the worker thread.
void StopWorker() {
busy_worker_->set_stop_work(true);
busy_worker_->Join();
delete busy_worker_;
}
// Gets the number of callbacks registered with the ProfileHandler.
uint32 GetCallbackCount() {
ProfileHandlerState state;
ProfileHandlerGetState(&state);
return state.callback_count;
}
// Gets the current ProfileHandler interrupt count.
uint64 GetInterruptCount() {
ProfileHandlerState state;
ProfileHandlerGetState(&state);
return state.interrupts;
}
// Verifies that a callback is correctly registered and receiving
// profile ticks.
void VerifyRegistration(const int& tick_counter) {
// Check the callback count.
EXPECT_GT(GetCallbackCount(), 0);
// Check that the profile timer is enabled.
EXPECT_EQ(FLAGS_test_profiler_enabled, linux_per_thread_timers_mode_ || IsTimerEnabled());
uint64 interrupts_before = GetInterruptCount();
// Sleep for a bit and check that tick counter is making progress.
int old_tick_count = tick_counter;
Delay(kSleepInterval);
int new_tick_count = tick_counter;
uint64 interrupts_after = GetInterruptCount();
if (FLAGS_test_profiler_enabled) {
EXPECT_GT(new_tick_count, old_tick_count);
EXPECT_GT(interrupts_after, interrupts_before);
} else {
EXPECT_EQ(new_tick_count, old_tick_count);
EXPECT_EQ(interrupts_after, interrupts_before);
}
}
// Verifies that a callback is not receiving profile ticks.
void VerifyUnregistration(const int& tick_counter) {
// Sleep for a bit and check that tick counter is not making progress.
int old_tick_count = tick_counter;
Delay(kSleepInterval);
int new_tick_count = tick_counter;
EXPECT_EQ(old_tick_count, new_tick_count);
// If no callbacks, timer should be disabled.
if (GetCallbackCount() == 0) {
EXPECT_FALSE(IsTimerEnabled());
}
}
// Verifies that the timer is disabled. Expects the worker to be running.
void VerifyDisabled() {
// Check that the callback count is 0.
EXPECT_EQ(0, GetCallbackCount());
// Check that the timer is disabled.
EXPECT_FALSE(IsTimerEnabled());
// Verify that the ProfileHandler is not accumulating profile ticks.
uint64 interrupts_before = GetInterruptCount();
Delay(kSleepInterval);
uint64 interrupts_after = GetInterruptCount();
EXPECT_EQ(interrupts_before, interrupts_after);
}
// Registers a callback and waits for kTimerResetInterval for timers to get
// reset.
ProfileHandlerToken* RegisterCallback(void* callback_arg) {
ProfileHandlerToken* token = ProfileHandlerRegisterCallback(
TickCounter, callback_arg);
Delay(kTimerResetInterval);
return token;
}
// Unregisters a callback and waits for kTimerResetInterval for timers to get
// reset.
void UnregisterCallback(ProfileHandlerToken* token) {
ProfileHandlerUnregisterCallback(token);
Delay(kTimerResetInterval);
}
// Busy worker thread to accumulate cpu usage.
BusyThread* busy_worker_;
private:
// The tests to run
void RegisterUnregisterCallback();
void MultipleCallbacks();
void Reset();
void RegisterCallbackBeforeThread();
public:
#define RUN(test) do { \
printf("Running %s\n", #test); \
ProfileHandlerTest pht; \
pht.SetUp(); \
pht.test(); \
pht.TearDown(); \
} while (0)
static int RUN_ALL_TESTS() {
SetUpTestCase();
RUN(RegisterUnregisterCallback);
RUN(MultipleCallbacks);
RUN(Reset);
RUN(RegisterCallbackBeforeThread);
printf("Done\n");
return 0;
}
};
// Verifies ProfileHandlerRegisterCallback and
// ProfileHandlerUnregisterCallback.
TEST_F(ProfileHandlerTest, RegisterUnregisterCallback) {
int tick_count = 0;
ProfileHandlerToken* token = RegisterCallback(&tick_count);
VerifyRegistration(tick_count);
UnregisterCallback(token);
VerifyUnregistration(tick_count);
}
// Verifies that multiple callbacks can be registered.
TEST_F(ProfileHandlerTest, MultipleCallbacks) {
// Register first callback.
int first_tick_count = 0;
ProfileHandlerToken* token1 = RegisterCallback(&first_tick_count);
// Check that callback was registered correctly.
VerifyRegistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
// Register second callback.
int second_tick_count = 0;
ProfileHandlerToken* token2 = RegisterCallback(&second_tick_count);
// Check that callback was registered correctly.
VerifyRegistration(second_tick_count);
EXPECT_EQ(2, GetCallbackCount());
// Unregister first callback.
UnregisterCallback(token1);
VerifyUnregistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
// Verify that second callback is still registered.
VerifyRegistration(second_tick_count);
// Unregister second callback.
UnregisterCallback(token2);
VerifyUnregistration(second_tick_count);
EXPECT_EQ(0, GetCallbackCount());
// Verify that the timers is correctly disabled.
if (!linux_per_thread_timers_mode_) VerifyDisabled();
}
// Verifies ProfileHandlerReset
TEST_F(ProfileHandlerTest, Reset) {
// Verify that the profile timer interrupt is disabled.
if (!linux_per_thread_timers_mode_) VerifyDisabled();
int first_tick_count = 0;
RegisterCallback(&first_tick_count);
VerifyRegistration(first_tick_count);
EXPECT_EQ(1, GetCallbackCount());
// Register second callback.
int second_tick_count = 0;
RegisterCallback(&second_tick_count);
VerifyRegistration(second_tick_count);
EXPECT_EQ(2, GetCallbackCount());
// Reset the profile handler and verify that callback were correctly
// unregistered and the timer is disabled.
ProfileHandlerReset();
VerifyUnregistration(first_tick_count);
VerifyUnregistration(second_tick_count);
if (!linux_per_thread_timers_mode_) VerifyDisabled();
}
// Verifies that ProfileHandler correctly handles a case where a callback was
// registered before the second thread started.
TEST_F(ProfileHandlerTest, RegisterCallbackBeforeThread) {
// Stop the worker.
StopWorker();
// Unregister all existing callbacks and stop the timer.
ProfileHandlerReset();
EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
// Start the worker.
StartWorker();
// Register a callback and check that profile ticks are being delivered and
// the timer is enabled.
int tick_count = 0;
RegisterCallback(&tick_count);
EXPECT_EQ(1, GetCallbackCount());
VerifyRegistration(tick_count);
EXPECT_EQ(FLAGS_test_profiler_enabled, linux_per_thread_timers_mode_ || IsTimerEnabled());
}
} // namespace
int main(int argc, char** argv) {
return ProfileHandlerTest::RUN_ALL_TESTS();
}

View file

@ -0,0 +1,612 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Chris Demetriou
//
// This file contains the unit tests for the ProfileData class.
#if defined HAVE_STDINT_H
#include <stdint.h> // to get uintptr_t
#elif defined HAVE_INTTYPES_H
#include <inttypes.h> // another place uintptr_t might be defined
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include "profiledata.h"
#include "base/commandlineflags.h"
#include "base/logging.h"
using std::string;
// Some helpful macros for the test class
#define TEST_F(cls, fn) void cls :: fn()
namespace {
template<typename T> class scoped_array {
public:
scoped_array(T* data) : data_(data) { }
~scoped_array() { delete[] data_; }
T* get() { return data_; }
T& operator[](int i) { return data_[i]; }
private:
T* const data_;
};
// Re-runs fn until it doesn't cause EINTR.
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
// Read up to "count" bytes from file descriptor "fd" into the buffer
// starting at "buf" while handling short reads and EINTR. On
// success, return the number of bytes read. Otherwise, return -1.
static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
CHECK_GE(fd, 0);
char *buf0 = reinterpret_cast<char *>(buf);
ssize_t num_bytes = 0;
while (num_bytes < count) {
ssize_t len;
NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
if (len < 0) { // There was an error other than EINTR.
return -1;
}
if (len == 0) { // Reached EOF.
break;
}
num_bytes += len;
}
CHECK(num_bytes <= count);
return num_bytes;
}
// Thin wrapper around a file descriptor so that the file descriptor
// gets closed for sure.
struct FileDescriptor {
const int fd_;
explicit FileDescriptor(int fd) : fd_(fd) {}
~FileDescriptor() {
if (fd_ >= 0) {
NO_INTR(close(fd_));
}
}
int get() { return fd_; }
};
// must be the same as with ProfileData::Slot.
typedef uintptr_t ProfileDataSlot;
// Quick and dirty function to make a number into a void* for use in a
// sample.
inline void* V(intptr_t x) { return reinterpret_cast<void*>(x); }
// String returned by ProfileDataChecker helper functions to indicate success.
const char kNoError[] = "";
class ProfileDataChecker {
public:
ProfileDataChecker() {
const char* tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
mkdir(tmpdir, 0755); // if necessary
filename_ = string(tmpdir) + "/profiledata_unittest.tmp";
}
string filename() const { return filename_; }
// Checks the first 'num_slots' profile data slots in the file
// against the data pointed to by 'slots'. Returns kNoError if the
// data matched, otherwise returns an indication of the cause of the
// mismatch.
string Check(const ProfileDataSlot* slots, int num_slots) {
return CheckWithSkips(slots, num_slots, NULL, 0);
}
// Checks the first 'num_slots' profile data slots in the file
// against the data pointed to by 'slots', skipping over entries
// described by 'skips' and 'num_skips'.
//
// 'skips' must be a sorted list of (0-based) slot numbers to be
// skipped, of length 'num_skips'. Note that 'num_slots' includes
// any skipped slots, i.e., the first 'num_slots' profile data slots
// will be considered, but some may be skipped.
//
// Returns kNoError if the data matched, otherwise returns an
// indication of the cause of the mismatch.
string CheckWithSkips(const ProfileDataSlot* slots, int num_slots,
const int* skips, int num_skips);
// Validate that a profile is correctly formed. The profile is
// assumed to have been created by the same kind of binary (e.g.,
// same slot size, same endian, etc.) as is validating the profile.
//
// Returns kNoError if the profile appears valid, otherwise returns
// an indication of the problem with the profile.
string ValidateProfile();
private:
string filename_;
};
string ProfileDataChecker::CheckWithSkips(const ProfileDataSlot* slots,
int num_slots, const int* skips,
int num_skips) {
FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
if (fd.get() < 0)
return "file open error";
scoped_array<ProfileDataSlot> filedata(new ProfileDataSlot[num_slots]);
size_t expected_bytes = num_slots * sizeof filedata[0];
ssize_t bytes_read = ReadPersistent(fd.get(), filedata.get(), expected_bytes);
if (expected_bytes != bytes_read)
return "file too small";
for (int i = 0; i < num_slots; i++) {
if (num_skips > 0 && *skips == i) {
num_skips--;
skips++;
continue;
}
if (slots[i] != filedata[i])
return "data mismatch";
}
return kNoError;
}
string ProfileDataChecker::ValidateProfile() {
FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
if (fd.get() < 0)
return "file open error";
struct stat statbuf;
if (fstat(fd.get(), &statbuf) != 0)
return "fstat error";
if (statbuf.st_size != static_cast<ssize_t>(statbuf.st_size))
return "file impossibly large";
ssize_t filesize = statbuf.st_size;
scoped_array<char> filedata(new char[filesize]);
if (ReadPersistent(fd.get(), filedata.get(), filesize) != filesize)
return "read of whole file failed";
// Must have enough data for the header and the trailer.
if (filesize < (5 + 3) * sizeof(ProfileDataSlot))
return "not enough data in profile for header + trailer";
// Check the header
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[0] != 0)
return "error in header: non-zero count";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[1] != 3)
return "error in header: num_slots != 3";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[2] != 0)
return "error in header: non-zero format version";
// Period (slot 3) can have any value.
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[4] != 0)
return "error in header: non-zero padding value";
ssize_t cur_offset = 5 * sizeof(ProfileDataSlot);
// While there are samples, skip them. Each sample consists of
// at least three slots.
bool seen_trailer = false;
while (!seen_trailer) {
if (cur_offset > filesize - 3 * sizeof(ProfileDataSlot))
return "truncated sample header";
ProfileDataSlot* sample =
reinterpret_cast<ProfileDataSlot*>(filedata.get() + cur_offset);
ProfileDataSlot slots_this_sample = 2 + sample[1];
ssize_t size_this_sample = slots_this_sample * sizeof(ProfileDataSlot);
if (cur_offset > filesize - size_this_sample)
return "truncated sample";
if (sample[0] == 0 && sample[1] == 1 && sample[2] == 0) {
seen_trailer = true;
} else {
if (sample[0] < 1)
return "error in sample: sample count < 1";
if (sample[1] < 1)
return "error in sample: num_pcs < 1";
for (int i = 2; i < slots_this_sample; i++) {
if (sample[i] == 0)
return "error in sample: NULL PC";
}
}
cur_offset += size_this_sample;
}
// There must be at least one line in the (text) list of mapped objects,
// and it must be terminated by a newline. Note, the use of newline
// here and below Might not be reasonable on non-UNIX systems.
if (cur_offset >= filesize)
return "no list of mapped objects";
if (filedata[filesize - 1] != '\n')
return "profile did not end with a complete line";
while (cur_offset < filesize) {
char* line_start = filedata.get() + cur_offset;
// Find the end of the line, and replace it with a NUL for easier
// scanning.
char* line_end = strchr(line_start, '\n');
*line_end = '\0';
// Advance past any leading space. It's allowed in some lines,
// but not in others.
bool has_leading_space = false;
char* line_cur = line_start;
while (*line_cur == ' ') {
has_leading_space = true;
line_cur++;
}
bool found_match = false;
// Check for build lines.
if (!found_match) {
found_match = (strncmp(line_cur, "build=", 6) == 0);
// Anything may follow "build=", and leading space is allowed.
}
// A line from ProcMapsIterator::FormatLine, of the form:
//
// 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
//
// Leading space is not allowed. The filename may be omitted or
// may consist of multiple words, so we scan only up to the
// space before the filename.
if (!found_match) {
int chars_scanned = -1;
sscanf(line_cur, "%*x-%*x %*c%*c%*c%*c %*x %*x:%*x %*d %n",
&chars_scanned);
found_match = (chars_scanned > 0 && !has_leading_space);
}
// A line from DumpAddressMap, of the form:
//
// 40000000-40015000: /lib/ld-2.3.2.so
//
// Leading space is allowed. The filename may be omitted or may
// consist of multiple words, so we scan only up to the space
// before the filename.
if (!found_match) {
int chars_scanned = -1;
sscanf(line_cur, "%*x-%*x: %n", &chars_scanned);
found_match = (chars_scanned > 0);
}
if (!found_match)
return "unrecognized line in text section";
cur_offset += (line_end - line_start) + 1;
}
return kNoError;
}
class ProfileDataTest {
protected:
void ExpectStopped() {
EXPECT_FALSE(collector_.enabled());
}
void ExpectRunningSamples(int samples) {
ProfileData::State state;
collector_.GetCurrentState(&state);
EXPECT_TRUE(state.enabled);
EXPECT_EQ(samples, state.samples_gathered);
}
void ExpectSameState(const ProfileData::State& before,
const ProfileData::State& after) {
EXPECT_EQ(before.enabled, after.enabled);
EXPECT_EQ(before.samples_gathered, after.samples_gathered);
EXPECT_EQ(before.start_time, after.start_time);
EXPECT_STREQ(before.profile_name, after.profile_name);
}
ProfileData collector_;
ProfileDataChecker checker_;
private:
// The tests to run
void OpsWhenStopped();
void StartStopEmpty();
void StartStopNoOptionsEmpty();
void StartWhenStarted();
void StartStopEmpty2();
void CollectOne();
void CollectTwoMatching();
void CollectTwoFlush();
void StartResetRestart();
public:
#define RUN(test) do { \
printf("Running %s\n", #test); \
ProfileDataTest pdt; \
pdt.test(); \
} while (0)
static int RUN_ALL_TESTS() {
RUN(OpsWhenStopped);
RUN(StartStopEmpty);
RUN(StartWhenStarted);
RUN(StartStopEmpty2);
RUN(CollectOne);
RUN(CollectTwoMatching);
RUN(CollectTwoFlush);
RUN(StartResetRestart);
RUN(StartStopNoOptionsEmpty);
return 0;
}
};
// Check that various operations are safe when stopped.
TEST_F(ProfileDataTest, OpsWhenStopped) {
ExpectStopped();
EXPECT_FALSE(collector_.enabled());
// Verify that state is disabled, all-empty/all-0
ProfileData::State state_before;
collector_.GetCurrentState(&state_before);
EXPECT_FALSE(state_before.enabled);
EXPECT_EQ(0, state_before.samples_gathered);
EXPECT_EQ(0, state_before.start_time);
EXPECT_STREQ("", state_before.profile_name);
// Safe to call stop again.
collector_.Stop();
// Safe to call FlushTable.
collector_.FlushTable();
// Safe to call Add.
const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
collector_.Add(arraysize(trace), trace);
ProfileData::State state_after;
collector_.GetCurrentState(&state_after);
ExpectSameState(state_before, state_after);
}
// Start and Stop, collecting no samples. Verify output contents.
TEST_F(ProfileDataTest, StartStopEmpty) {
const int frequency = 1;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
0, 1, 0 // binary trailer
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
// Start and Stop with no options, collecting no samples. Verify
// output contents.
TEST_F(ProfileDataTest, StartStopNoOptionsEmpty) {
// We're not requesting a specific period, implementation can do
// whatever it likes.
ProfileDataSlot slots[] = {
0, 3, 0, 0 /* skipped */, 0, // binary header
0, 1, 0 // binary trailer
};
int slots_to_skip[] = { 3 };
ExpectStopped();
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(),
ProfileData::Options()));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.CheckWithSkips(slots, arraysize(slots),
slots_to_skip,
arraysize(slots_to_skip)));
}
// Start after already started. Should return false and not impact
// collected data or state.
TEST_F(ProfileDataTest, StartWhenStarted) {
const int frequency = 1;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
0, 1, 0 // binary trailer
};
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ProfileData::State state_before;
collector_.GetCurrentState(&state_before);
options.set_frequency(frequency * 2);
CHECK(!collector_.Start("foobar", options));
ProfileData::State state_after;
collector_.GetCurrentState(&state_after);
ExpectSameState(state_before, state_after);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
// Like StartStopEmpty, but uses a different file name and frequency.
TEST_F(ProfileDataTest, StartStopEmpty2) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
0, 1, 0 // binary trailer
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectOne) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
1, 5, 100, 101, 102, 103, 104, // our sample
0, 1, 0 // binary trailer
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(1);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectTwoMatching) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
2, 5, 100, 201, 302, 403, 504, // our two samples
0, 1, 0 // binary trailer
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
for (int i = 0; i < 2; ++i) {
const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(i + 1);
}
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectTwoFlush) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
1, 5, 100, 201, 302, 403, 504, // first sample (flushed)
1, 5, 100, 201, 302, 403, 504, // second identical sample
0, 1, 0 // binary trailer
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(1);
collector_.FlushTable();
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(2);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
// Start then reset, verify that the result is *not* a valid profile.
// Then start again and make sure the result is OK.
TEST_F(ProfileDataTest, StartResetRestart) {
ExpectStopped();
ProfileData::Options options;
options.set_frequency(1);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Reset();
ExpectStopped();
// We expect the resulting file to be empty. This is a minimal test
// of ValidateProfile.
EXPECT_NE(kNoError, checker_.ValidateProfile());
struct stat statbuf;
EXPECT_EQ(0, stat(checker_.filename().c_str(), &statbuf));
EXPECT_EQ(0, statbuf.st_size);
const int frequency = 2; // Different frequency than used above.
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0, // binary header
0, 1, 0 // binary trailer
};
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
} // namespace
int main(int argc, char** argv) {
int rc = ProfileDataTest::RUN_ALL_TESTS();
printf("%s\n", rc == 0 ? "PASS" : "FAIL");
return rc;
}

View file

@ -0,0 +1,147 @@
// -*- 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.
// ---
// Author: Craig Silverstein
//
// Does some simple arithmetic and a few libc routines, so we can profile it.
// Define WITH_THREADS to add pthread functionality as well (otherwise, btw,
// the num_threads argument to this program is ingored).
#include "config_for_unittests.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for fork()
#endif
#include <sys/wait.h> // for wait()
#include "gperftools/profiler.h"
#include "base/simple_mutex.h"
#include "tests/testutil.h"
static volatile int result = 0;
static int g_iters = 0; // argv[1]
Mutex mutex(Mutex::LINKER_INITIALIZED);
static void test_other_thread() {
#ifndef NO_THREADS
ProfilerRegisterThread();
int i, m;
char b[128];
MutexLock ml(&mutex);
for (m = 0; m < 1000000; ++m) { // run millions of times
for (i = 0; i < g_iters; ++i ) {
result ^= i;
}
snprintf(b, sizeof(b), "other: %d", result); // get some libc action
}
#endif
}
static void test_main_thread() {
int i, m;
char b[128];
MutexLock ml(&mutex);
for (m = 0; m < 1000000; ++m) { // run millions of times
for (i = 0; i < g_iters; ++i ) {
result ^= i;
}
snprintf(b, sizeof(b), "same: %d", result); // get some libc action
}
}
int main(int argc, char** argv) {
if ( argc <= 1 ) {
fprintf(stderr, "USAGE: %s <iters> [num_threads] [filename]\n", argv[0]);
fprintf(stderr, " iters: How many million times to run the XOR test.\n");
fprintf(stderr, " num_threads: how many concurrent threads.\n");
fprintf(stderr, " 0 or 1 for single-threaded mode,\n");
fprintf(stderr, " -# to fork instead of thread.\n");
fprintf(stderr, " filename: The name of the output profile.\n");
fprintf(stderr, (" If you don't specify, set CPUPROFILE "
"in the environment instead!\n"));
return 1;
}
g_iters = atoi(argv[1]);
int num_threads = 1;
const char* filename = NULL;
if (argc > 2) {
num_threads = atoi(argv[2]);
}
if (argc > 3) {
filename = argv[3];
}
if (filename) {
ProfilerStart(filename);
}
test_main_thread();
ProfilerFlush(); // just because we can
// The other threads, if any, will run only half as long as the main thread
if(num_threads > 0) {
RunManyThreads(test_other_thread, num_threads);
} else {
// Or maybe they asked to fork. The fork test is only interesting
// when we use CPUPROFILE to name, so check for that
#ifdef HAVE_UNISTD_H
for (; num_threads < 0; ++num_threads) { // -<num_threads> to fork
if (filename) {
printf("FORK test only makes sense when no filename is specified.\n");
return 2;
}
switch (fork()) {
case -1:
printf("FORK failed!\n");
return 1;
case 0: // child
return execl(argv[0], argv[0], argv[1], NULL);
default:
wait(NULL); // we'll let the kids run one at a time
}
}
#else
fprintf(stderr, "%s was compiled without support for fork() and exec()\n", argv[0]);
#endif
}
test_main_thread();
if (filename) {
ProfilerStop();
}
return 0;
}

View file

@ -0,0 +1,269 @@
#!/bin/sh
# 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: Craig Silverstein
#
# Runs the 4 profiler unittests and makes sure their profiles look
# appropriate. We expect two commandline args, as described below.
#
# We run under the assumption that if $PROFILER1 is run with no
# arguments, it prints a usage line of the form
# USAGE: <actual executable being run> [...]
#
# This is because libtool sometimes turns the 'executable' into a
# shell script which runs an actual binary somewhere else.
# We expect BINDIR and PPROF_PATH to be set in the environment.
# If not, we set them to some reasonable values
BINDIR="${BINDIR:-.}"
PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir] [path to pprof]"
echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH"
exit 1
fi
TMPDIR=/tmp/profile_info
UNITTEST_DIR=${1:-$BINDIR}
PPROF=${2:-$PPROF_PATH}
# We test the sliding-window functionality of the cpu-profile reader
# by using a small stride, forcing lots of reads.
PPROF_FLAGS="--test_stride=128"
PROFILER1="$UNITTEST_DIR/profiler1_unittest"
PROFILER2="$UNITTEST_DIR/profiler2_unittest"
PROFILER3="$UNITTEST_DIR/profiler3_unittest"
PROFILER4="$UNITTEST_DIR/profiler4_unittest"
# Unfortunately, for us, libtool can replace executables with a shell
# script that does some work before calling the 'real' executable
# under a different name. We need the 'real' executable name to run
# pprof on it. We've constructed all the binaries used in this
# unittest so when they are called with no arguments, they report
# their argv[0], which is the real binary name.
Realname() {
"$1" 2>&1 | awk '{print $2; exit;}'
}
PROFILER1_REALNAME=`Realname "$PROFILER1"`
PROFILER2_REALNAME=`Realname "$PROFILER2"`
PROFILER3_REALNAME=`Realname "$PROFILER3"`
PROFILER4_REALNAME=`Realname "$PROFILER4"`
# It's meaningful to the profiler, so make sure we know its state
unset CPUPROFILE
# Some output/logging in the profiler can cause issues when running the unit
# tests. For example, logging a warning when the profiler is detected as being
# present but no CPUPROFILE is specified in the environment. Especially when
# we are checking for a silent run or specific timing constraints are being
# checked. So set the env variable signifying that we are running in a unit
# test environment.
PERFTOOLS_UNITTEST=1
rm -rf "$TMPDIR"
mkdir "$TMPDIR" || exit 2
num_failures=0
RegisterFailure() {
num_failures=`expr $num_failures + 1`
}
# Takes two filenames representing profiles, with their executable scripts,
# and a multiplier, and verifies that the 'contentful' functions in each
# profile take the same time (possibly scaled by the given multiplier). It
# used to be "same" meant within 50%, after adding an noise-reducing X units
# to each value. But even that would often spuriously fail, so now it's
# "both non-zero". We're pretty forgiving.
VerifySimilar() {
prof1="$TMPDIR/$1"
exec1="$2"
prof2="$TMPDIR/$3"
exec2="$4"
mult="$5"
# We are careful not to put exec1 and exec2 in quotes, because if
# they are the empty string, it means we want to use the 1-arg
# version of pprof.
mthread1=`"$PPROF" $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'`
mthread2=`"$PPROF" $PPROF_FLAGS $exec2 "$prof2" | grep test_main_thread | awk '{print $1}'`
mthread1_plus=`expr $mthread1 + 5`
mthread2_plus=`expr $mthread2 + 5`
if [ -z "$mthread1" ] || [ -z "$mthread2" ] || \
[ "$mthread1" -le 0 -o "$mthread2" -le 0 ]
# || [ `expr $mthread1_plus \* $mult` -gt `expr $mthread2_plus \* 2` -o \
# `expr $mthread1_plus \* $mult \* 2` -lt `expr $mthread2_plus` ]
then
echo
echo ">>> profile on $exec1 vs $exec2 with multiplier $mult failed:"
echo "Actual times (in profiling units) were '$mthread1' vs. '$mthread2'"
echo
RegisterFailure
fi
}
# Takes two filenames representing profiles, and optionally their
# executable scripts (these may be empty if the profiles include
# symbols), and verifies that the two profiles are identical.
VerifyIdentical() {
prof1="$TMPDIR/$1"
exec1="$2"
prof2="$TMPDIR/$3"
exec2="$4"
# We are careful not to put exec1 and exec2 in quotes, because if
# they are the empty string, it means we want to use the 1-arg
# version of pprof.
"$PPROF" $PPROF_FLAGS $exec1 "$prof1" > "$TMPDIR/out1"
"$PPROF" $PPROF_FLAGS $exec2 "$prof2" > "$TMPDIR/out2"
diff=`diff "$TMPDIR/out1" "$TMPDIR/out2"`
if [ ! -z "$diff" ]; then
echo
echo ">>> profile doesn't match, args: $exec1 $prof1 vs. $exec2 $prof2"
echo ">>> Diff:"
echo "$diff"
echo
RegisterFailure
fi
}
# Takes a filename representing a profile, with its executable,
# and a multiplier, and verifies that the main-thread function takes
# the same amount of time as the other-threads function (possibly scaled
# by the given multiplier). Figuring out the multiplier can be tricky,
# since by design the main thread runs twice as long as each of the
# 'other' threads! It used to be "same" meant within 50%, after adding an
# noise-reducing X units to each value. But even that would often
# spuriously fail, so now it's "both non-zero". We're pretty forgiving.
VerifyAcrossThreads() {
prof1="$TMPDIR/$1"
# We need to run the script with no args to get the actual exe name
exec1="$2"
mult="$3"
# We are careful not to put exec1 in quotes, because if it is the
# empty string, it means we want to use the 1-arg version of pprof.
mthread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'`
othread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_other_thread | awk '{print $1}'`
if [ -z "$mthread" ] || [ -z "$othread" ] || \
[ "$mthread" -le 0 -o "$othread" -le 0 ]
# || [ `expr $mthread \* $mult \* 3` -gt `expr $othread \* 10` -o \
# `expr $mthread \* $mult \* 10` -lt `expr $othread \* 3` ]
then
echo
echo ">>> profile on $exec1 (main vs thread) with multiplier $mult failed:"
echo "Actual times (in profiling units) were '$mthread' vs. '$othread'"
echo
RegisterFailure
fi
}
echo
echo ">>> WARNING <<<"
echo "This test looks at timing information to determine correctness."
echo "If your system is loaded, the test may spuriously fail."
echo "If the test does fail with an 'Actual times' error, try running again."
echo
# profiler1 is a non-threaded version
"$PROFILER1" 50 1 "$TMPDIR/p1" || RegisterFailure
"$PROFILER1" 100 1 "$TMPDIR/p2" || RegisterFailure
VerifySimilar p1 "$PROFILER1_REALNAME" p2 "$PROFILER1_REALNAME" 2
# Verify the same thing works if we statically link
"$PROFILER2" 50 1 "$TMPDIR/p3" || RegisterFailure
"$PROFILER2" 100 1 "$TMPDIR/p4" || RegisterFailure
VerifySimilar p3 "$PROFILER2_REALNAME" p4 "$PROFILER2_REALNAME" 2
# Verify the same thing works if we specify via CPUPROFILE
CPUPROFILE="$TMPDIR/p5" "$PROFILER2" 50 || RegisterFailure
CPUPROFILE="$TMPDIR/p6" "$PROFILER2" 100 || RegisterFailure
VerifySimilar p5 "$PROFILER2_REALNAME" p6 "$PROFILER2_REALNAME" 2
CPUPROFILE="$TMPDIR/p5b" "$PROFILER3" 30 || RegisterFailure
CPUPROFILE="$TMPDIR/p5c" "$PROFILER3" 60 || RegisterFailure
VerifySimilar p5b "$PROFILER3_REALNAME" p5c "$PROFILER3_REALNAME" 2
# Now try what happens when we use threads
"$PROFILER3" 30 2 "$TMPDIR/p7" || RegisterFailure
"$PROFILER3" 60 2 "$TMPDIR/p8" || RegisterFailure
VerifySimilar p7 "$PROFILER3_REALNAME" p8 "$PROFILER3_REALNAME" 2
"$PROFILER4" 30 2 "$TMPDIR/p9" || RegisterFailure
"$PROFILER4" 60 2 "$TMPDIR/p10" || RegisterFailure
VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2
# More threads!
"$PROFILER4" 25 3 "$TMPDIR/p9" || RegisterFailure
"$PROFILER4" 50 3 "$TMPDIR/p10" || RegisterFailure
VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2
# Compare how much time the main thread takes compared to the other threads
# Recall the main thread runs twice as long as the other threads, by design.
"$PROFILER4" 20 4 "$TMPDIR/p11" || RegisterFailure
VerifyAcrossThreads p11 "$PROFILER4_REALNAME" 2
# Test symbol save and restore
"$PROFILER1" 50 1 "$TMPDIR/p12" || RegisterFailure
"$PPROF" $PPROF_FLAGS "$PROFILER1_REALNAME" "$TMPDIR/p12" --raw \
>"$TMPDIR/p13" 2>/dev/null || RegisterFailure
VerifyIdentical p12 "$PROFILER1_REALNAME" p13 "" || RegisterFailure
"$PROFILER3" 30 2 "$TMPDIR/p14" || RegisterFailure
"$PPROF" $PPROF_FLAGS "$PROFILER3_REALNAME" "$TMPDIR/p14" --raw \
>"$TMPDIR/p15" 2>/dev/null || RegisterFailure
VerifyIdentical p14 "$PROFILER3_REALNAME" p15 "" || RegisterFailure
# Test using ITIMER_REAL instead of ITIMER_PROF.
env CPUPROFILE_REALTIME=1 "$PROFILER3" 30 2 "$TMPDIR/p16" || RegisterFailure
env CPUPROFILE_REALTIME=1 "$PROFILER3" 60 2 "$TMPDIR/p17" || RegisterFailure
VerifySimilar p16 "$PROFILER3_REALNAME" p17 "$PROFILER3_REALNAME" 2
# Make sure that when we have a process with a fork, the profiles don't
# clobber each other
CPUPROFILE="$TMPDIR/pfork" "$PROFILER1" 1 -2 || RegisterFailure
n=`ls $TMPDIR/pfork* | wc -l`
if [ $n != 3 ]; then
echo "FORK test FAILED: expected 3 profiles (for main + 2 children), found $n"
num_failures=`expr $num_failures + 1`
fi
rm -rf "$TMPDIR" # clean up
echo "Tests finished with $num_failures failures"
exit $num_failures

View file

@ -0,0 +1,64 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright 2009 Google Inc. All Rights Reserved.
// Author: sanjay@google.com (Sanjay Ghemawat)
//
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "raw_printer.h"
#include <stdio.h>
#include <string>
#include "base/logging.h"
using std::string;
#define TEST(a, b) void TEST_##a##_##b()
#define RUN_TEST(a, b) TEST_##a##_##b()
TEST(RawPrinter, Empty) {
char buffer[1];
base::RawPrinter printer(buffer, arraysize(buffer));
CHECK_EQ(0, printer.length());
CHECK_EQ(string(""), buffer);
CHECK_EQ(0, printer.space_left());
printer.Printf("foo");
CHECK_EQ(string(""), string(buffer));
CHECK_EQ(0, printer.length());
CHECK_EQ(0, printer.space_left());
}
TEST(RawPrinter, PartiallyFilled) {
char buffer[100];
base::RawPrinter printer(buffer, arraysize(buffer));
printer.Printf("%s %s", "hello", "world");
CHECK_EQ(string("hello world"), string(buffer));
CHECK_EQ(11, printer.length());
CHECK_LT(0, printer.space_left());
}
TEST(RawPrinter, Truncated) {
char buffer[3];
base::RawPrinter printer(buffer, arraysize(buffer));
printer.Printf("%d", 12345678);
CHECK_EQ(string("12"), string(buffer));
CHECK_EQ(2, printer.length());
CHECK_EQ(0, printer.space_left());
}
TEST(RawPrinter, ExactlyFilled) {
char buffer[12];
base::RawPrinter printer(buffer, arraysize(buffer));
printer.Printf("%s %s", "hello", "world");
CHECK_EQ(string("hello world"), string(buffer));
CHECK_EQ(11, printer.length());
CHECK_EQ(0, printer.space_left());
}
int main(int argc, char **argv) {
RUN_TEST(RawPrinter, Empty);
RUN_TEST(RawPrinter, PartiallyFilled);
RUN_TEST(RawPrinter, Truncated);
RUN_TEST(RawPrinter, ExactlyFilled);
printf("PASS\n");
return 0; // 0 means success
}

View file

@ -0,0 +1,125 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2004, 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: Sanjay Ghemawat
//
// Test realloc() functionality
#include "config_for_unittests.h"
#include <assert.h> // for assert
#include <stdio.h>
#include <stddef.h> // for size_t, NULL
#include <stdlib.h> // for free, malloc, realloc
#include <algorithm> // for min
#include "base/logging.h"
using std::min;
// Fill a buffer of the specified size with a predetermined pattern
static void Fill(unsigned char* buffer, int n) {
for (int i = 0; i < n; i++) {
buffer[i] = (i & 0xff);
}
}
// Check that the specified buffer has the predetermined pattern
// generated by Fill()
static bool Valid(unsigned char* buffer, int n) {
for (int i = 0; i < n; i++) {
if (buffer[i] != (i & 0xff)) {
return false;
}
}
return true;
}
// Return the next interesting size/delta to check. Returns -1 if no more.
static int NextSize(int size) {
if (size < 100) {
return size+1;
} else if (size < 100000) {
// Find next power of two
int power = 1;
while (power < size) {
power <<= 1;
}
// Yield (power-1, power, power+1)
if (size < power-1) {
return power-1;
} else if (size == power-1) {
return power;
} else {
assert(size == power);
return power+1;
}
} else {
return -1;
}
}
int main(int argc, char** argv) {
for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) {
for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) {
unsigned char* src = (unsigned char*) malloc(src_size);
Fill(src, src_size);
unsigned char* dst = (unsigned char*) realloc(src, dst_size);
CHECK(Valid(dst, min(src_size, dst_size)));
Fill(dst, dst_size);
CHECK(Valid(dst, dst_size));
if (dst != NULL) free(dst);
}
}
// Now make sure realloc works correctly even when we overflow the
// packed cache, so some entries are evicted from the cache.
// The cache has 2^12 entries, keyed by page number.
const int kNumEntries = 1 << 14;
int** p = (int**)malloc(sizeof(*p) * kNumEntries);
int sum = 0;
for (int i = 0; i < kNumEntries; i++) {
p[i] = (int*)malloc(8192); // no page size is likely to be bigger
p[i][1000] = i; // use memory deep in the heart of p
}
for (int i = 0; i < kNumEntries; i++) {
p[i] = (int*)realloc(p[i], 9000);
}
for (int i = 0; i < kNumEntries; i++) {
sum += p[i][1000];
free(p[i]);
}
CHECK_EQ(kNumEntries/2 * (kNumEntries - 1), sum); // assume kNE is even
free(p);
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,631 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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.
// ---
// All Rights Reserved.
//
// Author: Daniel Ford
//
// Checks basic properties of the sampler
#include "config_for_unittests.h"
#include <stdlib.h> // defines posix_memalign
#include <stdio.h> // for the printf at the end
#if defined HAVE_STDINT_H
#include <stdint.h> // to get uintptr_t
#elif defined HAVE_INTTYPES_H
#include <inttypes.h> // another place uintptr_t might be defined
#endif
#include <sys/types.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <math.h>
#include "base/logging.h"
#include "base/commandlineflags.h"
#include "sampler.h" // The Sampler class being tested
using std::sort;
using std::min;
using std::max;
using std::vector;
using std::abs;
vector<void (*)()> g_testlist; // the tests to run
#define TEST(a, b) \
struct Test_##a##_##b { \
Test_##a##_##b() { g_testlist.push_back(&Run); } \
static void Run(); \
}; \
static Test_##a##_##b g_test_##a##_##b; \
void Test_##a##_##b::Run()
static int RUN_ALL_TESTS() {
vector<void (*)()>::const_iterator it;
for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
(*it)(); // The test will error-exit if there's a problem.
}
fprintf(stderr, "\nPassed %d tests\n\nPASS\n", (int)g_testlist.size());
return 0;
}
#undef LOG // defined in base/logging.h
// Ideally, we'd put the newline at the end, but this hack puts the
// newline at the end of the previous log message, which is good enough :-)
#define LOG(level) std::cerr << "\n"
static std::string StringPrintf(const char* format, ...) {
char buf[256]; // should be big enough for all logging
va_list ap;
va_start(ap, format);
perftools_vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
return buf;
}
namespace {
template<typename T> class scoped_array {
public:
scoped_array(T* p) : p_(p) { }
~scoped_array() { delete[] p_; }
const T* get() const { return p_; }
T* get() { return p_; }
T& operator[](int i) { return p_[i]; }
private:
T* p_;
};
}
// Note that these tests are stochastic.
// This mean that the chance of correct code passing the test is,
// in the case of 5 standard deviations:
// kSigmas=5: ~99.99994267%
// in the case of 4 standard deviations:
// kSigmas=4: ~99.993666%
static const double kSigmas = 4;
static const size_t kSamplingInterval = 512*1024;
// Tests that GetSamplePeriod returns the expected value
// which is 1<<19
TEST(Sampler, TestGetSamplePeriod) {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t sample_period;
sample_period = sampler.GetSamplePeriod();
CHECK_GT(sample_period, 0);
}
// Tests of the quality of the random numbers generated
// This uses the Anderson Darling test for uniformity.
// See "Evaluating the Anderson-Darling Distribution" by Marsaglia
// for details.
// Short cut version of ADinf(z), z>0 (from Marsaglia)
// This returns the p-value for Anderson Darling statistic in
// the limit as n-> infinity. For finite n, apply the error fix below.
double AndersonDarlingInf(double z) {
if (z < 2) {
return exp(-1.2337141 / z) / sqrt(z) * (2.00012 + (0.247105 -
(0.0649821 - (0.0347962 - (0.011672 - 0.00168691
* z) * z) * z) * z) * z);
}
return exp( - exp(1.0776 - (2.30695 - (0.43424 - (0.082433 -
(0.008056 - 0.0003146 * z) * z) * z) * z) * z));
}
// Corrects the approximation error in AndersonDarlingInf for small values of n
// Add this to AndersonDarlingInf to get a better approximation
// (from Marsaglia)
double AndersonDarlingErrFix(int n, double x) {
if (x > 0.8) {
return (-130.2137 + (745.2337 - (1705.091 - (1950.646 -
(1116.360 - 255.7844 * x) * x) * x) * x) * x) / n;
}
double cutoff = 0.01265 + 0.1757 / n;
double t;
if (x < cutoff) {
t = x / cutoff;
t = sqrt(t) * (1 - t) * (49 * t - 102);
return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n;
} else {
t = (x - cutoff) / (0.8 - cutoff);
t = -0.00022633 + (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864
* t) * t) * t) * t) * t;
return t * (0.04213 + 0.01365 / n) / n;
}
}
// Returns the AndersonDarling p-value given n and the value of the statistic
double AndersonDarlingPValue(int n, double z) {
double ad = AndersonDarlingInf(z);
double errfix = AndersonDarlingErrFix(n, ad);
return ad + errfix;
}
double AndersonDarlingStatistic(int n, double* random_sample) {
double ad_sum = 0;
for (int i = 0; i < n; i++) {
ad_sum += (2*i + 1) * log(random_sample[i] * (1 - random_sample[n-1-i]));
}
double ad_statistic = - n - 1/static_cast<double>(n) * ad_sum;
return ad_statistic;
}
// Tests if the array of doubles is uniformly distributed.
// Returns the p-value of the Anderson Darling Statistic
// for the given set of sorted random doubles
// See "Evaluating the Anderson-Darling Distribution" by
// Marsaglia and Marsaglia for details.
double AndersonDarlingTest(int n, double* random_sample) {
double ad_statistic = AndersonDarlingStatistic(n, random_sample);
LOG(INFO) << StringPrintf("AD stat = %f, n=%d\n", ad_statistic, n);
double p = AndersonDarlingPValue(n, ad_statistic);
return p;
}
// Test the AD Test. The value of the statistic should go to zero as n->infty
// Not run as part of regular tests
void ADTestTest(int n) {
scoped_array<double> random_sample(new double[n]);
for (int i = 0; i < n; i++) {
random_sample[i] = (i+0.01)/n;
}
sort(random_sample.get(), random_sample.get() + n);
double ad_stat = AndersonDarlingStatistic(n, random_sample.get());
LOG(INFO) << StringPrintf("Testing the AD test. n=%d, ad_stat = %f",
n, ad_stat);
}
// Print the CDF of the distribution of the Anderson-Darling Statistic
// Used for checking the Anderson-Darling Test
// Not run as part of regular tests
void ADCDF() {
for (int i = 1; i < 40; i++) {
double x = i/10.0;
LOG(INFO) << "x= " << x << " adpv= "
<< AndersonDarlingPValue(100, x) << ", "
<< AndersonDarlingPValue(1000, x);
}
}
// Testing that NextRandom generates uniform
// random numbers.
// Applies the Anderson-Darling test for uniformity
void TestNextRandom(int n) {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t x = 1;
// This assumes that the prng returns 48 bit numbers
uint64_t max_prng_value = static_cast<uint64_t>(1)<<48;
// Initialize
for (int i = 1; i <= 20; i++) { // 20 mimics sampler.Init()
x = sampler.NextRandom(x);
}
scoped_array<uint64_t> int_random_sample(new uint64_t[n]);
// Collect samples
for (int i = 0; i < n; i++) {
int_random_sample[i] = x;
x = sampler.NextRandom(x);
}
// First sort them...
sort(int_random_sample.get(), int_random_sample.get() + n);
scoped_array<double> random_sample(new double[n]);
// Convert them to uniform randoms (in the range [0,1])
for (int i = 0; i < n; i++) {
random_sample[i] = static_cast<double>(int_random_sample[i])/max_prng_value;
}
// Now compute the Anderson-Darling statistic
double ad_pvalue = AndersonDarlingTest(n, random_sample.get());
LOG(INFO) << StringPrintf("pvalue for AndersonDarlingTest "
"with n= %d is p= %f\n", n, ad_pvalue);
CHECK_GT(min(ad_pvalue, 1 - ad_pvalue), 0.0001);
// << StringPrintf("prng is not uniform, %d\n", n);
}
TEST(Sampler, TestNextRandom_MultipleValues) {
TestNextRandom(10); // Check short-range correlation
TestNextRandom(100);
TestNextRandom(1000);
TestNextRandom(10000); // Make sure there's no systematic error
}
// Tests that PickNextSamplePeriod generates
// geometrically distributed random numbers.
// First converts to uniforms then applied the
// Anderson-Darling test for uniformity.
void TestPickNextSample(int n) {
tcmalloc::Sampler sampler;
sampler.Init(1);
scoped_array<uint64_t> int_random_sample(new uint64_t[n]);
int sample_period = sampler.GetSamplePeriod();
int ones_count = 0;
for (int i = 0; i < n; i++) {
int_random_sample[i] = sampler.PickNextSamplingPoint();
CHECK_GE(int_random_sample[i], 1);
if (int_random_sample[i] == 1) {
ones_count += 1;
}
CHECK_LT(ones_count, 4); // << " out of " << i << " samples.";
}
// First sort them...
sort(int_random_sample.get(), int_random_sample.get() + n);
scoped_array<double> random_sample(new double[n]);
// Convert them to uniform random numbers
// by applying the geometric CDF
for (int i = 0; i < n; i++) {
random_sample[i] = 1 - exp(-static_cast<double>(int_random_sample[i])
/ sample_period);
}
// Now compute the Anderson-Darling statistic
double geom_ad_pvalue = AndersonDarlingTest(n, random_sample.get());
LOG(INFO) << StringPrintf("pvalue for geometric AndersonDarlingTest "
"with n= %d is p= %f\n", n, geom_ad_pvalue);
CHECK_GT(min(geom_ad_pvalue, 1 - geom_ad_pvalue), 0.0001);
// << "PickNextSamplingPoint does not produce good "
// "geometric/exponential random numbers\n";
}
TEST(Sampler, TestPickNextSample_MultipleValues) {
TestPickNextSample(10); // Make sure the first few are good (enough)
TestPickNextSample(100);
TestPickNextSample(1000);
TestPickNextSample(10000); // Make sure there's no systematic error
}
// This is superceeded by the Anderson-Darling Test
// and it not run now.
// Tests how fast nearby values are spread out with LRand64
// The purpose of this code is to determine how many
// steps to apply to the seed during initialization
void TestLRand64Spread() {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t current_value;
printf("Testing LRand64 Spread\n");
for (int i = 1; i < 10; i++) {
printf("%d ", i);
current_value = i;
for (int j = 1; j < 100; j++) {
current_value = sampler.NextRandom(current_value);
}
LOG(INFO) << current_value;
}
}
// Futher tests
bool CheckMean(size_t mean, int num_samples) {
tcmalloc::Sampler sampler;
sampler.Init(1);
size_t total = 0;
for (int i = 0; i < num_samples; i++) {
total += sampler.PickNextSamplingPoint();
}
double empirical_mean = total / static_cast<double>(num_samples);
double expected_sd = mean / pow(num_samples * 1.0, 0.5);
return(fabs(mean-empirical_mean) < expected_sd * kSigmas);
}
// Prints a sequence so you can look at the distribution
void OutputSequence(int sequence_length) {
tcmalloc::Sampler sampler;
sampler.Init(1);
size_t next_step;
for (int i = 0; i< sequence_length; i++) {
next_step = sampler.PickNextSamplingPoint();
LOG(INFO) << next_step;
}
}
double StandardDeviationsErrorInSample(
int total_samples, int picked_samples,
int alloc_size, int sampling_interval) {
double p = 1 - exp(-(static_cast<double>(alloc_size) / sampling_interval));
double expected_samples = total_samples * p;
double sd = pow(p*(1-p)*total_samples, 0.5);
return((picked_samples - expected_samples) / sd);
}
TEST(Sampler, LargeAndSmallAllocs_CombinedTest) {
tcmalloc::Sampler sampler;
sampler.Init(1);
int counter_big = 0;
int counter_small = 0;
int size_big = 129*8*1024+1;
int size_small = 1024*8;
int num_iters = 128*4*8;
// Allocate in mixed chunks
for (int i = 0; i < num_iters; i++) {
if (!sampler.RecordAllocation(size_big)) {
counter_big += 1;
}
for (int i = 0; i < 129; i++) {
if (!sampler.RecordAllocation(size_small)) {
counter_small += 1;
}
}
}
// Now test that there are the right number of each
double large_allocs_sds =
StandardDeviationsErrorInSample(num_iters, counter_big,
size_big, kSamplingInterval);
double small_allocs_sds =
StandardDeviationsErrorInSample(num_iters*129, counter_small,
size_small, kSamplingInterval);
LOG(INFO) << StringPrintf("large_allocs_sds = %f\n", large_allocs_sds);
LOG(INFO) << StringPrintf("small_allocs_sds = %f\n", small_allocs_sds);
CHECK_LE(fabs(large_allocs_sds), kSigmas);
CHECK_LE(fabs(small_allocs_sds), kSigmas);
}
// Tests whether the mean is about right over 1000 samples
TEST(Sampler, IsMeanRight) {
CHECK(CheckMean(kSamplingInterval, 1000));
}
// This flag is for the OldSampler class to use
const int64 FLAGS_mock_tcmalloc_sample_parameter = 1<<19;
// A cut down and slightly refactored version of the old Sampler
class OldSampler {
public:
void Init(uint32_t seed);
void Cleanup() {}
// Record allocation of "k" bytes. Return true iff allocation
// should be sampled
bool SampleAllocation(size_t k);
// Generate a geometric with mean 1M (or FLAG value)
void PickNextSample(size_t k);
// Initialize the statics for the Sample class
static void InitStatics() {
sample_period = 1048583;
}
size_t bytes_until_sample_;
private:
uint32_t rnd_; // Cheap random number generator
static uint64_t sample_period;
// Should be a prime just above a power of 2:
// 2, 5, 11, 17, 37, 67, 131, 257,
// 521, 1031, 2053, 4099, 8209, 16411,
// 32771, 65537, 131101, 262147, 524309, 1048583,
// 2097169, 4194319, 8388617, 16777259, 33554467
};
// Statics for OldSampler
uint64_t OldSampler::sample_period;
void OldSampler::Init(uint32_t seed) {
// Initialize PRNG -- run it for a bit to get to good values
if (seed != 0) {
rnd_ = seed;
} else {
rnd_ = 12345;
}
bytes_until_sample_ = 0;
for (int i = 0; i < 100; i++) {
PickNextSample(sample_period * 2);
}
};
// A cut-down version of the old PickNextSampleRoutine
void OldSampler::PickNextSample(size_t k) {
// Make next "random" number
// x^32+x^22+x^2+x^1+1 is a primitive polynomial for random numbers
static const uint32_t kPoly = (1 << 22) | (1 << 2) | (1 << 1) | (1 << 0);
uint32_t r = rnd_;
rnd_ = (r << 1) ^ ((static_cast<int32_t>(r) >> 31) & kPoly);
// Next point is "rnd_ % (sample_period)". I.e., average
// increment is "sample_period/2".
const int flag_value = FLAGS_mock_tcmalloc_sample_parameter;
static int last_flag_value = -1;
if (flag_value != last_flag_value) {
// There should be a spinlock here, but this code is
// for benchmarking only.
sample_period = 1048583;
last_flag_value = flag_value;
}
bytes_until_sample_ += rnd_ % sample_period;
if (k > (static_cast<size_t>(-1) >> 2)) {
// If the user has asked for a huge allocation then it is possible
// for the code below to loop infinitely. Just return (note that
// this throws off the sampling accuracy somewhat, but a user who
// is allocating more than 1G of memory at a time can live with a
// minor inaccuracy in profiling of small allocations, and also
// would rather not wait for the loop below to terminate).
return;
}
while (bytes_until_sample_ < k) {
// Increase bytes_until_sample_ by enough average sampling periods
// (sample_period >> 1) to allow us to sample past the current
// allocation.
bytes_until_sample_ += (sample_period >> 1);
}
bytes_until_sample_ -= k;
}
inline bool OldSampler::SampleAllocation(size_t k) {
if (bytes_until_sample_ < k) {
PickNextSample(k);
return true;
} else {
bytes_until_sample_ -= k;
return false;
}
}
// This checks that the stated maximum value for the
// tcmalloc_sample_parameter flag never overflows bytes_until_sample_
TEST(Sampler, bytes_until_sample_Overflow_Underflow) {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t one = 1;
// sample_parameter = 0; // To test the edge case
uint64_t sample_parameter_array[4] = {0, 1, one<<19, one<<58};
for (int i = 0; i < 4; i++) {
uint64_t sample_parameter = sample_parameter_array[i];
LOG(INFO) << "sample_parameter = " << sample_parameter;
double sample_scaling = - log(2.0) * sample_parameter;
// Take the top 26 bits as the random number
// (This plus the 1<<26 sampling bound give a max step possible of
// 1209424308 bytes.)
const uint64_t prng_mod_power = 48; // Number of bits in prng
// First, check the largest_prng value
uint64_t largest_prng_value = (static_cast<uint64_t>(1)<<48) - 1;
double q = (largest_prng_value >> (prng_mod_power - 26)) + 1.0;
LOG(INFO) << StringPrintf("q = %f\n", q);
LOG(INFO) << StringPrintf("log2(q) = %f\n", log(q)/log(2.0));
uint64_t smallest_sample_step
= static_cast<uint64_t>(min(log2(q) - 26, 0.0)
* sample_scaling + 1);
LOG(INFO) << "Smallest sample step is " << smallest_sample_step;
uint64_t cutoff = static_cast<uint64_t>(10)
* (sample_parameter/(one<<24) + 1);
LOG(INFO) << "Acceptable value is < " << cutoff;
// This checks that the answer is "small" and positive
CHECK_LE(smallest_sample_step, cutoff);
// Next, check with the smallest prng value
uint64_t smallest_prng_value = 0;
q = (smallest_prng_value >> (prng_mod_power - 26)) + 1.0;
LOG(INFO) << StringPrintf("q = %f\n", q);
uint64_t largest_sample_step
= static_cast<uint64_t>(min(log2(q) - 26, 0.0)
* sample_scaling + 1);
LOG(INFO) << "Largest sample step is " << largest_sample_step;
CHECK_LE(largest_sample_step, one<<63);
CHECK_GE(largest_sample_step, smallest_sample_step);
}
}
// Test that NextRand is in the right range. Unfortunately, this is a
// stochastic test which could miss problems.
TEST(Sampler, NextRand_range) {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t one = 1;
// The next number should be (one << 48) - 1
uint64_t max_value = (one << 48) - 1;
uint64_t x = (one << 55);
int n = 22; // 27;
LOG(INFO) << "Running sampler.NextRandom 1<<" << n << " times";
for (int i = 1; i <= (1<<n); i++) { // 20 mimics sampler.Init()
x = sampler.NextRandom(x);
CHECK_LE(x, max_value);
}
}
// Tests certain arithmetic operations to make sure they compute what we
// expect them too (for testing across different platforms)
TEST(Sampler, arithmetic_1) {
tcmalloc::Sampler sampler;
sampler.Init(1);
uint64_t rnd; // our 48 bit random number, which we don't trust
const uint64_t prng_mod_power = 48;
uint64_t one = 1;
rnd = one;
uint64_t max_value = (one << 48) - 1;
for (int i = 1; i <= (1>>27); i++) { // 20 mimics sampler.Init()
rnd = sampler.NextRandom(rnd);
CHECK_LE(rnd, max_value);
double q = (rnd >> (prng_mod_power - 26)) + 1.0;
CHECK_GE(q, 0); // << rnd << " " << prng_mod_power;
}
// Test some potentially out of bounds value for rnd
for (int i = 1; i <= 63; i++) {
rnd = one << i;
double q = (rnd >> (prng_mod_power - 26)) + 1.0;
LOG(INFO) << "rnd = " << rnd << " i=" << i << " q=" << q;
CHECK_GE(q, 0);
// << " rnd=" << rnd << " i=" << i << " prng_mod_power" << prng_mod_power;
}
}
void test_arithmetic(uint64_t rnd) {
const uint64_t prng_mod_power = 48; // Number of bits in prng
uint64_t shifted_rnd = rnd >> (prng_mod_power - 26);
CHECK_GE(shifted_rnd, 0);
CHECK_LT(shifted_rnd, (1<<26));
LOG(INFO) << shifted_rnd;
LOG(INFO) << static_cast<double>(shifted_rnd);
CHECK_GE(static_cast<double>(static_cast<uint32_t>(shifted_rnd)), 0);
// << " rnd=" << rnd << " srnd=" << shifted_rnd;
CHECK_GE(static_cast<double>(shifted_rnd), 0);
// << " rnd=" << rnd << " srnd=" << shifted_rnd;
double q = static_cast<double>(shifted_rnd) + 1.0;
CHECK_GT(q, 0);
}
// Tests certain arithmetic operations to make sure they compute what we
// expect them too (for testing across different platforms)
// know bad values under with -c dbg --cpu piii for _some_ binaries:
// rnd=227453640600554
// shifted_rnd=54229173
// (hard to reproduce)
TEST(Sampler, arithmetic_2) {
uint64_t rnd = 227453640600554LL;
test_arithmetic(rnd);
}
// It's not really a test, but it's good to know
TEST(Sample, size_of_class) {
tcmalloc::Sampler sampler;
sampler.Init(1);
LOG(INFO) << "Size of Sampler class is: " << sizeof(tcmalloc::Sampler);
LOG(INFO) << "Size of Sampler object is: " << sizeof(sampler);
}
// Make sure sampling is enabled, or the tests won't work right.
DECLARE_int64(tcmalloc_sample_parameter);
int main(int argc, char **argv) {
if (FLAGS_tcmalloc_sample_parameter == 0)
FLAGS_tcmalloc_sample_parameter = 524288;
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,83 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Craig Silverstein
//
// This tests ReadStackTraces and ReadGrowthStackTraces. It does this
// by doing a bunch of allocations and then calling those functions.
// A driver shell-script can call this, and then call pprof, and
// verify the expected output. The output is written to
// argv[1].heap and argv[1].growth
#include "config_for_unittests.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "base/logging.h"
#include <gperftools/malloc_extension.h>
using std::string;
extern "C" void* AllocateAllocate() ATTRIBUTE_NOINLINE;
extern "C" void* AllocateAllocate() {
// The VLOG's are mostly to discourage inlining
VLOG(1, "Allocating some more");
void* p = malloc(10000);
VLOG(1, "Done allocating");
return p;
}
static void WriteStringToFile(const string& s, const string& filename) {
FILE* fp = fopen(filename.c_str(), "w");
fwrite(s.data(), 1, s.length(), fp);
fclose(fp);
}
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "USAGE: %s <base of output files>\n", argv[0]);
exit(1);
}
for (int i = 0; i < 8000; i++) {
AllocateAllocate();
}
string s;
MallocExtension::instance()->GetHeapSample(&s);
WriteStringToFile(s, string(argv[1]) + ".heap");
s.clear();
MallocExtension::instance()->GetHeapGrowthStacks(&s);
WriteStringToFile(s, string(argv[1]) + ".growth");
return 0;
}

View file

@ -0,0 +1,94 @@
#!/bin/sh
# Copyright (c) 2008, 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: Craig Silverstein
#
# This is a test that tcmalloc creates, and pprof reads, sampling data
# correctly: both for the heap profile (ReadStackTraces) and for
# growth in the heap sized (ReadGrowthStackTraces).
BINDIR="${BINDIR:-.}"
PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
echo "USAGE: $0 [unittest dir] [path to pprof]"
echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH"
exit 1
fi
SAMPLING_TEST="${1:-$BINDIR/sampling_test}"
PPROF="${2:-$PPROF_PATH}"
OUTDIR="/tmp/sampling_test_dir"
# libtool is annoying, and puts the actual executable in a different
# directory, replacing the seeming-executable with a shell script.
# We use the error output of sampling_test to indicate its real location
SAMPLING_TEST_BINARY=`"$SAMPLING_TEST" 2>&1 | awk '/USAGE/ {print $2; exit;}'`
# A kludge for cygwin. Unfortunately, 'test -f' says that 'foo' exists
# even when it doesn't, and only foo.exe exists. Other unix utilities
# (like nm) need you to say 'foo.exe'. We use one such utility, cat, to
# see what the *real* binary name is.
if ! cat "$SAMPLING_TEST_BINARY" >/dev/null 2>&1; then
SAMPLING_TEST_BINARY="$SAMPLING_TEST_BINARY".exe
fi
die() { # runs the command given as arguments, and then dies.
echo "FAILED. Output from $@"
echo "----"
"$@"
echo "----"
exit 1
}
rm -rf "$OUTDIR" || die "Unable to delete $OUTDIR"
mkdir "$OUTDIR" || die "Unable to create $OUTDIR"
# This puts the output into out.heap and out.growth. It allocates
# 8*10^7 bytes of memory, which is 76M. Because we sample, the
# estimate may be a bit high or a bit low: we accept anything from
# 50M to 99M.
"$SAMPLING_TEST" "$OUTDIR/out"
echo "Testing heap output..."
"$PPROF" --text "$SAMPLING_TEST_BINARY" "$OUTDIR/out.heap" \
| grep '[5-9][0-9]\.[0-9][ 0-9.%]*_*AllocateAllocate' >/dev/null \
|| die "$PPROF" --text "$SAMPLING_TEST_BINARY" "$OUTDIR/out.heap"
echo "OK"
echo "Testing growth output..."
"$PPROF" --text "$SAMPLING_TEST_BINARY" "$OUTDIR/out.growth" \
| grep '[5-9][0-9]\.[0-9][ 0-9.%]*_*AllocateAllocate' >/dev/null \
|| die "$PPROF" --text "$SAMPLING_TEST_BINARY" "$OUTDIR/out.growth"
echo "OK"
echo "PASS"

View file

@ -0,0 +1,71 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2012, 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: Craig Silverstein
//
// This just verifies that we can compile code that #includes stuff
// via the backwards-compatibility 'google/' #include-dir. It does
// not include config.h on purpose, to better simulate a perftools
// client.
#include <stddef.h>
#include <stdio.h>
#define GPERFTOOLS_SUPPRESS_LEGACY_WARNING
#include <google/heap-checker.h>
#include <google/heap-profiler.h>
#include <google/malloc_extension.h>
#include <google/malloc_extension_c.h>
#include <google/malloc_hook.h>
#include <google/malloc_hook_c.h>
#include <google/profiler.h>
#include <google/stacktrace.h>
#include <google/tcmalloc.h>
// We don't link in -lprofiler for this test, so be sure not to make
// any function calls that require the cpu-profiler code. The
// heap-profiler is ok.
HeapLeakChecker::Disabler* heap_checker_h;
void (*heap_profiler_h)(const char*) = &HeapProfilerStart;
MallocExtension::Ownership malloc_extension_h;
MallocExtension_Ownership malloc_extension_c_h;
MallocHook::NewHook* malloc_hook_h;
MallocHook_NewHook* malloc_hook_c_h;
ProfilerOptions* profiler_h;
int (*stacktrace_h)(void**, int, int) = &GetStackTrace;
void* (*tcmalloc_h)(size_t) = &tc_new;
int main(int argc, char** argv) {
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,93 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright 2009 Google Inc. All Rights Reserved.
// Author: fikes@google.com (Andrew Fikes)
//
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "config_for_unittests.h"
#include <stdio.h> // for puts()
#include "stack_trace_table.h"
#include "base/logging.h"
#include "base/spinlock.h"
#include "static_vars.h"
#undef ARRAYSIZE // may be defined on, eg, windows
#define ARRAYSIZE(a) ( sizeof(a) / sizeof(*(a)) )
static void CheckTracesAndReset(tcmalloc::StackTraceTable* table,
const uintptr_t* expected, int len) {
void** entries = table->ReadStackTracesAndClear();
for (int i = 0; i < len; ++i) {
CHECK_EQ(reinterpret_cast<uintptr_t>(entries[i]), expected[i]);
}
delete[] entries;
}
static void AddTrace(tcmalloc::StackTraceTable* table,
const tcmalloc::StackTrace& t) {
// Normally we'd need this lock, but since the test is single-threaded
// we don't. I comment it out on windows because the DLL-decl thing
// is really annoying in this case.
#ifndef _MSC_VER
SpinLockHolder h(tcmalloc::Static::pageheap_lock());
#endif
table->AddTrace(t);
}
int main(int argc, char **argv) {
tcmalloc::StackTraceTable table;
// Empty table
CHECK_EQ(table.depth_total(), 0);
CHECK_EQ(table.bucket_total(), 0);
static const uintptr_t k1[] = {0};
CheckTracesAndReset(&table, k1, ARRAYSIZE(k1));
tcmalloc::StackTrace t1;
t1.size = static_cast<uintptr_t>(1024);
t1.depth = static_cast<uintptr_t>(2);
t1.stack[0] = reinterpret_cast<void*>(1);
t1.stack[1] = reinterpret_cast<void*>(2);
tcmalloc::StackTrace t2;
t2.size = static_cast<uintptr_t>(512);
t2.depth = static_cast<uintptr_t>(2);
t2.stack[0] = reinterpret_cast<void*>(2);
t2.stack[1] = reinterpret_cast<void*>(1);
// Table w/ just t1
AddTrace(&table, t1);
CHECK_EQ(table.depth_total(), 2);
CHECK_EQ(table.bucket_total(), 1);
static const uintptr_t k2[] = {1, 1024, 2, 1, 2, 0};
CheckTracesAndReset(&table, k2, ARRAYSIZE(k2));
// Table w/ t1, t2
AddTrace(&table, t1);
AddTrace(&table, t2);
CHECK_EQ(table.depth_total(), 4);
CHECK_EQ(table.bucket_total(), 2);
static const uintptr_t k3[] = {1, 512, 2, 2, 1, 1, 1024, 2, 1, 2, 0};
CheckTracesAndReset(&table, k3, ARRAYSIZE(k3));
// Table w/ t1, t3
// Same stack as t1, but w/ different size
tcmalloc::StackTrace t3;
t3.size = static_cast<uintptr_t>(2);
t3.depth = static_cast<uintptr_t>(2);
t3.stack[0] = reinterpret_cast<void*>(1);
t3.stack[1] = reinterpret_cast<void*>(2);
AddTrace(&table, t1);
AddTrace(&table, t3);
CHECK_EQ(table.depth_total(), 4);
CHECK_EQ(table.bucket_total(), 2);
static const uintptr_t k5[] = {1, 2, 2, 1, 2, 1, 1024, 2, 1, 2, 0};
CheckTracesAndReset(&table, k5, ARRAYSIZE(k5));
puts("PASS");
return 0;
}

View file

@ -0,0 +1,298 @@
// -*- 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.
#include "config_for_unittests.h"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#include <stdio.h>
#include <stdlib.h>
// On those architectures we can and should test if backtracing with
// ucontext and from signal handler works
#if __GNUC__ && __linux__ && (__x86_64__ || __aarch64__ || __riscv)
#include <signal.h>
#define TEST_UCONTEXT_BITS 1
#endif
#include "base/commandlineflags.h"
#include "base/logging.h"
#include <gperftools/stacktrace.h>
#include "tests/testutil.h"
namespace {
// Obtain a backtrace, verify that the expected callers are present in the
// backtrace, and maybe print the backtrace to stdout.
// The sequence of functions whose return addresses we expect to see in the
// backtrace.
const int BACKTRACE_STEPS = 6;
struct AddressRange {
const void *start, *end;
};
// Expected function [start,end] range.
AddressRange expected_range[BACKTRACE_STEPS];
#if __GNUC__
// Using GCC extension: address of a label can be taken with '&&label'.
// Start should be a label somewhere before recursive call, end somewhere
// after it.
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
do { \
(prange)->start = &&start_label; \
(prange)->end = &&end_label; \
CHECK_LT((prange)->start, (prange)->end); \
} while (0)
// This macro expands into "unmovable" code (opaque to GCC), and that
// prevents GCC from moving a_label up or down in the code.
// Without it, there is no code following the 'end' label, and GCC
// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before
// the recursive call.
#define DECLARE_ADDRESS_LABEL(a_label) \
a_label: do { __asm__ __volatile__(""); } while (0)
// Gcc 4.4.0 may split function into multiple chunks, and the chunk
// performing recursive call may end up later in the code then the return
// instruction (this actually happens with FDO).
// Adjust function range from __builtin_return_address.
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \
do { \
void *ra = __builtin_return_address(0); \
CHECK_LT((prange)->start, ra); \
if (ra > (prange)->end) { \
printf("Adjusting range from %p..%p to %p..%p\n", \
(prange)->start, (prange)->end, \
(prange)->start, ra); \
(prange)->end = ra; \
} \
} while (0)
#else
// Assume the Check* functions below are not longer than 256 bytes.
#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
do { \
(prange)->start = reinterpret_cast<const void *>(&fn); \
(prange)->end = reinterpret_cast<const char *>(&fn) + 256; \
} while (0)
#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0)
#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0)
#endif // __GNUC__
//-----------------------------------------------------------------------//
void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range)
{
CHECK_GE(ret_addr, range.start);
CHECK_LE(ret_addr, range.end);
}
//-----------------------------------------------------------------------//
#if TEST_UCONTEXT_BITS
struct get_stack_trace_args {
int *size_ptr;
void **result;
int max_depth;
uintptr_t where;
} gst_args;
static
void SignalHandler(int dummy, siginfo_t *si, void* ucv) {
auto uc = static_cast<ucontext_t*>(ucv);
#ifdef __riscv
uc->uc_mcontext.__gregs[REG_PC] = gst_args.where;
#elif __aarch64__
uc->uc_mcontext.pc = gst_args.where;
#else
uc->uc_mcontext.gregs[REG_RIP] = gst_args.where;
#endif
*gst_args.size_ptr = GetStackTraceWithContext(
gst_args.result,
gst_args.max_depth,
2,
uc);
}
int ATTRIBUTE_NOINLINE CaptureLeafUContext(void **stack, int stack_len) {
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
DECLARE_ADDRESS_LABEL(start);
int size;
printf("Capturing stack trace from signal's ucontext\n");
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = SignalHandler;
sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
int rv = sigaction(SIGSEGV, &sa, nullptr);
CHECK(rv == 0);
gst_args.size_ptr = &size;
gst_args.result = stack;
gst_args.max_depth = stack_len;
gst_args.where = reinterpret_cast<uintptr_t>(noopt(&&after));
// now, "write" to null pointer and trigger sigsegv to run signal
// handler. It'll then change PC to after, as if we jumped one line
// below.
*noopt(reinterpret_cast<void**>(0)) = 0;
// this is not reached, but gcc gets really odd if we don't actually
// use computed goto.
static void* jump_target = &&after;
goto *noopt(&jump_target);
after:
printf("Obtained %d stack frames.\n", size);
CHECK_GE(size, 1);
CHECK_LE(size, stack_len);
DECLARE_ADDRESS_LABEL(end);
return size;
}
#endif // TEST_UCONTEXT_BITS
int ATTRIBUTE_NOINLINE CaptureLeafPlain(void **stack, int stack_len) {
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
DECLARE_ADDRESS_LABEL(start);
int size = GetStackTrace(stack, stack_len, 0);
printf("Obtained %d stack frames.\n", size);
CHECK_GE(size, 1);
CHECK_LE(size, stack_len);
DECLARE_ADDRESS_LABEL(end);
return size;
}
void ATTRIBUTE_NOINLINE CheckStackTrace(int);
int (*leaf_capture_fn)(void**, int) = CaptureLeafPlain;
void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(int i) {
const int STACK_LEN = 20;
void *stack[STACK_LEN];
int size;
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
size = leaf_capture_fn(stack, STACK_LEN);
#ifdef HAVE_EXECINFO_H
{
char **strings = backtrace_symbols(stack, size);
for (int i = 0; i < size; i++)
printf("%s %p\n", strings[i], stack[i]);
printf("CheckStackTrace() addr: %p\n", &CheckStackTrace);
free(strings);
}
#endif
for (int i = 0, j = 0; i < BACKTRACE_STEPS; i++, j++) {
if (i == 1 && j == 1) {
// this is expected to be our function for which we don't
// establish bounds. So skip.
j++;
}
printf("Backtrace %d: expected: %p..%p actual: %p ... ",
i, expected_range[i].start, expected_range[i].end, stack[j]);
fflush(stdout);
CheckRetAddrIsInFunction(stack[j], expected_range[i]);
printf("OK\n");
}
}
//-----------------------------------------------------------------------//
/* Dummy functions to make the backtrace more interesting. */
void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) {
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]);
INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]);
DECLARE_ADDRESS_LABEL(start);
for (int j = i; j >= 0; j--)
CheckStackTraceLeaf(j);
DECLARE_ADDRESS_LABEL(end);
}
void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) {
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]);
INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]);
DECLARE_ADDRESS_LABEL(start);
for (int j = i; j >= 0; j--)
CheckStackTrace4(j);
DECLARE_ADDRESS_LABEL(end);
}
void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) {
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]);
INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]);
DECLARE_ADDRESS_LABEL(start);
for (int j = i; j >= 0; j--)
CheckStackTrace3(j);
DECLARE_ADDRESS_LABEL(end);
}
void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) {
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]);
INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]);
DECLARE_ADDRESS_LABEL(start);
for (int j = i; j >= 0; j--)
CheckStackTrace2(j);
DECLARE_ADDRESS_LABEL(end);
}
void ATTRIBUTE_NOINLINE CheckStackTrace(int i) {
INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]);
DECLARE_ADDRESS_LABEL(start);
for (int j = i; j >= 0; j--) {
CheckStackTrace1(j);
}
DECLARE_ADDRESS_LABEL(end);
}
} // namespace
//-----------------------------------------------------------------------//
int main(int argc, char ** argv) {
CheckStackTrace(0);
printf("PASS\n");
#if TEST_UCONTEXT_BITS
leaf_capture_fn = CaptureLeafUContext;
CheckStackTrace(0);
printf("PASS\n");
#endif // TEST_UCONTEXT_BITS
return 0;
}

View file

@ -0,0 +1,161 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Arun Sharma
#include "config_for_unittests.h"
#include "system-alloc.h"
#include <stdio.h>
#if defined HAVE_STDINT_H
#include <stdint.h> // to get uintptr_t
#elif defined HAVE_INTTYPES_H
#include <inttypes.h> // another place uintptr_t might be defined
#endif
#include <sys/types.h>
#include <algorithm>
#include <limits>
#include "base/logging.h" // for Check_GEImpl, Check_LTImpl, etc
#include "common.h" // for kAddressBits
#include "gperftools/malloc_extension.h" // for MallocExtension::instance
#include "gperftools/tcmalloc.h"
#include "tests/testutil.h"
class ArraySysAllocator : public SysAllocator {
public:
// Was this allocator invoked at least once?
bool invoked_;
ArraySysAllocator() : SysAllocator() {
ptr_ = 0;
invoked_ = false;
}
void* Alloc(size_t size, size_t *actual_size, size_t alignment) {
invoked_ = true;
if (size > kArraySize) {
return NULL;
}
void *result = &array_[ptr_];
uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
if (actual_size) {
*actual_size = size;
}
// Try to get more memory for alignment
size_t extra = alignment - (ptr & (alignment-1));
size += extra;
CHECK_LT(ptr_ + size, kArraySize);
if ((ptr & (alignment-1)) != 0) {
ptr += alignment - (ptr & (alignment-1));
}
ptr_ += size;
return reinterpret_cast<void *>(ptr);
}
void DumpStats() {
}
private:
static const int kArraySize = 8 * 1024 * 1024;
char array_[kArraySize];
// We allocate the next chunk from here
int ptr_;
};
const int ArraySysAllocator::kArraySize;
ArraySysAllocator a;
static void TestBasicInvoked() {
MallocExtension::instance()->SetSystemAllocator(&a);
// An allocation size that is likely to trigger the system allocator.
// XXX: this is implementation specific.
char *p = noopt(new char[1024 * 1024]);
delete [] p;
// Make sure that our allocator was invoked.
CHECK(a.invoked_);
}
#if 0 // could port this to various OSs, but won't bother for now
TEST(AddressBits, CpuVirtualBits) {
// Check that kAddressBits is as least as large as either the number of bits
// in a pointer or as the number of virtual bits handled by the processor.
// To be effective this test must be run on each processor model.
const int kPointerBits = 8 * sizeof(void*);
const int kImplementedVirtualBits = NumImplementedVirtualBits();
CHECK_GE(kAddressBits, std::min(kImplementedVirtualBits, kPointerBits));
}
#endif
static void TestBasicRetryFailTest() {
// Check with the allocator still works after a failed allocation.
//
// There is no way to call malloc and guarantee it will fail. malloc takes a
// size_t parameter and the C++ standard does not constrain the size of
// size_t. For example, consider an implementation where size_t is 32 bits
// and pointers are 64 bits.
//
// It is likely, though, that sizeof(size_t) == sizeof(void*). In that case,
// the first allocation here might succeed but the second allocation must
// fail.
//
// If the second allocation succeeds, you will have to rewrite or
// disable this test.
// The weird parens are to avoid macro-expansion of 'max' on windows.
const size_t kHugeSize = (std::numeric_limits<size_t>::max)() / 2;
void* p1 = noopt(malloc(kHugeSize));
void* p2 = noopt(malloc(kHugeSize));
CHECK(p2 == NULL);
if (p1 != NULL) free(p1);
char* q = noopt(new char[1024]);
CHECK(q != NULL);
delete [] q;
}
int main(int argc, char** argv) {
TestBasicInvoked();
TestBasicRetryFailTest();
printf("PASS\n");
return 0;
}

View file

@ -0,0 +1,139 @@
// -*- 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.
// ---
// Author: Michael Chastain
//
// This is a unit test for large allocations in malloc and friends.
// "Large" means "so large that they overflow the address space".
// For 32 bits, this means allocations near 2^32 bytes and 2^31 bytes.
// For 64 bits, this means allocations near 2^64 bytes and 2^63 bytes.
#include <stddef.h> // for size_t, NULL
#include <stdlib.h> // for malloc, free, realloc
#include <stdio.h>
#include <set> // for set, etc
#include "base/logging.h" // for operator<<, CHECK, etc
#include "gperftools/tcmalloc.h"
#include "tests/testutil.h"
using std::set;
// Alloc a size that should always fail.
void TryAllocExpectFail(size_t size) {
void* p1 = noopt(malloc(size));
CHECK(p1 == NULL);
void* p2 = noopt(malloc(1));
CHECK(p2 != NULL);
void* p3 = noopt(realloc(p2, size));
CHECK(p3 == NULL);
free(p2);
}
// Alloc a size that might work and might fail.
// If it does work, touch some pages.
void TryAllocMightFail(size_t size) {
unsigned char* p = static_cast<unsigned char*>(noopt(malloc(size)));
if (p != NULL) {
static const size_t kPoints = 1024;
for ( size_t i = 0; i < kPoints; ++i ) {
p[i * (size / kPoints)] = static_cast<unsigned char>(i);
}
for ( size_t i = 0; i < kPoints; ++i ) {
CHECK(p[i * (size / kPoints)] == static_cast<unsigned char>(i));
}
p[size-1] = 'M';
CHECK(p[size-1] == 'M');
}
free(noopt(p));
}
int main (int argc, char** argv) {
// Allocate some 0-byte objects. They better be unique.
// 0 bytes is not large but it exercises some paths related to
// large-allocation code.
{
static const int kZeroTimes = 1024;
printf("Test malloc(0) x %d\n", kZeroTimes);
set<char*> p_set;
for ( int i = 0; i < kZeroTimes; ++i ) {
char* p = new char;
CHECK(p != NULL);
CHECK(p_set.find(p) == p_set.end());
p_set.insert(p_set.end(), p);
}
// Just leak the memory.
}
// Grab some memory so that some later allocations are guaranteed to fail.
printf("Test small malloc\n");
void* p_small = noopt(malloc(4*1048576));
CHECK(p_small != NULL);
// Test sizes up near the maximum size_t.
// These allocations test the wrap-around code.
printf("Test malloc(0 - N)\n");
const size_t zero = 0;
static const size_t kMinusNTimes = 16384;
for ( size_t i = 1; i < kMinusNTimes; ++i ) {
TryAllocExpectFail(zero - i);
}
// Test sizes a bit smaller.
// The small malloc above guarantees that all these return NULL.
printf("Test malloc(0 - 1048576 - N)\n");
static const size_t kMinusMBMinusNTimes = 16384;
for ( size_t i = 0; i < kMinusMBMinusNTimes; ++i) {
TryAllocExpectFail(zero - 1048576 - i);
}
// Test sizes at half of size_t.
// These might or might not fail to allocate.
printf("Test malloc(max/2 +- N)\n");
static const size_t kHalfPlusMinusTimes = 64;
const size_t half = (zero - 2) / 2 + 1;
for ( size_t i = 0; i < kHalfPlusMinusTimes; ++i) {
TryAllocMightFail(half - i);
TryAllocMightFail(half + i);
}
printf("PASS\n");
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,84 @@
#!/bin/sh
# Copyright (c) 2013, 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: Adhemerval Zanella
#
# Runs the tcmalloc_unittest with various environment variables.
# This is necessary because tuning some environment variables
# (TCMALLOC_TRANSFER_NUM_OBJ for instance) should not change program
# behavior, just performance.
BINDIR="${BINDIR:-.}"
TCMALLOC_UNITTEST="${1:-$BINDIR/tcmalloc_unittest}"
TMPDIR=/tmp/tcmalloc_unittest
rm -rf $TMPDIR || exit 2
mkdir $TMPDIR || exit 3
run_unittest() {
if $TCMALLOC_UNITTEST > $TMPDIR/output 2>&1; then
echo "OK"
else
echo "FAILED"
echo "Output from the failed run:"
echo "----"
cat $TMPDIR/output
echo "----"
exit 4
fi
}
# $1: value of tcmalloc_unittest env. var.
run_check_transfer_num_obj() {
[ -n "$1" ] && export TCMALLOC_TRANSFER_NUM_OBJ="$1"
echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_TRANSFER_NUM_OBJ=$1 ... "
run_unittest
}
run_check_transfer_num_obj ""
run_check_transfer_num_obj "40"
run_check_transfer_num_obj "4096"
echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_AGGRESSIVE_DECOMMIT=t ... "
TCMALLOC_AGGRESSIVE_DECOMMIT=t run_unittest
echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_HEAP_LIMIT_MB=512 ... "
TCMALLOC_HEAP_LIMIT_MB=512 run_unittest
echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_ENABLE_SIZED_DELETE=t ..."
TCMALLOC_ENABLE_SIZED_DELETE=t run_unittest
echo "PASS"

View file

@ -0,0 +1,224 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Craig Silverstein
//
// A few routines that are useful for multiple tests in this directory.
#include "config_for_unittests.h"
#include <stdlib.h> // for NULL, abort()
// On FreeBSD, if you #include <sys/resource.h>, you have to get stdint first.
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include "tests/testutil.h"
// When compiled 64-bit and run on systems with swap several unittests will end
// up trying to consume all of RAM+swap, and that can take quite some time. By
// limiting the address-space size we get sufficient coverage without blowing
// out job limits.
void SetTestResourceLimit() {
#ifdef HAVE_SYS_RESOURCE_H
// The actual resource we need to set varies depending on which flavour of
// unix. On Linux we need RLIMIT_AS because that covers the use of mmap.
// Otherwise hopefully RLIMIT_RSS is good enough. (Unfortunately 64-bit
// and 32-bit headers disagree on the type of these constants!)
#ifdef RLIMIT_AS
#define USE_RESOURCE RLIMIT_AS
#else
#define USE_RESOURCE RLIMIT_RSS
#endif
// Restrict the test to 1GiB, which should fit comfortably well on both
// 32-bit and 64-bit hosts, and executes in ~1s.
const rlim_t kMaxMem = 1<<30;
struct rlimit rlim;
if (getrlimit(USE_RESOURCE, &rlim) == 0) {
if (rlim.rlim_cur == RLIM_INFINITY || rlim.rlim_cur > kMaxMem) {
rlim.rlim_cur = kMaxMem;
setrlimit(USE_RESOURCE, &rlim); // ignore result
}
}
#endif /* HAVE_SYS_RESOURCE_H */
}
struct FunctionAndId {
void (*ptr_to_function)(int);
int id;
};
#if defined(NO_THREADS) || !(defined(HAVE_PTHREAD) || defined(_WIN32))
extern "C" void RunThread(void (*fn)()) {
(*fn)();
}
extern "C" void RunManyThreads(void (*fn)(), int count) {
// I guess the best we can do is run fn sequentially, 'count' times
for (int i = 0; i < count; i++)
(*fn)();
}
extern "C" void RunManyThreadsWithId(void (*fn)(int), int count, int) {
for (int i = 0; i < count; i++)
(*fn)(i); // stacksize doesn't make sense in a non-threaded context
}
#elif defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN /* We always want minimal includes */
#endif
#include <windows.h>
extern "C" {
// This helper function has the signature that pthread_create wants.
DWORD WINAPI RunFunctionInThread(LPVOID ptr_to_ptr_to_fn) {
(**static_cast<void (**)()>(ptr_to_ptr_to_fn))(); // runs fn
return 0;
}
DWORD WINAPI RunFunctionInThreadWithId(LPVOID ptr_to_fnid) {
FunctionAndId* fn_and_id = static_cast<FunctionAndId*>(ptr_to_fnid);
(*fn_and_id->ptr_to_function)(fn_and_id->id); // runs fn
return 0;
}
void RunManyThreads(void (*fn)(), int count) {
DWORD dummy;
HANDLE* hThread = new HANDLE[count];
for (int i = 0; i < count; i++) {
hThread[i] = CreateThread(NULL, 0, RunFunctionInThread, &fn, 0, &dummy);
if (hThread[i] == NULL) ExitProcess(i);
}
WaitForMultipleObjects(count, hThread, TRUE, INFINITE);
for (int i = 0; i < count; i++) {
CloseHandle(hThread[i]);
}
delete[] hThread;
}
void RunThread(void (*fn)()) {
RunManyThreads(fn, 1);
}
void RunManyThreadsWithId(void (*fn)(int), int count, int stacksize) {
DWORD dummy;
HANDLE* hThread = new HANDLE[count];
FunctionAndId* fn_and_ids = new FunctionAndId[count];
for (int i = 0; i < count; i++) {
fn_and_ids[i].ptr_to_function = fn;
fn_and_ids[i].id = i;
hThread[i] = CreateThread(NULL, stacksize, RunFunctionInThreadWithId,
&fn_and_ids[i], 0, &dummy);
if (hThread[i] == NULL) ExitProcess(i);
}
WaitForMultipleObjects(count, hThread, TRUE, INFINITE);
for (int i = 0; i < count; i++) {
CloseHandle(hThread[i]);
}
delete[] fn_and_ids;
delete[] hThread;
}
}
#else // not NO_THREADS, not !HAVE_PTHREAD, not _WIN32
#include <pthread.h>
#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
extern "C" {
// This helper function has the signature that pthread_create wants.
static void* RunFunctionInThread(void *ptr_to_ptr_to_fn) {
(**static_cast<void (**)()>(ptr_to_ptr_to_fn))(); // runs fn
return NULL;
}
static void* RunFunctionInThreadWithId(void *ptr_to_fnid) {
FunctionAndId* fn_and_id = static_cast<FunctionAndId*>(ptr_to_fnid);
(*fn_and_id->ptr_to_function)(fn_and_id->id); // runs fn
return NULL;
}
// Run a function in a thread of its own and wait for it to finish.
// This is useful for tcmalloc testing, because each thread is
// handled separately in tcmalloc, so there's interesting stuff to
// test even if the threads are not running concurrently.
void RunThread(void (*fn)()) {
pthread_t thr;
// Even though fn is on the stack, it's safe to pass a pointer to it,
// because we pthread_join immediately (ie, before RunInThread exits).
SAFE_PTHREAD(pthread_create(&thr, NULL, RunFunctionInThread, &fn));
SAFE_PTHREAD(pthread_join(thr, NULL));
}
void RunManyThreads(void (*fn)(), int count) {
pthread_t* thr = new pthread_t[count];
for (int i = 0; i < count; i++) {
SAFE_PTHREAD(pthread_create(&thr[i], NULL, RunFunctionInThread, &fn));
}
for (int i = 0; i < count; i++) {
SAFE_PTHREAD(pthread_join(thr[i], NULL));
}
delete[] thr;
}
void RunManyThreadsWithId(void (*fn)(int), int count, int stacksize) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, stacksize);
pthread_t* thr = new pthread_t[count];
FunctionAndId* fn_and_ids = new FunctionAndId[count];
for (int i = 0; i < count; i++) {
fn_and_ids[i].ptr_to_function = fn;
fn_and_ids[i].id = i;
SAFE_PTHREAD(pthread_create(&thr[i], &attr,
RunFunctionInThreadWithId, &fn_and_ids[i]));
}
for (int i = 0; i < count; i++) {
SAFE_PTHREAD(pthread_join(thr[i], NULL));
}
delete[] fn_and_ids;
delete[] thr;
pthread_attr_destroy(&attr);
}
}
#endif

View file

@ -0,0 +1,73 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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: Craig Silverstein
#ifndef TCMALLOC_TOOLS_TESTUTIL_H_
#define TCMALLOC_TOOLS_TESTUTIL_H_
// Run a function in a thread of its own and wait for it to finish.
// The function you pass in must have the signature
// void MyFunction();
extern "C" void RunThread(void (*fn)());
// Run a function X times, in X threads, and wait for them all to finish.
// The function you pass in must have the signature
// void MyFunction();
extern "C" void RunManyThreads(void (*fn)(), int count);
// The 'advanced' version: run a function X times, in X threads, and
// wait for them all to finish. Give them all the specified stack-size.
// (If you're curious why this takes a stacksize and the others don't,
// it's because the one client of this fn wanted to specify stacksize. :-) )
// The function you pass in must have the signature
// void MyFunction(int idx);
// where idx is the index of the thread (which of the X threads this is).
extern "C" void RunManyThreadsWithId(void (*fn)(int), int count, int stacksize);
// When compiled 64-bit and run on systems with swap several unittests will end
// up trying to consume all of RAM+swap, and that can take quite some time. By
// limiting the address-space size we get sufficient coverage without blowing
// out job limits.
void SetTestResourceLimit();
static void (* volatile noopt_helper)(void *) = +[] (void* dummy) {};
// This function forces compiler to forget specific knowledge about
// value of 'val'. This is useful to avoid compiler optimizing out
// new/delete pairs for our unit tests.
template <typename T>
T noopt(T val) {
noopt_helper(&val);
return val;
}
#endif // TCMALLOC_TOOLS_TESTUTIL_H_

View file

@ -0,0 +1,84 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2004, 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: Sanjay Ghemawat
//
// Check that we do not leak memory when cycling through lots of threads.
#include "config_for_unittests.h"
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for sleep()
#endif
#include "base/logging.h"
#include <gperftools/malloc_extension.h>
#include "tests/testutil.h" // for RunThread()
// Size/number of objects to allocate per thread (1 MB per thread)
static const int kObjectSize = 1024;
static const int kNumObjects = 1024;
// Number of threads to create and destroy
static const int kNumThreads = 1000;
// Allocate lots of stuff
static void AllocStuff() {
void** objects = new void*[kNumObjects];
for (int i = 0; i < kNumObjects; i++) {
objects[i] = malloc(kObjectSize);
}
for (int i = 0; i < kNumObjects; i++) {
free(objects[i]);
}
delete[] objects;
}
int main(int argc, char** argv) {
static const int kDisplaySize = 1048576;
char* display = new char[kDisplaySize];
for (int i = 0; i < kNumThreads; i++) {
RunThread(&AllocStuff);
if (((i+1) % 200) == 0) {
fprintf(stderr, "Iteration: %d of %d\n", (i+1), kNumThreads);
MallocExtension::instance()->GetStats(display, kDisplaySize);
fprintf(stderr, "%s\n", display);
}
}
delete[] display;
printf("PASS\n");
#ifdef HAVE_UNISTD_H
sleep(1); // Prevent exit race problem with glibc
#endif
return 0;
}