blob: 9537745b86f561fc65ff3cc859042ba29f3d89f9 [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// Copyright (c) 2013, 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: Petr Hosek
32
33#ifndef _WIN32
34# error You should only be including windows/system-alloc.cc in a windows environment!
35#endif
36
37#include <config.h>
38#include <windows.h>
39#include <algorithm> // std::min
40#include <gperftools/malloc_extension.h>
41#include "base/logging.h"
42#include "base/spinlock.h"
43#include "internal_logging.h"
44#include "system-alloc.h"
45
46static SpinLock spinlock(SpinLock::LINKER_INITIALIZED);
47
48// The current system allocator declaration
49SysAllocator* sys_alloc = NULL;
50// Number of bytes taken from system.
51size_t TCMalloc_SystemTaken = 0;
52
53class VirtualSysAllocator : public SysAllocator {
54public:
55 VirtualSysAllocator() : SysAllocator() {
56 }
57 void* Alloc(size_t size, size_t *actual_size, size_t alignment);
58};
59static char virtual_space[sizeof(VirtualSysAllocator)];
60
61// This is mostly like MmapSysAllocator::Alloc, except it does these weird
62// munmap's in the middle of the page, which is forbidden in windows.
63void* VirtualSysAllocator::Alloc(size_t size, size_t *actual_size,
64 size_t alignment) {
65 // Align on the pagesize boundary
66 const int pagesize = getpagesize();
67 if (alignment < pagesize) alignment = pagesize;
68 size = ((size + alignment - 1) / alignment) * alignment;
69
70 // Report the total number of bytes the OS actually delivered. This might be
71 // greater than |size| because of alignment concerns. The full size is
72 // necessary so that adjacent spans can be coalesced.
73 // TODO(antonm): proper processing of alignments
74 // in actual_size and decommitting.
75 if (actual_size) {
76 *actual_size = size;
77 }
78
79 // We currently do not support alignments larger than the pagesize or
80 // alignments that are not multiples of the pagesize after being floored.
81 // If this ability is needed it can be done by the caller (assuming it knows
82 // the page size).
83 assert(alignment <= pagesize);
84
85 void* result = VirtualAlloc(0, size,
86 MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
87 if (result == NULL)
88 return NULL;
89
90 // If the result is not aligned memory fragmentation will result which can
91 // lead to pathological memory use.
92 assert((reinterpret_cast<uintptr_t>(result) & (alignment - 1)) == 0);
93
94 return result;
95}
96
97#ifdef _MSC_VER
98
99extern "C" SysAllocator* tc_get_sysalloc_override(SysAllocator *def);
100extern "C" SysAllocator* tc_get_sysalloc_default(SysAllocator *def)
101{
102 return def;
103}
104
105#if defined(_M_IX86)
106#pragma comment(linker, "/alternatename:_tc_get_sysalloc_override=_tc_get_sysalloc_default")
107#elif defined(_M_X64)
108#pragma comment(linker, "/alternatename:tc_get_sysalloc_override=tc_get_sysalloc_default")
109#endif
110
111#else // !_MSC_VER
112
113extern "C" ATTRIBUTE_NOINLINE
114SysAllocator* tc_get_sysalloc_override(SysAllocator *def)
115{
116 return def;
117}
118
119#endif
120
121static bool system_alloc_inited = false;
122void InitSystemAllocators(void) {
123 VirtualSysAllocator *alloc = new (virtual_space) VirtualSysAllocator();
124 sys_alloc = tc_get_sysalloc_override(alloc);
125}
126
127extern PERFTOOLS_DLL_DECL
128void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
129 size_t alignment) {
130 SpinLockHolder lock_holder(&spinlock);
131
132 if (!system_alloc_inited) {
133 InitSystemAllocators();
134 system_alloc_inited = true;
135 }
136
137 void* result = sys_alloc->Alloc(size, actual_size, alignment);
138 if (result != NULL) {
139 if (actual_size) {
140 TCMalloc_SystemTaken += *actual_size;
141 } else {
142 TCMalloc_SystemTaken += size;
143 }
144 }
145 return result;
146}
147
148extern PERFTOOLS_DLL_DECL
149bool TCMalloc_SystemRelease(void* start, size_t length) {
150 if (VirtualFree(start, length, MEM_DECOMMIT))
151 return true;
152
153 // The decommit may fail if the memory region consists of allocations
154 // from more than one call to VirtualAlloc. In this case, fall back to
155 // using VirtualQuery to retrieve the allocation boundaries and decommit
156 // them each individually.
157
158 char* ptr = static_cast<char*>(start);
159 char* end = ptr + length;
160 MEMORY_BASIC_INFORMATION info;
161 while (ptr < end) {
162 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
163 assert(resultSize == sizeof(info));
164 size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr);
165 BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
166 assert(success == TRUE);
167 ptr += decommitSize;
168 }
169
170 return true;
171}
172
173extern PERFTOOLS_DLL_DECL
174void TCMalloc_SystemCommit(void* start, size_t length) {
175 if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
176 return;
177
178 // The commit may fail if the memory region consists of allocations
179 // from more than one call to VirtualAlloc. In this case, fall back to
180 // using VirtualQuery to retrieve the allocation boundaries and commit them
181 // each individually.
182
183 char* ptr = static_cast<char*>(start);
184 char* end = ptr + length;
185 MEMORY_BASIC_INFORMATION info;
186 while (ptr < end) {
187 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
188 assert(resultSize == sizeof(info));
189
190 size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr);
191 void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT,
192 PAGE_READWRITE);
193 assert(newAddress == ptr);
194 ptr += commitSize;
195 }
196}
197
198bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
199 return false; // we don't allow registration on windows, right now
200}
201
202void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
203 // We don't dump stats on windows, right now
204}