Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Copy the name of the "file", as we don't know where it comes from.
[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(xbt_strdup(filename)), filename_length(strlen(filename)), line(line) {}
102
103   /** Pointer to a static string containing the file name */
104   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 with size %zu: %s", fd, size, 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 (size > 0 && 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 - maybe its size was 0?", 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     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     }else{
629       XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
630     }
631   }else{
632     XBT_DEBUG("Classic free of %p", ptr);
633     xbt_free(ptr);
634   }
635 }
636 #endif
637
638 int smpi_shared_known_call(const char* func, const char* input)
639 {
640   char* loc = bprintf("%s:%s", func, input);
641   int known = 0;
642
643   if (calls==nullptr) {
644     calls = xbt_dict_new_homogeneous(nullptr);
645   }
646   try {
647     xbt_dict_get(calls, loc); /* Succeed or throw */
648     known = 1;
649     xbt_free(loc);
650   }
651   catch (xbt_ex& ex) {
652     xbt_free(loc);
653     if (ex.category != not_found_error)
654       throw;
655   }
656   catch(...) {
657     xbt_free(loc);
658     throw;
659   }
660   return known;
661 }
662
663 void* smpi_shared_get_call(const char* func, const char* input) {
664    char* loc = bprintf("%s:%s", func, input);
665    void* data;
666
667    if(calls==nullptr) {
668       calls = xbt_dict_new_homogeneous(nullptr);
669    }
670    data = xbt_dict_get(calls, loc);
671    xbt_free(loc);
672    return data;
673 }
674
675 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
676    char* loc = bprintf("%s:%s", func, input);
677
678    if(calls==0) {
679       calls = xbt_dict_new_homogeneous(nullptr);
680    }
681    xbt_dict_set(calls, loc, data, nullptr);
682    xbt_free(loc);
683    return data;
684 }
685
686
687 /** Map a given SMPI privatization segment (make a SMPI process active) */
688 void smpi_switch_data_segment(int dest) {
689   if (smpi_loaded_page == dest)//no need to switch, we've already loaded the one we want
690     return;
691
692   // So the job:
693   smpi_really_switch_data_segment(dest);
694 }
695
696 /** Map a given SMPI privatization segment (make a SMPI process active)  even if SMPI thinks it is already active
697  *
698  *  When doing a state restoration, the state of the restored variables  might not be consistent with the state of the
699  *  virtual memory. In this case, we to change the data segment.
700  */
701 void smpi_really_switch_data_segment(int dest) {
702   if(smpi_size_data_exe == 0)//no need to switch
703     return;
704
705 #if HAVE_PRIVATIZATION
706   if(smpi_loaded_page==-1){//initial switch, do the copy from the real page here
707     for (int i=0; i< smpi_process_count(); i++){
708       memcpy(smpi_privatisation_regions[i].address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
709     }
710   }
711
712   // FIXME, cross-process support (mmap across process when necessary)
713   int current = smpi_privatisation_regions[dest].file_descriptor;
714   XBT_DEBUG("Switching data frame to the one of process %d", dest);
715   void* tmp = mmap (TOPAGE(smpi_start_data_exe), smpi_size_data_exe,
716     PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, current, 0);
717   if (tmp != TOPAGE(smpi_start_data_exe))
718     xbt_die("Couldn't map the new region");
719   smpi_loaded_page = dest;
720 #endif
721 }
722
723 int smpi_is_privatisation_file(char* file)
724 {
725   return strncmp("/dev/shm/my-buffer-", file, std::strlen("/dev/shm/my-buffer-")) == 0;
726 }
727
728 void smpi_initialize_global_memory_segments(){
729
730 #if !HAVE_PRIVATIZATION
731   smpi_privatize_global_variables=false;
732   xbt_die("You are trying to use privatization on a system that does not support it. Don't.");
733   return;
734 #else
735
736   smpi_get_executable_global_size();
737
738   XBT_DEBUG ("bss+data segment found : size %d starting at %p", smpi_size_data_exe, smpi_start_data_exe );
739
740   if (smpi_size_data_exe == 0){//no need to switch
741     smpi_privatize_global_variables=false;
742     return;
743   }
744
745   smpi_privatisation_regions =
746     static_cast<smpi_privatisation_region_t>( xbt_malloc(smpi_process_count() * sizeof(struct s_smpi_privatisation_region)));
747
748   for (int i=0; i< smpi_process_count(); i++){
749       //create SIMIX_process_count() mappings of this size with the same data inside
750       void *address = nullptr;
751       char path[] = "/dev/shm/my-buffer-XXXXXX";
752       int status;
753
754       int file_descriptor= mkstemp (path);
755       if (file_descriptor < 0) {
756         if (errno==EMFILE) {
757           xbt_die("Impossible to create temporary file for memory mapping: %s\n\
758 The open() system call failed with the EMFILE error code (too many files). \n\n\
759 This means that you reached the system limits concerning the amount of files per process. \
760 This is not a surprise if you are trying to virtualize many processes on top of SMPI. \
761 Don't panic -- you should simply increase your system limits and try again. \n\n\
762 First, check what your limits are:\n\
763   cat /proc/sys/fs/file-max # Gives you the system-wide limit\n\
764   ulimit -Hn                # Gives you the per process hard limit\n\
765   ulimit -Sn                # Gives you the per process soft limit\n\
766   cat /proc/self/limits     # Displays any per-process limitation (including the one given above)\n\n\
767 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. \
768 Ask the Internet about tutorials on how to increase the files limit such as: https://rtcamp.com/tutorials/linux/increase-open-files-limit/",
769              strerror(errno));
770         }
771         xbt_die("Impossible to create temporary file for memory mapping: %s",
772             strerror(errno));
773       }
774
775       status = unlink (path);
776       if (status)
777         xbt_die("Impossible to unlink temporary file for memory mapping");
778
779       status = ftruncate(file_descriptor, smpi_size_data_exe);
780       if(status)
781         xbt_die("Impossible to set the size of the temporary file for memory mapping");
782
783       /* Ask for a free region */
784       address = mmap (nullptr, smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, 0);
785       if (address == MAP_FAILED)
786         xbt_die("Couldn't find a free region for memory mapping");
787
788       //initialize the values
789       memcpy(address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
790
791       //store the address of the mapping for further switches
792       smpi_privatisation_regions[i].file_descriptor = file_descriptor;
793       smpi_privatisation_regions[i].address = address;
794   }
795 #endif
796 }
797
798 void smpi_destroy_global_memory_segments(){
799   if (smpi_size_data_exe == 0)//no need to switch
800     return;
801 #if HAVE_PRIVATIZATION
802   int i;
803   for (i=0; i< smpi_process_count(); i++){
804     if(munmap(smpi_privatisation_regions[i].address, smpi_size_data_exe) < 0) {
805       XBT_WARN("Unmapping of fd %d failed: %s", smpi_privatisation_regions[i].file_descriptor, strerror(errno));
806     }
807     close(smpi_privatisation_regions[i].file_descriptor);
808   }
809   xbt_free(smpi_privatisation_regions);
810 #endif
811 }
812
813 extern "C" { /** These functions will be called from the user code **/
814   smpi_trace_call_location_t* smpi_trace_get_call_location() {
815     return smpi_process_get_call_location();
816   }
817
818   void smpi_trace_set_call_location(const char* file, const int line) {
819     smpi_trace_call_location_t* loc = smpi_process_get_call_location();
820
821     loc->previous_filename   = loc->filename;
822     loc->previous_linenumber = loc->linenumber;
823     loc->filename            = file;
824     loc->linenumber          = line;
825   }
826
827   /**
828    * Required for Fortran bindings
829    */
830   void smpi_trace_set_call_location_(const char* file, int* line) {
831     smpi_trace_set_call_location(file, *line);
832   }
833
834   /** 
835    * Required for Fortran if -fsecond-underscore is activated
836    */
837   void smpi_trace_set_call_location__(const char* file, int* line) {
838     smpi_trace_set_call_location(file, *line);
839   }
840 }