Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
leak --
[simgrid.git] / src / smpi / smpi_bench.cpp
1 /* Copyright (c) 2007, 2009-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 <cstring>
8
9 #include <unordered_map>
10 #include <utility>
11
12 #include "src/internal_config.h"
13 #include "private.h"
14 #include "private.hpp"
15 #include "xbt/dict.h"
16 #include "xbt/sysdep.h"
17 #include "xbt/ex.h"
18 #include "surf/surf.h"
19 #include "simgrid/sg_config.h"
20 #include "simgrid/modelchecker.h"
21 #include "src/mc/mc_replay.h"
22
23 #ifndef WIN32
24 #include <sys/mman.h>
25 #endif
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <math.h> // sqrt
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdio.h>
34
35 #if HAVE_PAPI
36 #include <papi.h>
37 #endif
38
39 #ifndef MAP_ANONYMOUS
40 #define MAP_ANONYMOUS MAP_ANON
41 #endif
42
43 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_bench, smpi, "Logging specific to SMPI (benchmarking)");
44
45 /* Shared allocations are handled through shared memory segments.
46  * Associated data and metadata are used as follows:
47  *
48  *                                                                    mmap #1
49  *    `allocs' dict                                                     ---- -.
50  *    ----------      shared_data_t               shared_metadata_t   / |  |  |
51  * .->| <name> | ---> -------------------- <--.   -----------------   | |  |  |
52  * |  ----------      | fd of <name>     |    |   | size of mmap  | --| |  |  |
53  * |                  | count (2)        |    |-- | data          |   \ |  |  |
54  * `----------------- | <name>           |    |   -----------------     ----  |
55  *                    --------------------    |   ^                           |
56  *                                            |   |                           |
57  *                                            |   |   `allocs_metadata' dict  |
58  *                                            |   |   ----------------------  |
59  *                                            |   `-- | <addr of mmap #1>  |<-'
60  *                                            |   .-- | <addr of mmap #2>  |<-.
61  *                                            |   |   ----------------------  |
62  *                                            |   |                           |
63  *                                            |   |                           |
64  *                                            |   |                           |
65  *                                            |   |                   mmap #2 |
66  *                                            |   v                     ---- -'
67  *                                            |   shared_metadata_t   / |  |
68  *                                            |   -----------------   | |  |
69  *                                            |   | size of mmap  | --| |  |
70  *                                            `-- | data          |   | |  |
71  *                                                -----------------   | |  |
72  *                                                                    \ |  |
73  *                                                                      ----
74  */
75
76 #define PTR_STRLEN (2 + 2 * sizeof(void*) + 1)
77
78 xbt_dict_t samples = nullptr;         /* Allocated on first use */
79 xbt_dict_t calls = nullptr;           /* Allocated on first use */
80
81 double smpi_cpu_threshold;
82 double smpi_running_power;
83
84 int smpi_loaded_page = -1;
85 char* smpi_start_data_exe = nullptr;
86 int smpi_size_data_exe = 0;
87 bool smpi_privatize_global_variables;
88 double smpi_total_benched_time = 0;
89 smpi_privatisation_region_t smpi_privatisation_regions;
90
91 namespace {
92
93 /** Some location in the source code
94  *
95  *  This information is used by SMPI_SHARED_MALLOC to allocate  some shared memory for all simulated processes.
96  */
97 class smpi_source_location {
98 public:
99   smpi_source_location(const char* filename, int line)
100     : filename(filename), filename_length(strlen(filename)), line(line) {}
101
102   /** Pointer to a static string containing the file name */
103   const char* filename = nullptr;
104   int filename_length = 0;
105   int line = 0;
106
107   bool operator==(smpi_source_location const& that) const
108   {
109     return filename_length == that.filename_length
110       && line == that.line
111       && std::memcmp(filename, that.filename, filename_length) == 0;
112   }
113   bool operator!=(smpi_source_location const& that) const
114   {
115     return !(*this == that);
116   }
117 };
118
119 }
120
121 namespace std {
122
123 template<>
124 class hash<smpi_source_location> {
125 public:
126   typedef smpi_source_location argument_type;
127   typedef std::size_t result_type;
128   result_type operator()(smpi_source_location const& loc) const
129   {
130     return xbt_str_hash_ext(loc.filename, loc.filename_length)
131       ^ xbt_str_hash_ext((const char*) &loc.line, sizeof(loc.line));
132   }
133 };
134
135 }
136
137 namespace {
138
139 typedef struct {
140   int fd = -1;
141   int count = 0;
142 } shared_data_t;
143
144 std::unordered_map<smpi_source_location, shared_data_t> allocs;
145 typedef std::unordered_map<smpi_source_location, shared_data_t>::value_type shared_data_key_type;
146
147 typedef struct  {
148   size_t size;
149   shared_data_key_type* data;
150 } shared_metadata_t;
151
152 std::unordered_map<void*, shared_metadata_t> allocs_metadata;
153
154 }
155
156 static size_t shm_size(int fd) {
157   struct stat st;
158
159   if(fstat(fd, &st) < 0) {
160     xbt_die("Could not stat fd %d: %s", fd, strerror(errno));
161   }
162   return static_cast<size_t>(st.st_size);
163 }
164
165 #ifndef WIN32
166 static void* shm_map(int fd, size_t size, shared_data_key_type* data) {
167   void* mem;
168   char loc[PTR_STRLEN];
169   shared_metadata_t meta;
170
171   if(size > shm_size(fd) && (ftruncate(fd, static_cast<off_t>(size)) < 0)) {
172       xbt_die("Could not truncate fd %d to %zu: %s", fd, size, strerror(errno));
173   }
174
175   mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
176   if(mem == MAP_FAILED) {
177     xbt_die("Could not map fd %d: %s", fd, strerror(errno));
178   }
179   snprintf(loc, PTR_STRLEN, "%p", mem);
180   meta.size = size;
181   meta.data = data;
182   allocs_metadata[mem] = meta;
183   XBT_DEBUG("MMAP %zu to %p", size, mem);
184   return mem;
185 }
186 #endif
187
188 void smpi_bench_destroy(void)
189 {
190   allocs.clear();
191   allocs_metadata.clear();
192   xbt_dict_free(&samples);
193   xbt_dict_free(&calls);
194 }
195
196 extern "C" XBT_PUBLIC(void) smpi_execute_flops_(double *flops);
197 void smpi_execute_flops_(double *flops)
198 {
199   smpi_execute_flops(*flops);
200 }
201
202 extern "C" XBT_PUBLIC(void) smpi_execute_(double *duration);
203 void smpi_execute_(double *duration)
204 {
205   smpi_execute(*duration);
206 }
207
208 void smpi_execute_flops(double flops) {
209   smx_synchro_t action;
210   XBT_DEBUG("Handle real computation time: %f flops", flops);
211   action = simcall_execution_start("computation", flops, 1, 0, 0);
212   simcall_set_category (action, TRACE_internal_smpi_get_category());
213   simcall_execution_wait(action);
214   smpi_switch_data_segment(smpi_process_index());
215 }
216
217 void smpi_execute(double duration)
218 {
219   if (duration >= smpi_cpu_threshold) {
220     XBT_DEBUG("Sleep for %g to handle real computation time", duration);
221     double flops = duration * smpi_running_power;
222     int rank = smpi_process_index();
223     instr_extra_data extra = xbt_new0(s_instr_extra_data_t,1);
224     extra->type=TRACING_COMPUTING;
225     extra->comp_size=flops;
226     TRACE_smpi_computing_in(rank, extra);
227     smpi_execute_flops(flops);
228
229     TRACE_smpi_computing_out(rank);
230
231   } else {
232     XBT_DEBUG("Real computation took %g while option smpi/cpu_threshold is set to %g => ignore it",
233               duration, smpi_cpu_threshold);
234   }
235 }
236
237 void smpi_bench_begin(void)
238 {
239   if (smpi_privatize_global_variables) {
240     smpi_switch_data_segment(smpi_process_index());
241   }
242
243   if (MC_is_active() || MC_record_replay_is_active())
244     return;
245
246 #if HAVE_PAPI
247   if (xbt_cfg_get_string("smpi/papi-events")[0] != '\0') {
248     int event_set = smpi_process_papi_event_set();
249     // PAPI_start sets everything to 0! See man(3) PAPI_start
250     if (PAPI_LOW_LEVEL_INITED == PAPI_is_initialized()) {
251       if (PAPI_start(event_set) != PAPI_OK) {
252         // TODO This needs some proper handling.
253         XBT_CRITICAL("Could not start PAPI counters.\n");
254         xbt_die("Error.");
255       }
256     }
257   }
258 #endif
259   xbt_os_threadtimer_start(smpi_process_timer());
260 }
261
262 void smpi_bench_end(void)
263 {
264   if (MC_is_active() || MC_record_replay_is_active())
265     return;
266
267   double speedup = 1;
268   xbt_os_timer_t timer = smpi_process_timer();
269   xbt_os_threadtimer_stop(timer);
270
271 #if HAVE_PAPI
272   /**
273    * An MPI function has been called and now is the right time to update
274    * our PAPI counters for this process.
275    */
276   if (xbt_cfg_get_string("smpi/papi-events")[0] != '\0') {
277     papi_counter_t& counter_data        = smpi_process_papi_counters();
278     int event_set                       = smpi_process_papi_event_set();
279     std::vector<long long> event_values = std::vector<long long>(counter_data.size());
280
281     if (PAPI_stop(event_set, &event_values[0]) != PAPI_OK) { // Error
282       XBT_CRITICAL("Could not stop PAPI counters.\n");
283       xbt_die("Error.");
284     } else {
285       for (unsigned int i = 0; i < counter_data.size(); i++) {
286         counter_data[i].second += event_values[i];
287         // XBT_DEBUG("[%i] PAPI: Counter %s: Value is now %lli (got increment by %lli\n", smpi_process_index(),
288         // counter_data[i].first.c_str(), counter_data[i].second, event_values[i]);
289       }
290     }
291   }
292 #endif
293
294   if (smpi_process_get_sampling()) {
295     XBT_CRITICAL("Cannot do recursive benchmarks.");
296     XBT_CRITICAL("Are you trying to make a call to MPI within a SMPI_SAMPLE_ block?");
297     xbt_backtrace_display_current();
298     xbt_die("Aborting.");
299   }
300
301   if (xbt_cfg_get_string("smpi/comp-adjustment-file")[0] != '\0') { // Maybe we need to artificially speed up or slow
302                                                          // down our computation based on our statistical analysis.
303
304     smpi_trace_call_location_t* loc                            = smpi_process_get_call_location();
305     std::string key                                            = loc->get_composed_key();
306     std::unordered_map<std::string, double>::const_iterator it = location2speedup.find(key);
307     if (it != location2speedup.end()) {
308       speedup = it->second;
309     }
310   }
311   
312   // Simulate the benchmarked computation unless disabled via command-line argument
313   if (xbt_cfg_get_boolean("smpi/simulate-computation")) {
314     smpi_execute(xbt_os_timer_elapsed(timer)/speedup);
315   }
316
317 #if HAVE_PAPI
318   if (xbt_cfg_get_string("smpi/papi-events")[0] != '\0' && TRACE_smpi_is_enabled()) {
319     char container_name[INSTR_DEFAULT_STR_SIZE];
320     smpi_container(smpi_process_index(), container_name, INSTR_DEFAULT_STR_SIZE);
321     container_t container        = PJ_container_get(container_name);
322     papi_counter_t& counter_data = smpi_process_papi_counters();
323
324     for (auto& pair : counter_data) {
325       new_pajeSetVariable(surf_get_clock(), container,
326                           PJ_type_get(/* countername */ pair.first.c_str(), container->type), pair.second);
327     }
328   }
329 #endif
330
331   smpi_total_benched_time += xbt_os_timer_elapsed(timer);
332 }
333
334 /* Private sleep function used by smpi_sleep() and smpi_usleep() */
335 static unsigned int private_sleep(double secs)
336 {
337   smpi_bench_end();
338
339   XBT_DEBUG("Sleep for: %lf secs", secs);
340   int rank = smpi_comm_rank(MPI_COMM_WORLD);
341   instr_extra_data extra = xbt_new0(s_instr_extra_data_t,1);
342   extra->type=TRACING_SLEEPING;
343   extra->sleep_duration=secs;
344   TRACE_smpi_sleeping_in(rank, extra);
345
346   simcall_process_sleep(secs);
347
348   TRACE_smpi_sleeping_out(rank);
349
350   smpi_bench_begin();
351   return 0;
352 }
353
354 unsigned int smpi_sleep(unsigned int secs)
355 {
356   return private_sleep(static_cast<double>(secs));
357 }
358
359 int smpi_usleep(useconds_t usecs)
360 {
361   return static_cast<int>(private_sleep(static_cast<double>(usecs) / 1000000.0));
362 }
363
364 #if _POSIX_TIMERS > 0
365 int smpi_nanosleep(const struct timespec *tp, struct timespec * t)
366 {
367   return static_cast<int>(private_sleep(static_cast<double>(tp->tv_sec + tp->tv_nsec / 1000000000.0)));
368 }
369 #endif
370
371 int smpi_gettimeofday(struct timeval *tv, void* tz)
372 {
373   double now;
374   smpi_bench_end();
375   now = SIMIX_get_clock();
376   if (tv) {
377     tv->tv_sec = static_cast<time_t>(now);
378 #ifdef WIN32
379     tv->tv_usec = static_cast<useconds_t>((now - tv->tv_sec) * 1e6);
380 #else
381     tv->tv_usec = static_cast<suseconds_t>((now - tv->tv_sec) * 1e6);
382 #endif
383   }
384   smpi_bench_begin();
385   return 0;
386 }
387
388 #if _POSIX_TIMERS > 0
389 int smpi_clock_gettime(clockid_t clk_id, struct timespec *tp)
390 {
391   //there is only one time in SMPI, so clk_id is ignored.
392   double now;
393   smpi_bench_end();
394   now = SIMIX_get_clock();
395   if (tp) {
396     tp->tv_sec = static_cast<time_t>(now);
397     tp->tv_nsec = static_cast<long int>((now - tp->tv_sec) * 1e9);
398   }
399   smpi_bench_begin();
400   return 0;
401 }
402 #endif
403
404 extern double sg_surf_precision;
405 unsigned long long smpi_rastro_resolution (void)
406 {
407   smpi_bench_end();
408   double resolution = (1/sg_surf_precision);
409   smpi_bench_begin();
410   return static_cast<unsigned long long>(resolution);
411 }
412
413 unsigned long long smpi_rastro_timestamp (void)
414 {
415   smpi_bench_end();
416   double now = SIMIX_get_clock();
417
418   unsigned long long sec = (unsigned long long)now;
419   unsigned long long pre = (now - sec) * smpi_rastro_resolution();
420   smpi_bench_begin();
421   return static_cast<unsigned long long>(sec) * smpi_rastro_resolution() + pre;
422 }
423
424 /* ****************************** Functions related to the SMPI_SAMPLE_ macros ************************************/
425 typedef struct {
426   double threshold; /* maximal stderr requested (if positive) */
427   double relstderr; /* observed stderr so far */
428   double mean;      /* mean of benched times, to be used if the block is disabled */
429   double sum;       /* sum of benched times (to compute the mean and stderr) */
430   double sum_pow2;  /* sum of the square of the benched times (to compute the stderr) */
431   int iters;        /* amount of requested iterations */
432   int count;        /* amount of iterations done so far */
433   int benching;     /* 1: we are benchmarking; 0: we have enough data, no bench anymore */
434 } local_data_t;
435
436 static char *sample_location(int global, const char *file, int line) {
437   if (global) {
438     return bprintf("%s:%d", file, line);
439   } else {
440     return bprintf("%s:%d:%d", file, line, smpi_process_index());
441   }
442 }
443
444 static int sample_enough_benchs(local_data_t *data) {
445   int res = data->count >= data->iters;
446   if (data->threshold>0.0) {
447     if (data->count <2)
448       res = 0; // not enough data
449     if (data->relstderr > data->threshold)
450       res = 0; // stderr too high yet
451   }
452   XBT_DEBUG("%s (count:%d iter:%d stderr:%f thres:%f mean:%fs)",
453       (res?"enough benchs":"need more data"), data->count, data->iters, data->relstderr, data->threshold, data->mean);
454   return res;
455 }
456
457 void smpi_sample_1(int global, const char *file, int line, int iters, double threshold)
458 {
459   char *loc = sample_location(global, file, line);
460   local_data_t *data;
461
462   smpi_bench_end();     /* Take time from previous, unrelated computation into account */
463   smpi_process_set_sampling(1);
464
465   if (samples==nullptr)
466     samples = xbt_dict_new_homogeneous(free);
467
468   data = static_cast<local_data_t *>(xbt_dict_get_or_null(samples, loc));
469   if (data==nullptr) {
470     xbt_assert(threshold>0 || iters>0,
471         "You should provide either a positive amount of iterations to bench, or a positive maximal stderr (or both)");
472     data = static_cast<local_data_t *>( xbt_new(local_data_t, 1));
473     data->count = 0;
474     data->sum = 0.0;
475     data->sum_pow2 = 0.0;
476     data->iters = iters;
477     data->threshold = threshold;
478     data->benching = 1; // If we have no data, we need at least one
479     data->mean = 0;
480     xbt_dict_set(samples, loc, data, nullptr);
481     XBT_DEBUG("XXXXX First time ever on benched nest %s.",loc);
482   } else {
483     if (data->iters != iters || data->threshold != threshold) {
484       XBT_ERROR("Asked to bench block %s with different settings %d, %f is not %d, %f. "
485                 "How did you manage to give two numbers at the same line??",
486                 loc, data->iters, data->threshold, iters,threshold);
487       THROW_IMPOSSIBLE;
488     }
489
490     // if we already have some data, check whether sample_2 should get one more bench or whether it should emulate
491     // the computation instead
492     data->benching = (sample_enough_benchs(data) == 0);
493     XBT_DEBUG("XXXX Re-entering the benched nest %s. %s",loc,
494              (data->benching?"more benching needed":"we have enough data, skip computes"));
495   }
496   xbt_free(loc);
497 }
498
499 int smpi_sample_2(int global, const char *file, int line)
500 {
501   char *loc = sample_location(global, file, line);
502   local_data_t *data;
503   int res;
504
505   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
506   data = static_cast<local_data_t *>(xbt_dict_get(samples, loc));
507   XBT_DEBUG("sample2 %s",loc);
508   xbt_free(loc);
509
510   if (data->benching==1) {
511     // we need to run a new bench
512     XBT_DEBUG("benchmarking: count:%d iter:%d stderr:%f thres:%f; mean:%f",
513         data->count, data->iters, data->relstderr, data->threshold, data->mean);
514     res = 1;
515   } else {
516     // Enough data, no more bench (either we got enough data from previous visits to this benched nest, or we just
517     //ran one bench and need to bail out now that our job is done). Just sleep instead
518     XBT_DEBUG("No benchmark (either no need, or just ran one): count >= iter (%d >= %d) or stderr<thres (%f<=%f)."
519               " apply the %fs delay instead", data->count, data->iters, data->relstderr, data->threshold, data->mean);
520     smpi_execute(data->mean);
521     smpi_process_set_sampling(0);
522     res = 0; // prepare to capture future, unrelated computations
523   }
524   smpi_bench_begin();
525   return res;
526 }
527
528 void smpi_sample_3(int global, const char *file, int line)
529 {
530   char *loc = sample_location(global, file, line);
531   local_data_t *data;
532
533   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
534   data = static_cast<local_data_t *>(xbt_dict_get(samples, loc));
535   XBT_DEBUG("sample3 %s",loc);
536   xbt_free(loc);
537
538   if (data->benching==0) {
539     THROW_IMPOSSIBLE;
540   }
541
542   // ok, benchmarking this loop is over
543   xbt_os_threadtimer_stop(smpi_process_timer());
544
545   // update the stats
546   double sample, n;
547   data->count++;
548   sample = xbt_os_timer_elapsed(smpi_process_timer());
549   data->sum += sample;
550   data->sum_pow2 += sample * sample;
551   n = static_cast<double>(data->count);
552   data->mean = data->sum / n;
553   data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
554   if (sample_enough_benchs(data)==0) {
555     data->mean = sample; // Still in benching process; We want sample_2 to simulate the exact time of this loop
556                          // occurrence before leaving, not the mean over the history
557   }
558   XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
559       data->mean, data->relstderr, sample);
560
561   // That's enough for now, prevent sample_2 to run the same code over and over
562   data->benching = 0;
563 }
564
565 #ifndef WIN32
566
567 void *smpi_shared_malloc(size_t size, const char *file, int line)
568 {
569   void* mem;
570   if (xbt_cfg_get_boolean("smpi/use-shared-malloc")){
571     int fd;
572     smpi_source_location loc(file, line);
573     auto res = allocs.insert(std::make_pair(loc, shared_data_t()));
574     auto data = res.first;
575     if (res.second) {
576       // The insertion did not take place.
577       // Generate a shared memory name from the address of the shared_data:
578       char shmname[32]; // cannot be longer than PSHMNAMLEN = 31 on Mac OS X (shm_open raises ENAMETOOLONG otherwise)
579       snprintf(shmname, 31, "/shmalloc%p", &*data);
580       fd = shm_open(shmname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
581       if (fd < 0) {
582         if(errno==EEXIST)
583           xbt_die("Please cleanup /dev/shm/%s", shmname);
584         else
585           xbt_die("An unhandled error occurred while opening %s. shm_open: %s", shmname, strerror(errno));
586       }
587       data->second.fd = fd;
588       data->second.count = 1;
589       mem = shm_map(fd, size, &*data);
590       if (shm_unlink(shmname) < 0) {
591         XBT_WARN("Could not early unlink %s. shm_unlink: %s", shmname, strerror(errno));
592       }
593       XBT_DEBUG("Mapping %s at %p through %d", shmname, mem, fd);
594     } else {
595       mem = shm_map(data->second.fd, size, &*data);
596       data->second.count++;
597     }
598     XBT_DEBUG("Shared malloc %zu in %p (metadata at %p)", size, mem, &*data);
599   } else {
600     mem = xbt_malloc(size);
601     XBT_DEBUG("Classic malloc %zu in %p", size, mem);
602   }
603
604   return mem;
605 }
606
607 void smpi_shared_free(void *ptr)
608 {
609   char loc[PTR_STRLEN];
610
611   if (xbt_cfg_get_boolean("smpi/use-shared-malloc")){
612     snprintf(loc, PTR_STRLEN, "%p", ptr);
613     auto meta = allocs_metadata.find(ptr);
614     if (meta == allocs_metadata.end()) {
615       XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
616       return;
617     }
618     shared_data_t* data = &meta->second.data->second;
619     if (munmap(ptr, meta->second.size) < 0) {
620       XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
621     }
622     data->count--;
623     XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
624     if (data->count <= 0) {
625       close(data->fd);
626       allocs.erase(allocs.find(meta->second.data->first));
627       XBT_DEBUG("Shared free - with removal - of %p", ptr);
628     }
629   }else{
630     XBT_DEBUG("Classic free of %p", ptr);
631     xbt_free(ptr);
632   }
633 }
634 #endif
635
636 int smpi_shared_known_call(const char* func, const char* input)
637 {
638   char* loc = bprintf("%s:%s", func, input);
639   int known = 0;
640
641   if (calls==nullptr) {
642     calls = xbt_dict_new_homogeneous(nullptr);
643   }
644   try {
645     xbt_dict_get(calls, loc); /* Succeed or throw */
646     known = 1;
647     xbt_free(loc);
648   }
649   catch (xbt_ex& ex) {
650     xbt_free(loc);
651     if (ex.category != not_found_error)
652       throw;
653   }
654   catch(...) {
655     xbt_free(loc);
656     throw;
657   }
658   return known;
659 }
660
661 void* smpi_shared_get_call(const char* func, const char* input) {
662    char* loc = bprintf("%s:%s", func, input);
663    void* data;
664
665    if(calls==nullptr) {
666       calls = xbt_dict_new_homogeneous(nullptr);
667    }
668    data = xbt_dict_get(calls, loc);
669    xbt_free(loc);
670    return data;
671 }
672
673 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
674    char* loc = bprintf("%s:%s", func, input);
675
676    if(calls==0) {
677       calls = xbt_dict_new_homogeneous(nullptr);
678    }
679    xbt_dict_set(calls, loc, data, nullptr);
680    xbt_free(loc);
681    return data;
682 }
683
684
685 /** Map a given SMPI privatization segment (make a SMPI process active) */
686 void smpi_switch_data_segment(int dest) {
687   if (smpi_loaded_page == dest)//no need to switch, we've already loaded the one we want
688     return;
689
690   // So the job:
691   smpi_really_switch_data_segment(dest);
692 }
693
694 /** Map a given SMPI privatization segment (make a SMPI process active)  even if SMPI thinks it is already active
695  *
696  *  When doing a state restoration, the state of the restored variables  might not be consistent with the state of the
697  *  virtual memory. In this case, we to change the data segment.
698  */
699 void smpi_really_switch_data_segment(int dest) {
700   if(smpi_size_data_exe == 0)//no need to switch
701     return;
702
703 #if HAVE_PRIVATIZATION
704   if(smpi_loaded_page==-1){//initial switch, do the copy from the real page here
705     for (int i=0; i< smpi_process_count(); i++){
706       memcpy(smpi_privatisation_regions[i].address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
707     }
708   }
709
710   // FIXME, cross-process support (mmap across process when necessary)
711   int current = smpi_privatisation_regions[dest].file_descriptor;
712   XBT_DEBUG("Switching data frame to the one of process %d", dest);
713   void* tmp = mmap (TOPAGE(smpi_start_data_exe), smpi_size_data_exe,
714     PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, current, 0);
715   if (tmp != TOPAGE(smpi_start_data_exe))
716     xbt_die("Couldn't map the new region");
717   smpi_loaded_page = dest;
718 #endif
719 }
720
721 int smpi_is_privatisation_file(char* file)
722 {
723   return strncmp("/dev/shm/my-buffer-", file, std::strlen("/dev/shm/my-buffer-")) == 0;
724 }
725
726 void smpi_initialize_global_memory_segments(){
727
728 #if !HAVE_PRIVATIZATION
729   smpi_privatize_global_variables=false;
730   xbt_die("You are trying to use privatization on a system that does not support it. Don't.");
731   return;
732 #else
733
734   smpi_get_executable_global_size();
735
736   XBT_DEBUG ("bss+data segment found : size %d starting at %p", smpi_size_data_exe, smpi_start_data_exe );
737
738   if (smpi_size_data_exe == 0){//no need to switch
739     smpi_privatize_global_variables=false;
740     return;
741   }
742
743   smpi_privatisation_regions =
744     static_cast<smpi_privatisation_region_t>( xbt_malloc(smpi_process_count() * sizeof(struct s_smpi_privatisation_region)));
745
746   for (int i=0; i< smpi_process_count(); i++){
747       //create SIMIX_process_count() mappings of this size with the same data inside
748       void *address = nullptr;
749       char path[] = "/dev/shm/my-buffer-XXXXXX";
750       int status;
751
752       int file_descriptor= mkstemp (path);
753       if (file_descriptor < 0) {
754         if (errno==EMFILE) {
755           xbt_die("Impossible to create temporary file for memory mapping: %s\n\
756 The open() system call failed with the EMFILE error code (too many files). \n\n\
757 This means that you reached the system limits concerning the amount of files per process. \
758 This is not a surprise if you are trying to virtualize many processes on top of SMPI. \
759 Don't panic -- you should simply increase your system limits and try again. \n\n\
760 First, check what your limits are:\n\
761   cat /proc/sys/fs/file-max # Gives you the system-wide limit\n\
762   ulimit -Hn                # Gives you the per process hard limit\n\
763   ulimit -Sn                # Gives you the per process soft limit\n\
764   cat /proc/self/limits     # Displays any per-process limitation (including the one given above)\n\n\
765 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. \
766 Ask the Internet about tutorials on how to increase the files limit such as: https://rtcamp.com/tutorials/linux/increase-open-files-limit/",
767              strerror(errno));
768         }
769         xbt_die("Impossible to create temporary file for memory mapping: %s",
770             strerror(errno));
771       }
772
773       status = unlink (path);
774       if (status)
775         xbt_die("Impossible to unlink temporary file for memory mapping");
776
777       status = ftruncate(file_descriptor, smpi_size_data_exe);
778       if(status)
779         xbt_die("Impossible to set the size of the temporary file for memory mapping");
780
781       /* Ask for a free region */
782       address = mmap (nullptr, smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, 0);
783       if (address == MAP_FAILED)
784         xbt_die("Couldn't find a free region for memory mapping");
785
786       //initialize the values
787       memcpy(address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
788
789       //store the address of the mapping for further switches
790       smpi_privatisation_regions[i].file_descriptor = file_descriptor;
791       smpi_privatisation_regions[i].address = address;
792   }
793 #endif
794 }
795
796 void smpi_destroy_global_memory_segments(){
797   if (smpi_size_data_exe == 0)//no need to switch
798     return;
799 #if HAVE_PRIVATIZATION
800   int i;
801   for (i=0; i< smpi_process_count(); i++){
802     if(munmap(smpi_privatisation_regions[i].address, smpi_size_data_exe) < 0) {
803       XBT_WARN("Unmapping of fd %d failed: %s", smpi_privatisation_regions[i].file_descriptor, strerror(errno));
804     }
805     close(smpi_privatisation_regions[i].file_descriptor);
806   }
807   xbt_free(smpi_privatisation_regions);
808 #endif
809 }
810
811 extern "C" { /** These functions will be called from the user code **/
812   smpi_trace_call_location_t* smpi_trace_get_call_location() {
813     return smpi_process_get_call_location();
814   }
815
816   void smpi_trace_set_call_location(const char* file, const int line) {
817     smpi_trace_call_location_t* loc = smpi_process_get_call_location();
818
819     loc->previous_filename   = loc->filename;
820     loc->previous_linenumber = loc->linenumber;
821     loc->filename            = file;
822     loc->linenumber          = line;
823   }
824
825   /**
826    * Required for Fortran bindings
827    */
828   void smpi_trace_set_call_location_(const char* file, int* line) {
829     smpi_trace_set_call_location(file, *line);
830   }
831
832   /** 
833    * Required for Fortran if -fsecond-underscore is activated
834    */
835   void smpi_trace_set_call_location__(const char* file, int* line) {
836     smpi_trace_set_call_location(file, *line);
837   }
838 }