diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | doc/tcmalloc.html | 7 | ||||
-rw-r--r-- | src/debugallocation.cc | 13 | ||||
-rw-r--r-- | src/heap-checker.cc | 32 | ||||
-rw-r--r-- | src/memfs_malloc.cc | 9 | ||||
-rwxr-xr-x | src/pprof | 37 | ||||
-rw-r--r-- | src/tcmalloc.cc | 147 | ||||
-rwxr-xr-x | src/tests/debugallocation_test.sh | 2 | ||||
-rw-r--r-- | src/tests/memalign_unittest.cc | 4 | ||||
-rw-r--r-- | src/tests/tcmalloc_unittest.cc | 14 | ||||
-rw-r--r-- | src/thread_cache.cc | 29 |
12 files changed, 216 insertions, 82 deletions
diff --git a/Makefile.am b/Makefile.am index a8a2527..327d98a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ AM_CXXFLAGS = # changes one day. gcc ignores functions it doesn't understand. if GCC AM_CXXFLAGS += -Wall -Wwrite-strings -Woverloaded-virtual \ - -Wno-sign-compare -Wno-unused-result \ + -Wno-sign-compare \ -fno-builtin-malloc -fno-builtin-free -fno-builtin-realloc \ -fno-builtin-calloc -fno-builtin-cfree \ -fno-builtin-memalign -fno-builtin-posix_memalign \ diff --git a/Makefile.in b/Makefile.in index e173564..339f47f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -52,7 +52,7 @@ host_triplet = @host@ # builtins now in any case, but it's best to be explicit in case that # changes one day. gcc ignores functions it doesn't understand. @GCC_TRUE@am__append_2 = -Wall -Wwrite-strings -Woverloaded-virtual \ -@GCC_TRUE@ -Wno-sign-compare -Wno-unused-result \ +@GCC_TRUE@ -Wno-sign-compare \ @GCC_TRUE@ -fno-builtin-malloc -fno-builtin-free -fno-builtin-realloc \ @GCC_TRUE@ -fno-builtin-calloc -fno-builtin-cfree \ @GCC_TRUE@ -fno-builtin-memalign -fno-builtin-posix_memalign \ diff --git a/doc/tcmalloc.html b/doc/tcmalloc.html index 9d7ab7e..4578984 100644 --- a/doc/tcmalloc.html +++ b/doc/tcmalloc.html @@ -604,6 +604,13 @@ tries to allocate memory from the kernel.</p> </td> </tr> +<tr valign=top> + <td><code>TCMALLOC_MEMFS_MAP_PRVIATE</code></td> + <td>default: false</td> + <td> + If true, use MAP_PRIVATE when mapping via memfs, not MAP_SHARED. + </td> +</tr> </table> diff --git a/src/debugallocation.cc b/src/debugallocation.cc index 12865eb..d38d1e0 100644 --- a/src/debugallocation.cc +++ b/src/debugallocation.cc @@ -36,10 +36,10 @@ // Malloc can be in several places on older versions of OS X. # if defined(HAVE_MALLOC_H) # include <malloc.h> -# elif defined(HAVE_SYS_MALLOC_H) -# include <sys/malloc.h> # elif defined(HAVE_MALLOC_MALLOC_H) # include <malloc/malloc.h> +# elif defined(HAVE_SYS_MALLOC_H) +# include <sys/malloc.h> # endif #endif #include <pthread.h> @@ -1404,7 +1404,14 @@ extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW { #endif extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW { - return BASE_MALLOC_SIZE(ptr); + if (!ptr) { + return 0; + } + MallocBlock* mb = MallocBlock::FromRawPointer(ptr); + // This is just to make sure we actually own mb (and ptr). We don't + // use the actual value, just the 'exception' it raises on error. + (void)BASE_MALLOC_SIZE(mb); + return mb->data_size(); } // Override __libc_memalign in libc on linux boxes. diff --git a/src/heap-checker.cc b/src/heap-checker.cc index dfda9ad..1794e8b 100644 --- a/src/heap-checker.cc +++ b/src/heap-checker.cc @@ -882,6 +882,8 @@ HeapLeakChecker::ProcMapsResult HeapLeakChecker::UseProcMapsLocked( int64 inode; char *permissions, *filename; bool saw_shared_lib = false; + bool saw_nonzero_inode = false; + bool saw_shared_lib_with_nonzero_inode = false; while (it.Next(&start_address, &end_address, &permissions, &file_offset, &inode, &filename)) { if (start_address >= end_address) { @@ -897,16 +899,22 @@ HeapLeakChecker::ProcMapsResult HeapLeakChecker::UseProcMapsLocked( // do things in this loop. continue; } - // Determine if any shared libraries are present. This is the same - // list of extensions as is found in pprof. - if (strstr(filename, ".dll")) { // for windows, which doesn't have inodes + // Determine if any shared libraries are present (this is the same + // list of extensions as is found in pprof). We want to ignore + // 'fake' libraries with inode 0 when determining. However, some + // systems don't share inodes via /proc, so we turn off this check + // if we don't see any evidence that we're getting inode info. + if (inode != 0) { + saw_nonzero_inode = true; + } + if ((strstr(filename, "lib") && strstr(filename, ".so")) || + strstr(filename, ".dll") || + // not all .dylib filenames start with lib. .dylib is big enough + // that we are unlikely to get false matches just checking that. + strstr(filename, ".dylib") || strstr(filename, ".bundle")) { saw_shared_lib = true; - } else if (inode != 0) { // ignore fake files - if ((strstr(filename, "lib") && strstr(filename, ".so")) || - // not all .dylib filenames start with lib. .dylib is big enough - // that we are unlikely to get false matches just checking that. - strstr(filename, ".dylib") || strstr(filename, ".bundle")) { - saw_shared_lib = true; + if (inode != 0) { + saw_shared_lib_with_nonzero_inode = true; } } @@ -927,6 +935,12 @@ HeapLeakChecker::ProcMapsResult HeapLeakChecker::UseProcMapsLocked( RAW_CHECK(0, ""); } } + // If /proc/self/maps is reporting inodes properly (we saw a + // non-zero inode), then we only say we saw a shared lib if we saw a + // 'real' one, with a non-zero inode. + if (saw_nonzero_inode) { + saw_shared_lib = saw_shared_lib_with_nonzero_inode; + } if (!saw_shared_lib) { RAW_LOG(ERROR, "No shared libs detected. Will likely report false leak " "positives for statically linked executables."); diff --git a/src/memfs_malloc.cc b/src/memfs_malloc.cc index 9df4cad..210a4ee 100644 --- a/src/memfs_malloc.cc +++ b/src/memfs_malloc.cc @@ -71,6 +71,9 @@ DEFINE_bool(memfs_malloc_abort_on_fail, DEFINE_bool(memfs_malloc_ignore_mmap_fail, EnvToBool("TCMALLOC_MEMFS_IGNORE_MMAP_FAIL", false), "Ignore failures from mmap"); +DEFINE_bool(memfs_malloc_map_private, + EnvToBool("TCMALLOC_MEMFS_MAP_PRIVATE", false), + "Use MAP_PRIVATE with mmap"); // Hugetlbfs based allocator for tcmalloc class HugetlbSysAllocator: public SysAllocator { @@ -162,8 +165,10 @@ void* HugetlbSysAllocator::Alloc(size_t size, size_t *actual_size, // size + alignment < (1<<NBITS). // and extra <= alignment // therefore size + extra < (1<<NBITS) - void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ, - MAP_SHARED, hugetlb_fd_, hugetlb_base_); + void *result; + result = mmap(0, size + extra, PROT_WRITE|PROT_READ, + FLAGS_memfs_malloc_map_private ? MAP_PRIVATE : 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 of size %"PRIuS" failed: %s\n", @@ -122,6 +122,11 @@ my $UNKNOWN_BINARY = "(unknown)"; # 64-bit profiles. To err on the safe size, default to 64-bit here: my $address_length = 16; +my $dev_null = "/dev/null"; +if (! -e $dev_null && $^O =~ /MSWin/) { # $^O is the OS perl was built for + $dev_null = "nul"; +} + # A list of paths to search for shared object files my @prefix_list = (); @@ -705,7 +710,7 @@ sub ReadlineMightFail { sub RunGV { my $fname = shift; my $bg = shift; # "" or " &" if we should run in background - if (!system("$GV --version >/dev/null 2>&1")) { + if (!system("$GV --version >$dev_null 2>&1")) { # Options using double dash are supported by this gv version. # Also, turn on noantialias to better handle bug in gv for # postscript files with large dimensions. @@ -4261,7 +4266,7 @@ sub MapToSymbols { # If "addr2line" isn't installed on the system at all, just use # nm to get what info we can (function names, but not line numbers). - if (system("$addr2line --help >/dev/null 2>&1") != 0) { + if (system("$addr2line --help >$dev_null 2>&1") != 0) { MapSymbolsWithNM($image, $offset, $pclist, $symbols); return; } @@ -4279,7 +4284,7 @@ sub MapToSymbols { if (defined($sep_address)) { # Only add " -i" to addr2line if the binary supports it. # addr2line --help returns 0, but not if it sees an unknown flag first. - if (system("$cmd -i --help >/dev/null 2>&1") == 0) { + if (system("$cmd -i --help >$dev_null 2>&1") == 0) { $cmd .= " -i"; } else { $sep_address = undef; # no need for sep_address if we don't support -i @@ -4425,8 +4430,16 @@ sub ConfigureObjTools { # predictably return error status in prod. (-e $prog_file) || error("$prog_file does not exist.\n"); - # Follow symlinks (at least for systems where "file" supports that) - my $file_type = `/usr/bin/file -L $prog_file 2>/dev/null || /usr/bin/file $prog_file`; + my $file_type = undef; + if (-e "/usr/bin/file") { + # Follow symlinks (at least for systems where "file" supports that). + $file_type = `/usr/bin/file -L $prog_file 2>$dev_null || /usr/bin/file $prog_file`; + } elsif ($^O == "MSWin32") { + $file_type = "MS Windows"; + } else { + print STDERR "WARNING: Can't determine the file type of $prog_file"; + } + if ($file_type =~ /64-bit/) { # Change $address_length to 16 if the program file is ELF 64-bit. # We can't detect this from many (most?) heap or lock contention @@ -4638,16 +4651,16 @@ sub GetProcedureBoundaries { # --demangle and -f. my $demangle_flag = ""; my $cppfilt_flag = ""; - if (system("$nm --demangle $image >/dev/null 2>&1") == 0) { + if (system("$nm --demangle $image >$dev_null 2>&1") == 0) { # In this mode, we do "nm --demangle <foo>" $demangle_flag = "--demangle"; $cppfilt_flag = ""; - } elsif (system("$cppfilt $image >/dev/null 2>&1") == 0) { + } elsif (system("$cppfilt $image >$dev_null 2>&1") == 0) { # In this mode, we do "nm <foo> | c++filt" $cppfilt_flag = " | $cppfilt"; }; my $flatten_flag = ""; - if (system("$nm -f $image >/dev/null 2>&1") == 0) { + if (system("$nm -f $image >$dev_null 2>&1") == 0) { $flatten_flag = "-f"; } @@ -4655,11 +4668,11 @@ sub GetProcedureBoundaries { # -D to at least get *exported* symbols. If we can't use --demangle, # we use c++filt instead, if it exists on this system. my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", + " $image 2>$dev_null $cppfilt_flag", "$nm -D -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", + " $image 2>$dev_null $cppfilt_flag", # 6nm is for Go binaries - "6nm $image 2>/dev/null | sort", + "6nm $image 2>$dev_null | sort", ); # If the executable is an MS Windows PDB-format executable, we'll @@ -4668,7 +4681,7 @@ sub GetProcedureBoundaries { # PDB-format executables can apparently include dwarf .o files. if (exists $obj_tool_map{"nm_pdb"}) { my $nm_pdb = $obj_tool_map{"nm_pdb"}; - push(@nm_commands, "$nm_pdb --demangle $image 2>/dev/null"); + push(@nm_commands, "$nm_pdb --demangle $image 2>$dev_null"); } foreach my $nm_command (@nm_commands) { diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc index b037f57..ab3f03b 100644 --- a/src/tcmalloc.cc +++ b/src/tcmalloc.cc @@ -97,15 +97,15 @@ #else #include <sys/types.h> #endif -// We only need malloc.h for struct mallinfo. -#ifdef HAVE_STRUCT_MALLINFO +// We only need malloc.h for struct mallinfo and for apple builds +#if defined(HAVE_STRUCT_MALLINFO) || defined(__APPLE__) // Malloc can be in several places on older versions of OS X. # if defined(HAVE_MALLOC_H) # include <malloc.h> -# elif defined(HAVE_SYS_MALLOC_H) -# include <sys/malloc.h> # elif defined(HAVE_MALLOC_MALLOC_H) # include <malloc/malloc.h> +# elif defined(HAVE_SYS_MALLOC_H) +# include <sys/malloc.h> # endif #endif #include <string.h> @@ -262,14 +262,116 @@ extern "C" { } // extern "C" #endif // #ifndef _WIN32 +// We define this here because one some architectures it's needed soon. +namespace { +inline size_t GetSizeWithCallback(void* ptr, + size_t (*invalid_getsize_fn)(void*)) { + if (ptr == NULL) + return 0; + const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; + size_t cl = Static::pageheap()->GetSizeClassIfCached(p); + if (cl != 0) { + return Static::sizemap()->ByteSizeForClass(cl); + } else { + Span *span = Static::pageheap()->GetDescriptor(p); + if (span == NULL) { // means we do not own this memory + return (*invalid_getsize_fn)(ptr); + } else if (span->sizeclass != 0) { + Static::pageheap()->CacheSizeClass(p, span->sizeclass); + return Static::sizemap()->ByteSizeForClass(span->sizeclass); + } else { + return span->length << kPageShift; + } + } +} +} + // Override the libc functions to prefer our own instead. This comes -// first so code in tcmalloc.cc can use the overridden versions. One -// exception: in windows, by default, we patch our code into these +// first so code in tcmalloc.cc can use the overridden versions. +#if defined(WIN32_DO_PATCHING) + +// One exception: in windows, by default, we patch our code into these // functions (via src/windows/patch_function.cc) rather than override // them. In that case, we don't want to do this overriding here. -#if !defined(WIN32_DO_PATCHING) -#if defined(__GNUC__) && !defined(__MACH__) +#elif defined(__APPLE__) + +// Mach's two-level naming scheme makes aliasing difficult, but we can +// use apple's malloc_default_zone() to replace the system alloc. +// http://www.opensource.apple.com/source/Libc/Libc-583/include/malloc/malloc.h +// http://www.opensource.apple.com/source/Libc/Libc-583/gen/malloc.c +// We need wrappers for all the routines, sadly. :-( + +// malloc_zone semantics are we return 0 if we don't own the memory. +static size_t mz_invalid_getsize(void*) { + return 0; +} +static size_t mz_size(malloc_zone_t* zone, const void* ptr) { + // TODO(csilvers): change this method to take a const void*, one day. + // TODO(csilvers): this is totally wrong with debugallocation. + return GetSizeWithCallback(const_cast<void*>(ptr), mz_invalid_getsize); +} +static void* mz_malloc(malloc_zone_t* zone, size_t size) { + return tc_malloc(size); +} +static void* mz_calloc(malloc_zone_t* zone, size_t num_items, size_t size) { + return tc_calloc(num_items, size); +} +static void* mz_valloc(malloc_zone_t* zone, size_t size) { + return tc_valloc(size); +} +static void mz_free(malloc_zone_t* zone, void* ptr) { + return tc_free(ptr); +} +static void* mz_realloc(malloc_zone_t* zone, void* ptr, size_t size) { + return tc_realloc(ptr, size); +} +static void mz_destroy(malloc_zone_t* zone) { + // A no-op -- we will not be destroyed! +} + +static void ReplaceSystemAlloc() { + static malloc_zone_t system_zone_copy; + malloc_zone_t* system_zone = malloc_default_zone(); + memcpy(&system_zone_copy, system_zone, sizeof(system_zone_copy)); + + system_zone->zone_name = "tcmalloc"; + system_zone->size = &mz_size; + system_zone->malloc = &mz_malloc; + system_zone->calloc = &mz_calloc; + system_zone->valloc = &mz_valloc; + system_zone->free = &mz_free; + system_zone->realloc = &mz_realloc; + system_zone->destroy = &mz_destroy; + // TODO(csilvers): figure out if this version of malloc.h supports + // batch_malloc, batch_free, memalign, and free_definite_size, and + // set those to NULL if so. + + // Now register the old system zone, so allocations that happened + // before we ran this command can still be executed. + malloc_zone_register(&system_zone_copy); +} +#define HAVE_REPLACE_SYSTEM_ALLOC 1 + +// OS X doesn't have memalign, posix_memalign, pvalloc, or cfree, so +// we can just define our own. :-) +extern "C" { + void cfree(void* p) __THROW { tc_cfree(p); } + void* memalign(size_t a, size_t s) __THROW { return tc_memalign(a, s); } + void* pvalloc(size_t s) __THROW { return tc_pvalloc(s); } + int posix_memalign(void** r, size_t a, size_t s) __THROW { + return tc_posix_memalign(r, a, s); + } + void malloc_stats(void) __THROW { tc_malloc_stats(); } + int mallopt(int cmd, int v) __THROW { return tc_mallopt(cmd, v); } +#ifdef HAVE_STRUCT_MALLINFO + struct mallinfo mallinfo(void) __THROW { return tc_mallinfo(); } +#endif + // An alias for malloc_size(), which os x defines. + size_t malloc_usable_size(void* p) __THROW { return tc_malloc_size(p); } +} // extern "C" + +#elif defined(__GNUC__) && !defined(__MACH__) // Potentially faster variants that use the gcc alias extension. // FreeBSD does support aliases, but apparently not correctly. :-( // NOTE: we make many of these symbols weak, but do so in the makefile @@ -344,7 +446,8 @@ extern "C" { size_t malloc_size(void* p) __THROW { return tc_malloc_size(p); } size_t malloc_usable_size(void* p) __THROW { return tc_malloc_size(p); } } // extern "C" -#endif // #if defined(__GNUC__) + +#endif // #if defined(WIN32_DO_PATCHING) ... // Some library routines on RedHat 9 allocate memory using malloc() // and free it using __libc_free() (or vice-versa). Since we provide @@ -381,8 +484,6 @@ extern "C" { #undef ALIAS -#endif // #ifndef(WIN32_DO_PATCHING) - // ----------------------- IMPLEMENTATION ------------------------------- @@ -912,6 +1013,9 @@ TCMallocGuard::TCMallocGuard() { // patch the windows VirtualAlloc, etc. PatchWindowsFunctions(); // defined in windows/patch_functions.cc #endif +#ifdef HAVE_REPLACE_SYSTEM_ALLOC + ReplaceSystemAlloc(); // for OS X +#endif tc_free(tc_malloc(1)); ThreadCache::InitTSD(); tc_free(tc_malloc(1)); @@ -1169,27 +1273,6 @@ inline void do_free(void* ptr) { return do_free_with_callback(ptr, &InvalidFree); } -inline size_t GetSizeWithCallback(void* ptr, - size_t (*invalid_getsize_fn)(void*)) { - if (ptr == NULL) - return 0; - const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; - size_t cl = Static::pageheap()->GetSizeClassIfCached(p); - if (cl != 0) { - return Static::sizemap()->ByteSizeForClass(cl); - } else { - Span *span = Static::pageheap()->GetDescriptor(p); - if (span == NULL) { // means we do not own this memory - return (*invalid_getsize_fn)(ptr); - } else if (span->sizeclass != 0) { - Static::pageheap()->CacheSizeClass(p, span->sizeclass); - return Static::sizemap()->ByteSizeForClass(span->sizeclass); - } else { - return span->length << kPageShift; - } - } -} - // This lets you call back to a given function pointer if ptr is invalid. // It is used primarily by windows code which wants a specialized callback. inline void* do_realloc_with_callback( diff --git a/src/tests/debugallocation_test.sh b/src/tests/debugallocation_test.sh index 5d9bd8b..faa6c79 100755 --- a/src/tests/debugallocation_test.sh +++ b/src/tests/debugallocation_test.sh @@ -65,7 +65,7 @@ OneDeathTest() { } death_test_num=0 # which death test to run -while /bin/true; do +while :; do # same as 'while true', but more portable echo -n "Running death test $death_test_num..." output="`OneDeathTest $death_test_num`" case $output in diff --git a/src/tests/memalign_unittest.cc b/src/tests/memalign_unittest.cc index ce2c1f3..1b707dd 100644 --- a/src/tests/memalign_unittest.cc +++ b/src/tests/memalign_unittest.cc @@ -60,10 +60,10 @@ // Malloc can be in several places on older versions of OS X. #if defined(HAVE_MALLOC_H) #include <malloc.h> // for memalign() and valloc() -#elif defined(HAVE_SYS_MALLOC_H) -#include <sys/malloc.h> #elif defined(HAVE_MALLOC_MALLOC_H) #include <malloc/malloc.h> +#elif defined(HAVE_SYS_MALLOC_H) +#include <sys/malloc.h> #endif #include "base/basictypes.h" #include "base/logging.h" diff --git a/src/tests/tcmalloc_unittest.cc b/src/tests/tcmalloc_unittest.cc index 7c21fc4..3af48f8 100644 --- a/src/tests/tcmalloc_unittest.cc +++ b/src/tests/tcmalloc_unittest.cc @@ -1015,7 +1015,10 @@ static int RunAllTests(int argc, char** argv) { p1 = calloc(10, 2); CHECK(p1 != NULL); VerifyNewHookWasCalled(); - p1 = realloc(p1, 30); + // We make sure we realloc to a big size, since some systems (OS + // X) will notice if the realloced size continues to fit into the + // malloc-block and make this a noop if so. + p1 = realloc(p1, 30000); CHECK(p1 != NULL); VerifyNewHookWasCalled(); VerifyDeleteHookWasCalled(); @@ -1092,6 +1095,15 @@ static int RunAllTests(int argc, char** argv) { ::operator delete(p2, std::nothrow); VerifyDeleteHookWasCalled(); + // Try strdup(), which the system allocates but we must free. If + // all goes well, libc will use our malloc! + p2 = strdup("test"); + CHECK(p2 != NULL); + VerifyNewHookWasCalled(); + free(p2); + VerifyDeleteHookWasCalled(); + + // Test mmap too: both anonymous mmap and mmap of a file // Note that for right now we only override mmap on linux // systems, so those are the only ones for which we check. diff --git a/src/thread_cache.cc b/src/thread_cache.cc index 8d31117..64e3b07 100644 --- a/src/thread_cache.cc +++ b/src/thread_cache.cc @@ -312,16 +312,6 @@ void ThreadCache::InitTSD() { ASSERT(!tsd_inited_); perftools_pthread_key_create(&heap_key_, DestroyThreadCache); tsd_inited_ = true; - - // We may have used a fake pthread_t for the main thread. Fix it. - pthread_t zero; - memset(&zero, 0, sizeof(zero)); - SpinLockHolder h(Static::pageheap_lock()); - for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) { - if (h->tid_ == zero) { - h->tid_ = pthread_self(); - } - } } ThreadCache* ThreadCache::CreateCacheIfNecessary() { @@ -329,14 +319,17 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { ThreadCache* heap = NULL; { SpinLockHolder h(Static::pageheap_lock()); - - // Early on in glibc's life, we cannot even call pthread_self() - pthread_t me; - if (!tsd_inited_) { - memset(&me, 0, sizeof(me)); - } else { - me = pthread_self(); - } + // On very old libc's, this call may crash if it happens too + // early. No libc using NPTL should be affected. If there + // is a crash here, we could use code (on linux, at least) + // to detect NPTL vs LinuxThreads: + // http://www.redhat.com/archives/phil-list/2003-April/msg00038.html + // If we detect not-NPTL, we could execute the old code from + // http://google-perftools.googlecode.com/svn/tags/google-perftools-1.7/src/thread_cache.cc + // that avoids calling pthread_self too early. The problem with + // that code is it caused a race condition when tcmalloc is linked + // in statically and other libraries spawn threads before main. + const pthread_t me = pthread_self(); // This may be a recursive malloc call from pthread_setspecific() // In that case, the heap for this thread has already been created |