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
53int 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
64extern "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.
73extern "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
85bool 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.
124struct DestrFnClosure {
125  void (*destr_fn)(void*);
126  pthread_key_t key_for_destr_fn_arg;
127};
128
129static DestrFnClosure destr_fn_info;   // initted to all NULL/0.
130
131static 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
144static 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.
154extern "C" {
155// This tells the linker to run these functions.
156#pragma data_seg(push, old_seg)
157#pragma data_seg(".CRT$XLB")
158void (NTAPI *p_thread_callback_tcmalloc)(
159    HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback;
160#pragma data_seg(".CRT$XTU")
161int (*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.
169BOOL 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
179extern "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...
194extern "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.
223extern 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
257void TCMalloc_SystemRelease(void* start, size_t length) {
258  // TODO(csilvers): should I be calling VirtualFree here?
259}
260
261bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
262  return false;   // we don't allow registration on windows, right now
263}
264
265void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
266  // We don't dump stats on windows, right now
267}
268
269// The current system allocator
270SysAllocator* 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.
278void 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