1 //===- llvm/unittest/Support/MemoryBufferTest.cpp - MemoryBuffer tests ----===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements unit tests for the MemoryBuffer support class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Support/MemoryBuffer.h" 14 #include "llvm/ADT/ScopeExit.h" 15 #include "llvm/Support/FileSystem.h" 16 #include "llvm/Support/FileUtilities.h" 17 #include "llvm/Support/raw_ostream.h" 18 #include "llvm/Testing/Support/Error.h" 19 #include "gtest/gtest.h" 20 21 using namespace llvm; 22 23 #define ASSERT_NO_ERROR(x) \ 24 if (std::error_code ASSERT_NO_ERROR_ec = x) { \ 25 SmallString<128> MessageStorage; \ 26 raw_svector_ostream Message(MessageStorage); \ 27 Message << #x ": did not return errc::success.\n" \ 28 << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ 29 << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ 30 GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \ 31 } else { \ 32 } 33 34 #define ASSERT_ERROR(x) \ 35 if (!x) { \ 36 SmallString<128> MessageStorage; \ 37 raw_svector_ostream Message(MessageStorage); \ 38 Message << #x ": did not return a failure error code.\n"; \ 39 GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \ 40 } 41 42 namespace { 43 44 class MemoryBufferTest : public testing::Test { 45 protected: 46 MemoryBufferTest() 47 : data("this is some data") 48 { } 49 50 void SetUp() override {} 51 52 /// Common testing for different modes of getOpenFileSlice. 53 /// Creates a temporary file with known contents, and uses 54 /// MemoryBuffer::getOpenFileSlice to map it. 55 /// If \p Reopen is true, the file is closed after creating and reopened 56 /// anew before using MemoryBuffer. 57 void testGetOpenFileSlice(bool Reopen); 58 59 typedef std::unique_ptr<MemoryBuffer> OwningBuffer; 60 61 std::string data; 62 }; 63 64 TEST_F(MemoryBufferTest, get) { 65 // Default name and null-terminator flag 66 OwningBuffer MB1(MemoryBuffer::getMemBuffer(data)); 67 EXPECT_TRUE(nullptr != MB1.get()); 68 69 // RequiresNullTerminator = false 70 OwningBuffer MB2(MemoryBuffer::getMemBuffer(data, "one", false)); 71 EXPECT_TRUE(nullptr != MB2.get()); 72 73 // RequiresNullTerminator = true 74 OwningBuffer MB3(MemoryBuffer::getMemBuffer(data, "two", true)); 75 EXPECT_TRUE(nullptr != MB3.get()); 76 77 // verify all 3 buffers point to the same address 78 EXPECT_EQ(MB1->getBufferStart(), MB2->getBufferStart()); 79 EXPECT_EQ(MB2->getBufferStart(), MB3->getBufferStart()); 80 81 // verify the original data is unmodified after deleting the buffers 82 MB1.reset(); 83 MB2.reset(); 84 MB3.reset(); 85 EXPECT_EQ("this is some data", data); 86 } 87 88 TEST_F(MemoryBufferTest, getOpenFile) { 89 int FD; 90 SmallString<64> TestPath; 91 ASSERT_EQ(sys::fs::createTemporaryFile("MemoryBufferTest_getOpenFile", "temp", 92 FD, TestPath), 93 std::error_code()); 94 95 FileRemover Cleanup(TestPath); 96 raw_fd_ostream OF(FD, /*shouldClose*/ true); 97 OF << "12345678"; 98 OF.close(); 99 100 { 101 Expected<sys::fs::file_t> File = sys::fs::openNativeFileForRead(TestPath); 102 ASSERT_THAT_EXPECTED(File, Succeeded()); 103 auto OnExit = 104 make_scope_exit([&] { ASSERT_NO_ERROR(sys::fs::closeFile(*File)); }); 105 ErrorOr<OwningBuffer> MB = MemoryBuffer::getOpenFile(*File, TestPath, 6); 106 ASSERT_NO_ERROR(MB.getError()); 107 EXPECT_EQ("123456", MB.get()->getBuffer()); 108 } 109 { 110 Expected<sys::fs::file_t> File = sys::fs::openNativeFileForWrite( 111 TestPath, sys::fs::CD_OpenExisting, sys::fs::OF_None); 112 ASSERT_THAT_EXPECTED(File, Succeeded()); 113 auto OnExit = 114 make_scope_exit([&] { ASSERT_NO_ERROR(sys::fs::closeFile(*File)); }); 115 ASSERT_ERROR(MemoryBuffer::getOpenFile(*File, TestPath, 6).getError()); 116 } 117 } 118 119 TEST_F(MemoryBufferTest, NullTerminator4K) { 120 // Test that a file with size that is a multiple of the page size can be null 121 // terminated correctly by MemoryBuffer. 122 int TestFD; 123 SmallString<64> TestPath; 124 sys::fs::createTemporaryFile("MemoryBufferTest_NullTerminator4K", "temp", 125 TestFD, TestPath); 126 FileRemover Cleanup(TestPath); 127 raw_fd_ostream OF(TestFD, true, /*unbuffered=*/true); 128 for (unsigned i = 0; i < 4096 / 16; ++i) { 129 OF << "0123456789abcdef"; 130 } 131 OF.close(); 132 133 ErrorOr<OwningBuffer> MB = MemoryBuffer::getFile(TestPath.c_str()); 134 std::error_code EC = MB.getError(); 135 ASSERT_FALSE(EC); 136 137 const char *BufData = MB.get()->getBufferStart(); 138 EXPECT_EQ('f', BufData[4095]); 139 EXPECT_EQ('\0', BufData[4096]); 140 } 141 142 TEST_F(MemoryBufferTest, copy) { 143 // copy with no name 144 OwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data)); 145 EXPECT_TRUE(nullptr != MBC1.get()); 146 147 // copy with a name 148 OwningBuffer MBC2(MemoryBuffer::getMemBufferCopy(data, "copy")); 149 EXPECT_TRUE(nullptr != MBC2.get()); 150 151 // verify the two copies do not point to the same place 152 EXPECT_NE(MBC1->getBufferStart(), MBC2->getBufferStart()); 153 } 154 155 TEST_F(MemoryBufferTest, make_new) { 156 // 0-sized buffer 157 OwningBuffer Zero(WritableMemoryBuffer::getNewUninitMemBuffer(0)); 158 EXPECT_TRUE(nullptr != Zero.get()); 159 160 // uninitialized buffer with no name 161 OwningBuffer One(WritableMemoryBuffer::getNewUninitMemBuffer(321)); 162 EXPECT_TRUE(nullptr != One.get()); 163 164 // uninitialized buffer with name 165 OwningBuffer Two(WritableMemoryBuffer::getNewUninitMemBuffer(123, "bla")); 166 EXPECT_TRUE(nullptr != Two.get()); 167 168 // 0-initialized buffer with no name 169 OwningBuffer Three(WritableMemoryBuffer::getNewMemBuffer(321, data)); 170 EXPECT_TRUE(nullptr != Three.get()); 171 for (size_t i = 0; i < 321; ++i) 172 EXPECT_EQ(0, Three->getBufferStart()[0]); 173 174 // 0-initialized buffer with name 175 OwningBuffer Four(WritableMemoryBuffer::getNewMemBuffer(123, "zeros")); 176 EXPECT_TRUE(nullptr != Four.get()); 177 for (size_t i = 0; i < 123; ++i) 178 EXPECT_EQ(0, Four->getBufferStart()[0]); 179 } 180 181 void MemoryBufferTest::testGetOpenFileSlice(bool Reopen) { 182 // Test that MemoryBuffer::getOpenFile works properly when no null 183 // terminator is requested and the size is large enough to trigger 184 // the usage of memory mapping. 185 int TestFD; 186 SmallString<64> TestPath; 187 // Create a temporary file and write data into it. 188 sys::fs::createTemporaryFile("prefix", "temp", TestFD, TestPath); 189 FileRemover Cleanup(TestPath); 190 // OF is responsible for closing the file; If the file is not 191 // reopened, it will be unbuffered so that the results are 192 // immediately visible through the fd. 193 raw_fd_ostream OF(TestFD, true, !Reopen); 194 for (int i = 0; i < 60000; ++i) { 195 OF << "0123456789"; 196 } 197 198 if (Reopen) { 199 OF.close(); 200 EXPECT_FALSE(sys::fs::openFileForRead(TestPath.c_str(), TestFD)); 201 } 202 203 ErrorOr<OwningBuffer> Buf = MemoryBuffer::getOpenFileSlice( 204 sys::fs::convertFDToNativeFile(TestFD), TestPath.c_str(), 205 40000, // Size 206 80000 // Offset 207 ); 208 209 std::error_code EC = Buf.getError(); 210 EXPECT_FALSE(EC); 211 212 StringRef BufData = Buf.get()->getBuffer(); 213 EXPECT_EQ(BufData.size(), 40000U); 214 EXPECT_EQ(BufData[0], '0'); 215 EXPECT_EQ(BufData[9], '9'); 216 } 217 218 TEST_F(MemoryBufferTest, getOpenFileNoReopen) { 219 testGetOpenFileSlice(false); 220 } 221 222 TEST_F(MemoryBufferTest, getOpenFileReopened) { 223 testGetOpenFileSlice(true); 224 } 225 226 TEST_F(MemoryBufferTest, reference) { 227 OwningBuffer MB(MemoryBuffer::getMemBuffer(data)); 228 MemoryBufferRef MBR(*MB); 229 230 EXPECT_EQ(MB->getBufferStart(), MBR.getBufferStart()); 231 EXPECT_EQ(MB->getBufferIdentifier(), MBR.getBufferIdentifier()); 232 } 233 234 TEST_F(MemoryBufferTest, slice) { 235 // Create a file that is six pages long with different data on each page. 236 int FD; 237 SmallString<64> TestPath; 238 sys::fs::createTemporaryFile("MemoryBufferTest_Slice", "temp", FD, TestPath); 239 FileRemover Cleanup(TestPath); 240 raw_fd_ostream OF(FD, true, /*unbuffered=*/true); 241 for (unsigned i = 0; i < 0x2000 / 8; ++i) { 242 OF << "12345678"; 243 } 244 for (unsigned i = 0; i < 0x2000 / 8; ++i) { 245 OF << "abcdefgh"; 246 } 247 for (unsigned i = 0; i < 0x2000 / 8; ++i) { 248 OF << "ABCDEFGH"; 249 } 250 OF.close(); 251 252 // Try offset of one page. 253 ErrorOr<OwningBuffer> MB = MemoryBuffer::getFileSlice(TestPath.str(), 254 0x4000, 0x1000); 255 std::error_code EC = MB.getError(); 256 ASSERT_FALSE(EC); 257 EXPECT_EQ(0x4000UL, MB.get()->getBufferSize()); 258 259 StringRef BufData = MB.get()->getBuffer(); 260 EXPECT_TRUE(BufData.substr(0x0000,8).equals("12345678")); 261 EXPECT_TRUE(BufData.substr(0x0FF8,8).equals("12345678")); 262 EXPECT_TRUE(BufData.substr(0x1000,8).equals("abcdefgh")); 263 EXPECT_TRUE(BufData.substr(0x2FF8,8).equals("abcdefgh")); 264 EXPECT_TRUE(BufData.substr(0x3000,8).equals("ABCDEFGH")); 265 EXPECT_TRUE(BufData.substr(0x3FF8,8).equals("ABCDEFGH")); 266 267 // Try non-page aligned. 268 ErrorOr<OwningBuffer> MB2 = MemoryBuffer::getFileSlice(TestPath.str(), 269 0x3000, 0x0800); 270 EC = MB2.getError(); 271 ASSERT_FALSE(EC); 272 EXPECT_EQ(0x3000UL, MB2.get()->getBufferSize()); 273 274 StringRef BufData2 = MB2.get()->getBuffer(); 275 EXPECT_TRUE(BufData2.substr(0x0000,8).equals("12345678")); 276 EXPECT_TRUE(BufData2.substr(0x17F8,8).equals("12345678")); 277 EXPECT_TRUE(BufData2.substr(0x1800,8).equals("abcdefgh")); 278 EXPECT_TRUE(BufData2.substr(0x2FF8,8).equals("abcdefgh")); 279 } 280 281 TEST_F(MemoryBufferTest, writableSlice) { 282 // Create a file initialized with some data 283 int FD; 284 SmallString<64> TestPath; 285 sys::fs::createTemporaryFile("MemoryBufferTest_WritableSlice", "temp", FD, 286 TestPath); 287 FileRemover Cleanup(TestPath); 288 raw_fd_ostream OF(FD, true); 289 for (unsigned i = 0; i < 0x1000; ++i) 290 OF << "0123456789abcdef"; 291 OF.close(); 292 293 { 294 auto MBOrError = 295 WritableMemoryBuffer::getFileSlice(TestPath.str(), 0x6000, 0x2000); 296 ASSERT_FALSE(MBOrError.getError()); 297 // Write some data. It should be mapped private, so that upon completion 298 // the original file contents are not modified. 299 WritableMemoryBuffer &MB = **MBOrError; 300 ASSERT_EQ(0x6000u, MB.getBufferSize()); 301 char *Start = MB.getBufferStart(); 302 ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize()); 303 ::memset(Start, 'x', MB.getBufferSize()); 304 } 305 306 auto MBOrError = MemoryBuffer::getFile(TestPath); 307 ASSERT_FALSE(MBOrError.getError()); 308 auto &MB = **MBOrError; 309 ASSERT_EQ(0x10000u, MB.getBufferSize()); 310 for (size_t i = 0; i < MB.getBufferSize(); i += 0x10) 311 EXPECT_EQ("0123456789abcdef", MB.getBuffer().substr(i, 0x10)) << "i: " << i; 312 } 313 314 TEST_F(MemoryBufferTest, writeThroughFile) { 315 // Create a file initialized with some data 316 int FD; 317 SmallString<64> TestPath; 318 sys::fs::createTemporaryFile("MemoryBufferTest_WriteThrough", "temp", FD, 319 TestPath); 320 FileRemover Cleanup(TestPath); 321 raw_fd_ostream OF(FD, true); 322 OF << "0123456789abcdef"; 323 OF.close(); 324 { 325 auto MBOrError = WriteThroughMemoryBuffer::getFile(TestPath); 326 ASSERT_FALSE(MBOrError.getError()); 327 // Write some data. It should be mapped readwrite, so that upon completion 328 // the original file contents are modified. 329 WriteThroughMemoryBuffer &MB = **MBOrError; 330 ASSERT_EQ(16u, MB.getBufferSize()); 331 char *Start = MB.getBufferStart(); 332 ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize()); 333 ::memset(Start, 'x', MB.getBufferSize()); 334 } 335 336 auto MBOrError = MemoryBuffer::getFile(TestPath); 337 ASSERT_FALSE(MBOrError.getError()); 338 auto &MB = **MBOrError; 339 ASSERT_EQ(16u, MB.getBufferSize()); 340 EXPECT_EQ("xxxxxxxxxxxxxxxx", MB.getBuffer()); 341 } 342 } 343