Logo AND Algorithmique Numérique Distribuée

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