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