memory_segment_mapped_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <util/tests/memory_segment_common_unittest.h>
  15. #include <util/unittests/check_valgrind.h>
  16. #include <util/memory_segment_mapped.h>
  17. #include <exceptions/exceptions.h>
  18. #include <gtest/gtest.h>
  19. #include <boost/interprocess/file_mapping.hpp>
  20. #include <boost/interprocess/mapped_region.hpp>
  21. #include <boost/scoped_ptr.hpp>
  22. #include <stdint.h>
  23. #include <cstdlib>
  24. #include <cstring>
  25. #include <limits>
  26. #include <stdexcept>
  27. #include <fstream>
  28. #include <string>
  29. #include <sys/stat.h>
  30. using namespace isc::util;
  31. using boost::scoped_ptr;
  32. namespace {
  33. const char* const mapped_file = TEST_DATA_BUILDDIR "/test.mapped";
  34. const size_t DEFAULT_INITIAL_SIZE = 32 * 1024; // intentionally hardcoded
  35. class MemorySegmentMappedTest : public ::testing::Test {
  36. protected:
  37. MemorySegmentMappedTest() {
  38. resetSegment();
  39. }
  40. ~MemorySegmentMappedTest() {
  41. segment_.reset();
  42. boost::interprocess::file_mapping::remove(mapped_file);
  43. }
  44. // For initialization and for tests after the segment possibly becomes
  45. // broken.
  46. void resetSegment() {
  47. segment_.reset();
  48. boost::interprocess::file_mapping::remove(mapped_file);
  49. segment_.reset(new MemorySegmentMapped(mapped_file, true));
  50. }
  51. scoped_ptr<MemorySegmentMapped> segment_;
  52. };
  53. TEST_F(MemorySegmentMappedTest, createAndModify) {
  54. // We are going to do the same set of basic tests twice; one after creating
  55. // the mapped file, the other by re-opening the existing file in the
  56. // read-write mode.
  57. for (int i = 0; i < 2; ++i) {
  58. // It should have the default size (intentionally hardcoded)
  59. EXPECT_EQ(DEFAULT_INITIAL_SIZE, segment_->getSize());
  60. // By default, nothing is allocated.
  61. EXPECT_TRUE(segment_->allMemoryDeallocated());
  62. void* ptr = segment_->allocate(1024);
  63. EXPECT_NE(static_cast<void*>(0), ptr);
  64. // Now, we have an allocation:
  65. EXPECT_FALSE(segment_->allMemoryDeallocated());
  66. // deallocate it; it shouldn't cause disruption.
  67. segment_->deallocate(ptr, 1024);
  68. EXPECT_TRUE(segment_->allMemoryDeallocated());
  69. // re-open it in read-write mode, but don't try to create it
  70. // this time.
  71. segment_.reset(new MemorySegmentMapped(mapped_file, false));
  72. }
  73. }
  74. TEST_F(MemorySegmentMappedTest, createWithSize) {
  75. boost::interprocess::file_mapping::remove(mapped_file);
  76. // Re-create the mapped file with a non-default initial size, and confirm
  77. // the size is actually the specified one.
  78. const size_t new_size = 64 * 1024;
  79. EXPECT_NE(new_size, segment_->getSize());
  80. segment_.reset(new MemorySegmentMapped(mapped_file, true, new_size));
  81. EXPECT_EQ(new_size, segment_->getSize());
  82. }
  83. TEST_F(MemorySegmentMappedTest, openFail) {
  84. // The given file is directory
  85. EXPECT_THROW(MemorySegmentMapped("/", true), MemorySegmentOpenError);
  86. // file doesn't exist and directory isn't writable (we assume the
  87. // following path is not writable for the user running the test).
  88. EXPECT_THROW(MemorySegmentMapped("/random-glkwjer098/test.mapped", true),
  89. MemorySegmentOpenError);
  90. // It should fail when file doesn't exist and it's read-only (so
  91. // open-only).
  92. EXPECT_THROW(MemorySegmentMapped(TEST_DATA_BUILDDIR "/nosuchfile.mapped"),
  93. MemorySegmentOpenError);
  94. // Likewise, it should fail in read-write mode when creation is
  95. // suppressed.
  96. EXPECT_THROW(MemorySegmentMapped(TEST_DATA_BUILDDIR "/nosuchfile.mapped",
  97. false),
  98. MemorySegmentOpenError);
  99. // Close the existing segment, break its file with bogus data, and
  100. // try to reopen. It should fail with exception whether in the
  101. // read-only or read-write, or "create if not exist" mode.
  102. segment_.reset();
  103. std::ofstream ofs(mapped_file, std::ios::trunc);
  104. ofs << std::string(1024, 'x');
  105. ofs.close();
  106. EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file), MemorySegmentOpenError);
  107. EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, false),
  108. MemorySegmentOpenError);
  109. EXPECT_THROW(MemorySegmentMapped sgmt(mapped_file, true),
  110. MemorySegmentOpenError);
  111. }
  112. TEST_F(MemorySegmentMappedTest, allocate) {
  113. // Various case of allocation. The simplest cases are covered above.
  114. // Initially, nothing is allocated.
  115. EXPECT_TRUE(segment_->allMemoryDeallocated());
  116. // (Clearly) exceeding the available size, which should cause growing
  117. // the segment
  118. const size_t prev_size = segment_->getSize();
  119. EXPECT_THROW(segment_->allocate(prev_size + 1), MemorySegmentGrown);
  120. // The size should have been doubled.
  121. EXPECT_EQ(prev_size * 2, segment_->getSize());
  122. // But nothing should have been allocated.
  123. EXPECT_TRUE(segment_->allMemoryDeallocated());
  124. // Now, the allocation should now succeed.
  125. void* ptr = segment_->allocate(prev_size + 1);
  126. EXPECT_NE(static_cast<void*>(NULL), ptr);
  127. EXPECT_FALSE(segment_->allMemoryDeallocated());
  128. // Same set of checks, but for a larger size.
  129. EXPECT_THROW(segment_->allocate(prev_size * 10), MemorySegmentGrown);
  130. // the segment should have grown to the minimum power-of-2 size that
  131. // could allocate the given size of memory.
  132. EXPECT_EQ(prev_size * 16, segment_->getSize());
  133. // And allocate() should now succeed.
  134. ptr = segment_->allocate(prev_size * 10);
  135. EXPECT_NE(static_cast<void*>(NULL), ptr);
  136. // (we'll left the regions created in the file there; the entire file
  137. // will be removed at the end of the test)
  138. }
  139. TEST_F(MemorySegmentMappedTest, badAllocate) {
  140. // Make the mapped file non-writable; managed_mapped_file::grow() will
  141. // fail, resulting in std::bad_alloc
  142. const int ret = chmod(mapped_file, 0444);
  143. ASSERT_EQ(0, ret);
  144. EXPECT_THROW(segment_->allocate(DEFAULT_INITIAL_SIZE * 2), std::bad_alloc);
  145. }
  146. // XXX: this test can cause too strong side effect (creating a very large
  147. // file), so we disable it by default
  148. TEST_F(MemorySegmentMappedTest, DISABLED_allocateHuge) {
  149. EXPECT_THROW(segment_->allocate(std::numeric_limits<size_t>::max()),
  150. std::bad_alloc);
  151. }
  152. TEST_F(MemorySegmentMappedTest, badDeallocate) {
  153. void* ptr = segment_->allocate(4);
  154. EXPECT_NE(static_cast<void*>(NULL), ptr);
  155. segment_->deallocate(ptr, 4); // this is okay
  156. // This is duplicate dealloc; should trigger assertion failure.
  157. if (!isc::util::unittests::runningOnValgrind()) {
  158. EXPECT_DEATH_IF_SUPPORTED({segment_->deallocate(ptr, 4);}, "");
  159. resetSegment(); // the segment is possibly broken; reset it.
  160. }
  161. // Deallocating at an invalid address; this would result in crash (the
  162. // behavior may not be portable enough; if so we should disable it by
  163. // default).
  164. if (!isc::util::unittests::runningOnValgrind()) {
  165. ptr = segment_->allocate(4);
  166. EXPECT_NE(static_cast<void*>(NULL), ptr);
  167. EXPECT_DEATH_IF_SUPPORTED({
  168. segment_->deallocate(static_cast<char*>(ptr) + 1, 3);
  169. }, "");
  170. resetSegment();
  171. }
  172. // Invalid size; this implementation doesn't detect such errors.
  173. ptr = segment_->allocate(4);
  174. EXPECT_NE(static_cast<void*>(NULL), ptr);
  175. segment_->deallocate(ptr, 8);
  176. EXPECT_TRUE(segment_->allMemoryDeallocated());
  177. }
  178. TEST_F(MemorySegmentMappedTest, namedAddress) {
  179. // common test cases
  180. isc::util::test::checkSegmentNamedAddress(*segment_, false);
  181. // Set it again and read it in the read-only mode.
  182. void* ptr16 = segment_->allocate(sizeof(uint16_t));
  183. const uint16_t test_val16 = 42000;
  184. std::memcpy(ptr16, &test_val16, sizeof(test_val16));
  185. EXPECT_FALSE(segment_->setNamedAddress("test address", ptr16));
  186. MemorySegmentMapped segment_ro(mapped_file);
  187. EXPECT_NE(static_cast<void*>(NULL),
  188. segment_ro.getNamedAddress("test address"));
  189. EXPECT_EQ(test_val16, *static_cast<const uint16_t*>(
  190. segment_ro.getNamedAddress("test address")));
  191. // try to set an unusually long name. We re-create the file so the
  192. // creating the name would cause allocation failure and trigger internal
  193. // segment extension.
  194. segment_.reset();
  195. boost::interprocess::file_mapping::remove(mapped_file);
  196. segment_.reset(new MemorySegmentMapped(mapped_file, true, 1024));
  197. const std::string long_name(1025, 'x'); // definitely larger than segment
  198. // setNamedAddress should return true, indicating segment has grown.
  199. EXPECT_TRUE(segment_->setNamedAddress(long_name.c_str(), NULL));
  200. EXPECT_EQ(static_cast<void*>(NULL),
  201. segment_->getNamedAddress(long_name.c_str()));
  202. }
  203. TEST_F(MemorySegmentMappedTest, nullDeallocate) {
  204. // NULL deallocation is a no-op.
  205. EXPECT_NO_THROW(segment_->deallocate(0, 1024));
  206. EXPECT_TRUE(segment_->allMemoryDeallocated());
  207. }
  208. TEST_F(MemorySegmentMappedTest, shrink) {
  209. segment_->shrinkToFit();
  210. // Normally we should be able to expect that the resulting size is
  211. // smaller than the initial default size. But it's not really
  212. // guaranteed by the API, so we may have to disable this check (or
  213. // use EXPECT_GE).
  214. const size_t shrinked_size = segment_->getSize();
  215. EXPECT_GT(DEFAULT_INITIAL_SIZE, shrinked_size);
  216. // Another shrink shouldn't cause disruption, and the size shouldn't
  217. // change.
  218. segment_->shrinkToFit();
  219. EXPECT_EQ(shrinked_size, segment_->getSize());
  220. // Check that the segment is still usable after shrink.
  221. void* p = segment_->allocate(sizeof(uint32_t));
  222. segment_->deallocate(p, sizeof(uint32_t));
  223. }
  224. TEST_F(MemorySegmentMappedTest, violateReadOnly) {
  225. // If the segment is opened in the read-only mode, modification
  226. // attempts are prohibited. When detectable it must result in an
  227. // exception.
  228. EXPECT_THROW(MemorySegmentMapped(mapped_file).allocate(16),
  229. MemorySegmentError);
  230. // allocation that would otherwise require growing the segment; permission
  231. // check should be performed before that.
  232. EXPECT_THROW(MemorySegmentMapped(mapped_file).
  233. allocate(DEFAULT_INITIAL_SIZE * 2), MemorySegmentError);
  234. EXPECT_THROW(MemorySegmentMapped(mapped_file).setNamedAddress("test",
  235. NULL),
  236. MemorySegmentError);
  237. EXPECT_THROW(MemorySegmentMapped(mapped_file).clearNamedAddress("test"),
  238. MemorySegmentError);
  239. EXPECT_THROW(MemorySegmentMapped(mapped_file).shrinkToFit(),
  240. MemorySegmentError);
  241. void* ptr = segment_->allocate(sizeof(uint32_t));
  242. segment_->setNamedAddress("test address", ptr);
  243. // Attempts to modify memory from the read-only segment directly
  244. // will result in a crash.
  245. if (!isc::util::unittests::runningOnValgrind()) {
  246. EXPECT_DEATH_IF_SUPPORTED({
  247. MemorySegmentMapped segment_ro(mapped_file);
  248. EXPECT_TRUE(segment_ro.getNamedAddress("test address"));
  249. *static_cast<uint32_t*>(
  250. segment_ro.getNamedAddress("test address")) = 0;
  251. }, "");
  252. }
  253. EXPECT_THROW(MemorySegmentMapped(mapped_file).deallocate(ptr, 4),
  254. MemorySegmentError);
  255. }
  256. TEST_F(MemorySegmentMappedTest, getCheckSum) {
  257. const size_t old_cksum = segment_->getCheckSum();
  258. // We assume the initial segment size is sufficiently larger than
  259. // the page size. We'll allocate memory of the page size, and
  260. // increment all bytes in that page by one. It will increase our
  261. // simple checksum value (which just uses the first byte of each
  262. // page) by one, too.
  263. const size_t page_sz = boost::interprocess::mapped_region::get_page_size();
  264. uint8_t* cp0 = static_cast<uint8_t*>(segment_->allocate(page_sz));
  265. for (uint8_t* cp = cp0; cp < cp0 + page_sz; ++cp) {
  266. ++*cp;
  267. }
  268. EXPECT_EQ(old_cksum + 1, segment_->getCheckSum());
  269. }
  270. }