Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Fix use after free (data is removed by the call to xbt_dict_remove()).
[simgrid.git] / src / smpi / smpi_bench.c
1 /* Copyright (c) 2007, 2009, 2010. 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 "private.h"
8 #include "xbt/dict.h"
9 #include "xbt/sysdep.h"
10 #include "xbt/ex.h"
11 #include "surf/surf.h"
12 #include "simgrid/sg_config.h"
13
14 #ifndef WIN32
15 #include <sys/mman.h>
16 #endif
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <math.h> // sqrt
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdio.h>
25
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_bench, smpi,
27                                 "Logging specific to SMPI (benchmarking)");
28
29 /* Shared allocations are handled through shared memory segments.
30  * Associated data and metadata are used as follows:
31  *
32  *                                                                    mmap #1
33  *    `allocs' dict                                                     ---- -.
34  *    ----------      shared_data_t               shared_metadata_t   / |  |  |
35  * .->| <name> | ---> -------------------- <--.   -----------------   | |  |  |
36  * |  ----------      | fd of <name>     |    |   | size of mmap  | --| |  |  |
37  * |                  | count (2)        |    |-- | data          |   \ |  |  |
38  * `----------------- | <name>           |    |   -----------------     ----  |
39  *                    --------------------    |   ^                           |
40  *                                            |   |                           |
41  *                                            |   |   `allocs_metadata' dict  |
42  *                                            |   |   ----------------------  |
43  *                                            |   `-- | <addr of mmap #1>  |<-'
44  *                                            |   .-- | <addr of mmap #2>  |<-.
45  *                                            |   |   ----------------------  |
46  *                                            |   |                           |
47  *                                            |   |                           |
48  *                                            |   |                           |
49  *                                            |   |                   mmap #2 |
50  *                                            |   v                     ---- -'
51  *                                            |   shared_metadata_t   / |  |
52  *                                            |   -----------------   | |  |
53  *                                            |   | size of mmap  | --| |  |
54  *                                            `-- | data          |   | |  |
55  *                                                -----------------   | |  |
56  *                                                                    \ |  |
57  *                                                                      ----
58  */
59
60 #define PTR_STRLEN (2 + 2 * sizeof(void*) + 1)
61
62 xbt_dict_t allocs = NULL;          /* Allocated on first use */
63 xbt_dict_t allocs_metadata = NULL; /* Allocated on first use */
64 xbt_dict_t samples = NULL;         /* Allocated on first use */
65 xbt_dict_t calls = NULL;           /* Allocated on first use */
66 __thread int smpi_current_rank = 0;      /* Updated after each MPI call */
67
68 typedef struct {
69   int fd;
70   int count;
71   char* loc;
72 } shared_data_t;
73
74 typedef struct  {
75   size_t size;
76   shared_data_t* data;
77 } shared_metadata_t;
78
79 static size_t shm_size(int fd) {
80   struct stat st;
81
82   if(fstat(fd, &st) < 0) {
83     xbt_die("Could not stat fd %d: %s", fd, strerror(errno));
84   }
85   return (size_t)st.st_size;
86 }
87
88 #ifndef WIN32
89 static void* shm_map(int fd, size_t size, shared_data_t* data) {
90   void* mem;
91   char loc[PTR_STRLEN];
92   shared_metadata_t* meta;
93
94   if(size > shm_size(fd)) {
95     if(ftruncate(fd, (off_t)size) < 0) {
96       xbt_die("Could not truncate fd %d to %zu: %s", fd, size, strerror(errno));
97     }
98   }
99
100   mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
101   if(mem == MAP_FAILED) {
102     xbt_die("Could not map fd %d: %s", fd, strerror(errno));
103   }
104   if(!allocs_metadata) {
105     allocs_metadata = xbt_dict_new();
106   }
107   snprintf(loc, PTR_STRLEN, "%p", mem);
108   meta = xbt_new(shared_metadata_t, 1);
109   meta->size = size;
110   meta->data = data;
111   xbt_dict_set(allocs_metadata, loc, meta, &free);
112   XBT_DEBUG("MMAP %zu to %p", size, mem);
113   return mem;
114 }
115 #endif
116
117 void smpi_bench_destroy(void)
118 {
119   xbt_dict_free(&allocs);
120   xbt_dict_free(&samples);
121   xbt_dict_free(&calls);
122 }
123
124 void smpi_execute_flops(double flops) {
125   smx_action_t action;
126   smx_host_t host;
127   host = SIMIX_host_self();
128
129   XBT_DEBUG("Handle real computation time: %f flops", flops);
130   action = simcall_host_execute("computation", host, flops, 1);
131 #ifdef HAVE_TRACING
132   simcall_set_category (action, TRACE_internal_smpi_get_category());
133 #endif
134   simcall_host_execution_wait(action);
135 }
136
137 static void smpi_execute(double duration)
138 {
139   /* FIXME: a global variable would be less expensive to consult than a call to xbt_cfg_get_double() right on the critical path */
140   if (duration >= sg_cfg_get_double("smpi/cpu_threshold")) {
141     XBT_DEBUG("Sleep for %f to handle real computation time", duration);
142     smpi_execute_flops(duration *
143                 sg_cfg_get_double("smpi/running_power"));
144   } else {
145     XBT_DEBUG("Real computation took %f while option smpi/cpu_threshold is set to %f => ignore it",
146         duration, sg_cfg_get_double("smpi/cpu_threshold"));
147   }
148 }
149
150 void smpi_bench_begin(void)
151 {
152   xbt_os_threadtimer_start(smpi_process_timer());
153   smpi_current_rank = smpi_process_index();
154 }
155
156 void smpi_bench_end(void)
157 {
158   xbt_os_timer_t timer = smpi_process_timer();
159
160   xbt_os_threadtimer_stop(timer);
161   smpi_execute(xbt_os_timer_elapsed(timer));
162 }
163
164 unsigned int smpi_sleep(unsigned int secs)
165 {
166   smpi_bench_end();
167   smpi_execute_flops((double) secs*simcall_host_get_speed(SIMIX_host_self()));
168   smpi_bench_begin();
169   return secs;
170 }
171
172 int smpi_gettimeofday(struct timeval *tv)
173 {
174   double now;
175   smpi_bench_end();
176   now = SIMIX_get_clock();
177   if (tv) {
178     tv->tv_sec = (time_t)now;
179 #ifdef WIN32
180     tv->tv_usec = (useconds_t)((now - tv->tv_sec) * 1e6);
181 #else
182     tv->tv_usec = (suseconds_t)((now - tv->tv_sec) * 1e6);
183 #endif
184   }
185   smpi_bench_begin();
186   return 0;
187 }
188
189 extern double sg_maxmin_precision;
190 unsigned long long smpi_rastro_resolution (void)
191 {
192   smpi_bench_end();
193   double resolution = (1/sg_maxmin_precision);
194   smpi_bench_begin();
195   return (unsigned long long)resolution;
196 }
197
198 unsigned long long smpi_rastro_timestamp (void)
199 {
200   smpi_bench_end();
201   double now = SIMIX_get_clock();
202
203   unsigned long long sec = (unsigned long long)now;
204   unsigned long long pre = (now - sec) * smpi_rastro_resolution();
205   smpi_bench_begin();
206   return (unsigned long long)sec * smpi_rastro_resolution() + pre;
207 }
208
209 /* ****************************** Functions related to the SMPI_SAMPLE_ macros ************************************/
210 typedef struct {
211   int iters;        /* amount of requested iterations */
212   int count;        /* amount of iterations done so far */
213   double threshold; /* maximal stderr requested (if positive) */
214   double relstderr; /* observed stderr so far */
215   double mean;      /* mean of benched times, to be used if the block is disabled */
216   double sum;       /* sum of benched times (to compute the mean and stderr) */
217   double sum_pow2;  /* sum of the square of the benched times (to compute the stderr) */
218   int benching;     /* 1: we are benchmarking; 0: we have enough data, no bench anymore */
219 } local_data_t;
220
221 static char *sample_location(int global, const char *file, int line) {
222   if (global) {
223     return bprintf("%s:%d", file, line);
224   } else {
225     return bprintf("%s:%d:%d", file, line, smpi_process_index());
226   }
227 }
228 static int sample_enough_benchs(local_data_t *data) {
229   int res = data->count >= data->iters;
230   if (data->threshold>0.0) {
231     if (data->count <2)
232       res = 0; // not enough data
233     if (data->relstderr > data->threshold)
234       res = 0; // stderr too high yet
235   }
236   XBT_DEBUG("%s (count:%d iter:%d stderr:%f thres:%f mean:%fs)",
237       (res?"enough benchs":"need more data"),
238       data->count, data->iters, data->relstderr, data->threshold, data->mean);
239   return res;
240 }
241
242 void smpi_sample_1(int global, const char *file, int line, int iters, double threshold)
243 {
244   char *loc = sample_location(global, file, line);
245   local_data_t *data;
246
247   smpi_bench_end();     /* Take time from previous, unrelated computation into account */
248   if (!samples)
249     samples = xbt_dict_new_homogeneous(free);
250
251   data = xbt_dict_get_or_null(samples, loc);
252   if (!data) {
253     xbt_assert(threshold>0 || iters>0,
254         "You should provide either a positive amount of iterations to bench, or a positive maximal stderr (or both)");
255     data = (local_data_t *) xbt_new(local_data_t, 1);
256     data->count = 0;
257     data->sum = 0.0;
258     data->sum_pow2 = 0.0;
259     data->iters = iters;
260     data->threshold = threshold;
261     data->benching = 1; // If we have no data, we need at least one
262     data->mean = 0;
263     xbt_dict_set(samples, loc, data, NULL);
264     XBT_DEBUG("XXXXX First time ever on benched nest %s.",loc);
265   } else {
266     if (data->iters != iters || data->threshold != threshold) {
267       XBT_ERROR("Asked to bench block %s with different settings %d, %f is not %d, %f. How did you manage to give two numbers at the same line??",
268           loc, data->iters, data->threshold, iters,threshold);
269       THROW_IMPOSSIBLE;
270     }
271
272     // if we already have some data, check whether sample_2 should get one more bench or whether it should emulate the computation instead
273     data->benching = !sample_enough_benchs(data);
274     XBT_DEBUG("XXXX Re-entering the benched nest %s. %s",loc, (data->benching?"more benching needed":"we have enough data, skip computes"));
275   }
276   xbt_free(loc);
277 }
278
279 int smpi_sample_2(int global, const char *file, int line)
280 {
281   char *loc = sample_location(global, file, line);
282   local_data_t *data;
283
284   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
285   data = xbt_dict_get(samples, loc);
286   XBT_DEBUG("sample2 %s",loc);
287   xbt_free(loc);
288
289   if (data->benching==1) {
290     // we need to run a new bench
291     XBT_DEBUG("benchmarking: count:%d iter:%d stderr:%f thres:%f; mean:%f",
292         data->count, data->iters, data->relstderr, data->threshold, data->mean);
293     smpi_bench_begin();
294     return 1;
295   } else {
296     // Enough data, no more bench (either we got enough data from previous visits to this benched nest, or we just ran one bench and need to bail out now that our job is done).
297     // Just sleep instead
298     XBT_DEBUG("No benchmark (either no need, or just ran one): count >= iter (%d >= %d) or stderr<thres (%f<=%f). apply the %fs delay instead",
299         data->count, data->iters, data->relstderr, data->threshold, data->mean);
300     smpi_execute(data->mean);
301
302     smpi_bench_begin(); // prepare to capture future, unrelated computations
303     return 0;
304   }
305 }
306
307
308 void smpi_sample_3(int global, const char *file, int line)
309 {
310   char *loc = sample_location(global, file, line);
311   local_data_t *data;
312
313   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
314   data = xbt_dict_get(samples, loc);
315   XBT_DEBUG("sample3 %s",loc);
316   xbt_free(loc);
317
318   if (data->benching==0) {
319     THROW_IMPOSSIBLE;
320   }
321
322   // ok, benchmarking this loop is over
323   xbt_os_threadtimer_stop(smpi_process_timer());
324
325   // update the stats
326   double sample, n;
327   data->count++;
328   sample = xbt_os_timer_elapsed(smpi_process_timer());
329   data->sum += sample;
330   data->sum_pow2 += sample * sample;
331   n = (double)data->count;
332   data->mean = data->sum / n;
333   data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
334   if (!sample_enough_benchs(data)) {
335     data->mean = sample; // Still in benching process; We want sample_2 to simulate the exact time of this loop occurrence before leaving, not the mean over the history
336   }
337   XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
338       data->mean, data->relstderr, sample);
339
340   // That's enough for now, prevent sample_2 to run the same code over and over
341   data->benching = 0;
342 }
343
344 #ifndef WIN32
345 static void smpi_shared_alloc_free(void *p)
346 {
347   shared_data_t *data = p;
348   xbt_free(data->loc);
349   xbt_free(data);
350 }
351
352 void *smpi_shared_malloc(size_t size, const char *file, int line)
353 {
354   char *loc = bprintf("%zu_%s_%d", (size_t)getpid(), file, line);
355   size_t len = strlen(loc);
356   size_t i;
357   int fd;
358   void* mem;
359   shared_data_t *data;
360   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
361     for(i = 0; i < len; i++) {
362       /* Make the 'loc' ID be a flat filename */
363       if(loc[i] == '/') {
364         loc[i] = '_';
365       }
366     }
367     if (!allocs) {
368       allocs = xbt_dict_new_homogeneous(smpi_shared_alloc_free);
369     }
370     data = xbt_dict_get_or_null(allocs, loc);
371     if(!data) {
372       fd = shm_open(loc, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
373       if(fd < 0) {
374         switch(errno) {
375           case EEXIST:
376             xbt_die("Please cleanup /dev/shm/%s", loc);
377           default:
378             xbt_die("An unhandled error occured while opening %s: %s", loc, strerror(errno));
379         }
380       }
381       data = xbt_new(shared_data_t, 1);
382       data->fd = fd;
383       data->count = 1;
384       data->loc = loc;
385       mem = shm_map(fd, size, data);
386       if(shm_unlink(loc) < 0) {
387         XBT_WARN("Could not early unlink %s: %s", loc, strerror(errno));
388       }
389       xbt_dict_set(allocs, loc, data, NULL);
390       XBT_DEBUG("Mapping %s at %p through %d", loc, mem, fd);
391     } else {
392       mem = shm_map(data->fd, size, data);
393       data->count++;
394     }
395     XBT_DEBUG("Shared malloc %zu in %p (metadata at %p)", size, mem, data);
396   }else{
397     mem = xbt_malloc(size);
398     XBT_DEBUG("Classic malloc %zu in %p", size, mem);
399   }
400
401   return mem;
402 }
403 void smpi_shared_free(void *ptr)
404 {
405   char loc[PTR_STRLEN];
406   shared_metadata_t* meta;
407   shared_data_t* data;
408   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
409   
410     if (!allocs) {
411       XBT_WARN("Cannot free: nothing was allocated");
412       return;
413     }
414     if(!allocs_metadata) {
415       XBT_WARN("Cannot free: no metadata was allocated");
416     }
417     snprintf(loc, PTR_STRLEN, "%p", ptr);
418     meta = (shared_metadata_t*)xbt_dict_get_or_null(allocs_metadata, loc);
419     if (!meta) {
420       XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
421       return;
422     }
423     data = meta->data;
424     if(!data) {
425       XBT_WARN("Cannot free: something is broken in the metadata link");
426       return;
427     }
428     if(munmap(ptr, meta->size) < 0) {
429       XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
430     }
431     data->count--;
432     XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
433     if (data->count <= 0) {
434       close(data->fd);
435       xbt_dict_remove(allocs, data->loc);
436       XBT_DEBUG("Shared free - with removal - of %p", ptr);
437     }
438   }else{
439     XBT_DEBUG("Classic free of %p", ptr);
440     xbt_free(ptr);
441   }
442 }
443 #endif
444
445 int smpi_shared_known_call(const char* func, const char* input) {
446    char* loc = bprintf("%s:%s", func, input);
447    xbt_ex_t ex;
448    int known;
449
450    if(!calls) {
451       calls = xbt_dict_new_homogeneous(NULL);
452    }
453    TRY {
454       xbt_dict_get(calls, loc); /* Succeed or throw */
455       known = 1;
456    }
457    CATCH(ex) {
458       if(ex.category == not_found_error) {
459          known = 0;
460          xbt_ex_free(ex);
461       } else {
462          RETHROW;
463       }
464    }
465    free(loc);
466    return known;
467 }
468
469 void* smpi_shared_get_call(const char* func, const char* input) {
470    char* loc = bprintf("%s:%s", func, input);
471    void* data;
472
473    if(!calls) {
474       calls = xbt_dict_new_homogeneous(NULL);
475    }
476    data = xbt_dict_get(calls, loc);
477    free(loc);
478    return data;
479 }
480
481 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
482    char* loc = bprintf("%s:%s", func, input);
483
484    if(!calls) {
485       calls = xbt_dict_new_homogeneous(NULL);
486    }
487    xbt_dict_set(calls, loc, data, NULL);
488    free(loc);
489    return data;
490 }