source: webkit/trunk/JavaScriptCore/wtf/FastMalloc.cpp@ 14256

Last change on this file since 14256 was 14256, checked in by mjs, 19 years ago

JavaScriptCore:

Rubber stamped by Anders.


  • renamed kxmlcore to wtf


kxmlcore --> wtf
KXMLCore --> WTF
WKC --> WTF

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • bindings/c/c_instance.cpp:
  • bindings/objc/WebScriptObject.mm:
  • kjs/JSImmediate.h:
  • kjs/Parser.cpp:
  • kjs/Parser.h:
  • kjs/array_object.cpp:
  • kjs/collector.cpp: (KJS::Collector::registerThread):
  • kjs/collector.h:
  • kjs/config.h:
  • kjs/function.cpp: (KJS::isStrWhiteSpace):
  • kjs/function.h:
  • kjs/identifier.cpp:
  • kjs/internal.cpp:
  • kjs/internal.h:
  • kjs/lexer.cpp: (Lexer::shift): (Lexer::isWhiteSpace): (Lexer::isIdentStart): (Lexer::isIdentPart):
  • kjs/lookup.cpp:
  • kjs/nodes.cpp:
  • kjs/nodes.h:
  • kjs/number_object.cpp:
  • kjs/object.h:
  • kjs/property_map.cpp:
  • kjs/property_map.h:
  • kjs/string_object.cpp: (StringProtoFunc::callAsFunction):
  • kjs/testkjs.cpp: (testIsInteger):
  • kjs/ustring.cpp:
  • kjs/ustring.h:
  • kxmlcore: Removed.
  • kxmlcore/AlwaysInline.h: Removed.
  • kxmlcore/Assertions.cpp: Removed.
  • kxmlcore/Assertions.h: Removed.
  • kxmlcore/FastMalloc.cpp: Removed.
  • kxmlcore/FastMalloc.h: Removed.
  • kxmlcore/FastMallocInternal.h: Removed.
  • kxmlcore/Forward.h: Removed.
  • kxmlcore/HashCountedSet.h: Removed.
  • kxmlcore/HashFunctions.h: Removed.
  • kxmlcore/HashMap.h: Removed.
  • kxmlcore/HashSet.h: Removed.
  • kxmlcore/HashTable.cpp: Removed.
  • kxmlcore/HashTable.h: Removed.
  • kxmlcore/HashTraits.h: Removed.
  • kxmlcore/ListRefPtr.h: Removed.
  • kxmlcore/Noncopyable.h: Removed.
  • kxmlcore/OwnArrayPtr.h: Removed.
  • kxmlcore/OwnPtr.h: Removed.
  • kxmlcore/PassRefPtr.h: Removed.
  • kxmlcore/Platform.h: Removed.
  • kxmlcore/RefPtr.h: Removed.
  • kxmlcore/TCPageMap.h: Removed.
  • kxmlcore/TCSpinLock.h: Removed.
  • kxmlcore/TCSystemAlloc.cpp: Removed.
  • kxmlcore/TCSystemAlloc.h: Removed.
  • kxmlcore/UnusedParam.h: Removed.
  • kxmlcore/Vector.h: Removed.
  • kxmlcore/VectorTraits.h: Removed.
  • kxmlcore/unicode: Removed.
  • kxmlcore/unicode/Unicode.h: Removed.
  • kxmlcore/unicode/UnicodeCategory.h: Removed.
  • kxmlcore/unicode/icu: Removed.
  • kxmlcore/unicode/icu/UnicodeIcu.h: Removed.
  • kxmlcore/unicode/posix: Removed.
  • kxmlcore/unicode/qt3: Removed.
  • kxmlcore/unicode/qt4: Removed.
  • kxmlcore/unicode/qt4/UnicodeQt4.h: Removed.
  • pcre/pcre_get.c:
  • wtf: Added.
  • wtf/Assertions.cpp:
  • wtf/Assertions.h:
  • wtf/FastMalloc.cpp: (WTF::TCMalloc_ThreadCache::Scavenge): (WTF::do_malloc): (WTF::do_free): (WTF::TCMallocGuard::TCMallocGuard): (WTF::malloc): (WTF::free): (WTF::calloc): (WTF::cfree): (WTF::realloc):
  • wtf/FastMalloc.h:
  • wtf/FastMallocInternal.h:
  • wtf/Forward.h:
  • wtf/HashCountedSet.h:
  • wtf/HashFunctions.h:
  • wtf/HashMap.h:
  • wtf/HashSet.h:
  • wtf/HashTable.cpp:
  • wtf/HashTable.h:
  • wtf/HashTraits.h:
  • wtf/ListRefPtr.h:
  • wtf/Noncopyable.h:
  • wtf/OwnArrayPtr.h:
  • wtf/OwnPtr.h:
  • wtf/PassRefPtr.h:
  • wtf/RefPtr.h:
  • wtf/TCSystemAlloc.cpp: (TCMalloc_SystemAlloc):
  • wtf/Vector.h:
  • wtf/VectorTraits.h:
  • wtf/unicode/UnicodeCategory.h:
  • wtf/unicode/icu/UnicodeIcu.h:

JavaScriptGlue:

Rubber stamped by Anders.


  • renamed kxmlcore to wtf


kxmlcore --> wtf
KXMLCore --> WTF
WKC --> WTF

  • config.h:
  • kxmlcore: Removed.
  • kxmlcore/AlwaysInline.h: Removed.
  • kxmlcore/Assertions.h: Removed.
  • kxmlcore/FastMalloc.h: Removed.
  • kxmlcore/Forward.h: Removed.
  • kxmlcore/HashCountedSet.h: Removed.
  • kxmlcore/HashSet.h: Removed.
  • kxmlcore/Noncopyable.h: Removed.
  • kxmlcore/OwnArrayPtr.h: Removed.
  • kxmlcore/OwnPtr.h: Removed.
  • kxmlcore/PassRefPtr.h: Removed.
  • kxmlcore/Platform.h: Removed.
  • kxmlcore/RefPtr.h: Removed.
  • kxmlcore/Vector.h: Removed.
  • wtf: Added.

WebCore:

Rubber stamped by Anders.


  • renamed kxmlcore to wtf


kxmlcore --> wtf
KXMLCore --> WTF
WKC --> WTF

  • ForwardingHeaders/kxmlcore: Removed.
  • ForwardingHeaders/kxmlcore/AlwaysInline.h: Removed.
  • ForwardingHeaders/kxmlcore/Assertions.h: Removed.
  • ForwardingHeaders/kxmlcore/FastMalloc.h: Removed.
  • ForwardingHeaders/kxmlcore/Forward.h: Removed.
  • ForwardingHeaders/kxmlcore/HashCountedSet.h: Removed.
  • ForwardingHeaders/kxmlcore/HashMap.h: Removed.
  • ForwardingHeaders/kxmlcore/HashSet.h: Removed.
  • ForwardingHeaders/kxmlcore/HashTraits.h: Removed.
  • ForwardingHeaders/kxmlcore/Noncopyable.h: Removed.
  • ForwardingHeaders/kxmlcore/OwnArrayPtr.h: Removed.
  • ForwardingHeaders/kxmlcore/OwnPtr.h: Removed.
  • ForwardingHeaders/kxmlcore/PassRefPtr.h: Removed.
  • ForwardingHeaders/kxmlcore/Platform.h: Removed.
  • ForwardingHeaders/kxmlcore/RefPtr.h: Removed.
  • ForwardingHeaders/kxmlcore/Vector.h: Removed.
  • ForwardingHeaders/wtf: Added.
  • bindings/js/JSHTMLElementWrapperFactory.h:
  • bindings/js/kjs_binding.cpp:
  • bindings/js/kjs_window.h:
  • bindings/objc/DOMImplementationFront.h:
  • bridge/JavaAppletWidget.h:
  • bridge/mac/WebCoreFrameNamespaces.mm:
  • bridge/mac/WebCorePageBridge.mm: (initializeLogChannel):
  • bridge/mac/WebCoreStringTruncator.mm:
  • bridge/mac/WebCoreViewFactory.m:
  • config.h:
  • css/css_base.h:
  • css/css_valueimpl.h:
  • css/csshelper.cpp:
  • css/cssparser.h:
  • dom/DOMImplementation.h:
  • dom/Document.h:
  • dom/NamedNodeMap.h:
  • dom/Node.h:
  • dom/NodeList.h:
  • dom/QualifiedName.cpp:
  • dom/Range.h:
  • dom/StyledElement.cpp:
  • dom/dom2_traversalimpl.h:
  • dom/xml_tokenizer.h:
  • editing/RebalanceWhitespaceCommand.cpp:
  • editing/RemoveCSSPropertyCommand.cpp:
  • editing/RemoveNodeAttributeCommand.cpp:
  • editing/RemoveNodeCommand.cpp:
  • editing/RemoveNodePreservingChildrenCommand.cpp:
  • editing/ReplaceSelectionCommand.h:
  • editing/Selection.cpp:
  • editing/SetNodeAttributeCommand.cpp:
  • editing/SplitElementCommand.cpp:
  • editing/SplitTextNodeCommand.cpp:
  • editing/SplitTextNodeContainingElementCommand.cpp:
  • editing/TextIterator.h:
  • editing/htmlediting.h:
  • editing/markup.h:
  • html/CanvasGradient.h:
  • html/CanvasRenderingContext2D.h:
  • html/CanvasStyle.cpp:
  • html/HTMLCollection.h:
  • html/HTMLElementFactory.h:
  • kcanvas/KCanvasFilters.cpp:
  • kcanvas/KCanvasPath.h:
  • kcanvas/RenderPath.cpp:
  • kcanvas/RenderSVGImage.cpp:
  • kcanvas/RenderSVGText.cpp:
  • kcanvas/device/quartz/KCanvasItemQuartz.mm:
  • kcanvas/device/quartz/KRenderingPaintServerGradientQuartz.mm:
  • kcanvas/device/quartz/QuartzSupport.mm:
  • ksvg2/misc/KSVGTimeScheduler.h:
  • ksvg2/misc/SVGDocumentExtensions.h:
  • ksvg2/scripts/make_names.pl:
  • ksvg2/svg/SVGDOMImplementation.cpp:
  • ksvg2/svg/SVGExternalResourcesRequired.h:
  • ksvg2/svg/SVGFilterPrimitiveStandardAttributes.cpp:
  • ksvg2/svg/SVGForeignObjectElement.cpp:
  • ksvg2/svg/SVGImageElement.cpp:
  • ksvg2/svg/SVGMaskElement.cpp:
  • ksvg2/svg/SVGStyledElement.cpp:
  • ksvg2/svg/SVGTests.h:
  • ksvg2/svg/SVGTransform.h:
  • ksvg2/svg/SVGTransformable.cpp:
  • kwq/AccessibilityObjectCache.h:
  • kwq/KWQCString.cpp:
  • kwq/KWQFormData.mm:
  • kwq/KWQListBox.mm:
  • kwq/KWQResourceLoader.mm:
  • kwq/KWQTextEdit.mm:
  • loader/Cache.h:
  • loader/CachedObject.h:
  • loader/CachedObjectClientWalker.h:
  • loader/Decoder.h:
  • loader/DocLoader.h:
  • loader/loader.cpp:
  • loader/loader.h:
  • page/DOMWindow.h:
  • page/Frame.h:
  • page/FramePrivate.h:
  • page/FrameTree.cpp:
  • page/Page.cpp:
  • page/Page.h:
  • page/Plugin.h:
  • platform/Arena.cpp:
  • platform/ArrayImpl.h:
  • platform/AtomicString.cpp:
  • platform/CharsetNames.cpp:
  • platform/Color.cpp:
  • platform/DeprecatedPtrListImpl.cpp:
  • platform/DeprecatedValueListImpl.h:
  • platform/FontFallbackList.h:
  • platform/GraphicsContext.h:
  • platform/GraphicsTypes.cpp:
  • platform/Image.h:
  • platform/KURL.cpp:
  • platform/Logging.cpp:
  • platform/Logging.h:
  • platform/PlatformString.h:
  • platform/PlugInInfoStore.h:
  • platform/StreamingTextDecoder.cpp:
  • platform/StreamingTextDecoder.h:
  • platform/String.cpp:
  • platform/StringHash.h:
  • platform/StringImpl.cpp:
  • platform/StringImpl.h:
  • platform/TextEncoding.cpp:
  • platform/Timer.cpp:
  • platform/Timer.h:
  • platform/TransferJob.h:
  • platform/TransferJobInternal.h:
  • platform/mac/BlockExceptions.mm:
  • platform/mac/ColorMac.mm:
  • platform/mac/FontData.mm:
  • platform/mac/KURLMac.mm:
  • platform/mac/QStringMac.mm:
  • platform/mac/SharedTimerMac.cpp:
  • platform/mac/TextEncodingMac.cpp:
  • platform/mac/WebCoreImageRendererFactory.m:
  • platform/mac/WebCoreKeyGenerator.m:
  • platform/mac/WebCoreTextArea.mm:
  • platform/mac/WebCoreTextField.mm:
  • platform/mac/WebTextRendererFactory.h:
  • platform/mac/WebTextRendererFactory.mm:
  • platform/win/TemporaryLinkStubs.cpp: (JavaAppletWidget::JavaAppletWidget):
  • rendering/InlineTextBox.cpp:
  • rendering/RenderText.cpp:
  • rendering/RenderTreeAsText.cpp:
  • rendering/bidi.cpp:
  • xml/XSLTProcessor.h:
  • xpath/impl/XPathExpressionNode.h:
  • xpath/impl/XPathParser.h:
  • xpath/impl/XPathPath.h:
  • xpath/impl/XPathUtil.h:

WebKit:

Rubber stamped by Anders.


  • renamed kxmlcore to wtf


kxmlcore --> wtf
KXMLCore --> WTF
WKC --> WTF

  • Misc/WebKitLogging.h:
  • Misc/WebKitLogging.m: (initializeLogChannel):
  • Property svn:eol-style set to native
File size: 69.5 KB
Line 
1// Copyright (c) 2005, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// ---
31// Author: Sanjay Ghemawat <[email protected]>
32//
33// A malloc that uses a per-thread cache to satisfy small malloc requests.
34// (The time for malloc/free of a small object drops from 300 ns to 50 ns.)
35//
36// See doc/tcmalloc.html for a high-level
37// description of how this malloc works.
38//
39// SYNCHRONIZATION
40// 1. The thread-specific lists are accessed without acquiring any locks.
41// This is safe because each such list is only accessed by one thread.
42// 2. We have a lock per central free-list, and hold it while manipulating
43// the central free list for a particular size.
44// 3. The central page allocator is protected by "pageheap_lock".
45// 4. The pagemap (which maps from page-number to descriptor),
46// can be read without holding any locks, and written while holding
47// the "pageheap_lock".
48//
49// This multi-threaded access to the pagemap is safe for fairly
50// subtle reasons. We basically assume that when an object X is
51// allocated by thread A and deallocated by thread B, there must
52// have been appropriate synchronization in the handoff of object
53// X from thread A to thread B.
54//
55// TODO: Bias reclamation to larger addresses
56// TODO: implement mallinfo/mallopt
57// TODO: Better testing
58// TODO: Return memory to system
59//
60// 9/28/2003 (new page-level allocator replaces ptmalloc2):
61// * malloc/free of small objects goes from ~300 ns to ~50 ns.
62// * allocation of a reasonably complicated struct
63// goes from about 1100 ns to about 300 ns.
64
65#include "config.h"
66#include "FastMalloc.h"
67
68#ifndef USE_SYSTEM_MALLOC
69#ifndef NDEBUG
70#define USE_SYSTEM_MALLOC 1
71#else
72#define USE_SYSTEM_MALLOC 0
73#endif
74#endif
75
76#if USE_SYSTEM_MALLOC
77
78#include <stdlib.h>
79#if !PLATFORM(WIN_OS)
80 #include <pthread.h>
81#endif
82
83namespace WTF {
84
85void *fastMalloc(size_t n)
86{
87 return malloc(n);
88}
89
90void *fastCalloc(size_t n_elements, size_t element_size)
91{
92 return calloc(n_elements, element_size);
93}
94
95void fastFree(void* p)
96{
97 free(p);
98}
99
100void *fastRealloc(void* p, size_t n)
101{
102 return realloc(p, n);
103}
104
105#if !PLATFORM(WIN_OS)
106void fastMallocRegisterThread(pthread_t)
107{
108}
109#endif
110
111} // namespace WTF
112
113#else
114
115#if HAVE(STDINT_H)
116#include <stdint.h>
117#elif HAVE(INTTYPES_H)
118#include <inttypes.h>
119#else
120#include <sys/types.h>
121#endif
122
123#include "AlwaysInline.h"
124#include "Assertions.h"
125#include "TCPageMap.h"
126#include "TCSpinLock.h"
127#include "TCSystemAlloc.h"
128#include <errno.h>
129#include <new>
130#include <pthread.h>
131#include <stdarg.h>
132#include <stddef.h>
133#include <stdio.h>
134#include <string.h>
135#include <unistd.h>
136
137#if WTF_CHANGES
138namespace WTF {
139
140#define malloc fastMalloc
141#define calloc fastCalloc
142#define free fastFree
143#define realloc fastRealloc
144
145#define MESSAGE LOG_ERROR
146#define CHECK_CONDITION ASSERT
147
148#endif
149
150#if HAVE(INTTYPES_H)
151#define __STDC_FORMAT_MACROS
152#include <inttypes.h>
153#define LLU PRIu64
154#else
155#define LLU "llu" // hope for the best
156#endif
157
158//-------------------------------------------------------------------
159// Configuration
160//-------------------------------------------------------------------
161
162// Not all possible combinations of the following parameters make
163// sense. In particular, if kMaxSize increases, you may have to
164// increase kNumClasses as well.
165static const size_t kPageShift = 12;
166static const size_t kPageSize = 1 << kPageShift;
167static const size_t kMaxSize = 8u * kPageSize;
168static const size_t kAlignShift = 3;
169static const size_t kAlignment = 1 << kAlignShift;
170static const size_t kNumClasses = 170;
171static const size_t kMaxTinySize = 1 << 8;
172
173// Minimum number of pages to fetch from system at a time. Must be
174// significantly bigger than kBlockSize to amortize system-call
175// overhead, and also to reduce external fragementation. Also, we
176// should keep this value big because various incarnations of Linux
177// have small limits on the number of mmap() regions per
178// address-space.
179static const size_t kMinSystemAlloc = 1 << (20 - kPageShift);
180
181// Number of objects to move between a per-thread list and a central
182// list in one shot. We want this to be not too small so we can
183// amortize the lock overhead for accessing the central list. Making
184// it too big may temporarily cause unnecessary memory wastage in the
185// per-thread free list until the scavenger cleans up the list.
186static const int kNumObjectsToMove = 32;
187
188// Maximum length we allow a per-thread free-list to have before we
189// move objects from it into the corresponding central free-list. We
190// want this big to avoid locking the central free-list too often. It
191// should not hurt to make this list somewhat big because the
192// scavenging code will shrink it down when its contents are not in use.
193static const int kMaxFreeListLength = 256;
194
195// Lower and upper bounds on the per-thread cache sizes
196static const size_t kMinThreadCacheSize = kMaxSize * 2;
197static const size_t kMaxThreadCacheSize = 2 << 20;
198
199// Default bound on the total amount of thread caches
200static const size_t kDefaultOverallThreadCacheSize = 16 << 20;
201
202// For all span-lengths < kMaxPages we keep an exact-size list.
203// REQUIRED: kMaxPages >= kMinSystemAlloc;
204static const size_t kMaxPages = kMinSystemAlloc;
205
206// Twice the approximate gap between sampling actions.
207// I.e., we take one sample approximately once every
208// kSampleParameter/2
209// bytes of allocation, i.e., ~ once every 128KB.
210// Must be a prime number.
211static const size_t kSampleParameter = 266053;
212
213//-------------------------------------------------------------------
214// Mapping from size to size_class and vice versa
215//-------------------------------------------------------------------
216
217// A pair of arrays we use for implementing the mapping from a size to
218// its size class. Indexed by "floor(lg(size))".
219static const int kSizeBits = 8 * sizeof(size_t);
220static unsigned char size_base[kSizeBits];
221static unsigned char size_shift[kSizeBits];
222
223// Mapping from size class to size
224static size_t class_to_size[kNumClasses];
225
226// Mapping from size class to number of pages to allocate at a time
227static size_t class_to_pages[kNumClasses];
228
229// Return floor(log2(n)) for n > 0.
230#if PLATFORM(X86) && COMPILER(GCC)
231static inline int LgFloor(size_t n) {
232 // "ro" for the input spec means the input can come from either a
233 // register ("r") or offsetable memory ("o").
234 int result;
235 __asm__("bsrl %1, %0"
236 : "=r" (result) // Output spec
237 : "ro" (n) // Input spec
238 : "cc" // Clobbers condition-codes
239 );
240 return result;
241}
242
243#elif PLATFORM(PPC) && COMPILER(GCC)
244static inline int LgFloor(size_t n) {
245 // "r" for the input spec means the input must come from a
246 // register ("r")
247 int result;
248
249 __asm__ ("{cntlz|cntlzw} %0,%1"
250 : "=r" (result) // Output spec
251 : "r" (n)); // Input spec
252
253 return 31 - result;
254}
255
256#else
257// Note: the following only works for "n"s that fit in 32-bits, but
258// that is fine since we only use it for small sizes.
259static inline int LgFloor(size_t n) {
260 int log = 0;
261 for (int i = 4; i >= 0; --i) {
262 int shift = (1 << i);
263 size_t x = n >> shift;
264 if (x != 0) {
265 n = x;
266 log += shift;
267 }
268 }
269 ASSERT(n == 1);
270 return log;
271}
272#endif
273
274static inline int SizeClass(size_t size) {
275 if (size == 0) size = 1;
276 const int lg = LgFloor(size);
277 const int align = size_shift[lg];
278 return static_cast<int>(size_base[lg]) + ((size-1) >> align);
279}
280
281// Get the byte-size for a specified class
282static inline size_t ByteSizeForClass(size_t cl) {
283 return class_to_size[cl];
284}
285
286// Initialize the mapping arrays
287static void InitSizeClasses() {
288 // Special initialization for small sizes
289 for (size_t lg = 0; lg < kAlignShift; lg++) {
290 size_base[lg] = 1;
291 size_shift[lg] = kAlignShift;
292 }
293
294 size_t next_class = 1;
295 int alignshift = kAlignShift;
296 int last_lg = -1;
297 for (size_t size = kAlignment; size <= kMaxSize; size += (1 << alignshift)) {
298 int lg = LgFloor(size);
299 if (lg > last_lg) {
300 // Increase alignment every so often.
301 //
302 // Since we double the alignment every time size doubles and
303 // size >= 256, this means that space wasted due to alignment is
304 // at most 16/256 i.e., 6.25%. Plus we cap the alignment at 512
305 // bytes, so the space wasted as a percentage starts falling for
306 // sizes > 4K.
307 if ((lg >= 8) && (alignshift < 9)) {
308 alignshift++;
309 }
310 size_base[lg] = next_class - ((size-1) >> alignshift);
311 size_shift[lg] = alignshift;
312 }
313
314 class_to_size[next_class] = size;
315 last_lg = lg;
316
317 next_class++;
318 }
319 if (next_class >= kNumClasses) {
320 MESSAGE("used up too many size classes: %d\n", next_class);
321 abort();
322 }
323
324 // Initialize the number of pages we should allocate to split into
325 // small objects for a given class.
326 for (size_t cl = 1; cl < next_class; cl++) {
327 // Allocate enough pages so leftover is less than 1/16 of total.
328 // This bounds wasted space to at most 6.25%.
329 size_t psize = kPageSize;
330 const size_t s = class_to_size[cl];
331 while ((psize % s) > (psize >> 4)) {
332 psize += kPageSize;
333 }
334 class_to_pages[cl] = psize >> kPageShift;
335 }
336
337 // Double-check sizes just to be safe
338 for (size_t size = 0; size <= kMaxSize; size++) {
339 const size_t sc = SizeClass(size);
340 if (sc == 0) {
341 MESSAGE("Bad size class %d for %" PRIuS "\n", sc, size);
342 abort();
343 }
344 if (sc > 1 && size <= class_to_size[sc-1]) {
345 MESSAGE("Allocating unnecessarily large class %d for %" PRIuS
346 "\n", sc, size);
347 abort();
348 }
349 if (sc >= kNumClasses) {
350 MESSAGE("Bad size class %d for %" PRIuS "\n", sc, size);
351 abort();
352 }
353 const size_t s = class_to_size[sc];
354 if (size > s) {
355 MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %d)\n", s, size, sc);
356 abort();
357 }
358 if (s == 0) {
359 MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %d)\n", s, size, sc);
360 abort();
361 }
362 }
363}
364
365// -------------------------------------------------------------------------
366// Simple allocator for objects of a specified type. External locking
367// is required before accessing one of these objects.
368// -------------------------------------------------------------------------
369
370// Metadata allocator -- keeps stats about how many bytes allocated
371static uint64_t metadata_system_bytes = 0;
372static void* MetaDataAlloc(size_t bytes) {
373 void* result = TCMalloc_SystemAlloc(bytes);
374 if (result != NULL) {
375 metadata_system_bytes += bytes;
376 }
377 return result;
378}
379
380template <class T>
381class PageHeapAllocator {
382 private:
383 // How much to allocate from system at a time
384 static const int kAllocIncrement = 32 << 10;
385
386 // Aligned size of T
387 static const size_t kAlignedSize
388 = (((sizeof(T) + kAlignment - 1) / kAlignment) * kAlignment);
389
390 // Free area from which to carve new objects
391 char* free_area_;
392 size_t free_avail_;
393
394 // Free list of already carved objects
395 void* free_list_;
396
397 // Number of allocated but unfreed objects
398 int inuse_;
399
400 public:
401 void Init() {
402 ASSERT(kAlignedSize <= kAllocIncrement);
403 inuse_ = 0;
404 free_area_ = NULL;
405 free_avail_ = 0;
406 free_list_ = NULL;
407 }
408
409 T* New() {
410 // Consult free list
411 void* result;
412 if (free_list_ != NULL) {
413 result = free_list_;
414 free_list_ = *(reinterpret_cast<void**>(result));
415 } else {
416 if (free_avail_ < kAlignedSize) {
417 // Need more room
418 free_area_ = reinterpret_cast<char*>(MetaDataAlloc(kAllocIncrement));
419 if (free_area_ == NULL) abort();
420 free_avail_ = kAllocIncrement;
421 }
422 result = free_area_;
423 free_area_ += kAlignedSize;
424 free_avail_ -= kAlignedSize;
425 }
426 inuse_++;
427 return reinterpret_cast<T*>(result);
428 }
429
430 void Delete(T* p) {
431 *(reinterpret_cast<void**>(p)) = free_list_;
432 free_list_ = p;
433 inuse_--;
434 }
435
436 int inuse() const { return inuse_; }
437};
438
439// -------------------------------------------------------------------------
440// Span - a contiguous run of pages
441// -------------------------------------------------------------------------
442
443// Type that can hold a page number
444typedef uintptr_t PageID;
445
446// Type that can hold the length of a run of pages
447typedef uintptr_t Length;
448
449// Convert byte size into pages
450static inline Length pages(size_t bytes) {
451 return ((bytes + kPageSize - 1) >> kPageShift);
452}
453
454// Convert a user size into the number of bytes that will actually be
455// allocated
456static size_t AllocationSize(size_t bytes) {
457 if (bytes > kMaxSize) {
458 // Large object: we allocate an integral number of pages
459 return pages(bytes) << kPageShift;
460 } else {
461 // Small object: find the size class to which it belongs
462 return ByteSizeForClass(SizeClass(bytes));
463 }
464}
465
466// Information kept for a span (a contiguous run of pages).
467struct Span {
468 PageID start; // Starting page number
469 Length length; // Number of pages in span
470 Span* next; // Used when in link list
471 Span* prev; // Used when in link list
472 void* objects; // Linked list of free objects
473 unsigned int free : 1; // Is the span free
474 unsigned int sample : 1; // Sampled object?
475 unsigned int sizeclass : 8; // Size-class for small objects (or 0)
476 unsigned int refcount : 11; // Number of non-free objects
477
478#undef SPAN_HISTORY
479#ifdef SPAN_HISTORY
480 // For debugging, we can keep a log events per span
481 int nexthistory;
482 char history[64];
483 int value[64];
484#endif
485};
486
487#ifdef SPAN_HISTORY
488void Event(Span* span, char op, int v = 0) {
489 span->history[span->nexthistory] = op;
490 span->value[span->nexthistory] = v;
491 span->nexthistory++;
492 if (span->nexthistory == sizeof(span->history)) span->nexthistory = 0;
493}
494#else
495#define Event(s,o,v) ((void) 0)
496#endif
497
498// Allocator/deallocator for spans
499static PageHeapAllocator<Span> span_allocator;
500static Span* NewSpan(PageID p, Length len) {
501 Span* result = span_allocator.New();
502 memset(result, 0, sizeof(*result));
503 result->start = p;
504 result->length = len;
505#ifdef SPAN_HISTORY
506 result->nexthistory = 0;
507#endif
508 return result;
509}
510
511static inline void DeleteSpan(Span* span) {
512#ifndef NDEBUG
513 // In debug mode, trash the contents of deleted Spans
514 memset(span, 0x3f, sizeof(*span));
515#endif
516 span_allocator.Delete(span);
517}
518
519// -------------------------------------------------------------------------
520// Doubly linked list of spans.
521// -------------------------------------------------------------------------
522
523static inline void DLL_Init(Span* list) {
524 list->next = list;
525 list->prev = list;
526}
527
528static inline void DLL_Remove(Span* span) {
529 span->prev->next = span->next;
530 span->next->prev = span->prev;
531 span->prev = NULL;
532 span->next = NULL;
533}
534
535static ALWAYS_INLINE bool DLL_IsEmpty(const Span* list) {
536 return list->next == list;
537}
538
539#ifndef WTF_CHANGES
540static int DLL_Length(const Span* list) {
541 int result = 0;
542 for (Span* s = list->next; s != list; s = s->next) {
543 result++;
544 }
545 return result;
546}
547#endif
548
549#if 0 /* Not needed at the moment -- causes compiler warnings if not used */
550static void DLL_Print(const char* label, const Span* list) {
551 MESSAGE("%-10s %p:", label, list);
552 for (const Span* s = list->next; s != list; s = s->next) {
553 MESSAGE(" <%p,%u,%u>", s, s->start, s->length);
554 }
555 MESSAGE("\n");
556}
557#endif
558
559static inline void DLL_Prepend(Span* list, Span* span) {
560 ASSERT(span->next == NULL);
561 ASSERT(span->prev == NULL);
562 span->next = list->next;
563 span->prev = list;
564 list->next->prev = span;
565 list->next = span;
566}
567
568static void DLL_InsertOrdered(Span* list, Span* span) {
569 ASSERT(span->next == NULL);
570 ASSERT(span->prev == NULL);
571 // Look for appropriate place to insert
572 Span* x = list;
573 while ((x->next != list) && (x->next->start < span->start)) {
574 x = x->next;
575 }
576 span->next = x->next;
577 span->prev = x;
578 x->next->prev = span;
579 x->next = span;
580}
581
582// -------------------------------------------------------------------------
583// Stack traces kept for sampled allocations
584// The following state is protected by pageheap_lock_.
585// -------------------------------------------------------------------------
586
587static const int kMaxStackDepth = 31;
588struct StackTrace {
589 uintptr_t size; // Size of object
590 int depth; // Number of PC values stored in array below
591 void* stack[kMaxStackDepth];
592};
593static PageHeapAllocator<StackTrace> stacktrace_allocator;
594static Span sampled_objects;
595
596// -------------------------------------------------------------------------
597// Map from page-id to per-page data
598// -------------------------------------------------------------------------
599
600// We use PageMap2<> for 32-bit and PageMap3<> for 64-bit machines.
601
602// Selector class -- general selector uses 3-level map
603template <int BITS> class MapSelector {
604 public:
605 typedef TCMalloc_PageMap3<BITS-kPageShift> Type;
606};
607
608// A two-level map for 32-bit machines
609template <> class MapSelector<32> {
610 public:
611 typedef TCMalloc_PageMap2<32-kPageShift> Type;
612};
613
614// -------------------------------------------------------------------------
615// Page-level allocator
616// * Eager coalescing
617//
618// Heap for page-level allocation. We allow allocating and freeing a
619// contiguous runs of pages (called a "span").
620// -------------------------------------------------------------------------
621
622class TCMalloc_PageHeap {
623 public:
624 TCMalloc_PageHeap();
625
626 // Allocate a run of "n" pages. Returns zero if out of memory.
627 Span* New(Length n);
628
629 // Delete the span "[p, p+n-1]".
630 // REQUIRES: span was returned by earlier call to New() and
631 // has not yet been deleted.
632 void Delete(Span* span);
633
634 // Mark an allocated span as being used for small objects of the
635 // specified size-class.
636 // REQUIRES: span was returned by an earlier call to New()
637 // and has not yet been deleted.
638 void RegisterSizeClass(Span* span, size_t sc);
639
640 // Split an allocated span into two spans: one of length "n" pages
641 // followed by another span of length "span->length - n" pages.
642 // Modifies "*span" to point to the first span of length "n" pages.
643 // Returns a pointer to the second span.
644 //
645 // REQUIRES: "0 < n < span->length"
646 // REQUIRES: !span->free
647 // REQUIRES: span->sizeclass == 0
648 Span* Split(Span* span, Length n);
649
650 // Return the descriptor for the specified page.
651 inline Span* GetDescriptor(PageID p) const {
652 return reinterpret_cast<Span*>(pagemap_.get(p));
653 }
654
655 // Dump state to stderr
656#ifndef WTF_CHANGES
657 void Dump(TCMalloc_Printer* out);
658#endif
659
660 // Return number of bytes allocated from system
661 inline uint64_t SystemBytes() const { return system_bytes_; }
662
663 // Return number of free bytes in heap
664 uint64_t FreeBytes() const {
665 return (static_cast<uint64_t>(free_pages_) << kPageShift);
666 }
667
668 bool Check();
669 bool CheckList(Span* list, Length min_pages, Length max_pages);
670
671 private:
672 // Pick the appropriate map type based on pointer size
673 typedef MapSelector<8*sizeof(uintptr_t)>::Type PageMap;
674 PageMap pagemap_;
675
676 // List of free spans of length >= kMaxPages
677 Span large_;
678
679 // Array mapping from span length to a doubly linked list of free spans
680 Span free_[kMaxPages];
681
682 // Number of pages kept in free lists
683 uintptr_t free_pages_;
684
685 // Bytes allocated from system
686 uint64_t system_bytes_;
687
688 bool GrowHeap(Length n);
689
690 // REQUIRES span->length >= n
691 // Remove span from its free list, and move any leftover part of
692 // span into appropriate free lists. Also update "span" to have
693 // length exactly "n" and mark it as non-free so it can be returned
694 // to the client.
695 void Carve(Span* span, Length n);
696
697 void RecordSpan(Span* span) {
698 pagemap_.set(span->start, span);
699 if (span->length > 1) {
700 pagemap_.set(span->start + span->length - 1, span);
701 }
702 }
703};
704
705TCMalloc_PageHeap::TCMalloc_PageHeap() : pagemap_(MetaDataAlloc),
706 free_pages_(0),
707 system_bytes_(0) {
708 DLL_Init(&large_);
709 for (size_t i = 0; i < kMaxPages; i++) {
710 DLL_Init(&free_[i]);
711 }
712}
713
714inline Span* TCMalloc_PageHeap::New(Length n) {
715 ASSERT(Check());
716 if (n == 0) n = 1;
717
718 // Find first size >= n that has a non-empty list
719 for (size_t s = n; s < kMaxPages; s++) {
720 if (!DLL_IsEmpty(&free_[s])) {
721 Span* result = free_[s].next;
722 Carve(result, n);
723 ASSERT(Check());
724 free_pages_ -= n;
725 return result;
726 }
727 }
728
729 // Look in large list. If we first do not find something, we try to
730 // grow the heap and try again.
731 for (int i = 0; i < 2; i++) {
732 // find the best span (closest to n in size)
733 Span *best = NULL;
734 for (Span* span = large_.next; span != &large_; span = span->next) {
735 if (span->length >= n &&
736 (best == NULL || span->length < best->length)) {
737 best = span;
738 }
739 }
740 if (best != NULL) {
741 Carve(best, n);
742 ASSERT(Check());
743 free_pages_ -= n;
744 return best;
745 }
746 if (i == 0) {
747 // Nothing suitable in large list. Grow the heap and look again.
748 if (!GrowHeap(n)) {
749 ASSERT(Check());
750 return NULL;
751 }
752 }
753 }
754 return NULL;
755}
756
757Span* TCMalloc_PageHeap::Split(Span* span, Length n) {
758 ASSERT(0 < n);
759 ASSERT(n < span->length);
760 ASSERT(!span->free);
761 ASSERT(span->sizeclass == 0);
762 Event(span, 'T', n);
763
764 const int extra = span->length - n;
765 Span* leftover = NewSpan(span->start + n, extra);
766 Event(leftover, 'U', extra);
767 RecordSpan(leftover);
768 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span
769 span->length = n;
770
771 return leftover;
772}
773
774inline void TCMalloc_PageHeap::Carve(Span* span, Length n) {
775 ASSERT(n > 0);
776 DLL_Remove(span);
777 span->free = 0;
778 Event(span, 'A', n);
779
780 const size_t extra = span->length - n;
781 ASSERT(extra >= 0);
782 if (extra > 0) {
783 Span* leftover = NewSpan(span->start + n, extra);
784 leftover->free = 1;
785 Event(leftover, 'S', extra);
786 RecordSpan(leftover);
787 if (extra < kMaxPages) {
788 DLL_Prepend(&free_[extra], leftover);
789 } else {
790 DLL_InsertOrdered(&large_, leftover);
791 }
792 span->length = n;
793 pagemap_.set(span->start + n - 1, span);
794 }
795}
796
797inline void TCMalloc_PageHeap::Delete(Span* span) {
798 ASSERT(Check());
799 ASSERT(!span->free);
800 ASSERT(span->length > 0);
801 ASSERT(GetDescriptor(span->start) == span);
802 ASSERT(GetDescriptor(span->start + span->length - 1) == span);
803 span->sizeclass = 0;
804 span->sample = 0;
805
806 // Coalesce -- we guarantee that "p" != 0, so no bounds checking
807 // necessary. We do not bother resetting the stale pagemap
808 // entries for the pieces we are merging together because we only
809 // care about the pagemap entries for the boundaries.
810 const PageID p = span->start;
811 const Length n = span->length;
812 Span* prev = GetDescriptor(p-1);
813 if (prev != NULL && prev->free) {
814 // Merge preceding span into this span
815 ASSERT(prev->start + prev->length == p);
816 const Length len = prev->length;
817 DLL_Remove(prev);
818 DeleteSpan(prev);
819 span->start -= len;
820 span->length += len;
821 pagemap_.set(span->start, span);
822 Event(span, 'L', len);
823 }
824 Span* next = GetDescriptor(p+n);
825 if (next != NULL && next->free) {
826 // Merge next span into this span
827 ASSERT(next->start == p+n);
828 const Length len = next->length;
829 DLL_Remove(next);
830 DeleteSpan(next);
831 span->length += len;
832 pagemap_.set(span->start + span->length - 1, span);
833 Event(span, 'R', len);
834 }
835
836 Event(span, 'D', span->length);
837 span->free = 1;
838 if (span->length < kMaxPages) {
839 DLL_Prepend(&free_[span->length], span);
840 } else {
841 DLL_InsertOrdered(&large_, span);
842 }
843 free_pages_ += n;
844
845 ASSERT(Check());
846}
847
848void TCMalloc_PageHeap::RegisterSizeClass(Span* span, size_t sc) {
849 // Associate span object with all interior pages as well
850 ASSERT(!span->free);
851 ASSERT(GetDescriptor(span->start) == span);
852 ASSERT(GetDescriptor(span->start+span->length-1) == span);
853 Event(span, 'C', sc);
854 span->sizeclass = sc;
855 for (Length i = 1; i < span->length-1; i++) {
856 pagemap_.set(span->start+i, span);
857 }
858}
859
860#ifndef WTF_CHANGES
861void TCMalloc_PageHeap::Dump(TCMalloc_Printer* out) {
862 int nonempty_sizes = 0;
863 for (int s = 0; s < kMaxPages; s++) {
864 if (!DLL_IsEmpty(&free_[s])) nonempty_sizes++;
865 }
866 out->printf("------------------------------------------------\n");
867 out->printf("PageHeap: %d sizes; %6.1f MB free\n", nonempty_sizes,
868 (static_cast<double>(free_pages_) * kPageSize) / 1048576.0);
869 out->printf("------------------------------------------------\n");
870 uint64_t cumulative = 0;
871 for (int s = 0; s < kMaxPages; s++) {
872 if (!DLL_IsEmpty(&free_[s])) {
873 const int list_length = DLL_Length(&free_[s]);
874 uint64_t s_pages = s * list_length;
875 cumulative += s_pages;
876 out->printf("%6u pages * %6u spans ~ %6.1f MB; %6.1f MB cum\n",
877 s, list_length,
878 (s_pages << kPageShift) / 1048576.0,
879 (cumulative << kPageShift) / 1048576.0);
880 }
881 }
882
883 uint64_t large_pages = 0;
884 int large_spans = 0;
885 for (Span* s = large_.next; s != &large_; s = s->next) {
886 out->printf(" [ %6" PRIuS " spans ]\n", s->length);
887 large_pages += s->length;
888 large_spans++;
889 }
890 cumulative += large_pages;
891 out->printf(">255 large * %6u spans ~ %6.1f MB; %6.1f MB cum\n",
892 large_spans,
893 (large_pages << kPageShift) / 1048576.0,
894 (cumulative << kPageShift) / 1048576.0);
895}
896#endif
897
898bool TCMalloc_PageHeap::GrowHeap(Length n) {
899 ASSERT(kMaxPages >= kMinSystemAlloc);
900 Length ask = (n>kMinSystemAlloc) ? n : static_cast<Length>(kMinSystemAlloc);
901 void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, kPageSize);
902 if (ptr == NULL) {
903 if (n < ask) {
904 // Try growing just "n" pages
905 ask = n;
906 ptr = TCMalloc_SystemAlloc(ask << kPageShift, kPageSize);
907 }
908 if (ptr == NULL) return false;
909 }
910 system_bytes_ += (ask << kPageShift);
911 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
912 ASSERT(p > 0);
913
914 // Make sure pagemap_ has entries for all of the new pages.
915 // Plus ensure one before and one after so coalescing code
916 // does not need bounds-checking.
917 if (pagemap_.Ensure(p-1, ask+2)) {
918 // Pretend the new area is allocated and then Delete() it to
919 // cause any necessary coalescing to occur.
920 //
921 // We do not adjust free_pages_ here since Delete() will do it for us.
922 Span* span = NewSpan(p, ask);
923 RecordSpan(span);
924 Delete(span);
925 ASSERT(Check());
926 return true;
927 } else {
928 // We could not allocate memory within "pagemap_"
929 // TODO: Once we can return memory to the system, return the new span
930 return false;
931 }
932}
933
934bool TCMalloc_PageHeap::Check() {
935 ASSERT(free_[0].next == &free_[0]);
936 CheckList(&large_, kMaxPages, 1000000000);
937 for (Length s = 1; s < kMaxPages; s++) {
938 CheckList(&free_[s], s, s);
939 }
940 return true;
941}
942
943#if ASSERT_DISABLED
944bool TCMalloc_PageHeap::CheckList(Span*, Length, Length) {
945 return true;
946}
947#else
948bool TCMalloc_PageHeap::CheckList(Span* list, Length min_pages, Length max_pages) {
949 for (Span* s = list->next; s != list; s = s->next) {
950 CHECK_CONDITION(s->free);
951 CHECK_CONDITION(s->length >= min_pages);
952 CHECK_CONDITION(s->length <= max_pages);
953 CHECK_CONDITION(GetDescriptor(s->start) == s);
954 CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s);
955 }
956 return true;
957}
958#endif
959
960//-------------------------------------------------------------------
961// Free list
962//-------------------------------------------------------------------
963
964class TCMalloc_ThreadCache_FreeList {
965 private:
966 void* list_; // Linked list of nodes
967 uint16_t length_; // Current length
968 uint16_t lowater_; // Low water mark for list length
969
970 public:
971 void Init() {
972 list_ = NULL;
973 length_ = 0;
974 lowater_ = 0;
975 }
976
977 // Return current length of list
978 int length() const {
979 return length_;
980 }
981
982 // Is list empty?
983 bool empty() const {
984 return list_ == NULL;
985 }
986
987 // Low-water mark management
988 int lowwatermark() const { return lowater_; }
989 void clear_lowwatermark() { lowater_ = length_; }
990
991 ALWAYS_INLINE void Push(void* ptr) {
992 *(reinterpret_cast<void**>(ptr)) = list_;
993 list_ = ptr;
994 length_++;
995 }
996
997 ALWAYS_INLINE void* Pop() {
998 ASSERT(list_ != NULL);
999 void* result = list_;
1000 list_ = *(reinterpret_cast<void**>(result));
1001 length_--;
1002 if (length_ < lowater_) lowater_ = length_;
1003 return result;
1004 }
1005};
1006
1007//-------------------------------------------------------------------
1008// Data kept per thread
1009//-------------------------------------------------------------------
1010
1011class TCMalloc_ThreadCache {
1012 private:
1013 typedef TCMalloc_ThreadCache_FreeList FreeList;
1014
1015 size_t size_; // Combined size of data
1016 pthread_t tid_; // Which thread owns it
1017 bool setspecific_; // Called pthread_setspecific?
1018 FreeList list_[kNumClasses]; // Array indexed by size-class
1019
1020 // We sample allocations, biased by the size of the allocation
1021 uint32_t rnd_; // Cheap random number generator
1022 size_t bytes_until_sample_; // Bytes until we sample next
1023
1024 public:
1025 // All ThreadCache objects are kept in a linked list (for stats collection)
1026 TCMalloc_ThreadCache* next_;
1027 TCMalloc_ThreadCache* prev_;
1028
1029 void Init(pthread_t tid);
1030 void Cleanup();
1031
1032 // Accessors (mostly just for printing stats)
1033 int freelist_length(size_t cl) const { return list_[cl].length(); }
1034
1035 // Total byte size in cache
1036 size_t Size() const { return size_; }
1037
1038 void* Allocate(size_t size);
1039 void Deallocate(void* ptr, size_t size_class);
1040
1041 void FetchFromCentralCache(size_t cl, size_t allocationSize);
1042 void ReleaseToCentralCache(size_t cl, int N);
1043 void Scavenge();
1044 void Print() const;
1045
1046 // Record allocation of "k" bytes. Return true iff allocation
1047 // should be sampled
1048 bool SampleAllocation(size_t k);
1049
1050 // Pick next sampling point
1051 void PickNextSample();
1052
1053 static void InitModule();
1054 static void InitTSD();
1055 static TCMalloc_ThreadCache* GetCache();
1056 static TCMalloc_ThreadCache* GetCacheIfPresent();
1057 static void* CreateCacheIfNecessary();
1058 static void DeleteCache(void* ptr);
1059 static void RecomputeThreadCacheSize();
1060};
1061
1062//-------------------------------------------------------------------
1063// Data kept per size-class in central cache
1064//-------------------------------------------------------------------
1065
1066class TCMalloc_Central_FreeList {
1067 public:
1068 void Init(size_t cl);
1069
1070 // REQUIRES: lock_ is held
1071 // Insert object.
1072 // May temporarily release lock_.
1073 void Insert(void* object);
1074
1075 // REQUIRES: lock_ is held
1076 // Remove object from cache and return.
1077 // Return NULL if no free entries in cache.
1078 void* Remove();
1079
1080 // REQUIRES: lock_ is held
1081 // Populate cache by fetching from the page heap.
1082 // May temporarily release lock_.
1083 void Populate();
1084
1085 // REQUIRES: lock_ is held
1086 // Number of free objects in cache
1087 int length() const { return counter_; }
1088
1089 // Lock -- exposed because caller grabs it before touching this object
1090 SpinLock lock_;
1091
1092 private:
1093 // We keep linked lists of empty and non-emoty spans.
1094 size_t size_class_; // My size class
1095 Span empty_; // Dummy header for list of empty spans
1096 Span nonempty_; // Dummy header for list of non-empty spans
1097 size_t counter_; // Number of free objects in cache entry
1098};
1099
1100// Pad each CentralCache object to multiple of 64 bytes
1101class TCMalloc_Central_FreeListPadded : public TCMalloc_Central_FreeList {
1102 private:
1103 char pad_[(64 - (sizeof(TCMalloc_Central_FreeList) % 64)) % 64];
1104};
1105
1106//-------------------------------------------------------------------
1107// Global variables
1108//-------------------------------------------------------------------
1109
1110// Central cache -- a collection of free-lists, one per size-class.
1111// We have a separate lock per free-list to reduce contention.
1112static TCMalloc_Central_FreeListPadded central_cache[kNumClasses];
1113
1114// Page-level allocator
1115static SpinLock pageheap_lock = SPINLOCK_INITIALIZER;
1116static char pageheap_memory[sizeof(TCMalloc_PageHeap)];
1117static bool phinited = false;
1118
1119// Avoid extra level of indirection by making "pageheap" be just an alias
1120// of pageheap_memory.
1121#define pageheap ((TCMalloc_PageHeap*) pageheap_memory)
1122
1123// Thread-specific key. Initialization here is somewhat tricky
1124// because some Linux startup code invokes malloc() before it
1125// is in a good enough state to handle pthread_keycreate().
1126// Therefore, we use TSD keys only after tsd_inited is set to true.
1127// Until then, we use a slow path to get the heap object.
1128static bool tsd_inited = false;
1129static pthread_key_t heap_key;
1130
1131// Allocator for thread heaps
1132static PageHeapAllocator<TCMalloc_ThreadCache> threadheap_allocator;
1133
1134// Linked list of heap objects. Protected by pageheap_lock.
1135static TCMalloc_ThreadCache* thread_heaps = NULL;
1136static int thread_heap_count = 0;
1137
1138// Overall thread cache size. Protected by pageheap_lock.
1139static size_t overall_thread_cache_size = kDefaultOverallThreadCacheSize;
1140
1141// Global per-thread cache size. Writes are protected by
1142// pageheap_lock. Reads are done without any locking, which should be
1143// fine as long as size_t can be written atomically and we don't place
1144// invariants between this variable and other pieces of state.
1145static volatile size_t per_thread_cache_size = kMaxThreadCacheSize;
1146
1147//-------------------------------------------------------------------
1148// Central cache implementation
1149//-------------------------------------------------------------------
1150
1151void TCMalloc_Central_FreeList::Init(size_t cl) {
1152 lock_.Init();
1153 size_class_ = cl;
1154 DLL_Init(&empty_);
1155 DLL_Init(&nonempty_);
1156 counter_ = 0;
1157}
1158
1159ALWAYS_INLINE void TCMalloc_Central_FreeList::Insert(void* object) {
1160 const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift;
1161 Span* span = pageheap->GetDescriptor(p);
1162 ASSERT(span != NULL);
1163 ASSERT(span->refcount > 0);
1164
1165 // If span is empty, move it to non-empty list
1166 if (span->objects == NULL) {
1167 DLL_Remove(span);
1168 DLL_Prepend(&nonempty_, span);
1169 Event(span, 'N', 0);
1170 }
1171
1172 // The following check is expensive, so it is disabled by default
1173 if (false) {
1174 // Check that object does not occur in list
1175 int got = 0;
1176 for (void* p = span->objects; p != NULL; p = *((void**) p)) {
1177 ASSERT(p != object);
1178 got++;
1179 }
1180 ASSERT(got + span->refcount ==
1181 (span->length<<kPageShift)/ByteSizeForClass(span->sizeclass));
1182 }
1183
1184 counter_++;
1185 span->refcount--;
1186 if (span->refcount == 0) {
1187 Event(span, '#', 0);
1188 counter_ -= (span->length<<kPageShift) / ByteSizeForClass(span->sizeclass);
1189 DLL_Remove(span);
1190
1191 // Release central list lock while operating on pageheap
1192 lock_.Unlock();
1193 {
1194 SpinLockHolder h(&pageheap_lock);
1195 pageheap->Delete(span);
1196 }
1197 lock_.Lock();
1198 } else {
1199 *(reinterpret_cast<void**>(object)) = span->objects;
1200 span->objects = object;
1201 }
1202}
1203
1204ALWAYS_INLINE void* TCMalloc_Central_FreeList::Remove() {
1205 if (DLL_IsEmpty(&nonempty_)) return NULL;
1206 Span* span = nonempty_.next;
1207
1208 ASSERT(span->objects != NULL);
1209 span->refcount++;
1210 void* result = span->objects;
1211 span->objects = *(reinterpret_cast<void**>(result));
1212 if (span->objects == NULL) {
1213 // Move to empty list
1214 DLL_Remove(span);
1215 DLL_Prepend(&empty_, span);
1216 Event(span, 'E', 0);
1217 }
1218 counter_--;
1219 return result;
1220}
1221
1222// Fetch memory from the system and add to the central cache freelist.
1223ALWAYS_INLINE void TCMalloc_Central_FreeList::Populate() {
1224 // Release central list lock while operating on pageheap
1225 lock_.Unlock();
1226 const size_t npages = class_to_pages[size_class_];
1227
1228 Span* span;
1229 {
1230 SpinLockHolder h(&pageheap_lock);
1231 span = pageheap->New(npages);
1232 if (span) pageheap->RegisterSizeClass(span, size_class_);
1233 }
1234 if (span == NULL) {
1235 MESSAGE("allocation failed: %d\n", errno);
1236 lock_.Lock();
1237 return;
1238 }
1239
1240 // Split the block into pieces and add to the free-list
1241 // TODO: coloring of objects to avoid cache conflicts?
1242 void** tail = &span->objects;
1243 char* ptr = reinterpret_cast<char*>(span->start << kPageShift);
1244 char* limit = ptr + (npages << kPageShift);
1245 const size_t size = ByteSizeForClass(size_class_);
1246 int num = 0;
1247 char* nptr;
1248 while ((nptr = ptr + size) <= limit) {
1249 *tail = ptr;
1250 tail = reinterpret_cast<void**>(ptr);
1251 ptr = nptr;
1252 num++;
1253 }
1254 ASSERT(ptr <= limit);
1255 *tail = NULL;
1256 span->refcount = 0; // No sub-object in use yet
1257
1258 // Add span to list of non-empty spans
1259 lock_.Lock();
1260 DLL_Prepend(&nonempty_, span);
1261 counter_ += num;
1262}
1263
1264//-------------------------------------------------------------------
1265// TCMalloc_ThreadCache implementation
1266//-------------------------------------------------------------------
1267
1268inline bool TCMalloc_ThreadCache::SampleAllocation(size_t k) {
1269 if (bytes_until_sample_ < k) {
1270 PickNextSample();
1271 return true;
1272 } else {
1273 bytes_until_sample_ -= k;
1274 return false;
1275 }
1276}
1277
1278void TCMalloc_ThreadCache::Init(pthread_t tid) {
1279 size_ = 0;
1280 next_ = NULL;
1281 prev_ = NULL;
1282 tid_ = tid;
1283 setspecific_ = false;
1284 for (size_t cl = 0; cl < kNumClasses; ++cl) {
1285 list_[cl].Init();
1286 }
1287
1288 // Initialize RNG -- run it for a bit to get to good values
1289 rnd_ = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this));
1290 for (int i = 0; i < 100; i++) {
1291 PickNextSample();
1292 }
1293}
1294
1295void TCMalloc_ThreadCache::Cleanup() {
1296 // Put unused memory back into central cache
1297 for (size_t cl = 0; cl < kNumClasses; ++cl) {
1298 FreeList* src = &list_[cl];
1299 TCMalloc_Central_FreeList* dst = &central_cache[cl];
1300 SpinLockHolder h(&dst->lock_);
1301 while (!src->empty()) {
1302 dst->Insert(src->Pop());
1303 }
1304 }
1305}
1306
1307ALWAYS_INLINE void* TCMalloc_ThreadCache::Allocate(size_t size) {
1308 ASSERT(size <= kMaxSize);
1309 const size_t cl = SizeClass(size);
1310 FreeList* list = &list_[cl];
1311 size_t allocationSize = (size <= kMaxTinySize) ? (size + 7) & ~0x7 : ByteSizeForClass(cl);
1312 if (list->empty()) {
1313 FetchFromCentralCache(cl, allocationSize);
1314 if (list->empty()) return NULL;
1315 }
1316 size_ -= allocationSize;
1317 return list->Pop();
1318}
1319
1320inline void TCMalloc_ThreadCache::Deallocate(void* ptr, size_t cl) {
1321 size_ += ByteSizeForClass(cl);
1322 FreeList* list = &list_[cl];
1323 list->Push(ptr);
1324 // If enough data is free, put back into central cache
1325 if (list->length() > kMaxFreeListLength) {
1326 ReleaseToCentralCache(cl, kNumObjectsToMove);
1327 }
1328 if (size_ >= per_thread_cache_size) Scavenge();
1329}
1330
1331// Remove some objects of class "cl" from central cache and add to thread heap
1332ALWAYS_INLINE void TCMalloc_ThreadCache::FetchFromCentralCache(size_t cl, size_t byteSize) {
1333 TCMalloc_Central_FreeList* src = &central_cache[cl];
1334 FreeList* dst = &list_[cl];
1335 SpinLockHolder h(&src->lock_);
1336 for (int i = 0; i < kNumObjectsToMove; i++) {
1337 void* object = src->Remove();
1338 if (object == NULL) {
1339 if (i == 0) {
1340 src->Populate(); // Temporarily releases src->lock_
1341 object = src->Remove();
1342 }
1343 if (object == NULL) {
1344 break;
1345 }
1346 }
1347 dst->Push(object);
1348 size_ += byteSize;
1349 }
1350}
1351
1352// Remove some objects of class "cl" from thread heap and add to central cache
1353inline void TCMalloc_ThreadCache::ReleaseToCentralCache(size_t cl, int N) {
1354 FreeList* src = &list_[cl];
1355 TCMalloc_Central_FreeList* dst = &central_cache[cl];
1356 SpinLockHolder h(&dst->lock_);
1357 if (N > src->length()) N = src->length();
1358 size_ -= N*ByteSizeForClass(cl);
1359 while (N-- > 0) {
1360 void* ptr = src->Pop();
1361 dst->Insert(ptr);
1362 }
1363}
1364
1365// Release idle memory to the central cache
1366inline void TCMalloc_ThreadCache::Scavenge() {
1367 // If the low-water mark for the free list is L, it means we would
1368 // not have had to allocate anything from the central cache even if
1369 // we had reduced the free list size by L. We aim to get closer to
1370 // that situation by dropping L/2 nodes from the free list. This
1371 // may not release much memory, but if so we will call scavenge again
1372 // pretty soon and the low-water marks will be high on that call.
1373#ifndef WTF_CHANGES
1374 int64 start = CycleClock::Now();
1375#endif
1376
1377 for (size_t cl = 0; cl < kNumClasses; cl++) {
1378 FreeList* list = &list_[cl];
1379 const int lowmark = list->lowwatermark();
1380 if (lowmark > 0) {
1381 const int drop = (lowmark > 1) ? lowmark/2 : 1;
1382 ReleaseToCentralCache(cl, drop);
1383 }
1384 list->clear_lowwatermark();
1385 }
1386
1387#ifndef WTF_CHANGES
1388 int64 finish = CycleClock::Now();
1389 CycleTimer ct;
1390 MESSAGE("GC: %.0f ns\n", ct.CyclesToUsec(finish-start)*1000.0);
1391#endif
1392}
1393
1394bool isMultiThreaded;
1395TCMalloc_ThreadCache *mainThreadCache;
1396pthread_t mainThreadID;
1397static SpinLock multiThreadedLock = SPINLOCK_INITIALIZER;
1398
1399void fastMallocRegisterThread(pthread_t thread)
1400{
1401 if (thread != mainThreadID) {
1402 // We lock when writing isMultiThreaded but not when reading it.
1403 // It's ok if the main thread gets it wrong - for the main thread, the
1404 // global variable cache is the same as the thread-specific cache.
1405 // And other threads can't get it wrong because they must have gone through
1406 // this function before allocating so they've synchronized.
1407 // Also, mainThreadCache is only set when isMultiThreaded is false,
1408 // to save a branch in some cases.
1409 SpinLockHolder lock(&multiThreadedLock);
1410 isMultiThreaded = true;
1411 mainThreadCache = 0;
1412 }
1413}
1414
1415ALWAYS_INLINE TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCache() {
1416 void* ptr = NULL;
1417 if (!tsd_inited) {
1418 InitModule();
1419 } else {
1420 if (mainThreadCache)
1421 ptr = mainThreadCache;
1422 else
1423 ptr = pthread_getspecific(heap_key);
1424 }
1425 if (ptr == NULL) ptr = CreateCacheIfNecessary();
1426 return reinterpret_cast<TCMalloc_ThreadCache*>(ptr);
1427}
1428
1429// In deletion paths, we do not try to create a thread-cache. This is
1430// because we may be in the thread destruction code and may have
1431// already cleaned up the cache for this thread.
1432inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCacheIfPresent() {
1433 if (mainThreadCache)
1434 return mainThreadCache;
1435 if (!tsd_inited) return NULL;
1436 return reinterpret_cast<TCMalloc_ThreadCache*>
1437 (pthread_getspecific(heap_key));
1438}
1439
1440void TCMalloc_ThreadCache::PickNextSample() {
1441 // Make next "random" number
1442 // x^32+x^22+x^2+x^1+1 is a primitive polynomial for random numbers
1443 static const uint32_t kPoly = (1 << 22) | (1 << 2) | (1 << 1) | (1 << 0);
1444 uint32_t r = rnd_;
1445 rnd_ = (r << 1) ^ ((static_cast<int32_t>(r) >> 31) & kPoly);
1446
1447 // Next point is "rnd_ % (2*sample_period)". I.e., average
1448 // increment is "sample_period".
1449 bytes_until_sample_ = rnd_ % kSampleParameter;
1450}
1451
1452void TCMalloc_ThreadCache::InitModule() {
1453 // There is a slight potential race here because of double-checked
1454 // locking idiom. However, as long as the program does a small
1455 // allocation before switching to multi-threaded mode, we will be
1456 // fine. We increase the chances of doing such a small allocation
1457 // by doing one in the constructor of the module_enter_exit_hook
1458 // object declared below.
1459 SpinLockHolder h(&pageheap_lock);
1460 if (!phinited) {
1461 InitSizeClasses();
1462 threadheap_allocator.Init();
1463 span_allocator.Init();
1464 span_allocator.New(); // Reduce cache conflicts
1465 span_allocator.New(); // Reduce cache conflicts
1466 stacktrace_allocator.Init();
1467 DLL_Init(&sampled_objects);
1468 for (size_t i = 0; i < kNumClasses; ++i) {
1469 central_cache[i].Init(i);
1470 }
1471 new ((void*)pageheap_memory) TCMalloc_PageHeap;
1472 phinited = 1;
1473 }
1474}
1475
1476void TCMalloc_ThreadCache::InitTSD() {
1477 ASSERT(!tsd_inited);
1478 pthread_key_create(&heap_key, DeleteCache);
1479 tsd_inited = true;
1480
1481 // We may have used a fake pthread_t for the main thread. Fix it.
1482 pthread_t zero;
1483 memset(&zero, 0, sizeof(zero));
1484 SpinLockHolder h(&pageheap_lock);
1485 for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
1486 if (h->tid_ == zero) {
1487 h->tid_ = pthread_self();
1488 }
1489 }
1490}
1491
1492void* TCMalloc_ThreadCache::CreateCacheIfNecessary() {
1493 // Initialize per-thread data if necessary
1494 TCMalloc_ThreadCache* heap = NULL;
1495 {
1496 SpinLockHolder h(&pageheap_lock);
1497
1498 // Early on in glibc's life, we cannot even call pthread_self()
1499 pthread_t me;
1500 if (!tsd_inited) {
1501 memset(&me, 0, sizeof(me));
1502 } else {
1503 me = pthread_self();
1504 }
1505
1506 // This may be a recursive malloc call from pthread_setspecific()
1507 // In that case, the heap for this thread has already been created
1508 // and added to the linked list. So we search for that first.
1509 for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
1510 if (h->tid_ == me) {
1511 heap = h;
1512 break;
1513 }
1514 }
1515
1516 if (heap == NULL) {
1517 // Create the heap and add it to the linked list
1518 heap = threadheap_allocator.New();
1519 heap->Init(me);
1520 heap->next_ = thread_heaps;
1521 heap->prev_ = NULL;
1522 if (thread_heaps != NULL) thread_heaps->prev_ = heap;
1523 thread_heaps = heap;
1524 thread_heap_count++;
1525 RecomputeThreadCacheSize();
1526 if (!isMultiThreaded) {
1527 mainThreadCache = heap;
1528 mainThreadID = pthread_self();
1529 }
1530 }
1531 }
1532
1533 // We call pthread_setspecific() outside the lock because it may
1534 // call malloc() recursively. The recursive call will never get
1535 // here again because it will find the already allocated heap in the
1536 // linked list of heaps.
1537 if (!heap->setspecific_ && tsd_inited) {
1538 heap->setspecific_ = true;
1539 pthread_setspecific(heap_key, heap);
1540 }
1541 return heap;
1542}
1543
1544void TCMalloc_ThreadCache::DeleteCache(void* ptr) {
1545 // Remove all memory from heap
1546 TCMalloc_ThreadCache* heap;
1547 heap = reinterpret_cast<TCMalloc_ThreadCache*>(ptr);
1548 heap->Cleanup();
1549
1550 // Remove from linked list
1551 SpinLockHolder h(&pageheap_lock);
1552 if (heap->next_ != NULL) heap->next_->prev_ = heap->prev_;
1553 if (heap->prev_ != NULL) heap->prev_->next_ = heap->next_;
1554 if (thread_heaps == heap) thread_heaps = heap->next_;
1555 thread_heap_count--;
1556 RecomputeThreadCacheSize();
1557
1558 threadheap_allocator.Delete(heap);
1559}
1560
1561void TCMalloc_ThreadCache::RecomputeThreadCacheSize() {
1562 // Divide available space across threads
1563 int n = thread_heap_count > 0 ? thread_heap_count : 1;
1564 size_t space = overall_thread_cache_size / n;
1565
1566 // Limit to allowed range
1567 if (space < kMinThreadCacheSize) space = kMinThreadCacheSize;
1568 if (space > kMaxThreadCacheSize) space = kMaxThreadCacheSize;
1569
1570 per_thread_cache_size = space;
1571}
1572
1573void TCMalloc_ThreadCache::Print() const {
1574 for (size_t cl = 0; cl < kNumClasses; ++cl) {
1575 MESSAGE(" %5" PRIuS " : %4d len; %4d lo\n",
1576 ByteSizeForClass(cl),
1577 list_[cl].length(),
1578 list_[cl].lowwatermark());
1579 }
1580}
1581
1582// Extract interesting stats
1583struct TCMallocStats {
1584 uint64_t system_bytes; // Bytes alloced from system
1585 uint64_t thread_bytes; // Bytes in thread caches
1586 uint64_t central_bytes; // Bytes in central cache
1587 uint64_t pageheap_bytes; // Bytes in page heap
1588 uint64_t metadata_bytes; // Bytes alloced for metadata
1589};
1590
1591#ifndef WTF_CHANGES
1592// Get stats into "r". Also get per-size-class counts if class_count != NULL
1593static void ExtractStats(TCMallocStats* r, uint64_t* class_count) {
1594 r->central_bytes = 0;
1595 for (size_t cl = 0; cl < kNumClasses; ++cl) {
1596 SpinLockHolder h(&central_cache[cl].lock_);
1597 const int length = central_cache[cl].length();
1598 r->central_bytes += static_cast<uint64_t>(ByteSizeForClass(cl)) * length;
1599 if (class_count) class_count[cl] = length;
1600 }
1601
1602 // Add stats from per-thread heaps
1603 r->thread_bytes = 0;
1604 { // scope
1605 SpinLockHolder h(&pageheap_lock);
1606 for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
1607 r->thread_bytes += h->Size();
1608 if (class_count) {
1609 for (size_t cl = 0; cl < kNumClasses; ++cl) {
1610 class_count[cl] += h->freelist_length(cl);
1611 }
1612 }
1613 }
1614 }
1615
1616 { //scope
1617 SpinLockHolder h(&pageheap_lock);
1618 r->system_bytes = pageheap->SystemBytes();
1619 r->metadata_bytes = metadata_system_bytes;
1620 r->pageheap_bytes = pageheap->FreeBytes();
1621 }
1622}
1623#endif
1624
1625#ifndef WTF_CHANGES
1626// WRITE stats to "out"
1627static void DumpStats(TCMalloc_Printer* out, int level) {
1628 TCMallocStats stats;
1629 uint64_t class_count[kNumClasses];
1630 ExtractStats(&stats, (level >= 2 ? class_count : NULL));
1631
1632 if (level >= 2) {
1633 out->printf("------------------------------------------------\n");
1634 uint64_t cumulative = 0;
1635 for (int cl = 0; cl < kNumClasses; ++cl) {
1636 if (class_count[cl] > 0) {
1637 uint64_t class_bytes = class_count[cl] * ByteSizeForClass(cl);
1638 cumulative += class_bytes;
1639 out->printf("class %3d [ %8" PRIuS " bytes ] : "
1640 "%8" LLU " objs; %5.1f MB; %5.1f cum MB\n",
1641 cl, ByteSizeForClass(cl),
1642 class_count[cl],
1643 class_bytes / 1048576.0,
1644 cumulative / 1048576.0);
1645 }
1646 }
1647
1648 SpinLockHolder h(&pageheap_lock);
1649 pageheap->Dump(out);
1650 }
1651
1652 const uint64_t bytes_in_use = stats.system_bytes
1653 - stats.pageheap_bytes
1654 - stats.central_bytes
1655 - stats.thread_bytes;
1656
1657 out->printf("------------------------------------------------\n"
1658 "MALLOC: %12" LLU " Heap size\n"
1659 "MALLOC: %12" LLU " Bytes in use by application\n"
1660 "MALLOC: %12" LLU " Bytes free in page heap\n"
1661 "MALLOC: %12" LLU " Bytes free in central cache\n"
1662 "MALLOC: %12" LLU " Bytes free in thread caches\n"
1663 "MALLOC: %12" LLU " Spans in use\n"
1664 "MALLOC: %12" LLU " Thread heaps in use\n"
1665 "MALLOC: %12" LLU " Metadata allocated\n"
1666 "------------------------------------------------\n",
1667 stats.system_bytes,
1668 bytes_in_use,
1669 stats.pageheap_bytes,
1670 stats.central_bytes,
1671 stats.thread_bytes,
1672 uint64_t(span_allocator.inuse()),
1673 uint64_t(threadheap_allocator.inuse()),
1674 stats.metadata_bytes);
1675}
1676
1677static void PrintStats(int level) {
1678 const int kBufferSize = 16 << 10;
1679 char* buffer = new char[kBufferSize];
1680 TCMalloc_Printer printer(buffer, kBufferSize);
1681 DumpStats(&printer, level);
1682 write(STDERR_FILENO, buffer, strlen(buffer));
1683 delete[] buffer;
1684}
1685
1686static void** DumpStackTraces() {
1687 // Count how much space we need
1688 int needed_slots = 0;
1689 {
1690 SpinLockHolder h(&pageheap_lock);
1691 for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) {
1692 StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects);
1693 needed_slots += 3 + stack->depth;
1694 }
1695 needed_slots += 100; // Slop in case sample grows
1696 needed_slots += needed_slots/8; // An extra 12.5% slop
1697 }
1698
1699 void** result = new void*[needed_slots];
1700 if (result == NULL) {
1701 MESSAGE("tcmalloc: could not allocate %d slots for stack traces\n",
1702 needed_slots);
1703 return NULL;
1704 }
1705
1706 SpinLockHolder h(&pageheap_lock);
1707 int used_slots = 0;
1708 for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) {
1709 ASSERT(used_slots < needed_slots); // Need to leave room for terminator
1710 StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects);
1711 if (used_slots + 3 + stack->depth >= needed_slots) {
1712 // No more room
1713 break;
1714 }
1715
1716 result[used_slots+0] = reinterpret_cast<void*>(1);
1717 result[used_slots+1] = reinterpret_cast<void*>(stack->size);
1718 result[used_slots+2] = reinterpret_cast<void*>(stack->depth);
1719 for (int d = 0; d < stack->depth; d++) {
1720 result[used_slots+3+d] = stack->stack[d];
1721 }
1722 used_slots += 3 + stack->depth;
1723 }
1724 result[used_slots] = reinterpret_cast<void*>(0);
1725 return result;
1726}
1727#endif
1728
1729#ifndef WTF_CHANGES
1730
1731// TCMalloc's support for extra malloc interfaces
1732class TCMallocImplementation : public MallocExtension {
1733 public:
1734 virtual void GetStats(char* buffer, int buffer_length) {
1735 ASSERT(buffer_length > 0);
1736 TCMalloc_Printer printer(buffer, buffer_length);
1737
1738 // Print level one stats unless lots of space is available
1739 if (buffer_length < 10000) {
1740 DumpStats(&printer, 1);
1741 } else {
1742 DumpStats(&printer, 2);
1743 }
1744 }
1745
1746 virtual void** ReadStackTraces() {
1747 return DumpStackTraces();
1748 }
1749
1750 virtual bool GetNumericProperty(const char* name, size_t* value) {
1751 ASSERT(name != NULL);
1752
1753 if (strcmp(name, "generic.current_allocated_bytes") == 0) {
1754 TCMallocStats stats;
1755 ExtractStats(&stats, NULL);
1756 *value = stats.system_bytes
1757 - stats.thread_bytes
1758 - stats.central_bytes
1759 - stats.pageheap_bytes;
1760 return true;
1761 }
1762
1763 if (strcmp(name, "generic.heap_size") == 0) {
1764 TCMallocStats stats;
1765 ExtractStats(&stats, NULL);
1766 *value = stats.system_bytes;
1767 return true;
1768 }
1769
1770 if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
1771 // We assume that bytes in the page heap are not fragmented too
1772 // badly, and are therefore available for allocation.
1773 SpinLockHolder l(&pageheap_lock);
1774 *value = pageheap->FreeBytes();
1775 return true;
1776 }
1777
1778 if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
1779 SpinLockHolder l(&pageheap_lock);
1780 *value = overall_thread_cache_size;
1781 return true;
1782 }
1783
1784 if (strcmp(name, "tcmalloc.current_total_thread_cache_bytes") == 0) {
1785 TCMallocStats stats;
1786 ExtractStats(&stats, NULL);
1787 *value = stats.thread_bytes;
1788 return true;
1789 }
1790
1791 return false;
1792 }
1793
1794 virtual bool SetNumericProperty(const char* name, size_t value) {
1795 ASSERT(name != NULL);
1796
1797 if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
1798 // Clip the value to a reasonable range
1799 if (value < kMinThreadCacheSize) value = kMinThreadCacheSize;
1800 if (value > (1<<30)) value = (1<<30); // Limit to 1GB
1801
1802 SpinLockHolder l(&pageheap_lock);
1803 overall_thread_cache_size = static_cast<size_t>(value);
1804 TCMalloc_ThreadCache::RecomputeThreadCacheSize();
1805 return true;
1806 }
1807
1808 return false;
1809 }
1810};
1811#endif
1812
1813// RedHat 9's pthread manager allocates an object directly by calling
1814// a __libc_XXX() routine. This memory block is not known to tcmalloc.
1815// At cleanup time, the pthread manager calls free() on this
1816// pointer, which then crashes.
1817//
1818// We hack around this problem by disabling all deallocations
1819// after a global object destructor in this module has been called.
1820#ifndef WTF_CHANGES
1821static bool tcmalloc_is_destroyed = false;
1822#endif
1823
1824//-------------------------------------------------------------------
1825// Helpers for the exported routines below
1826//-------------------------------------------------------------------
1827
1828#ifndef WTF_CHANGES
1829
1830static Span* DoSampledAllocation(size_t size) {
1831 SpinLockHolder h(&pageheap_lock);
1832
1833 // Allocate span
1834 Span* span = pageheap->New(pages(size == 0 ? 1 : size));
1835 if (span == NULL) {
1836 return NULL;
1837 }
1838
1839 // Allocate stack trace
1840 StackTrace* stack = stacktrace_allocator.New();
1841 if (stack == NULL) {
1842 // Sampling failed because of lack of memory
1843 return span;
1844 }
1845
1846 // Fill stack trace and record properly
1847 stack->depth = GetStackTrace(stack->stack, kMaxStackDepth, 2);
1848 stack->size = size;
1849 span->sample = 1;
1850 span->objects = stack;
1851 DLL_Prepend(&sampled_objects, span);
1852
1853 return span;
1854}
1855#endif
1856
1857static ALWAYS_INLINE void* do_malloc(size_t size) {
1858
1859#ifndef WTF_CHANGES
1860 if (TCMallocDebug::level >= TCMallocDebug::kVerbose)
1861 MESSAGE("In tcmalloc do_malloc(%" PRIuS")\n", size);
1862#endif
1863
1864#ifndef WTF_CHANGES
1865 // The following call forces module initialization
1866 TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache();
1867 if (heap->SampleAllocation(size)) {
1868 Span* span = DoSampledAllocation(size);
1869 if (span == NULL) return NULL;
1870 return reinterpret_cast<void*>(span->start << kPageShift);
1871 } else
1872#endif
1873 if (size > kMaxSize) {
1874 // Use page-level allocator
1875 if (!tsd_inited && !phinited)
1876 TCMalloc_ThreadCache::InitModule();
1877
1878 SpinLockHolder h(&pageheap_lock);
1879
1880
1881 Span* span = pageheap->New(pages(size));
1882 if (span == NULL) return NULL;
1883 return reinterpret_cast<void*>(span->start << kPageShift);
1884 } else {
1885#ifdef WTF_CHANGES
1886 TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache();
1887#endif
1888 return heap->Allocate(size);
1889 }
1890}
1891
1892static ALWAYS_INLINE void do_free(void* ptr) {
1893#ifndef WTF_CHANGES
1894 if (TCMallocDebug::level >= TCMallocDebug::kVerbose)
1895 MESSAGE("In tcmalloc do_free(%p)\n", ptr);
1896#endif
1897#if WTF_CHANGES
1898 if (ptr == NULL) return;
1899#else WTF_CHANGES
1900 if (ptr == NULL || tcmalloc_is_destroyed) return;
1901#endif
1902
1903 ASSERT(pageheap != NULL); // Should not call free() before malloc()
1904 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
1905 Span* span = pageheap->GetDescriptor(p);
1906
1907#ifndef WTF_CHANGES
1908 if (span == NULL) {
1909 // We've seen systems where a piece of memory allocated using the
1910 // allocator built in to libc is deallocated using free() and
1911 // therefore ends up inside tcmalloc which can't find the
1912 // corresponding span. We silently throw this object on the floor
1913 // instead of crashing.
1914 MESSAGE("tcmalloc: ignoring potential glibc-2.3.5 induced free "
1915 "of an unknown object %p\n", ptr);
1916 return;
1917 }
1918#endif
1919
1920 ASSERT(span != NULL);
1921 ASSERT(!span->free);
1922 const size_t cl = span->sizeclass;
1923 if (cl != 0) {
1924 ASSERT(!span->sample);
1925 TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCacheIfPresent();
1926 if (heap != NULL) {
1927 heap->Deallocate(ptr, cl);
1928 } else {
1929 // Delete directly into central cache
1930 SpinLockHolder h(&central_cache[cl].lock_);
1931 central_cache[cl].Insert(ptr);
1932 }
1933 } else {
1934 SpinLockHolder h(&pageheap_lock);
1935 ASSERT(reinterpret_cast<uintptr_t>(ptr) % kPageSize == 0);
1936 ASSERT(span->start == p);
1937 if (span->sample) {
1938 DLL_Remove(span);
1939 stacktrace_allocator.Delete(reinterpret_cast<StackTrace*>(span->objects));
1940 span->objects = NULL;
1941 }
1942 pageheap->Delete(span);
1943 }
1944}
1945
1946#ifndef WTF_CHANGES
1947// For use by exported routines below that want specific alignments
1948//
1949// Note: this code can be slow, and can significantly fragment memory.
1950// The expectation is that memalign/posix_memalign/valloc/pvalloc will
1951// not be invoked very often. This requirement simplifies our
1952// implementation and allows us to tune for expected allocation
1953// patterns.
1954static void* do_memalign(size_t align, size_t size) {
1955 ASSERT((align & (align - 1)) == 0);
1956 ASSERT(align > 0);
1957 if (pageheap == NULL) TCMalloc_ThreadCache::InitModule();
1958
1959 // Allocate at least one byte to avoid boundary conditions below
1960 if (size == 0) size = 1;
1961
1962 if (size <= kMaxSize && align < kPageSize) {
1963 // Search through acceptable size classes looking for one with
1964 // enough alignment. This depends on the fact that
1965 // InitSizeClasses() currently produces several size classes that
1966 // are aligned at powers of two. We will waste time and space if
1967 // we miss in the size class array, but that is deemed acceptable
1968 // since memalign() should be used rarely.
1969 size_t cl = SizeClass(size);
1970 while (cl < kNumClasses && ((class_to_size[cl] & (align - 1)) != 0)) {
1971 cl++;
1972 }
1973 if (cl < kNumClasses) {
1974 TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache();
1975 return heap->Allocate(class_to_size[cl]);
1976 }
1977 }
1978
1979 // We will allocate directly from the page heap
1980 SpinLockHolder h(&pageheap_lock);
1981
1982 if (align <= kPageSize) {
1983 // Any page-level allocation will be fine
1984 // TODO: We could put the rest of this page in the appropriate
1985 // TODO: cache but it does not seem worth it.
1986 Span* span = pageheap->New(pages(size));
1987 if (span == NULL) return NULL;
1988 return reinterpret_cast<void*>(span->start << kPageShift);
1989 }
1990
1991 // Allocate extra pages and carve off an aligned portion
1992 const int alloc = pages(size + align);
1993 Span* span = pageheap->New(alloc);
1994 if (span == NULL) return NULL;
1995
1996 // Skip starting portion so that we end up aligned
1997 int skip = 0;
1998 while ((((span->start+skip) << kPageShift) & (align - 1)) != 0) {
1999 skip++;
2000 }
2001 ASSERT(skip < alloc);
2002 if (skip > 0) {
2003 Span* rest = pageheap->Split(span, skip);
2004 pageheap->Delete(span);
2005 span = rest;
2006 }
2007
2008 // Skip trailing portion that we do not need to return
2009 const size_t needed = pages(size);
2010 ASSERT(span->length >= needed);
2011 if (span->length > needed) {
2012 Span* trailer = pageheap->Split(span, needed);
2013 pageheap->Delete(trailer);
2014 }
2015 return reinterpret_cast<void*>(span->start << kPageShift);
2016}
2017#endif
2018
2019
2020// The constructor allocates an object to ensure that initialization
2021// runs before main(), and therefore we do not have a chance to become
2022// multi-threaded before initialization. We also create the TSD key
2023// here. Presumably by the time this constructor runs, glibc is in
2024// good enough shape to handle pthread_key_create().
2025//
2026// The constructor also takes the opportunity to tell STL to use
2027// tcmalloc. We want to do this early, before construct time, so
2028// all user STL allocations go through tcmalloc (which works really
2029// well for STL).
2030//
2031// The destructor prints stats when the program exits.
2032
2033class TCMallocGuard {
2034 public:
2035 TCMallocGuard() {
2036#ifndef WTF_CHANGES
2037 char *envval;
2038 if ((envval = getenv("TCMALLOC_DEBUG"))) {
2039 TCMallocDebug::level = atoi(envval);
2040 MESSAGE("Set tcmalloc debugging level to %d\n", TCMallocDebug::level);
2041 }
2042#endif
2043 do_free(do_malloc(1));
2044 TCMalloc_ThreadCache::InitTSD();
2045 do_free(do_malloc(1));
2046#ifndef WTF_CHANGES
2047 MallocExtension::Register(new TCMallocImplementation);
2048#endif
2049 }
2050
2051#ifndef WTF_CHANGES
2052 ~TCMallocGuard() {
2053 const char* env = getenv("MALLOCSTATS");
2054 if (env != NULL) {
2055 int level = atoi(env);
2056 if (level < 1) level = 1;
2057 PrintStats(level);
2058 }
2059 }
2060#endif
2061};
2062
2063static TCMallocGuard module_enter_exit_hook;
2064
2065
2066//-------------------------------------------------------------------
2067// Exported routines
2068//-------------------------------------------------------------------
2069
2070// CAVEAT: The code structure below ensures that MallocHook methods are always
2071// called from the stack frame of the invoked allocation function.
2072// heap-checker.cc depends on this to start a stack trace from
2073// the call to the (de)allocation function.
2074
2075#ifndef WTF_CHANGES
2076extern "C"
2077#endif
2078void* malloc(size_t size) {
2079 void* result = do_malloc(size);
2080#ifndef WTF_CHANGES
2081 MallocHook::InvokeNewHook(result, size);
2082#endif
2083 return result;
2084}
2085
2086#ifndef WTF_CHANGES
2087extern "C"
2088#endif
2089void free(void* ptr) {
2090#ifndef WTF_CHANGES
2091 MallocHook::InvokeDeleteHook(ptr);
2092#endif
2093 do_free(ptr);
2094}
2095
2096#ifndef WTF_CHANGES
2097extern "C"
2098#endif
2099void* calloc(size_t n, size_t elem_size) {
2100 void* result = do_malloc(n * elem_size);
2101 if (result != NULL) {
2102 memset(result, 0, n * elem_size);
2103 }
2104#ifndef WTF_CHANGES
2105 MallocHook::InvokeNewHook(result, n * elem_size);
2106#endif
2107 return result;
2108}
2109
2110#ifndef WTF_CHANGES
2111extern "C"
2112#endif
2113void cfree(void* ptr) {
2114#ifndef WTF_CHANGES
2115 MallocHook::InvokeDeleteHook(ptr);
2116#endif
2117 do_free(ptr);
2118}
2119
2120#ifndef WTF_CHANGES
2121extern "C"
2122#endif
2123void* realloc(void* old_ptr, size_t new_size) {
2124 if (old_ptr == NULL) {
2125 void* result = do_malloc(new_size);
2126#ifndef WTF_CHANGES
2127 MallocHook::InvokeNewHook(result, new_size);
2128#endif
2129 return result;
2130 }
2131 if (new_size == 0) {
2132#ifndef WTF_CHANGES
2133 MallocHook::InvokeDeleteHook(old_ptr);
2134#endif
2135 free(old_ptr);
2136 return NULL;
2137 }
2138
2139 // Get the size of the old entry
2140 const PageID p = reinterpret_cast<uintptr_t>(old_ptr) >> kPageShift;
2141 Span* span = pageheap->GetDescriptor(p);
2142 size_t old_size;
2143 if (span->sizeclass != 0) {
2144 old_size = ByteSizeForClass(span->sizeclass);
2145 } else {
2146 old_size = span->length << kPageShift;
2147 }
2148
2149 // Reallocate if the new size is larger than the old size,
2150 // or if the new size is significantly smaller than the old size.
2151 if ((new_size > old_size) || (AllocationSize(new_size) < old_size)) {
2152 // Need to reallocate
2153 void* new_ptr = do_malloc(new_size);
2154 if (new_ptr == NULL) {
2155 return NULL;
2156 }
2157#ifndef WTF_CHANGES
2158 MallocHook::InvokeNewHook(new_ptr, new_size);
2159#endif
2160 memcpy(new_ptr, old_ptr, ((old_size < new_size) ? old_size : new_size));
2161#ifndef WTF_CHANGES
2162 MallocHook::InvokeDeleteHook(old_ptr);
2163#endif
2164 free(old_ptr);
2165 return new_ptr;
2166 } else {
2167 return old_ptr;
2168 }
2169}
2170
2171#ifndef COMPILER_INTEL
2172#define OPNEW_THROW
2173#define OPDELETE_THROW
2174#else
2175#define OPNEW_THROW throw(std::bad_alloc)
2176#define OPDELETE_THROW throw()
2177#endif
2178
2179#ifndef WTF_CHANGES
2180
2181void* operator new(size_t size) OPNEW_THROW {
2182 void* p = do_malloc(size);
2183 if (p == NULL) {
2184 MESSAGE("Unable to allocate %" PRIuS " bytes: new failed\n", size);
2185 abort();
2186 }
2187 MallocHook::InvokeNewHook(p, size);
2188 return p;
2189}
2190
2191void operator delete(void* p) OPDELETE_THROW {
2192 MallocHook::InvokeDeleteHook(p);
2193 do_free(p);
2194}
2195
2196void* operator new[](size_t size) OPNEW_THROW {
2197 void* p = do_malloc(size);
2198 if (p == NULL) {
2199 MESSAGE("Unable to allocate %" PRIuS " bytes: new failed\n", size);
2200 abort();
2201 }
2202 MallocHook::InvokeNewHook(p, size);
2203 return p;
2204}
2205
2206void operator delete[](void* p) OPDELETE_THROW {
2207 MallocHook::InvokeDeleteHook(p);
2208 do_free(p);
2209}
2210
2211extern "C" void* memalign(size_t align, size_t size) {
2212 void* result = do_memalign(align, size);
2213 MallocHook::InvokeNewHook(result, size);
2214 return result;
2215}
2216
2217extern "C" int posix_memalign(void** result_ptr, size_t align, size_t size) {
2218 if (((align % sizeof(void*)) != 0) ||
2219 ((align & (align - 1)) != 0) ||
2220 (align == 0)) {
2221 return EINVAL;
2222 }
2223
2224 void* result = do_memalign(align, size);
2225 MallocHook::InvokeNewHook(result, size);
2226 if (result == NULL) {
2227 return ENOMEM;
2228 } else {
2229 *result_ptr = result;
2230 return 0;
2231 }
2232}
2233
2234static size_t pagesize = 0;
2235
2236extern "C" void* valloc(size_t size) {
2237 // Allocate page-aligned object of length >= size bytes
2238 if (pagesize == 0) pagesize = getpagesize();
2239 void* result = do_memalign(pagesize, size);
2240 MallocHook::InvokeNewHook(result, size);
2241 return result;
2242}
2243
2244extern "C" void* pvalloc(size_t size) {
2245 // Round up size to a multiple of pagesize
2246 if (pagesize == 0) pagesize = getpagesize();
2247 size = (size + pagesize - 1) & ~(pagesize - 1);
2248 void* result = do_memalign(pagesize, size);
2249 MallocHook::InvokeNewHook(result, size);
2250 return result;
2251}
2252
2253extern "C" void malloc_stats(void) {
2254 PrintStats(1);
2255}
2256
2257extern "C" int mallopt(int cmd, int value) {
2258 return 1; // Indicates error
2259}
2260
2261extern "C" struct mallinfo mallinfo(void) {
2262 TCMallocStats stats;
2263 ExtractStats(&stats, NULL);
2264
2265 // Just some of the fields are filled in.
2266 struct mallinfo info;
2267 memset(&info, 0, sizeof(info));
2268
2269 // Unfortunately, the struct contains "int" field, so some of the
2270 // size values will be truncated.
2271 info.arena = static_cast<int>(stats.system_bytes);
2272 info.fsmblks = static_cast<int>(stats.thread_bytes + stats.central_bytes);
2273 info.fordblks = static_cast<int>(stats.pageheap_bytes);
2274 info.uordblks = static_cast<int>(stats.system_bytes
2275 - stats.thread_bytes
2276 - stats.central_bytes
2277 - stats.pageheap_bytes);
2278
2279 return info;
2280}
2281
2282//-------------------------------------------------------------------
2283// Some library routines on RedHat 9 allocate memory using malloc()
2284// and free it using __libc_free() (or vice-versa). Since we provide
2285// our own implementations of malloc/free, we need to make sure that
2286// the __libc_XXX variants also point to the same implementations.
2287//-------------------------------------------------------------------
2288
2289extern "C" {
2290#if COMPILER(GCC) && HAVE(__ATTRIBUTE__)
2291 // Potentially faster variants that use the gcc alias extension
2292#define ALIAS(x) __attribute__ ((weak, alias (x)))
2293 void* __libc_malloc(size_t size) ALIAS("malloc");
2294 void __libc_free(void* ptr) ALIAS("free");
2295 void* __libc_realloc(void* ptr, size_t size) ALIAS("realloc");
2296 void* __libc_calloc(size_t n, size_t size) ALIAS("calloc");
2297 void __libc_cfree(void* ptr) ALIAS("cfree");
2298 void* __libc_memalign(size_t align, size_t s) ALIAS("memalign");
2299 void* __libc_valloc(size_t size) ALIAS("valloc");
2300 void* __libc_pvalloc(size_t size) ALIAS("pvalloc");
2301 int __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign");
2302#undef ALIAS
2303#else
2304 // Portable wrappers
2305 void* __libc_malloc(size_t size) { return malloc(size); }
2306 void __libc_free(void* ptr) { free(ptr); }
2307 void* __libc_realloc(void* ptr, size_t size) { return realloc(ptr, size); }
2308 void* __libc_calloc(size_t n, size_t size) { return calloc(n, size); }
2309 void __libc_cfree(void* ptr) { cfree(ptr); }
2310 void* __libc_memalign(size_t align, size_t s) { return memalign(align, s); }
2311 void* __libc_valloc(size_t size) { return valloc(size); }
2312 void* __libc_pvalloc(size_t size) { return pvalloc(size); }
2313 int __posix_memalign(void** r, size_t a, size_t s) {
2314 return posix_memalign(r, a, s);
2315 }
2316#endif
2317
2318}
2319
2320#endif
2321
2322#if WTF_CHANGES
2323} // namespace WTF
2324#endif
2325
2326#endif // USE_SYSTEM_MALLOC
Note: See TracBrowser for help on using the repository browser.