Logo AND Algorithmique Numérique Distribuée

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