Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] s/NULL/nullptr/
[simgrid.git] / src / mc / PageStore.cpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <unistd.h>
8 #include <string.h> // memcpy, memcp
9
10 #include <sys/mman.h>
11
12 #include <xbt.h>
13
14 #include "src/mc/PageStore.hpp"
15
16 #include "src/mc/mc_mmu.h"
17
18 extern "C" {
19
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_page_snapshot, mc,
21                                 "Logging specific to mc_page_snapshot");
22
23 namespace simgrid {
24 namespace mc {
25
26 /** @brief Compte a hash for the given memory page
27  *
28  *  The page is used before inserting the page in the page store
29  *  in order to find duplicate of this page in the page store.
30  *
31  *  @param data Memory page
32  *  @return hash off the page
33  */
34 static inline  __attribute__ ((always_inline))
35 PageStore::hash_type mc_hash_page(const void* data)
36 {
37   const std::uint64_t* values = (const uint64_t*) data;
38   std::size_t n = xbt_pagesize / sizeof(uint64_t);
39
40   // This djb2:
41   std::uint64_t hash = 5381;
42   for (std::size_t i = 0; i != n; ++i) {
43     hash = ((hash << 5) + hash) + values[i];
44   }
45   return hash;
46 }
47
48 // ***** snapshot_page_manager
49
50 PageStore::PageStore(size_t size) :
51   memory_(nullptr), capacity_(0), top_index_(0)
52 {
53   // Using mmap in order to be able to expand the region
54   // by relocating it somewhere else in the virtual memory
55   // space:
56   void* memory = ::mmap(nullptr, size << xbt_pagebits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
57   if (memory == MAP_FAILED) {
58     xbt_die("Could not mmap initial snapshot pages.");
59   }
60
61   this->top_index_ = 0;
62   this->capacity_ = size;
63   this->memory_ = memory;
64   this->page_counts_.resize(size);
65 }
66
67 PageStore::~PageStore()
68 {
69   ::munmap(this->memory_, this->capacity_ << xbt_pagebits);
70 }
71
72 void PageStore::resize(std::size_t size)
73 {
74   size_t old_bytesize = this->capacity_ << xbt_pagebits;
75   size_t new_bytesize = size << xbt_pagebits;
76
77   // Expand the memory region by moving it into another
78   // virtual memory address if necessary:
79   void* new_memory = mremap(this->memory_, old_bytesize, new_bytesize, MREMAP_MAYMOVE);
80   if (new_memory == MAP_FAILED) {
81     xbt_die("Could not mremap snapshot pages.");
82   }
83
84   this->capacity_ = size;
85   this->memory_ = new_memory;
86   this->page_counts_.resize(size, 0);
87 }
88
89 /** Allocate a free page
90  *
91  *  @return index of the free page
92  */
93 std::size_t PageStore::alloc_page()
94 {
95   if (this->free_pages_.empty()) {
96
97     // Expand the region:
98     if (this->top_index_ == this->capacity_) {
99       // All the pages are allocated, we need add more pages:
100       this->resize(2 * this->capacity_);
101     }
102
103     // Use a page from the top:
104     return this->top_index_++;
105
106   } else {
107
108     // Use a page from free_pages_ (inside of the region):
109     size_t res = this->free_pages_[this->free_pages_.size() - 1];
110     this->free_pages_.pop_back();
111     return res;
112
113   }
114 }
115
116 void PageStore::remove_page(std::size_t pageno)
117 {
118   this->free_pages_.push_back(pageno);
119   const void* page = this->get_page(pageno);
120   hash_type hash = mc_hash_page(page);
121   this->hash_index_[hash].erase(pageno);
122 }
123
124 /** Store a page in memory */
125 std::size_t PageStore::store_page(void* page)
126 {
127   xbt_assert(top_index_ <= this->capacity_, "top_index is not consistent");
128
129   // First, we check if a page with the same content is already in the page
130   // store:
131   //  1. compute the hash of the page;
132   //  2. find pages with the same hash using `hash_index_`;
133   //  3. find a page with the same content.
134   hash_type hash = mc_hash_page(page);
135
136   // Try to find a duplicate in set of pages with the same hash:
137   page_set_type& page_set = this->hash_index_[hash];
138   for (size_t pageno : page_set) {
139     const void* snapshot_page = this->get_page(pageno);
140     if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
141
142       // If a page with the same content is already in the page store it is
143       // reused and its reference count is incremented.
144       page_counts_[pageno]++;
145       return pageno;
146
147     }
148   }
149
150   // Otherwise, a new page is allocated in the page store and the content
151   // of the page is `memcpy()`-ed to this new page.
152   std::size_t pageno = alloc_page();
153   xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
154   void* snapshot_page = (void*) this->get_page(pageno);
155   memcpy(snapshot_page, page, xbt_pagesize);
156   page_set.insert(pageno);
157   page_counts_[pageno]++;
158   return pageno;
159 }
160
161 }
162 }
163
164 #ifdef SIMGRID_TEST
165
166 #include <cstring>
167 #include <cstdint>
168
169 #include <unistd.h>
170 #include <sys/mman.h>
171
172 #include <memory>
173
174 #include "src/mc/PageStore.hpp"
175
176 static int value = 0;
177
178 static void new_content(void* data, std::size_t size)
179 {
180   ::memset(data, ++value, size);
181 }
182
183 static void* getpage()
184 {
185   return mmap(nullptr, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
186 }
187
188 XBT_TEST_SUITE("mc_page_store", "Page store");
189
190 XBT_TEST_UNIT("base", test_mc_page_store, "Test adding/removing pages in the store")
191 {
192   using simgrid::mc::PageStore;
193   
194   xbt_test_add("Init");
195   std::size_t pagesize = (size_t) getpagesize();
196   std::unique_ptr<PageStore> store
197     = std::unique_ptr<PageStore>(new simgrid::mc::PageStore(500));
198   void* data = getpage();
199   xbt_test_assert(store->size()==0, "Bad size");
200
201   xbt_test_add("Store the page once");
202   new_content(data, pagesize);
203   size_t pageno1 = store->store_page(data);
204   xbt_test_assert(store->get_ref(pageno1)==1, "Bad refcount");
205   const void* copy = store->get_page(pageno1);
206   xbt_test_assert(::memcmp(data, copy, pagesize)==0, "Page data should be the same");
207   xbt_test_assert(store->size()==1, "Bad size");
208
209   xbt_test_add("Store the same page again");
210   size_t pageno2 = store->store_page(data);
211   xbt_test_assert(pageno1==pageno2, "Page should be the same");
212   xbt_test_assert(store->get_ref(pageno1)==2, "Bad refcount");
213   xbt_test_assert(store->size()==1, "Bad size");
214
215   xbt_test_add("Store a new page");
216   new_content(data, pagesize);
217   size_t pageno3 = store->store_page(data);
218   xbt_test_assert(pageno1 != pageno3, "New page should be different");
219   xbt_test_assert(store->size()==2, "Bad size");
220
221   xbt_test_add("Unref pages");
222   store->unref_page(pageno1);
223   xbt_assert(store->get_ref(pageno1)==1, "Bad refcount");
224   xbt_assert(store->size()==2, "Bad size");
225   store->unref_page(pageno2);
226   xbt_test_assert(store->size()==1, "Bad size");
227
228   xbt_test_add("Reallocate page");
229   new_content(data, pagesize);
230   size_t pageno4 = store->store_page(data);
231   xbt_test_assert(pageno1 == pageno4, "Page was not reused");
232   xbt_test_assert(store->get_ref(pageno4)==1, "Bad refcount");
233   xbt_test_assert(store->size()==2, "Bad size");
234 }
235
236 #endif /* SIMGRID_TEST */
237
238 }