OLD | NEW |
1 /* Copyright (c) 2007, Google Inc. | 1 /* Copyright (c) 2007, Google Inc. |
2 * All rights reserved. | 2 * All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 26 matching lines...) Expand all Loading... |
37 | 37 |
38 #define NOMINMAX // so std::max, below, compiles correctly | 38 #define NOMINMAX // so std::max, below, compiles correctly |
39 #include <config.h> | 39 #include <config.h> |
40 #include <string.h> // for strlen(), memset(), memcmp() | 40 #include <string.h> // for strlen(), memset(), memcmp() |
41 #include <assert.h> | 41 #include <assert.h> |
42 #include <stdarg.h> // for va_list, va_start, va_end | 42 #include <stdarg.h> // for va_list, va_start, va_end |
43 #include <windows.h> | 43 #include <windows.h> |
44 #include "port.h" | 44 #include "port.h" |
45 #include "base/logging.h" | 45 #include "base/logging.h" |
46 #include "base/spinlock.h" | 46 #include "base/spinlock.h" |
| 47 #include "internal_logging.h" |
47 #include "system-alloc.h" | 48 #include "system-alloc.h" |
48 | 49 |
49 // ----------------------------------------------------------------------- | 50 // ----------------------------------------------------------------------- |
50 // Basic libraries | 51 // Basic libraries |
51 | 52 |
52 int getpagesize() { | 53 int getpagesize() { |
53 static int pagesize = 0; | 54 static int pagesize = 0; |
54 if (pagesize == 0) { | 55 if (pagesize == 0) { |
55 SYSTEM_INFO system_info; | 56 SYSTEM_INFO system_info; |
56 GetSystemInfo(&system_info); | 57 GetSystemInfo(&system_info); |
57 pagesize = std::max(system_info.dwPageSize, | 58 pagesize = std::max(system_info.dwPageSize, |
58 system_info.dwAllocationGranularity); | 59 system_info.dwAllocationGranularity); |
59 } | 60 } |
60 return pagesize; | 61 return pagesize; |
61 } | 62 } |
62 | 63 |
63 extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) { | 64 extern "C" PERFTOOLS_DLL_DECL void* __sbrk(std::ptrdiff_t increment) { |
64 LOG(FATAL, "Windows doesn't implement sbrk!\n"); | 65 LOG(FATAL, "Windows doesn't implement sbrk!\n"); |
65 return NULL; | 66 return NULL; |
66 } | 67 } |
67 | 68 |
68 // We need to write to 'stderr' without having windows allocate memory. | 69 // We need to write to 'stderr' without having windows allocate memory. |
69 // The safest way is via a low-level call like WriteConsoleA(). But | 70 // The safest way is via a low-level call like WriteConsoleA(). But |
70 // even then we need to be sure to print in small bursts so as to not | 71 // even then we need to be sure to print in small bursts so as to not |
71 // require memory allocation. | 72 // require memory allocation. |
72 extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { | 73 extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { |
73 // Looks like windows allocates for writes of >80 bytes | 74 // Looks like windows allocates for writes of >80 bytes |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 } | 142 } |
142 | 143 |
143 static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { | 144 static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { |
144 if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed! | 145 if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed! |
145 on_process_term(); | 146 on_process_term(); |
146 } | 147 } |
147 } | 148 } |
148 | 149 |
149 #ifdef _MSC_VER | 150 #ifdef _MSC_VER |
150 | 151 |
151 // extern "C" suppresses C++ name mangling so we know the symbol names | 152 // extern "C" suppresses C++ name mangling so we know the symbol names for the |
152 // for the linker /INCLUDE:symbol pragmas above. | 153 // linker /INCLUDE:symbol pragmas above. |
153 extern "C" { | 154 extern "C" { |
154 // This tells the linker to run these functions. | 155 // This tells the linker to run these functions. |
155 #pragma data_seg(push, old_seg) | 156 #pragma data_seg(push, old_seg) |
156 #pragma data_seg(".CRT$XLB") | 157 // Use CRT$XLY instead of CRT$XLB to ensure we're called LATER in sequence. |
| 158 #pragma data_seg(".CRT$XLY") |
157 void (NTAPI *p_thread_callback_tcmalloc)( | 159 void (NTAPI *p_thread_callback_tcmalloc)( |
158 HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback; | 160 HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback; |
159 #pragma data_seg(".CRT$XTU") | 161 #pragma data_seg(".CRT$XTU") |
160 int (*p_process_term_tcmalloc)(void) = on_process_term; | 162 int (*p_process_term_tcmalloc)(void) = on_process_term; |
161 #pragma data_seg(pop, old_seg) | 163 #pragma data_seg(pop, old_seg) |
162 } // extern "C" | 164 } // extern "C" |
163 | 165 |
164 #else // #ifdef _MSC_VER [probably msys/mingw] | 166 #else // #ifdef _MSC_VER [probably msys/mingw] |
165 | 167 |
166 // We have to try the DllMain solution here, because we can't use the | 168 // We have to try the DllMain solution here, because we can't use the |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
210 } | 212 } |
211 } | 213 } |
212 } | 214 } |
213 return 0; | 215 return 0; |
214 } | 216 } |
215 | 217 |
216 | 218 |
217 // ----------------------------------------------------------------------- | 219 // ----------------------------------------------------------------------- |
218 // These functions replace system-alloc.cc | 220 // These functions replace system-alloc.cc |
219 | 221 |
| 222 // The current system allocator. Because we don't link with system-alloc.cc, |
| 223 // we need to define our own. |
| 224 SysAllocator* sys_alloc = NULL; |
| 225 |
220 // This is mostly like MmapSysAllocator::Alloc, except it does these weird | 226 // This is mostly like MmapSysAllocator::Alloc, except it does these weird |
221 // munmap's in the middle of the page, which is forbidden in windows. | 227 // munmap's in the middle of the page, which is forbidden in windows. |
222 extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, | 228 extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, |
223 size_t alignment) { | 229 size_t alignment) { |
224 // Align on the pagesize boundary | 230 // Align on the pagesize boundary |
225 const int pagesize = getpagesize(); | 231 const int pagesize = getpagesize(); |
226 if (alignment < pagesize) alignment = pagesize; | 232 if (alignment < pagesize) alignment = pagesize; |
227 size = ((size + alignment - 1) / alignment) * alignment; | 233 size = ((size + alignment - 1) / alignment) * alignment; |
228 | 234 |
229 // Safest is to make actual_size same as input-size. | 235 // Report the total number of bytes the OS actually delivered. This might be |
| 236 // greater than |size| because of alignment concerns. The full size is |
| 237 // necessary so that adjacent spans can be coalesced. |
| 238 // TODO(antonm): proper processing of alignments |
| 239 // in actual_size and decommitting. |
230 if (actual_size) { | 240 if (actual_size) { |
231 *actual_size = size; | 241 *actual_size = size; |
232 } | 242 } |
233 | 243 |
234 // Ask for extra memory if alignment > pagesize | 244 // We currently do not support alignments larger than the pagesize or |
235 size_t extra = 0; | 245 // alignments that are not multiples of the pagesize after being floored. |
236 if (alignment > pagesize) { | 246 // If this ability is needed it can be done by the caller (assuming it knows |
237 extra = alignment - pagesize; | 247 // the page size). |
238 } | 248 assert(alignment <= pagesize); |
239 | 249 |
240 void* result = VirtualAlloc(0, size + extra, | 250 void* result = VirtualAlloc(0, size, |
241 MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); | 251 MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); |
242 if (result == NULL) | 252 if (result == NULL) |
243 return NULL; | 253 return NULL; |
244 | 254 |
245 // Adjust the return memory so it is aligned | 255 // If the result is not aligned memory fragmentation will result which can |
246 uintptr_t ptr = reinterpret_cast<uintptr_t>(result); | 256 // lead to pathological memory use. |
247 size_t adjust = 0; | 257 assert((reinterpret_cast<uintptr_t>(result) & (alignment - 1)) == 0); |
248 if ((ptr & (alignment - 1)) != 0) { | 258 |
249 adjust = alignment - (ptr & (alignment - 1)); | 259 return result; |
| 260 } |
| 261 |
| 262 size_t TCMalloc_SystemAddGuard(void* start, size_t size) { |
| 263 static size_t pagesize = 0; |
| 264 if (pagesize == 0) { |
| 265 SYSTEM_INFO system_info; |
| 266 GetSystemInfo(&system_info); |
| 267 pagesize = system_info.dwPageSize; |
250 } | 268 } |
251 | 269 |
252 ptr += adjust; | 270 // We know that TCMalloc_SystemAlloc will give us a correct page alignment |
253 return reinterpret_cast<void*>(ptr); | 271 // regardless, so we can just assert to detect erroneous callers. |
| 272 assert(reinterpret_cast<size_t>(start) % pagesize == 0); |
| 273 |
| 274 // Add a guard page to catch metadata corruption. We're using the |
| 275 // PAGE_GUARD flag rather than NO_ACCESS because we want the unique |
| 276 // exception in crash reports. |
| 277 DWORD permissions = 0; |
| 278 if (size > pagesize && |
| 279 VirtualProtect(start, pagesize, PAGE_READONLY | PAGE_GUARD, |
| 280 &permissions)) { |
| 281 return pagesize; |
| 282 } |
| 283 |
| 284 return 0; |
254 } | 285 } |
255 | 286 |
256 void TCMalloc_SystemRelease(void* start, size_t length) { | 287 void TCMalloc_SystemRelease(void* start, size_t length) { |
257 // TODO(csilvers): should I be calling VirtualFree here? | 288 if (VirtualFree(start, length, MEM_DECOMMIT)) |
| 289 return; |
| 290 |
| 291 // The decommit may fail if the memory region consists of allocations |
| 292 // from more than one call to VirtualAlloc. In this case, fall back to |
| 293 // using VirtualQuery to retrieve the allocation boundaries and decommit |
| 294 // them each individually. |
| 295 |
| 296 char* ptr = static_cast<char*>(start); |
| 297 char* end = ptr + length; |
| 298 MEMORY_BASIC_INFORMATION info; |
| 299 while (ptr < end) { |
| 300 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); |
| 301 assert(resultSize == sizeof(info)); |
| 302 size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr); |
| 303 BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT); |
| 304 assert(success == TRUE); |
| 305 ptr += decommitSize; |
| 306 } |
| 307 } |
| 308 |
| 309 void TCMalloc_SystemCommit(void* start, size_t length) { |
| 310 if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start) |
| 311 return; |
| 312 |
| 313 // The commit may fail if the memory region consists of allocations |
| 314 // from more than one call to VirtualAlloc. In this case, fall back to |
| 315 // using VirtualQuery to retrieve the allocation boundaries and commit them |
| 316 // each individually. |
| 317 |
| 318 char* ptr = static_cast<char*>(start); |
| 319 char* end = ptr + length; |
| 320 MEMORY_BASIC_INFORMATION info; |
| 321 while (ptr < end) { |
| 322 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); |
| 323 assert(resultSize == sizeof(info)); |
| 324 |
| 325 size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr); |
| 326 void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, |
| 327 PAGE_READWRITE); |
| 328 assert(newAddress == ptr); |
| 329 ptr += commitSize; |
| 330 } |
258 } | 331 } |
259 | 332 |
260 bool RegisterSystemAllocator(SysAllocator *allocator, int priority) { | 333 bool RegisterSystemAllocator(SysAllocator *allocator, int priority) { |
261 return false; // we don't allow registration on windows, right now | 334 return false; // we don't allow registration on windows, right now |
262 } | 335 } |
263 | 336 |
264 void DumpSystemAllocatorStats(TCMalloc_Printer* printer) { | 337 void DumpSystemAllocatorStats(TCMalloc_Printer* printer) { |
265 // We don't dump stats on windows, right now | 338 // We don't dump stats on windows, right now |
266 } | 339 } |
267 | 340 |
(...skipping 13 matching lines...) Expand all Loading... |
281 if ((strlen(fname) >= prefix_length) && | 354 if ((strlen(fname) >= prefix_length) && |
282 (memcmp(fname, prefix, prefix_length) == 0)) { | 355 (memcmp(fname, prefix, prefix_length) == 0)) { |
283 RAW_VLOG(0, "Removing old heap profile %s\n", fname); | 356 RAW_VLOG(0, "Removing old heap profile %s\n", fname); |
284 // TODO(csilvers): we really need to unlink dirname + fname | 357 // TODO(csilvers): we really need to unlink dirname + fname |
285 _unlink(fname); | 358 _unlink(fname); |
286 } | 359 } |
287 } while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi | 360 } while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi |
288 FindClose(hFind); | 361 FindClose(hFind); |
289 } | 362 } |
290 } | 363 } |
OLD | NEW |