diff options
author | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2011-03-21 21:41:55 +0000 |
---|---|---|
committer | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2011-03-21 21:41:55 +0000 |
commit | 1d30e525ae6ac38ae381bb3118f7f47998af2942 (patch) | |
tree | a8349a4801e90c96515f3c0329a5a6c1813fc27f | |
parent | 6fe07cd2c0527e18276cc79a57e2212a4b048746 (diff) | |
download | gperftools-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.am | 9 | ||||
-rw-r--r-- | Makefile.in | 82 | ||||
-rwxr-xr-x | google-perftools.sln | 5 | ||||
-rw-r--r-- | src/base/atomicops-internals-arm-generic.h | 236 | ||||
-rw-r--r-- | src/base/atomicops-internals-arm-v6plus.h | 244 | ||||
-rw-r--r-- | src/base/atomicops.h | 8 | ||||
-rw-r--r-- | src/central_freelist.cc | 16 | ||||
-rw-r--r-- | src/central_freelist.h | 6 | ||||
-rw-r--r-- | src/google/malloc_hook.h | 181 | ||||
-rw-r--r-- | src/google/malloc_hook_c.h | 52 | ||||
-rw-r--r-- | src/heap-checker-bcad.cc | 8 | ||||
-rw-r--r-- | src/heap-checker.cc | 123 | ||||
-rw-r--r-- | src/heap-profiler.cc | 76 | ||||
-rw-r--r-- | src/malloc_hook-inl.h | 99 | ||||
-rw-r--r-- | src/malloc_hook.cc | 450 | ||||
-rw-r--r-- | src/memory_region_map.cc | 32 | ||||
-rw-r--r-- | src/memory_region_map.h | 5 | ||||
-rw-r--r-- | src/tcmalloc.cc | 37 | ||||
-rw-r--r-- | src/tests/current_allocated_bytes_test.cc | 63 | ||||
-rw-r--r-- | src/tests/low_level_alloc_unittest.cc | 16 | ||||
-rw-r--r-- | src/tests/malloc_extension_c_test.c | 14 | ||||
-rw-r--r-- | src/tests/tcmalloc_unittest.cc | 19 | ||||
-rwxr-xr-x | vsprojects/current_allocated_bytes_test/current_allocated_bytes_test.vcproj | 155 |
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>
|