mirror of
https://github.com/gperftools/gperftools
synced 2025-02-16 11:47:07 +00:00
* 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
This commit is contained in:
parent
5b80f01df1
commit
a94d5f7974
29
configure
vendored
29
configure
vendored
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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));
|
||||
|
@ -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");
|
||||
|
@ -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',
|
||||
|
@ -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, ¤t_timer), "getitimer");
|
||||
RAW_CHECK(0 == getitimer(timer_type_, ¤t_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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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(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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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, ¤t_timer));
|
||||
EXPECT_EQ(0, getitimer(timer_type_, ¤t_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_, ¤t_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());
|
||||
}
|
||||
|
@ -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`
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user