/*++ Copyright (c) Microsoft. All rights reserved. Module Name: madvise.c Abstract: This file is a madvise test. --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include #include #include #include #include #include #include #define LXT_NAME "madvise" int MadviseVariation2(PLXT_ARGS Args); int MadviseVariation3(PLXT_ARGS Args); int MadviseVariation4(PLXT_ARGS Args); int MadviseVariation5(PLXT_ARGS Args); // // Global constants // static const LXT_VARIATION g_LxtVariations[] = { {"Anonymous mapping", MadviseVariation2}, {"Anonymous mapping with fork", MadviseVariation3}, {"File-backed mapping", MadviseVariation4}, {"File-backed with fork", MadviseVariation5}}; int MadviseTestEntry(int Argc, char* Argv[]) /*++ --*/ { LXT_ARGS Args; int Result; LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return !LXT_SUCCESS(Result); } #define MADVISE_TEST_BUFFER_SIZE 0x5000 int MadviseRunTest(int FileId, int BufferSize, int DoFork) { int Result; unsigned char* SharedBuffer; unsigned char* PrivateBuffer; unsigned char* Ptr; unsigned char* EndPtr; unsigned char ExpectedByte; int MapAnonymousFlag; int IsChild; // // Initialize locals. // SharedBuffer = MAP_FAILED; PrivateBuffer = MAP_FAILED; IsChild = 0; MapAnonymousFlag = 0; if (FileId == -1) { MapAnonymousFlag = MAP_ANONYMOUS; } // // Allocate the shared buffer. // SharedBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_SHARED | MapAnonymousFlag, FileId, 0); if (SharedBuffer == MAP_FAILED) { Result = errno; LxtLogError("Could not map shared! %d", Result); goto cleanup; } // // Allocate the private buffer. // PrivateBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MapAnonymousFlag, FileId, 0); if (PrivateBuffer == MAP_FAILED) { Result = errno; LxtLogError("Could not map private! %d", Result); goto cleanup; } // // Fill the shared buffer. // Ptr = SharedBuffer; EndPtr = Ptr + BufferSize; for (; Ptr < EndPtr; Ptr += 1) { *Ptr = 0xDE; } // // Make sure the private buffer sees the shared buffer. // if (FileId != -1) { Ptr = PrivateBuffer; EndPtr = Ptr + BufferSize; for (; Ptr < EndPtr; Ptr += 1) { if (*Ptr != 0xDE) { *((volatile int*)0) = 0xDEADBEEF; } } } // // Fill the private buffer. // Ptr = PrivateBuffer; EndPtr = Ptr + BufferSize; for (; Ptr < EndPtr; Ptr += 1) { *Ptr = 0xCA; } // // Protect one of the two pages that will be reverted. // Result = mprotect(PrivateBuffer + PAGE_SIZE, PAGE_SIZE, PROT_READ); if (Result == -1) { Result = errno; LxtLogError("Could not set protection on buffer! %d", Result); goto cleanup; } // // Fork the process if desired. // if (DoFork) { Result = fork(); if (Result == -1) { Result = errno; LxtLogError("Could not fork the child! %d", Result); goto cleanup; } if (Result == 0) { IsChild = 1; } } // // Revert the middle two pages back to the shared buffer. // Result = madvise((PrivateBuffer + PAGE_SIZE), (PAGE_SIZE * 2), MADV_DONTNEED); if (Result == -1) { Result = errno; LxtLogError("Could not madvise on private buffer! %d", Result); goto cleanup; } // // Make sure the first page contains private data. // Ptr = PrivateBuffer; EndPtr = Ptr + PAGE_SIZE; for (; Ptr < EndPtr; Ptr += 1) { if (*Ptr != 0xCA) { LxtLogError("Private buffer not contain expected data (1), %d", *Ptr); Result = EFAULT; goto cleanup; } } // // Make sure the next two pages contain shared data or zeroes in the private // allocation case. // EndPtr = Ptr + (PAGE_SIZE * 2); for (; Ptr < EndPtr; Ptr += 1) { if (FileId == -1) { ExpectedByte = 0; } else { ExpectedByte = 0xDE; } if (*Ptr != ExpectedByte) { LxtLogError("Private buffer not contain expected data (2), %d", *Ptr); Result = EFAULT; goto cleanup; } } // // Make sure the remaining pages contain private data. // EndPtr = PrivateBuffer + BufferSize; for (; Ptr < EndPtr; Ptr += 1) { if (*Ptr != 0xCA) { LxtLogError("Private buffer not contain expected data (3), %d", *Ptr); Result = EFAULT; goto cleanup; } } // // Make sure that the shared buffer contains all shared data. // Ptr = SharedBuffer; EndPtr = Ptr + BufferSize; for (; Ptr < EndPtr; Ptr += 1) { if (*Ptr != 0xDE) { LxtLogError("Shared buffer not contain expected data (4), %d", *Ptr); Result = EFAULT; goto cleanup; } } Result = 0; cleanup: if (SharedBuffer != MAP_FAILED) { munmap(SharedBuffer, BufferSize); } if (PrivateBuffer != MAP_FAILED) { munmap(PrivateBuffer, BufferSize); } if (IsChild) { _exit(Result); } return Result; } int MadviseVariation2(PLXT_ARGS Args) /*++ --*/ { int Result; int FileId; int BufferSize; // // Initialize locals. // FileId = -1; BufferSize = MADVISE_TEST_BUFFER_SIZE; // // Run the test on private memory. // Result = MadviseRunTest(FileId, BufferSize, 0); if (Result != 0) { LxtLogError("Variation2 failed! %d", Result); goto ErrorExit; } Result = LXT_RESULT_SUCCESS; ErrorExit: if (FileId != -1) { close(FileId); } return Result; } int MadviseVariation3(PLXT_ARGS Args) /*++ --*/ { int Result; int FileId; int BufferSize; // // Initialize locals. // FileId = -1; BufferSize = MADVISE_TEST_BUFFER_SIZE; // // Run the test on private memory with fork. // Result = MadviseRunTest(FileId, BufferSize, 1); if (Result != 0) { LxtLogError("Variation3 failed! %d", Result); goto ErrorExit; } Result = LXT_RESULT_SUCCESS; ErrorExit: if (FileId != -1) { close(FileId); } return Result; } int MadviseVariation4(PLXT_ARGS Args) /*++ --*/ { int Result; int FileId; int BufferSize; unsigned int WriteData; int BytesWritten; int ByteIndex; // // Initialize locals. // FileId = -1; BufferSize = MADVISE_TEST_BUFFER_SIZE; // // Open the file and fill it to the appropriate size. // FileId = open("/data/test/madvise_test.bin", O_RDWR | O_CREAT, S_IRWXU); if (FileId == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } WriteData = (unsigned int)-1; for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData)) { BytesWritten = write(FileId, &WriteData, sizeof(WriteData)); if (BytesWritten != sizeof(WriteData)) { Result = errno; LxtLogError("Could not write to file! %d", Result); goto ErrorExit; } } // // Run the test on file-backed memory. // Result = MadviseRunTest(FileId, BufferSize, 0); if (Result != 0) { LxtLogError("Variation4 failed! %d", Result); goto ErrorExit; } Result = LXT_RESULT_SUCCESS; ErrorExit: if (FileId != -1) { close(FileId); } return Result; } int MadviseVariation5(PLXT_ARGS Args) /*++ --*/ { int Result; int FileId; int BufferSize; unsigned int WriteData; int BytesWritten; int ByteIndex; // // Initialize locals. // FileId = -1; BufferSize = MADVISE_TEST_BUFFER_SIZE; // // Open the file and fill it to the appropriate size. // FileId = open("/data/test/madvise_test.bin", O_RDWR | O_CREAT, S_IRWXU); if (FileId == -1) { Result = errno; LxtLogError("Could not create test file! %d", Result); goto ErrorExit; } WriteData = (unsigned int)-1; for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData)) { BytesWritten = write(FileId, &WriteData, sizeof(WriteData)); if (BytesWritten != sizeof(WriteData)) { Result = errno; LxtLogError("Could not write to file! %d", Result); goto ErrorExit; } } // // Run the test on file-backed memory with fork. // Result = MadviseRunTest(FileId, BufferSize, 1); if (Result != 0) { LxtLogError("Variation5 failed! %d", Result); goto ErrorExit; } Result = LXT_RESULT_SUCCESS; ErrorExit: if (FileId != -1) { close(FileId); } return Result; }