• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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 "port.h"
45 #include "base/logging.h"
46 #include "base/spinlock.h"
47 #include "internal_logging.h"
48 #include "system-alloc.h"
49 
50 // -----------------------------------------------------------------------
51 // Basic libraries
52 
getpagesize()53 int getpagesize() {
54   static int pagesize = 0;
55   if (pagesize == 0) {
56     SYSTEM_INFO system_info;
57     GetSystemInfo(&system_info);
58     pagesize = std::max(system_info.dwPageSize,
59                         system_info.dwAllocationGranularity);
60   }
61   return pagesize;
62 }
63 
__sbrk(ptrdiff_t increment)64 extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) {
65   LOG(FATAL, "Windows doesn't implement sbrk!\n");
66   return NULL;
67 }
68 
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.
WriteToStderr(const char * buf,int len)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));
77   }
78 }
79 
80 
81 // -----------------------------------------------------------------------
82 // Threads code
83 
84 // Declared (not extern "C") in thread_cache.h
CheckIfKernelSupportsTLS()85 bool CheckIfKernelSupportsTLS() {
86   // TODO(csilvers): return true (all win's since win95, at least, support this)
87   return false;
88 }
89 
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).
99 
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.
104 #ifdef _MSC_VER
105 #if defined(_M_IX86)
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")
113 #endif
114 #endif
115 
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
123 // into an array.
124 struct DestrFnClosure {
125   void (*destr_fn)(void*);
126   pthread_key_t key_for_destr_fn_arg;
127 };
128 
129 static DestrFnClosure destr_fn_info;   // initted to all NULL/0.
130 
on_process_term(void)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);
140   }
141   return 0;
142 }
143 
on_tls_callback(HINSTANCE h,DWORD dwReason,PVOID pv)144 static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) {
145   if (dwReason == DLL_THREAD_DETACH) {   // thread is being destroyed!
146     on_process_term();
147   }
148 }
149 
150 #ifdef _MSC_VER
151 
152 // extern "C" suppresses C++ name mangling so we know the symbol names
153 // for the linker /INCLUDE:symbol pragmas above.
154 extern "C" {
155 // This tells the linker to run these functions.
156 #pragma data_seg(push, old_seg)
157 #pragma data_seg(".CRT$XLB")
158 void (NTAPI *p_thread_callback_tcmalloc)(
159     HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback;
160 #pragma data_seg(".CRT$XTU")
161 int (*p_process_term_tcmalloc)(void) = on_process_term;
162 #pragma data_seg(pop, old_seg)
163 }  // extern "C"
164 
165 #else  // #ifdef _MSC_VER  [probably msys/mingw]
166 
167 // We have to try the DllMain solution here, because we can't use the
168 // msvc-specific pragmas.
DllMain(HINSTANCE h,DWORD dwReason,PVOID pv)169 BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) {
170   if (dwReason == DLL_THREAD_DETACH)
171     on_tls_callback(h, dwReason, pv);
172   else if (dwReason == DLL_PROCESS_DETACH)
173     on_process_term();
174   return TRUE;
175 }
176 
177 #endif  // #ifdef _MSC_VER
178 
PthreadKeyCreate(void (* destr_fn)(void *))179 extern "C" pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) {
180   // Semantics are: we create a new key, and then promise to call
181   // destr_fn with TlsGetValue(key) when the thread is destroyed
182   // (as long as TlsGetValue(key) is not NULL).
183   pthread_key_t key = TlsAlloc();
184   if (destr_fn) {   // register it
185     // If this assert fails, we'll need to support an array of destr_fn_infos
186     assert(destr_fn_info.destr_fn == NULL);
187     destr_fn_info.destr_fn = destr_fn;
188     destr_fn_info.key_for_destr_fn_arg = key;
189   }
190   return key;
191 }
192 
193 // NOTE: this is Win2K and later.  For Win98 we could use a CRITICAL_SECTION...
perftools_pthread_once(pthread_once_t * once_control,void (* init_routine)(void))194 extern "C" int perftools_pthread_once(pthread_once_t *once_control,
195                                       void (*init_routine)(void)) {
196   // Try for a fast path first. Note: this should be an acquire semantics read.
197   // It is on x86 and x64, where Windows runs.
198   if (*once_control != 1) {
199     while (true) {
200       switch (InterlockedCompareExchange(once_control, 2, 0)) {
201         case 0:
202           init_routine();
203           InterlockedExchange(once_control, 1);
204           return 0;
205         case 1:
206           // The initializer has already been executed
207           return 0;
208         default:
209           // The initializer is being processed by another thread
210           SwitchToThread();
211       }
212     }
213   }
214   return 0;
215 }
216 
217 
218 // -----------------------------------------------------------------------
219 // These functions replace system-alloc.cc
220 
221 // This is mostly like MmapSysAllocator::Alloc, except it does these weird
222 // munmap's in the middle of the page, which is forbidden in windows.
TCMalloc_SystemAlloc(size_t size,size_t * actual_size,size_t alignment)223 extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
224                                   size_t alignment) {
225   // Align on the pagesize boundary
226   const int pagesize = getpagesize();
227   if (alignment < pagesize) alignment = pagesize;
228   size = ((size + alignment - 1) / alignment) * alignment;
229 
230   // Safest is to make actual_size same as input-size.
231   if (actual_size) {
232     *actual_size = size;
233   }
234 
235   // Ask for extra memory if alignment > pagesize
236   size_t extra = 0;
237   if (alignment > pagesize) {
238     extra = alignment - pagesize;
239   }
240 
241   void* result = VirtualAlloc(0, size + extra,
242                               MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
243   if (result == NULL)
244     return NULL;
245 
246   // Adjust the return memory so it is aligned
247   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
248   size_t adjust = 0;
249   if ((ptr & (alignment - 1)) != 0) {
250     adjust = alignment - (ptr & (alignment - 1));
251   }
252 
253   ptr += adjust;
254   return reinterpret_cast<void*>(ptr);
255 }
256 
TCMalloc_SystemRelease(void * start,size_t length)257 void TCMalloc_SystemRelease(void* start, size_t length) {
258   // TODO(csilvers): should I be calling VirtualFree here?
259 }
260 
RegisterSystemAllocator(SysAllocator * allocator,int priority)261 bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
262   return false;   // we don't allow registration on windows, right now
263 }
264 
DumpSystemAllocatorStats(TCMalloc_Printer * printer)265 void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
266   // We don't dump stats on windows, right now
267 }
268 
269 // The current system allocator
270 SysAllocator* sys_alloc = NULL;
271 
272 
273 // -----------------------------------------------------------------------
274 // These functions rework existing functions of the same name in the
275 // Google codebase.
276 
277 // A replacement for HeapProfiler::CleanupOldProfiles.
DeleteMatchingFiles(const char * prefix,const char * full_glob)278 void DeleteMatchingFiles(const char* prefix, const char* full_glob) {
279   WIN32_FIND_DATAA found;  // that final A is for Ansi (as opposed to Unicode)
280   HANDLE hFind = FindFirstFileA(full_glob, &found);   // A is for Ansi
281   if (hFind != INVALID_HANDLE_VALUE) {
282     const int prefix_length = strlen(prefix);
283     do {
284       const char *fname = found.cFileName;
285       if ((strlen(fname) >= prefix_length) &&
286           (memcmp(fname, prefix, prefix_length) == 0)) {
287         RAW_VLOG(0, "Removing old heap profile %s\n", fname);
288         // TODO(csilvers): we really need to unlink dirname + fname
289         _unlink(fname);
290       }
291     } while (FindNextFileA(hFind, &found) != FALSE);  // A is for Ansi
292     FindClose(hFind);
293   }
294 }
295