Logo AND Algorithmique Numérique Distribuée

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