Revert of Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320) (patchset...
[chromium-blink-merge.git] / third_party / tcmalloc / chromium / src / windows / port.cc
blob690ab0bccb7e460294bd103a1b1e2f3a3c0d171e
1 /* Copyright (c) 2007, 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.
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.
30 * ---
31 * Author: Craig Silverstein
34 #ifndef _WIN32
35 # error You should only be including windows/port.cc in a windows environment!
36 #endif
38 #define NOMINMAX // so std::max, below, compiles correctly
39 #include <config.h>
40 #include <string.h> // for strlen(), memset(), memcmp()
41 #include <assert.h>
42 #include <stdarg.h> // for va_list, va_start, va_end
43 #include <windows.h>
44 #include <algorithm>
45 #include "port.h"
46 #include "base/logging.h"
47 #include "base/spinlock.h"
48 #include "internal_logging.h"
49 #include "system-alloc.h"
51 // -----------------------------------------------------------------------
52 // Basic libraries
54 int getpagesize() {
55 static int pagesize = 0;
56 if (pagesize == 0) {
57 SYSTEM_INFO system_info;
58 GetSystemInfo(&system_info);
59 pagesize = std::max(system_info.dwPageSize,
60 system_info.dwAllocationGranularity);
62 return pagesize;
65 extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) {
66 LOG(FATAL, "Windows doesn't implement sbrk!\n");
67 return NULL;
70 // We need to write to 'stderr' without having windows allocate memory.
71 // The safest way is via a low-level call like WriteConsoleA(). But
72 // even then we need to be sure to print in small bursts so as to not
73 // require memory allocation.
74 extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) {
75 // Looks like windows allocates for writes of >80 bytes
76 for (int i = 0; i < len; i += 80) {
77 write(STDERR_FILENO, buf + i, std::min(80, len - i));
82 // -----------------------------------------------------------------------
83 // Threads code
85 // Declared (not extern "C") in thread_cache.h
86 bool CheckIfKernelSupportsTLS() {
87 // TODO(csilvers): return true (all win's since win95, at least, support this)
88 return false;
91 // Windows doesn't support pthread_key_create's destr_function, and in
92 // fact it's a bit tricky to get code to run when a thread exits. This
93 // is cargo-cult magic from http://www.codeproject.com/threads/tls.asp.
94 // This code is for VC++ 7.1 and later; VC++ 6.0 support is possible
95 // but more busy-work -- see the webpage for how to do it. If all
96 // this fails, we could use DllMain instead. The big problem with
97 // DllMain is it doesn't run if this code is statically linked into a
98 // binary (it also doesn't run if the thread is terminated via
99 // TerminateThread, which if we're lucky this routine does).
101 // Force a reference to _tls_used to make the linker create the TLS directory
102 // if it's not already there (that is, even if __declspec(thread) is not used).
103 // Force a reference to p_thread_callback_tcmalloc and p_process_term_tcmalloc
104 // to prevent whole program optimization from discarding the variables.
105 #ifdef _MSC_VER
106 #if defined(_M_IX86)
107 #pragma comment(linker, "/INCLUDE:__tls_used")
108 #pragma comment(linker, "/INCLUDE:_p_thread_callback_tcmalloc")
109 #pragma comment(linker, "/INCLUDE:_p_process_term_tcmalloc")
110 #elif defined(_M_X64)
111 #pragma comment(linker, "/INCLUDE:_tls_used")
112 #pragma comment(linker, "/INCLUDE:p_thread_callback_tcmalloc")
113 #pragma comment(linker, "/INCLUDE:p_process_term_tcmalloc")
114 #endif
115 #endif
117 // When destr_fn eventually runs, it's supposed to take as its
118 // argument the tls-value associated with key that pthread_key_create
119 // creates. (Yeah, it sounds confusing but it's really not.) We
120 // store the destr_fn/key pair in this data structure. Because we
121 // store this in a single var, this implies we can only have one
122 // destr_fn in a program! That's enough in practice. If asserts
123 // trigger because we end up needing more, we'll have to turn this
124 // into an array.
125 struct DestrFnClosure {
126 void (*destr_fn)(void*);
127 pthread_key_t key_for_destr_fn_arg;
130 static DestrFnClosure destr_fn_info; // initted to all NULL/0.
132 static int on_process_term(void) {
133 if (destr_fn_info.destr_fn) {
134 void *ptr = TlsGetValue(destr_fn_info.key_for_destr_fn_arg);
135 // This shouldn't be necessary, but in Release mode, Windows
136 // sometimes trashes the pointer in the TLS slot, so we need to
137 // remove the pointer from the TLS slot before the thread dies.
138 TlsSetValue(destr_fn_info.key_for_destr_fn_arg, NULL);
139 if (ptr) // pthread semantics say not to call if ptr is NULL
140 (*destr_fn_info.destr_fn)(ptr);
142 return 0;
145 static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) {
146 if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed!
147 on_process_term();
151 #ifdef _MSC_VER
153 // extern "C" suppresses C++ name mangling so we know the symbol names
154 // for the linker /INCLUDE:symbol pragmas above.
155 extern "C" {
156 // This tells the linker to run these functions.
157 // We use CRT$XLY instead of CRT$XLB to ensure we're called LATER in sequence.
158 #pragma section(".CRT$XLY", read)
159 _declspec(allocate(".CRT$XLY")) \
160 void (NTAPI *p_thread_callback_tcmalloc)(
161 HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback;
162 #pragma section(".CRT$XTU", read)
163 _declspec(allocate(".CRT$XTU")) \
164 int (*p_process_term_tcmalloc)(void) = on_process_term;
165 } // extern "C"
167 #else // #ifdef _MSC_VER [probably msys/mingw]
169 // We have to try the DllMain solution here, because we can't use the
170 // msvc-specific pragmas.
171 BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) {
172 if (dwReason == DLL_THREAD_DETACH)
173 on_tls_callback(h, dwReason, pv);
174 else if (dwReason == DLL_PROCESS_DETACH)
175 on_process_term();
176 return TRUE;
179 #endif // #ifdef _MSC_VER
181 extern "C" pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) {
182 // Semantics are: we create a new key, and then promise to call
183 // destr_fn with TlsGetValue(key) when the thread is destroyed
184 // (as long as TlsGetValue(key) is not NULL).
185 pthread_key_t key = TlsAlloc();
186 if (destr_fn) { // register it
187 // If this assert fails, we'll need to support an array of destr_fn_infos
188 assert(destr_fn_info.destr_fn == NULL);
189 destr_fn_info.destr_fn = destr_fn;
190 destr_fn_info.key_for_destr_fn_arg = key;
192 return key;
195 // NOTE: this is Win2K and later. For Win98 we could use a CRITICAL_SECTION...
196 extern "C" int perftools_pthread_once(pthread_once_t *once_control,
197 void (*init_routine)(void)) {
198 // Try for a fast path first. Note: this should be an acquire semantics read.
199 // It is on x86 and x64, where Windows runs.
200 if (*once_control != 1) {
201 while (true) {
202 switch (InterlockedCompareExchange(once_control, 2, 0)) {
203 case 0:
204 init_routine();
205 InterlockedExchange(once_control, 1);
206 return 0;
207 case 1:
208 // The initializer has already been executed
209 return 0;
210 default:
211 // The initializer is being processed by another thread
212 SwitchToThread();
216 return 0;
220 // -----------------------------------------------------------------------
221 // These functions replace system-alloc.cc
223 // This is mostly like MmapSysAllocator::Alloc, except it does these weird
224 // munmap's in the middle of the page, which is forbidden in windows.
225 extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
226 size_t alignment) {
227 // Align on the pagesize boundary
228 const int pagesize = getpagesize();
229 if (alignment < pagesize) alignment = pagesize;
230 size = ((size + alignment - 1) / alignment) * alignment;
232 // Report the total number of bytes the OS actually delivered. This might be
233 // greater than |size| because of alignment concerns. The full size is
234 // necessary so that adjacent spans can be coalesced.
235 // TODO(antonm): proper processing of alignments
236 // in actual_size and decommitting.
237 if (actual_size) {
238 *actual_size = size;
241 // We currently do not support alignments larger than the pagesize or
242 // alignments that are not multiples of the pagesize after being floored.
243 // If this ability is needed it can be done by the caller (assuming it knows
244 // the page size).
245 assert(alignment <= pagesize);
247 void* result = VirtualAlloc(0, size,
248 MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
249 if (result == NULL)
250 return NULL;
252 // If the result is not aligned memory fragmentation will result which can
253 // lead to pathological memory use.
254 assert((reinterpret_cast<uintptr_t>(result) & (alignment - 1)) == 0);
256 return result;
259 size_t TCMalloc_SystemAddGuard(void* start, size_t size) {
260 static size_t pagesize = 0;
261 if (pagesize == 0) {
262 SYSTEM_INFO system_info;
263 GetSystemInfo(&system_info);
264 pagesize = system_info.dwPageSize;
267 // We know that TCMalloc_SystemAlloc will give us a correct page alignment
268 // regardless, so we can just assert to detect erroneous callers.
269 assert(reinterpret_cast<size_t>(start) % pagesize == 0);
271 // Add a guard page to catch metadata corruption. We're using the
272 // PAGE_GUARD flag rather than NO_ACCESS because we want the unique
273 // exception in crash reports.
274 DWORD permissions = 0;
275 if (size > pagesize &&
276 VirtualProtect(start, pagesize, PAGE_READONLY | PAGE_GUARD,
277 &permissions)) {
278 return pagesize;
281 return 0;
284 void TCMalloc_SystemRelease(void* start, size_t length) {
285 if (VirtualFree(start, length, MEM_DECOMMIT))
286 return;
288 // The decommit may fail if the memory region consists of allocations
289 // from more than one call to VirtualAlloc. In this case, fall back to
290 // using VirtualQuery to retrieve the allocation boundaries and decommit
291 // them each individually.
293 char* ptr = static_cast<char*>(start);
294 char* end = ptr + length;
295 MEMORY_BASIC_INFORMATION info;
296 while (ptr < end) {
297 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
298 assert(resultSize == sizeof(info));
299 size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr);
300 BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
301 assert(success == TRUE);
302 ptr += decommitSize;
306 void TCMalloc_SystemCommit(void* start, size_t length) {
307 if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
308 return;
310 // The commit may fail if the memory region consists of allocations
311 // from more than one call to VirtualAlloc. In this case, fall back to
312 // using VirtualQuery to retrieve the allocation boundaries and commit them
313 // each individually.
315 char* ptr = static_cast<char*>(start);
316 char* end = ptr + length;
317 MEMORY_BASIC_INFORMATION info;
318 while (ptr < end) {
319 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
320 assert(resultSize == sizeof(info));
322 size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr);
323 void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT,
324 PAGE_READWRITE);
325 assert(newAddress == ptr);
326 ptr += commitSize;
330 bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
331 return false; // we don't allow registration on windows, right now
334 void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
335 // We don't dump stats on windows, right now
338 // The current system allocator
339 SysAllocator* sys_alloc = NULL;
342 // -----------------------------------------------------------------------
343 // These functions rework existing functions of the same name in the
344 // Google codebase.
346 // A replacement for HeapProfiler::CleanupOldProfiles.
347 void DeleteMatchingFiles(const char* prefix, const char* full_glob) {
348 WIN32_FIND_DATAA found; // that final A is for Ansi (as opposed to Unicode)
349 HANDLE hFind = FindFirstFileA(full_glob, &found); // A is for Ansi
350 if (hFind != INVALID_HANDLE_VALUE) {
351 const int prefix_length = strlen(prefix);
352 do {
353 const char *fname = found.cFileName;
354 if ((strlen(fname) >= prefix_length) &&
355 (memcmp(fname, prefix, prefix_length) == 0)) {
356 RAW_VLOG(0, "Removing old heap profile %s\n", fname);
357 // TODO(csilvers): we really need to unlink dirname + fname
358 _unlink(fname);
360 } while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi
361 FindClose(hFind);