The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
dymelor.c
Go to the documentation of this file.
1 
34 #include <core/init.h>
35 #include <mm/mm.h>
36 #include <mm/dymelor.h>
37 #include <scheduler/scheduler.h>
38 
50 static void malloc_area_init(malloc_area * m_area, size_t size, int num_chunks)
51 {
52  m_area->chunk_size = size;
53  m_area->alloc_chunks = 0;
54  m_area->dirty_chunks = 0;
55  m_area->next_chunk = 0;
56  m_area->num_chunks = num_chunks;
57  m_area->state_changed = 0;
58  m_area->last_access = -1;
59  m_area->use_bitmap = NULL;
60  m_area->dirty_bitmap = NULL;
61  m_area->self_pointer = NULL;
62  m_area->area = NULL;
63 
64 #ifndef NDEBUG
65  atomic_set(&m_area->presence, 0);
66 #endif
67 
68  m_area->prev = -1;
69  m_area->next = -1;
70 
71  SET_AREA_LOCK_BIT(m_area);
72 
73 }
74 
79 {
80  int i, num_chunks;
81  size_t chunk_size;
82  malloc_state *state;
83 
84  state = rsalloc(sizeof(malloc_state));
85 
86  state->total_log_size = sizeof(malloc_state);
87  state->total_inc_size = sizeof(malloc_state);
88  state->num_areas = NUM_AREAS;
89  state->max_num_areas = MAX_NUM_AREAS;
90  state->timestamp = -1;
91  state->is_incremental = false;
92 
93  state->areas = (malloc_area *) rsalloc(state->max_num_areas * sizeof(malloc_area));
94  if (unlikely(state->areas == NULL)) {
95  rootsim_error(true, "Unable to allocate memory.\n");
96  }
97 
98  chunk_size = MIN_CHUNK_SIZE;
99  num_chunks = MIN_NUM_CHUNKS;
100 
101  for (i = 0; i < NUM_AREAS; i++) {
102 
103  malloc_area_init(&state->areas[i], chunk_size, num_chunks);
104  state->areas[i].idx = i;
105  chunk_size = chunk_size << 1;
106  }
107 
108  return state;
109 }
110 
111 void malloc_state_wipe(struct memory_map *mm)
112 {
113  int i;
114 
115  for (i = 0; i < NUM_AREAS; i++) {
116  free_buddy_memory(mm->buddy, mm->segment->base, mm->m_state->areas[i].self_pointer);
117  }
118 
119  rsfree(mm->m_state);
120 }
121 
131 static size_t compute_size(size_t size)
132 {
133  // Account for the space needed to keep the pointer to the malloc area
134  size += sizeof(long long);
135  size_t size_new;
136 
137  size_new = POWEROF2(size);
138  if (unlikely(size_new < MIN_CHUNK_SIZE))
139  size_new = MIN_CHUNK_SIZE;
140 
141  return size_new;
142 }
143 
154 static void find_next_free(malloc_area * m_area)
155 {
156  m_area->next_chunk++;
157 
158  while (m_area->next_chunk < m_area->num_chunks) {
159  if (!bitmap_check(m_area->use_bitmap, m_area->next_chunk))
160  break;
161 
162  m_area->next_chunk++;
163  }
164 
165 }
166 
167 void *do_malloc(struct lp_struct *lp, size_t size)
168 {
169  malloc_area *m_area, *prev_area = NULL;
170  void *ptr;
171  size_t area_size, bitmap_size;
172  malloc_state *m_state;
173 
174  if(unlikely(size == 0)){
175 #ifndef NDEBUG
176  rootsim_error(false, "Requested a 0 sized malloc\n");
177 #endif
178  return NULL;
179  }
180 
181  size = compute_size(size);
182 
183  if (unlikely(size > MAX_CHUNK_SIZE)) {
184  rootsim_error(false, "Requested a memory allocation of %d but the limit is %d. Reconfigure MAX_CHUNK_SIZE. Returning NULL.\n",
185  size, MAX_CHUNK_SIZE);
186  return NULL;
187  }
188 
189  m_state = lp->mm->m_state;
190  m_area = &m_state->areas[B_CTZ(size) - B_CTZ(MIN_CHUNK_SIZE)]; // both size and MIN_CHUNK_SIZE are power of 2
191 
192 #ifndef NDEBUG
193  atomic_inc(&m_area->presence);
194  assert(atomic_read(&m_area->presence) == 1);
195 #endif
196 
197  while (m_area != NULL && m_area->alloc_chunks == m_area->num_chunks) {
198  prev_area = m_area;
199  if (m_area->next == -1) {
200  m_area = NULL;
201  } else {
202  m_area = &(m_state->areas[m_area->next]);
203 #ifndef NDEBUG
204  atomic_inc(&m_area->presence);
205  assert(atomic_read(&m_area->presence) == 1);
206 #endif
207  }
208  }
209 
210 #ifndef NDEBUG
211  if (prev_area != NULL) {
212  atomic_dec(&prev_area->presence);
213  }
214 #endif
215 
216  if (m_area == NULL) {
217 
218  if (m_state->num_areas == m_state->max_num_areas) {
219 
220  malloc_area *tmp = NULL;
221 
222  if ((m_state->max_num_areas << 1) > MAX_LIMIT_NUM_AREAS) {
223 #ifndef NDEBUG
224  atomic_dec(&m_area->presence);
225 #endif
226  return NULL;
227  }
228 
229  m_state->max_num_areas <<= 1;
230 
231  rootsim_error(true, "To reimplement\n");
232 // tmp = (malloc_area *)pool_realloc_memory(lp->mm->m_state->areas, lp->mm->m_state->max_num_areas * sizeof(malloc_area));
233  if (tmp == NULL) {
234 
238  rootsim_error(false,
239  "DyMeLoR: cannot reallocate the block of malloc_area.\n");
240  m_state->max_num_areas >>= 1;
241 
242 #ifndef NDEBUG
243  atomic_dec(&m_area->presence);
244 #endif
245  return NULL;
246  }
247 
248  m_state->areas = tmp;
249  }
250  // Allocate a new malloc area
251  m_area = &m_state->areas[m_state->num_areas];
252 
253  // The malloc area to be instantiated has twice the number of chunks wrt the last full malloc area for the same chunks size
254  malloc_area_init(m_area, size, prev_area->num_chunks << 1);
255 
256 #ifndef NDEBUG
257  atomic_inc(&m_area->presence);
258  assert(atomic_read(&m_area->presence) == 1);
259 #endif
260 
261  m_area->idx = m_state->num_areas;
262  m_state->num_areas++;
263  prev_area->next = m_area->idx;
264  m_area->prev = prev_area->idx;
265 
266  }
267 
268  if (m_area->area == NULL) {
269 
270  bitmap_size = bitmap_required_size(m_area->num_chunks);
271 
272  area_size = sizeof(malloc_area *) + bitmap_size * 2 + m_area->num_chunks * size;
273 
274  m_area->self_pointer = (malloc_area *)allocate_buddy_memory(lp->mm->buddy, lp->mm->segment->base, area_size);
275  memset(m_area->self_pointer, 0, area_size);
276 
277  if (unlikely(m_area->self_pointer == NULL)) {
278  rootsim_error(true, "Error while allocating memory.\n");
279  }
280 
281  m_area->dirty_chunks = 0;
282  *(unsigned long long *)(m_area->self_pointer) =
283  (unsigned long long)m_area;
284 
285  m_area->use_bitmap =
286  ((unsigned char *)m_area->self_pointer +
287  sizeof(malloc_area *));
288 
289  m_area->dirty_bitmap =
290  ((unsigned char *)m_area->use_bitmap + bitmap_size);
291 
292  m_area->area =
293  (void *)((char *)m_area->dirty_bitmap + bitmap_size);
294  }
295 
296  if (unlikely(m_area->area == NULL)) {
297  rootsim_error(true, "Error while allocating memory.\n");
298  }
299 
300 #ifndef NDEBUG
301  if (bitmap_check(m_area->use_bitmap, m_area->next_chunk)) {
302  rootsim_error(true, "Error: reallocating an already allocated chunk\n");
303  }
304 #endif
305 
306  ptr = (void *)((char *)m_area->area + (m_area->next_chunk * size));
307 
308  bitmap_set(m_area->use_bitmap, m_area->next_chunk);
309 
310  bitmap_size = bitmap_required_size(m_area->num_chunks);
311 
312  if (m_area->alloc_chunks == 0) {
313  m_state->total_log_size += bitmap_size + sizeof(malloc_area);
314  }
315 
316  if (m_area->state_changed == 0) {
317  m_state->total_inc_size += bitmap_size + sizeof(malloc_area);
318  }
319 
320  m_area->state_changed = 1;
321  m_area->last_access = lvt(current);
322 
323  if (!CHECK_LOG_MODE_BIT(m_area)) {
324  if ((double)m_area->alloc_chunks / (double)m_area->num_chunks > MAX_LOG_THRESHOLD) {
325  SET_LOG_MODE_BIT(m_area);
326  m_state->total_log_size += (m_area->num_chunks - m_area->alloc_chunks) * size;
327  } else
328  m_state->total_log_size += size;
329  }
330 
331  m_area->alloc_chunks++;
332  find_next_free(m_area);
333 
334  // TODO: togliere
335  memset(ptr, 0xe8, size);
336 
337  // Keep track of the malloc_area which this chunk belongs to
338  *(unsigned long long *)ptr = (unsigned long long)m_area->self_pointer;
339 
340 #ifndef NDEBUG
341  atomic_dec(&m_area->presence);
342 #endif
343 
344  return (void *)((char *)ptr + sizeof(long long));
345 }
346 
347 void do_free(struct lp_struct *lp, void *ptr)
348 {
349  (void)lp;
350 
351  malloc_area *m_area = NULL;
352  int idx;
353  size_t chunk_size, bitmap_size;
354  malloc_state *m_state;
355 
356  if (unlikely(ptr == NULL)) {
357 #ifndef NDEBUG
358  rootsim_error(false, "Requested a free on the NULL pointer\n");
359 #endif
360  return;
361  }
362 
363  m_state = lp->mm->m_state;
364  m_area = get_area(ptr);
365  if (unlikely(m_area == NULL)) {
366  rootsim_error(false,
367  "Invalid pointer during free: malloc_area NULL\n");
368  return;
369  }
370 #ifndef NDEBUG
371  atomic_inc(&m_area->presence);
372  assert(atomic_read(&m_area->presence) == 1);
373 #endif
374 
375  chunk_size = UNTAGGED_CHUNK_SIZE(m_area);
376 
377  idx = (int)((char *)ptr - (char *)m_area->area) / chunk_size;
378 
379  if (!bitmap_check(m_area->use_bitmap, idx)) {
380  rootsim_error(false, "double free() corruption or address not malloc'd\n");
381  abort();
382  }
383  bitmap_reset(m_area->use_bitmap, idx);
384 
385  bitmap_size = bitmap_required_size(m_area->num_chunks);
386 
387  m_area->alloc_chunks--;
388 
389  if (m_area->alloc_chunks == 0) {
390  m_state->total_log_size -= bitmap_size + sizeof(malloc_area);
391  }
392 
393  if (m_area->state_changed == 0) {
394  m_state->total_inc_size += bitmap_size + sizeof(malloc_area);
395  }
396 
397  if (bitmap_check(m_area->dirty_bitmap, idx)) {
398  bitmap_reset(m_area->dirty_bitmap, idx);
399  m_area->dirty_chunks--;
400 
401  if (m_area->state_changed == 1 && m_area->dirty_chunks == 0)
402  m_state->total_inc_size -= bitmap_size;
403 
404  m_state->total_inc_size -= chunk_size;
405 
406  if (unlikely(m_area->dirty_chunks < 0)) {
407  rootsim_error(true, "negative number of chunks\n");
408  }
409  }
410 
411  m_area->state_changed = 1;
412 
413  m_area->last_access = lvt(current);
414 
415  if (CHECK_LOG_MODE_BIT(m_area)) {
416  if ((double)m_area->alloc_chunks / (double)m_area->num_chunks < MIN_LOG_THRESHOLD) {
417  RESET_LOG_MODE_BIT(m_area);
418  m_state->total_log_size -= (m_area->num_chunks - m_area->alloc_chunks) * chunk_size;
419  }
420  } else {
421  m_state->total_log_size -= chunk_size;
422  }
423 
424  if (idx < m_area->next_chunk)
425  m_area->next_chunk = idx;
426 
427 #ifndef NDEBUG
428  atomic_dec(&m_area->presence);
429 #endif
430  // TODO: when do we free unrecoverable areas?
431 }
432 
445 void dirty_mem(void *base, int size)
446 {
447 
448  // TODO: Quando reintegriamo l'incrementale questo qui deve ricomparire!
449  (void)base;
450  (void)size;
451 
452  return;
453 
454 #if 0
455 // unsigned long long current_cost;
456 
457  // Sanity check on passed address
458 /* if(base == NULL) {
459  rootsim_error(false, "Trying to access NULL. Memory interception aborted\n");
460  return;
461  }
462 */
463 /* if (rootsim_config.snapshot == AUTONOMIC_SNAPSHOT ||
464  rootsim_config.snapshot == AUTONOMIC_INC_SNAPSHOT ||
465  rootsim_config.snapshot == AUTONOMIC_FULL_SNAPSHOT)
466  add_counter++;
467 */
468  int first_chunk, last_chunk, i, chk_size;
469 
470  size_t bitmap_size;
471 
472  malloc_area *m_area = get_area(base);
473 
474  if (m_area != NULL) {
475 
476  chk_size = UNTAGGED_CHUNK_SIZE(m_area->chunk_size);
477 
478  // Compute the number of chunks affected by the write
479  first_chunk =
480  (int)(((char *)base - (char *)m_area->area) / chk_size);
481 
482  // If size == -1, then we adopt a conservative approach: dirty all the chunks from the base to the end
483  // of the actual malloc area base address belongs to.
484  // This has been inserted to support the wrapping of third-party libraries where the size of the
485  // update (or even the actual update) cannot be statically/dynamically determined.
486  if (size == -1)
487  last_chunk = m_area->num_chunks - 1;
488  else
489  last_chunk = (int)(((char *)base + size - 1 - (char *)m_area->area) / chk_size);
490 
491  bitmap_size = bitmap_required_size(m_area->num_chunks);
492 
493  if (m_area->state_changed == 1) {
494  if (m_area->dirty_chunks == 0)
495  lp->mm->m_state->dirty_bitmap_size += bitmap_size;
496  } else {
497  lp->mm->m_state->dirty_areas++;
498  lp->mm->m_state->dirty_bitmap_size += bitmap_size * 2;
499  m_area->state_changed = 1;
500  }
501 
502  for (i = first_chunk; i <= last_chunk; i++) {
503 
504  // If it is dirtied a clean chunk, set it dirty and increase dirty object count for the malloc_area
505  if (!bitmap_check(m_area->dirty_bitmap, i)) {
506  bitmap_set(m_area->dirty_bitmap, i);
507  lp->mm->m_state->total_inc_size += chk_size;
508 
509  m_area->dirty_chunks++;
510  }
511  }
512  }
513 #endif
514 }
515 
526 size_t get_log_size(malloc_state * logged_state)
527 {
528  if (unlikely(logged_state == NULL))
529  return 0;
530 
531  if (is_incremental(logged_state)) {
532  return logged_state->total_inc_size;
533  } else {
534  return logged_state->total_log_size;
535  }
536 }
537 
553 void *__wrap_malloc(size_t size)
554 {
555  void *ptr;
556 
557  switch_to_platform_mode();
558 
560  ptr = rsalloc(size);
561  goto out;
562  }
563 
564  ptr = do_malloc(current, size);
565 
566  out:
567  switch_to_application_mode();
568  return ptr;
569 }
570 
589 void __wrap_free(void *ptr)
590 {
591  switch_to_platform_mode();
592 
594  rsfree(ptr);
595  goto out;
596  }
597 
598  do_free(current, ptr);
599 
600  out:
601  switch_to_application_mode();
602 }
603 
615 void *__wrap_realloc(void *ptr, size_t size)
616 {
617  void *new_buffer;
618  size_t old_size;
619  size_t copy_size;
620  malloc_area *m_area;
621 
623  return rsrealloc(ptr, size);
624  }
625  // If ptr is NULL realloc is equivalent to the malloc
626  if (unlikely(ptr == NULL)) {
627  return __wrap_malloc(size);
628  }
629  // If ptr is not NULL and the size is 0 realloc is equivalent to the free
630  if (unlikely(size == 0)) {
631  __wrap_free(ptr);
632  return NULL;
633  }
634 
635  m_area = get_area(ptr);
636 
637  // The size could be greater than the real request, but it does not matter since the realloc specific requires that
638  // is copied at least the smaller buffer size between the new and the old one
639  old_size = UNTAGGED_CHUNK_SIZE(m_area);
640 
641  new_buffer = __wrap_malloc(size);
642 
643  if (unlikely(new_buffer == NULL))
644  return NULL;
645 
646  copy_size = min(size, old_size);
647  memcpy(new_buffer, ptr, copy_size);
648  __wrap_free(ptr);
649 
650  return new_buffer;
651 }
652 
664 void *__wrap_calloc(size_t nmemb, size_t size)
665 {
666  void *buffer;
667 
669  return rscalloc(nmemb, size);
670  }
671 
672  if (unlikely(nmemb == 0 || size == 0))
673  return NULL;
674 
675  buffer = __wrap_malloc(nmemb * size);
676  if (unlikely(buffer == NULL))
677  return NULL;
678 
679  bzero(buffer, nmemb * size);
680 
681  return buffer;
682 }
683 
684 void clean_buffers_on_gvt(struct lp_struct *lp, simtime_t time_barrier)
685 {
686  int i;
687  malloc_area *m_area;
688  malloc_state *state = lp->mm->m_state;
689 
690  // The first NUM_AREAS malloc_areas are placed according to their chunks' sizes. The exceeding malloc_areas can be compacted
691  for (i = NUM_AREAS; i < state->num_areas; i++) {
692  m_area = &state->areas[i];
693 
694  if (m_area->alloc_chunks == 0
695  && m_area->last_access < time_barrier
696  && !CHECK_AREA_LOCK_BIT(m_area)) {
697 
698  if (m_area->self_pointer != NULL) {
699 
700  //free_lp_memory(lp, m_area->self_pointer);
701  rsfree(m_area->self_pointer);
702 
703  m_area->use_bitmap = NULL;
704  m_area->dirty_bitmap = NULL;
705  m_area->self_pointer = NULL;
706  m_area->area = NULL;
707  m_area->state_changed = 0;
708 
709  // Set the pointers
710  if (m_area->prev != -1)
711  state->areas[m_area->prev].next = m_area->next;
712  if (m_area->next != -1)
713  state->areas[m_area->next].prev = m_area->prev;
714 
715  // Swap, if possible
716  if (i < state->num_areas - 1) {
717  memcpy(m_area, &state->areas[state->num_areas - 1], sizeof(malloc_area));
718  m_area->idx = i;
719  if (m_area->prev != -1)
720  state->areas[m_area->prev].next = m_area->idx;
721  if (m_area->next != -1)
722  state->areas[m_area->next].prev = m_area->idx;
723 
724  // Update the self pointer
725  *(long long *)m_area->self_pointer = (long long)m_area;
726 
727  // The swapped area will now be checked
728  i--;
729  }
730  // Decrement the expected number of areas
731  state->num_areas--;
732  }
733  }
734  }
735 }
#define lvt(lp)
Definition: process.h:168
Initialization routines.
#define bitmap_set(bitmap, bit_index)
This sets the bit with index bit_index of the bitmap bitmap.
Definition: bitmap.h:116
#define min(a, b)
Macro to find the minimum among two values.
Definition: core.h:115
static size_t compute_size(size_t size)
Definition: dymelor.c:131
void * do_malloc(struct lp_struct *lp, size_t size)
Definition: dymelor.c:167
#define atomic_read(v)
Read operation on an atomic counter.
Definition: atomic.h:66
#define bitmap_required_size(requested_bits)
Computes the required size of a bitmap with requested_bits entries.
Definition: bitmap.h:92
Definition: mm.h:41
void dirty_mem(void *base, int size)
Definition: dymelor.c:445
void atomic_dec(atomic_t *)
Definition: x86.c:103
void * __wrap_calloc(size_t nmemb, size_t size)
Definition: dymelor.c:664
Dynamic Memory Logger and Restorer (DyMeLoR)
The ROOT-Sim scheduler main module header.
#define bitmap_reset(bitmap, bit_index)
This resets the bit with index bit_index of the bitmap bitmap.
Definition: bitmap.h:125
struct memory_map * mm
Memory map of the LP.
Definition: process.h:76
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
void __wrap_free(void *ptr)
Definition: dymelor.c:589
bool is_incremental
Tells if it is an incremental log or a full one (when used for logging)
Definition: dymelor.h:121
#define atomic_set(v, i)
Set operation on an atomic counter.
Definition: atomic.h:69
double simtime_t
This defines the type with whom timestamps are represented.
Definition: ROOT-Sim.h:56
simulation_configuration rootsim_config
This global variable holds the configuration for the current simulation.
Definition: core.c:70
static void malloc_area_init(malloc_area *m_area, size_t size, int num_chunks)
Definition: dymelor.c:50
Memory Manager main header.
static void find_next_free(malloc_area *m_area)
Definition: dymelor.c:154
#define bitmap_check(bitmap, bit_index)
This checks if the bit with index bit_index of the bitmap bitmap is set or unset. ...
Definition: bitmap.h:135
void * __wrap_realloc(void *ptr, size_t size)
Definition: dymelor.c:615
Definition of the memory map.
Definition: dymelor.h:120
size_t get_log_size(malloc_state *logged_state)
Definition: dymelor.c:526
malloc_state * malloc_state_init(void)
Definition: dymelor.c:78
void * __wrap_malloc(size_t size)
Definition: dymelor.c:553
bool serial
If the simulation must be run serially.
Definition: init.h:71
void atomic_inc(atomic_t *)
Definition: x86.c:90
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74
This structure let DyMeLoR handle one malloc area (for serving given-size memory requests) ...
Definition: dymelor.h:97