summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2011-03-21 21:41:55 +0000
committercsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2011-03-21 21:41:55 +0000
commit1d30e525ae6ac38ae381bb3118f7f47998af2942 (patch)
treea8349a4801e90c96515f3c0329a5a6c1813fc27f
parent6fe07cd2c0527e18276cc79a57e2212a4b048746 (diff)
downloadgperftools-1d30e525ae6ac38ae381bb3118f7f47998af2942.tar.gz
* Improve debugallocation tc_malloc_size (csilvers)
* Extend atomicops.h to use ARM V6+ optimized code (sanek) * Fix failure in Ranges test (ppluzhnikov) * Change malloc-hook to use a list-like structure (llib) * Update tcmalloc_regtest to use new malloc hooks (llib) * PARTIAL: Keep track of 'overhead' bytes in the page cache (csilvers) git-svn-id: http://gperftools.googlecode.com/svn/trunk@108 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rw-r--r--Makefile.am9
-rw-r--r--Makefile.in82
-rwxr-xr-xgoogle-perftools.sln5
-rw-r--r--src/base/atomicops-internals-arm-generic.h236
-rw-r--r--src/base/atomicops-internals-arm-v6plus.h244
-rw-r--r--src/base/atomicops.h8
-rw-r--r--src/central_freelist.cc16
-rw-r--r--src/central_freelist.h6
-rw-r--r--src/google/malloc_hook.h181
-rw-r--r--src/google/malloc_hook_c.h52
-rw-r--r--src/heap-checker-bcad.cc8
-rw-r--r--src/heap-checker.cc123
-rw-r--r--src/heap-profiler.cc76
-rw-r--r--src/malloc_hook-inl.h99
-rw-r--r--src/malloc_hook.cc450
-rw-r--r--src/memory_region_map.cc32
-rw-r--r--src/memory_region_map.h5
-rw-r--r--src/tcmalloc.cc37
-rw-r--r--src/tests/current_allocated_bytes_test.cc63
-rw-r--r--src/tests/low_level_alloc_unittest.cc16
-rw-r--r--src/tests/malloc_extension_c_test.c14
-rw-r--r--src/tests/tcmalloc_unittest.cc19
-rwxr-xr-xvsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj155
23 files changed, 1549 insertions, 387 deletions
diff --git a/Makefile.am b/Makefile.am
index 327d98a..e71e4c7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -258,6 +258,7 @@ LOW_LEVEL_ALLOC_UNITTEST_INCLUDES = src/base/low_level_alloc.h \
$(LOGGING_INCLUDES)
low_level_alloc_unittest_SOURCES = src/base/low_level_alloc.cc \
src/malloc_hook.cc \
+ src/maybe_threads.cc \
src/tests/low_level_alloc_unittest.cc \
$(LOW_LEVEL_ALLOC_UNITTEST_INCLUDES)
# By default, MallocHook takes stack traces for use by the heap-checker.
@@ -544,6 +545,14 @@ markidle_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(AM_CXXFLAGS)
markidle_unittest_LDFLAGS = $(PTHREAD_CFLAGS) $(TCMALLOC_FLAGS)
markidle_unittest_LDADD = $(LIBTCMALLOC_MINIMAL) $(PTHREAD_LIBS)
+TESTS += current_allocated_bytes_test
+WINDOWS_PROJECTS += vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj
+current_allocated_bytes_test_SOURCES = src/tests/current_allocated_bytes_test.cc \
+ src/config_for_unittests.h
+current_allocated_bytes_test_CXXFLAGS = $(PTHREAD_CFLAGS) $(AM_CXXFLAGS)
+current_allocated_bytes_test_LDFLAGS = $(PTHREAD_CFLAGS) $(TCMALLOC_FLAGS)
+current_allocated_bytes_test_LDADD = $(LIBTCMALLOC_MINIMAL) $(PTHREAD_LIBS)
+
TESTS += malloc_extension_test
WINDOWS_PROJECTS += vsprojects/malloc_extension_test/malloc_extension_test.vcproj
malloc_extension_test_SOURCES = src/tests/malloc_extension_test.cc \
diff --git a/Makefile.in b/Makefile.in
index 339f47f..8c534fa 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -766,8 +766,10 @@ am__EXEEXT_24 = low_level_alloc_unittest$(EXEEXT) \
tcmalloc_minimal_large_unittest$(EXEEXT) $(am__EXEEXT_10) \
addressmap_unittest$(EXEEXT) $(am__EXEEXT_11) \
packed_cache_test$(EXEEXT) frag_unittest$(EXEEXT) \
- markidle_unittest$(EXEEXT) malloc_extension_test$(EXEEXT) \
- $(am__EXEEXT_12) $(am__EXEEXT_13) page_heap_test$(EXEEXT) \
+ markidle_unittest$(EXEEXT) \
+ current_allocated_bytes_test$(EXEEXT) \
+ malloc_extension_test$(EXEEXT) $(am__EXEEXT_12) \
+ $(am__EXEEXT_13) page_heap_test$(EXEEXT) \
pagemap_unittest$(EXEEXT) realloc_unittest$(EXEEXT) \
stack_trace_table_test$(EXEEXT) \
thread_dealloc_unittest$(EXEEXT) $(am__EXEEXT_14) \
@@ -791,6 +793,14 @@ am_atomicops_unittest_OBJECTS = atomicops_unittest.$(OBJEXT) \
$(am__objects_6)
atomicops_unittest_OBJECTS = $(am_atomicops_unittest_OBJECTS)
atomicops_unittest_DEPENDENCIES = $(am__DEPENDENCIES_2)
+am_current_allocated_bytes_test_OBJECTS = current_allocated_bytes_test-current_allocated_bytes_test.$(OBJEXT)
+current_allocated_bytes_test_OBJECTS = \
+ $(am_current_allocated_bytes_test_OBJECTS)
+@MINGW_FALSE@am__DEPENDENCIES_5 = libtcmalloc_minimal.la
+@MINGW_TRUE@am__DEPENDENCIES_5 = libtcmalloc_minimal.la \
+@MINGW_TRUE@ libstacktrace.la
+current_allocated_bytes_test_DEPENDENCIES = $(am__DEPENDENCIES_5) \
+ $(am__DEPENDENCIES_1)
am__debugallocation_test_SOURCES_DIST = \
src/tests/debugallocation_test.cc
@WITH_DEBUGALLOC_TRUE@@WITH_STACK_TRACE_TRUE@am_debugallocation_test_OBJECTS = debugallocation_test-debugallocation_test.$(OBJEXT)
@@ -805,9 +815,6 @@ debugallocation_test_sh_OBJECTS = \
debugallocation_test_sh_LDADD = $(LDADD)
am_frag_unittest_OBJECTS = frag_unittest-frag_unittest.$(OBJEXT)
frag_unittest_OBJECTS = $(am_frag_unittest_OBJECTS)
-@MINGW_FALSE@am__DEPENDENCIES_5 = libtcmalloc_minimal.la
-@MINGW_TRUE@am__DEPENDENCIES_5 = libtcmalloc_minimal.la \
-@MINGW_TRUE@ libstacktrace.la
frag_unittest_DEPENDENCIES = $(am__DEPENDENCIES_5) \
$(am__DEPENDENCIES_1)
am__getpc_test_SOURCES_DIST = src/tests/getpc_test.cc src/getpc.h
@@ -894,7 +901,7 @@ heap_profiler_unittest_sh_OBJECTS = \
heap_profiler_unittest_sh_LDADD = $(LDADD)
am__low_level_alloc_unittest_SOURCES_DIST = \
src/base/low_level_alloc.cc src/malloc_hook.cc \
- src/tests/low_level_alloc_unittest.cc \
+ src/maybe_threads.cc src/tests/low_level_alloc_unittest.cc \
src/base/low_level_alloc.h src/base/basictypes.h \
src/google/malloc_hook.h src/google/malloc_hook_c.h \
src/malloc_hook-inl.h src/base/spinlock.h \
@@ -913,6 +920,7 @@ am__objects_29 = $(am__objects_1) $(am__objects_1)
am_low_level_alloc_unittest_OBJECTS = \
low_level_alloc_unittest-low_level_alloc.$(OBJEXT) \
low_level_alloc_unittest-malloc_hook.$(OBJEXT) \
+ low_level_alloc_unittest-maybe_threads.$(OBJEXT) \
low_level_alloc_unittest-low_level_alloc_unittest.$(OBJEXT) \
$(am__objects_29)
low_level_alloc_unittest_OBJECTS = \
@@ -1275,7 +1283,9 @@ SOURCES = $(liblogging_la_SOURCES) $(libprofiler_la_SOURCES) \
$(libtcmalloc_minimal_debug_la_SOURCES) \
$(libtcmalloc_minimal_internal_la_SOURCES) \
$(libwindows_la_SOURCES) $(addressmap_unittest_SOURCES) \
- $(atomicops_unittest_SOURCES) $(debugallocation_test_SOURCES) \
+ $(atomicops_unittest_SOURCES) \
+ $(current_allocated_bytes_test_SOURCES) \
+ $(debugallocation_test_SOURCES) \
$(debugallocation_test_sh_SOURCES) $(frag_unittest_SOURCES) \
$(getpc_test_SOURCES) \
$(heap_checker_death_unittest_sh_SOURCES) \
@@ -1329,6 +1339,7 @@ DIST_SOURCES = $(liblogging_la_SOURCES) \
$(am__libwindows_la_SOURCES_DIST) \
$(am__addressmap_unittest_SOURCES_DIST) \
$(atomicops_unittest_SOURCES) \
+ $(current_allocated_bytes_test_SOURCES) \
$(am__debugallocation_test_SOURCES_DIST) \
$(am__debugallocation_test_sh_SOURCES_DIST) \
$(frag_unittest_SOURCES) $(am__getpc_test_SOURCES_DIST) \
@@ -1714,6 +1725,7 @@ WINDOWS_PROJECTS = google-perftools.sln \
vsprojects/packed-cache_test/packed-cache_test.vcproj \
vsprojects/frag_unittest/frag_unittest.vcproj \
vsprojects/markidle_unittest/markidle_unittest.vcproj \
+ vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj \
vsprojects/malloc_extension_test/malloc_extension_test.vcproj \
vsprojects/page_heap_test/page_heap_test.vcproj \
vsprojects/pagemap_unittest/pagemap_unittest.vcproj \
@@ -1743,13 +1755,13 @@ TESTS = low_level_alloc_unittest atomicops_unittest $(am__append_11) \
tcmalloc_minimal_unittest tcmalloc_minimal_large_unittest \
$(am__append_15) addressmap_unittest $(am__append_18) \
packed_cache_test frag_unittest markidle_unittest \
- malloc_extension_test $(am__append_19) $(am__append_21) \
- page_heap_test pagemap_unittest realloc_unittest \
- stack_trace_table_test thread_dealloc_unittest \
- $(am__append_24) $(am__append_25) $(am__append_35) \
- $(am__append_40) $(am__append_43) $(am__append_50) \
- $(am__append_52) $(am__append_54) $(am__append_58) \
- $(am__append_63)
+ current_allocated_bytes_test malloc_extension_test \
+ $(am__append_19) $(am__append_21) page_heap_test \
+ pagemap_unittest realloc_unittest stack_trace_table_test \
+ thread_dealloc_unittest $(am__append_24) $(am__append_25) \
+ $(am__append_35) $(am__append_40) $(am__append_43) \
+ $(am__append_50) $(am__append_52) $(am__append_54) \
+ $(am__append_58) $(am__append_63)
# TESTS_ENVIRONMENT sets environment variables for when you run unittest.
# We always get "srcdir" set for free.
# We'll add to this later, on a library-by-library basis.
@@ -1863,6 +1875,7 @@ LOW_LEVEL_ALLOC_UNITTEST_INCLUDES = src/base/low_level_alloc.h \
low_level_alloc_unittest_SOURCES = src/base/low_level_alloc.cc \
src/malloc_hook.cc \
+ src/maybe_threads.cc \
src/tests/low_level_alloc_unittest.cc \
$(LOW_LEVEL_ALLOC_UNITTEST_INCLUDES)
@@ -2045,6 +2058,12 @@ markidle_unittest_SOURCES = src/tests/markidle_unittest.cc \
markidle_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(AM_CXXFLAGS)
markidle_unittest_LDFLAGS = $(PTHREAD_CFLAGS) $(TCMALLOC_FLAGS)
markidle_unittest_LDADD = $(LIBTCMALLOC_MINIMAL) $(PTHREAD_LIBS)
+current_allocated_bytes_test_SOURCES = src/tests/current_allocated_bytes_test.cc \
+ src/config_for_unittests.h
+
+current_allocated_bytes_test_CXXFLAGS = $(PTHREAD_CFLAGS) $(AM_CXXFLAGS)
+current_allocated_bytes_test_LDFLAGS = $(PTHREAD_CFLAGS) $(TCMALLOC_FLAGS)
+current_allocated_bytes_test_LDADD = $(LIBTCMALLOC_MINIMAL) $(PTHREAD_LIBS)
malloc_extension_test_SOURCES = src/tests/malloc_extension_test.cc \
src/config_for_unittests.h \
src/base/logging.h \
@@ -2606,6 +2625,9 @@ addressmap_unittest$(EXEEXT): $(addressmap_unittest_OBJECTS) $(addressmap_unitte
atomicops_unittest$(EXEEXT): $(atomicops_unittest_OBJECTS) $(atomicops_unittest_DEPENDENCIES)
@rm -f atomicops_unittest$(EXEEXT)
$(CXXLINK) $(atomicops_unittest_LDFLAGS) $(atomicops_unittest_OBJECTS) $(atomicops_unittest_LDADD) $(LIBS)
+current_allocated_bytes_test$(EXEEXT): $(current_allocated_bytes_test_OBJECTS) $(current_allocated_bytes_test_DEPENDENCIES)
+ @rm -f current_allocated_bytes_test$(EXEEXT)
+ $(CXXLINK) $(current_allocated_bytes_test_LDFLAGS) $(current_allocated_bytes_test_OBJECTS) $(current_allocated_bytes_test_LDADD) $(LIBS)
debugallocation_test$(EXEEXT): $(debugallocation_test_OBJECTS) $(debugallocation_test_DEPENDENCIES)
@rm -f debugallocation_test$(EXEEXT)
$(CXXLINK) $(debugallocation_test_LDFLAGS) $(debugallocation_test_OBJECTS) $(debugallocation_test_LDADD) $(LIBS)
@@ -2807,6 +2829,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addressmap_unittest-port.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomicops-internals-x86.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomicops_unittest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debugallocation_test-debugallocation_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamic_annotations.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frag_unittest-frag_unittest.Po@am__quote@
@@ -2873,6 +2896,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/low_level_alloc_unittest-low_level_alloc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/low_level_alloc_unittest-low_level_alloc_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/low_level_alloc_unittest-malloc_hook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_extension_c_test-malloc_extension_c_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_extension_debug_test-malloc_extension_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_extension_test-malloc_extension_test.Po@am__quote@
@@ -3531,6 +3555,20 @@ atomicops_unittest.obj: src/tests/atomicops_unittest.cc
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o atomicops_unittest.obj `if test -f 'src/tests/atomicops_unittest.cc'; then $(CYGPATH_W) 'src/tests/atomicops_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/tests/atomicops_unittest.cc'; fi`
+current_allocated_bytes_test-current_allocated_bytes_test.o: src/tests/current_allocated_bytes_test.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(current_allocated_bytes_test_CXXFLAGS) $(CXXFLAGS) -MT current_allocated_bytes_test-current_allocated_bytes_test.o -MD -MP -MF "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo" -c -o current_allocated_bytes_test-current_allocated_bytes_test.o `test -f 'src/tests/current_allocated_bytes_test.cc' || echo '$(srcdir)/'`src/tests/current_allocated_bytes_test.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo" "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Po"; else rm -f "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/tests/current_allocated_bytes_test.cc' object='current_allocated_bytes_test-current_allocated_bytes_test.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(current_allocated_bytes_test_CXXFLAGS) $(CXXFLAGS) -c -o current_allocated_bytes_test-current_allocated_bytes_test.o `test -f 'src/tests/current_allocated_bytes_test.cc' || echo '$(srcdir)/'`src/tests/current_allocated_bytes_test.cc
+
+current_allocated_bytes_test-current_allocated_bytes_test.obj: src/tests/current_allocated_bytes_test.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(current_allocated_bytes_test_CXXFLAGS) $(CXXFLAGS) -MT current_allocated_bytes_test-current_allocated_bytes_test.obj -MD -MP -MF "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo" -c -o current_allocated_bytes_test-current_allocated_bytes_test.obj `if test -f 'src/tests/current_allocated_bytes_test.cc'; then $(CYGPATH_W) 'src/tests/current_allocated_bytes_test.cc'; else $(CYGPATH_W) '$(srcdir)/src/tests/current_allocated_bytes_test.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo" "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Po"; else rm -f "$(DEPDIR)/current_allocated_bytes_test-current_allocated_bytes_test.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/tests/current_allocated_bytes_test.cc' object='current_allocated_bytes_test-current_allocated_bytes_test.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(current_allocated_bytes_test_CXXFLAGS) $(CXXFLAGS) -c -o current_allocated_bytes_test-current_allocated_bytes_test.obj `if test -f 'src/tests/current_allocated_bytes_test.cc'; then $(CYGPATH_W) 'src/tests/current_allocated_bytes_test.cc'; else $(CYGPATH_W) '$(srcdir)/src/tests/current_allocated_bytes_test.cc'; fi`
+
debugallocation_test-debugallocation_test.o: src/tests/debugallocation_test.cc
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(debugallocation_test_CXXFLAGS) $(CXXFLAGS) -MT debugallocation_test-debugallocation_test.o -MD -MP -MF "$(DEPDIR)/debugallocation_test-debugallocation_test.Tpo" -c -o debugallocation_test-debugallocation_test.o `test -f 'src/tests/debugallocation_test.cc' || echo '$(srcdir)/'`src/tests/debugallocation_test.cc; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/debugallocation_test-debugallocation_test.Tpo" "$(DEPDIR)/debugallocation_test-debugallocation_test.Po"; else rm -f "$(DEPDIR)/debugallocation_test-debugallocation_test.Tpo"; exit 1; fi
@@ -3657,6 +3695,20 @@ low_level_alloc_unittest-malloc_hook.obj: src/malloc_hook.cc
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -c -o low_level_alloc_unittest-malloc_hook.obj `if test -f 'src/malloc_hook.cc'; then $(CYGPATH_W) 'src/malloc_hook.cc'; else $(CYGPATH_W) '$(srcdir)/src/malloc_hook.cc'; fi`
+low_level_alloc_unittest-maybe_threads.o: src/maybe_threads.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -MT low_level_alloc_unittest-maybe_threads.o -MD -MP -MF "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo" -c -o low_level_alloc_unittest-maybe_threads.o `test -f 'src/maybe_threads.cc' || echo '$(srcdir)/'`src/maybe_threads.cc; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo" "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Po"; else rm -f "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/maybe_threads.cc' object='low_level_alloc_unittest-maybe_threads.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -c -o low_level_alloc_unittest-maybe_threads.o `test -f 'src/maybe_threads.cc' || echo '$(srcdir)/'`src/maybe_threads.cc
+
+low_level_alloc_unittest-maybe_threads.obj: src/maybe_threads.cc
+@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -MT low_level_alloc_unittest-maybe_threads.obj -MD -MP -MF "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo" -c -o low_level_alloc_unittest-maybe_threads.obj `if test -f 'src/maybe_threads.cc'; then $(CYGPATH_W) 'src/maybe_threads.cc'; else $(CYGPATH_W) '$(srcdir)/src/maybe_threads.cc'; fi`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo" "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Po"; else rm -f "$(DEPDIR)/low_level_alloc_unittest-maybe_threads.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/maybe_threads.cc' object='low_level_alloc_unittest-maybe_threads.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -c -o low_level_alloc_unittest-maybe_threads.obj `if test -f 'src/maybe_threads.cc'; then $(CYGPATH_W) 'src/maybe_threads.cc'; else $(CYGPATH_W) '$(srcdir)/src/maybe_threads.cc'; fi`
+
low_level_alloc_unittest-low_level_alloc_unittest.o: src/tests/low_level_alloc_unittest.cc
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(low_level_alloc_unittest_CXXFLAGS) $(CXXFLAGS) -MT low_level_alloc_unittest-low_level_alloc_unittest.o -MD -MP -MF "$(DEPDIR)/low_level_alloc_unittest-low_level_alloc_unittest.Tpo" -c -o low_level_alloc_unittest-low_level_alloc_unittest.o `test -f 'src/tests/low_level_alloc_unittest.cc' || echo '$(srcdir)/'`src/tests/low_level_alloc_unittest.cc; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/low_level_alloc_unittest-low_level_alloc_unittest.Tpo" "$(DEPDIR)/low_level_alloc_unittest-low_level_alloc_unittest.Po"; else rm -f "$(DEPDIR)/low_level_alloc_unittest-low_level_alloc_unittest.Tpo"; exit 1; fi
@@ -4576,7 +4628,7 @@ check-TESTS: $(TESTS)
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
- $(mkdir_p) $(distdir)/$(top_srcdir) $(distdir)/doc $(distdir)/m4 $(distdir)/packages $(distdir)/packages/rpm $(distdir)/src $(distdir)/src/google $(distdir)/src/solaris $(distdir)/src/tests $(distdir)/src/windows $(distdir)/src/windows/google $(distdir)/vsprojects/addr2line-pdb $(distdir)/vsprojects/addressmap_unittest $(distdir)/vsprojects/frag_unittest $(distdir)/vsprojects/libtcmalloc_minimal $(distdir)/vsprojects/low_level_alloc_unittest $(distdir)/vsprojects/malloc_extension_test $(distdir)/vsprojects/markidle_unittest $(distdir)/vsprojects/nm-pdb $(distdir)/vsprojects/packed-cache_test $(distdir)/vsprojects/page_heap_test $(distdir)/vsprojects/pagemap_unittest $(distdir)/vsprojects/realloc_unittest $(distdir)/vsprojects/sampler_test $(distdir)/vsprojects/stack_trace_table_test $(distdir)/vsprojects/tcmalloc_minimal_large $(distdir)/vsprojects/tcmalloc_minimal_unittest $(distdir)/vsprojects/thread_dealloc_unittest $(distdir)/vsprojects/tmu-static
+ $(mkdir_p) $(distdir)/$(top_srcdir) $(distdir)/doc $(distdir)/m4 $(distdir)/packages $(distdir)/packages/rpm $(distdir)/src $(distdir)/src/google $(distdir)/src/solaris $(distdir)/src/tests $(distdir)/src/windows $(distdir)/src/windows/google $(distdir)/vsprojects/addr2line-pdb $(distdir)/vsprojects/addressmap_unittest $(distdir)/vsprojects/current_allocated_bytes_test $(distdir)/vsprojects/frag_unittest $(distdir)/vsprojects/libtcmalloc_minimal $(distdir)/vsprojects/low_level_alloc_unittest $(distdir)/vsprojects/malloc_extension_test $(distdir)/vsprojects/markidle_unittest $(distdir)/vsprojects/nm-pdb $(distdir)/vsprojects/packed-cache_test $(distdir)/vsprojects/page_heap_test $(distdir)/vsprojects/pagemap_unittest $(distdir)/vsprojects/realloc_unittest $(distdir)/vsprojects/sampler_test $(distdir)/vsprojects/stack_trace_table_test $(distdir)/vsprojects/tcmalloc_minimal_large $(distdir)/vsprojects/tcmalloc_minimal_unittest $(distdir)/vsprojects/thread_dealloc_unittest $(distdir)/vsprojects/tmu-static
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
diff --git a/google-perftools.sln b/google-perftools.sln
index 7aecbe9..da5f8ab 100755
--- a/google-perftools.sln
+++ b/google-perftools.sln
@@ -36,6 +36,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "markidle_unittest", "vsproj
{55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F} = {55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "current_allocated_bytes_test", "vsprojects\current_allocated_bytes_test\current_allocated_bytes_test.vcproj", "{4AFFF21D-9D0A-410C-A7DB-7D21DA5166C0}"
+ ProjectSection(ProjectDependencies) = postProject
+ {55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F} = {55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F}
+ EndProjectSection
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "packed-cache_test", "vsprojects\packed-cache_test\packed-cache_test.vcproj", "{605D3CED-B530-424E-B7D2-2A31F14FD570}"
ProjectSection(ProjectDependencies) = postProject
{55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F} = {55E2B3AE-3CA1-4DB6-97F7-0A044D6F446F}
diff --git a/src/base/atomicops-internals-arm-generic.h b/src/base/atomicops-internals-arm-generic.h
new file mode 100644
index 0000000..7882b0d
--- /dev/null
+++ b/src/base/atomicops-internals-arm-generic.h
@@ -0,0 +1,236 @@
+// Copyright (c) 2003, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// ---
+//
+// Author: Lei Zhang, Sasha Levitskiy
+//
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+//
+// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_
+#define BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "base/macros.h" // For COMPILE_ASSERT
+#include "base/port.h" // ATTRIBUTE_WEAK
+
+typedef int32_t Atomic32;
+
+namespace base {
+namespace subtle {
+
+typedef int64_t Atomic64;
+
+// 0xffff0fc0 is the hard coded address of a function provided by
+// the kernel which implements an atomic compare-exchange. On older
+// ARM architecture revisions (pre-v6) this may be implemented using
+// a syscall. This address is stable, and in active use (hard coded)
+// by at least glibc-2.7 and the Android C library.
+// pLinuxKernelCmpxchg has both acquire and release barrier sematincs.
+typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
+ Atomic32 new_value,
+ volatile Atomic32* ptr);
+LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg ATTRIBUTE_WEAK =
+ (LinuxKernelCmpxchgFunc) 0xffff0fc0;
+
+typedef void (*LinuxKernelMemoryBarrierFunc)(void);
+LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier ATTRIBUTE_WEAK =
+ (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
+
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev_value = *ptr;
+ do {
+ if (!pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 old_value;
+ do {
+ old_value = *ptr;
+ } while (pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr)));
+ return old_value;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ for (;;) {
+ // Atomic exchange the old value with an incremented one.
+ Atomic32 old_value = *ptr;
+ Atomic32 new_value = old_value + increment;
+ if (pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr)) == 0) {
+ // The exchange took place as expected.
+ return new_value;
+ }
+ // Otherwise, *ptr changed mid-loop and we need to retry.
+ }
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void MemoryBarrier() {
+ pLinuxKernelMemoryBarrier();
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+
+// 64-bit versions are not implemented yet.
+
+inline void NotImplementedFatalError(const char *function_name) {
+ fprintf(stderr, "64-bit %s() not implemented on this platform\n",
+ function_name);
+ abort();
+}
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("NoBarrier_CompareAndSwap");
+ return 0;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ NotImplementedFatalError("NoBarrier_AtomicExchange");
+ return 0;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ NotImplementedFatalError("NoBarrier_AtomicIncrement");
+ return 0;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ NotImplementedFatalError("Barrier_AtomicIncrement");
+ return 0;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("NoBarrier_Store");
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("Acquire_Store64");
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("Release_Store");
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("NoBarrier_Load");
+ return 0;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("Atomic64 Acquire_Load");
+ return 0;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("Atomic64 Release_Load");
+ return 0;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("Atomic64 Acquire_CompareAndSwap");
+ return 0;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("Atomic64 Release_CompareAndSwap");
+ return 0;
+}
+
+} // namespace base::subtle
+} // namespace base
+
+#endif // BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_
diff --git a/src/base/atomicops-internals-arm-v6plus.h b/src/base/atomicops-internals-arm-v6plus.h
new file mode 100644
index 0000000..ee09f32
--- /dev/null
+++ b/src/base/atomicops-internals-arm-v6plus.h
@@ -0,0 +1,244 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// ---
+//
+// Author: Sasha Levitskiy
+// based on atomicops-internals by Sanjay Ghemawat
+//
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+//
+// This code implements ARM atomics for architectures V6 and newer.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
+#define BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "base/basictypes.h" // For COMPILE_ASSERT
+
+typedef int32_t Atomic32;
+
+namespace base {
+namespace subtle {
+
+typedef int64_t Atomic64;
+
+// 32-bit low-level ops
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 oldval, res;
+ do {
+ __asm__ __volatile__(
+ "ldrex %1, [%3]\n"
+ "mov %0, #0\n"
+ "teq %1, %4\n"
+ "strexeq %0, %5, [%3]\n"
+ : "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
+ : "r" (ptr), "Ir" (old_value), "r" (new_value)
+ : "cc");
+ } while (res);
+ return oldval;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 tmp, old;
+ __asm__ __volatile__(
+ "1:\n"
+ "ldrex %1, [%2]\n"
+ "strex %0, %3, [%2]\n"
+ "teq %0, #0\n"
+ "bne 1b"
+ : "=&r" (tmp), "=&r" (old)
+ : "r" (ptr), "r" (new_value)
+ : "cc", "memory");
+ return old;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 tmp, res;
+ __asm__ __volatile__(
+ "1:\n"
+ "ldrex %1, [%2]\n"
+ "add %1, %1, %3\n"
+ "strex %0, %1, [%2]\n"
+ "teq %0, #0\n"
+ "bne 1b"
+ : "=&r" (tmp), "=&r"(res)
+ : "r" (ptr), "r"(increment)
+ : "cc", "memory");
+ return res;
+}
+
+inline void MemoryBarrier() {
+ __asm__ __volatile__("dmb" : : : "memory");
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 tmp, res;
+ __asm__ __volatile__(
+ "1:\n"
+ "ldrex %1, [%2]\n"
+ "add %1, %1, %3\n"
+ "dmb\n"
+ "strex %0, %1, [%2]\n"
+ "teq %0, #0\n"
+ "bne 1b"
+ : "=&r" (tmp), "=&r"(res)
+ : "r" (ptr), "r"(increment)
+ : "cc", "memory");
+ return res;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 value = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ MemoryBarrier();
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+// 64-bit versions are not implemented yet.
+
+inline void NotImplementedFatalError(const char *function_name) {
+ fprintf(stderr, "64-bit %s() not implemented on this platform\n",
+ function_name);
+ abort();
+}
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("NoBarrier_CompareAndSwap");
+ return 0;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ NotImplementedFatalError("NoBarrier_AtomicExchange");
+ return 0;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ NotImplementedFatalError("NoBarrier_AtomicIncrement");
+ return 0;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ NotImplementedFatalError("Barrier_AtomicIncrement");
+ return 0;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("NoBarrier_Store");
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("Acquire_Store64");
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NotImplementedFatalError("Release_Store");
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("NoBarrier_Load");
+ return 0;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("Atomic64 Acquire_Load");
+ return 0;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ NotImplementedFatalError("Atomic64 Release_Load");
+ return 0;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("Atomic64 Acquire_CompareAndSwap");
+ return 0;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ NotImplementedFatalError("Atomic64 Release_CompareAndSwap");
+ return 0;
+}
+
+} // namespace subtle ends
+} // namespace base ends
+
+#endif // BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
diff --git a/src/base/atomicops.h b/src/base/atomicops.h
index ec60489..f683766 100644
--- a/src/base/atomicops.h
+++ b/src/base/atomicops.h
@@ -86,11 +86,15 @@
// TODO(csilvers): figure out ARCH_PIII/ARCH_K8 (perhaps via ./configure?)
// ------------------------------------------------------------------------
+#include "base/arm_instruction_set_select.h"
+
// TODO(csilvers): match piii, not just __i386. Also, match k8
#if defined(__MACH__) && defined(__APPLE__)
#include "base/atomicops-internals-macosx.h"
-#elif defined(__GNUC__) && defined(__ARM_ARCH_5T__)
-#include "base/atomicops-internals-arm-gcc.h"
+#elif defined(__GNUC__) && defined(ARMV6)
+#include "base/atomicops-internals-arm-v6plus.h"
+#elif defined(ARMV3)
+#include "base/atomicops-internals-arm-generic.h"
#elif defined(_MSC_VER) && defined(_M_IX86)
#include "base/atomicops-internals-x86-msvc.h"
#elif defined(__MINGW32__) && defined(__i386__)
diff --git a/src/central_freelist.cc b/src/central_freelist.cc
index da498e6..7fe8c4f 100644
--- a/src/central_freelist.cc
+++ b/src/central_freelist.cc
@@ -323,4 +323,20 @@ int CentralFreeList::tc_length() {
return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_);
}
+size_t CentralFreeList::OverheadBytes() {
+ SpinLockHolder h(&lock_);
+ size_t overhead = 0;
+ for (const Span* s = empty_.next; s != &empty_; s = s->next) {
+ ASSERT(size_class_ == s->sizeclass);
+ ASSERT(size_class_ != 0);
+ overhead += (s->length * kPageSize) % size_class_;
+ }
+ for (const Span* s = nonempty_.next; s != &nonempty_; s = s->next) {
+ ASSERT(size_class_ == s->sizeclass);
+ ASSERT(size_class_ != 0);
+ overhead += (s->length * kPageSize) % size_class_;
+ }
+ return overhead;
+}
+
} // namespace tcmalloc
diff --git a/src/central_freelist.h b/src/central_freelist.h
index 2e6a31b..a520f68 100644
--- a/src/central_freelist.h
+++ b/src/central_freelist.h
@@ -64,6 +64,12 @@ class CentralFreeList {
// Returns the number of free objects in the transfer cache.
int tc_length();
+ // Returns the memory overhead (internal fragmentation) attributable
+ // to the freelist. This is memory lost when the size of elements
+ // in a freelist doesn't exactly divide the page-size (a 8192-byte
+ // page full of 5-byte objects would have 2 bytes memory overhead).
+ size_t OverheadBytes();
+
private:
// TransferCache is used to cache transfers of
// sizemap.num_objects_to_move(size_class) back and forth between
diff --git a/src/google/malloc_hook.h b/src/google/malloc_hook.h
index 48d92da..16d9075 100644
--- a/src/google/malloc_hook.h
+++ b/src/google/malloc_hook.h
@@ -30,31 +30,35 @@
// ---
// Author: Sanjay Ghemawat
//
-// Some of our malloc implementations can invoke the following hooks
-// whenever memory is allocated or deallocated. If the hooks are
-// NULL, they are not invoked. MallocHook is thread-safe, and things
-// you do before calling SetFooHook(MyHook) are visible to any
-// resulting calls to MyHook. Hooks must be thread-safe, and if you
-// write:
+// Some of our malloc implementations can invoke the following hooks whenever
+// memory is allocated or deallocated. MallocHook is thread-safe, and things
+// you do before calling AddFooHook(MyHook) are visible to any resulting calls
+// to MyHook. Hooks must be thread-safe. If you write:
//
-// MallocHook::NewHook old_new_hook_ = NULL;
-// ...
-// old_new_hook_ = MallocHook::SetNewHook(&MyNewHook);
+// CHECK(MallocHook::AddNewHook(&MyNewHook));
//
-// old_new_hook_ could still be NULL the first couple times MyNewHook
-// is called.
+// MyNewHook will be invoked in subsequent calls in the current thread, but
+// there are no guarantees on when it might be invoked in other threads.
+//
+// There are a limited number of slots available for each hook type. Add*Hook
+// will return false if there are no slots available. Remove*Hook will return
+// false if the given hook was not already installed.
+//
+// The order in which individual hooks are called in Invoke*Hook is undefined.
+//
+// It is safe for a hook to remove itself within Invoke*Hook and add other
+// hooks. Any hooks added inside a hook invocation (for the same hook type)
+// will not be invoked for the current invocation.
//
// One important user of these hooks is the heap profiler.
//
-// CAVEAT: If you add new MallocHook::Invoke* calls (not for chaining hooks),
-// then those calls must be directly in the code of the (de)allocation
-// function that is provided to the user and that function must have
-// an ATTRIBUTE_SECTION(malloc_hook) attribute.
+// CAVEAT: If you add new MallocHook::Invoke* calls then those calls must be
+// directly in the code of the (de)allocation function that is provided to the
+// user and that function must have an ATTRIBUTE_SECTION(malloc_hook) attribute.
//
-// Note: Get*Hook() and Invoke*Hook() functions are defined in
-// malloc_hook-inl.h. If you need to get or invoke a hook (which you
-// shouldn't unless you're part of tcmalloc), be sure to #include
-// malloc_hook-inl.h in addition to malloc_hook.h.
+// Note: the Invoke*Hook() functions are defined in malloc_hook-inl.h. If you
+// need to invoke a hook (which you shouldn't unless you're part of tcmalloc),
+// be sure to #include malloc_hook-inl.h in addition to malloc_hook.h.
//
// NOTE FOR C USERS: If you want to use malloc_hook functionality from
// a C program, #include malloc_hook_c.h instead of this file.
@@ -82,27 +86,31 @@ extern "C" {
#endif
// Note: malloc_hook_c.h defines MallocHook_*Hook and
-// MallocHook_Set*Hook. The version of these inside the MallocHook
-// class are defined in terms of the malloc_hook_c version. See
-// malloc_hook_c.h for details of these types/functions.
+// MallocHook_{Add,Remove}*Hook. The version of these inside the MallocHook
+// class are defined in terms of the malloc_hook_c version. See malloc_hook_c.h
+// for details of these types/functions.
class PERFTOOLS_DLL_DECL MallocHook {
public:
// The NewHook is invoked whenever an object is allocated.
// It may be passed NULL if the allocator returned NULL.
typedef MallocHook_NewHook NewHook;
- inline static NewHook GetNewHook();
- inline static NewHook SetNewHook(NewHook hook) {
- return MallocHook_SetNewHook(hook);
+ inline static bool AddNewHook(NewHook hook) {
+ return MallocHook_AddNewHook(hook);
+ }
+ inline static bool RemoveNewHook(NewHook hook) {
+ return MallocHook_RemoveNewHook(hook);
}
inline static void InvokeNewHook(const void* p, size_t s);
// The DeleteHook is invoked whenever an object is deallocated.
// It may be passed NULL if the caller is trying to delete NULL.
typedef MallocHook_DeleteHook DeleteHook;
- inline static DeleteHook GetDeleteHook();
- inline static DeleteHook SetDeleteHook(DeleteHook hook) {
- return MallocHook_SetDeleteHook(hook);
+ inline static bool AddDeleteHook(DeleteHook hook) {
+ return MallocHook_AddDeleteHook(hook);
+ }
+ inline static bool RemoveDeleteHook(DeleteHook hook) {
+ return MallocHook_RemoveDeleteHook(hook);
}
inline static void InvokeDeleteHook(const void* p);
@@ -111,9 +119,11 @@ class PERFTOOLS_DLL_DECL MallocHook {
// in memory limited contexts, to catch allocations that will exceed
// a memory limit, and take outside actions to increase that limit.
typedef MallocHook_PreMmapHook PreMmapHook;
- inline static PreMmapHook GetPreMmapHook();
- inline static PreMmapHook SetPreMmapHook(PreMmapHook hook) {
- return MallocHook_SetPreMmapHook(hook);
+ inline static bool AddPreMmapHook(PreMmapHook hook) {
+ return MallocHook_AddPreMmapHook(hook);
+ }
+ inline static bool RemovePreMmapHook(PreMmapHook hook) {
+ return MallocHook_RemovePreMmapHook(hook);
}
inline static void InvokePreMmapHook(const void* start,
size_t size,
@@ -125,9 +135,11 @@ class PERFTOOLS_DLL_DECL MallocHook {
// The MmapHook is invoked whenever a region of memory is mapped.
// It may be passed MAP_FAILED if the mmap failed.
typedef MallocHook_MmapHook MmapHook;
- inline static MmapHook GetMmapHook();
- inline static MmapHook SetMmapHook(MmapHook hook) {
- return MallocHook_SetMmapHook(hook);
+ inline static bool AddMmapHook(MmapHook hook) {
+ return MallocHook_AddMmapHook(hook);
+ }
+ inline static bool RemoveMmapHook(MmapHook hook) {
+ return MallocHook_RemoveMmapHook(hook);
}
inline static void InvokeMmapHook(const void* result,
const void* start,
@@ -139,17 +151,21 @@ class PERFTOOLS_DLL_DECL MallocHook {
// The MunmapHook is invoked whenever a region of memory is unmapped.
typedef MallocHook_MunmapHook MunmapHook;
- inline static MunmapHook GetMunmapHook();
- inline static MunmapHook SetMunmapHook(MunmapHook hook) {
- return MallocHook_SetMunmapHook(hook);
+ inline static bool AddMunmapHook(MunmapHook hook) {
+ return MallocHook_AddMunmapHook(hook);
+ }
+ inline static bool RemoveMunmapHook(MunmapHook hook) {
+ return MallocHook_RemoveMunmapHook(hook);
}
inline static void InvokeMunmapHook(const void* p, size_t size);
// The MremapHook is invoked whenever a region of memory is remapped.
typedef MallocHook_MremapHook MremapHook;
- inline static MremapHook GetMremapHook();
- inline static MremapHook SetMremapHook(MremapHook hook) {
- return MallocHook_SetMremapHook(hook);
+ inline static bool AddMremapHook(MremapHook hook) {
+ return MallocHook_AddMremapHook(hook);
+ }
+ inline static bool RemoveMremapHook(MremapHook hook) {
+ return MallocHook_RemoveMremapHook(hook);
}
inline static void InvokeMremapHook(const void* result,
const void* old_addr,
@@ -165,9 +181,11 @@ class PERFTOOLS_DLL_DECL MallocHook {
// to catch allocations that will exceed the limit and take outside
// actions to increase such a limit.
typedef MallocHook_PreSbrkHook PreSbrkHook;
- inline static PreSbrkHook GetPreSbrkHook();
- inline static PreSbrkHook SetPreSbrkHook(PreSbrkHook hook) {
- return MallocHook_SetPreSbrkHook(hook);
+ inline static bool AddPreSbrkHook(PreSbrkHook hook) {
+ return MallocHook_AddPreSbrkHook(hook);
+ }
+ inline static bool RemovePreSbrkHook(PreSbrkHook hook) {
+ return MallocHook_RemovePreSbrkHook(hook);
}
inline static void InvokePreSbrkHook(ptrdiff_t increment);
@@ -176,9 +194,11 @@ class PERFTOOLS_DLL_DECL MallocHook {
// to get the top of the memory stack, and is not actually a
// memory-allocation call.
typedef MallocHook_SbrkHook SbrkHook;
- inline static SbrkHook GetSbrkHook();
- inline static SbrkHook SetSbrkHook(SbrkHook hook) {
- return MallocHook_SetSbrkHook(hook);
+ inline static bool AddSbrkHook(SbrkHook hook) {
+ return MallocHook_AddSbrkHook(hook);
+ }
+ inline static bool RemoveSbrkHook(SbrkHook hook) {
+ return MallocHook_RemoveSbrkHook(hook);
}
inline static void InvokeSbrkHook(const void* result, ptrdiff_t increment);
@@ -197,6 +217,75 @@ class PERFTOOLS_DLL_DECL MallocHook {
static void* UnhookedMMap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
static int UnhookedMUnmap(void *start, size_t length);
+
+ // The following are DEPRECATED.
+ inline static NewHook GetNewHook();
+ inline static NewHook SetNewHook(NewHook hook) {
+ return MallocHook_SetNewHook(hook);
+ }
+
+ inline static DeleteHook GetDeleteHook();
+ inline static DeleteHook SetDeleteHook(DeleteHook hook) {
+ return MallocHook_SetDeleteHook(hook);
+ }
+
+ inline static PreMmapHook GetPreMmapHook();
+ inline static PreMmapHook SetPreMmapHook(PreMmapHook hook) {
+ return MallocHook_SetPreMmapHook(hook);
+ }
+
+ inline static MmapHook GetMmapHook();
+ inline static MmapHook SetMmapHook(MmapHook hook) {
+ return MallocHook_SetMmapHook(hook);
+ }
+
+ inline static MunmapHook GetMunmapHook();
+ inline static MunmapHook SetMunmapHook(MunmapHook hook) {
+ return MallocHook_SetMunmapHook(hook);
+ }
+
+ inline static MremapHook GetMremapHook();
+ inline static MremapHook SetMremapHook(MremapHook hook) {
+ return MallocHook_SetMremapHook(hook);
+ }
+
+ inline static PreSbrkHook GetPreSbrkHook();
+ inline static PreSbrkHook SetPreSbrkHook(PreSbrkHook hook) {
+ return MallocHook_SetPreSbrkHook(hook);
+ }
+
+ inline static SbrkHook GetSbrkHook();
+ inline static SbrkHook SetSbrkHook(SbrkHook hook) {
+ return MallocHook_SetSbrkHook(hook);
+ }
+ // End of DEPRECATED methods.
+
+ private:
+ // Slow path versions of Invoke*Hook.
+ static void InvokeNewHookSlow(const void* p, size_t s);
+ static void InvokeDeleteHookSlow(const void* p);
+ static void InvokePreMmapHookSlow(const void* start,
+ size_t size,
+ int protection,
+ int flags,
+ int fd,
+ off_t offset);
+ static void InvokeMmapHookSlow(const void* result,
+ const void* start,
+ size_t size,
+ int protection,
+ int flags,
+ int fd,
+ off_t offset);
+ static void InvokeMunmapHookSlow(const void* p, size_t size);
+ static void InvokeMremapHookSlow(const void* result,
+ const void* old_addr,
+ size_t old_size,
+ size_t new_size,
+ int flags,
+ const void* new_addr);
+ static void InvokePreSbrkHookSlow(ptrdiff_t increment);
+ static void InvokeSbrkHookSlow(const void* result, ptrdiff_t increment);
};
#endif /* _MALLOC_HOOK_H_ */
diff --git a/src/google/malloc_hook_c.h b/src/google/malloc_hook_c.h
index 51ccc95..420cd33 100644
--- a/src/google/malloc_hook_c.h
+++ b/src/google/malloc_hook_c.h
@@ -63,14 +63,21 @@ PERFTOOLS_DLL_DECL
int MallocHook_GetCallerStackTrace(void** result, int max_depth,
int skip_count);
+/* The MallocHook_{Add,Remove}*Hook functions return 1 on success and 0 on
+ * failure.
+ */
typedef void (*MallocHook_NewHook)(const void* ptr, size_t size);
PERFTOOLS_DLL_DECL
-MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook);
+int MallocHook_AddNewHook(MallocHook_NewHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveNewHook(MallocHook_NewHook hook);
typedef void (*MallocHook_DeleteHook)(const void* ptr);
PERFTOOLS_DLL_DECL
-MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook);
+int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook);
typedef void (*MallocHook_PreMmapHook)(const void *start,
size_t size,
@@ -79,7 +86,9 @@ typedef void (*MallocHook_PreMmapHook)(const void *start,
int fd,
off_t offset);
PERFTOOLS_DLL_DECL
-MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook);
+int MallocHook_AddPreMmapHook(MallocHook_PreMmapHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemovePreMmapHook(MallocHook_PreMmapHook hook);
typedef void (*MallocHook_MmapHook)(const void* result,
const void* start,
@@ -89,11 +98,15 @@ typedef void (*MallocHook_MmapHook)(const void* result,
int fd,
off_t offset);
PERFTOOLS_DLL_DECL
-MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook);
+int MallocHook_AddMmapHook(MallocHook_MmapHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveMmapHook(MallocHook_MmapHook hook);
typedef void (*MallocHook_MunmapHook)(const void* ptr, size_t size);
PERFTOOLS_DLL_DECL
-MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook);
+int MallocHook_AddMunmapHook(MallocHook_MunmapHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveMunmapHook(MallocHook_MunmapHook hook);
typedef void (*MallocHook_MremapHook)(const void* result,
const void* old_addr,
@@ -102,15 +115,40 @@ typedef void (*MallocHook_MremapHook)(const void* result,
int flags,
const void* new_addr);
PERFTOOLS_DLL_DECL
-MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook);
+int MallocHook_AddMremapHook(MallocHook_MremapHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveMremapHook(MallocHook_MremapHook hook);
typedef void (*MallocHook_PreSbrkHook)(ptrdiff_t increment);
PERFTOOLS_DLL_DECL
-MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook);
+int MallocHook_AddPreSbrkHook(MallocHook_PreSbrkHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemovePreSbrkHook(MallocHook_PreSbrkHook hook);
typedef void (*MallocHook_SbrkHook)(const void* result, ptrdiff_t increment);
PERFTOOLS_DLL_DECL
+int MallocHook_AddSbrkHook(MallocHook_SbrkHook hook);
+PERFTOOLS_DLL_DECL
+int MallocHook_RemoveSbrkHook(MallocHook_SbrkHook hook);
+
+/* The following are DEPRECATED. */
+PERFTOOLS_DLL_DECL
+MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook);
+PERFTOOLS_DLL_DECL
+MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook);
+PERFTOOLS_DLL_DECL
MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook);
+/* End of DEPRECATED functions. */
#ifdef __cplusplus
} // extern "C"
diff --git a/src/heap-checker-bcad.cc b/src/heap-checker-bcad.cc
index 87d3d87..82a3109 100644
--- a/src/heap-checker-bcad.cc
+++ b/src/heap-checker-bcad.cc
@@ -51,7 +51,6 @@
// sure this file is not optimized out by the linker.
bool heap_leak_checker_bcad_variable;
-extern void HeapLeakChecker_BeforeConstructors(); // in heap-checker.cc
extern void HeapLeakChecker_AfterDestructors(); // in heap-checker.cc
// A helper class to ensure that some components of heap leak checking
@@ -61,7 +60,12 @@ class HeapLeakCheckerGlobalPrePost {
public:
HeapLeakCheckerGlobalPrePost() {
if (count_ == 0) {
- HeapLeakChecker_BeforeConstructors();
+ // The 'new int' will ensure that we have run an initial malloc
+ // hook, which will set up the heap checker via
+ // MallocHook_InitAtFirstAllocation_HeapLeakChecker. See malloc_hook.cc.
+ // This is done in this roundabout fashion in order to avoid self-deadlock
+ // if we directly called HeapLeakChecker_BeforeConstructors here.
+ delete new int;
// This needs to be called before the first allocation of an STL
// object, but after libc is done setting up threads (because it
// calls setenv, which requires a thread-aware errno). By
diff --git a/src/heap-checker.cc b/src/heap-checker.cc
index 1794e8b..6fb2a62 100644
--- a/src/heap-checker.cc
+++ b/src/heap-checker.cc
@@ -1699,18 +1699,6 @@ bool HeapLeakChecker::DoNoLeaks(ShouldSymbolize should_symbolize) {
MemoryRegionMap::LockHolder ml;
int a_local_var; // Use our stack ptr to make stack data live:
- // Sanity check that nobody is messing with the hooks we need:
- // Important to have it here: else we can misteriously SIGSEGV
- // in IgnoreLiveObjectsLocked inside ListAllProcessThreads's callback
- // by looking into a region that got unmapped w/o our knowledge.
- MemoryRegionMap::CheckMallocHooks();
- if (MallocHook::GetNewHook() != NewHook ||
- MallocHook::GetDeleteHook() != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client? "
- "Use --heap_check=\"\" to avoid this conflict.");
- }
-
// Make the heap profile, other threads are locked out.
HeapProfileTable::Snapshot* base =
reinterpret_cast<HeapProfileTable::Snapshot*>(start_snapshot_);
@@ -2126,98 +2114,15 @@ void HeapLeakChecker::CancelGlobalCheck() {
}
}
-//----------------------------------------------------------------------
-// HeapLeakChecker global constructor/destructor ordering components
-//----------------------------------------------------------------------
-
-#ifdef HAVE___ATTRIBUTE__ // we need __attribute__((weak)) for this to work
-#define INSTALLED_INITIAL_MALLOC_HOOKS
-
-void HeapLeakChecker_BeforeConstructors(); // below
-static bool in_initial_malloc_hook = false;
-
-// Helper for InitialMallocHook_* below
-static inline void InitHeapLeakCheckerFromMallocHook() {
- { SpinLockHolder l(&heap_checker_lock);
- RAW_CHECK(!in_initial_malloc_hook,
- "Something did not reset initial MallocHook-s");
- in_initial_malloc_hook = true;
- }
- // Initialize heap checker on the very first allocation/mmap/sbrk call:
- HeapLeakChecker_BeforeConstructors();
- { SpinLockHolder l(&heap_checker_lock);
- in_initial_malloc_hook = false;
- }
-}
-
-// These will owerwrite the weak definitions in malloc_hook.cc:
-
-// Important to have this to catch the first allocation call from the binary:
-extern "C" void InitialMallocHook_New(const void* ptr, size_t size) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first allocation as well (if we need to):
- MallocHook::InvokeNewHook(ptr, size);
-}
-
-// Important to have this to catch the first mmap call (say from tcmalloc):
-extern "C" void InitialMallocHook_MMap(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first mmap as well (if we need to):
- MallocHook::InvokeMmapHook(
- result, start, size, protection, flags, fd, offset);
-}
-
-// Important to have this to catch the first sbrk call (say from tcmalloc):
-extern "C" void InitialMallocHook_Sbrk(const void* result,
- ptrdiff_t increment) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first sbrk as well (if we need to):
- MallocHook::InvokeSbrkHook(result, increment);
-}
-
-// static
-void CancelInitialMallocHooks() {
- if (MallocHook::GetNewHook() == InitialMallocHook_New) {
- MallocHook::SetNewHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetNewHook() == NULL, "");
- if (MallocHook::GetMmapHook() == InitialMallocHook_MMap) {
- MallocHook::SetMmapHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetMmapHook() == NULL, "");
- if (MallocHook::GetSbrkHook() == InitialMallocHook_Sbrk) {
- MallocHook::SetSbrkHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetSbrkHook() == NULL, "");
-}
-
-#else
-
-// static
-void CancelInitialMallocHooks() {}
-
-#endif
-
// static
void HeapLeakChecker::BeforeConstructorsLocked() {
RAW_DCHECK(heap_checker_lock.IsHeld(), "");
RAW_CHECK(!constructor_heap_profiling,
"BeforeConstructorsLocked called multiple times");
- CancelInitialMallocHooks();
// Set hooks early to crash if 'new' gets called before we make heap_profile,
// and make sure no other hooks existed:
- if (MallocHook::SetNewHook(NewHook) != NULL ||
- MallocHook::SetDeleteHook(DeleteHook) != NULL) {
- RAW_LOG(FATAL, "Had other new/delete MallocHook-s set. "
- "Somehow leak checker got activated "
- "after something else have set up these hooks.");
- }
+ RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
constructor_heap_profiling = true;
MemoryRegionMap::Init(1);
// Set up MemoryRegionMap with (at least) one caller stack frame to record
@@ -2240,12 +2145,9 @@ void HeapLeakChecker::TurnItselfOffLocked() {
RAW_CHECK(heap_checker_on, "");
RAW_VLOG(heap_checker_info_level, "Turning perftools heap leak checking off");
heap_checker_on = false;
- // Unset our hooks checking they were the ones set:
- if (MallocHook::SetNewHook(NULL) != NewHook ||
- MallocHook::SetDeleteHook(NULL) != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client?");
- }
+ // Unset our hooks checking they were set:
+ RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
Allocator::DeleteAndNull(&heap_profile);
// free our optional global data:
Allocator::DeleteAndNullIfNot(&ignored_objects);
@@ -2261,6 +2163,9 @@ extern bool heap_leak_checker_bcad_variable; // in heap-checker-bcad.cc
static bool has_called_before_constructors = false;
+// TODO(maxim): inline this function with
+// MallocHook_InitAtFirstAllocation_HeapLeakChecker, and also rename
+// HeapLeakChecker::BeforeConstructorsLocked.
void HeapLeakChecker_BeforeConstructors() {
SpinLockHolder l(&heap_checker_lock);
// We can be called from several places: the first mmap/sbrk/alloc call
@@ -2299,11 +2204,19 @@ void HeapLeakChecker_BeforeConstructors() {
#endif
if (need_heap_check) {
HeapLeakChecker::BeforeConstructorsLocked();
- } else { // cancel our initial hooks
- CancelInitialMallocHooks();
}
}
+// This function overrides the weak function defined in malloc_hook.cc and
+// called by one of the initial malloc hooks (malloc_hook.cc) when the very
+// first memory allocation or an mmap/sbrk happens. This ensures that
+// HeapLeakChecker is initialized and installs all its hooks early enough to
+// track absolutely all memory allocations and all memory region acquisitions
+// via mmap and sbrk.
+extern "C" void MallocHook_InitAtFirstAllocation_HeapLeakChecker() {
+ HeapLeakChecker_BeforeConstructors();
+}
+
// This function is executed after all global object destructors run.
void HeapLeakChecker_AfterDestructors() {
{ SpinLockHolder l(&heap_checker_lock);
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index f28dffb..fc4f154 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -182,7 +182,6 @@ enum AddOrRemove { ADD, REMOVE };
static void AddRemoveMMapDataLocked(AddOrRemove mode) {
RAW_DCHECK(heap_lock.IsHeld(), "");
if (!FLAGS_mmap_profile || !is_on) return;
- if (!FLAGS_mmap_log) MemoryRegionMap::CheckMallocHooks();
// MemoryRegionMap maintained all the data we need for all
// mmap-like allocations, so we just use it here:
MemoryRegionMap::LockHolder l;
@@ -245,15 +244,6 @@ static void DumpProfileLocked(const char* reason) {
if (filename_prefix == NULL) return; // we do not yet need dumping
- if (FLAGS_only_mmap_profile == false) {
- if (MallocHook::GetNewHook() != NewHook ||
- MallocHook::GetDeleteHook() != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client? "
- "Do not use --heap_profile=... to avoid this conflict.");
- }
- }
-
dumping = true;
// Make file name
@@ -372,12 +362,6 @@ static void RawInfoStackDumper(const char* message, void*) {
}
#endif
-// Saved MemoryRegionMap's hooks to daisy-chain calls to.
-MallocHook::MmapHook saved_mmap_hook = NULL;
-MallocHook::MremapHook saved_mremap_hook = NULL;
-MallocHook::MunmapHook saved_munmap_hook = NULL;
-MallocHook::SbrkHook saved_sbrk_hook = NULL;
-
static void MmapHook(const void* result, const void* start, size_t size,
int prot, int flags, int fd, off_t offset) {
if (FLAGS_mmap_log) { // log it
@@ -393,11 +377,6 @@ static void MmapHook(const void* result, const void* start, size_t size,
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
- if (saved_mmap_hook) {
- // Call MemoryRegionMap's hook: it will record needed info about the mmap
- // for us w/o deadlocks:
- (*saved_mmap_hook)(result, start, size, prot, flags, fd, offset);
- }
}
static void MremapHook(const void* result, const void* old_addr,
@@ -417,9 +396,6 @@ static void MremapHook(const void* result, const void* old_addr,
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
- if (saved_mremap_hook) { // call MemoryRegionMap's hook
- (*saved_mremap_hook)(result, old_addr, old_size, new_size, flags, new_addr);
- }
}
static void MunmapHook(const void* ptr, size_t size) {
@@ -433,9 +409,6 @@ static void MunmapHook(const void* ptr, size_t size) {
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
- if (saved_munmap_hook) { // call MemoryRegionMap's hook
- (*saved_munmap_hook)(ptr, size);
- }
}
static void SbrkHook(const void* result, ptrdiff_t increment) {
@@ -446,9 +419,6 @@ static void SbrkHook(const void* result, ptrdiff_t increment) {
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
- if (saved_sbrk_hook) { // call MemoryRegionMap's hook
- (*saved_sbrk_hook)(result, increment);
- }
}
//----------------------------------------------------------------------
@@ -479,12 +449,11 @@ extern "C" void HeapProfilerStart(const char* prefix) {
}
if (FLAGS_mmap_log) {
- // Install our hooks to do the logging
- // and maybe save MemoryRegionMap's hooks to call:
- saved_mmap_hook = MallocHook::SetMmapHook(MmapHook);
- saved_mremap_hook = MallocHook::SetMremapHook(MremapHook);
- saved_munmap_hook = MallocHook::SetMunmapHook(MunmapHook);
- saved_sbrk_hook = MallocHook::SetSbrkHook(SbrkHook);
+ // Install our hooks to do the logging:
+ RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
+ RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
+ RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
+ RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
}
heap_profiler_memory =
@@ -507,14 +476,9 @@ extern "C" void HeapProfilerStart(const char* prefix) {
// sequence of profiles.
if (FLAGS_only_mmap_profile == false) {
- // Now set the hooks that capture new/delete and malloc/free
- // and check that these are the only hooks:
- if (MallocHook::SetNewHook(NewHook) != NULL ||
- MallocHook::SetDeleteHook(DeleteHook) != NULL) {
- RAW_LOG(FATAL, "Had other new/delete MallocHook-s set. "
- "Are you using the heap leak checker? "
- "Use --heap_check=\"\" to avoid this conflict.");
- }
+ // Now set the hooks that capture new/delete and malloc/free.
+ RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
}
// Copy filename prefix
@@ -536,24 +500,16 @@ extern "C" void HeapProfilerStop() {
if (!is_on) return;
if (FLAGS_only_mmap_profile == false) {
- // Unset our new/delete hooks, checking they were the ones set:
- if (MallocHook::SetNewHook(NULL) != NewHook ||
- MallocHook::SetDeleteHook(NULL) != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client? "
- "Do not use --heap_profile=... to avoid this conflict.");
- }
+ // Unset our new/delete hooks, checking they were set:
+ RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
}
if (FLAGS_mmap_log) {
- // Restore mmap/sbrk hooks, checking that our hooks were the ones set:
- if (MallocHook::SetMmapHook(saved_mmap_hook) != MmapHook ||
- MallocHook::SetMremapHook(saved_mremap_hook) != MremapHook ||
- MallocHook::SetMunmapHook(saved_munmap_hook) != MunmapHook ||
- MallocHook::SetSbrkHook(saved_sbrk_hook) != SbrkHook) {
- RAW_LOG(FATAL, "Had our mmap/mremap/munmap/sbrk MallocHook-s replaced. "
- "Are you using another MallocHook client? "
- "Do not use --heap_profile=... to avoid this conflict.");
- }
+ // Restore mmap/sbrk hooks, checking that our hooks were set:
+ RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
+ RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
+ RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
+ RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
}
// free profile
diff --git a/src/malloc_hook-inl.h b/src/malloc_hook-inl.h
index a690b07..027d6e2 100644
--- a/src/malloc_hook-inl.h
+++ b/src/malloc_hook-inl.h
@@ -45,6 +45,7 @@
namespace base { namespace internal {
+// The following (implementation) code is DEPRECATED.
// A simple atomic pointer class that can be initialized by the linker
// when you define a namespace-scope variable as:
//
@@ -95,27 +96,90 @@ extern AtomicPtr<MallocHook::MunmapHook> munmap_hook_;
extern AtomicPtr<MallocHook::MremapHook> mremap_hook_;
extern AtomicPtr<MallocHook::PreSbrkHook> presbrk_hook_;
extern AtomicPtr<MallocHook::SbrkHook> sbrk_hook_;
+// End DEPRECATED code.
+
+// Maximum of 7 hooks means that HookList is 8 words.
+static const int kHookListMaxValues = 7;
+
+// HookList: a class that provides synchronized insertions and removals and
+// lockless traversal. Most of the implementation is in malloc_hook.cc.
+template <typename T>
+struct HookList {
+ COMPILE_ASSERT(sizeof(T) <= sizeof(AtomicWord), T_should_fit_in_AtomicWord);
+
+ // Adds value to the list. Note that duplicates are allowed. Thread-safe and
+ // blocking (acquires hooklist_spinlock). Returns true on success; false
+ // otherwise (failures include invalid value and no space left).
+ bool Add(T value);
+
+ // Removes the first entry matching value from the list. Thread-safe and
+ // blocking (acquires hooklist_spinlock). Returns true on success; false
+ // otherwise (failures include invalid value and no value found).
+ bool Remove(T value);
+
+ // Store up to n values of the list in output_array, and return the number of
+ // elements stored. Thread-safe and non-blocking. This is fast (one memory
+ // access) if the list is empty.
+ int Traverse(T* output_array, int n) const;
+
+ // Fast inline implementation for fast path of Invoke*Hook.
+ bool empty() const {
+ return base::subtle::Acquire_Load(&priv_end) == 0;
+ }
+
+ // This internal data is not private so that the class is an aggregate and can
+ // be initialized by the linker. Don't access this directly. Use the
+ // INIT_HOOK_LIST macro in malloc_hook.cc.
+
+ // One more than the index of the last valid element in priv_data. During
+ // 'Remove' this may be past the last valid element in priv_data, but
+ // subsequent values will be 0.
+ AtomicWord priv_end;
+ AtomicWord priv_data[kHookListMaxValues];
+};
+
+extern HookList<MallocHook::NewHook> new_hooks_;
+extern HookList<MallocHook::DeleteHook> delete_hooks_;
+extern HookList<MallocHook::PreMmapHook> premmap_hooks_;
+extern HookList<MallocHook::MmapHook> mmap_hooks_;
+extern HookList<MallocHook::MunmapHook> munmap_hooks_;
+extern HookList<MallocHook::MremapHook> mremap_hooks_;
+extern HookList<MallocHook::PreSbrkHook> presbrk_hooks_;
+extern HookList<MallocHook::SbrkHook> sbrk_hooks_;
} } // namespace base::internal
+// The following method is DEPRECATED
inline MallocHook::NewHook MallocHook::GetNewHook() {
return base::internal::new_hook_.Get();
}
inline void MallocHook::InvokeNewHook(const void* p, size_t s) {
+ if (!base::internal::new_hooks_.empty()) {
+ InvokeNewHookSlow(p, s);
+ }
+ // The following code is DEPRECATED.
MallocHook::NewHook hook = MallocHook::GetNewHook();
if (hook != NULL) (*hook)(p, s);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::DeleteHook MallocHook::GetDeleteHook() {
return base::internal::delete_hook_.Get();
}
inline void MallocHook::InvokeDeleteHook(const void* p) {
+ if (!base::internal::delete_hooks_.empty()) {
+ InvokeDeleteHookSlow(p);
+ }
+ // The following code is DEPRECATED.
MallocHook::DeleteHook hook = MallocHook::GetDeleteHook();
if (hook != NULL) (*hook)(p);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::PreMmapHook MallocHook::GetPreMmapHook() {
return base::internal::premmap_hook_.Get();
}
@@ -126,12 +190,18 @@ inline void MallocHook::InvokePreMmapHook(const void* start,
int flags,
int fd,
off_t offset) {
+ if (!base::internal::premmap_hooks_.empty()) {
+ InvokePreMmapHookSlow(start, size, protection, flags, fd, offset);
+ }
+ // The following code is DEPRECATED.
MallocHook::PreMmapHook hook = MallocHook::GetPreMmapHook();
if (hook != NULL) (*hook)(start, size,
protection, flags,
fd, offset);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::MmapHook MallocHook::GetMmapHook() {
return base::internal::mmap_hook_.Get();
}
@@ -143,22 +213,34 @@ inline void MallocHook::InvokeMmapHook(const void* result,
int flags,
int fd,
off_t offset) {
+ if (!base::internal::mmap_hooks_.empty()) {
+ InvokeMmapHookSlow(result, start, size, protection, flags, fd, offset);
+ }
+ // The following code is DEPRECATED.
MallocHook::MmapHook hook = MallocHook::GetMmapHook();
if (hook != NULL) (*hook)(result,
start, size,
protection, flags,
fd, offset);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::MunmapHook MallocHook::GetMunmapHook() {
return base::internal::munmap_hook_.Get();
}
inline void MallocHook::InvokeMunmapHook(const void* p, size_t size) {
+ if (!base::internal::munmap_hooks_.empty()) {
+ InvokeMunmapHookSlow(p, size);
+ }
+ // The following code is DEPRECATED.
MallocHook::MunmapHook hook = MallocHook::GetMunmapHook();
if (hook != NULL) (*hook)(p, size);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::MremapHook MallocHook::GetMremapHook() {
return base::internal::mremap_hook_.Get();
}
@@ -169,29 +251,46 @@ inline void MallocHook::InvokeMremapHook(const void* result,
size_t new_size,
int flags,
const void* new_addr) {
+ if (!base::internal::mremap_hooks_.empty()) {
+ InvokeMremapHookSlow(result, old_addr, old_size, new_size, flags, new_addr);
+ }
+ // The following code is DEPRECATED.
MallocHook::MremapHook hook = MallocHook::GetMremapHook();
if (hook != NULL) (*hook)(result,
old_addr, old_size,
new_size, flags, new_addr);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::PreSbrkHook MallocHook::GetPreSbrkHook() {
return base::internal::presbrk_hook_.Get();
}
inline void MallocHook::InvokePreSbrkHook(ptrdiff_t increment) {
+ if (!base::internal::presbrk_hooks_.empty() && increment != 0) {
+ InvokePreSbrkHookSlow(increment);
+ }
+ // The following code is DEPRECATED.
MallocHook::PreSbrkHook hook = MallocHook::GetPreSbrkHook();
if (hook != NULL && increment != 0) (*hook)(increment);
+ // End DEPRECATED code.
}
+// The following method is DEPRECATED
inline MallocHook::SbrkHook MallocHook::GetSbrkHook() {
return base::internal::sbrk_hook_.Get();
}
inline void MallocHook::InvokeSbrkHook(const void* result,
ptrdiff_t increment) {
+ if (!base::internal::sbrk_hooks_.empty() && increment != 0) {
+ InvokeSbrkHookSlow(result, increment);
+ }
+ // The following code is DEPRECATED.
MallocHook::SbrkHook hook = MallocHook::GetSbrkHook();
if (hook != NULL && increment != 0) (*hook)(result, increment);
+ // End DEPRECATED code.
}
#endif /* _MALLOC_HOOK_INL_H_ */
diff --git a/src/malloc_hook.cc b/src/malloc_hook.cc
index 537dcee..b185905 100644
--- a/src/malloc_hook.cc
+++ b/src/malloc_hook.cc
@@ -44,6 +44,8 @@
#include <algorithm>
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/spinlock.h"
+#include "maybe_threads.h"
#include "malloc_hook-inl.h"
#include <google/malloc_hook.h>
@@ -66,54 +68,93 @@
using std::copy;
-// Declarations of five default weak hook functions, that can be overridden by
-// linking-in a strong definition (as heap-checker.cc does). These are extern
-// "C" so that they don't trigger gold's --detect-odr-violations warning, which
-// only looks at C++ symbols.
-//
-// These default hooks let some other library we link in
-// to define strong versions of InitialMallocHook_New, InitialMallocHook_MMap,
-// InitialMallocHook_PreMMap, InitialMallocHook_PreSbrk, and
-// InitialMallocHook_Sbrk to have a chance to hook into the very first
-// invocation of an allocation function call, mmap, or sbrk.
-//
-// These functions are declared here as weak, and defined later, rather than a
-// more straightforward simple weak definition, as a workround for an icc
-// compiler issue ((Intel reference 290819). This issue causes icc to resolve
-// weak symbols too early, at compile rather than link time. By declaring it
-// (weak) here, then defining it below after its use, we can avoid the problem.
+// Declaration of default weak initialization function, that can be overridden
+// by linking-in a strong definition (as heap-checker.cc does). This is
+// extern "C" so that it doesn't trigger gold's --detect-odr-violations warning,
+// which only looks at C++ symbols.
//
+// This function is declared here as weak, and defined later, rather than a more
+// straightforward simple weak definition, as a workround for an icc compiler
+// issue ((Intel reference 290819). This issue causes icc to resolve weak
+// symbols too early, at compile rather than link time. By declaring it (weak)
+// here, then defining it below after its use, we can avoid the problem.
extern "C" {
+ATTRIBUTE_WEAK void MallocHook_InitAtFirstAllocation_HeapLeakChecker();
+}
+
+namespace {
+
+void RemoveInitialHooksAndCallInitializers(); // below.
+
+pthread_once_t once = PTHREAD_ONCE_INIT;
-ATTRIBUTE_WEAK
-void InitialMallocHook_New(const void* ptr, size_t size);
+// These hooks are installed in MallocHook as the only initial hooks. The first
+// hook that is called will run RemoveInitialHooksAndCallInitializers (see the
+// definition below) and then redispatch to any malloc hooks installed by
+// RemoveInitialHooksAndCallInitializers.
+//
+// Note(llib): there is a possibility of a race in the event that there are
+// multiple threads running before the first allocation. This is pretty
+// difficult to achieve, but if it is then multiple threads may concurrently do
+// allocations. The first caller will call
+// RemoveInitialHooksAndCallInitializers via one of the initial hooks. A
+// concurrent allocation may, depending on timing either:
+// * still have its initial malloc hook installed, run that and block on waiting
+// for the first caller to finish its call to
+// RemoveInitialHooksAndCallInitializers, and proceed normally.
+// * occur some time during the RemoveInitialHooksAndCallInitializers call, at
+// which point there could be no initial hooks and the subsequent hooks that
+// are about to be set up by RemoveInitialHooksAndCallInitializers haven't
+// been installed yet. I think the worst we can get is that some allocations
+// will not get reported to some hooks set by the initializers called from
+// RemoveInitialHooksAndCallInitializers.
+
+void InitialNewHook(const void* ptr, size_t size) {
+ perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
+ MallocHook::InvokeNewHook(ptr, size);
+}
-ATTRIBUTE_WEAK
-void InitialMallocHook_PreMMap(const void* start,
+void InitialPreMMapHook(const void* start,
size_t size,
int protection,
int flags,
int fd,
- off_t offset);
+ off_t offset) {
+ perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
+ MallocHook::InvokePreMmapHook(start, size, protection, flags, fd, offset);
+}
-ATTRIBUTE_WEAK
-void InitialMallocHook_MMap(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset);
+void InitialPreSbrkHook(ptrdiff_t increment) {
+ perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
+ MallocHook::InvokePreSbrkHook(increment);
+}
-ATTRIBUTE_WEAK
-void InitialMallocHook_PreSbrk(ptrdiff_t increment);
+// This function is called at most once by one of the above initial malloc
+// hooks. It removes all initial hooks and initializes all other clients that
+// want to get control at the very first memory allocation. The initializers
+// may assume that the initial malloc hooks have been removed. The initializers
+// may set up malloc hooks and allocate memory.
+void RemoveInitialHooksAndCallInitializers() {
+ RAW_CHECK(MallocHook::RemoveNewHook(&InitialNewHook), "");
+ RAW_CHECK(MallocHook::RemovePreMmapHook(&InitialPreMMapHook), "");
+ RAW_CHECK(MallocHook::RemovePreSbrkHook(&InitialPreSbrkHook), "");
+
+ // HeapLeakChecker is currently the only module that needs to get control on
+ // the first memory allocation, but one can add other modules by following the
+ // same weak/strong function pattern.
+ MallocHook_InitAtFirstAllocation_HeapLeakChecker();
+}
-ATTRIBUTE_WEAK
-void InitialMallocHook_Sbrk(const void* result, ptrdiff_t increment);
+} // namespace
-} // extern "C"
+// Weak default initialization function that must go after its use.
+extern "C" void MallocHook_InitAtFirstAllocation_HeapLeakChecker() {
+ // Do nothing.
+}
namespace base { namespace internal {
+
+// The code below is DEPRECATED.
template<typename PtrT>
PtrT AtomicPtr<PtrT>::Exchange(PtrT new_val) {
base::subtle::MemoryBarrier(); // Release semantics.
@@ -141,22 +182,123 @@ PtrT AtomicPtr<PtrT>::CompareAndSwap(PtrT old_val, PtrT new_val) {
return retval;
}
-AtomicPtr<MallocHook::NewHook> new_hook_ = {
- reinterpret_cast<AtomicWord>(InitialMallocHook_New) };
+AtomicPtr<MallocHook::NewHook> new_hook_ = { 0 };
AtomicPtr<MallocHook::DeleteHook> delete_hook_ = { 0 };
-AtomicPtr<MallocHook::PreMmapHook> premmap_hook_ = {
- reinterpret_cast<AtomicWord>(InitialMallocHook_PreMMap) };
-AtomicPtr<MallocHook::MmapHook> mmap_hook_ = {
- reinterpret_cast<AtomicWord>(InitialMallocHook_MMap) };
+AtomicPtr<MallocHook::PreMmapHook> premmap_hook_ = { 0 };
+AtomicPtr<MallocHook::MmapHook> mmap_hook_ = { 0 };
AtomicPtr<MallocHook::MunmapHook> munmap_hook_ = { 0 };
AtomicPtr<MallocHook::MremapHook> mremap_hook_ = { 0 };
-AtomicPtr<MallocHook::PreSbrkHook> presbrk_hook_ = {
- reinterpret_cast<AtomicWord>(InitialMallocHook_PreSbrk) };
-AtomicPtr<MallocHook::SbrkHook> sbrk_hook_ = {
- reinterpret_cast<AtomicWord>(InitialMallocHook_Sbrk) };
+AtomicPtr<MallocHook::PreSbrkHook> presbrk_hook_ = { 0 };
+AtomicPtr<MallocHook::SbrkHook> sbrk_hook_ = { 0 };
+// End of DEPRECATED code section.
+
+// This lock is shared between all implementations of HookList::Add & Remove.
+// The potential for contention is very small. This needs to be a SpinLock and
+// not a Mutex since it's possible for Mutex locking to allocate memory (e.g.,
+// per-thread allocation in debug builds), which could cause infinite recursion.
+static SpinLock hooklist_spinlock(base::LINKER_INITIALIZED);
+
+template <typename T>
+bool HookList<T>::Add(T value_as_t) {
+ // Note: we need to check this _before_ reinterpret_cast, since
+ // reinterpret_cast may include random junk from memory.
+ if (value_as_t == 0) {
+ return false;
+ }
+ AtomicWord value = reinterpret_cast<const AtomicWord&>(value_as_t);
+ if (value == 0) {
+ // This should not actually happen, but just to be sure...
+ return false;
+ }
+ SpinLockHolder l(&hooklist_spinlock);
+ // Find the first slot in data that is 0.
+ int index = 0;
+ while ((index < kHookListMaxValues) &&
+ (base::subtle::NoBarrier_Load(&priv_data[index]) != 0)) {
+ ++index;
+ }
+ if (index == kHookListMaxValues) {
+ return false;
+ }
+ AtomicWord prev_num_hooks = base::subtle::Acquire_Load(&priv_end);
+ base::subtle::Release_Store(&priv_data[index], value);
+ if (prev_num_hooks <= index) {
+ base::subtle::Release_Store(&priv_end, index + 1);
+ }
+ return true;
+}
+
+template <typename T>
+bool HookList<T>::Remove(T value_as_t) {
+ if (value_as_t == 0) {
+ return false;
+ }
+ SpinLockHolder l(&hooklist_spinlock);
+ AtomicWord hooks_end = base::subtle::Acquire_Load(&priv_end);
+ int index = 0;
+ // Note: we need to cast back to T since T may be smaller than AtomicWord.
+ while (index < hooks_end && value_as_t != reinterpret_cast<T>(
+ base::subtle::Acquire_Load(&priv_data[index]))) {
+ ++index;
+ }
+ if (index == hooks_end) {
+ return false;
+ }
+ base::subtle::Release_Store(&priv_data[index], 0);
+ if (hooks_end == index + 1) {
+ // Adjust hooks_end down to the lowest possible value.
+ hooks_end = index;
+ while ((hooks_end > 0) &&
+ (base::subtle::Acquire_Load(&priv_data[hooks_end - 1]) == 0)) {
+ --hooks_end;
+ }
+ base::subtle::Release_Store(&priv_end, hooks_end);
+ }
+ return true;
+}
+
+template <typename T>
+int HookList<T>::Traverse(T* output_array, int n) const {
+ AtomicWord hooks_end = base::subtle::Acquire_Load(&priv_end);
+ int actual_hooks_end = 0;
+ for (int i = 0; i < hooks_end && n > 0; ++i) {
+ AtomicWord data = base::subtle::Acquire_Load(&priv_data[i]);
+ if (data != 0) {
+ *output_array++ = reinterpret_cast<const T&>(data);
+ ++actual_hooks_end;
+ --n;
+ }
+ }
+ return actual_hooks_end;
+}
+
+// Initialize a HookList (optionally with the given initial_value in index 0).
+#define INIT_HOOK_LIST { 0 }
+#define INIT_HOOK_LIST_WITH_VALUE(initial_value) \
+ { 1, { reinterpret_cast<AtomicWord>(initial_value) } }
+
+// Explicit instantiation for malloc_hook_test.cc. This ensures all the methods
+// are instantiated.
+template class HookList<MallocHook::NewHook>;
+
+HookList<MallocHook::NewHook> new_hooks_ =
+ INIT_HOOK_LIST_WITH_VALUE(&InitialNewHook);
+HookList<MallocHook::DeleteHook> delete_hooks_ = INIT_HOOK_LIST;
+HookList<MallocHook::PreMmapHook> premmap_hooks_ =
+ INIT_HOOK_LIST_WITH_VALUE(&InitialPreMMapHook);
+HookList<MallocHook::MmapHook> mmap_hooks_ = INIT_HOOK_LIST;
+HookList<MallocHook::MunmapHook> munmap_hooks_ = INIT_HOOK_LIST;
+HookList<MallocHook::MremapHook> mremap_hooks_ = INIT_HOOK_LIST;
+HookList<MallocHook::PreSbrkHook> presbrk_hooks_ =
+ INIT_HOOK_LIST_WITH_VALUE(InitialPreSbrkHook);
+HookList<MallocHook::SbrkHook> sbrk_hooks_ = INIT_HOOK_LIST;
+
+#undef INIT_HOOK_LIST_WITH_VALUE
+#undef INIT_HOOK_LIST
} } // namespace base::internal
+// The code below is DEPRECATED.
using base::internal::new_hook_;
using base::internal::delete_hook_;
using base::internal::premmap_hook_;
@@ -165,105 +307,231 @@ using base::internal::munmap_hook_;
using base::internal::mremap_hook_;
using base::internal::presbrk_hook_;
using base::internal::sbrk_hook_;
-
+// End of DEPRECATED code section.
+
+using base::internal::kHookListMaxValues;
+using base::internal::new_hooks_;
+using base::internal::delete_hooks_;
+using base::internal::premmap_hooks_;
+using base::internal::mmap_hooks_;
+using base::internal::munmap_hooks_;
+using base::internal::mremap_hooks_;
+using base::internal::presbrk_hooks_;
+using base::internal::sbrk_hooks_;
// These are available as C bindings as well as C++, hence their
// definition outside the MallocHook class.
extern "C"
+int MallocHook_AddNewHook(MallocHook_NewHook hook) {
+ RAW_VLOG(10, "AddNewHook(%p)", hook);
+ return new_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveNewHook(MallocHook_NewHook hook) {
+ RAW_VLOG(10, "RemoveNewHook(%p)", hook);
+ return new_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) {
+ RAW_VLOG(10, "AddDeleteHook(%p)", hook);
+ return delete_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook) {
+ RAW_VLOG(10, "RemoveDeleteHook(%p)", hook);
+ return delete_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddPreMmapHook(MallocHook_PreMmapHook hook) {
+ RAW_VLOG(10, "AddPreMmapHook(%p)", hook);
+ return premmap_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemovePreMmapHook(MallocHook_PreMmapHook hook) {
+ RAW_VLOG(10, "RemovePreMmapHook(%p)", hook);
+ return premmap_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddMmapHook(MallocHook_MmapHook hook) {
+ RAW_VLOG(10, "AddMmapHook(%p)", hook);
+ return mmap_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveMmapHook(MallocHook_MmapHook hook) {
+ RAW_VLOG(10, "RemoveMmapHook(%p)", hook);
+ return mmap_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddMunmapHook(MallocHook_MunmapHook hook) {
+ RAW_VLOG(10, "AddMunmapHook(%p)", hook);
+ return munmap_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveMunmapHook(MallocHook_MunmapHook hook) {
+ RAW_VLOG(10, "RemoveMunmapHook(%p)", hook);
+ return munmap_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddMremapHook(MallocHook_MremapHook hook) {
+ RAW_VLOG(10, "AddMremapHook(%p)", hook);
+ return mremap_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveMremapHook(MallocHook_MremapHook hook) {
+ RAW_VLOG(10, "RemoveMremapHook(%p)", hook);
+ return mremap_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddPreSbrkHook(MallocHook_PreSbrkHook hook) {
+ RAW_VLOG(10, "AddPreSbrkHook(%p)", hook);
+ return presbrk_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemovePreSbrkHook(MallocHook_PreSbrkHook hook) {
+ RAW_VLOG(10, "RemovePreSbrkHook(%p)", hook);
+ return presbrk_hooks_.Remove(hook);
+}
+
+extern "C"
+int MallocHook_AddSbrkHook(MallocHook_SbrkHook hook) {
+ RAW_VLOG(10, "AddSbrkHook(%p)", hook);
+ return sbrk_hooks_.Add(hook);
+}
+
+extern "C"
+int MallocHook_RemoveSbrkHook(MallocHook_SbrkHook hook) {
+ RAW_VLOG(10, "RemoveSbrkHook(%p)", hook);
+ return sbrk_hooks_.Remove(hook);
+}
+
+// The code below is DEPRECATED.
+extern "C"
MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook) {
+ RAW_VLOG(10, "SetNewHook(%p)", hook);
return new_hook_.Exchange(hook);
}
extern "C"
MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook) {
+ RAW_VLOG(10, "SetDeleteHook(%p)", hook);
return delete_hook_.Exchange(hook);
}
extern "C"
MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook) {
+ RAW_VLOG(10, "SetPreMmapHook(%p)", hook);
return premmap_hook_.Exchange(hook);
}
extern "C"
MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook) {
+ RAW_VLOG(10, "SetMmapHook(%p)", hook);
return mmap_hook_.Exchange(hook);
}
extern "C"
MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook) {
+ RAW_VLOG(10, "SetMunmapHook(%p)", hook);
return munmap_hook_.Exchange(hook);
}
extern "C"
MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook) {
+ RAW_VLOG(10, "SetMremapHook(%p)", hook);
return mremap_hook_.Exchange(hook);
}
extern "C"
MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook) {
+ RAW_VLOG(10, "SetPreSbrkHook(%p)", hook);
return presbrk_hook_.Exchange(hook);
}
extern "C"
MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook) {
+ RAW_VLOG(10, "SetSbrkHook(%p)", hook);
return sbrk_hook_.Exchange(hook);
}
+// End of DEPRECATED code section.
+
+// Note: embedding the function calls inside the traversal of HookList would be
+// very confusing, as it is legal for a hook to remove itself and add other
+// hooks. Doing traversal first, and then calling the hooks ensures we only
+// call the hooks registered at the start.
+#define INVOKE_HOOKS(HookType, hook_list, args) do { \
+ HookType hooks[kHookListMaxValues]; \
+ int num_hooks = hook_list.Traverse(hooks, kHookListMaxValues); \
+ for (int i = 0; i < num_hooks; ++i) { \
+ (*hooks[i])args; \
+ } \
+ } while (0)
+
+void MallocHook::InvokeNewHookSlow(const void* p, size_t s) {
+ INVOKE_HOOKS(NewHook, new_hooks_, (p, s));
+}
+void MallocHook::InvokeDeleteHookSlow(const void* p) {
+ INVOKE_HOOKS(DeleteHook, delete_hooks_, (p));
+}
-// The definitions of weak default malloc hooks (New, MMap, and Sbrk)
-// that self deinstall on their first call. This is entirely for
-// efficiency: the default version of these functions will be called a
-// maximum of one time. If these functions were a no-op instead, they'd
-// be called every time, costing an extra function call per malloc.
-//
-// However, this 'delete self' isn't safe in general -- it's possible
-// that this function will be called via a daisy chain. That is,
-// someone else might do
-// old_hook = MallocHook::SetNewHook(&myhook);
-// void myhook(void* ptr, size_t size) {
-// do_my_stuff();
-// old_hook(ptr, size); // daisy-chain the hooks
-// }
-// If old_hook is InitialMallocHook_New(), then this is broken code! --
-// after the first run it'll deregister not only InitialMallocHook_New()
-// but also myhook. To protect against that, InitialMallocHook_New()
-// makes sure it's the 'top-level' hook before doing the deregistration.
-// This means the daisy-chain case will be less efficient because the
-// hook will be called, and do an if check, for every new. Alas.
-// TODO(csilvers): add support for removing a hook from the middle of a chain.
-
-void InitialMallocHook_New(const void* ptr, size_t size) {
- // Set new_hook to NULL iff its previous value was InitialMallocHook_New
- new_hook_.CompareAndSwap(&InitialMallocHook_New, NULL);
-}
-
-void InitialMallocHook_PreMMap(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- premmap_hook_.CompareAndSwap(&InitialMallocHook_PreMMap, NULL);
+void MallocHook::InvokePreMmapHookSlow(const void* start,
+ size_t size,
+ int protection,
+ int flags,
+ int fd,
+ off_t offset) {
+ INVOKE_HOOKS(PreMmapHook, premmap_hooks_, (start, size, protection, flags, fd,
+ offset));
}
-void InitialMallocHook_MMap(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- mmap_hook_.CompareAndSwap(&InitialMallocHook_MMap, NULL);
+void MallocHook::InvokeMmapHookSlow(const void* result,
+ const void* start,
+ size_t size,
+ int protection,
+ int flags,
+ int fd,
+ off_t offset) {
+ INVOKE_HOOKS(MmapHook, mmap_hooks_, (result, start, size, protection, flags,
+ fd, offset));
}
-void InitialMallocHook_PreSbrk(ptrdiff_t increment) {
- presbrk_hook_.CompareAndSwap(&InitialMallocHook_PreSbrk, NULL);
+void MallocHook::InvokeMunmapHookSlow(const void* p, size_t s) {
+ INVOKE_HOOKS(MunmapHook, munmap_hooks_, (p, s));
}
-void InitialMallocHook_Sbrk(const void* result, ptrdiff_t increment) {
- sbrk_hook_.CompareAndSwap(&InitialMallocHook_Sbrk, NULL);
+void MallocHook::InvokeMremapHookSlow(const void* result,
+ const void* old_addr,
+ size_t old_size,
+ size_t new_size,
+ int flags,
+ const void* new_addr) {
+ INVOKE_HOOKS(MremapHook, mremap_hooks_, (result, old_addr, old_size, new_size,
+ flags, new_addr));
}
+void MallocHook::InvokePreSbrkHookSlow(ptrdiff_t increment) {
+ INVOKE_HOOKS(PreSbrkHook, presbrk_hooks_, (increment));
+}
+
+void MallocHook::InvokeSbrkHookSlow(const void* result, ptrdiff_t increment) {
+ INVOKE_HOOKS(SbrkHook, sbrk_hooks_, (result, increment));
+}
+
+#undef INVOKE_HOOKS
+
DEFINE_ATTRIBUTE_SECTION_VARS(google_malloc);
DECLARE_ATTRIBUTE_SECTION_VARS(google_malloc);
// actual functions are in debugallocation.cc or tcmalloc.cc
diff --git a/src/memory_region_map.cc b/src/memory_region_map.cc
index 3f8509f..17599af 100644
--- a/src/memory_region_map.cc
+++ b/src/memory_region_map.cc
@@ -194,15 +194,11 @@ void MemoryRegionMap::Init(int max_stack_depth) {
RAW_VLOG(10, "MemoryRegionMap Init increment done");
return;
}
- // Set our hooks and make sure no other hooks existed:
- if (MallocHook::SetMmapHook(MmapHook) != NULL ||
- MallocHook::SetMremapHook(MremapHook) != NULL ||
- MallocHook::SetSbrkHook(SbrkHook) != NULL ||
- MallocHook::SetMunmapHook(MunmapHook) != NULL) {
- RAW_LOG(FATAL, "Had other mmap/mremap/munmap/sbrk MallocHook-s set. "
- "Make sure only one of MemoryRegionMap and the other "
- "client is active.");
- }
+ // Set our hooks and make sure they were installed:
+ RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
+ RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
+ RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
+ RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
// We need to set recursive_insert since the NewArena call itself
// will already do some allocations with mmap which our hooks will catch
// recursive_insert allows us to buffer info about these mmap calls.
@@ -229,11 +225,10 @@ bool MemoryRegionMap::Shutdown() {
RAW_VLOG(10, "MemoryRegionMap Shutdown decrement done");
return true;
}
- CheckMallocHooks(); // we assume no other hooks
- MallocHook::SetMmapHook(NULL);
- MallocHook::SetMremapHook(NULL);
- MallocHook::SetSbrkHook(NULL);
- MallocHook::SetMunmapHook(NULL);
+ RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
+ RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
+ RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
+ RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
if (regions_) regions_->~RegionSet();
regions_ = NULL;
bool deleted_arena = LowLevelAlloc::DeleteArena(arena_);
@@ -247,15 +242,6 @@ bool MemoryRegionMap::Shutdown() {
return deleted_arena;
}
-void MemoryRegionMap::CheckMallocHooks() {
- if (MallocHook::GetMmapHook() != MmapHook ||
- MallocHook::GetMunmapHook() != MunmapHook ||
- MallocHook::GetMremapHook() != MremapHook ||
- MallocHook::GetSbrkHook() != SbrkHook) {
- RAW_LOG(FATAL, "Our mmap/mremap/munmap/sbrk MallocHook-s got changed.");
- }
-}
-
// Invariants (once libpthread_initialized is true):
// * While lock_ is not held, recursion_count_ is 0 (and
// lock_owner_tid_ is the previous owner, but we don't rely on
diff --git a/src/memory_region_map.h b/src/memory_region_map.h
index 776abb3..bc2862e 100644
--- a/src/memory_region_map.h
+++ b/src/memory_region_map.h
@@ -98,10 +98,6 @@ class MemoryRegionMap {
// the number of Init() calls.
static bool Shutdown();
- // Check that our hooks are still in place and crash if not.
- // No need for locking.
- static void CheckMallocHooks();
-
// Locks to protect our internal data structures.
// These also protect use of arena_ if our Init() has been done.
// The lock is recursive.
@@ -260,7 +256,6 @@ class MemoryRegionMap {
union RegionSetRep;
private:
-
// representation ===========================================================
// Counter of clients of this module that have called Init().
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index ab3f03b..61300e6 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -262,12 +262,12 @@ extern "C" {
} // extern "C"
#endif // #ifndef _WIN32
-// We define this here because one some architectures it's needed soon.
+// We define this here because on some architectures it's needed soon.
namespace {
inline size_t GetSizeWithCallback(void* ptr,
size_t (*invalid_getsize_fn)(void*)) {
if (ptr == NULL)
- return 0;
+ return (*invalid_getsize_fn)(ptr);
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
if (cl != 0) {
@@ -296,39 +296,46 @@ inline size_t GetSizeWithCallback(void* ptr,
#elif defined(__APPLE__)
+#include <AvailabilityMacros.h>
+
// 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. :-(
+namespace {
// malloc_zone semantics are we return 0 if we don't own the memory.
-static size_t mz_invalid_getsize(void*) {
+size_t mz_invalid_getsize(void*) {
return 0;
}
-static size_t mz_size(malloc_zone_t* zone, const void* ptr) {
+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) {
+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) {
+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) {
+void* mz_valloc(malloc_zone_t* zone, size_t size) {
return tc_valloc(size);
}
-static void mz_free(malloc_zone_t* zone, void* ptr) {
+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) {
+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) {
+void* mz_memalign(malloc_zone_t* zone, size_t align, size_t size) {
+ return tc_memalign(align, size);
+}
+void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
}
+} // unnamed namespace
static void ReplaceSystemAlloc() {
static malloc_zone_t system_zone_copy;
@@ -343,6 +350,13 @@ static void ReplaceSystemAlloc() {
system_zone->free = &mz_free;
system_zone->realloc = &mz_realloc;
system_zone->destroy = &mz_destroy;
+ system_zone->batch_malloc = NULL;
+ system_zone->batch_free = NULL;
+#ifdef MAC_OS_X_VERSION_10_6 // from AvailabilityMacros.h
+ system_zone->memalign = &mz_memalign;
+ system_zone->free_definite_size = NULL;
+#endif
+
// 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.
@@ -526,9 +540,10 @@ static void ExtractStats(TCMallocStats* r, uint64_t* class_count) {
for (int cl = 0; cl < kNumClasses; ++cl) {
const int length = Static::central_cache()[cl].length();
const int tc_length = Static::central_cache()[cl].tc_length();
+ const size_t cache_overhead = Static::central_cache()[cl].OverheadBytes();
const size_t size = static_cast<uint64_t>(
Static::sizemap()->ByteSizeForClass(cl));
- r->central_bytes += (size * length);
+ r->central_bytes += (size * length) + cache_overhead;
r->transfer_bytes += (size * tc_length);
if (class_count) class_count[cl] = length + tc_length;
}
diff --git a/src/tests/current_allocated_bytes_test.cc b/src/tests/current_allocated_bytes_test.cc
new file mode 100644
index 0000000..8188e7b
--- /dev/null
+++ b/src/tests/current_allocated_bytes_test.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// ---
+//
+// Author: Craig Silverstein
+
+// This tests the accounting done by tcmalloc. When we allocate and
+// free a small buffer, the number of bytes used by the application
+// before the alloc+free should match the number of bytes used after.
+// However, the internal data structures used by tcmalloc will be
+// quite different -- new spans will have been allocated, etc. This
+// is, thus, a simple test that we account properly for the internal
+// data structures, so that we report the actual application-used
+// bytes properly.
+
+#include "config_for_unittests.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <google/malloc_extension.h>
+#include "base/logging.h"
+
+const char kCurrent[] = "generic.current_allocated_bytes";
+
+int main() {
+ // We don't do accounting right when using debugallocation.cc, so
+ // turn off the test then. TODO(csilvers): get this working too.
+#ifdef NDEBUG
+ size_t before_bytes, after_bytes;
+ MallocExtension::instance()->GetNumericProperty(kCurrent, &before_bytes);
+ free(malloc(200));
+ MallocExtension::instance()->GetNumericProperty(kCurrent, &after_bytes);
+
+ CHECK_EQ(before_bytes, after_bytes);
+#endif
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/tests/low_level_alloc_unittest.cc b/src/tests/low_level_alloc_unittest.cc
index f98f8a5..4228e12 100644
--- a/src/tests/low_level_alloc_unittest.cc
+++ b/src/tests/low_level_alloc_unittest.cc
@@ -146,17 +146,12 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
// used for counting allocates and frees
static int32 allocates;
static int32 frees;
-static MallocHook::NewHook old_alloc_hook;
-static MallocHook::DeleteHook old_free_hook;
// called on each alloc if kCallMallocHook specified
static void AllocHook(const void *p, size_t size) {
if (using_low_level_alloc) {
allocates++;
}
- if (old_alloc_hook != 0) {
- (*old_alloc_hook)(p, size);
- }
}
// called on each free if kCallMallocHook specified
@@ -164,9 +159,6 @@ static void FreeHook(const void *p) {
if (using_low_level_alloc) {
frees++;
}
- if (old_free_hook != 0) {
- (*old_free_hook)(p);
- }
}
int main(int argc, char *argv[]) {
@@ -177,8 +169,8 @@ int main(int argc, char *argv[]) {
return 1;
}
- old_alloc_hook = MallocHook::SetNewHook(AllocHook);
- old_free_hook = MallocHook::SetDeleteHook(FreeHook);
+ CHECK(MallocHook::AddNewHook(&AllocHook));
+ CHECK(MallocHook::AddDeleteHook(&FreeHook));
CHECK_EQ(allocates, 0);
CHECK_EQ(frees, 0);
Test(false, false, 50000);
@@ -198,7 +190,7 @@ int main(int argc, char *argv[]) {
}
}
printf("\nPASS\n");
- CHECK_EQ(MallocHook::SetNewHook(old_alloc_hook), AllocHook);
- CHECK_EQ(MallocHook::SetDeleteHook(old_free_hook), FreeHook);
+ CHECK(MallocHook::RemoveNewHook(&AllocHook));
+ CHECK(MallocHook::RemoveDeleteHook(&FreeHook));
return 0;
}
diff --git a/src/tests/malloc_extension_c_test.c b/src/tests/malloc_extension_c_test.c
index b6319a1..e384b76 100644
--- a/src/tests/malloc_extension_c_test.c
+++ b/src/tests/malloc_extension_c_test.c
@@ -72,8 +72,12 @@ void TestMallocHook(void) {
}
#endif
- MallocHook_SetNewHook(&TestNewHook);
- MallocHook_SetDeleteHook(&TestDeleteHook);
+ if (!MallocHook_AddNewHook(&TestNewHook)) {
+ FAIL("Failed to add new hook");
+ }
+ if (!MallocHook_AddDeleteHook(&TestDeleteHook)) {
+ FAIL("Failed to add delete hook");
+ }
free(malloc(10));
free(malloc(20));
if (g_new_hook_calls != 2) {
@@ -82,6 +86,12 @@ void TestMallocHook(void) {
if (g_delete_hook_calls != 2) {
FAIL("Wrong number of calls to the delete hook");
}
+ if (!MallocHook_RemoveNewHook(&TestNewHook)) {
+ FAIL("Failed to remove new hook");
+ }
+ if (!MallocHook_RemoveDeleteHook(&TestDeleteHook)) {
+ FAIL("Failed to remove delete hook");
+ }
}
void TestMallocExtension(void) {
diff --git a/src/tests/tcmalloc_unittest.cc b/src/tests/tcmalloc_unittest.cc
index 3af48f8..b430460 100644
--- a/src/tests/tcmalloc_unittest.cc
+++ b/src/tests/tcmalloc_unittest.cc
@@ -692,14 +692,13 @@ static void TestNothrowNew(void* (*func)(size_t, const std::nothrow_t&)) {
CHECK_GT(g_##hook_type##_calls, 0); \
g_##hook_type##_calls = 0; /* reset for next call */ \
} \
- static MallocHook::hook_type g_old_##hook_type; \
static void Set##hook_type() { \
- g_old_##hook_type = MallocHook::Set##hook_type( \
- (MallocHook::hook_type)&IncrementCallsTo##hook_type); \
+ CHECK(MallocHook::Add##hook_type( \
+ (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
} \
static void Reset##hook_type() { \
- CHECK_EQ(MallocHook::Set##hook_type(g_old_##hook_type), \
- (MallocHook::hook_type)&IncrementCallsTo##hook_type); \
+ CHECK(MallocHook::Remove##hook_type( \
+ (MallocHook::hook_type)&IncrementCallsTo##hook_type)); \
}
// We do one for each hook typedef in malloc_hook.h
@@ -765,7 +764,15 @@ static void RangeCallback(void* arg, const base::MallocRange* r) {
RangeCallbackState* state = reinterpret_cast<RangeCallbackState*>(arg);
if (state->ptr >= r->address &&
state->ptr < r->address + r->length) {
- CHECK_EQ(r->type, state->expected_type);
+ if (state->expected_type == base::MallocRange::FREE) {
+ // We are expecting r->type == FREE, but ReleaseMemory
+ // may have already moved us to UNMAPPED state instead (this happens in
+ // approximately 0.1% of executions). Accept either state.
+ CHECK(r->type == base::MallocRange::FREE ||
+ r->type == base::MallocRange::UNMAPPED);
+ } else {
+ CHECK_EQ(r->type, state->expected_type);
+ }
CHECK_GE(r->length, state->min_size);
state->matched = true;
}
diff --git a/vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj b/vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj
new file mode 100755
index 0000000..1ec974d
--- /dev/null
+++ b/vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="current_allocated_bytes_test"
+ ProjectGUID="{4AF7E21D-9D0A-410C-A7DB-7D21DA5166C0}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/current_allocated_bytes_test.exe"
+ LinkIncremental="2"
+ ForceSymbolReferences="__tcmalloc"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/current_allocated_bytes_test.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/current_allocated_bytes_test.exe"
+ LinkIncremental="1"
+ ForceSymbolReferences="__tcmalloc"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath="..\..\src\tests\current_allocated_bytes_test.cc">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\src\windows; ..\..\src"
+ RuntimeLibrary="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\src\windows; ..\..\src"
+ RuntimeLibrary="2"/>
+ </FileConfiguration>
+ </File>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath="..\..\src\windows\config.h">
+ </File>
+ <File
+ RelativePath="..\..\src\config_for_unittests.h">
+ </File>
+ <File
+ RelativePath="..\..\src\windows\port.h">
+ </File>
+ <File
+ RelativePath="..\..\src\base\logging.h">
+ </File>
+ <File
+ RelativePath="..\..\src\google\malloc_extension.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>