Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
642f46a45d81f2c4a28d04342c73eb606c4f33ce
[simgrid.git] / src / smpi / internals / smpi_memory.cpp
1 /* Copyright (c) 2015-2017. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include <cstdint>
7 #include <climits>
8 #include <cstring>
9
10 #include <vector>
11
12 #include <cerrno>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19
20 #ifndef WIN32
21 #include <sys/mman.h>
22 #include <unistd.h>
23
24 #include "src/internal_config.h"
25 #include "src/xbt/memory_map.hpp"
26
27 #include "private.hpp"
28 #include "smpi_process.hpp"
29
30 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_memory, smpi, "Memory layout support for SMPI");
31
32 int smpi_loaded_page      = -1;
33 char* smpi_data_exe_start = nullptr;
34 int smpi_data_exe_size    = 0;
35 int smpi_privatize_global_variables;
36 static char* smpi_data_exe_copy;
37
38 // We keep a copy of all the privatization regions: We can then delete everything easily by iterating over this
39 // collection and nothing can be leaked. We could also iterate over all actors but we would have to be diligent when two
40 // actors use the same privatization region (so, smart pointers would have to be used etc.)
41 static std::set<smpi_privatization_region_t> smpi_privatization_regions;
42
43 static const int PROT_RWX = (PROT_READ | PROT_WRITE | PROT_EXEC);
44 static const int PROT_RW  = (PROT_READ | PROT_WRITE );
45 XBT_ATTRIB_UNUSED static const int PROT_RX  = (PROT_READ | PROT_EXEC );
46
47 void smpi_get_executable_global_size()
48 {
49   char buffer[PATH_MAX];
50   char* full_name = realpath(xbt_binary_name, buffer);
51   if (full_name == nullptr)
52     xbt_die("Could not resolve binary file name");
53
54   std::vector<simgrid::xbt::VmMap> map = simgrid::xbt::get_memory_map(getpid());
55   for (auto i = map.begin(); i != map.end() ; ++i) {
56     // TODO, In practice, this implementation would not detect a completely
57     // anonymous data segment. This does not happen in practice, however.
58
59     // File backed RW entry:
60     if (i->pathname == full_name && (i->prot & PROT_RWX) == PROT_RW) {
61       smpi_data_exe_start = (char*)i->start_addr;
62       smpi_data_exe_size  = i->end_addr - i->start_addr;
63       ++i;
64       /* Here we are making the assumption that a suitable empty region
65          following the rw- area is the end of the data segment. It would
66          be better to check with the size of the data segment. */
67       if (i != map.end() && i->pathname.empty() && (i->prot & PROT_RWX) == PROT_RW &&
68           (char*)i->start_addr == smpi_data_exe_start + smpi_data_exe_size) {
69         smpi_data_exe_size = (char*)i->end_addr - smpi_data_exe_start;
70       }
71       return;
72     }
73   }
74   xbt_die("Did not find my data segment.");
75 }
76 #endif
77
78 #if HAVE_SANITIZE_ADDRESS
79 #include <sanitizer/asan_interface.h>
80 static void* asan_safe_memcpy(void* dest, void* src, size_t n)
81 {
82   char* psrc  = static_cast<char*>(src);
83   char* pdest = static_cast<char*>(dest);
84   for (size_t i = 0; i < n;) {
85     while (i < n && __asan_address_is_poisoned(psrc + i))
86       ++i;
87     if (i < n) {
88       char* p  = static_cast<char*>(__asan_region_is_poisoned(psrc + i, n - i));
89       size_t j = p ? (p - psrc) : n;
90       memcpy(pdest + i, psrc + i, j - i);
91       i = j;
92     }
93   }
94   return dest;
95 }
96 #else
97 #define asan_safe_memcpy(dest, src, n) memcpy(dest, src, n)
98 #endif
99
100 /** Map a given SMPI privatization segment (make a SMPI process active) */
101 void smpi_switch_data_segment(int dest) {
102   if (smpi_loaded_page == dest)//no need to switch, we've already loaded the one we want
103     return;
104
105   // So the job:
106   smpi_really_switch_data_segment(dest);
107 }
108
109 /** Map a given SMPI privatization segment (make a SMPI process active)  even if SMPI thinks it is already active
110  *
111  *  When doing a state restoration, the state of the restored variables  might not be consistent with the state of the
112  *  virtual memory. In this case, we to change the data segment.
113  */
114 void smpi_really_switch_data_segment(int dest)
115 {
116   if (smpi_data_exe_size == 0) // no need to switch
117     return;
118
119 #if HAVE_PRIVATIZATION
120   // FIXME, cross-process support (mmap across process when necessary)
121   simgrid::smpi::Process* process = smpi_process_remote(dest);
122   int current                     = process->privatized_region()->file_descriptor;
123   XBT_DEBUG("Switching data frame to the one of process %d", dest);
124   void* tmp =
125       mmap(TOPAGE(smpi_data_exe_start), smpi_data_exe_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, current, 0);
126   if (tmp != TOPAGE(smpi_data_exe_start))
127     xbt_die("Couldn't map the new region (errno %d): %s", errno, strerror(errno));
128   smpi_loaded_page = dest;
129 #endif
130 }
131
132 int smpi_is_privatization_file(char* file)
133 {
134   const std::string buffer_path("/dev/shm/my-buffer-");
135   return buffer_path.compare(0, std::string::npos, file, buffer_path.length()) == 0;
136 }
137
138 // TODO: cheinrich: The behavior changed; this now only makes a backup of the
139 // data segment. I think the function should be renamed.
140 void smpi_initialize_global_memory_segments()
141 {
142 #if HAVE_PRIVATIZATION
143   smpi_get_executable_global_size();
144
145   XBT_DEBUG("bss+data segment found : size %d starting at %p", smpi_data_exe_size, smpi_data_exe_start);
146
147   if (smpi_data_exe_size == 0) { // no need to switch as global variables don't exist
148     smpi_privatize_global_variables=false;
149     return;
150   }
151
152   smpi_data_exe_copy = (char*)malloc(smpi_data_exe_size);
153   // Make a copy of the data segment. This clean copy is retained over the whole runtime
154   // of the simulation and can be used to initialize a dynamically added, new process.
155   asan_safe_memcpy(smpi_data_exe_copy, TOPAGE(smpi_data_exe_start), smpi_data_exe_size);
156 #else /* ! HAVE_PRIVATIZATION */
157   smpi_privatize_global_variables = false;
158   xbt_die("You are trying to use privatization on a system that does not support it. Don't.");
159   return;
160 #endif
161 }
162
163 // Initializes the memory mapping for a single process and returns the privatization region
164 smpi_privatization_region_t smpi_init_global_memory_segment_process()
165 {
166   int file_descriptor;
167   void* address = nullptr;
168   char path[24];
169   int status;
170
171   do {
172     snprintf(path, sizeof(path), "/smpi-buffer-%06x", rand() % 0xffffffU);
173     file_descriptor = shm_open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
174   } while (file_descriptor == -1 && errno == EEXIST);
175   if (file_descriptor < 0) {
176     if (errno == EMFILE) {
177       xbt_die("Impossible to create temporary file for memory mapping: %s\n\
178 The open() system call failed with the EMFILE error code (too many files). \n\n\
179 This means that you reached the system limits concerning the amount of files per process. \
180 This is not a surprise if you are trying to virtualize many processes on top of SMPI. \
181 Don't panic -- you should simply increase your system limits and try again. \n\n\
182 First, check what your limits are:\n\
183   cat /proc/sys/fs/file-max # Gives you the system-wide limit\n\
184   ulimit -Hn                # Gives you the per process hard limit\n\
185   ulimit -Sn                # Gives you the per process soft limit\n\
186   cat /proc/self/limits     # Displays any per-process limitation (including the one given above)\n\n\
187 If one of these values is less than the amount of MPI processes that you try to run, then you got the explanation of this error. \
188 Ask the Internet about tutorials on how to increase the files limit such as: https://rtcamp.com/tutorials/linux/increase-open-files-limit/",
189               strerror(errno));
190     }
191     xbt_die("Impossible to create temporary file for memory mapping: %s", strerror(errno));
192   }
193
194   status = ftruncate(file_descriptor, smpi_data_exe_size);
195   if (status)
196     xbt_die("Impossible to set the size of the temporary file for memory mapping");
197
198   /* Ask for a free region */
199   address = mmap(nullptr, smpi_data_exe_size, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, 0);
200   if (address == MAP_FAILED)
201     xbt_die("Couldn't find a free region for memory mapping");
202
203   status = shm_unlink(path);
204   if (status)
205     xbt_die("Impossible to unlink temporary file for memory mapping");
206
207   // initialize the values
208   asan_safe_memcpy(address, smpi_data_exe_copy, smpi_data_exe_size);
209
210   // store the address of the mapping for further switches
211   smpi_privatization_region_t tmp =
212       static_cast<smpi_privatization_region_t>(new struct s_smpi_privatization_region_t);
213
214   tmp->file_descriptor = file_descriptor;
215   tmp->address         = address;
216   smpi_privatization_regions.insert(tmp);
217
218   return tmp;
219 }
220
221 void smpi_destroy_global_memory_segments(){
222   if (smpi_data_exe_size == 0) // no need to switch
223     return;
224 #if HAVE_PRIVATIZATION
225   for (auto& it : smpi_privatization_regions) {
226     if (munmap(it->address, smpi_data_exe_size) < 0)
227       XBT_WARN("Unmapping of fd %d failed: %s", it->file_descriptor, strerror(errno));
228     close(it->file_descriptor);
229   }
230 #endif
231 }
232