Logo AND Algorithmique Numérique Distribuée

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