diff options
author | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2007-11-29 23:39:24 +0000 |
---|---|---|
committer | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2007-11-29 23:39:24 +0000 |
commit | 11b02f7aebd05cf39f6f93bdd48786909f99f34e (patch) | |
tree | 660c613e74cffd643ee4a7d0f0cb170057abbf7d /src/tests | |
parent | 49b74b9508797f8aafe6b86e62e7efc4ec200e48 (diff) | |
download | gperftools-11b02f7aebd05cf39f6f93bdd48786909f99f34e.tar.gz |
Thu Nov 29 07:59:43 2007 Google Inc. <opensource@google.com>
* google-perftools: version 0.94 release
* PORTING: MinGW/Msys support -- runs same code as MSVC does (csilvers)
* PORTING: Add NumCPUs support for Mac OS X (csilvers)
* Work around a sscanf bug in glibc(?) (waldemar)
* Fix Windows MSVC bug triggered by thread deletion (csilvers)
* Fix bug that triggers in MSVC /O2: missing volatile (gpike)
* March-of-time support: quiet warnings/errors for gcc 4.2, OS X 10.5
* Modify pprof so it works without nm: useful for windows (csilvers)
* pprof: Support filtering for CPU profiles (cgd)
* Bugfix: have realloc report to hooks in all situations (maxim)
* Speed improvement: replace slow memcpy with std::copy (soren)
* Speed: better iterator efficiency in RecordRegionRemoval (soren)
* Speed: minor speed improvements via better bitfield alignment (gpike)
* Documentation: add documentation of binary profile output (cgd)
git-svn-id: http://gperftools.googlecode.com/svn/trunk@40 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/addressmap_unittest.cc | 1 | ||||
-rw-r--r-- | src/tests/heap-checker_unittest.cc | 1 | ||||
-rw-r--r-- | src/tests/low_level_alloc_unittest.cc | 7 | ||||
-rwxr-xr-x | src/tests/maybe_threads_unittest.sh | 4 | ||||
-rw-r--r-- | src/tests/profiledata_unittest.cc | 320 | ||||
-rw-r--r-- | src/tests/system-alloc_unittest.cc | 4 | ||||
-rw-r--r-- | src/tests/tcmalloc_unittest.cc | 2 | ||||
-rw-r--r-- | src/tests/testutil.cc | 72 |
8 files changed, 400 insertions, 11 deletions
diff --git a/src/tests/addressmap_unittest.cc b/src/tests/addressmap_unittest.cc index 2cf125d..50d9b94 100644 --- a/src/tests/addressmap_unittest.cc +++ b/src/tests/addressmap_unittest.cc @@ -33,6 +33,7 @@ #include <vector> #include <set> #include <algorithm> +#include <utility> #include "addressmap-inl.h" #include "base/logging.h" #include "base/commandlineflags.h" diff --git a/src/tests/heap-checker_unittest.cc b/src/tests/heap-checker_unittest.cc index 594323a..5b64b8b 100644 --- a/src/tests/heap-checker_unittest.cc +++ b/src/tests/heap-checker_unittest.cc @@ -95,6 +95,7 @@ #include <iomanip> // for hex #include <set> #include <map> +#include <memory> #include <vector> #include <string> diff --git a/src/tests/low_level_alloc_unittest.cc b/src/tests/low_level_alloc_unittest.cc index 1e6f0a5..c1de5ae 100644 --- a/src/tests/low_level_alloc_unittest.cc +++ b/src/tests/low_level_alloc_unittest.cc @@ -167,6 +167,13 @@ static void FreeHook(const void *p) { } 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; + } + old_alloc_hook = MallocHook::SetNewHook(AllocHook); old_free_hook = MallocHook::SetDeleteHook(FreeHook); CHECK_EQ(allocates, 0); diff --git a/src/tests/maybe_threads_unittest.sh b/src/tests/maybe_threads_unittest.sh index f319ea1..1c1a1d0 100755 --- a/src/tests/maybe_threads_unittest.sh +++ b/src/tests/maybe_threads_unittest.sh @@ -57,12 +57,12 @@ fi UNITTEST_DIR=${1:-$BINDIR} # Figure out the "real" unittest directory. Also holds the .so files. -UNITTEST_DIR=`$UNITTEST_DIR/profiler1_unittest 2>&1 \ +UNITTEST_DIR=`$UNITTEST_DIR/low_level_alloc_unittest --help 2>&1 \ | awk '{print $2; exit;}' \ | xargs dirname` # We need to set the library-path too: libtcmalloc depends on libstacktrace # (Note we try several different names: OS X uses its own libpath varname). LD_LIBRARY_PATH="$UNITTEST_DIR" DYLD_LIBRARY_PATH="$UNITTEST_DIR" \ -LD_PRELOAD="$UNITTEST_DIR/libtcmalloc.so" \ +LD_PRELOAD="$UNITTEST_DIR/libtcmalloc_minimal.so" \ $UNITTEST_DIR/low_level_alloc_unittest diff --git a/src/tests/profiledata_unittest.cc b/src/tests/profiledata_unittest.cc new file mode 100644 index 0000000..a49257d --- /dev/null +++ b/src/tests/profiledata_unittest.cc @@ -0,0 +1,320 @@ +// 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 EXPECT_TRUE(cond) CHECK(cond) +#define EXPECT_FALSE(cond) CHECK(!(cond)) +#define EXPECT_EQ(a, b) CHECK_EQ(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 { + +// 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); } + +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_; } + + void Check(const ProfileDataSlot* slots, int num_slots) { + size_t num_bytes = num_slots * sizeof slots[0]; + char* filedata = new char[num_bytes]; + + int fd = open(filename_.c_str(), O_RDONLY); + EXPECT_GE(fd, 0); + ssize_t numread = read(fd, filedata, num_bytes); + EXPECT_EQ(numread, num_bytes); + EXPECT_EQ(0, memcmp(slots, filedata, num_bytes)); + close(fd); + + delete[] filedata; + } + + private: + string filename_; +}; + +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 StartWhenStarted(); + void StartStopEmpty2(); + void CollectOne(); + void CollectTwoMatching(); + void CollectTwoFlush(); + + 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); + 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(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +// 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 + }; + + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + + ProfileData::State state_before; + collector_.GetCurrentState(&state_before); + + CHECK(!collector_.Start("foobar", frequency * 2)); + + ProfileData::State state_after; + collector_.GetCurrentState(&state_after); + ExpectSameState(state_before, state_after); + + collector_.Stop(); + ExpectStopped(); + 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(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + collector_.Stop(); + ExpectStopped(); + 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(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + 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(); + 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(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + 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(); + 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(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + 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(); + 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; +} diff --git a/src/tests/system-alloc_unittest.cc b/src/tests/system-alloc_unittest.cc index 7045abe..a160a34 100644 --- a/src/tests/system-alloc_unittest.cc +++ b/src/tests/system-alloc_unittest.cc @@ -72,6 +72,10 @@ public: ptr_ += size; return reinterpret_cast<void *>(ptr); } + + void DumpStats(TCMalloc_Printer* printer) { + } + private: static const int kArraySize = 8 * 1024 * 1024; char array_[kArraySize]; diff --git a/src/tests/tcmalloc_unittest.cc b/src/tests/tcmalloc_unittest.cc index c4c18cd..e818a21 100644 --- a/src/tests/tcmalloc_unittest.cc +++ b/src/tests/tcmalloc_unittest.cc @@ -332,7 +332,7 @@ class TesterThread { case UPDATE: UpdateObject(); break; case PASS: PassObject(); break; case -1: goto done; - default: assert("" == "Unknown type"); + default: assert(NULL == "Unknown type"); } ShrinkHeap(); diff --git a/src/tests/testutil.cc b/src/tests/testutil.cc index cb523de..9ab4c3c 100644 --- a/src/tests/testutil.cc +++ b/src/tests/testutil.cc @@ -36,7 +36,12 @@ #include <stdlib.h> // for NULL, abort() #include "tests/testutil.h" -#if defined(NO_THREADS) || !defined(HAVE_PTHREADS) +struct FunctionAndId { + void (*ptr_to_function)(int); + int id; +}; + +#if defined(NO_THREADS) || !(defined(HAVE_PTHREADS) || defined(WIN32)) extern "C" void RunThread(void (*fn)()) { (*fn)(); @@ -53,19 +58,70 @@ extern "C" void RunManyThreadsWithId(void (*fn)(int), int count, int) { (*fn)(i); // stacksize doesn't make sense in a non-threaded context } -#else // NO_THREADS || !HAVE_PTHREAD +#elif defined(WIN32) + +#define WIN32_LEAN_AND_MEAN /* We always want minimal includes */ +#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" { - struct FunctionAndId { - void (*ptr_to_function)(int); - int id; - }; - -// This helper function has the signature that pthread_create wants. + // 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; |