diff options
author | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2009-09-11 18:42:32 +0000 |
---|---|---|
committer | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2009-09-11 18:42:32 +0000 |
commit | 19dfa9e3733155e57406fbd082273eb53cb2750e (patch) | |
tree | 8c000b5035acf1bd01cb7208972e128bbd98e4b2 /src/tests | |
parent | 2197cc670204c583bba3903b765c77620f349609 (diff) | |
download | gperftools-19dfa9e3733155e57406fbd082273eb53cb2750e.tar.gz |
Thu Sep 10 13:51:15 2009 Google Inc. <opensource@google.com>
* google-perftools: version 1.4 release
* Add debugallocation library, to catch memory leaks, stomping, etc
* Add --raw mode to allow for delayed processing of pprof files
* Use less memory when reading CPU profiles
* New environment variables to control kernel-allocs (sbrk, memfs, etc)
* Add MarkThreadBusy(): performance improvement
* Remove static thread-cache-size code; all is dynamic now
* Add new HiddenPointer class to heap checker
* BUGFIX: pvalloc(0) allocates now (found by new debugalloc library)
* BUGFIX: valloc test (not implementation) no longer overruns memory
* BUGFIX: GetHeapProfile no longer deadlocks
* BUGFIX: Support unmapping memory regions before main
* BUGFIX: Fix some malloc-stats formatting
* BUGFIX: Don't crash as often when freeing libc-allocated memory
* BUGFIX: Deal better with incorrect PPROF_PATH when symbolizing
* BUGFIX: weaken new/delete/etc in addition to malloc/free/etc
* BUGFIX: Fix return value of GetAllocatedSize
* PORTING: Fix mmap-#define problem on some 64-bit systems
* PORTING: Call ranlib again (some OS X versions need it)
* PORTING: Fix a leak when building with LLVM
* PORTING: Remove some unneeded bash-ishs from testing scripts
* WINDOWS: Support library unloading as well as loading
* WINDOWS/BUGFIX: Set page to 'xrw' instead of 'rw' when patching
git-svn-id: http://gperftools.googlecode.com/svn/trunk@76 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/addressmap_unittest.cc | 2 | ||||
-rw-r--r-- | src/tests/debugallocation_test.cc | 223 | ||||
-rwxr-xr-x | src/tests/debugallocation_test.sh | 78 | ||||
-rw-r--r-- | src/tests/heap-checker_unittest.cc | 29 | ||||
-rw-r--r-- | src/tests/heap-profiler_unittest.cc | 22 | ||||
-rwxr-xr-x | src/tests/heap-profiler_unittest.sh | 4 | ||||
-rw-r--r-- | src/tests/low_level_alloc_unittest.cc | 12 | ||||
-rw-r--r-- | src/tests/malloc_extension_test.cc | 7 | ||||
-rw-r--r-- | src/tests/markidle_unittest.cc | 1 | ||||
-rw-r--r-- | src/tests/memalign_unittest.cc | 26 | ||||
-rw-r--r-- | src/tests/pagemap_unittest.cc | 2 | ||||
-rw-r--r-- | src/tests/profile-handler_unittest.cc | 9 | ||||
-rw-r--r-- | src/tests/profiledata_unittest.cc | 10 | ||||
-rw-r--r-- | src/tests/profiler_unittest.cc | 2 | ||||
-rwxr-xr-x | src/tests/profiler_unittest.sh | 150 | ||||
-rwxr-xr-x | src/tests/sampler_test.cc | 1 | ||||
-rw-r--r-- | src/tests/stacktrace_unittest.cc | 123 | ||||
-rw-r--r-- | src/tests/tcmalloc_unittest.cc | 27 |
18 files changed, 595 insertions, 133 deletions
diff --git a/src/tests/addressmap_unittest.cc b/src/tests/addressmap_unittest.cc index 6b65cc3..bfbb9a8 100644 --- a/src/tests/addressmap_unittest.cc +++ b/src/tests/addressmap_unittest.cc @@ -87,6 +87,8 @@ int main(int argc, char** argv) { } 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 random_shuffle(ptrs_and_sizes.begin(), ptrs_and_sizes.end()); diff --git a/src/tests/debugallocation_test.cc b/src/tests/debugallocation_test.cc new file mode 100644 index 0000000..4274b7e --- /dev/null +++ b/src/tests/debugallocation_test.cc @@ -0,0 +1,223 @@ +// 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 <vector> +#include "google/malloc_extension.h" +#include "base/logging.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(DebugAllocationTest, DeallocMismatch) { + // Allocate with malloc. + { + int* x = static_cast<int*>(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 = new int; + IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free"); + IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]"); + delete x; + } + + // Allocate with new[]. + { + int* x = 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; + } +} + +TEST(DebugAllocationTest, FreeQueueTest) { + // Verify that the allocator doesn't return blocks that were recently freed. + int* x = new int; + int* old_x = x; + delete x; + x = 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 = new int; + delete x; + int poisoned_x_value = *x; + *x = 1; // a dangling write. + + char* s = 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 = 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. +} + +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(malloc(1)); + FLAGS_max_free_queue_size = 1048576; +#endif + + // Free something and check that it disappears from allocated bytes + // immediately. + char* p = 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 = malloc(i); + EXPECT_EQ(i, MallocExtension::instance()->GetAllocatedSize(p)); + free(p); + } +#endif + void* a = 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); +} + +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(); +} diff --git a/src/tests/debugallocation_test.sh b/src/tests/debugallocation_test.sh new file mode 100755 index 0000000..2568d54 --- /dev/null +++ b/src/tests/debugallocation_test.sh @@ -0,0 +1,78 @@ +#!/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:-.}" + +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 | { + read regex_line + regex=`expr "$regex_line" : "Expected regex:\(.*\)"` + test -z "$regex" && echo "done" # no regex line, not a death-case + grep "$regex" >/dev/null 2>&1 # pass the rest of the lines through grep + } || num_failures=`expr $num_failures + 1` +} + +death_test_num=0 # which death test to run +while test -z `OneDeathTest "$death_test_num"`; do + echo "Done with death test $death_test_num" + death_test_num=`expr $death_test_num + 1` +done + +# Test the non-death parts of the test too +if ! "$DEBUGALLOCATION_TEST"; then + 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 diff --git a/src/tests/heap-checker_unittest.cc b/src/tests/heap-checker_unittest.cc index 55b6a21..4be1a7c 100644 --- a/src/tests/heap-checker_unittest.cc +++ b/src/tests/heap-checker_unittest.cc @@ -1,10 +1,10 @@ // 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 @@ -14,7 +14,7 @@ // * 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 @@ -58,7 +58,9 @@ // (see the comment in our .h file). #include "config_for_unittests.h" -#include <sys/poll.h> +#ifdef HAVE_POLL_H +#include <poll.h> +#endif #if defined HAVE_STDINT_H #include <stdint.h> // to get uint16_t (ISO naming madness) #elif defined HAVE_INTTYPES_H @@ -539,6 +541,20 @@ static void TestHeapLeakCheckerDeathCountMore() { DeAllocHidden(&bar2); } +static void TestHiddenPointer() { + int i; + void* foo = &i; + HiddenPointer<void> p(foo); + CHECK_EQ(foo, p.get()); + + // Confirm pointer doesn't appear to contain a byte sequence + // that == the pointer. We don't really need to test that + // the xor trick itself works, as without it nothing in this + // test suite would work. See the Hide/Unhide/*Hidden* set + // of helper methods. + CHECK_NE(foo, *reinterpret_cast<void**>(&p)); +} + // simple tests that deallocate what they allocated static void TestHeapLeakChecker() { { HeapLeakChecker check("trivial"); @@ -1304,7 +1320,6 @@ static int Pass() { int main(int argc, char** argv) { run_hidden_ptr = DoRunHidden; wipe_stack_ptr = DoWipeStack; - if (!HeapLeakChecker::IsActive()) { CHECK_EQ(FLAGS_heap_check, ""); LOG(WARNING, "HeapLeakChecker got turned off; we won't test much..."); @@ -1376,6 +1391,8 @@ int main(int argc, char** argv) { HeapLeakChecker heap_check("all"); + TestHiddenPointer(); + TestHeapLeakChecker(); Pause(); TestLeakButTotalsMatch(); @@ -1443,6 +1460,6 @@ int main(int argc, char** argv) { } CHECK(HeapLeakChecker::NoGlobalLeaks()); // so far, so good - + return Pass(); } diff --git a/src/tests/heap-profiler_unittest.cc b/src/tests/heap-profiler_unittest.cc index a9b6510..e5dedee 100644 --- a/src/tests/heap-profiler_unittest.cc +++ b/src/tests/heap-profiler_unittest.cc @@ -96,6 +96,27 @@ static void TestHeapProfilerStartStopIsRunning() { } } +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]); @@ -107,6 +128,7 @@ int main(int argc, char** argv) { } TestHeapProfilerStartStopIsRunning(); + TestDumpHeapProfiler(); Allocate(0, 40, 100); Deallocate(0, 40); diff --git a/src/tests/heap-profiler_unittest.sh b/src/tests/heap-profiler_unittest.sh index a728aa9..ad0a1ec 100755 --- a/src/tests/heap-profiler_unittest.sh +++ b/src/tests/heap-profiler_unittest.sh @@ -136,6 +136,10 @@ VerifyMemFunction Allocate "$HEAPPROFILE"_*.1448.heap "$HEAPPROFILE"_*.1548.heap 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 $TMPDIR # clean up if [ $num_failures = 0 ]; then diff --git a/src/tests/low_level_alloc_unittest.cc b/src/tests/low_level_alloc_unittest.cc index 1ec0f1b..f98f8a5 100644 --- a/src/tests/low_level_alloc_unittest.cc +++ b/src/tests/low_level_alloc_unittest.cc @@ -26,9 +26,6 @@ * 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: Mike Burrows */ // A test for low_level_alloc.cc @@ -78,7 +75,7 @@ static bool using_low_level_alloc = false; // 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, +// 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) { @@ -93,6 +90,11 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { 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; @@ -195,7 +197,7 @@ int main(int argc, char *argv[]) { CHECK_EQ(frees, 0); } } - printf("PASS\n"); + printf("\nPASS\n"); CHECK_EQ(MallocHook::SetNewHook(old_alloc_hook), AllocHook); CHECK_EQ(MallocHook::SetDeleteHook(old_free_hook), FreeHook); return 0; diff --git a/src/tests/malloc_extension_test.cc b/src/tests/malloc_extension_test.cc index 7254fe5..1f00f73 100644 --- a/src/tests/malloc_extension_test.cc +++ b/src/tests/malloc_extension_test.cc @@ -58,6 +58,13 @@ int main(int argc, char** argv) { CHECK_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000); CHECK_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000); + for (int i = 0; i < 10; ++i) { + void *p = malloc(i); + CHECK_GE(MallocExtension::instance()->GetAllocatedSize(p), + MallocExtension::instance()->GetEstimatedAllocatedSize(i)); + free(p); + } + // Check the c-shim version too. CHECK_GE(MallocExtension_GetAllocatedSize(a), 1000); CHECK_LE(MallocExtension_GetAllocatedSize(a), 5000); diff --git a/src/tests/markidle_unittest.cc b/src/tests/markidle_unittest.cc index 0d9178f..ac9971f 100644 --- a/src/tests/markidle_unittest.cc +++ b/src/tests/markidle_unittest.cc @@ -31,6 +31,7 @@ // Author: Sanjay Ghemawat // // MallocExtension::MarkThreadIdle() testing +#include <stdio.h> #include "config_for_unittests.h" #include "base/logging.h" diff --git a/src/tests/memalign_unittest.cc b/src/tests/memalign_unittest.cc index a8d418e..d5b60db 100644 --- a/src/tests/memalign_unittest.cc +++ b/src/tests/memalign_unittest.cc @@ -170,13 +170,19 @@ int main(int argc, char** argv) { 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 - for (size_t s = 0; ; s += (10 << 20)) { - int r = posix_memalign(&ptr, 1024, s); - if (r == ENOMEM) break; - CHECK(r == 0); - free(ptr); + 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(); @@ -185,8 +191,8 @@ int main(int argc, char** argv) { for (int s = 0; s != -1; s = NextSize(s)) { void* p = valloc(s); CheckAlignment(p, pagesize); - Fill(p, pagesize, 'v'); - CHECK(Valid(p, pagesize, 'v')); + Fill(p, s, 'v'); + CHECK(Valid(p, s, 'v')); free(p); } } @@ -201,12 +207,6 @@ int main(int argc, char** argv) { CHECK(Valid(p, alloc_needed, 'x')); free(p); } - - // should be safe to write upto a page in pvalloc(0) region - void* p = pvalloc(0); - Fill(p, pagesize, 'y'); - CHECK(Valid(p, pagesize, 'y')); - free(p); } printf("PASS\n"); diff --git a/src/tests/pagemap_unittest.cc b/src/tests/pagemap_unittest.cc index 902b8dd..dcf6c9a 100644 --- a/src/tests/pagemap_unittest.cc +++ b/src/tests/pagemap_unittest.cc @@ -63,6 +63,8 @@ static void Permute(vector<intptr_t>* elements) { // 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++) { diff --git a/src/tests/profile-handler_unittest.cc b/src/tests/profile-handler_unittest.cc index d780aac..4b247c7 100644 --- a/src/tests/profile-handler_unittest.cc +++ b/src/tests/profile-handler_unittest.cc @@ -14,15 +14,6 @@ #include "base/simple_mutex.h" // Some helpful macros for the test class -#define EXPECT_TRUE(cond) CHECK(cond) -#define EXPECT_FALSE(cond) CHECK(!(cond)) -#define EXPECT_EQ(a, b) CHECK_EQ(a, b) -#define EXPECT_NE(a, b) CHECK_NE(a, b) -#define EXPECT_GT(a, b) CHECK_GT(a, b) -#define EXPECT_LT(a, b) CHECK_LT(a, b) -#define EXPECT_GE(a, b) CHECK_GE(a, b) -#define EXPECT_LE(a, b) CHECK_LE(a, b) -#define EXPECT_STREQ(a, b) CHECK(strcmp(a, b) == 0) #define TEST_F(cls, fn) void cls :: fn() namespace { diff --git a/src/tests/profiledata_unittest.cc b/src/tests/profiledata_unittest.cc index 31ba3b6..f569f64 100644 --- a/src/tests/profiledata_unittest.cc +++ b/src/tests/profiledata_unittest.cc @@ -51,18 +51,8 @@ using std::string; // Some helpful macros for the test class -#define EXPECT_TRUE(cond) CHECK(cond) -#define EXPECT_FALSE(cond) CHECK(!(cond)) -#define EXPECT_EQ(a, b) CHECK_EQ(a, b) -#define EXPECT_NE(a, b) CHECK_NE(a, b) -#define EXPECT_GT(a, b) CHECK_GT(a, b) -#define EXPECT_LT(a, b) CHECK_LT(a, b) -#define EXPECT_GE(a, b) CHECK_GE(a, b) -#define EXPECT_LE(a, b) CHECK_LE(a, b) -#define EXPECT_STREQ(a, b) CHECK(strcmp(a, b) == 0) #define TEST_F(cls, fn) void cls :: fn() - namespace { template<typename T> class scoped_array { diff --git a/src/tests/profiler_unittest.cc b/src/tests/profiler_unittest.cc index 1063751..1908b03 100644 --- a/src/tests/profiler_unittest.cc +++ b/src/tests/profiler_unittest.cc @@ -48,7 +48,7 @@ static int result = 0; static int g_iters = 0; // argv[1] -Mutex mutex; +Mutex mutex(Mutex::LINKER_INITIALIZED); static void test_other_thread() { #ifndef NO_THREADS diff --git a/src/tests/profiler_unittest.sh b/src/tests/profiler_unittest.sh index 2524b96..73be680 100755 --- a/src/tests/profiler_unittest.sh +++ b/src/tests/profiler_unittest.sh @@ -53,21 +53,40 @@ if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then exit 1 fi +TMPDIR=/tmp/profile_info + UNITTEST_DIR=${1:-$BINDIR} PPROF=${2:-$PPROF_PATH} -PROFILER1=$UNITTEST_DIR/profiler1_unittest -PROFILER2=$UNITTEST_DIR/profiler2_unittest -PROFILER3=$UNITTEST_DIR/profiler3_unittest -PROFILER4=$UNITTEST_DIR/profiler4_unittest +# 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" -TMPDIR=/tmp/profile_info +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 -rm -rf $TMPDIR -mkdir $TMPDIR || exit 2 +rm -rf "$TMPDIR" +mkdir "$TMPDIR" || exit 2 num_failures=0 @@ -82,15 +101,17 @@ RegisterFailure() { # 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 - # We need to run the script with no args to get the actual exe name - exec1=`$2 2>&1 | awk '{print $2; exit;}'` - prof2=$TMPDIR/$3 - exec2=`$4 2>&1 | awk '{print $2; exit;}'` - mult=$5 + prof1="$TMPDIR/$1" + exec1="$2" + prof2="$TMPDIR/$3" + exec2="$4" + mult="$5" - mthread1=`$PPROF $exec1 $prof1 | grep test_main_thread | awk '{print $1}'` - mthread2=`$PPROF $exec2 $prof2 | grep test_main_thread | awk '{print $1}'` + # 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" ] || \ @@ -106,6 +127,32 @@ VerifySimilar() { 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 @@ -115,13 +162,15 @@ VerifySimilar() { # 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 + prof1="$TMPDIR/$1" # We need to run the script with no args to get the actual exe name - exec1=`$2 2>&1 | awk '{print $2; exit;}'` - mult=$3 + exec1="$2" + mult="$3" - mthread=`$PPROF $exec1 $prof1 | grep test_main_thread | awk '{print $1}'` - othread=`$PPROF $exec1 $prof1 | grep test_other_thread | awk '{print $1}'` + # 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 \ @@ -143,54 +192,65 @@ 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 p2 $PROFILER1 2 +"$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 p4 $PROFILER2 2 +"$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 p6 $PROFILER2 2 +CPUPROFILE="$TMPDIR/p5" "$PROFILER2" 50 || RegisterFailure +CPUPROFILE="$TMPDIR/p6" "$PROFILER2" 100 || RegisterFailure +VerifySimilar p5 "$PROFILER2_REALNAME" p6 "$PROFILER2_REALNAME" 2 # When we compile with threads, things take a lot longer even when we only use 1 -CPUPROFILE=$TMPDIR/p5b $PROFILER3 10 || RegisterFailure -CPUPROFILE=$TMPDIR/p5c $PROFILER3 20 || RegisterFailure -VerifySimilar p5b $PROFILER3 p5c $PROFILER3 2 +CPUPROFILE="$TMPDIR/p5b" "$PROFILER3" 10 || RegisterFailure +CPUPROFILE="$TMPDIR/p5c" "$PROFILER3" 20 || RegisterFailure +VerifySimilar p5b "$PROFILER3_REALNAME" p5c "$PROFILER3_REALNAME" 2 # Now try what happens when we use threads -$PROFILER3 5 2 $TMPDIR/p7 || RegisterFailure -$PROFILER3 10 2 $TMPDIR/p8 || RegisterFailure -VerifySimilar p7 $PROFILER3 p8 $PROFILER3 2 +"$PROFILER3" 5 2 "$TMPDIR/p7" || RegisterFailure +"$PROFILER3" 10 2 "$TMPDIR/p8" || RegisterFailure +VerifySimilar p7 "$PROFILER3_REALNAME" p8 "$PROFILER3_REALNAME" 2 -$PROFILER4 5 2 $TMPDIR/p9 || RegisterFailure -$PROFILER4 10 2 $TMPDIR/p10 || RegisterFailure -VerifySimilar p9 $PROFILER4 p10 $PROFILER4 2 +"$PROFILER4" 5 2 "$TMPDIR/p9" || RegisterFailure +"$PROFILER4" 10 2 "$TMPDIR/p10" || RegisterFailure +VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2 # More threads! -$PROFILER4 2 3 $TMPDIR/p9 || RegisterFailure -$PROFILER4 4 3 $TMPDIR/p10 || RegisterFailure -VerifySimilar p9 $PROFILER4 p10 $PROFILER4 2 +"$PROFILER4" 2 3 "$TMPDIR/p9" || RegisterFailure +"$PROFILER4" 4 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 2 4 $TMPDIR/p11 || RegisterFailure -VerifyAcrossThreads p11 $PROFILER4 2 +"$PROFILER4" 2 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" 5 2 "$TMPDIR/p14" || RegisterFailure +"$PPROF" $PPROF_FLAGS "$PROFILER3_REALNAME" "$TMPDIR/p14" --raw \ + >"$TMPDIR/p15" 2>/dev/null || RegisterFailure +VerifyIdentical p14 "$PROFILER3_REALNAME" p15 "" || RegisterFailure # Make sure that when we have a process with a fork, the profiles don't # clobber each other -CPUPROFILE=$TMPDIR/p6 $PROFILER1 1 -2 || RegisterFailure +CPUPROFILE="$TMPDIR/p6" "$PROFILER1" 1 -2 || RegisterFailure n=`ls $TMPDIR/p6* | 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 +rm -rf "$TMPDIR" # clean up echo "Tests finished with $num_failures failures" exit $num_failures diff --git a/src/tests/sampler_test.cc b/src/tests/sampler_test.cc index 2d5824e..fca10ac 100755 --- a/src/tests/sampler_test.cc +++ b/src/tests/sampler_test.cc @@ -468,7 +468,6 @@ void OldSampler::Init(uint32_t seed) { // A cut-down version of the old PickNextSampleRoutine void OldSampler::PickNextSample(size_t k) { - // Copied from "base/synchronization.cc" (written by Mike Burrows) // 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); diff --git a/src/tests/stacktrace_unittest.cc b/src/tests/stacktrace_unittest.cc index 02a2093..69e20ba 100644 --- a/src/tests/stacktrace_unittest.cc +++ b/src/tests/stacktrace_unittest.cc @@ -37,65 +37,84 @@ #include "base/logging.h" #include <google/stacktrace.h> +namespace { // Obtain a backtrace, verify that the expected callers are present in the // backtrace, and maybe print the backtrace to stdout. -//-----------------------------------------------------------------------// -void CheckStackTraceLeaf(); -void CheckStackTrace4(int i); -void CheckStackTrace3(int i); -void CheckStackTrace2(int i); -void CheckStackTrace1(int i); -void CheckStackTrace(int i); -//-----------------------------------------------------------------------// - // The sequence of functions whose return addresses we expect to see in the // backtrace. const int BACKTRACE_STEPS = 6; -void * expected_stack[BACKTRACE_STEPS] = { - (void *) &CheckStackTraceLeaf, - (void *) &CheckStackTrace4, - (void *) &CheckStackTrace3, - (void *) &CheckStackTrace2, - (void *) &CheckStackTrace1, - (void *) &CheckStackTrace, + +struct AddressRange { + const void *start, *end; }; -// Depending on the architecture/compiler/libraries, (not sure which) -// the current function may or may not appear in the backtrace. -// For gcc-2: -// -// stack[0] is ret addr within CheckStackTrace4 -// stack[1] is ret addr within CheckStackTrace3 -// stack[2] is ret addr within CheckStackTrace2 -// stack[3] is ret addr within CheckStackTrace1 -// stack[4] is ret addr within CheckStackTrace -// -// For gcc3-k8: -// -// stack[0] is ret addr within CheckStackTraceLeaf -// stack[1] is ret addr within CheckStackTrace4 -// ... -// stack[5] is ret addr within CheckStackTrace +// 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__ //-----------------------------------------------------------------------// -const int kMaxFnLen = 0x40; // assume relevant functions are only this long - -void CheckRetAddrIsInFunction( void * ret_addr, void * function_start_addr) +void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range) { - CHECK_GE(ret_addr, function_start_addr); - CHECK_LE(ret_addr, (void *) ((char *) function_start_addr + kMaxFnLen)); + CHECK_GE(ret_addr, range.start); + CHECK_LE(ret_addr, range.end); } //-----------------------------------------------------------------------// -void CheckStackTraceLeaf(void) { +void ATTRIBUTE_NOINLINE CheckStackTrace(int); +void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(void) { const int STACK_LEN = 10; void *stack[STACK_LEN]; int size; + ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]); + INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]); + DECLARE_ADDRESS_LABEL(start); size = GetStackTrace(stack, STACK_LEN, 0); printf("Obtained %d stack frames.\n", size); CHECK_GE(size, 1); @@ -114,44 +133,62 @@ void CheckStackTraceLeaf(void) { for (int i = 0; i < BACKTRACE_STEPS; i++) { printf("Backtrace %d: expected: %p..%p actual: %p ... ", - i, expected_stack[i], - reinterpret_cast<char*>(expected_stack[i]) + kMaxFnLen, stack[i]); + i, expected_range[i].start, expected_range[i].end, stack[i]); fflush(stdout); - CheckRetAddrIsInFunction(stack[i], expected_stack[i]); + CheckRetAddrIsInFunction(stack[i], expected_range[i]); printf("OK\n"); } + DECLARE_ADDRESS_LABEL(end); } //-----------------------------------------------------------------------// /* 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(); + 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"); return 0; } diff --git a/src/tests/tcmalloc_unittest.cc b/src/tests/tcmalloc_unittest.cc index 1fdd300..f1f52b8 100644 --- a/src/tests/tcmalloc_unittest.cc +++ b/src/tests/tcmalloc_unittest.cc @@ -45,6 +45,13 @@ // d. Free an object // Also, at the end of every step, object(s) are freed to maintain // the memory upper-bound. +// +// If this test is compiled with -DDEBUGALLOCATION, then we don't +// run some tests that test the inner workings of tcmalloc and +// break on debugallocation: that certain allocations are aligned +// in a certain way (even though no standard requires it), and that +// realloc() tries to minimize copying (which debug allocators don't +// care about). #include "config_for_unittests.h" // Complicated ordering requirements. tcmalloc.h defines (indirectly) @@ -82,6 +89,7 @@ #include "base/simple_mutex.h" #include "google/malloc_hook.h" #include "google/malloc_extension.h" +#include "google/tcmalloc.h" #include "thread_cache.h" #include "tests/testutil.h" @@ -511,6 +519,7 @@ static void TestHugeAllocations(AllocatorState* rnd) { } // Asking for memory sizes near signed/unsigned boundary (kMaxSignedSize) // might work or not, depending on the amount of virtual memory. +#ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs for (size_t i = 0; i < 100; i++) { void* p = NULL; p = rnd->alloc(kMaxSignedSize + i); @@ -518,6 +527,7 @@ static void TestHugeAllocations(AllocatorState* rnd) { p = rnd->alloc(kMaxSignedSize - i); if (p) free(p); } +#endif // Check that ReleaseFreeMemory has no visible effect (aka, does not // crash the test): @@ -544,6 +554,7 @@ static void TestCalloc(size_t n, size_t s, bool ok) { // This makes sure that reallocing a small number of bytes in either // direction doesn't cause us to allocate new memory. static void TestRealloc() { +#ifndef DEBUGALLOCATION // debug alloc doesn't try to minimize reallocs int start_sizes[] = { 100, 1000, 10000, 100000 }; int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 }; @@ -562,6 +573,7 @@ static void TestRealloc() { } free(p); } +#endif } static void TestNewHandler() throw (std::bad_alloc) { @@ -701,9 +713,11 @@ static void TestAlignmentForSize(int size) { CHECK((p % sizeof(double)) == 0); // Must have 16-byte alignment for large enough objects +#ifndef DEBUGALLOCATION // debug allocation doesn't need to align like this if (size >= 16) { CHECK((p % 16) == 0); } +#endif } for (int i = 0; i < kNum; i++) { free(ptrs[i]); @@ -964,12 +978,14 @@ static int RunAllTests(int argc, char** argv) { TestHugeAllocations(&rnd); // Check that large allocations fail with NULL instead of crashing +#ifndef DEBUGALLOCATION // debug allocation takes forever for huge allocs fprintf(LOGSTREAM, "Testing out of memory\n"); for (int s = 0; ; s += (10<<20)) { void* large_object = rnd.alloc(s); if (large_object == NULL) break; free(large_object); } +#endif TestHugeThreadCache(); @@ -983,5 +999,16 @@ using testing::RunAllTests; int main(int argc, char** argv) { RunAllTests(argc, argv); + // Test tc_version() + fprintf(LOGSTREAM, "Testing tc_version()\n"); + int major; + int minor; + const char* patch; + char mmp[64]; + const char* human_version = tc_version(&major, &minor, &patch); + snprintf(mmp, sizeof(mmp), "%d.%d%s", major, minor, patch); + CHECK(!strcmp(PACKAGE_STRING, human_version)); + CHECK(!strcmp(PACKAGE_VERSION, mmp)); + fprintf(LOGSTREAM, "PASS\n"); } |