Logo AND Algorithmique Numérique Distribuée

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