Logo AND Algorithmique Numérique Distribuée

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