Logo AND Algorithmique Numérique Distribuée

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