logging: add a FileHandlerFactory class
[folly.git] / folly / portability / SysMman.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 8420c82..af76d8b
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <folly/portability/SysMman.h>
 
 #ifdef _WIN32
+
+#include <cassert>
+
+#include <folly/Portability.h>
 #include <folly/portability/Windows.h>
 
-static bool mmap_to_page_protection(int prot, DWORD& ret) {
+static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) {
   if (prot == PROT_NONE) {
     ret = PAGE_NOACCESS;
+    acc = 0;
   } else if (prot == PROT_READ) {
     ret = PAGE_READONLY;
+    acc = FILE_MAP_READ;
   } else if (prot == PROT_EXEC) {
     ret = PAGE_EXECUTE;
+    acc = FILE_MAP_EXECUTE;
   } else if (prot == (PROT_READ | PROT_EXEC)) {
     ret = PAGE_EXECUTE_READ;
+    acc = FILE_MAP_READ | FILE_MAP_EXECUTE;
   } else if (prot == (PROT_READ | PROT_WRITE)) {
     ret = PAGE_READWRITE;
+    acc = FILE_MAP_READ | FILE_MAP_WRITE;
   } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
     ret = PAGE_EXECUTE_READWRITE;
+    acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE;
   } else {
     return false;
   }
   return true;
 }
 
+static size_t alignToAllocationGranularity(size_t s) {
+  static size_t granularity = [] {
+    static SYSTEM_INFO inf;
+    GetSystemInfo(&inf);
+    return inf.dwAllocationGranularity;
+  }();
+  return (s + granularity - 1) / granularity * granularity;
+}
+
 extern "C" {
-int madvise(const void* addr, size_t len, int advise) {
+int madvise(const void* /* addr */, size_t /* len */, int /* advise */) {
   // We do nothing at all.
   // Could probably implement dontneed via VirtualAlloc
   // with the MEM_RESET and MEM_RESET_UNDO flags.
@@ -47,12 +66,27 @@ int madvise(const void* addr, size_t len, int advise) {
 }
 
 int mlock(const void* addr, size_t len) {
+  // For some strange reason, it's allowed to
+  // lock a nullptr as long as length is zero.
+  // VirtualLock doesn't allow it, so handle
+  // it specially.
+  if (addr == nullptr && len == 0) {
+    return 0;
+  }
   if (!VirtualLock((void*)addr, len)) {
     return -1;
   }
   return 0;
 }
 
+namespace {
+constexpr uint32_t kMMapLengthMagic = 0xFACEB00C;
+struct MemMapDebugTrailer {
+  size_t length;
+  uint32_t magic;
+};
+}
+
 void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
   // Make sure it's something we support first.
 
@@ -70,7 +104,8 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
   }
 
   DWORD newProt;
-  if (!mmap_to_page_protection(prot, newProt)) {
+  DWORD accessFlags;
+  if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
     return MAP_FAILED;
   }
 
@@ -84,14 +119,17 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
     HANDLE fmh = CreateFileMapping(
         h,
         nullptr,
-        newProt | SEC_COMMIT | SEC_RESERVE,
+        newProt,
         (DWORD)((length >> 32) & 0xFFFFFFFF),
         (DWORD)(length & 0xFFFFFFFF),
         nullptr);
+    if (fmh == nullptr) {
+      return MAP_FAILED;
+    }
     ret = MapViewOfFileEx(
         fmh,
-        FILE_MAP_ALL_ACCESS,
-        (DWORD)((off >> 32) & 0xFFFFFFFF),
+        accessFlags,
+        (DWORD)(0), // off_t is only 32-bit :(
         (DWORD)(off & 0xFFFFFFFF),
         0,
         addr);
@@ -100,10 +138,27 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
     }
     CloseHandle(fmh);
   } else {
+    auto baseLength = length;
+    if (folly::kIsDebug) {
+      // In debug mode we keep track of the length to make
+      // sure you're only munmapping the entire thing if
+      // we're using VirtualAlloc.
+      length += sizeof(MemMapDebugTrailer);
+    }
+
+    // VirtualAlloc rounds size down to a multiple
+    // of the system allocation granularity :(
+    length = alignToAllocationGranularity(length);
     ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt);
     if (ret == nullptr) {
       return MAP_FAILED;
     }
+
+    if (folly::kIsDebug) {
+      auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength);
+      deb->length = baseLength;
+      deb->magic = kMMapLengthMagic;
+    }
   }
 
   // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory
@@ -113,7 +168,8 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
 
 int mprotect(void* addr, size_t size, int prot) {
   DWORD newProt;
-  if (!mmap_to_page_protection(prot, newProt)) {
+  DWORD access;
+  if (!mmap_to_page_protection(prot, newProt, access)) {
     return -1;
   }
 
@@ -126,6 +182,10 @@ int mprotect(void* addr, size_t size, int prot) {
 }
 
 int munlock(const void* addr, size_t length) {
+  // See comment in mlock
+  if (addr == nullptr && length == 0) {
+    return 0;
+  }
   if (!VirtualUnlock((void*)addr, length)) {
     return -1;
   }
@@ -135,7 +195,19 @@ int munlock(const void* addr, size_t length) {
 int munmap(void* addr, size_t length) {
   // Try to unmap it as a file, otherwise VirtualFree.
   if (!UnmapViewOfFile(addr)) {
-    if (!VirtualFree(addr, length, MEM_RELEASE)) {
+    if (folly::kIsDebug) {
+      // We can't do partial unmapping with Windows, so
+      // assert that we aren't trying to do that if we're
+      // in debug mode.
+      MEMORY_BASIC_INFORMATION inf;
+      VirtualQuery(addr, &inf, sizeof(inf));
+      assert(inf.AllocationBase == addr);
+
+      auto deb = (MemMapDebugTrailer*)((char*)addr + length);
+      assert(deb->length == length);
+      assert(deb->magic == kMMapLengthMagic);
+    }
+    if (!VirtualFree(addr, 0, MEM_RELEASE)) {
       return -1;
     }
     return 0;
@@ -143,4 +215,5 @@ int munmap(void* addr, size_t length) {
   return 0;
 }
 }
+
 #endif