#include <sys/mman.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <emmintrin.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#ifdef _WIN32
#include <memoryapi.h>
#endif
#include "libpmem.h"
#include "pmem.h"
#include "cpu.h"
#include "out.h"
#include "util.h"
#include "os.h"
#include "mmap.h"
#include "file.h"
#include "valgrind_internal.h"
#ifndef _MSC_VER
#define _mm_clflushopt(addr)\
asm volatile(".byte 0x66; clflush %0" : "+m" (*(volatile char *)addr));
#define _mm_clwb(addr)\
asm volatile(".byte 0x66; xsaveopt %0" : "+m" (*(volatile char *)addr));
#endif
#define FLUSH_ALIGN ((uintptr_t)64)
#define ALIGN_MASK (FLUSH_ALIGN - 1)
#define CHUNK_SIZE 128
#define CHUNK_SHIFT 7
#define CHUNK_MASK (CHUNK_SIZE - 1)
#define DWORD_SIZE 4
#define DWORD_SHIFT 2
#define DWORD_MASK (DWORD_SIZE - 1)
#define MOVNT_SIZE 16
#define MOVNT_MASK (MOVNT_SIZE - 1)
#define MOVNT_SHIFT 4
#define MOVNT_THRESHOLD 256
static size_t Movnt_threshold = MOVNT_THRESHOLD;
int
pmem_has_hw_drain(void)
{
LOG(3, NULL);
return 0;
}
static void
predrain_fence_empty(void)
{
LOG(15, NULL);
VALGRIND_DO_FENCE;
}
static void
predrain_fence_sfence(void)
{
LOG(15, NULL);
_mm_sfence();
}
static void (*Func_predrain_fence)(void) = predrain_fence_empty;
void
pmem_drain(void)
{
LOG(15, NULL);
Func_predrain_fence();
VALGRIND_DO_COMMIT;
VALGRIND_DO_FENCE;
}
static void
flush_clflush(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
uintptr_t uptr;
for (uptr = (uintptr_t)addr & ~(FLUSH_ALIGN - 1);
uptr < (uintptr_t)addr + len; uptr += FLUSH_ALIGN)
_mm_clflush((char *)uptr);
}
static void
flush_clwb(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
uintptr_t uptr;
for (uptr = (uintptr_t)addr & ~(FLUSH_ALIGN - 1);
uptr < (uintptr_t)addr + len; uptr += FLUSH_ALIGN) {
_mm_clwb((char *)uptr);
}
}
static void
flush_clflushopt(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
uintptr_t uptr;
for (uptr = (uintptr_t)addr & ~(FLUSH_ALIGN - 1);
uptr < (uintptr_t)addr + len; uptr += FLUSH_ALIGN) {
_mm_clflushopt((char *)uptr);
}
}
static void
flush_empty(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
}
static void (*Func_flush)(const void *, size_t) = flush_clflush;
void
pmem_flush(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
VALGRIND_DO_CHECK_MEM_IS_ADDRESSABLE(addr, len);
Func_flush(addr, len);
}
void
pmem_persist(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
pmem_flush(addr, len);
pmem_drain();
}
int
pmem_msync(const void *addr, size_t len)
{
LOG(15, "addr %p len %zu", addr, len);
VALGRIND_DO_CHECK_MEM_IS_ADDRESSABLE(addr, len);
len += (uintptr_t)addr & (Pagesize - 1);
uintptr_t uptr = (uintptr_t)addr & ~((uintptr_t)Pagesize - 1);
VALGRIND_DO_DISABLE_ERROR_REPORTING;
int ret;
if ((ret = msync((void *)uptr, len, MS_SYNC)) < 0)
ERR("!msync");
VALGRIND_DO_ENABLE_ERROR_REPORTING;
VALGRIND_DO_PERSIST(uptr, len);
return ret;
}
static int
is_pmem_always(const void *addr, size_t len)
{
LOG(3, "addr %p len %zu", addr, len);
return 1;
}
static int
is_pmem_never(const void *addr, size_t len)
{
LOG(3, "addr %p len %zu", addr, len);
return 0;
}
static int (*Func_is_pmem)(const void *addr, size_t len) = is_pmem_never;
static void
pmem_is_pmem_init(void)
{
LOG(3, NULL);
static volatile unsigned init;
while (init != 2) {
if (!util_bool_compare_and_swap32(&init, 0, 1))
continue;
char *ptr = os_getenv("PMEM_IS_PMEM_FORCE");
if (ptr) {
int val = atoi(ptr);
if (val == 0)
Func_is_pmem = is_pmem_never;
else if (val == 1)
Func_is_pmem = is_pmem_always;
VALGRIND_ANNOTATE_HAPPENS_BEFORE(&Func_is_pmem);
LOG(4, "PMEM_IS_PMEM_FORCE=%d", val);
}
if (!util_bool_compare_and_swap32(&init, 1, 2))
FATAL("util_bool_compare_and_swap32");
}
}
int
pmem_is_pmem(const void *addr, size_t len)
{
LOG(10, "addr %p len %zu", addr, len);
static int once;
if (once == 0) {
pmem_is_pmem_init();
util_fetch_and_add32(&once, 1);
}
VALGRIND_ANNOTATE_HAPPENS_AFTER(&Func_is_pmem);
return Func_is_pmem(addr, len);
}
#define PMEM_FILE_ALL_FLAGS\
(PMEM_FILE_CREATE|PMEM_FILE_EXCL|PMEM_FILE_SPARSE|PMEM_FILE_TMPFILE)
#define PMEM_DAX_VALID_FLAGS\
(PMEM_FILE_CREATE|PMEM_FILE_SPARSE)
#ifndef _WIN32
static inline
#endif
void *
pmem_map_fileU(const char *path, size_t len, int flags,
mode_t mode, size_t *mapped_lenp, int *is_pmemp)
{
LOG(3, "path \"%s\" size %zu flags %x mode %o mapped_lenp %p "
"is_pmemp %p", path, len, flags, mode, mapped_lenp, is_pmemp);
int oerrno;
int fd;
int open_flags = O_RDWR;
int delete_on_err = 0;
int is_dev_dax = util_file_is_device_dax(path);
if (flags & ~(PMEM_FILE_ALL_FLAGS)) {
ERR("invalid flag specified %x", flags);
errno = EINVAL;
return NULL;
}
if (is_dev_dax) {
if (flags & ~(PMEM_DAX_VALID_FLAGS)) {
ERR("flag unsupported for Device DAX %x", flags);
errno = EINVAL;
return NULL;
} else {
flags = 0;
ssize_t actual_len = util_file_get_size(path);
if (actual_len < 0) {
ERR("unable to read Device DAX size");
errno = EINVAL;
return NULL;
}
if (len != 0 && len != (size_t)actual_len) {
ERR("Device DAX length must be either 0 or "
"the exact size of the device %zu",
len);
errno = EINVAL;
return NULL;
}
len = 0;
}
}
if (flags & PMEM_FILE_CREATE) {
if ((os_off_t)len < 0) {
ERR("invalid file length %zu", len);
errno = EINVAL;
return NULL;
}
open_flags |= O_CREAT;
}
if (flags & PMEM_FILE_EXCL)
open_flags |= O_EXCL;
if ((len != 0) && !(flags & PMEM_FILE_CREATE)) {
ERR("non-zero 'len' not allowed without PMEM_FILE_CREATE");
errno = EINVAL;
return NULL;
}
if ((len == 0) && (flags & PMEM_FILE_CREATE)) {
ERR("zero 'len' not allowed with PMEM_FILE_CREATE");
errno = EINVAL;
return NULL;
}
if ((flags & PMEM_FILE_TMPFILE) && !(flags & PMEM_FILE_CREATE)) {
ERR("PMEM_FILE_TMPFILE not allowed without PMEM_FILE_CREATE");
errno = EINVAL;
return NULL;
}
if (flags & PMEM_FILE_TMPFILE) {
if ((fd = util_tmpfile(path,
OS_DIR_SEP_STR"pmem.XXXXXX",
open_flags & O_EXCL)) < 0) {
LOG(2, "failed to create temporary file at \"%s\"",
path);
return NULL;
}
} else {
if ((fd = os_open(path, open_flags, mode)) < 0) {
ERR("!open %s", path);
return NULL;
}
if ((flags & PMEM_FILE_CREATE) && (flags & PMEM_FILE_EXCL))
delete_on_err = 1;
}
if (flags & PMEM_FILE_CREATE) {
if (os_ftruncate(fd, (os_off_t)len) != 0) {
ERR("!ftruncate");
goto err;
}
if ((flags & PMEM_FILE_SPARSE) == 0) {
if ((errno = os_posix_fallocate(fd, 0,
(os_off_t)len)) != 0) {
ERR("!posix_fallocate");
goto err;
}
}
} else {
ssize_t actual_size = util_file_get_size(path);
if (actual_size < 0) {
ERR("stat %s: negative size", path);
errno = EINVAL;
goto err;
}
len = (size_t)actual_size;
}
void *addr;
if ((addr = util_map(fd, len, MAP_SHARED, 0, 0)) == NULL)
goto err;
#ifndef _WIN32
if (is_dev_dax && util_range_register(addr, len) != 0) {
LOG(2, "can't track mapped region");
}
#endif
if (mapped_lenp != NULL)
*mapped_lenp = len;
if (is_pmemp != NULL)
*is_pmemp = is_dev_dax || pmem_is_pmem(addr, len);
LOG(3, "returning %p", addr);
VALGRIND_REGISTER_PMEM_MAPPING(addr, len);
VALGRIND_REGISTER_PMEM_FILE(fd, addr, len, 0);
(void) os_close(fd);
return addr;
err:
oerrno = errno;
(void) os_close(fd);
if (delete_on_err)
(void) os_unlink(path);
errno = oerrno;
return NULL;
}
#ifndef _WIN32
void *
pmem_map_file(const char *path, size_t len, int flags,
mode_t mode, size_t *mapped_lenp, int *is_pmemp)
{
return pmem_map_fileU(path, len, flags, mode, mapped_lenp, is_pmemp);
}
#else
void *
pmem_map_fileW(const wchar_t *path, size_t len, int flags, mode_t mode,
size_t *mapped_lenp, int *is_pmemp) {
char *upath = util_toUTF8(path);
if (upath == NULL)
return NULL;
void *ret = pmem_map_fileU(upath, len, flags, mode, mapped_lenp,
is_pmemp);
util_free_UTF8(upath);
return ret;
}
#endif
int
pmem_unmap(void *addr, size_t len)
{
LOG(3, "addr %p len %zu", addr, len);
#ifndef _WIN32
util_range_unregister(addr, len);
#endif
VALGRIND_REMOVE_PMEM_MAPPING(addr, len);
return util_unmap(addr, len);
}
static void *
memmove_nodrain_normal(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
memmove(pmemdest, src, len);
pmem_flush(pmemdest, len);
return pmemdest;
}
static void *
memmove_nodrain_movnt(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7;
size_t i;
__m128i *d;
__m128i *s;
void *dest1 = pmemdest;
size_t cnt;
if (len == 0 || src == pmemdest)
return pmemdest;
if (len < Movnt_threshold) {
memmove(pmemdest, src, len);
pmem_flush(pmemdest, len);
return pmemdest;
}
if ((uintptr_t)dest1 - (uintptr_t)src >= len) {
cnt = (uint64_t)dest1 & ALIGN_MASK;
if (cnt > 0) {
cnt = FLUSH_ALIGN - cnt;
if (cnt > len)
cnt = len;
uint8_t *d8 = (uint8_t *)dest1;
const uint8_t *s8 = (uint8_t *)src;
for (i = 0; i < cnt; i++) {
*d8 = *s8;
d8++;
s8++;
}
pmem_flush(dest1, cnt);
dest1 = (char *)dest1 + cnt;
src = (char *)src + cnt;
len -= cnt;
}
d = (__m128i *)dest1;
s = (__m128i *)src;
cnt = len >> CHUNK_SHIFT;
for (i = 0; i < cnt; i++) {
xmm0 = _mm_loadu_si128(s);
xmm1 = _mm_loadu_si128(s + 1);
xmm2 = _mm_loadu_si128(s + 2);
xmm3 = _mm_loadu_si128(s + 3);
xmm4 = _mm_loadu_si128(s + 4);
xmm5 = _mm_loadu_si128(s + 5);
xmm6 = _mm_loadu_si128(s + 6);
xmm7 = _mm_loadu_si128(s + 7);
s += 8;
_mm_stream_si128(d, xmm0);
_mm_stream_si128(d + 1, xmm1);
_mm_stream_si128(d + 2, xmm2);
_mm_stream_si128(d + 3, xmm3);
_mm_stream_si128(d + 4, xmm4);
_mm_stream_si128(d + 5, xmm5);
_mm_stream_si128(d + 6, xmm6);
_mm_stream_si128(d + 7, xmm7);
VALGRIND_DO_FLUSH(d, 8 * sizeof(*d));
d += 8;
}
len &= CHUNK_MASK;
if (len != 0) {
cnt = len >> MOVNT_SHIFT;
for (i = 0; i < cnt; i++) {
xmm0 = _mm_loadu_si128(s);
_mm_stream_si128(d, xmm0);
VALGRIND_DO_FLUSH(d, sizeof(*d));
s++;
d++;
}
}
len &= MOVNT_MASK;
if (len != 0) {
cnt = len >> DWORD_SHIFT;
int32_t *d32 = (int32_t *)d;
int32_t *s32 = (int32_t *)s;
for (i = 0; i < cnt; i++) {
_mm_stream_si32(d32, *s32);
VALGRIND_DO_FLUSH(d32, sizeof(*d32));
d32++;
s32++;
}
cnt = len & DWORD_MASK;
uint8_t *d8 = (uint8_t *)d32;
const uint8_t *s8 = (uint8_t *)s32;
for (i = 0; i < cnt; i++) {
*d8 = *s8;
d8++;
s8++;
}
pmem_flush(d32, cnt);
}
} else {
dest1 = (char *)dest1 + len;
src = (char *)src + len;
cnt = (uint64_t)dest1 & ALIGN_MASK;
if (cnt > 0) {
if (cnt > len)
cnt = len;
uint8_t *d8 = (uint8_t *)dest1;
const uint8_t *s8 = (uint8_t *)src;
for (i = 0; i < cnt; i++) {
d8--;
s8--;
*d8 = *s8;
}
pmem_flush(d8, cnt);
dest1 = (char *)dest1 - cnt;
src = (char *)src - cnt;
len -= cnt;
}
d = (__m128i *)dest1;
s = (__m128i *)src;
cnt = len >> CHUNK_SHIFT;
for (i = 0; i < cnt; i++) {
xmm0 = _mm_loadu_si128(s - 1);
xmm1 = _mm_loadu_si128(s - 2);
xmm2 = _mm_loadu_si128(s - 3);
xmm3 = _mm_loadu_si128(s - 4);
xmm4 = _mm_loadu_si128(s - 5);
xmm5 = _mm_loadu_si128(s - 6);
xmm6 = _mm_loadu_si128(s - 7);
xmm7 = _mm_loadu_si128(s - 8);
s -= 8;
_mm_stream_si128(d - 1, xmm0);
_mm_stream_si128(d - 2, xmm1);
_mm_stream_si128(d - 3, xmm2);
_mm_stream_si128(d - 4, xmm3);
_mm_stream_si128(d - 5, xmm4);
_mm_stream_si128(d - 6, xmm5);
_mm_stream_si128(d - 7, xmm6);
_mm_stream_si128(d - 8, xmm7);
d -= 8;
VALGRIND_DO_FLUSH(d, 8 * sizeof(*d));
}
len &= CHUNK_MASK;
if (len != 0) {
cnt = len >> MOVNT_SHIFT;
for (i = 0; i < cnt; i++) {
d--;
s--;
xmm0 = _mm_loadu_si128(s);
_mm_stream_si128(d, xmm0);
VALGRIND_DO_FLUSH(d, sizeof(*d));
}
}
len &= MOVNT_MASK;
if (len != 0) {
cnt = len >> DWORD_SHIFT;
int32_t *d32 = (int32_t *)d;
int32_t *s32 = (int32_t *)s;
for (i = 0; i < cnt; i++) {
d32--;
s32--;
_mm_stream_si32(d32, *s32);
VALGRIND_DO_FLUSH(d32, sizeof(*d32));
}
cnt = len & DWORD_MASK;
uint8_t *d8 = (uint8_t *)d32;
const uint8_t *s8 = (uint8_t *)s32;
for (i = 0; i < cnt; i++) {
d8--;
s8--;
*d8 = *s8;
}
pmem_flush(d8, cnt);
}
}
predrain_fence_sfence();
return pmemdest;
}
static void *(*Func_memmove_nodrain)
(void *pmemdest, const void *src, size_t len) = memmove_nodrain_normal;
void *
pmem_memmove_nodrain(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
return Func_memmove_nodrain(pmemdest, src, len);
}
void *
pmem_memcpy_nodrain(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
return pmem_memmove_nodrain(pmemdest, src, len);
}
void *
pmem_memmove_persist(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
pmem_memmove_nodrain(pmemdest, src, len);
pmem_drain();
return pmemdest;
}
void *
pmem_memcpy_persist(void *pmemdest, const void *src, size_t len)
{
LOG(15, "pmemdest %p src %p len %zu", pmemdest, src, len);
pmem_memcpy_nodrain(pmemdest, src, len);
pmem_drain();
return pmemdest;
}
static void *
memset_nodrain_normal(void *pmemdest, int c, size_t len)
{
LOG(15, "pmemdest %p c 0x%x len %zu", pmemdest, c, len);
memset(pmemdest, c, len);
pmem_flush(pmemdest, len);
return pmemdest;
}
static void *
memset_nodrain_movnt(void *pmemdest, int c, size_t len)
{
LOG(15, "pmemdest %p c 0x%x len %zu", pmemdest, c, len);
size_t i;
void *dest1 = pmemdest;
size_t cnt;
__m128i xmm0;
__m128i *d;
if (len < Movnt_threshold) {
memset(pmemdest, c, len);
pmem_flush(pmemdest, len);
return pmemdest;
}
cnt = (uint64_t)dest1 & ALIGN_MASK;
if (cnt != 0) {
cnt = FLUSH_ALIGN - cnt;
if (cnt > len)
cnt = len;
memset(dest1, c, cnt);
pmem_flush(dest1, cnt);
len -= cnt;
dest1 = (char *)dest1 + cnt;
}
xmm0 = _mm_set1_epi8((char)c);
d = (__m128i *)dest1;
cnt = len / CHUNK_SIZE;
if (cnt != 0) {
for (i = 0; i < cnt; i++) {
_mm_stream_si128(d, xmm0);
_mm_stream_si128(d + 1, xmm0);
_mm_stream_si128(d + 2, xmm0);
_mm_stream_si128(d + 3, xmm0);
_mm_stream_si128(d + 4, xmm0);
_mm_stream_si128(d + 5, xmm0);
_mm_stream_si128(d + 6, xmm0);
_mm_stream_si128(d + 7, xmm0);
VALGRIND_DO_FLUSH(d, 8 * sizeof(*d));
d += 8;
}
}
len &= CHUNK_MASK;
if (len != 0) {
cnt = len >> MOVNT_SHIFT;
for (i = 0; i < cnt; i++) {
_mm_stream_si128(d, xmm0);
VALGRIND_DO_FLUSH(d, sizeof(*d));
d++;
}
}
len &= MOVNT_MASK;
if (len != 0) {
int32_t *d32 = (int32_t *)d;
cnt = len >> DWORD_SHIFT;
if (cnt != 0) {
for (i = 0; i < cnt; i++) {
_mm_stream_si32(d32,
_mm_cvtsi128_si32(xmm0));
VALGRIND_DO_FLUSH(d32, sizeof(*d32));
d32++;
}
}
cnt = len & DWORD_MASK;
if (cnt != 0) {
memset((void *)d32, c, cnt);
pmem_flush(d32, cnt);
}
}
predrain_fence_sfence();
return pmemdest;
}
static void *(*Func_memset_nodrain)
(void *pmemdest, int c, size_t len) = memset_nodrain_normal;
void *
pmem_memset_nodrain(void *pmemdest, int c, size_t len)
{
LOG(15, "pmemdest %p c 0x%x len %zu", pmemdest, c, len);
return Func_memset_nodrain(pmemdest, c, len);
}
void *
pmem_memset_persist(void *pmemdest, int c, size_t len)
{
LOG(15, "pmemdest %p c 0x%x len %zu", pmemdest, c, len);
pmem_memset_nodrain(pmemdest, c, len);
pmem_drain();
return pmemdest;
}
static void
pmem_log_cpuinfo(void)
{
LOG(3, NULL);
if (Func_flush == flush_clwb)
LOG(3, "using clwb");
else if (Func_flush == flush_clflushopt)
LOG(3, "using clflushopt");
else if (Func_flush == flush_clflush)
LOG(3, "using clflush");
else if (Func_flush == flush_empty)
LOG(3, "not flushing CPU cache");
else
FATAL("invalid flush function address");
if (Func_memmove_nodrain == memmove_nodrain_movnt)
LOG(3, "using movnt");
else if (Func_memmove_nodrain == memmove_nodrain_normal)
LOG(3, "not using movnt");
else
FATAL("invalid memove_nodrain function address");
}
static void
pmem_get_cpuinfo(void)
{
LOG(3, NULL);
if (is_cpu_clflush_present()) {
Func_is_pmem = is_pmem_detect;
LOG(3, "clflush supported");
}
if (is_cpu_clflushopt_present()) {
LOG(3, "clflushopt supported");
char *e = os_getenv("PMEM_NO_CLFLUSHOPT");
if (e && strcmp(e, "1") == 0)
LOG(3, "PMEM_NO_CLFLUSHOPT forced no clflushopt");
else {
Func_flush = flush_clflushopt;
Func_predrain_fence = predrain_fence_sfence;
}
}
if (is_cpu_clwb_present()) {
LOG(3, "clwb supported");
char *e = os_getenv("PMEM_NO_CLWB");
if (e && strcmp(e, "1") == 0)
LOG(3, "PMEM_NO_CLWB forced no clwb");
else {
Func_flush = flush_clwb;
Func_predrain_fence = predrain_fence_sfence;
}
}
}
void
pmem_init(void)
{
LOG(3, NULL);
pmem_get_cpuinfo();
char *e = os_getenv("PMEM_NO_FLUSH");
if (e && strcmp(e, "1") == 0) {
LOG(3, "forced not flushing CPU cache");
Func_flush = flush_empty;
Func_predrain_fence = predrain_fence_sfence;
}
char *ptr = os_getenv("PMEM_MOVNT_THRESHOLD");
if (ptr) {
long long val = atoll(ptr);
if (val < 0)
LOG(3, "Invalid PMEM_MOVNT_THRESHOLD");
else {
LOG(3, "PMEM_MOVNT_THRESHOLD set to %zu", (size_t)val);
Movnt_threshold = (size_t)val;
}
}
ptr = os_getenv("PMEM_NO_MOVNT");
if (ptr && strcmp(ptr, "1") == 0)
LOG(3, "PMEM_NO_MOVNT forced no movnt");
else {
Func_memmove_nodrain = memmove_nodrain_movnt;
Func_memset_nodrain = memset_nodrain_movnt;
}
pmem_log_cpuinfo();
#if defined(_WIN32) && (NTDDI_VERSION >= NTDDI_WIN10_RS1)
Func_qvmi = (PQVM)GetProcAddress(
GetModuleHandle(TEXT("KernelBase.dll")),
"QueryVirtualMemoryInformation");
#endif
}