blob: ea1f17d95d723f6c7f00f5a0eadc39aa0f5d723a [file] [log] [blame]
// Copyright (c) 2013, 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: Petr Hosek
#ifndef _WIN32
# error You should only be including windows/system-alloc.cc in a windows environment!
#endif
#include <config.h>
#include <windows.h>
#include <algorithm> // std::min
#include <gperftools/malloc_extension.h>
#include "base/logging.h"
#include "base/spinlock.h"
#include "internal_logging.h"
#include "system-alloc.h"
static SpinLock spinlock(SpinLock::LINKER_INITIALIZED);
// The current system allocator declaration
SysAllocator* tcmalloc_sys_alloc = NULL;
// Number of bytes taken from system.
size_t TCMalloc_SystemTaken = 0;
class VirtualSysAllocator : public SysAllocator {
public:
VirtualSysAllocator() : SysAllocator() {
}
void* Alloc(size_t size, size_t *actual_size, size_t alignment);
};
static char virtual_space[sizeof(VirtualSysAllocator)];
// This is mostly like MmapSysAllocator::Alloc, except it does these weird
// munmap's in the middle of the page, which is forbidden in windows.
void* VirtualSysAllocator::Alloc(size_t size, size_t *actual_size,
size_t alignment) {
// Align on the pagesize boundary
const int pagesize = getpagesize();
if (alignment < pagesize) alignment = pagesize;
size = ((size + alignment - 1) / alignment) * alignment;
// Report the total number of bytes the OS actually delivered. This might be
// greater than |size| because of alignment concerns. The full size is
// necessary so that adjacent spans can be coalesced.
// TODO(antonm): proper processing of alignments
// in actual_size and decommitting.
if (actual_size) {
*actual_size = size;
}
// We currently do not support alignments larger than the pagesize or
// alignments that are not multiples of the pagesize after being floored.
// If this ability is needed it can be done by the caller (assuming it knows
// the page size).
assert(alignment <= pagesize);
void* result = VirtualAlloc(0, size,
MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (result == NULL)
return NULL;
// If the result is not aligned memory fragmentation will result which can
// lead to pathological memory use.
assert((reinterpret_cast<uintptr_t>(result) & (alignment - 1)) == 0);
return result;
}
#ifdef _MSC_VER
extern "C" SysAllocator* tc_get_sysalloc_override(SysAllocator *def);
extern "C" SysAllocator* tc_get_sysalloc_default(SysAllocator *def)
{
return def;
}
#if defined(_M_IX86)
#pragma comment(linker, "/alternatename:_tc_get_sysalloc_override=_tc_get_sysalloc_default")
#elif defined(_M_X64)
#pragma comment(linker, "/alternatename:tc_get_sysalloc_override=tc_get_sysalloc_default")
#endif
#else // !_MSC_VER
extern "C" ATTRIBUTE_NOINLINE
SysAllocator* tc_get_sysalloc_override(SysAllocator *def)
{
return def;
}
#endif
static bool system_alloc_inited = false;
void InitSystemAllocators(void) {
VirtualSysAllocator *alloc = new (virtual_space) VirtualSysAllocator();
tcmalloc_sys_alloc = tc_get_sysalloc_override(alloc);
}
extern PERFTOOLS_DLL_DECL
void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
size_t alignment) {
SpinLockHolder lock_holder(&spinlock);
if (!system_alloc_inited) {
InitSystemAllocators();
system_alloc_inited = true;
}
void* result = tcmalloc_sys_alloc->Alloc(size, actual_size, alignment);
if (result != NULL) {
if (actual_size) {
TCMalloc_SystemTaken += *actual_size;
} else {
TCMalloc_SystemTaken += size;
}
}
return result;
}
extern PERFTOOLS_DLL_DECL
bool TCMalloc_SystemRelease(void* start, size_t length) {
if (VirtualFree(start, length, MEM_DECOMMIT))
return true;
// 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;
}
return true;
}
extern PERFTOOLS_DLL_DECL
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
}