OLD | NEW |
(Empty) | |
| 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. |
| 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: Craig Silverstein |
| 32 */ |
| 33 |
| 34 #ifndef _WIN32 |
| 35 # error You should only be including windows/port.cc in a windows environment! |
| 36 #endif |
| 37 |
| 38 #include <config.h> |
| 39 #include <string.h> // for strlen(), memset(), memcmp() |
| 40 #include <assert.h> |
| 41 #include <stdarg.h> // for va_list, va_start, va_end |
| 42 #include <windows.h> |
| 43 #include "port.h" |
| 44 #include "base/logging.h" |
| 45 #include "base/spinlock.h" |
| 46 #include "system-alloc.h" |
| 47 |
| 48 // ----------------------------------------------------------------------- |
| 49 // Basic libraries |
| 50 |
| 51 // These call the windows _vsnprintf, but always NUL-terminate. |
| 52 int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) { |
| 53 if (size == 0) // not even room for a \0? |
| 54 return -1; // not what C99 says to do, but what windows does |
| 55 str[size-1] = '\0'; |
| 56 return _vsnprintf(str, size-1, format, ap); |
| 57 } |
| 58 |
| 59 #ifndef HAVE_SNPRINTF |
| 60 int snprintf(char *str, size_t size, const char *format, ...) { |
| 61 va_list ap; |
| 62 va_start(ap, format); |
| 63 const int r = vsnprintf(str, size, format, ap); |
| 64 va_end(ap); |
| 65 return r; |
| 66 } |
| 67 #endif |
| 68 |
| 69 int getpagesize() { |
| 70 static int pagesize = 0; |
| 71 if (pagesize == 0) { |
| 72 SYSTEM_INFO system_info; |
| 73 GetSystemInfo(&system_info); |
| 74 pagesize = system_info.dwPageSize; |
| 75 } |
| 76 return pagesize; |
| 77 } |
| 78 |
| 79 extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) { |
| 80 LOG(FATAL, "Windows doesn't implement sbrk!\n"); |
| 81 return NULL; |
| 82 } |
| 83 |
| 84 // ----------------------------------------------------------------------- |
| 85 // Threads code |
| 86 |
| 87 bool CheckIfKernelSupportsTLS() { |
| 88 // TODO(csilvers): return true (all win's since win95, at least, support this) |
| 89 return false; |
| 90 } |
| 91 |
| 92 // Windows doesn't support pthread_key_create's destr_function, and in |
| 93 // fact it's a bit tricky to get code to run when a thread exits. This |
| 94 // is cargo-cult magic from http://www.codeproject.com/threads/tls.asp. |
| 95 // This code is for VC++ 7.1 and later; VC++ 6.0 support is possible |
| 96 // but more busy-work -- see the webpage for how to do it. If all |
| 97 // this fails, we could use DllMain instead. The big problem with |
| 98 // DllMain is it doesn't run if this code is statically linked into a |
| 99 // binary (it also doesn't run if the thread is terminated via |
| 100 // TerminateThread, which if we're lucky this routine does). |
| 101 |
| 102 // This makes the linker create the TLS directory if it's not already |
| 103 // there (that is, even if __declspec(thead) is not used). |
| 104 #ifdef _MSC_VER |
| 105 #pragma comment(linker, "/INCLUDE:__tls_used") |
| 106 #endif |
| 107 |
| 108 // When destr_fn eventually runs, it's supposed to take as its |
| 109 // argument the tls-value associated with key that pthread_key_create |
| 110 // creates. (Yeah, it sounds confusing but it's really not.) We |
| 111 // store the destr_fn/key pair in this data structure. Because we |
| 112 // store this in a single var, this implies we can only have one |
| 113 // destr_fn in a program! That's enough in practice. If asserts |
| 114 // trigger because we end up needing more, we'll have to turn this |
| 115 // into an array. |
| 116 struct DestrFnClosure { |
| 117 void (*destr_fn)(void*); |
| 118 pthread_key_t key_for_destr_fn_arg; |
| 119 }; |
| 120 |
| 121 static DestrFnClosure destr_fn_info; // initted to all NULL/0. |
| 122 |
| 123 static int on_process_term(void) { |
| 124 if (destr_fn_info.destr_fn) { |
| 125 void *ptr = TlsGetValue(destr_fn_info.key_for_destr_fn_arg); |
| 126 // This shouldn't be necessary, but in Release mode, Windows |
| 127 // sometimes trashes the pointer in the TLS slot, so we need to |
| 128 // remove the pointer from the TLS slot before the thread dies. |
| 129 TlsSetValue(destr_fn_info.key_for_destr_fn_arg, NULL); |
| 130 if (ptr) // pthread semantics say not to call if ptr is NULL |
| 131 (*destr_fn_info.destr_fn)(ptr); |
| 132 } |
| 133 return 0; |
| 134 } |
| 135 |
| 136 static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) { |
| 137 if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed! |
| 138 on_process_term(); |
| 139 } |
| 140 } |
| 141 |
| 142 #ifdef _MSC_VER |
| 143 |
| 144 // This tells the linker to run these functions. |
| 145 #pragma data_seg(push, old_seg) |
| 146 #pragma data_seg(".CRT$XLB") |
| 147 static void (NTAPI *p_thread_callback)(HINSTANCE h, DWORD dwReason, PVOID pv) |
| 148 = on_tls_callback; |
| 149 #pragma data_seg(".CRT$XTU") |
| 150 static int (*p_process_term)(void) = on_process_term; |
| 151 #pragma data_seg(pop, old_seg) |
| 152 |
| 153 #else // #ifdef _MSC_VER [probably msys/mingw] |
| 154 |
| 155 // We have to try the DllMain solution here, because we can't use the |
| 156 // msvc-specific pragmas. |
| 157 BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) { |
| 158 if (dwReason == DLL_THREAD_DETACH) |
| 159 on_tls_callback(h, dwReason, pv); |
| 160 else if (dwReason == DLL_PROCESS_DETACH) |
| 161 on_process_term(); |
| 162 return TRUE; |
| 163 } |
| 164 |
| 165 #endif // #ifdef _MSC_VER |
| 166 |
| 167 pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) { |
| 168 // Semantics are: we create a new key, and then promise to call |
| 169 // destr_fn with TlsGetValue(key) when the thread is destroyed |
| 170 // (as long as TlsGetValue(key) is not NULL). |
| 171 pthread_key_t key = TlsAlloc(); |
| 172 if (destr_fn) { // register it |
| 173 // If this assert fails, we'll need to support an array of destr_fn_infos |
| 174 assert(destr_fn_info.destr_fn == NULL); |
| 175 destr_fn_info.destr_fn = destr_fn; |
| 176 destr_fn_info.key_for_destr_fn_arg = key; |
| 177 } |
| 178 return key; |
| 179 } |
| 180 |
| 181 |
| 182 // ----------------------------------------------------------------------- |
| 183 // These functions replace system-alloc.cc |
| 184 |
| 185 static SpinLock alloc_lock(SpinLock::LINKER_INITIALIZED); |
| 186 |
| 187 // This is mostly like MmapSysAllocator::Alloc, except it does these weird |
| 188 // munmap's in the middle of the page, which is forbidden in windows. |
| 189 extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, |
| 190 size_t alignment) { |
| 191 // Safest is to make actual_size same as input-size. |
| 192 if (actual_size) { |
| 193 *actual_size = size; |
| 194 } |
| 195 |
| 196 SpinLockHolder sh(&alloc_lock); |
| 197 // Align on the pagesize boundary |
| 198 const int pagesize = getpagesize(); |
| 199 if (alignment < pagesize) alignment = pagesize; |
| 200 size = ((size + alignment - 1) / alignment) * alignment; |
| 201 |
| 202 // Ask for extra memory if alignment > pagesize |
| 203 size_t extra = 0; |
| 204 if (alignment > pagesize) { |
| 205 extra = alignment - pagesize; |
| 206 } |
| 207 |
| 208 void* result = VirtualAlloc(0, size + extra, |
| 209 MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); |
| 210 if (result == NULL) |
| 211 return NULL; |
| 212 |
| 213 // Adjust the return memory so it is aligned |
| 214 uintptr_t ptr = reinterpret_cast<uintptr_t>(result); |
| 215 size_t adjust = 0; |
| 216 if ((ptr & (alignment - 1)) != 0) { |
| 217 adjust = alignment - (ptr & (alignment - 1)); |
| 218 } |
| 219 |
| 220 ptr += adjust; |
| 221 return reinterpret_cast<void*>(ptr); |
| 222 } |
| 223 |
| 224 void TCMalloc_SystemRelease(void* start, size_t length) { |
| 225 if (VirtualFree(start, length, MEM_DECOMMIT)) |
| 226 return; |
| 227 |
| 228 // The decommit may fail if the memory region consists of allocations |
| 229 // from more than one call to VirtualAlloc. In this case, fall back to |
| 230 // using VirtualQuery to retrieve the allocation boundaries and decommit |
| 231 // them each individually. |
| 232 |
| 233 char* ptr = static_cast<char*>(start); |
| 234 char* end = ptr + length; |
| 235 MEMORY_BASIC_INFORMATION info; |
| 236 while (ptr < end) { |
| 237 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); |
| 238 assert(resultSize == sizeof(info)); |
| 239 size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr); |
| 240 BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT); |
| 241 assert(success == TRUE); |
| 242 ptr += decommitSize; |
| 243 } |
| 244 } |
| 245 |
| 246 void TCMalloc_SystemCommit(void* start, size_t length) |
| 247 { |
| 248 if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start) |
| 249 return; |
| 250 |
| 251 // The commit may fail if the memory region consists of allocations |
| 252 // from more than one call to VirtualAlloc. In this case, fall back to |
| 253 // using VirtualQuery to retrieve the allocation boundaries and commit them |
| 254 // each individually. |
| 255 |
| 256 char* ptr = static_cast<char*>(start); |
| 257 char* end = ptr + length; |
| 258 MEMORY_BASIC_INFORMATION info; |
| 259 while (ptr < end) { |
| 260 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); |
| 261 assert(resultSize == sizeof(info)); |
| 262 |
| 263 size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr); |
| 264 void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, |
| 265 PAGE_READWRITE); |
| 266 assert(newAddress == ptr); |
| 267 ptr += commitSize; |
| 268 } |
| 269 } |
| 270 |
| 271 bool RegisterSystemAllocator(SysAllocator *allocator, int priority) { |
| 272 return false; // we don't allow registration on windows, right now |
| 273 } |
| 274 |
| 275 void DumpSystemAllocatorStats(TCMalloc_Printer* printer) { |
| 276 // We don't dump stats on windows, right now |
| 277 } |
| 278 |
| 279 |
| 280 // ----------------------------------------------------------------------- |
| 281 // These functions rework existing functions of the same name in the |
| 282 // Google codebase. |
| 283 |
| 284 // A replacement for HeapProfiler::CleanupOldProfiles. |
| 285 void DeleteMatchingFiles(const char* prefix, const char* full_glob) { |
| 286 WIN32_FIND_DATAA found; // that final A is for Ansi (as opposed to Unicode) |
| 287 HANDLE hFind = FindFirstFileA(full_glob, &found); // A is for Ansi |
| 288 if (hFind != INVALID_HANDLE_VALUE) { |
| 289 const int prefix_length = strlen(prefix); |
| 290 do { |
| 291 const char *fname = found.cFileName; |
| 292 if ((strlen(fname) >= prefix_length) && |
| 293 (memcmp(fname, prefix, prefix_length) == 0)) { |
| 294 RAW_VLOG(0, "Removing old heap profile %s\n", fname); |
| 295 // TODO(csilvers): we really need to unlink dirname + fname |
| 296 _unlink(fname); |
| 297 } |
| 298 } while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi |
| 299 FindClose(hFind); |
| 300 } |
| 301 } |
OLD | NEW |