* 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:
csilvers 2009-12-02 18:15:13 +00:00
parent 5b80f01df1
commit a94d5f7974
28 changed files with 525 additions and 238 deletions

29
configure vendored
View File

@ -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:

View File

@ -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:

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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,

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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));

View File

@ -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");

View File

@ -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',

View File

@ -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) {

View File

@ -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
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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());
}

View File

@ -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`

View File

@ -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;
}

View File

@ -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

View File

@ -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);