summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
authorcsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2007-11-29 23:39:24 +0000
committercsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2007-11-29 23:39:24 +0000
commit11b02f7aebd05cf39f6f93bdd48786909f99f34e (patch)
tree660c613e74cffd643ee4a7d0f0cb170057abbf7d /src/tests
parent49b74b9508797f8aafe6b86e62e7efc4ec200e48 (diff)
downloadgperftools-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.cc1
-rw-r--r--src/tests/heap-checker_unittest.cc1
-rw-r--r--src/tests/low_level_alloc_unittest.cc7
-rwxr-xr-xsrc/tests/maybe_threads_unittest.sh4
-rw-r--r--src/tests/profiledata_unittest.cc320
-rw-r--r--src/tests/system-alloc_unittest.cc4
-rw-r--r--src/tests/tcmalloc_unittest.cc2
-rw-r--r--src/tests/testutil.cc72
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;