Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add an smpi_is_shared call, and ignore memcpys in SMPI copy callback when buffers...
[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()->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()->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 = static_cast<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       xbt_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       xbt_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     shared_metadata_t newmeta;
640     //register metadata for memcpy avoidance
641     shared_data_key_type* data = (shared_data_key_type*)xbt_malloc(sizeof(shared_data_key_type));
642     data->second.fd = -1;
643     data->second.count = 1;
644     newmeta.size = size;
645     newmeta.data = data;
646     allocs_metadata[mem] = newmeta;
647   } else {
648     mem = xbt_malloc(size);
649     XBT_DEBUG("Classic malloc %zu in %p", size, mem);
650   }
651
652   return mem;
653 }
654
655 int smpi_is_shared(void*ptr){
656   if ( smpi_cfg_shared_malloc == shmalloc_local || smpi_cfg_shared_malloc == shmalloc_global) {
657     if (allocs_metadata.count(ptr) != 0) 
658      return 1;
659     for(auto it : allocs_metadata){
660       if (ptr >= it.first && ptr < (char*)it.first + it.second.size)
661         return 1;
662     }
663       return 0;
664   } else {
665     return 0;
666   }
667 }
668
669 void smpi_shared_free(void *ptr)
670 {
671   if (smpi_cfg_shared_malloc == shmalloc_local) {
672     char loc[PTR_STRLEN];
673     snprintf(loc, PTR_STRLEN, "%p", ptr);
674     auto meta = allocs_metadata.find(ptr);
675     if (meta == allocs_metadata.end()) {
676       XBT_WARN("Cannot free: %p was not shared-allocated by SMPI - maybe its size was 0?", ptr);
677       return;
678     }
679     shared_data_t* data = &meta->second.data->second;
680     if (munmap(ptr, meta->second.size) < 0) {
681       XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
682     }
683     data->count--;
684     if (data->count <= 0) {
685       close(data->fd);
686       allocs.erase(allocs.find(meta->second.data->first));
687       allocs_metadata.erase(ptr);
688       XBT_DEBUG("Shared free - with removal - of %p", ptr);
689     } else {
690       XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
691     }
692
693   } else if (smpi_cfg_shared_malloc == shmalloc_global) {
694     auto meta = allocs_metadata.find(ptr);
695     if (meta != allocs_metadata.end()){
696       meta->second.data->second.count--;
697       if(meta->second.data->second.count==0)
698         xbt_free(meta->second.data);
699     }
700
701     munmap(ptr, 0); // the POSIX says that I should not give 0 as a length, but it seems to work OK
702   } else {
703     XBT_DEBUG("Classic free of %p", ptr);
704     xbt_free(ptr);
705   }
706 }
707 #endif
708
709 int smpi_shared_known_call(const char* func, const char* input)
710 {
711   char* loc = bprintf("%s:%s", func, input);
712   int known = 0;
713
714   if (calls==nullptr) {
715     calls = xbt_dict_new_homogeneous(nullptr);
716   }
717   try {
718     xbt_dict_get(calls, loc); /* Succeed or throw */
719     known = 1;
720     xbt_free(loc);
721   }
722   catch (xbt_ex& ex) {
723     xbt_free(loc);
724     if (ex.category != not_found_error)
725       throw;
726   }
727   catch(...) {
728     xbt_free(loc);
729     throw;
730   }
731   return known;
732 }
733
734 void* smpi_shared_get_call(const char* func, const char* input) {
735   char* loc = bprintf("%s:%s", func, input);
736
737   if (calls == nullptr)
738     calls    = xbt_dict_new_homogeneous(nullptr);
739   void* data = xbt_dict_get(calls, loc);
740   xbt_free(loc);
741   return data;
742 }
743
744 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
745   char* loc = bprintf("%s:%s", func, input);
746
747   if (calls == nullptr)
748     calls = xbt_dict_new_homogeneous(nullptr);
749   xbt_dict_set(calls, loc, data, nullptr);
750   xbt_free(loc);
751   return data;
752 }
753
754
755 /** Map a given SMPI privatization segment (make a SMPI process active) */
756 void smpi_switch_data_segment(int dest) {
757   if (smpi_loaded_page == dest)//no need to switch, we've already loaded the one we want
758     return;
759
760   // So the job:
761   smpi_really_switch_data_segment(dest);
762 }
763
764 /** Map a given SMPI privatization segment (make a SMPI process active)  even if SMPI thinks it is already active
765  *
766  *  When doing a state restoration, the state of the restored variables  might not be consistent with the state of the
767  *  virtual memory. In this case, we to change the data segment.
768  */
769 void smpi_really_switch_data_segment(int dest)
770 {
771   if(smpi_size_data_exe == 0)//no need to switch
772     return;
773
774 #if HAVE_PRIVATIZATION
775   if(smpi_loaded_page==-1){//initial switch, do the copy from the real page here
776     for (int i=0; i< smpi_process_count(); i++){
777       memcpy(smpi_privatisation_regions[i].address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
778     }
779   }
780
781   // FIXME, cross-process support (mmap across process when necessary)
782   int current = smpi_privatisation_regions[dest].file_descriptor;
783   XBT_DEBUG("Switching data frame to the one of process %d", dest);
784   void* tmp =
785       mmap(TOPAGE(smpi_start_data_exe), smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, current, 0);
786   if (tmp != TOPAGE(smpi_start_data_exe))
787     xbt_die("Couldn't map the new region");
788   smpi_loaded_page = dest;
789 #endif
790 }
791
792 int smpi_is_privatisation_file(char* file)
793 {
794   return strncmp("/dev/shm/my-buffer-", file, std::strlen("/dev/shm/my-buffer-")) == 0;
795 }
796
797 void smpi_initialize_global_memory_segments()
798 {
799
800 #if !HAVE_PRIVATIZATION
801   smpi_privatize_global_variables=false;
802   xbt_die("You are trying to use privatization on a system that does not support it. Don't.");
803   return;
804 #else
805
806   smpi_get_executable_global_size();
807
808   XBT_DEBUG ("bss+data segment found : size %d starting at %p", smpi_size_data_exe, smpi_start_data_exe );
809
810   if (smpi_size_data_exe == 0){//no need to switch
811     smpi_privatize_global_variables=false;
812     return;
813   }
814
815   smpi_privatisation_regions = static_cast<smpi_privatisation_region_t>(
816       xbt_malloc(smpi_process_count() * sizeof(struct s_smpi_privatisation_region)));
817
818   for (int i=0; i< smpi_process_count(); i++){
819     // create SIMIX_process_count() mappings of this size with the same data inside
820     int file_descriptor;
821     void* address = nullptr;
822     char path[24];
823     int status;
824
825     do {
826       snprintf(path, sizeof(path), "/smpi-buffer-%06x", rand() % 0xffffff);
827       file_descriptor = shm_open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
828     } while (file_descriptor == -1 && errno == EEXIST);
829     if (file_descriptor < 0) {
830       if (errno == EMFILE) {
831         xbt_die("Impossible to create temporary file for memory mapping: %s\n\
832 The open() system call failed with the EMFILE error code (too many files). \n\n\
833 This means that you reached the system limits concerning the amount of files per process. \
834 This is not a surprise if you are trying to virtualize many processes on top of SMPI. \
835 Don't panic -- you should simply increase your system limits and try again. \n\n\
836 First, check what your limits are:\n\
837   cat /proc/sys/fs/file-max # Gives you the system-wide limit\n\
838   ulimit -Hn                # Gives you the per process hard limit\n\
839   ulimit -Sn                # Gives you the per process soft limit\n\
840   cat /proc/self/limits     # Displays any per-process limitation (including the one given above)\n\n\
841 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. \
842 Ask the Internet about tutorials on how to increase the files limit such as: https://rtcamp.com/tutorials/linux/increase-open-files-limit/",
843                 strerror(errno));
844       }
845       xbt_die("Impossible to create temporary file for memory mapping: %s", strerror(errno));
846     }
847
848     status = ftruncate(file_descriptor, smpi_size_data_exe);
849     if (status)
850       xbt_die("Impossible to set the size of the temporary file for memory mapping");
851
852     /* Ask for a free region */
853     address = mmap(nullptr, smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, 0);
854     if (address == MAP_FAILED)
855       xbt_die("Couldn't find a free region for memory mapping");
856
857     status = shm_unlink(path);
858     if (status)
859       xbt_die("Impossible to unlink temporary file for memory mapping");
860
861     // initialize the values
862     memcpy(address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
863
864     // store the address of the mapping for further switches
865     smpi_privatisation_regions[i].file_descriptor = file_descriptor;
866     smpi_privatisation_regions[i].address         = address;
867   }
868 #endif
869 }
870
871 void smpi_destroy_global_memory_segments(){
872   if (smpi_size_data_exe == 0)//no need to switch
873     return;
874 #if HAVE_PRIVATIZATION
875   for (int i=0; i< smpi_process_count(); i++) {
876     if (munmap(smpi_privatisation_regions[i].address, smpi_size_data_exe) < 0)
877       XBT_WARN("Unmapping of fd %d failed: %s", smpi_privatisation_regions[i].file_descriptor, strerror(errno));
878     close(smpi_privatisation_regions[i].file_descriptor);
879   }
880   xbt_free(smpi_privatisation_regions);
881 #endif
882 }
883
884 extern "C" { /** These functions will be called from the user code **/
885   smpi_trace_call_location_t* smpi_trace_get_call_location() {
886     return smpi_process()->call_location();
887   }
888
889   void smpi_trace_set_call_location(const char* file, const int line) {
890     smpi_trace_call_location_t* loc = smpi_process()->call_location();
891
892     loc->previous_filename   = loc->filename;
893     loc->previous_linenumber = loc->linenumber;
894     loc->filename            = file;
895     loc->linenumber          = line;
896   }
897
898   /**
899    * Required for Fortran bindings
900    */
901   void smpi_trace_set_call_location_(const char* file, int* line) {
902     smpi_trace_set_call_location(file, *line);
903   }
904
905   /** 
906    * Required for Fortran if -fsecond-underscore is activated
907    */
908   void smpi_trace_set_call_location__(const char* file, int* line) {
909     smpi_trace_set_call_location(file, *line);
910   }
911 }