| // 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* 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(); |
| 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 = 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 |
| } |