Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Bug: MC was reading from the wrong region
[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 #include "mc_mmu.h"
19
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_page_snapshot, mc,
21                                 "Logging specific to mc_page_snapshot");
22
23 // ***** Utility:
24
25 /** @brief Compte a hash for the given memory page
26  *
27  *  The page is used before inserting the page in the page store
28  *  in order to find duplicate of this pae in the page store.
29  *
30  *  @param data Memory page
31  *  @return hash off the page
32  */
33 static inline  __attribute__ ((always_inline))
34 uint64_t mc_hash_page(const void* data)
35 {
36   const uint64_t* values = (const uint64_t*) data;
37   size_t n = xbt_pagesize / sizeof(uint64_t);
38
39   // This djb2:
40   uint64_t hash = 5381;
41   for (size_t i=0; i!=n; ++i) {
42     hash = ((hash << 5) + hash) + values[i];
43   }
44   return hash;
45 }
46
47 // ***** snapshot_page_manager
48
49 s_mc_pages_store::s_mc_pages_store(size_t size) :
50   memory_(NULL), capacity_(0), top_index_(0)
51 {
52   // Using mmap in order to be able to expand the region
53   // by relocating it somewhere else in the virtual memory
54   // space:
55   void * memory = ::mmap(NULL, size << xbt_pagebits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
56   if (memory==MAP_FAILED) {
57     xbt_die("Could not mmap initial snapshot pages.");
58   }
59
60   this->top_index_ = 0;
61   this->capacity_ = size;
62   this->memory_ = memory;
63   this->page_counts_.resize(size);
64 }
65
66 s_mc_pages_store::~s_mc_pages_store()
67 {
68   ::munmap(this->memory_, this->capacity_ << xbt_pagebits);
69 }
70
71 void s_mc_pages_store::resize(size_t size)
72 {
73   size_t old_bytesize = this->capacity_ << xbt_pagebits;
74   size_t new_bytesize = size << xbt_pagebits;
75
76   // Expand the memory region by moving it into another
77   // virtual memory address if necessary:
78   void* new_memory = mremap(this->memory_, old_bytesize, new_bytesize, MREMAP_MAYMOVE);
79   if (new_memory == MAP_FAILED) {
80     xbt_die("Could not mremap snapshot pages.");
81   }
82
83   this->capacity_ = size;
84   this->memory_ = new_memory;
85   this->page_counts_.resize(size, 0);
86 }
87
88 /** Allocate a free page
89  *
90  *  @return index of the free page
91  */
92 size_t s_mc_pages_store::alloc_page()
93 {
94   if (this->free_pages_.empty()) {
95
96     // Expand the region:
97     if (this->top_index_ == this->capacity_) {
98       // All the pages are allocated, we need add more pages:
99       this->resize(2 * this->capacity_);
100     }
101
102     // Use a page from the top:
103     return this->top_index_++;
104
105   } else {
106
107     // Use a page from free_pages_ (inside of the region):
108     size_t res = this->free_pages_[this->free_pages_.size() - 1];
109     this->free_pages_.pop_back();
110     return res;
111
112   }
113 }
114
115 void s_mc_pages_store::remove_page(size_t pageno)
116 {
117   this->free_pages_.push_back(pageno);
118   const void* page = this->get_page(pageno);
119   uint64_t hash = mc_hash_page(page);
120   this->hash_index_[hash].erase(pageno);
121 }
122
123 /** Store a page in memory */
124 size_t s_mc_pages_store::store_page(void* page)
125 {
126   xbt_assert(mc_page_offset(page)==0, "Not at the beginning of a page");
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   uint64_t hash = mc_hash_page(page);
135   page_set_type& page_set = this->hash_index_[hash];
136   BOOST_FOREACH (size_t pageno, page_set) {
137     const void* snapshot_page = this->get_page(pageno);
138     if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
139
140       // If a page with the same content is already in the page store it is
141       // reused and its reference count is incremented.
142       page_counts_[pageno]++;
143       return pageno;
144
145     }
146   }
147
148   // Otherwise, a new page is allocated in the page store and the content
149   // of the page is `memcpy()`-ed to this new page.
150   size_t pageno = alloc_page();
151   xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
152   void* snapshot_page = (void*) this->get_page(pageno);
153   memcpy(snapshot_page, page, xbt_pagesize);
154   page_set.insert(pageno);
155   page_counts_[pageno]++;
156   return pageno;
157 }
158
159 // ***** Main C API
160
161 extern "C" {
162
163 mc_pages_store_t mc_pages_store_new()
164 {
165   return new s_mc_pages_store_t(500);
166 }
167
168 }