summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
authorcsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2009-09-11 18:42:32 +0000
committercsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2009-09-11 18:42:32 +0000
commit19dfa9e3733155e57406fbd082273eb53cb2750e (patch)
tree8c000b5035acf1bd01cb7208972e128bbd98e4b2 /src/tests
parent2197cc670204c583bba3903b765c77620f349609 (diff)
downloadgperftools-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.cc2
-rw-r--r--src/tests/debugallocation_test.cc223
-rwxr-xr-xsrc/tests/debugallocation_test.sh78
-rw-r--r--src/tests/heap-checker_unittest.cc29
-rw-r--r--src/tests/heap-profiler_unittest.cc22
-rwxr-xr-xsrc/tests/heap-profiler_unittest.sh4
-rw-r--r--src/tests/low_level_alloc_unittest.cc12
-rw-r--r--src/tests/malloc_extension_test.cc7
-rw-r--r--src/tests/markidle_unittest.cc1
-rw-r--r--src/tests/memalign_unittest.cc26
-rw-r--r--src/tests/pagemap_unittest.cc2
-rw-r--r--src/tests/profile-handler_unittest.cc9
-rw-r--r--src/tests/profiledata_unittest.cc10
-rw-r--r--src/tests/profiler_unittest.cc2
-rwxr-xr-xsrc/tests/profiler_unittest.sh150
-rwxr-xr-xsrc/tests/sampler_test.cc1
-rw-r--r--src/tests/stacktrace_unittest.cc123
-rw-r--r--src/tests/tcmalloc_unittest.cc27
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");
}