| Index: third_party/tcmalloc/port.cc
|
| ===================================================================
|
| --- third_party/tcmalloc/port.cc (revision 0)
|
| +++ third_party/tcmalloc/port.cc (revision 0)
|
| @@ -0,0 +1,301 @@
|
| +/* Copyright (c) 2007, Google Inc.
|
| + * All rights reserved.
|
| + *
|
| + * Redistribution and use in source and binary forms, with or without
|
| + * modification, are permitted provided that the following conditions are
|
| + * met:
|
| + *
|
| + * * Redistributions of source code must retain the above copyright
|
| + * notice, this list of conditions and the following disclaimer.
|
| + * * Redistributions in binary form must reproduce the above
|
| + * copyright notice, this list of conditions and the following disclaimer
|
| + * in the documentation and/or other materials provided with the
|
| + * distribution.
|
| + * * Neither the name of Google Inc. nor the names of its
|
| + * contributors may be used to endorse or promote products derived from
|
| + * this software without specific prior written permission.
|
| + *
|
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| + *
|
| + * ---
|
| + * Author: Craig Silverstein
|
| + */
|
| +
|
| +#ifndef _WIN32
|
| +# error You should only be including windows/port.cc in a windows environment!
|
| +#endif
|
| +
|
| +#include <config.h>
|
| +#include <string.h> // for strlen(), memset(), memcmp()
|
| +#include <assert.h>
|
| +#include <stdarg.h> // for va_list, va_start, va_end
|
| +#include <windows.h>
|
| +#include "port.h"
|
| +#include "base/logging.h"
|
| +#include "base/spinlock.h"
|
| +#include "system-alloc.h"
|
| +
|
| +// -----------------------------------------------------------------------
|
| +// Basic libraries
|
| +
|
| +// These call the windows _vsnprintf, but always NUL-terminate.
|
| +int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
| + if (size == 0) // not even room for a \0?
|
| + return -1; // not what C99 says to do, but what windows does
|
| + str[size-1] = '\0';
|
| + return _vsnprintf(str, size-1, format, ap);
|
| +}
|
| +
|
| +#ifndef HAVE_SNPRINTF
|
| +int snprintf(char *str, size_t size, const char *format, ...) {
|
| + va_list ap;
|
| + va_start(ap, format);
|
| + const int r = vsnprintf(str, size, format, ap);
|
| + va_end(ap);
|
| + return r;
|
| +}
|
| +#endif
|
| +
|
| +int getpagesize() {
|
| + static int pagesize = 0;
|
| + if (pagesize == 0) {
|
| + SYSTEM_INFO system_info;
|
| + GetSystemInfo(&system_info);
|
| + pagesize = system_info.dwPageSize;
|
| + }
|
| + return pagesize;
|
| +}
|
| +
|
| +extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) {
|
| + LOG(FATAL, "Windows doesn't implement sbrk!\n");
|
| + return NULL;
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------
|
| +// Threads code
|
| +
|
| +bool CheckIfKernelSupportsTLS() {
|
| + // TODO(csilvers): return true (all win's since win95, at least, support this)
|
| + return false;
|
| +}
|
| +
|
| +// Windows doesn't support pthread_key_create's destr_function, and in
|
| +// fact it's a bit tricky to get code to run when a thread exits. This
|
| +// is cargo-cult magic from http://www.codeproject.com/threads/tls.asp.
|
| +// This code is for VC++ 7.1 and later; VC++ 6.0 support is possible
|
| +// but more busy-work -- see the webpage for how to do it. If all
|
| +// this fails, we could use DllMain instead. The big problem with
|
| +// DllMain is it doesn't run if this code is statically linked into a
|
| +// binary (it also doesn't run if the thread is terminated via
|
| +// TerminateThread, which if we're lucky this routine does).
|
| +
|
| +// This makes the linker create the TLS directory if it's not already
|
| +// there (that is, even if __declspec(thead) is not used).
|
| +#ifdef _MSC_VER
|
| +#pragma comment(linker, "/INCLUDE:__tls_used")
|
| +#endif
|
| +
|
| +// When destr_fn eventually runs, it's supposed to take as its
|
| +// argument the tls-value associated with key that pthread_key_create
|
| +// creates. (Yeah, it sounds confusing but it's really not.) We
|
| +// store the destr_fn/key pair in this data structure. Because we
|
| +// store this in a single var, this implies we can only have one
|
| +// destr_fn in a program! That's enough in practice. If asserts
|
| +// trigger because we end up needing more, we'll have to turn this
|
| +// into an array.
|
| +struct DestrFnClosure {
|
| + void (*destr_fn)(void*);
|
| + pthread_key_t key_for_destr_fn_arg;
|
| +};
|
| +
|
| +static DestrFnClosure destr_fn_info; // initted to all NULL/0.
|
| +
|
| +static int on_process_term(void) {
|
| + if (destr_fn_info.destr_fn) {
|
| + void *ptr = TlsGetValue(destr_fn_info.key_for_destr_fn_arg);
|
| + // This shouldn't be necessary, but in Release mode, Windows
|
| + // sometimes trashes the pointer in the TLS slot, so we need to
|
| + // remove the pointer from the TLS slot before the thread dies.
|
| + TlsSetValue(destr_fn_info.key_for_destr_fn_arg, NULL);
|
| + if (ptr) // pthread semantics say not to call if ptr is NULL
|
| + (*destr_fn_info.destr_fn)(ptr);
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) {
|
| + if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed!
|
| + on_process_term();
|
| + }
|
| +}
|
| +
|
| +#ifdef _MSC_VER
|
| +
|
| +// This tells the linker to run these functions.
|
| +#pragma data_seg(push, old_seg)
|
| +#pragma data_seg(".CRT$XLB")
|
| +static void (NTAPI *p_thread_callback)(HINSTANCE h, DWORD dwReason, PVOID pv)
|
| + = on_tls_callback;
|
| +#pragma data_seg(".CRT$XTU")
|
| +static int (*p_process_term)(void) = on_process_term;
|
| +#pragma data_seg(pop, old_seg)
|
| +
|
| +#else // #ifdef _MSC_VER [probably msys/mingw]
|
| +
|
| +// We have to try the DllMain solution here, because we can't use the
|
| +// msvc-specific pragmas.
|
| +BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) {
|
| + if (dwReason == DLL_THREAD_DETACH)
|
| + on_tls_callback(h, dwReason, pv);
|
| + else if (dwReason == DLL_PROCESS_DETACH)
|
| + on_process_term();
|
| + return TRUE;
|
| +}
|
| +
|
| +#endif // #ifdef _MSC_VER
|
| +
|
| +pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) {
|
| + // Semantics are: we create a new key, and then promise to call
|
| + // destr_fn with TlsGetValue(key) when the thread is destroyed
|
| + // (as long as TlsGetValue(key) is not NULL).
|
| + pthread_key_t key = TlsAlloc();
|
| + if (destr_fn) { // register it
|
| + // If this assert fails, we'll need to support an array of destr_fn_infos
|
| + assert(destr_fn_info.destr_fn == NULL);
|
| + destr_fn_info.destr_fn = destr_fn;
|
| + destr_fn_info.key_for_destr_fn_arg = key;
|
| + }
|
| + return key;
|
| +}
|
| +
|
| +
|
| +// -----------------------------------------------------------------------
|
| +// These functions replace system-alloc.cc
|
| +
|
| +static SpinLock alloc_lock(SpinLock::LINKER_INITIALIZED);
|
| +
|
| +// This is mostly like MmapSysAllocator::Alloc, except it does these weird
|
| +// munmap's in the middle of the page, which is forbidden in windows.
|
| +extern void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
|
| + size_t alignment) {
|
| + // Safest is to make actual_size same as input-size.
|
| + if (actual_size) {
|
| + *actual_size = size;
|
| + }
|
| +
|
| + SpinLockHolder sh(&alloc_lock);
|
| + // Align on the pagesize boundary
|
| + const int pagesize = getpagesize();
|
| + if (alignment < pagesize) alignment = pagesize;
|
| + size = ((size + alignment - 1) / alignment) * alignment;
|
| +
|
| + // Ask for extra memory if alignment > pagesize
|
| + size_t extra = 0;
|
| + if (alignment > pagesize) {
|
| + extra = alignment - pagesize;
|
| + }
|
| +
|
| + void* result = VirtualAlloc(0, size + extra,
|
| + MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
|
| + if (result == NULL)
|
| + return NULL;
|
| +
|
| + // Adjust the return memory so it is aligned
|
| + uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
|
| + size_t adjust = 0;
|
| + if ((ptr & (alignment - 1)) != 0) {
|
| + adjust = alignment - (ptr & (alignment - 1));
|
| + }
|
| +
|
| + ptr += adjust;
|
| + return reinterpret_cast<void*>(ptr);
|
| +}
|
| +
|
| +void TCMalloc_SystemRelease(void* start, size_t length) {
|
| + if (VirtualFree(start, length, MEM_DECOMMIT))
|
| + return;
|
| +
|
| + // The decommit may fail if the memory region consists of allocations
|
| + // from more than one call to VirtualAlloc. In this case, fall back to
|
| + // using VirtualQuery to retrieve the allocation boundaries and decommit
|
| + // them each individually.
|
| +
|
| + char* ptr = static_cast<char*>(start);
|
| + char* end = ptr + length;
|
| + MEMORY_BASIC_INFORMATION info;
|
| + while (ptr < end) {
|
| + size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
|
| + assert(resultSize == sizeof(info));
|
| + size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr);
|
| + BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
|
| + assert(success == TRUE);
|
| + ptr += decommitSize;
|
| + }
|
| +}
|
| +
|
| +void TCMalloc_SystemCommit(void* start, size_t length)
|
| +{
|
| + if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
|
| + return;
|
| +
|
| + // The commit may fail if the memory region consists of allocations
|
| + // from more than one call to VirtualAlloc. In this case, fall back to
|
| + // using VirtualQuery to retrieve the allocation boundaries and commit them
|
| + // each individually.
|
| +
|
| + char* ptr = static_cast<char*>(start);
|
| + char* end = ptr + length;
|
| + MEMORY_BASIC_INFORMATION info;
|
| + while (ptr < end) {
|
| + size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
|
| + assert(resultSize == sizeof(info));
|
| +
|
| + size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr);
|
| + void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT,
|
| + PAGE_READWRITE);
|
| + assert(newAddress == ptr);
|
| + ptr += commitSize;
|
| + }
|
| +}
|
| +
|
| +bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
|
| + return false; // we don't allow registration on windows, right now
|
| +}
|
| +
|
| +void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
|
| + // We don't dump stats on windows, right now
|
| +}
|
| +
|
| +
|
| +// -----------------------------------------------------------------------
|
| +// These functions rework existing functions of the same name in the
|
| +// Google codebase.
|
| +
|
| +// A replacement for HeapProfiler::CleanupOldProfiles.
|
| +void DeleteMatchingFiles(const char* prefix, const char* full_glob) {
|
| + WIN32_FIND_DATAA found; // that final A is for Ansi (as opposed to Unicode)
|
| + HANDLE hFind = FindFirstFileA(full_glob, &found); // A is for Ansi
|
| + if (hFind != INVALID_HANDLE_VALUE) {
|
| + const int prefix_length = strlen(prefix);
|
| + do {
|
| + const char *fname = found.cFileName;
|
| + if ((strlen(fname) >= prefix_length) &&
|
| + (memcmp(fname, prefix, prefix_length) == 0)) {
|
| + RAW_VLOG(0, "Removing old heap profile %s\n", fname);
|
| + // TODO(csilvers): we really need to unlink dirname + fname
|
| + _unlink(fname);
|
| + }
|
| + } while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi
|
| + FindClose(hFind);
|
| + }
|
| +}
|
|
|