summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2009-12-02 18:15:13 +0000
committercsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2009-12-02 18:15:13 +0000
commita94d5f797412bac7b811bf8d69d9298fec54f5cc (patch)
tree450a1654e76492ca99c444a083b19f8fe9146dff
parent5b80f01df1137337131b4c50ce97faaff9973e90 (diff)
downloadgperftools-a94d5f797412bac7b811bf8d69d9298fec54f5cc.tar.gz
* Prefer __environ to /proc/self/environ (csilvers)
* Add HEAP_CHECK_MAX_LEAKS envvar (glider) * BUGFIX: debugallocation now calls cpp_alloc for new (willchan) * BUGFIX: tc_set_new_mode() respected for realloc and calloc (willchan) * BUGFIX: fix opt-mode maybe-crash on debugallocation_test (csilvers) * Print alloc size when mmap fails (hakon) * Add ITIMER_REAL support (csilvers, nabeelmian) * BUGFIX: correctly report double-frees (csilvers) * Export tc_set_new_mode() from the .h file (willchan) * Restructure Symbolize to make it more efficient (glider) * PORTING: Augment sysinfo to work on 64-bit OS X (csilvers) * Add two numeric pageheap properties to MallocExtension (fikes) * PORTING: Use libunwind for i386 when using --omitfp (ppluzhnikov) * Add ReleaseToSystem(num_bytes) (kash) * Provide correct library filenames under solaris (jeffrey) * BUGFIX: simple fix in pprof --raw mode (mrabkin) * PORTING: Prefer sys/ucontext.h to fix OS 10.6 builds (csilvers) * Improve support for inlined functions in pprof (sanjay) * Update wget code to not use keepalive (mrabkin, csilvers) * PORTING: correctly handle x86_64 machines that use fp's (csilvers) git-svn-id: http://gperftools.googlecode.com/svn/trunk@79 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rwxr-xr-xconfigure29
-rw-r--r--configure.ac9
-rw-r--r--doc/cpuprofile.html12
-rw-r--r--doc/heap_checker.html10
-rw-r--r--m4/compiler_characteristics.m42
-rw-r--r--src/base/logging.h11
-rw-r--r--src/base/sysinfo.cc30
-rw-r--r--src/config.h.in3
-rw-r--r--src/debugallocation.cc53
-rw-r--r--src/google/malloc_extension.h3
-rw-r--r--src/google/malloc_extension_c.h2
-rw-r--r--src/google/tcmalloc.h.in1
-rw-r--r--src/heap-profile-table.cc24
-rw-r--r--src/malloc_extension.cc6
-rw-r--r--src/memfs_malloc.cc4
-rwxr-xr-xsrc/pprof1
-rw-r--r--src/profile-handler.cc30
-rw-r--r--src/symbolize.cc78
-rw-r--r--src/symbolize.h44
-rw-r--r--src/tcmalloc.cc29
-rw-r--r--src/tests/atomicops_unittest.cc142
-rw-r--r--src/tests/debugallocation_test.cc47
-rw-r--r--src/tests/malloc_extension_test.cc30
-rw-r--r--src/tests/profile-handler_unittest.cc121
-rwxr-xr-xsrc/tests/profiler_unittest.sh10
-rw-r--r--src/tests/tcmalloc_unittest.cc44
-rw-r--r--src/windows/config.h3
-rw-r--r--src/windows/google/tcmalloc.h1
28 files changed, 533 insertions, 246 deletions
diff --git a/configure b/configure
index 7d62793..39b1837 100755
--- a/configure
+++ b/configure
@@ -20592,7 +20592,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_formatting_prius_prefix" >&5
$as_echo "$ac_cv_formatting_prius_prefix" >&6; }
- if test -z "$ac_cv_formatting_prius_defined"; then
+ if test -z "$ac_cv_prius_defined"; then
ac_cv_formatting_prius_prefix=z;
fi
@@ -20643,6 +20643,33 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
+# Check if __environ is available (for GetenvBeforeMain)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __environ" >&5
+$as_echo_n "checking for __environ... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <unistd.h>
+int
+main ()
+{
+char **env = __environ
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE___ENVIRON 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
# If we support __thread, that can speed up tcmalloc a bit.
# Note, however, that our code tickles a bug in gcc < 4.1.2
# involving TLS and -fPIC (which our libraries will use) on x86:
diff --git a/configure.ac b/configure.ac
index 4d2a271..0c8a232 100644
--- a/configure.ac
+++ b/configure.ac
@@ -236,6 +236,15 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM(, [void *sp = __builtin_stack_pointer()])],
AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])])
+# Check if __environ is available (for GetenvBeforeMain)
+AC_MSG_CHECKING([for __environ])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <unistd.h>],
+ [char **env = __environ])],
+ [AC_DEFINE(HAVE___ENVIRON, 1,
+ [Define to 1 if compiler supports __environ])
+ AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])])
+
# If we support __thread, that can speed up tcmalloc a bit.
# Note, however, that our code tickles a bug in gcc < 4.1.2
# involving TLS and -fPIC (which our libraries will use) on x86:
diff --git a/doc/cpuprofile.html b/doc/cpuprofile.html
index 4f71c02..3d2b4cc 100644
--- a/doc/cpuprofile.html
+++ b/doc/cpuprofile.html
@@ -86,6 +86,18 @@ environment variables.</p>
</td>
</tr>
+<tr valign=top>
+ <td><code>CPUPROFILE_REALTIME=1</code></td>
+ <td>default: [not set]</td>
+ <td>
+ If set to any value (including 0 or the empty string), use
+ ITIMER_REAL instead of ITIMER_PROF to gather profiles. In
+ general, ITIMER_REAL is not as accurate as ITIMER_PROF, and also
+ interacts badly with use of alarm(), so prefer ITIMER_PROF unless
+ you have a reason prefer ITIMER_REAL.
+ </td>
+</tr>
+
</table>
diff --git a/doc/heap_checker.html b/doc/heap_checker.html
index 491ec3d..87e497a 100644
--- a/doc/heap_checker.html
+++ b/doc/heap_checker.html
@@ -316,6 +316,16 @@ checking.</p>
</td>
</tr>
+<tr valign=top>
+ <td><code>HEAP_CHECK_MAX_LEAKS</code></td>
+ <td>Default: 20</td>
+ <td>
+ The maximum number of leaks to be reported. If negative or zero, print all
+ the leaks found.
+ </td>
+</tr>
+
+
</table>
<p>These options apply to all types of leak checking.</p>
diff --git a/m4/compiler_characteristics.m4 b/m4/compiler_characteristics.m4
index f4d29c9..2b62893 100644
--- a/m4/compiler_characteristics.m4
+++ b/m4/compiler_characteristics.m4
@@ -15,7 +15,7 @@ AC_DEFUN([AC_COMPILER_CHARACTERISTICS],
AC_TYPES_COMPATIBLE(unsigned long long, size_t,
ac_cv_formatting_prius_prefix=ll; ac_cv_prius_defined=1
)])
- if test -z "$ac_cv_formatting_prius_defined"; then
+ if test -z "$ac_cv_prius_defined"; then
ac_cv_formatting_prius_prefix=z;
fi
AC_DEFINE_UNQUOTED(PRIuS, "${ac_cv_formatting_prius_prefix}u", AC_PRIUS_COMMENT)
diff --git a/src/base/logging.h b/src/base/logging.h
index 3b1dacf..6aa5c3f 100644
--- a/src/base/logging.h
+++ b/src/base/logging.h
@@ -136,17 +136,26 @@ enum { DEBUG_MODE = 1 };
#define CHECK_GE(val1, val2) CHECK_OP(>=, val1, val2)
#define CHECK_GT(val1, val2) CHECK_OP(> , val1, val2)
-// A synonym for CHECK_* that is used in some unittests.
+// Synonyms for CHECK_* that are used in some unittests.
#define EXPECT_EQ(val1, val2) CHECK_EQ(val1, val2)
#define EXPECT_NE(val1, val2) CHECK_NE(val1, val2)
#define EXPECT_LE(val1, val2) CHECK_LE(val1, val2)
#define EXPECT_LT(val1, val2) CHECK_LT(val1, val2)
#define EXPECT_GE(val1, val2) CHECK_GE(val1, val2)
#define EXPECT_GT(val1, val2) CHECK_GT(val1, val2)
+#define ASSERT_EQ(val1, val2) EXPECT_EQ(val1, val2)
+#define ASSERT_NE(val1, val2) EXPECT_NE(val1, val2)
+#define ASSERT_LE(val1, val2) EXPECT_LE(val1, val2)
+#define ASSERT_LT(val1, val2) EXPECT_LT(val1, val2)
+#define ASSERT_GE(val1, val2) EXPECT_GE(val1, val2)
+#define ASSERT_GT(val1, val2) EXPECT_GT(val1, val2)
// As are these variants.
#define EXPECT_TRUE(cond) CHECK(cond)
#define EXPECT_FALSE(cond) CHECK(!(cond))
#define EXPECT_STREQ(a, b) CHECK(strcmp(a, b) == 0)
+#define ASSERT_TRUE(cond) EXPECT_TRUE(cond)
+#define ASSERT_FALSE(cond) EXPECT_FALSE(cond)
+#define ASSERT_STREQ(a, b) EXPECT_STREQ(a, b)
// Used for (libc) functions that return -1 and set errno
#define CHECK_ERR(invocation) PCHECK((invocation) != -1)
diff --git a/src/base/sysinfo.cc b/src/base/sysinfo.cc
index 3919ba4..7af0495 100644
--- a/src/base/sysinfo.cc
+++ b/src/base/sysinfo.cc
@@ -100,11 +100,33 @@
// Some non-trivial getenv-related functions.
// ----------------------------------------------------------------------
+// It's not safe to call getenv() in the malloc hooks, because they
+// might be called extremely early, before libc is done setting up
+// correctly. In particular, the thread library may not be done
+// setting up errno. So instead, we use the built-in __environ array
+// if it exists, and otherwise read /proc/self/environ directly, using
+// system calls to read the file, and thus avoid setting errno.
+// /proc/self/environ has a limit of how much data it exports (around
+// 8K), so it's not an ideal solution.
const char* GetenvBeforeMain(const char* name) {
+#if defined(HAVE___ENVIRON) // if we have it, it's declared in unistd.h
+ const int namelen = strlen(name);
+ for (char** p = __environ; *p; p++) {
+ if (!memcmp(*p, name, namelen) && (*p)[namelen] == '=') // it's a match
+ return *p + namelen+1; // point after =
+ }
+ return NULL;
+#elif defined(PLATFORM_WINDOWS)
+ // TODO(mbelshe) - repeated calls to this function will overwrite the
+ // contents of the static buffer.
+ static char envbuf[1024]; // enough to hold any envvar we care about
+ if (!GetEnvironmentVariableA(name, envbuf, sizeof(envbuf)-1))
+ return NULL;
+ return envbuf;
+#else
// static is ok because this function should only be called before
// main(), when we're single-threaded.
static char envbuf[16<<10];
-#ifndef PLATFORM_WINDOWS
if (*envbuf == '\0') { // haven't read the environ yet
int fd = safeopen("/proc/self/environ", O_RDONLY);
// The -2 below guarantees the last two bytes of the buffer will be \0\0
@@ -129,12 +151,6 @@ const char* GetenvBeforeMain(const char* name) {
p = endp + 1;
}
return NULL; // env var never found
-#else
- // TODO(mbelshe) - repeated calls to this function will overwrite the
- // contents of the static buffer.
- if (!GetEnvironmentVariableA(name, envbuf, sizeof(envbuf)-1))
- return NULL;
- return envbuf;
#endif
}
diff --git a/src/config.h.in b/src/config.h.in
index 1b1a3d9..1ad2642 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -153,6 +153,9 @@
/* define if your compiler has __attribute__ */
#undef HAVE___ATTRIBUTE__
+/* Define to 1 if compiler supports __environ */
+#undef HAVE___ENVIRON
+
/* Define to 1 if the system has the type `__int64'. */
#undef HAVE___INT64
diff --git a/src/debugallocation.cc b/src/debugallocation.cc
index 47fef16..1a9ddcb 100644
--- a/src/debugallocation.cc
+++ b/src/debugallocation.cc
@@ -152,18 +152,20 @@ extern "C" {
// The do_* functions are defined in tcmalloc.cc,
// which is included before this file
// when TCMALLOC_FOR_DEBUGALLOCATION is defined.
-#define BASE_MALLOC do_malloc
-#define BASE_FREE do_free
-#define BASE_MALLOPT do_mallopt
-#define BASE_MALLINFO do_mallinfo
+#define BASE_MALLOC_NEW(size) cpp_alloc(size, false)
+#define BASE_MALLOC do_malloc_or_cpp_alloc
+#define BASE_FREE do_free
+#define BASE_MALLOPT do_mallopt
+#define BASE_MALLINFO do_mallinfo
#else
// We are working on top of standard libc's malloc library
-#define BASE_MALLOC __libc_malloc
-#define BASE_FREE __libc_free
-#define BASE_MALLOPT __libc_mallopt
-#define BASE_MALLINFO __libc_mallinfo
+#define BASE_MALLOC_NEW __libc_malloc
+#define BASE_MALLOC __libc_malloc
+#define BASE_FREE __libc_free
+#define BASE_MALLOPT __libc_mallopt
+#define BASE_MALLINFO __libc_mallinfo
#endif
@@ -524,10 +526,14 @@ class MallocBlock {
}
b = (MallocBlock*) (p + (num_pages - 1) * pagesize - sz);
} else {
- b = (MallocBlock*) BASE_MALLOC(real_malloced_size(size));
+ b = (MallocBlock*) (type == kMallocType ?
+ BASE_MALLOC(real_malloced_size(size)) :
+ BASE_MALLOC_NEW(real_malloced_size(size)));
}
#else
- b = (MallocBlock*) BASE_MALLOC(real_malloced_size(size));
+ b = (MallocBlock*) (type == kMallocType ?
+ BASE_MALLOC(real_malloced_size(size)) :
+ BASE_MALLOC_NEW(real_malloced_size(size)));
#endif
// It would be nice to output a diagnostic on allocation failure
@@ -656,25 +662,24 @@ class MallocBlock {
reinterpret_cast<void*>(
PRINTABLE_PTHREAD(queue_entry.deleter_threadid)));
- SymbolMap symbolization_table;
+ SymbolTable symbolization_table;
const int num_symbols = queue_entry.num_deleter_pcs; // short alias name
for (int i = 0; i < num_symbols; i++) {
// Symbolizes the previous address of pc because pc may be in the
// next function. This may happen when the function ends with
// a call to a function annotated noreturn (e.g. CHECK).
- uintptr_t pc =
- reinterpret_cast<uintptr_t>(queue_entry.deleter_pcs[i]) - 1;
- symbolization_table[pc] = "";
+ char* pc =
+ reinterpret_cast<char*>(queue_entry.deleter_pcs[i]) - 1;
+ symbolization_table.Add(pc);
}
- int sym_buffer_len = kSymbolSize * num_symbols;
- char *sym_buffer = new char[sym_buffer_len];
if (FLAGS_symbolize_stacktrace)
- Symbolize(sym_buffer, sym_buffer_len, &symbolization_table);
+ symbolization_table.Symbolize();
for (int i = 0; i < num_symbols; i++) {
- uintptr_t pc =
- reinterpret_cast<uintptr_t>(queue_entry.deleter_pcs[i]) - 1;
- TracePrintf(STDERR_FILENO, " @ %p %s\n",
- reinterpret_cast<void*>(pc), symbolization_table[pc]);
+ char *pc =
+ reinterpret_cast<char*>(queue_entry.deleter_pcs[i]) - 1;
+ TracePrintf(STDERR_FILENO, " @ %"PRIxPTR" %s\n",
+ reinterpret_cast<uintptr_t>(pc),
+ symbolization_table.GetSymbol(pc));
}
} else {
RAW_LOG(ERROR,
@@ -696,6 +701,12 @@ class MallocBlock {
// Find the header just before client's memory.
MallocBlock *mb = reinterpret_cast<MallocBlock *>(
reinterpret_cast<char *>(p) - data_offset);
+ // If mb->alloc_type_ is kMagicDeletedInt, we're not an ok pointer.
+ if (mb->alloc_type_ == kMagicDeletedInt) {
+ RAW_LOG(FATAL, "memory allocation bug: object at %p has been already"
+ " deallocated; or else a word before the object has been"
+ " corrupted (memory stomping bug)", p);
+ }
// If mb->offset_ is zero (common case), mb is the real header. If
// mb->offset_ is non-zero, this block was allocated by memalign, and
// mb->offset_ is the distance backwards to the real header from mb,
diff --git a/src/google/malloc_extension.h b/src/google/malloc_extension.h
index 0342843..fc272c9 100644
--- a/src/google/malloc_extension.h
+++ b/src/google/malloc_extension.h
@@ -198,8 +198,7 @@ class PERFTOOLS_DLL_DECL MallocExtension {
// system for reuse. Use this extension with caution -- to get this
// memory back may require faulting pages back in by the OS, and
// that may be slow. (Currently only implemented in tcmalloc.)
- // A negative values for num_bytes results in a noop.
- virtual void ReleaseToSystem(ssize_t num_bytes);
+ virtual void ReleaseToSystem(size_t num_bytes);
// Same as ReleaseToSystem() but release as much memory as possible.
virtual void ReleaseFreeMemory();
diff --git a/src/google/malloc_extension_c.h b/src/google/malloc_extension_c.h
index 95f7f4c..fcaa8cd 100644
--- a/src/google/malloc_extension_c.h
+++ b/src/google/malloc_extension_c.h
@@ -75,7 +75,7 @@ PERFTOOLS_DLL_DECL int MallocExtension_GetNumericProperty(const char* property,
PERFTOOLS_DLL_DECL int MallocExtension_SetNumericProperty(const char* property, size_t value);
PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadIdle(void);
PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadBusy(void);
-PERFTOOLS_DLL_DECL void MallocExtension_ReleaseToSystem(ssize_t num_bytes);
+PERFTOOLS_DLL_DECL void MallocExtension_ReleaseToSystem(size_t num_bytes);
PERFTOOLS_DLL_DECL void MallocExtension_ReleaseFreeMemory(void);
PERFTOOLS_DLL_DECL size_t MallocExtension_GetEstimatedAllocatedSize(size_t size);
PERFTOOLS_DLL_DECL size_t MallocExtension_GetAllocatedSize(void* p);
diff --git a/src/google/tcmalloc.h.in b/src/google/tcmalloc.h.in
index cf62c70..e5c873d 100644
--- a/src/google/tcmalloc.h.in
+++ b/src/google/tcmalloc.h.in
@@ -89,6 +89,7 @@ extern "C" {
#endif
#ifdef __cplusplus
+ PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW;
PERFTOOLS_DLL_DECL void* tc_new(size_t size);
PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
diff --git a/src/heap-profile-table.cc b/src/heap-profile-table.cc
index ce65c47..66e4f20 100644
--- a/src/heap-profile-table.cc
+++ b/src/heap-profile-table.cc
@@ -82,6 +82,10 @@ DEFINE_bool(cleanup_old_heap_profiles,
EnvToBool("HEAP_PROFILE_CLEANUP", true),
"At initialization time, delete old heap profiles.");
+DEFINE_int32(heap_check_max_leaks,
+ EnvToInt("HEAP_CHECK_MAX_LEAKS", 20),
+ "The maximum number of leak reports to print.");
+
//----------------------------------------------------------------------
// header of the dumped heap profile
@@ -539,26 +543,23 @@ void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name,
// Report a bounded number of leaks to keep the leak report from
// growing too long.
- const int to_report = (n > 20) ? 20 : n;
+ const int to_report =
+ (FLAGS_heap_check_max_leaks > 0 &&
+ n > FLAGS_heap_check_max_leaks) ? FLAGS_heap_check_max_leaks : n;
RAW_LOG(ERROR, "The %d largest leaks:", to_report);
// Print
- SymbolMap symbolization_table;
- int num_symbols = 0;
+ SymbolTable symbolization_table;
for (int i = 0; i < to_report; i++) {
const Entry& e = entries[i];
for (int j = 0; j < e.bucket->depth; j++) {
- const void* pc = e.bucket->stack[j];
- symbolization_table[reinterpret_cast<uintptr_t>(pc)] = "";
- num_symbols++;
+ symbolization_table.Add(e.bucket->stack[j]);
}
}
static const int kBufSize = 2<<10;
char buffer[kBufSize];
- int sym_buffer_len = kSymbolSize * num_symbols;
- char *sym_buffer = new char[sym_buffer_len];
if (should_symbolize)
- Symbolize(sym_buffer, sym_buffer_len, &symbolization_table);
+ symbolization_table.Symbolize();
for (int i = 0; i < to_report; i++) {
const Entry& e = entries[i];
base::RawPrinter printer(buffer, kBufSize);
@@ -566,12 +567,11 @@ void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name,
e.bytes, e.count);
for (int j = 0; j < e.bucket->depth; j++) {
const void* pc = e.bucket->stack[j];
- printer.Printf("\t@ %p %s\n",
- pc, symbolization_table[reinterpret_cast<uintptr_t>(pc)]);
+ printer.Printf("\t@ %"PRIxPTR" %s\n",
+ reinterpret_cast<uintptr_t>(pc), symbolization_table.GetSymbol(pc));
}
RAW_LOG(ERROR, "%s", buffer);
}
- delete[] sym_buffer;
if (to_report < n) {
RAW_LOG(ERROR, "Skipping leaks numbered %d..%d",
diff --git a/src/malloc_extension.cc b/src/malloc_extension.cc
index 95fd1c1..4ce262f 100644
--- a/src/malloc_extension.cc
+++ b/src/malloc_extension.cc
@@ -143,12 +143,12 @@ void MallocExtension::MarkThreadBusy() {
// Default implementation does nothing
}
-void MallocExtension::ReleaseToSystem(ssize_t num_bytes) {
+void MallocExtension::ReleaseToSystem(size_t num_bytes) {
// Default implementation does nothing
}
void MallocExtension::ReleaseFreeMemory() {
- ReleaseToSystem(LONG_MAX);
+ ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX
}
void MallocExtension::SetMemoryReleaseRate(double rate) {
@@ -333,6 +333,6 @@ C_SHIM(SetNumericProperty, int,
C_SHIM(MarkThreadIdle, void, (void), ());
C_SHIM(MarkThreadBusy, void, (void), ());
C_SHIM(ReleaseFreeMemory, void, (void), ());
-C_SHIM(ReleaseToSystem, void, (ssize_t num_bytes), (num_bytes));
+C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes));
C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size));
C_SHIM(GetAllocatedSize, size_t, (void* p), (p));
diff --git a/src/memfs_malloc.cc b/src/memfs_malloc.cc
index bef2d3c..fbef9c8 100644
--- a/src/memfs_malloc.cc
+++ b/src/memfs_malloc.cc
@@ -161,8 +161,8 @@ void* HugetlbSysAllocator::Alloc(size_t size, size_t *actual_size,
MAP_SHARED, hugetlb_fd_, hugetlb_base_);
if (result == reinterpret_cast<void*>(MAP_FAILED)) {
if (!FLAGS_memfs_malloc_ignore_mmap_fail) {
- TCMalloc_MESSAGE(__FILE__, __LINE__, "mmap failed: %s\n",
- strerror(errno));
+ TCMalloc_MESSAGE(__FILE__, __LINE__, "mmap of size %"PRIuS" failed: %s\n",
+ size + extra, strerror(errno));
failed_ = true;
if (FLAGS_memfs_malloc_abort_on_fail) {
CRASH("memfs_malloc_abort_on_fail is set\n");
diff --git a/src/pprof b/src/pprof
index 62cce12..93ffa98 100755
--- a/src/pprof
+++ b/src/pprof
@@ -1968,6 +1968,7 @@ sub RemoveUninterestingFrames {
'tc_newarray_nothrow',
'do_malloc',
'::do_malloc', # new name -- got moved to an unnamed ns
+ '::do_malloc_or_cpp_alloc',
'DoSampledAllocation',
'simple_alloc::allocate',
'__malloc_alloc_template::allocate',
diff --git a/src/profile-handler.cc b/src/profile-handler.cc
index e658d30..b68dbc2 100644
--- a/src/profile-handler.cc
+++ b/src/profile-handler.cc
@@ -89,18 +89,18 @@ class ProfileHandler {
// Registers a callback routine to receive profile timer ticks. The returned
// token is to be used when unregistering this callback and must not be
// deleted by the caller. Registration of the first callback enables the
- // SIGPROF handler.
+ // SIGPROF handler (or SIGALRM if using ITIMER_REAL).
ProfileHandlerToken* RegisterCallback(ProfileHandlerCallback callback,
void* callback_arg);
// Unregisters a previously registered callback. Expects the token returned
// by the corresponding RegisterCallback routine. Unregistering the last
- // callback disables the SIGPROF handler.
+ // callback disables the SIGPROF handler (or SIGALRM if using ITIMER_REAL).
void UnregisterCallback(ProfileHandlerToken* token)
NO_THREAD_SAFETY_ANALYSIS;
// Unregisters all the callbacks, stops the timer if shared, disables the
- // SIGPROF handler and clears the timer_sharing_ state.
+ // SIGPROF (or SIGALRM) handler and clears the timer_sharing_ state.
void Reset();
// Gets the current state of profile handler.
@@ -127,12 +127,15 @@ class ProfileHandler {
// Initializes the ProfileHandler singleton via GoogleOnceInit.
static void Init();
- // Counts the number of SIGPROF interrupts received.
+ // The number of SIGPROF (or SIGALRM for ITIMER_REAL) interrupts received.
int64 interrupts_ GUARDED_BY(signal_lock_);
- // SIGPROF interrupt frequency, read-only after construction.
+ // SIGPROF/SIGALRM interrupt frequency, read-only after construction.
int32 frequency_;
+ // ITIMER_PROF (which uses SIGPROF), or ITIMER_REAL (which uses SIGALRM)
+ int timer_type_;
+
// Counts the number of callbacks registered.
int32 callback_count_ GUARDED_BY(control_lock_);
@@ -196,7 +199,7 @@ class ProfileHandler {
// Disables (ignores) the timer interrupt signal.
void DisableHandler() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
- // SIGPROF handler. Iterate over and call all the registered callbacks.
+ // SIGPROF/SIGALRM handler. Iterate over and call all the registered callbacks.
static void SignalHandler(int sig, siginfo_t* sinfo, void* ucontext);
DISALLOW_EVIL_CONSTRUCTORS(ProfileHandler);
@@ -241,6 +244,9 @@ ProfileHandler::ProfileHandler()
callback_count_(0),
timer_sharing_(TIMERS_UNTOUCHED) {
SpinLockHolder cl(&control_lock_);
+
+ timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
+
// Get frequency of interrupts (if specified)
char junk;
const char* fr = getenv("CPUPROFILE_FREQUENCY");
@@ -390,18 +396,18 @@ void ProfileHandler::StartTimer() {
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 1000000 / frequency_;
timer.it_value = timer.it_interval;
- setitimer(ITIMER_PROF, &timer, 0);
+ setitimer(timer_type_, &timer, 0);
}
void ProfileHandler::StopTimer() {
struct itimerval timer;
memset(&timer, 0, sizeof timer);
- setitimer(ITIMER_PROF, &timer, 0);
+ setitimer(timer_type_, &timer, 0);
}
bool ProfileHandler::IsTimerRunning() {
struct itimerval current_timer;
- RAW_CHECK(0 == getitimer(ITIMER_PROF, &current_timer), "getitimer");
+ RAW_CHECK(0 == getitimer(timer_type_, &current_timer), "getitimer");
return (current_timer.it_value.tv_sec != 0 ||
current_timer.it_value.tv_usec != 0);
}
@@ -411,7 +417,8 @@ void ProfileHandler::EnableHandler() {
sa.sa_sigaction = SignalHandler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
- RAW_CHECK(sigaction(SIGPROF, &sa, NULL) == 0, "sigprof (enable)");
+ const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
+ RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (enable)");
}
void ProfileHandler::DisableHandler() {
@@ -419,7 +426,8 @@ void ProfileHandler::DisableHandler() {
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
- RAW_CHECK(sigaction(SIGPROF, &sa, NULL) == 0, "sigprof (disable)");
+ const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
+ RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (disable)");
}
void ProfileHandler::SignalHandler(int sig, siginfo_t* sinfo, void* ucontext) {
diff --git a/src/symbolize.cc b/src/symbolize.cc
index 6fe44b9..9dd890e 100644
--- a/src/symbolize.cc
+++ b/src/symbolize.cc
@@ -65,30 +65,37 @@ DEFINE_string(symbolize_pprof,
// a more-permanent copy that won't ever get destroyed.
static string* g_pprof_path = new string(FLAGS_symbolize_pprof);
+void SymbolTable::Add(const void* addr) {
+ symbolization_table_[addr] = "";
+}
+
+const char* SymbolTable::GetSymbol(const void* addr) {
+ return symbolization_table_[addr];
+}
+
// Updates symbolization_table with the pointers to symbol names corresponding
// to its keys. The symbol names are stored in out, which is allocated and
// freed by the caller of this routine.
// Note that the forking/etc is not thread-safe or re-entrant. That's
// ok for the purpose we need -- reporting leaks detected by heap-checker
// -- but be careful if you decide to use this routine for other purposes.
-extern bool Symbolize(char *out, int out_size,
- SymbolMap *symbolization_table) {
+int SymbolTable::Symbolize() {
#if !defined(HAVE_UNISTD_H) || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS_WAIT_H)
- return false;
+ return 0;
#elif !defined(HAVE_PROGRAM_INVOCATION_NAME)
- return false; // TODO(csilvers): get argv[0] somehow
+ return 0; // TODO(csilvers): get argv[0] somehow
#else
// All this work is to do two-way communication. ugh.
extern char* program_invocation_name; // gcc provides this
int child_in[2]; // file descriptors
int child_out[2]; // for now, we don't worry about child_err
if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_in) == -1) {
- return false;
+ return 0;
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_out) == -1) {
close(child_in[0]);
close(child_in[1]);
- return false;
+ return 0;
}
switch (fork()) {
case -1: { // error
@@ -96,7 +103,7 @@ extern bool Symbolize(char *out, int out_size,
close(child_in[1]);
close(child_out[0]);
close(child_out[1]);
- return false;
+ return 0;
}
case 0: { // child
close(child_in[1]); // child uses the 0's, parent uses the 1's
@@ -125,30 +132,36 @@ extern bool Symbolize(char *out, int out_size,
struct pollfd pfd = { child_in[1], POLLOUT, 0 };
if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) ||
(pfd.revents & (POLLHUP|POLLERR))) {
- return false;
+ return 0;
}
#endif
DumpProcSelfMaps(child_in[1]); // what pprof expects on stdin
- char pcstr[64]; // enough for a single address
- for (SymbolMap::const_iterator iter = symbolization_table->begin();
- iter != symbolization_table->end(); ++iter) {
- snprintf(pcstr, sizeof(pcstr), // pprof expects format to be 0xXXXXXX
- "0x%" PRIxPTR "\n", iter->first);
- // TODO(glider): the number of write()s can be reduced by using
- // snprintf() here.
- write(child_in[1], pcstr, strlen(pcstr));
+ // Allocate 24 bytes = ("0x" + 8 bytes + "\n" + overhead) for each
+ // address to feed to pprof.
+ const int kOutBufSize = 24 * symbolization_table_.size();
+ char *pprof_buffer = new char[kOutBufSize];
+ int written = 0;
+ for (SymbolMap::const_iterator iter = symbolization_table_.begin();
+ iter != symbolization_table_.end(); ++iter) {
+ written += snprintf(pprof_buffer + written, kOutBufSize - written,
+ // pprof expects format to be 0xXXXXXX
+ "0x%"PRIxPTR"\n", reinterpret_cast<uintptr_t>(iter->first));
}
+ write(child_in[1], pprof_buffer, strlen(pprof_buffer));
close(child_in[1]); // that's all we need to write
+ const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size();
int total_bytes_read = 0;
- memset(out, '\0', out_size);
+ delete[] symbol_buffer_;
+ symbol_buffer_ = new char[kSymbolBufferSize];
+ memset(symbol_buffer_, '\0', kSymbolBufferSize);
while (1) {
- int bytes_read = read(child_out[1], out + total_bytes_read,
- out_size - total_bytes_read);
+ int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read,
+ kSymbolBufferSize - total_bytes_read);
if (bytes_read < 0) {
close(child_out[1]);
- return false;
+ return 0;
} else if (bytes_read == 0) {
close(child_out[1]);
wait(NULL);
@@ -159,25 +172,24 @@ extern bool Symbolize(char *out, int out_size,
}
// We have successfully read the output of pprof into out. Make sure
// the last symbol is full (we can tell because it ends with a \n).
- // TODO(glider): even when the last symbol is full, the list of symbols
- // may be incomplete. We should check for that and return the number of
- // symbols we actually get from pprof.
- if (total_bytes_read == 0 || out[total_bytes_read - 1] != '\n')
- return false;
- // make the symbolization_table values point to the output vector
- SymbolMap::iterator fill = symbolization_table->begin();
- const char *current_name = out;
+ if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n')
+ return 0;
+ // make the symbolization_table_ values point to the output vector
+ SymbolMap::iterator fill = symbolization_table_.begin();
+ int num_symbols = 0;
+ const char *current_name = symbol_buffer_;
for (int i = 0; i < total_bytes_read; i++) {
- if (out[i] == '\n') {
+ if (symbol_buffer_[i] == '\n') {
fill->second = current_name;
- out[i] = '\0';
- current_name = out + i + 1;
+ symbol_buffer_[i] = '\0';
+ current_name = symbol_buffer_ + i + 1;
fill++;
+ num_symbols++;
}
}
- return true;
+ return num_symbols;
}
}
- return false; // shouldn't be reachable
+ return 0; // shouldn't be reachable
#endif
}
diff --git a/src/symbolize.h b/src/symbolize.h
index 8fb0366..1ab4ed6 100644
--- a/src/symbolize.h
+++ b/src/symbolize.h
@@ -35,20 +35,48 @@
#include "config.h"
#ifdef HAVE_STDINT_H
-#include <stdint.h> // for uintptr_t
+#include <stdint.h> // for uintptr_t
#endif
#include <map>
using std::map;
-// An average size of memory allocated for a stack trace symbol.
-static const int kSymbolSize = 1024;
+// SymbolTable encapsulates the address operations necessary for stack trace
+// symbolization. A common use-case is to Add() the addresses from one or
+// several stack traces to a table, call Symbolize() once and use GetSymbol()
+// to get the symbol names for pretty-printing the stack traces.
+class SymbolTable {
+ public:
+ SymbolTable()
+ : symbol_buffer_(NULL) {}
+ ~SymbolTable() {
+ delete[] symbol_buffer_;
+ }
-// TODO(glider): it's better to make SymbolMap a class that encapsulates the
-// address operations and has the Symbolize() method.
-typedef map<uintptr_t, const char*> SymbolMap;
+ // Adds an address to the table. This may overwrite a currently known symbol
+ // name, so Add() should not generally be called after Symbolize().
+ void Add(const void* addr);
-extern bool Symbolize(char *out, int out_size,
- SymbolMap *symbolization_table);
+ // Returns the symbol name for addr, if the given address was added before
+ // the last successful call to Symbolize(). Otherwise may return an empty
+ // c-string.
+ const char* GetSymbol(const void* addr);
+
+ // Obtains the symbol names for the addresses stored in the table and returns
+ // the number of addresses actually symbolized.
+ int Symbolize();
+
+ private:
+ typedef map<const void*, const char*> SymbolMap;
+
+ // An average size of memory allocated for a stack trace symbol.
+ static const int kSymbolSize = 1024;
+
+ // Map from addresses to symbol names.
+ SymbolMap symbolization_table_;
+
+ // Pointer to the buffer that stores the symbol names.
+ char *symbol_buffer_;
+};
#endif // TCMALLOC_SYMBOLIZE_H_
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index 450c1ab..09c545d 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -360,6 +360,8 @@ extern "C" {
// ----------------------- IMPLEMENTATION -------------------------------
+static int tc_new_mode = 0; // See tc_set_new_mode().
+
// Routines such as free() and realloc() catch some erroneous pointers
// passed to them, and invoke the below when they do. (An erroneous pointer
// won't be caught if it's within a valid span or a stale span for which
@@ -689,10 +691,7 @@ class TCMallocImplementation : public MallocExtension {
virtual void MarkThreadBusy(); // Implemented below
- virtual void ReleaseToSystem(ssize_t num_bytes) {
- if (num_bytes <= 0) {
- return;
- }
+ virtual void ReleaseToSystem(size_t num_bytes) {
SpinLockHolder h(Static::pageheap_lock());
if (num_bytes <= extra_bytes_released_) {
// We released too much on a prior call, so don't release any
@@ -860,6 +859,17 @@ static void ReportLargeAlloc(Length num_pages, void* result) {
namespace {
+inline void* cpp_alloc(size_t size, bool nothrow);
+inline void* do_malloc(size_t size);
+
+// TODO(willchan): Investigate whether or not lining this much is harmful to
+// performance.
+// This is equivalent to do_malloc() except when tc_new_mode is set to true.
+// Otherwise, it will run the std::new_handler if set.
+inline void* do_malloc_or_cpp_alloc(size_t size) {
+ return tc_new_mode ? cpp_alloc(size, true) : do_malloc(size);
+}
+
// Helper for do_malloc().
inline void* do_malloc_pages(Length num_pages) {
Span *span;
@@ -910,7 +920,7 @@ inline void* do_calloc(size_t n, size_t elem_size) {
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n) return NULL;
- void* result = do_malloc(size);
+ void* result = do_malloc_or_cpp_alloc(size);
if (result != NULL) {
memset(result, 0, size);
}
@@ -1019,11 +1029,11 @@ inline void* do_realloc_with_callback(
void* new_ptr = NULL;
if (new_size > old_size && new_size < lower_bound_to_grow) {
- new_ptr = do_malloc(lower_bound_to_grow);
+ new_ptr = do_malloc_or_cpp_alloc(lower_bound_to_grow);
}
if (new_ptr == NULL) {
// Either new_size is not a tiny increment, or last do_malloc failed.
- new_ptr = do_malloc(new_size);
+ new_ptr = do_malloc_or_cpp_alloc(new_size);
}
if (new_ptr == NULL) {
return NULL;
@@ -1241,9 +1251,8 @@ extern "C" PERFTOOLS_DLL_DECL const char* tc_version(
// heap-checker.cc depends on this to start a stack trace from
// the call to the (de)allocation function.
-static int tc_new_mode = 0; // See tc_set_new_mode().
extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW {
- void* result = (tc_new_mode ? cpp_alloc(size, false) : do_malloc(size));
+ void* result = do_malloc_or_cpp_alloc(size);
MallocHook::InvokeNewHook(result, size);
return result;
}
@@ -1268,7 +1277,7 @@ extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW {
extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr,
size_t new_size) __THROW {
if (old_ptr == NULL) {
- void* result = do_malloc(new_size);
+ void* result = do_malloc_or_cpp_alloc(new_size);
MallocHook::InvokeNewHook(result, new_size);
return result;
}
diff --git a/src/tests/atomicops_unittest.cc b/src/tests/atomicops_unittest.cc
index 5a620f5..3892b59 100644
--- a/src/tests/atomicops_unittest.cc
+++ b/src/tests/atomicops_unittest.cc
@@ -60,50 +60,50 @@ static void TestAtomicIncrement() {
s.count = 0;
s.next_word = next_word_value;
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1);
- CHECK_EQ(s.count, 1);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3);
- CHECK_EQ(s.count, 3);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6);
- CHECK_EQ(s.count, 6);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3);
- CHECK_EQ(s.count, 3);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1);
- CHECK_EQ(s.count, 1);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0);
- CHECK_EQ(s.count, 0);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1);
- CHECK_EQ(s.count, -1);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5);
- CHECK_EQ(s.count, -5);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
-
- CHECK_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0);
- CHECK_EQ(s.count, 0);
- CHECK_EQ(s.prev_word, prev_word_value);
- CHECK_EQ(s.next_word, next_word_value);
+ ASSERT_EQ(1, base::subtle::NoBarrier_AtomicIncrement(&s.count, 1));
+ ASSERT_EQ(1, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(3, base::subtle::NoBarrier_AtomicIncrement(&s.count, 2));
+ ASSERT_EQ(3, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(6, base::subtle::NoBarrier_AtomicIncrement(&s.count, 3));
+ ASSERT_EQ(6, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(3, base::subtle::NoBarrier_AtomicIncrement(&s.count, -3));
+ ASSERT_EQ(3, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(1, base::subtle::NoBarrier_AtomicIncrement(&s.count, -2));
+ ASSERT_EQ(1, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(0, base::subtle::NoBarrier_AtomicIncrement(&s.count, -1));
+ ASSERT_EQ(0, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(-1, base::subtle::NoBarrier_AtomicIncrement(&s.count, -1));
+ ASSERT_EQ(-1, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(-5, base::subtle::NoBarrier_AtomicIncrement(&s.count, -4));
+ ASSERT_EQ(-5, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
+
+ ASSERT_EQ(0, base::subtle::NoBarrier_AtomicIncrement(&s.count, 5));
+ ASSERT_EQ(0, s.count);
+ ASSERT_EQ(prev_word_value, s.prev_word);
+ ASSERT_EQ(next_word_value, s.next_word);
}
@@ -114,8 +114,8 @@ template <class AtomicType>
static void TestCompareAndSwap() {
AtomicType value = 0;
AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1);
- CHECK_EQ(1, value);
- CHECK_EQ(0, prev);
+ ASSERT_EQ(1, value);
+ ASSERT_EQ(0, prev);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
@@ -123,13 +123,13 @@ static void TestCompareAndSwap() {
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5);
- CHECK_EQ(k_test_val, value);
- CHECK_EQ(k_test_val, prev);
+ ASSERT_EQ(k_test_val, value);
+ ASSERT_EQ(k_test_val, prev);
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5);
- CHECK_EQ(5, value);
- CHECK_EQ(k_test_val, prev);
+ ASSERT_EQ(5, value);
+ ASSERT_EQ(k_test_val, prev);
}
@@ -137,8 +137,8 @@ template <class AtomicType>
static void TestAtomicExchange() {
AtomicType value = 0;
AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1);
- CHECK_EQ(1, value);
- CHECK_EQ(0, new_value);
+ ASSERT_EQ(1, value);
+ ASSERT_EQ(0, new_value);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
@@ -146,13 +146,13 @@ static void TestAtomicExchange() {
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val);
- CHECK_EQ(k_test_val, value);
- CHECK_EQ(k_test_val, new_value);
+ ASSERT_EQ(k_test_val, value);
+ ASSERT_EQ(k_test_val, new_value);
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5);
- CHECK_EQ(5, value);
- CHECK_EQ(k_test_val, new_value);
+ ASSERT_EQ(5, value);
+ ASSERT_EQ(k_test_val, new_value);
}
@@ -163,11 +163,11 @@ static void TestAtomicIncrementBounds() {
AtomicType test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2);
AtomicType value = test_val - 1;
AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
- CHECK_EQ(test_val, value);
- CHECK_EQ(value, new_value);
+ ASSERT_EQ(test_val, value);
+ ASSERT_EQ(value, new_value);
base::subtle::NoBarrier_AtomicIncrement(&value, -1);
- CHECK_EQ(test_val - 1, value);
+ ASSERT_EQ(test_val - 1, value);
}
// This is a simple sanity check that values are correct. Not testing
@@ -180,19 +180,19 @@ static void TestStore() {
AtomicType value;
base::subtle::NoBarrier_Store(&value, kVal1);
- CHECK_EQ(kVal1, value);
+ ASSERT_EQ(kVal1, value);
base::subtle::NoBarrier_Store(&value, kVal2);
- CHECK_EQ(kVal2, value);
+ ASSERT_EQ(kVal2, value);
base::subtle::Acquire_Store(&value, kVal1);
- CHECK_EQ(kVal1, value);
+ ASSERT_EQ(kVal1, value);
base::subtle::Acquire_Store(&value, kVal2);
- CHECK_EQ(kVal2, value);
+ ASSERT_EQ(kVal2, value);
base::subtle::Release_Store(&value, kVal1);
- CHECK_EQ(kVal1, value);
+ ASSERT_EQ(kVal1, value);
base::subtle::Release_Store(&value, kVal2);
- CHECK_EQ(kVal2, value);
+ ASSERT_EQ(kVal2, value);
}
// This is a simple sanity check that values are correct. Not testing
@@ -205,19 +205,19 @@ static void TestLoad() {
AtomicType value;
value = kVal1;
- CHECK_EQ(kVal1, base::subtle::NoBarrier_Load(&value));
+ ASSERT_EQ(kVal1, base::subtle::NoBarrier_Load(&value));
value = kVal2;
- CHECK_EQ(kVal2, base::subtle::NoBarrier_Load(&value));
+ ASSERT_EQ(kVal2, base::subtle::NoBarrier_Load(&value));
value = kVal1;
- CHECK_EQ(kVal1, base::subtle::Acquire_Load(&value));
+ ASSERT_EQ(kVal1, base::subtle::Acquire_Load(&value));
value = kVal2;
- CHECK_EQ(kVal2, base::subtle::Acquire_Load(&value));
+ ASSERT_EQ(kVal2, base::subtle::Acquire_Load(&value));
value = kVal1;
- CHECK_EQ(kVal1, base::subtle::Release_Load(&value));
+ ASSERT_EQ(kVal1, base::subtle::Release_Load(&value));
value = kVal2;
- CHECK_EQ(kVal2, base::subtle::Release_Load(&value));
+ ASSERT_EQ(kVal2, base::subtle::Release_Load(&value));
}
template <class AtomicType>
diff --git a/src/tests/debugallocation_test.cc b/src/tests/debugallocation_test.cc
index 4274b7e..ca00e36 100644
--- a/src/tests/debugallocation_test.cc
+++ b/src/tests/debugallocation_test.cc
@@ -102,6 +102,28 @@ TEST(DebugAllocationTest, DeallocMismatch) {
}
}
+TEST(DebugAllocationTest, DoubleFree) {
+ int* pint = new int;
+ delete pint;
+ IF_DEBUG_EXPECT_DEATH(delete pint, "has been already deallocated");
+}
+
+TEST(DebugAllocationTest, StompBefore) {
+ int* pint = new int;
+#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
+ pint[-1] = 5;
+ IF_DEBUG_EXPECT_DEATH(delete pint, "a word before object");
+#endif
+}
+
+TEST(DebugAllocationTest, StompAfter) {
+ int* pint = new int;
+#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
+ pint[1] = 5;
+ IF_DEBUG_EXPECT_DEATH(delete pint, "a word after object");
+#endif
+}
+
TEST(DebugAllocationTest, FreeQueueTest) {
// Verify that the allocator doesn't return blocks that were recently freed.
int* x = new int;
@@ -205,6 +227,31 @@ TEST(DebugAllocationTest, GetAllocatedSizeTest) {
free(a);
}
+TEST(DebugAllocationTest, HugeAlloc) {
+ const size_t kTooBig = ~static_cast<size_t>(0);
+ void* a = NULL;
+ char* b = NULL;
+
+#ifndef NDEBUG
+
+ a = malloc(kTooBig);
+ EXPECT_EQ(NULL, a);
+ b = NULL;
+ IF_DEBUG_EXPECT_DEATH(b = new char[kTooBig],
+ "Unable to allocate.*new\\[\\] failed\\.");
+ EXPECT_EQ(NULL, b);
+
+ // kAlsoTooBig is small enough not to get caught by debugallocation's check,
+ // but will still fall through to tcmalloc's check.
+ const size_t kAlsoTooBig = kTooBig - 1024;
+
+ a = malloc(kAlsoTooBig);
+ EXPECT_EQ(NULL, a);
+ IF_DEBUG_EXPECT_DEATH(b = new char[kAlsoTooBig], "Unable to allocate.*new failed");
+ EXPECT_EQ(NULL, b);
+#endif
+}
+
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
diff --git a/src/tests/malloc_extension_test.cc b/src/tests/malloc_extension_test.cc
index 1f00f73..ef76766 100644
--- a/src/tests/malloc_extension_test.cc
+++ b/src/tests/malloc_extension_test.cc
@@ -43,32 +43,32 @@ int main(int argc, char** argv) {
void* a = malloc(1000);
size_t cxx_bytes_used, c_bytes_used;
- CHECK(MallocExtension::instance()->GetNumericProperty(
- "generic.current_allocated_bytes", &cxx_bytes_used));
- CHECK(MallocExtension_GetNumericProperty(
- "generic.current_allocated_bytes", &c_bytes_used));
- CHECK_GT(cxx_bytes_used, 1000);
- CHECK_EQ(cxx_bytes_used, c_bytes_used);
+ ASSERT_TRUE(MallocExtension::instance()->GetNumericProperty(
+ "generic.current_allocated_bytes", &cxx_bytes_used));
+ ASSERT_TRUE(MallocExtension_GetNumericProperty(
+ "generic.current_allocated_bytes", &c_bytes_used));
+ ASSERT_GT(cxx_bytes_used, 1000);
+ ASSERT_EQ(cxx_bytes_used, c_bytes_used);
- CHECK(MallocExtension::instance()->VerifyAllMemory());
- CHECK(MallocExtension_VerifyAllMemory());
+ ASSERT_TRUE(MallocExtension::instance()->VerifyAllMemory());
+ ASSERT_TRUE(MallocExtension_VerifyAllMemory());
- CHECK_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000);
+ ASSERT_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000);
// This is just a sanity check. If we allocated too much, tcmalloc is broken
- CHECK_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000);
- CHECK_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000);
+ ASSERT_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000);
+ ASSERT_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000);
for (int i = 0; i < 10; ++i) {
void *p = malloc(i);
- CHECK_GE(MallocExtension::instance()->GetAllocatedSize(p),
+ ASSERT_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);
- CHECK_GE(MallocExtension_GetEstimatedAllocatedSize(1000), 1000);
+ ASSERT_GE(MallocExtension_GetAllocatedSize(a), 1000);
+ ASSERT_LE(MallocExtension_GetAllocatedSize(a), 5000);
+ ASSERT_GE(MallocExtension_GetEstimatedAllocatedSize(1000), 1000);
free(a);
diff --git a/src/tests/profile-handler_unittest.cc b/src/tests/profile-handler_unittest.cc
index 1e72b2e..84e035c 100644
--- a/src/tests/profile-handler_unittest.cc
+++ b/src/tests/profile-handler_unittest.cc
@@ -47,19 +47,41 @@ class Thread {
bool joinable_;
};
-// timespec of the sleep interval. To ensure a SIGPROF timer interrupt under
-// heavy load, this is set to a 20x of ProfileHandler timer interval (i.e 100Hz)
-// TODO(nabeelmian) Under very heavy loads, the worker thread may not accumulate
-// enough cpu usage to get a profile tick.
-const struct timespec sleep_interval = { 0, 200000000 }; // 200 ms
+// Sleep interval in nano secs. ITIMER_PROF goes off only afer the specified CPU
+// time is consumed. Under heavy load this process may no get scheduled in a
+// timely fashion. Therefore, give enough time (20x of ProfileHandle timer
+// interval 10ms (100Hz)) for this process to accumulate enought CPU time to get
+// a profile tick.
+int kSleepInterval = 200000000;
+
+// Sleep interval in nano secs. To ensure that if the timer has expired it is
+// reset.
+int kTimerResetInterval = 5000000;
// Whether each thread has separate timers.
static bool timer_separate_ = false;
+static int timer_type_ = ITIMER_PROF;
+static int signal_number_ = SIGPROF;
+
+// Delays processing by the specified number of nano seconds. 'delay_ns'
+// must be less than the number of nano seconds in a second (1000000000).
+void Delay(int delay_ns) {
+ static const int kNumNSecInSecond = 1000000000;
+ EXPECT_LT(delay_ns, kNumNSecInSecond);
+ struct timespec delay = { 0, delay_ns };
+ nanosleep(&delay, 0);
+}
// Checks whether the profile timer is enabled for the current thread.
bool IsTimerEnabled() {
itimerval current_timer;
- EXPECT_EQ(0, getitimer(ITIMER_PROF, &current_timer));
+ EXPECT_EQ(0, getitimer(timer_type_, &current_timer));
+ if ((current_timer.it_value.tv_sec == 0) &&
+ (current_timer.it_value.tv_usec != 0)) {
+ // May be the timer has expired. Sleep for a bit and check again.
+ Delay(kTimerResetInterval);
+ EXPECT_EQ(0, getitimer(timer_type_, &current_timer));
+ }
return (current_timer.it_value.tv_sec != 0 ||
current_timer.it_value.tv_usec != 0);
}
@@ -161,11 +183,15 @@ class ProfileHandlerTest {
// Determines whether threads have separate timers.
static void SetUpTestCase() {
+ timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
+ signal_number_ = (getenv("CPUPROFILE_REALTIME") ? SIGALRM : SIGPROF);
+
timer_separate_ = threads_have_separate_timers();
+ Delay(kTimerResetInterval);
}
- // Sets up the profile timers and SIGPROF handler in a known state. It does
- // the following:
+ // Sets up the profile timers and SIGPROF/SIGALRM handler in a known state.
+ // It does the following:
// 1. Unregisters all the callbacks, stops the timer (if shared) and
// clears out timer_sharing state in the ProfileHandler. This clears
// out any state left behind by the previous test or during module
@@ -177,7 +203,7 @@ class ProfileHandlerTest {
// Reset the state of ProfileHandler between each test. This unregisters
// all callbacks, stops timer (if shared) and clears timer sharing state.
ProfileHandlerReset();
- EXPECT_EQ(GetCallbackCount(), 0);
+ EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
// ProfileHandler requires at least two threads to be registerd to determine
// whether timers are shared.
@@ -214,7 +240,7 @@ class ProfileHandlerTest {
busy_worker_->Start();
// Wait for worker to start up and register with the ProfileHandler.
// TODO(nabeelmian) This may not work under very heavy load.
- nanosleep(&sleep_interval, NULL);
+ Delay(kSleepInterval);
}
// Stops the worker thread.
@@ -224,10 +250,10 @@ class ProfileHandlerTest {
delete busy_worker_;
}
- // Checks whether SIGPROF signal handler is enabled.
+ // Checks whether SIGPROF/SIGALRM signal handler is enabled.
bool IsSignalEnabled() {
struct sigaction sa;
- CHECK_EQ(sigaction(SIGPROF, NULL, &sa), 0);
+ CHECK_EQ(sigaction(signal_number_, NULL, &sa), 0);
return ((sa.sa_handler == SIG_IGN) || (sa.sa_handler == SIG_DFL)) ?
false : true;
}
@@ -258,7 +284,7 @@ class ProfileHandlerTest {
uint64 interrupts_before = GetInterruptCount();
// Sleep for a bit and check that tick counter is making progress.
int old_tick_count = tick_counter;
- nanosleep(&sleep_interval, NULL);
+ Delay(kSleepInterval);
int new_tick_count = tick_counter;
EXPECT_GT(new_tick_count, old_tick_count);
uint64 interrupts_after = GetInterruptCount();
@@ -269,9 +295,9 @@ class ProfileHandlerTest {
void VerifyUnregistration(const int& tick_counter) {
// Sleep for a bit and check that tick counter is not making progress.
int old_tick_count = tick_counter;
- nanosleep(&sleep_interval, NULL);
+ Delay(kSleepInterval);
int new_tick_count = tick_counter;
- EXPECT_EQ(new_tick_count, old_tick_count);
+ EXPECT_EQ(old_tick_count, new_tick_count);
// If no callbacks, signal handler and shared timer should be disabled.
if (GetCallbackCount() == 0) {
EXPECT_FALSE(IsSignalEnabled());
@@ -283,13 +309,13 @@ class ProfileHandlerTest {
}
}
- // Verifies that the SIGPROF interrupt handler is disabled and the timer,
- // if shared, is disabled. Expects the worker to be running.
+ // Verifies that the SIGPROF/SIGALRM interrupt handler is disabled and the
+ // timer, if shared, is disabled. Expects the worker to be running.
void VerifyDisabled() {
// Check that the signal handler is disabled.
EXPECT_FALSE(IsSignalEnabled());
// Check that the callback count is 0.
- EXPECT_EQ(GetCallbackCount(), 0);
+ EXPECT_EQ(0, GetCallbackCount());
// Check that the timer is disabled if shared, enabled otherwise.
if (timer_separate_) {
EXPECT_TRUE(IsTimerEnabled());
@@ -298,9 +324,25 @@ class ProfileHandlerTest {
}
// Verify that the ProfileHandler is not accumulating profile ticks.
uint64 interrupts_before = GetInterruptCount();
- nanosleep(&sleep_interval, NULL);
+ Delay(kSleepInterval);
uint64 interrupts_after = GetInterruptCount();
- EXPECT_EQ(interrupts_after, interrupts_before);
+ EXPECT_EQ(interrupts_before, interrupts_after);
+ }
+
+ // Registers a callback and waits for kTimerResetInterval for timers to get
+ // reset.
+ ProfileHandlerToken* RegisterCallback(void* callback_arg) {
+ ProfileHandlerToken* token = ProfileHandlerRegisterCallback(
+ TickCounter, callback_arg);
+ Delay(kTimerResetInterval);
+ return token;
+ }
+
+ // Unregisters a callback and waits for kTimerResetInterval for timers to get
+ // reset.
+ void UnregisterCallback(ProfileHandlerToken* token) {
+ ProfileHandlerUnregisterCallback(token);
+ Delay(kTimerResetInterval);
}
// Busy worker thread to accumulate cpu usage.
@@ -337,10 +379,9 @@ class ProfileHandlerTest {
// ProfileHandlerUnregisterCallback.
TEST_F(ProfileHandlerTest, RegisterUnregisterCallback) {
int tick_count = 0;
- ProfileHandlerToken* token = ProfileHandlerRegisterCallback(
- TickCounter, &tick_count);
+ ProfileHandlerToken* token = RegisterCallback(&tick_count);
VerifyRegistration(tick_count);
- ProfileHandlerUnregisterCallback(token);
+ UnregisterCallback(token);
VerifyUnregistration(tick_count);
}
@@ -348,31 +389,29 @@ TEST_F(ProfileHandlerTest, RegisterUnregisterCallback) {
TEST_F(ProfileHandlerTest, MultipleCallbacks) {
// Register first callback.
int first_tick_count;
- ProfileHandlerToken* token1 = ProfileHandlerRegisterCallback(
- TickCounter, &first_tick_count);
+ ProfileHandlerToken* token1 = RegisterCallback(&first_tick_count);
// Check that callback was registered correctly.
VerifyRegistration(first_tick_count);
- EXPECT_EQ(GetCallbackCount(), 1);
+ EXPECT_EQ(1, GetCallbackCount());
// Register second callback.
int second_tick_count;
- ProfileHandlerToken* token2 = ProfileHandlerRegisterCallback(
- TickCounter, &second_tick_count);
+ ProfileHandlerToken* token2 = RegisterCallback(&second_tick_count);
// Check that callback was registered correctly.
VerifyRegistration(second_tick_count);
- EXPECT_EQ(GetCallbackCount(), 2);
+ EXPECT_EQ(2, GetCallbackCount());
// Unregister first callback.
- ProfileHandlerUnregisterCallback(token1);
+ UnregisterCallback(token1);
VerifyUnregistration(first_tick_count);
- EXPECT_EQ(GetCallbackCount(), 1);
+ EXPECT_EQ(1, GetCallbackCount());
// Verify that second callback is still registered.
VerifyRegistration(second_tick_count);
// Unregister second callback.
- ProfileHandlerUnregisterCallback(token2);
+ UnregisterCallback(token2);
VerifyUnregistration(second_tick_count);
- EXPECT_EQ(GetCallbackCount(), 0);
+ EXPECT_EQ(0, GetCallbackCount());
// Verify that the signal handler and timers are correctly disabled.
VerifyDisabled();
@@ -383,15 +422,15 @@ TEST_F(ProfileHandlerTest, Reset) {
// Verify that the profile timer interrupt is disabled.
VerifyDisabled();
int first_tick_count;
- ProfileHandlerRegisterCallback(TickCounter, &first_tick_count);
+ RegisterCallback(&first_tick_count);
VerifyRegistration(first_tick_count);
- EXPECT_EQ(GetCallbackCount(), 1);
+ EXPECT_EQ(1, GetCallbackCount());
// Register second callback.
int second_tick_count;
- ProfileHandlerRegisterCallback(TickCounter, &second_tick_count);
+ RegisterCallback(&second_tick_count);
VerifyRegistration(second_tick_count);
- EXPECT_EQ(GetCallbackCount(), 2);
+ EXPECT_EQ(2, GetCallbackCount());
// Reset the profile handler and verify that callback were correctly
// unregistered and timer/signal are disabled.
@@ -410,7 +449,7 @@ TEST_F(ProfileHandlerTest, RegisterCallbackBeforeThread) {
// the signal handler and reset the timer sharing state in the Profile
// Handler.
ProfileHandlerReset();
- EXPECT_EQ(GetCallbackCount(), 0);
+ EXPECT_EQ(0, GetCallbackCount());
VerifyDisabled();
// Start the worker. At this time ProfileHandler doesn't know if timers are
@@ -418,14 +457,14 @@ TEST_F(ProfileHandlerTest, RegisterCallbackBeforeThread) {
StartWorker();
// Register a callback and check that profile ticks are being delivered.
int tick_count;
- ProfileHandlerRegisterCallback(TickCounter, &tick_count);
- EXPECT_EQ(GetCallbackCount(), 1);
+ RegisterCallback(&tick_count);
+ EXPECT_EQ(1, GetCallbackCount());
VerifyRegistration(tick_count);
// Register a second thread and verify that timer and signal handler are
// correctly enabled.
RegisterThread();
- EXPECT_EQ(GetCallbackCount(), 1);
+ EXPECT_EQ(1, GetCallbackCount());
EXPECT_TRUE(IsTimerEnabled());
EXPECT_TRUE(IsSignalEnabled());
}
diff --git a/src/tests/profiler_unittest.sh b/src/tests/profiler_unittest.sh
index 73be680..5766f2e 100755
--- a/src/tests/profiler_unittest.sh
+++ b/src/tests/profiler_unittest.sh
@@ -241,10 +241,16 @@ VerifyIdentical p12 "$PROFILER1_REALNAME" p13 "" || RegisterFailure
>"$TMPDIR/p15" 2>/dev/null || RegisterFailure
VerifyIdentical p14 "$PROFILER3_REALNAME" p15 "" || RegisterFailure
+# Test using ITIMER_REAL instead of ITIMER_PROF.
+env CPUPROFILE_REALTIME=1 "$PROFILER3" 5 2 "$TMPDIR/p16" || RegisterFailure
+env CPUPROFILE_REALTIME=1 "$PROFILER3" 10 2 "$TMPDIR/p17" || RegisterFailure
+VerifySimilar p16 "$PROFILER3_REALNAME" p17 "$PROFILER3_REALNAME" 2
+
+
# Make sure that when we have a process with a fork, the profiles don't
# clobber each other
-CPUPROFILE="$TMPDIR/p6" "$PROFILER1" 1 -2 || RegisterFailure
-n=`ls $TMPDIR/p6* | wc -l`
+CPUPROFILE="$TMPDIR/pfork" "$PROFILER1" 1 -2 || RegisterFailure
+n=`ls $TMPDIR/pfork* | wc -l`
if [ $n != 3 ]; then
echo "FORK test FAILED: expected 3 profiles (for main + 2 children), found $n"
num_failures=`expr $num_failures + 1`
diff --git a/src/tests/tcmalloc_unittest.cc b/src/tests/tcmalloc_unittest.cc
index 8eccb18..cc47ad4 100644
--- a/src/tests/tcmalloc_unittest.cc
+++ b/src/tests/tcmalloc_unittest.cc
@@ -802,12 +802,14 @@ static void TestRanges() {
CheckRangeCallback(b, base::MallocRange::FREE, MB);
}
+#ifndef DEBUGALLOCATION
static size_t GetUnmappedBytes() {
size_t bytes;
CHECK(MallocExtension::instance()->GetNumericProperty(
"tcmalloc.pageheap_unmapped_bytes", &bytes));
return bytes;
}
+#endif
static void TestReleaseToSystem() {
// Debug allocation mode adds overhead to each allocation which
@@ -833,10 +835,6 @@ static void TestReleaseToSystem() {
free(a);
- // Negative numbers should be ignored.
- MallocExtension::instance()->ReleaseToSystem(-5);
- EXPECT_EQ(starting_bytes, GetUnmappedBytes());
-
// The span to release should be 1MB.
MallocExtension::instance()->ReleaseToSystem(MB/2);
EXPECT_EQ(starting_bytes + MB, GetUnmappedBytes());
@@ -871,6 +869,43 @@ static void TestReleaseToSystem() {
#endif // #ifndef DEBUGALLOCATION
}
+bool g_no_memory = false;
+std::new_handler g_old_handler = NULL;
+static void OnNoMemory() {
+ g_no_memory = true;
+ std::set_new_handler(g_old_handler);
+}
+
+static void TestSetNewMode() {
+ int old_mode = tc_set_new_mode(1);
+
+ // DebugAllocation will try to catch huge allocations. We need to avoid this
+ // by requesting a smaller malloc block, that still can't be satisfied.
+ const size_t kHugeRequest = kTooBig - 1024;
+
+ g_old_handler = std::set_new_handler(&OnNoMemory);
+ g_no_memory = false;
+ void* ret = malloc(kHugeRequest);
+ EXPECT_EQ(NULL, ret);
+ EXPECT_TRUE(g_no_memory);
+
+ g_old_handler = std::set_new_handler(&OnNoMemory);
+ g_no_memory = false;
+ ret = calloc(1, kHugeRequest);
+ EXPECT_EQ(NULL, ret);
+ EXPECT_TRUE(g_no_memory);
+
+ g_old_handler = std::set_new_handler(&OnNoMemory);
+ g_no_memory = false;
+ ret = realloc(NULL, kHugeRequest);
+ EXPECT_EQ(NULL, ret);
+ EXPECT_TRUE(g_no_memory);
+
+ g_no_memory = false;
+
+ tc_set_new_mode(old_mode);
+}
+
static int RunAllTests(int argc, char** argv) {
// Optional argv[1] is the seed
AllocatorState rnd(argc > 1 ? atoi(argv[1]) : 100);
@@ -1149,6 +1184,7 @@ static int RunAllTests(int argc, char** argv) {
TestHugeThreadCache();
TestRanges();
TestReleaseToSystem();
+ TestSetNewMode();
return 0;
}
diff --git a/src/windows/config.h b/src/windows/config.h
index b3a6852..99de82c 100644
--- a/src/windows/config.h
+++ b/src/windows/config.h
@@ -175,6 +175,9 @@
/* define if your compiler has __attribute__ */
#undef HAVE___ATTRIBUTE__
+/* Define to 1 if compiler supports __environ */
+#undef HAVE___ENVIRON
+
/* Define to 1 if the system has the type `__int64'. */
#define HAVE___INT64 1
diff --git a/src/windows/google/tcmalloc.h b/src/windows/google/tcmalloc.h
index 15a2e19..4b97b15 100644
--- a/src/windows/google/tcmalloc.h
+++ b/src/windows/google/tcmalloc.h
@@ -90,6 +90,7 @@ extern "C" {
#endif
#ifdef __cplusplus
+ PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW;
PERFTOOLS_DLL_DECL void* tc_new(size_t size);
PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);