From dded2c661060371215ee1c9816c9394f6d39788d Mon Sep 17 00:00:00 2001
From: "mbelshe@google.com"
 <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Tue, 15 Sep 2009 21:10:48 +0000
Subject: [PATCH] Issue 172109: Enable scavenging in RenderThread::IdleHandler.

Landing of Anton Muhin's patch (antonm@google.com)

BUG=none
TEST=none


Review URL: http://codereview.chromium.org/206017

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26264 0039d316-1c4b-4281-b951-d872f2087c98
---
 chrome/chrome.gyp                             |  17 +-
 chrome/renderer/DEPS                          |   1 +
 chrome/renderer/render_thread.cc              |  40 ++-
 third_party/tcmalloc/config_linux.h           |   2 +-
 third_party/tcmalloc/config_win.h             |   2 +-
 .../tcmalloc/google/malloc_extension.h        | 239 +++++++++++++
 third_party/tcmalloc/malloc_extension.cc      | 326 ++++++++++++++++++
 third_party/tcmalloc/tcmalloc.cc              |   5 +
 third_party/tcmalloc/tcmalloc.gyp             |  15 +-
 9 files changed, 625 insertions(+), 22 deletions(-)
 create mode 100644 third_party/tcmalloc/google/malloc_extension.h
 create mode 100644 third_party/tcmalloc/malloc_extension.cc

diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 51ea6ac8b94d1..5066b65c910b0 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -2941,6 +2941,10 @@
         ['OS=="linux"', {
           'dependencies': [
             '../build/linux/system.gyp:gtk',
+            '../third_party/tcmalloc/tcmalloc.gyp:tcmalloc',
+          ],
+          'export_dependent_settings': [
+            '../third_party/tcmalloc/tcmalloc.gyp:tcmalloc',
           ],
         }],
         # Windows-specific rules.
@@ -2948,6 +2952,12 @@
           'include_dirs': [
             'third_party/wtl/include',
           ],
+          'dependencies': [
+            '../third_party/tcmalloc/tcmalloc.gyp:tcmalloc',
+          ],
+          'export_dependent_settings': [
+            '../third_party/tcmalloc/tcmalloc.gyp:tcmalloc',
+          ],
         },],
       ],
     },
@@ -3612,6 +3622,9 @@
         '../testing/gmock.gyp:gmock',
         '../testing/gtest.gyp:gtest',
       ],
+      'export_dependent_settings': [
+        'renderer',
+      ],
       'include_dirs': [
         '..',
       ],
@@ -3692,6 +3705,9 @@
         '../skia/skia.gyp:skia',
         '../testing/gtest.gyp:gtest',
       ],
+      'export_dependent_settings': [
+        'test_support_common',
+      ],
       'include_dirs': [
         '..',
       ],
@@ -4694,7 +4710,6 @@
                 '../net/net.gyp:net_resources',
                 '../build/util/support/support.gyp:*',
                 '../third_party/cld/cld.gyp:cld',
-                '../third_party/tcmalloc/tcmalloc.gyp:tcmalloc',
                 '../views/views.gyp:views',
                 '../webkit/webkit.gyp:webkit_resources',
                 '../gears/gears.gyp:gears',
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 4012ed491c68a..f8b66fe2d58d6 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -13,6 +13,7 @@ include_rules = [
   "+webkit/glue/plugins",
   "+v8/include",
   "+third_party/sqlite/preprocessed",
+  "+third_party/tcmalloc",
 
   # FIXME - refactor code and remove these dependencies
   "+chrome/browser/net",
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index fd076ba67970a..e7c04edda0c16 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -48,6 +48,7 @@
 #include "webkit/extensions/v8/gears_extension.h"
 #include "webkit/extensions/v8/interval_extension.h"
 #include "webkit/extensions/v8/playback_extension.h"
+#include "third_party/tcmalloc/google/malloc_extension.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -459,26 +460,29 @@ void RenderThread::IdleHandler() {
   if (!widget_count_ || hidden_widget_count_ < widget_count_)
     return;
 
-  if (v8::V8::IsDead())
-    return;
-
-  LOG(INFO) << "RenderThread calling v8 IdleNotification for " << this;
-
-  // When V8::IdleNotification returns true, it means that it has cleaned up
-  // as much as it can.  There is no point in continuing to call it.
-  if (!v8::V8::IdleNotification(false)) {
-    // Dampen the delay using the algorithm:
-    //    delay = delay + 1 / (delay + 2)
-    // Using floor(delay) has a dampening effect such as:
-    //    1s, 1, 1, 2, 2, 2, 2, 3, 3, ...
-    idle_notification_delay_in_s_ +=
-        1.0 / (idle_notification_delay_in_s_ + 2.0);
+#if defined(OS_WIN)
+  MallocExtension::instance()->Scavenge();
+#endif
 
-    // Schedule the next timer.
-    MessageLoop::current()->PostDelayedTask(FROM_HERE,
-        task_factory_->NewRunnableMethod(&RenderThread::IdleHandler),
-        static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000);
+  if (!v8::V8::IsDead()) {
+    LOG(INFO) << "RenderThread calling v8 IdleNotification for " << this;
+    v8::V8::IdleNotification(false);
   }
+
+  // Schedule next invocation.
+  // Dampen the delay using the algorithm:
+  //    delay = delay + 1 / (delay + 2)
+  // Using floor(delay) has a dampening effect such as:
+  //    1s, 1, 1, 2, 2, 2, 2, 3, 3, ...
+  // Note that idle_notification_delay_in_s_ would be reset to
+  // kInitialIdleHandlerDelayS in RenderThread::WidgetHidden.
+  idle_notification_delay_in_s_ +=
+      1.0 / (idle_notification_delay_in_s_ + 2.0);
+
+  // Schedule the next timer.
+  MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      task_factory_->NewRunnableMethod(&RenderThread::IdleHandler),
+      static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000);
 }
 
 void RenderThread::OnExtensionMessageInvoke(const std::string& function_name,
diff --git a/third_party/tcmalloc/config_linux.h b/third_party/tcmalloc/config_linux.h
index af127848f85dd..e813dc50de0af 100644
--- a/third_party/tcmalloc/config_linux.h
+++ b/third_party/tcmalloc/config_linux.h
@@ -35,7 +35,7 @@
    (on OSes which have ability) which could be performed offline
    (either by background thread or in idle time.)
    */
-#define DEFER_DECOMMIT 0
+#define DEFER_DECOMMIT 1
 
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #define HAVE_DLFCN_H 1
diff --git a/third_party/tcmalloc/config_win.h b/third_party/tcmalloc/config_win.h
index 51c73c5ddd697..9bca17b8aa68b 100644
--- a/third_party/tcmalloc/config_win.h
+++ b/third_party/tcmalloc/config_win.h
@@ -65,7 +65,7 @@
    (on OSes which have ability) which could be performed offline
    (either by background thread or in idle time.)
    */
-#define DEFER_DECOMMIT 0
+#define DEFER_DECOMMIT 1
 
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
diff --git a/third_party/tcmalloc/google/malloc_extension.h b/third_party/tcmalloc/google/malloc_extension.h
new file mode 100644
index 0000000000000..cfe26edbfdb84
--- /dev/null
+++ b/third_party/tcmalloc/google/malloc_extension.h
@@ -0,0 +1,239 @@
+// Copyright (c) 2005, 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: Sanjay Ghemawat <opensource@google.com>
+//
+// Extra extensions exported by some malloc implementations.  These
+// extensions are accessed through a virtual base class so an
+// application can link against a malloc that does not implement these
+// extensions, and it will get default versions that do nothing.
+//
+// NOTE FOR C USERS: If you wish to use this functionality from within
+// a C program, see malloc_extension_c.h.
+
+#ifndef BASE_MALLOC_EXTENSION_H_
+#define BASE_MALLOC_EXTENSION_H_
+
+#include <stddef.h>
+#include <string>
+
+// Annoying stuff for windows -- makes sure clients can import these functions
+#ifndef PERFTOOLS_DLL_DECL
+# ifdef _WIN32
+#   define PERFTOOLS_DLL_DECL  __declspec(dllimport)
+# else
+#   define PERFTOOLS_DLL_DECL
+# endif
+#endif
+
+static const int kMallocHistogramSize = 64;
+
+// One day, we could support other types of writers (perhaps for C?)
+typedef std::string MallocExtensionWriter;
+
+// The default implementations of the following routines do nothing.
+// All implementations should be thread-safe; the current one
+// (TCMallocImplementation) is.
+class PERFTOOLS_DLL_DECL MallocExtension {
+ public:
+  virtual ~MallocExtension();
+
+  // Call this very early in the program execution -- say, in a global
+  // constructor -- to set up parameters and state needed by all
+  // instrumented malloc implemenatations.  One example: this routine
+  // sets environemnt variables to tell STL to use libc's malloc()
+  // instead of doing its own memory management.  This is safe to call
+  // multiple times, as long as each time is before threads start up.
+  static void Initialize();
+
+  // See "verify_memory.h" to see what these routines do
+  virtual bool VerifyAllMemory();
+  virtual bool VerifyNewMemory(void* p);
+  virtual bool VerifyArrayNewMemory(void* p);
+  virtual bool VerifyMallocMemory(void* p);
+  virtual bool MallocMemoryStats(int* blocks, size_t* total,
+                                 int histogram[kMallocHistogramSize]);
+
+  // Get a human readable description of the current state of the malloc
+  // data structures.  The state is stored as a null-terminated string
+  // in a prefix of "buffer[0,buffer_length-1]".
+  // REQUIRES: buffer_length > 0.
+  virtual void GetStats(char* buffer, int buffer_length);
+
+  // Outputs to "writer" a sample of live objects and the stack traces
+  // that allocated these objects.  The format of the returned output
+  // is equivalent to the output of the heap profiler and can
+  // therefore be passed to "pprof".
+  virtual void GetHeapSample(MallocExtensionWriter* writer);
+
+  // Outputs to "writer" the stack traces that caused growth in the
+  // address space size.  The format of the returned output is
+  // equivalent to the output of the heap profiler and can therefore
+  // be passed to "pprof".
+  virtual void GetHeapGrowthStacks(MallocExtensionWriter* writer);
+
+  // -------------------------------------------------------------------
+  // Control operations for getting and setting malloc implementation
+  // specific parameters.  Some currently useful properties:
+  //
+  // generic
+  // -------
+  // "generic.current_allocated_bytes"
+  //      Number of bytes currently allocated by application
+  //      This property is not writable.
+  //
+  // "generic.heap_size"
+  //      Number of bytes in the heap ==
+  //            current_allocated_bytes +
+  //            fragmentation +
+  //            freed memory regions
+  //      This property is not writable.
+  //
+  // tcmalloc
+  // --------
+  // "tcmalloc.max_total_thread_cache_bytes"
+  //      Upper limit on total number of bytes stored across all
+  //      per-thread caches.  Default: 16MB.
+  //
+  // "tcmalloc.current_total_thread_cache_bytes"
+  //      Number of bytes used across all thread caches.
+  //      This property is not writable.
+  //
+  // "tcmalloc.slack_bytes"
+  //      Number of bytes allocated from system, but not currently
+  //      in use by malloced objects.  I.e., bytes available for
+  //      allocation without needing more bytes from system.
+  //      This property is not writable.
+  //
+  // TODO: Add more properties as necessary
+  // -------------------------------------------------------------------
+
+  // Get the named "property"'s value.  Returns true if the property
+  // is known.  Returns false if the property is not a valid property
+  // name for the current malloc implementation.
+  // REQUIRES: property != NULL; value != NULL
+  virtual bool GetNumericProperty(const char* property, size_t* value);
+
+  // Set the named "property"'s value.  Returns true if the property
+  // is known and writable.  Returns false if the property is not a
+  // valid property name for the current malloc implementation, or
+  // is not writable.
+  // REQUIRES: property != NULL
+  virtual bool SetNumericProperty(const char* property, size_t value);
+
+  // Mark the current thread as "idle".  This routine may optionally
+  // be called by threads as a hint to the malloc implementation that
+  // any thread-specific resources should be released.  Note: this may
+  // be an expensive routine, so it should not be called too often.
+  //
+  // Also, if the code that calls this routine will go to sleep for
+  // a while, it should take care to not allocate anything between
+  // the call to this routine and the beginning of the sleep.
+  //
+  // Most malloc implementations ignore this routine.
+  virtual void MarkThreadIdle();
+
+  // Scavenge at least some resources and free them back to OS.
+  // This method doesn't promise to do anything useful (it might be
+  // implemented as noop), but it's a good idea to invoke it when
+  // application is idle.
+  virtual void Scavenge();
+
+  // Try to free memory back to the operating system for reuse.  Only
+  // use this extension if the application has recently freed a lot of
+  // memory, and does not anticipate using it again for a long time --
+  // to get this memory back may require faulting pages back in by the
+  // OS, and that may be slow.  (Currently only implemented in
+  // tcmalloc.)
+  virtual void ReleaseFreeMemory();
+
+  // Sets the rate at which we release unused memory to the system.
+  // Zero means we never release memory back to the system.  Increase
+  // this flag to return memory faster; decrease it to return memory
+  // slower.  Reasonable rates are in the range [0,10].  (Currently
+  // only implemented in tcmalloc).
+  virtual void SetMemoryReleaseRate(double rate);
+
+  // Gets the release rate.  Returns a value < 0 if unknown.
+  virtual double GetMemoryReleaseRate();
+
+  // Returns the estimated number of bytes that will be allocated for
+  // a request of "size" bytes.  This is an estimate: an allocation of
+  // SIZE bytes may reserve more bytes, but will never reserve less.
+  // (Currently only implemented in tcmalloc, other implementations
+  // always return SIZE.)
+  virtual size_t GetEstimatedAllocatedSize(size_t size);
+
+  // Returns the actual number of bytes reserved by tcmalloc for the
+  // pointer p.  This number may be equal to or greater than
+  // the number of bytes requested when p was allocated.
+  // p must have been allocated by this malloc implementation,
+  // must not be an interior pointer -- that is, must be exactly
+  // the pointer returned to by malloc() et al., not some offset
+  // from that -- and should not have been freed yet.  p may be NULL.
+  // (Currently only implemented in tcmalloc; other implementations
+  // will return 0.)
+  virtual size_t GetAllocatedSize(void* p);
+
+  // The current malloc implementation.  Always non-NULL.
+  static MallocExtension* instance();
+
+  // Change the malloc implementation.  Typically called by the
+  // malloc implementation during initialization.
+  static void Register(MallocExtension* implementation);
+
+ protected:
+  // Get a list of stack traces of sampled allocation points.  Returns
+  // a pointer to a "new[]-ed" result array, and stores the sample
+  // period in "sample_period".
+  //
+  // The state is stored as a sequence of adjacent entries
+  // in the returned array.  Each entry has the following form:
+  //    uintptr_t count;        // Number of objects with following trace
+  //    uintptr_t size;         // Total size of objects with following trace
+  //    uintptr_t depth;        // Number of PC values in stack trace
+  //    void*     stack[depth]; // PC values that form the stack trace
+  //
+  // The list of entries is terminated by a "count" of 0.
+  //
+  // It is the responsibility of the caller to "delete[]" the returned array.
+  //
+  // May return NULL to indicate no results.
+  //
+  // This is an internal extension.  Callers should use the more
+  // convenient "GetHeapSample(string*)" method defined above.
+  virtual void** ReadStackTraces(int* sample_period);
+
+  // Like ReadStackTraces(), but returns stack traces that caused growth
+  // in the address space size.
+  virtual void** ReadHeapGrowthStackTraces();
+};
+
+#endif  // BASE_MALLOC_EXTENSION_H_
diff --git a/third_party/tcmalloc/malloc_extension.cc b/third_party/tcmalloc/malloc_extension.cc
new file mode 100644
index 0000000000000..befb416e09421
--- /dev/null
+++ b/third_party/tcmalloc/malloc_extension.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2005, 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: Sanjay Ghemawat <opensource@google.com>
+
+#include <config.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
+#if defined HAVE_STDINT_H
+#include <stdint.h>
+#elif defined HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <sys/types.h>
+#endif
+#include <string>
+#include "base/dynamic_annotations.h"
+#include "base/sysinfo.h"    // for FillProcSelfMaps
+#include "google/malloc_extension.h"
+#include "maybe_threads.h"
+
+using STL_NAMESPACE::string;
+
+static void DumpAddressMap(string* result) {
+  *result += "\nMAPPED_LIBRARIES:\n";
+  // We keep doubling until we get a fit
+  const size_t old_resultlen = result->size();
+  for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) {
+    result->resize(old_resultlen + amap_size);
+    const int bytes_written =
+        tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size);
+    if (bytes_written < amap_size - 1) {   // we fit!
+      (*result)[old_resultlen + bytes_written] = '\0';
+      result->resize(old_resultlen + bytes_written);
+      return;
+    }
+  }
+  result->reserve(old_resultlen);   // just don't print anything
+}
+
+// Note: this routine is meant to be called before threads are spawned.
+void MallocExtension::Initialize() {
+  static bool initialize_called = false;
+
+  if (initialize_called) return;
+  initialize_called = true;
+
+#ifdef __GLIBC__
+  // GNU libc++ versions 3.3 and 3.4 obey the environment variables
+  // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively.  Setting
+  // one of these variables forces the STL default allocator to call
+  // new() or delete() for each allocation or deletion.  Otherwise
+  // the STL allocator tries to avoid the high cost of doing
+  // allocations by pooling memory internally.  However, tcmalloc
+  // does allocations really fast, especially for the types of small
+  // items one sees in STL, so it's better off just using us.
+  // TODO: control whether we do this via an environment variable?
+  setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/);
+  setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/);
+
+  // Now we need to make the setenv 'stick', which it may not do since
+  // the env is flakey before main() is called.  But luckily stl only
+  // looks at this env var the first time it tries to do an alloc, and
+  // caches what it finds.  So we just cause an stl alloc here.
+  string dummy("I need to be allocated");
+  dummy += "!";         // so the definition of dummy isn't optimized out
+#endif  /* __GLIBC__ */
+}
+
+// Default implementation -- does nothing
+MallocExtension::~MallocExtension() { }
+bool MallocExtension::VerifyAllMemory() { return true; }
+bool MallocExtension::VerifyNewMemory(void* p) { return true; }
+bool MallocExtension::VerifyArrayNewMemory(void* p) { return true; }
+bool MallocExtension::VerifyMallocMemory(void* p) { return true; }
+
+bool MallocExtension::GetNumericProperty(const char* property, size_t* value) {
+  return false;
+}
+
+bool MallocExtension::SetNumericProperty(const char* property, size_t value) {
+  return false;
+}
+
+void MallocExtension::GetStats(char* buffer, int length) {
+  assert(length > 0);
+  buffer[0] = '\0';
+}
+
+bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total,
+                                       int histogram[kMallocHistogramSize]) {
+  *blocks = 0;
+  *total = 0;
+  memset(histogram, 0, sizeof(histogram));
+  return true;
+}
+
+void** MallocExtension::ReadStackTraces(int* sample_period) {
+  return NULL;
+}
+
+void** MallocExtension::ReadHeapGrowthStackTraces() {
+  return NULL;
+}
+
+void MallocExtension::MarkThreadIdle() {
+  // Default implementation does nothing
+}
+
+void MallocExtension::ReleaseFreeMemory() {
+  // Default implementation does nothing
+}
+
+void MallocExtension::Scavenge() {
+  // Default implementation does nothing
+}
+
+void MallocExtension::SetMemoryReleaseRate(double rate) {
+  // Default implementation does nothing
+}
+
+double MallocExtension::GetMemoryReleaseRate() {
+  return -1.0;
+}
+
+size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) {
+  return size;
+}
+
+size_t MallocExtension::GetAllocatedSize(void* p) {
+  return 0;
+}
+
+// The current malloc extension object.  We also keep a pointer to
+// the default implementation so that the heap-leak checker does not
+// complain about a memory leak.
+
+static pthread_once_t module_init = PTHREAD_ONCE_INIT;
+static MallocExtension* default_instance = NULL;
+static MallocExtension* current_instance = NULL;
+
+static void InitModule() {
+  default_instance = new MallocExtension;
+  current_instance = default_instance;
+}
+
+MallocExtension* MallocExtension::instance() {
+  perftools_pthread_once(&module_init, InitModule);
+  return current_instance;
+}
+
+void MallocExtension::Register(MallocExtension* implementation) {
+  perftools_pthread_once(&module_init, InitModule);
+  // When running under valgrind, our custom malloc is replaced with
+  // valgrind's one and malloc extensions will not work.
+  if (!RunningOnValgrind()) {
+    current_instance = implementation;
+  }
+}
+
+// -----------------------------------------------------------------------
+// Heap sampling support
+// -----------------------------------------------------------------------
+
+namespace {
+
+// Accessors
+uintptr_t Count(void** entry) {
+  return reinterpret_cast<uintptr_t>(entry[0]);
+}
+uintptr_t Size(void** entry) {
+  return reinterpret_cast<uintptr_t>(entry[1]);
+}
+uintptr_t Depth(void** entry) {
+  return reinterpret_cast<uintptr_t>(entry[2]);
+}
+void* PC(void** entry, int i) {
+  return entry[3+i];
+}
+
+void PrintCountAndSize(MallocExtensionWriter* writer,
+                       uintptr_t count, uintptr_t size) {
+  char buf[100];
+  snprintf(buf, sizeof(buf),
+           "%6lld: %8lld [%6lld: %8lld] @",
+           static_cast<long long>(count),
+           static_cast<long long>(size),
+           static_cast<long long>(count),
+           static_cast<long long>(size));
+  writer->append(buf, strlen(buf));
+}
+
+void PrintHeader(MallocExtensionWriter* writer,
+                 const char* label, void** entries) {
+  // Compute the total count and total size
+  uintptr_t total_count = 0;
+  uintptr_t total_size = 0;
+  for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
+    total_count += Count(entry);
+    total_size += Size(entry);
+  }
+
+  const char* const kTitle = "heap profile: ";
+  writer->append(kTitle, strlen(kTitle));
+  PrintCountAndSize(writer, total_count, total_size);
+  writer->append(" ", 1);
+  writer->append(label, strlen(label));
+  writer->append("\n", 1);
+}
+
+void PrintStackEntry(MallocExtensionWriter* writer, void** entry) {
+  PrintCountAndSize(writer, Count(entry), Size(entry));
+
+  for (int i = 0; i < Depth(entry); i++) {
+    char buf[32];
+    snprintf(buf, sizeof(buf), " %p", PC(entry, i));
+    writer->append(buf, strlen(buf));
+  }
+  writer->append("\n", 1);
+}
+
+}
+
+void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) {
+  int sample_period = 0;
+  void** entries = ReadStackTraces(&sample_period);
+  if (entries == NULL) {
+    const char* const kErrorMsg =
+        "This malloc implementation does not support sampling.\n"
+        "As of 2005/01/26, only tcmalloc supports sampling, and\n"
+        "you are probably running a binary that does not use\n"
+        "tcmalloc.\n";
+    writer->append(kErrorMsg, strlen(kErrorMsg));
+    return;
+  }
+
+  char label[32];
+  sprintf(label, "heap_v2/%d", sample_period);
+  PrintHeader(writer, label, entries);
+  for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
+    PrintStackEntry(writer, entry);
+  }
+  delete[] entries;
+
+  DumpAddressMap(writer);
+}
+
+void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) {
+  void** entries = ReadHeapGrowthStackTraces();
+  if (entries == NULL) {
+    const char* const kErrorMsg =
+        "This malloc implementation does not support "
+        "ReadHeapGrowthStackTraces().\n"
+        "As of 2005/09/27, only tcmalloc supports this, and you\n"
+        "are probably running a binary that does not use tcmalloc.\n";
+    writer->append(kErrorMsg, strlen(kErrorMsg));
+    return;
+  }
+
+  // Do not canonicalize the stack entries, so that we get a
+  // time-ordered list of stack traces, which may be useful if the
+  // client wants to focus on the latest stack traces.
+  PrintHeader(writer, "growth", entries);
+  for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
+    PrintStackEntry(writer, entry);
+  }
+  delete[] entries;
+
+  DumpAddressMap(writer);
+}
+
+// These are C shims that work on the current instance.
+
+#define C_SHIM(fn, retval, paramlist, arglist)          \
+  extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist {    \
+    return MallocExtension::instance()->fn arglist;     \
+  }
+
+C_SHIM(VerifyAllMemory, bool, (), ());
+C_SHIM(VerifyNewMemory, bool, (void* p), (p));
+C_SHIM(VerifyArrayNewMemory, bool, (void* p), (p));
+C_SHIM(VerifyMallocMemory, bool, (void* p), (p));
+C_SHIM(MallocMemoryStats, bool,
+       (int* blocks, size_t* total, int histogram[kMallocHistogramSize]),
+       (blocks, total, histogram));
+
+C_SHIM(GetStats, void,
+       (char* buffer, int buffer_length), (buffer, buffer_length));
+C_SHIM(GetNumericProperty, bool,
+       (const char* property, size_t* value), (property, value));
+C_SHIM(SetNumericProperty, bool,
+       (const char* property, size_t value), (property, value));
+
+C_SHIM(MarkThreadIdle, void, (), ());
+C_SHIM(ReleaseFreeMemory, void, (), ());
+C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size));
+C_SHIM(GetAllocatedSize, size_t, (void* p), (p));
diff --git a/third_party/tcmalloc/tcmalloc.cc b/third_party/tcmalloc/tcmalloc.cc
index e3bbb819c44d5..6853ebaeba26d 100644
--- a/third_party/tcmalloc/tcmalloc.cc
+++ b/third_party/tcmalloc/tcmalloc.cc
@@ -616,6 +616,11 @@ class TCMallocImplementation : public MallocExtension {
     Static::pageheap()->ReleaseFreePages();
   }
 
+  virtual void Scavenge() {
+    SpinLockHolder h(Static::pageheap_lock());
+    Static::pageheap()->Scavenge();
+  }
+
   virtual void SetMemoryReleaseRate(double rate) {
     FLAGS_tcmalloc_release_rate = rate;
   }
diff --git a/third_party/tcmalloc/tcmalloc.gyp b/third_party/tcmalloc/tcmalloc.gyp
index ddc85eef69e22..d3fdb39017f00 100644
--- a/third_party/tcmalloc/tcmalloc.gyp
+++ b/third_party/tcmalloc/tcmalloc.gyp
@@ -44,6 +44,13 @@
             },
           },
         },
+        'conditions': [
+          ['OS=="win"', {
+            'defines': [
+              ['PERFTOOLS_DLL_DECL', '']
+            ],
+          }],
+        ],
       },
       'sources': [
         'config.h',
@@ -70,7 +77,6 @@
         'tcmalloc/src/internal_logging.cc',
         'tcmalloc/src/internal_logging.h',
         'tcmalloc/src/linked_list.h',
-        'tcmalloc/src/malloc_extension.cc',
         'tcmalloc/src/malloc_hook.cc',
         'tcmalloc/src/malloc_hook-inl.h',
         'tcmalloc/src/page_heap.cc',
@@ -120,6 +126,8 @@
         # tcmalloc forked files
         'allocator_shim.cc',
         'generic_allocators.cc',
+        'malloc_extension.cc',
+        'google/malloc_extension.h',
         'page_heap.cc',
         'page_heap.h',
         'port.cc',
@@ -141,6 +149,8 @@
         'generic_allocators.cc',
         'tcmalloc.cc',
         'win_allocator.cc',
+        'tcmalloc/src/malloc_extension.cc',
+        'tcmalloc/src/google/malloc_extension.h',
       ],
       'msvs_settings': {
         # TODO(sgk):  merge this with build/common.gypi settings
@@ -164,6 +174,9 @@
       },
       'conditions': [
         ['OS=="win"', {
+          'defines': [
+            ['PERFTOOLS_DLL_DECL', '']
+          ],
           'dependencies': [
             'libcmt',
           ],
-- 
GitLab