Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
aad1013626a49a593de9bd52acd09ea28038b9cd
[simgrid.git] / src / mc / compare.cpp
1 /* Copyright (c) 2008-2019. 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 /** \file compare.cpp Memory snapshooting and comparison                    */
7
8 #include "src/mc/mc_config.hpp"
9 #include "src/mc/mc_private.hpp"
10 #include "src/mc/mc_smx.hpp"
11 #include "src/mc/sosp/Snapshot.hpp"
12
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_compare, xbt, "Logging specific to mc_compare in mc");
14
15 namespace simgrid {
16 namespace mc {
17
18 struct HeapLocation;
19 typedef std::array<HeapLocation, 2> HeapLocationPair;
20 typedef std::set<HeapLocationPair> HeapLocationPairs;
21 struct HeapArea;
22 struct ProcessComparisonState;
23 struct StateComparator;
24
25 static int compare_heap_area(StateComparator& state, const void* area1, const void* area2, Snapshot* snapshot1,
26                              Snapshot* snapshot2, HeapLocationPairs* previous, Type* type, int pointer_level);
27 }
28 }
29
30 using simgrid::mc::remote;
31
32 /*********************************** Heap comparison ***********************************/
33 /***************************************************************************************/
34
35 namespace simgrid {
36 namespace mc {
37
38 class HeapLocation {
39 public:
40   int block_    = 0;
41   int fragment_ = 0;
42
43   HeapLocation() = default;
44   HeapLocation(int block, int fragment = 0) : block_(block), fragment_(fragment) {}
45
46   bool operator==(HeapLocation const& that) const
47   {
48     return block_ == that.block_ && fragment_ == that.fragment_;
49   }
50   bool operator<(HeapLocation const& that) const
51   {
52     return std::make_pair(block_, fragment_) < std::make_pair(that.block_, that.fragment_);
53   }
54 };
55
56 static inline
57 HeapLocationPair makeHeapLocationPair(int block1, int fragment1, int block2, int fragment2)
58 {
59   return simgrid::mc::HeapLocationPair{{
60     simgrid::mc::HeapLocation(block1, fragment1),
61     simgrid::mc::HeapLocation(block2, fragment2)
62   }};
63 }
64
65 class HeapArea : public HeapLocation {
66 public:
67   bool valid_ = false;
68   HeapArea() = default;
69   explicit HeapArea(int block) : valid_(true) { block_ = block; }
70   HeapArea(int block, int fragment) : valid_(true)
71   {
72     block_    = block;
73     fragment_ = fragment;
74   }
75 };
76
77 class ProcessComparisonState {
78 public:
79   std::vector<simgrid::mc::IgnoredHeapRegion>* to_ignore = nullptr;
80   std::vector<HeapArea> equals_to;
81   std::vector<simgrid::mc::Type*> types;
82   std::size_t heapsize = 0;
83
84   void initHeapInformation(xbt_mheap_t heap, std::vector<simgrid::mc::IgnoredHeapRegion>* i);
85 };
86
87 namespace {
88
89 /** A hash which works with more stuff
90  *
91  *  It can hash pairs: the standard hash currently doesn't include this.
92  */
93 template <class X> class hash : public std::hash<X> {
94 };
95
96 template <class X, class Y> class hash<std::pair<X, Y>> {
97 public:
98   std::size_t operator()(std::pair<X,Y>const& x) const
99   {
100     hash<X> h1;
101     hash<X> h2;
102     return h1(x.first) ^ h2(x.second);
103   }
104 };
105
106 }
107
108 class StateComparator {
109 public:
110   s_xbt_mheap_t std_heap_copy;
111   std::size_t heaplimit;
112   std::array<ProcessComparisonState, 2> processStates;
113
114   std::unordered_set<std::pair<void*, void*>, hash<std::pair<void*, void*>>> compared_pointers;
115
116   void clear()
117   {
118     compared_pointers.clear();
119   }
120
121   int initHeapInformation(
122     xbt_mheap_t heap1, xbt_mheap_t heap2,
123     std::vector<simgrid::mc::IgnoredHeapRegion>* i1,
124     std::vector<simgrid::mc::IgnoredHeapRegion>* i2);
125
126   HeapArea& equals_to1_(std::size_t i, std::size_t j)
127   {
128     return processStates[0].equals_to[ MAX_FRAGMENT_PER_BLOCK * i + j];
129   }
130   HeapArea& equals_to2_(std::size_t i, std::size_t j)
131   {
132     return processStates[1].equals_to[ MAX_FRAGMENT_PER_BLOCK * i + j];
133   }
134   Type*& types1_(std::size_t i, std::size_t j)
135   {
136     return processStates[0].types[ MAX_FRAGMENT_PER_BLOCK * i + j];
137   }
138   Type*& types2_(std::size_t i, std::size_t j)
139   {
140     return processStates[1].types[ MAX_FRAGMENT_PER_BLOCK * i + j];
141   }
142
143   HeapArea const& equals_to1_(std::size_t i, std::size_t j) const
144   {
145     return processStates[0].equals_to[ MAX_FRAGMENT_PER_BLOCK * i + j];
146   }
147   HeapArea const& equals_to2_(std::size_t i, std::size_t j) const
148   {
149     return processStates[1].equals_to[ MAX_FRAGMENT_PER_BLOCK * i + j];
150   }
151   Type* const& types1_(std::size_t i, std::size_t j) const
152   {
153     return processStates[0].types[ MAX_FRAGMENT_PER_BLOCK * i + j];
154   }
155   Type* const& types2_(std::size_t i, std::size_t j) const
156   {
157     return processStates[1].types[ MAX_FRAGMENT_PER_BLOCK * i + j];
158   }
159
160   /** Check whether two blocks are known to be matching
161    *
162    *  @param b1     Block of state 1
163    *  @param b2     Block of state 2
164    *  @return       if the blocks are known to be matching
165    */
166   bool blocksEqual(int b1, int b2) const
167   {
168     return this->equals_to1_(b1, 0).block_ == b2 && this->equals_to2_(b2, 0).block_ == b1;
169   }
170
171   /** Check whether two fragments are known to be matching
172    *
173    *  @param b1     Block of state 1
174    *  @param f1     Fragment of state 1
175    *  @param b2     Block of state 2
176    *  @param f2     Fragment of state 2
177    *  @return       if the fragments are known to be matching
178    */
179   int fragmentsEqual(int b1, int f1, int b2, int f2) const
180   {
181     return this->equals_to1_(b1, f1).block_ == b2 && this->equals_to1_(b1, f1).fragment_ == f2 &&
182            this->equals_to2_(b2, f2).block_ == b1 && this->equals_to2_(b2, f2).fragment_ == f1;
183   }
184
185   void match_equals(HeapLocationPairs* list);
186 };
187
188 }
189 }
190
191 /************************************************************************************/
192
193 static ssize_t heap_comparison_ignore_size(
194   std::vector<simgrid::mc::IgnoredHeapRegion>* ignore_list,
195   const void *address)
196 {
197   int start = 0;
198   int end = ignore_list->size() - 1;
199
200   while (start <= end) {
201     unsigned int cursor = (start + end) / 2;
202     simgrid::mc::IgnoredHeapRegion const& region = (*ignore_list)[cursor];
203     if (region.address == address)
204       return region.size;
205     if (region.address < address)
206       start = cursor + 1;
207     if (region.address > address)
208       end = cursor - 1;
209   }
210
211   return -1;
212 }
213
214 static bool is_stack(const void *address)
215 {
216   for (auto const& stack : mc_model_checker->process().stack_areas())
217     if (address == stack.address)
218       return true;
219   return false;
220 }
221
222 // TODO, this should depend on the snapshot?
223 static bool is_block_stack(int block)
224 {
225   for (auto const& stack : mc_model_checker->process().stack_areas())
226     if (block == stack.block)
227       return true;
228   return false;
229 }
230
231 namespace simgrid {
232 namespace mc {
233
234 void StateComparator::match_equals(HeapLocationPairs* list)
235 {
236   for (auto const& pair : *list) {
237     if (pair[0].fragment_ != -1) {
238       this->equals_to1_(pair[0].block_, pair[0].fragment_) = simgrid::mc::HeapArea(pair[1].block_, pair[1].fragment_);
239       this->equals_to2_(pair[1].block_, pair[1].fragment_) = simgrid::mc::HeapArea(pair[0].block_, pair[0].fragment_);
240     } else {
241       this->equals_to1_(pair[0].block_, 0) = simgrid::mc::HeapArea(pair[1].block_, pair[1].fragment_);
242       this->equals_to2_(pair[1].block_, 0) = simgrid::mc::HeapArea(pair[0].block_, pair[0].fragment_);
243     }
244   }
245 }
246
247 void ProcessComparisonState::initHeapInformation(xbt_mheap_t heap,
248                         std::vector<simgrid::mc::IgnoredHeapRegion>* i)
249 {
250   auto heaplimit  = heap->heaplimit;
251   this->heapsize  = heap->heapsize;
252   this->to_ignore = i;
253   this->equals_to.assign(heaplimit * MAX_FRAGMENT_PER_BLOCK, HeapArea());
254   this->types.assign(heaplimit * MAX_FRAGMENT_PER_BLOCK, nullptr);
255 }
256
257 int StateComparator::initHeapInformation(xbt_mheap_t heap1, xbt_mheap_t heap2,
258                           std::vector<simgrid::mc::IgnoredHeapRegion>* i1,
259                           std::vector<simgrid::mc::IgnoredHeapRegion>* i2)
260 {
261   if ((heap1->heaplimit != heap2->heaplimit) || (heap1->heapsize != heap2->heapsize))
262     return -1;
263   this->heaplimit     = heap1->heaplimit;
264   this->std_heap_copy = *mc_model_checker->process().get_heap();
265   this->processStates[0].initHeapInformation(heap1, i1);
266   this->processStates[1].initHeapInformation(heap2, i2);
267   return 0;
268 }
269
270 // TODO, have a robust way to find it in O(1)
271 static inline Region* MC_get_heap_region(Snapshot* snapshot)
272 {
273   for (auto const& region : snapshot->snapshot_regions_)
274     if (region->region_type() == simgrid::mc::RegionType::Heap)
275       return region.get();
276   xbt_die("No heap region");
277 }
278
279 static
280 int mmalloc_compare_heap(
281   simgrid::mc::StateComparator& state, simgrid::mc::Snapshot* snapshot1, simgrid::mc::Snapshot* snapshot2)
282 {
283   simgrid::mc::RemoteClient* process = &mc_model_checker->process();
284
285   /* Start comparison */
286   int nb_diff1 = 0;
287   int nb_diff2 = 0;
288   bool equal;
289
290   /* Check busy blocks */
291   size_t i1 = 1;
292
293   malloc_info heapinfo_temp1;
294   malloc_info heapinfo_temp2;
295   malloc_info heapinfo_temp2b;
296
297   simgrid::mc::Region* heap_region1 = MC_get_heap_region(snapshot1);
298   simgrid::mc::Region* heap_region2 = MC_get_heap_region(snapshot2);
299
300   // This is the address of std_heap->heapinfo in the application process:
301   void* heapinfo_address = &((xbt_mheap_t) process->heap_address)->heapinfo;
302
303   // This is in snapshot do not use them directly:
304   const malloc_info* heapinfos1 =
305       snapshot1->read<malloc_info*>(RemotePtr<malloc_info*>((std::uint64_t)heapinfo_address));
306   const malloc_info* heapinfos2 =
307       snapshot2->read<malloc_info*>(RemotePtr<malloc_info*>((std::uint64_t)heapinfo_address));
308
309   while (i1 < state.heaplimit) {
310
311     const malloc_info* heapinfo1 =
312         (const malloc_info*)heap_region1->read(&heapinfo_temp1, &heapinfos1[i1], sizeof(malloc_info));
313     const malloc_info* heapinfo2 =
314         (const malloc_info*)heap_region2->read(&heapinfo_temp2, &heapinfos2[i1], sizeof(malloc_info));
315
316     if (heapinfo1->type == MMALLOC_TYPE_FREE || heapinfo1->type == MMALLOC_TYPE_HEAPINFO) {      /* Free block */
317       i1 ++;
318       continue;
319     }
320
321     if (heapinfo1->type < 0) {
322       fprintf(stderr, "Unkown mmalloc block type.\n");
323       abort();
324     }
325
326     void* addr_block1 = ((void*)(((ADDR2UINT(i1)) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase));
327
328     if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED) {       /* Large block */
329
330       if (is_stack(addr_block1)) {
331         for (size_t k = 0; k < heapinfo1->busy_block.size; k++)
332           state.equals_to1_(i1 + k, 0) = HeapArea(i1, -1);
333         for (size_t k = 0; k < heapinfo2->busy_block.size; k++)
334           state.equals_to2_(i1 + k, 0) = HeapArea(i1, -1);
335         i1 += heapinfo1->busy_block.size;
336         continue;
337       }
338
339       if (state.equals_to1_(i1, 0).valid_) {
340         i1++;
341         continue;
342       }
343
344       size_t i2 = 1;
345       equal     = false;
346
347       /* Try first to associate to same block in the other heap */
348       if (heapinfo2->type == heapinfo1->type && state.equals_to2_(i1, 0).valid_ == 0) {
349         void* addr_block2 = (ADDR2UINT(i1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
350         int res_compare = compare_heap_area(state, addr_block1, addr_block2, snapshot1, snapshot2, nullptr, nullptr, 0);
351         if (res_compare != 1) {
352           for (size_t k = 1; k < heapinfo2->busy_block.size; k++)
353             state.equals_to2_(i1 + k, 0) = HeapArea(i1, -1);
354           for (size_t k = 1; k < heapinfo1->busy_block.size; k++)
355             state.equals_to1_(i1 + k, 0) = HeapArea(i1, -1);
356           equal = true;
357           i1 += heapinfo1->busy_block.size;
358         }
359       }
360
361       while (i2 < state.heaplimit && not equal) {
362
363         void* addr_block2 = (ADDR2UINT(i2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
364
365         if (i2 == i1) {
366           i2++;
367           continue;
368         }
369
370         const malloc_info* heapinfo2b =
371             (const malloc_info*)heap_region2->read(&heapinfo_temp2b, &heapinfos2[i2], sizeof(malloc_info));
372
373         if (heapinfo2b->type != MMALLOC_TYPE_UNFRAGMENTED) {
374           i2++;
375           continue;
376         }
377
378         if (state.equals_to2_(i2, 0).valid_) {
379           i2++;
380           continue;
381         }
382
383         int res_compare = compare_heap_area(state, addr_block1, addr_block2, snapshot1, snapshot2, nullptr, nullptr, 0);
384
385         if (res_compare != 1) {
386           for (size_t k = 1; k < heapinfo2b->busy_block.size; k++)
387             state.equals_to2_(i2 + k, 0) = HeapArea(i1, -1);
388           for (size_t k = 1; k < heapinfo1->busy_block.size; k++)
389             state.equals_to1_(i1 + k, 0) = HeapArea(i2, -1);
390           equal = true;
391           i1 += heapinfo1->busy_block.size;
392         }
393
394         i2++;
395       }
396
397       if (not equal) {
398         XBT_DEBUG("Block %zu not found (size_used = %zu, addr = %p)", i1, heapinfo1->busy_block.busy_size, addr_block1);
399         i1 = state.heaplimit + 1;
400         nb_diff1++;
401       }
402
403     } else {                    /* Fragmented block */
404
405       for (size_t j1 = 0; j1 < (size_t)(BLOCKSIZE >> heapinfo1->type); j1++) {
406
407         if (heapinfo1->busy_frag.frag_size[j1] == -1) /* Free fragment_ */
408           continue;
409
410         if (state.equals_to1_(i1, j1).valid_)
411           continue;
412
413         void* addr_frag1 = (void*)((char*)addr_block1 + (j1 << heapinfo1->type));
414
415         size_t i2 = 1;
416         equal     = false;
417
418         /* Try first to associate to same fragment_ in the other heap */
419         if (heapinfo2->type == heapinfo1->type && not state.equals_to2_(i1, j1).valid_) {
420           void* addr_block2 = (ADDR2UINT(i1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
421           void* addr_frag2  = (void*)((char*)addr_block2 + (j1 << heapinfo2->type));
422           int res_compare = compare_heap_area(state, addr_frag1, addr_frag2, snapshot1, snapshot2, nullptr, nullptr, 0);
423           if (res_compare != 1)
424             equal = true;
425         }
426
427         while (i2 < state.heaplimit && not equal) {
428
429           const malloc_info* heapinfo2b =
430               (const malloc_info*)heap_region2->read(&heapinfo_temp2b, &heapinfos2[i2], sizeof(malloc_info));
431
432           if (heapinfo2b->type == MMALLOC_TYPE_FREE || heapinfo2b->type == MMALLOC_TYPE_HEAPINFO) {
433             i2 ++;
434             continue;
435           }
436
437           // We currently do not match fragments with unfragmented blocks (maybe we should).
438           if (heapinfo2b->type == MMALLOC_TYPE_UNFRAGMENTED) {
439             i2++;
440             continue;
441           }
442
443           if (heapinfo2b->type < 0) {
444             fprintf(stderr, "Unknown mmalloc block type.\n");
445             abort();
446           }
447
448           for (size_t j2 = 0; j2 < (size_t)(BLOCKSIZE >> heapinfo2b->type); j2++) {
449
450             if (i2 == i1 && j2 == j1)
451               continue;
452
453             if (state.equals_to2_(i2, j2).valid_)
454               continue;
455
456             void* addr_block2 = (ADDR2UINT(i2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
457             void* addr_frag2  = (void*)((char*)addr_block2 + (j2 << heapinfo2b->type));
458
459             int res_compare =
460                 compare_heap_area(state, addr_frag1, addr_frag2, snapshot2, snapshot2, nullptr, nullptr, 0);
461             if (res_compare != 1) {
462               equal = true;
463               break;
464             }
465           }
466
467           i2++;
468         }
469
470         if (not equal) {
471           XBT_DEBUG("Block %zu, fragment_ %zu not found (size_used = %zd, address = %p)\n", i1, j1,
472                     heapinfo1->busy_frag.frag_size[j1], addr_frag1);
473           i1 = state.heaplimit + 1;
474           nb_diff1++;
475           break;
476         }
477       }
478
479       i1++;
480     }
481   }
482
483   /* All blocks/fragments are equal to another block/fragment_ ? */
484   for (size_t i = 1; i < state.heaplimit; i++) {
485     const malloc_info* heapinfo1 =
486         (const malloc_info*)heap_region1->read(&heapinfo_temp1, &heapinfos1[i], sizeof(malloc_info));
487
488     if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED && i1 == state.heaplimit && heapinfo1->busy_block.busy_size > 0 &&
489         not state.equals_to1_(i, 0).valid_) {
490       XBT_DEBUG("Block %zu not found (size used = %zu)", i, heapinfo1->busy_block.busy_size);
491       nb_diff1++;
492     }
493
494     if (heapinfo1->type <= 0)
495       continue;
496     for (size_t j = 0; j < (size_t)(BLOCKSIZE >> heapinfo1->type); j++)
497       if (i1 == state.heaplimit && heapinfo1->busy_frag.frag_size[j] > 0 && not state.equals_to1_(i, j).valid_) {
498         XBT_DEBUG("Block %zu, Fragment %zu not found (size used = %zd)", i, j, heapinfo1->busy_frag.frag_size[j]);
499         nb_diff1++;
500       }
501   }
502
503   if (i1 == state.heaplimit)
504     XBT_DEBUG("Number of blocks/fragments not found in heap1: %d", nb_diff1);
505
506   for (size_t i = 1; i < state.heaplimit; i++) {
507     const malloc_info* heapinfo2 =
508         (const malloc_info*)heap_region2->read(&heapinfo_temp2, &heapinfos2[i], sizeof(malloc_info));
509     if (heapinfo2->type == MMALLOC_TYPE_UNFRAGMENTED && i1 == state.heaplimit && heapinfo2->busy_block.busy_size > 0 &&
510         not state.equals_to2_(i, 0).valid_) {
511       XBT_DEBUG("Block %zu not found (size used = %zu)", i,
512                 heapinfo2->busy_block.busy_size);
513       nb_diff2++;
514     }
515
516     if (heapinfo2->type <= 0)
517       continue;
518
519     for (size_t j = 0; j < (size_t)(BLOCKSIZE >> heapinfo2->type); j++)
520       if (i1 == state.heaplimit && heapinfo2->busy_frag.frag_size[j] > 0 && not state.equals_to2_(i, j).valid_) {
521         XBT_DEBUG("Block %zu, Fragment %zu not found (size used = %zd)",
522           i, j, heapinfo2->busy_frag.frag_size[j]);
523         nb_diff2++;
524       }
525
526   }
527
528   if (i1 == state.heaplimit)
529     XBT_DEBUG("Number of blocks/fragments not found in heap2: %d", nb_diff2);
530
531   return nb_diff1 > 0 || nb_diff2 > 0;
532 }
533
534 /**
535  *
536  * @param state
537  * @param real_area1     Process address for state 1
538  * @param real_area2     Process address for state 2
539  * @param snapshot1      Snapshot of state 1
540  * @param snapshot2      Snapshot of state 2
541  * @param previous
542  * @param size
543  * @param check_ignore
544  */
545 static int compare_heap_area_without_type(simgrid::mc::StateComparator& state, const void* real_area1,
546                                           const void* real_area2, simgrid::mc::Snapshot* snapshot1,
547                                           simgrid::mc::Snapshot* snapshot2, HeapLocationPairs* previous, int size,
548                                           int check_ignore)
549 {
550   simgrid::mc::RemoteClient* process = &mc_model_checker->process();
551   simgrid::mc::Region* heap_region1  = MC_get_heap_region(snapshot1);
552   simgrid::mc::Region* heap_region2  = MC_get_heap_region(snapshot2);
553
554   for (int i = 0; i < size; ) {
555
556     if (check_ignore > 0) {
557       ssize_t ignore1 = heap_comparison_ignore_size(
558         state.processStates[0].to_ignore, (char *) real_area1 + i);
559       if (ignore1 != -1) {
560         ssize_t ignore2 = heap_comparison_ignore_size(
561           state.processStates[1].to_ignore, (char *) real_area2 + i);
562         if (ignore2 == ignore1) {
563           if (ignore1 == 0) {
564             check_ignore--;
565             return 0;
566           } else {
567             i = i + ignore2;
568             check_ignore--;
569             continue;
570           }
571         }
572       }
573     }
574
575     if (MC_snapshot_region_memcmp(((char *) real_area1) + i, heap_region1, ((char *) real_area2) + i, heap_region2, 1) != 0) {
576
577       int pointer_align = (i / sizeof(void *)) * sizeof(void *);
578       const void* addr_pointed1 = snapshot1->read(remote((void**)((char*)real_area1 + pointer_align)));
579       const void* addr_pointed2 = snapshot2->read(remote((void**)((char*)real_area2 + pointer_align)));
580
581       if (process->in_maestro_stack(remote(addr_pointed1))
582         && process->in_maestro_stack(remote(addr_pointed2))) {
583         i = pointer_align + sizeof(void *);
584         continue;
585       }
586
587       if (addr_pointed1 > state.std_heap_copy.heapbase
588            && addr_pointed1 < mc_snapshot_get_heap_end(snapshot1)
589            && addr_pointed2 > state.std_heap_copy.heapbase
590            && addr_pointed2 < mc_snapshot_get_heap_end(snapshot2)) {
591         // Both addreses are in the heap:
592         int res_compare =
593             compare_heap_area(state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, previous, nullptr, 0);
594         if (res_compare == 1)
595           return res_compare;
596         i = pointer_align + sizeof(void *);
597         continue;
598       }
599
600       return 1;
601     }
602
603     i++;
604   }
605
606   return 0;
607 }
608
609 /**
610  *
611  * @param state
612  * @param real_area1     Process address for state 1
613  * @param real_area2     Process address for state 2
614  * @param snapshot1      Snapshot of state 1
615  * @param snapshot2      Snapshot of state 2
616  * @param previous
617  * @param type
618  * @param area_size      either a byte_size or an elements_count (?)
619  * @param check_ignore
620  * @param pointer_level
621  * @return               0 (same), 1 (different), -1 (unknown)
622  */
623 static int compare_heap_area_with_type(simgrid::mc::StateComparator& state, const void* real_area1,
624                                        const void* real_area2, simgrid::mc::Snapshot* snapshot1,
625                                        simgrid::mc::Snapshot* snapshot2, HeapLocationPairs* previous,
626                                        simgrid::mc::Type* type, int area_size, int check_ignore, int pointer_level)
627 {
628   do {
629
630     // HACK: This should not happen but in pratice, there are some
631     // DW_TAG_typedef without an associated DW_AT_type:
632     //<1><538832>: Abbrev Number: 111 (DW_TAG_typedef)
633     //    <538833>   DW_AT_name        : (indirect string, offset: 0x2292f3): gregset_t
634     //    <538837>   DW_AT_decl_file   : 98
635     //    <538838>   DW_AT_decl_line   : 37
636     if (type == nullptr)
637       return 0;
638
639     if (is_stack(real_area1) && is_stack(real_area2))
640       return 0;
641
642     if (check_ignore > 0) {
643       ssize_t ignore1 = heap_comparison_ignore_size(state.processStates[0].to_ignore, real_area1);
644       if (ignore1 > 0 && heap_comparison_ignore_size(state.processStates[1].to_ignore, real_area2) == ignore1)
645         return 0;
646     }
647
648     simgrid::mc::Type* subtype;
649     simgrid::mc::Type* subsubtype;
650     int elm_size;
651     const void* addr_pointed1;
652     const void* addr_pointed2;
653
654     simgrid::mc::Region* heap_region1 = MC_get_heap_region(snapshot1);
655     simgrid::mc::Region* heap_region2 = MC_get_heap_region(snapshot2);
656
657     switch (type->type) {
658       case DW_TAG_unspecified_type:
659         return 1;
660
661       case DW_TAG_base_type:
662         if (not type->name.empty() && type->name == "char") { /* String, hence random (arbitrary ?) size */
663           if (real_area1 == real_area2)
664             return -1;
665           else
666             return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, area_size) != 0;
667         } else {
668           if (area_size != -1 && type->byte_size != area_size)
669             return -1;
670           else
671             return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, type->byte_size) != 0;
672         }
673
674       case DW_TAG_enumeration_type:
675         if (area_size != -1 && type->byte_size != area_size)
676           return -1;
677         return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, type->byte_size) != 0;
678
679       case DW_TAG_typedef:
680       case DW_TAG_const_type:
681       case DW_TAG_volatile_type:
682         // Poor man's TCO:
683         type = type->subtype;
684         continue; // restart
685
686       case DW_TAG_array_type:
687         subtype = type->subtype;
688         switch (subtype->type) {
689           case DW_TAG_unspecified_type:
690             return 1;
691
692           case DW_TAG_base_type:
693           case DW_TAG_enumeration_type:
694           case DW_TAG_pointer_type:
695           case DW_TAG_reference_type:
696           case DW_TAG_rvalue_reference_type:
697           case DW_TAG_structure_type:
698           case DW_TAG_class_type:
699           case DW_TAG_union_type:
700             if (subtype->full_type)
701               subtype = subtype->full_type;
702             elm_size  = subtype->byte_size;
703             break;
704           // TODO, just remove the type indirection?
705           case DW_TAG_const_type:
706           case DW_TAG_typedef:
707           case DW_TAG_volatile_type:
708             subsubtype = subtype->subtype;
709             if (subsubtype->full_type)
710               subsubtype = subsubtype->full_type;
711             elm_size     = subsubtype->byte_size;
712             break;
713           default:
714             return 0;
715         }
716         for (int i = 0; i < type->element_count; i++) {
717           // TODO, add support for variable stride (DW_AT_byte_stride)
718           int res = compare_heap_area_with_type(state, (char*)real_area1 + (i * elm_size),
719                                                 (char*)real_area2 + (i * elm_size), snapshot1, snapshot2, previous,
720                                                 type->subtype, subtype->byte_size, check_ignore, pointer_level);
721           if (res == 1)
722             return res;
723         }
724         return 0;
725
726       case DW_TAG_reference_type:
727       case DW_TAG_rvalue_reference_type:
728       case DW_TAG_pointer_type:
729         if (type->subtype && type->subtype->type == DW_TAG_subroutine_type) {
730           addr_pointed1 = snapshot1->read(remote((void**)real_area1));
731           addr_pointed2 = snapshot2->read(remote((void**)real_area2));
732           return (addr_pointed1 != addr_pointed2);
733         }
734         pointer_level++;
735         if (pointer_level <= 1) {
736           addr_pointed1 = snapshot1->read(remote((void**)real_area1));
737           addr_pointed2 = snapshot2->read(remote((void**)real_area2));
738           if (addr_pointed1 > state.std_heap_copy.heapbase && addr_pointed1 < mc_snapshot_get_heap_end(snapshot1) &&
739               addr_pointed2 > state.std_heap_copy.heapbase && addr_pointed2 < mc_snapshot_get_heap_end(snapshot2))
740             return compare_heap_area(state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, previous, type->subtype,
741                                      pointer_level);
742           else
743             return (addr_pointed1 != addr_pointed2);
744         }
745         for (size_t i = 0; i < (area_size / sizeof(void*)); i++) {
746           addr_pointed1 = snapshot1->read(remote((void**)((char*)real_area1 + i * sizeof(void*))));
747           addr_pointed2 = snapshot2->read(remote((void**)((char*)real_area2 + i * sizeof(void*))));
748           int res;
749           if (addr_pointed1 > state.std_heap_copy.heapbase && addr_pointed1 < mc_snapshot_get_heap_end(snapshot1) &&
750               addr_pointed2 > state.std_heap_copy.heapbase && addr_pointed2 < mc_snapshot_get_heap_end(snapshot2))
751             res = compare_heap_area(state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, previous, type->subtype,
752                                     pointer_level);
753           else
754             res = (addr_pointed1 != addr_pointed2);
755           if (res == 1)
756             return res;
757         }
758         return 0;
759
760       case DW_TAG_structure_type:
761       case DW_TAG_class_type:
762         if (type->full_type)
763           type = type->full_type;
764         if (area_size != -1 && type->byte_size != area_size) {
765           if (area_size <= type->byte_size || area_size % type->byte_size != 0)
766             return -1;
767           for (size_t i = 0; i < (size_t)(area_size / type->byte_size); i++) {
768             int res = compare_heap_area_with_type(state, (char*)real_area1 + i * type->byte_size,
769                                                   (char*)real_area2 + i * type->byte_size, snapshot1, snapshot2,
770                                                   previous, type, -1, check_ignore, 0);
771             if (res == 1)
772               return res;
773           }
774         } else {
775           for (simgrid::mc::Member& member : type->members) {
776             // TODO, optimize this? (for the offset case)
777             void* real_member1 =
778                 simgrid::dwarf::resolve_member(real_area1, type, &member, (simgrid::mc::AddressSpace*)snapshot1);
779             void* real_member2 =
780                 simgrid::dwarf::resolve_member(real_area2, type, &member, (simgrid::mc::AddressSpace*)snapshot2);
781             int res = compare_heap_area_with_type(state, real_member1, real_member2, snapshot1, snapshot2, previous,
782                                                   member.type, -1, check_ignore, 0);
783             if (res == 1)
784               return res;
785           }
786         }
787         return 0;
788
789       case DW_TAG_union_type:
790         return compare_heap_area_without_type(state, real_area1, real_area2, snapshot1, snapshot2, previous,
791                                               type->byte_size, check_ignore);
792
793       default:
794         return 0;
795     }
796
797     xbt_die("Unreachable");
798   } while (true);
799 }
800
801 /** Infer the type of a part of the block from the type of the block
802  *
803  * TODO, handle DW_TAG_array_type as well as arrays of the object ((*p)[5], p[5])
804  *
805  * TODO, handle subfields ((*p).bar.foo, (*p)[5].bar…)
806  *
807  * @param  type               DWARF type ID of the root address
808  * @param  area_size
809  * @return                    DWARF type ID for given offset
810  */
811 static simgrid::mc::Type* get_offset_type(void* real_base_address, simgrid::mc::Type* type, int offset, int area_size,
812                                           simgrid::mc::Snapshot* snapshot)
813 {
814
815   // Beginning of the block, the infered variable type if the type of the block:
816   if (offset == 0)
817     return type;
818
819   switch (type->type) {
820
821   case DW_TAG_structure_type:
822   case DW_TAG_class_type:
823     if (type->full_type)
824       type = type->full_type;
825     if (area_size != -1 && type->byte_size != area_size) {
826       if (area_size > type->byte_size && area_size % type->byte_size == 0)
827         return type;
828       else
829         return nullptr;
830     }
831
832     for (simgrid::mc::Member& member : type->members) {
833       if (member.has_offset_location()) {
834         // We have the offset, use it directly (shortcut):
835         if (member.offset() == offset)
836           return member.type;
837       } else {
838         void* real_member = simgrid::dwarf::resolve_member(real_base_address, type, &member, snapshot);
839         if ((char*)real_member - (char*)real_base_address == offset)
840           return member.type;
841       }
842     }
843     return nullptr;
844
845   default:
846     /* FIXME: other cases ? */
847     return nullptr;
848
849   }
850 }
851
852 /**
853  *
854  * @param area1          Process address for state 1
855  * @param area2          Process address for state 2
856  * @param snapshot1      Snapshot of state 1
857  * @param snapshot2      Snapshot of state 2
858  * @param previous       Pairs of blocks already compared on the current path (or nullptr)
859  * @param type_id        Type of variable
860  * @param pointer_level
861  * @return 0 (same), 1 (different), -1
862  */
863 static int compare_heap_area(simgrid::mc::StateComparator& state, const void* area1, const void* area2,
864                              simgrid::mc::Snapshot* snapshot1, simgrid::mc::Snapshot* snapshot2,
865                              HeapLocationPairs* previous, simgrid::mc::Type* type, int pointer_level)
866 {
867   simgrid::mc::RemoteClient* process = &mc_model_checker->process();
868
869   ssize_t block1;
870   ssize_t block2;
871   ssize_t size;
872   int check_ignore = 0;
873
874   int type_size = -1;
875   int offset1   = 0;
876   int offset2   = 0;
877   int new_size1 = -1;
878   int new_size2 = -1;
879
880   simgrid::mc::Type* new_type1 = nullptr;
881   simgrid::mc::Type* new_type2 = nullptr;
882
883   bool match_pairs = false;
884
885   // This is the address of std_heap->heapinfo in the application process:
886   void* heapinfo_address = &((xbt_mheap_t) process->heap_address)->heapinfo;
887
888   const malloc_info* heapinfos1 = snapshot1->read(remote((const malloc_info**)heapinfo_address));
889   const malloc_info* heapinfos2 = snapshot2->read(remote((const malloc_info**)heapinfo_address));
890
891   malloc_info heapinfo_temp1;
892   malloc_info heapinfo_temp2;
893
894   simgrid::mc::HeapLocationPairs current;
895   if (previous == nullptr) {
896     previous = &current;
897     match_pairs = true;
898   }
899
900   // Get block number:
901   block1 = ((char*)area1 - (char*)state.std_heap_copy.heapbase) / BLOCKSIZE + 1;
902   block2 = ((char*)area2 - (char*)state.std_heap_copy.heapbase) / BLOCKSIZE + 1;
903
904   // If either block is a stack block:
905   if (is_block_stack((int) block1) && is_block_stack((int) block2)) {
906     previous->insert(simgrid::mc::makeHeapLocationPair(block1, -1, block2, -1));
907     if (match_pairs)
908       state.match_equals(previous);
909     return 0;
910   }
911
912   // If either block is not in the expected area of memory:
913   if (((char*)area1 < (char*)state.std_heap_copy.heapbase) || (block1 > (ssize_t)state.processStates[0].heapsize) ||
914       (block1 < 1) || ((char*)area2 < (char*)state.std_heap_copy.heapbase) ||
915       (block2 > (ssize_t)state.processStates[1].heapsize) || (block2 < 1)) {
916     return 1;
917   }
918
919   // Process address of the block:
920   void* real_addr_block1 = (ADDR2UINT(block1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
921   void* real_addr_block2 = (ADDR2UINT(block2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
922
923   if (type) {
924     if (type->full_type)
925       type = type->full_type;
926
927     // This assume that for "boring" types (volatile ...) byte_size is absent:
928     while (type->byte_size == 0 && type->subtype != nullptr)
929       type = type->subtype;
930
931     // Find type_size:
932     if (type->type == DW_TAG_pointer_type ||
933         (type->type == DW_TAG_base_type && not type->name.empty() && type->name == "char"))
934       type_size = -1;
935     else
936       type_size = type->byte_size;
937
938   }
939
940   simgrid::mc::Region* heap_region1 = MC_get_heap_region(snapshot1);
941   simgrid::mc::Region* heap_region2 = MC_get_heap_region(snapshot2);
942
943   const malloc_info* heapinfo1 =
944       (const malloc_info*)heap_region1->read(&heapinfo_temp1, &heapinfos1[block1], sizeof(malloc_info));
945   const malloc_info* heapinfo2 =
946       (const malloc_info*)heap_region2->read(&heapinfo_temp2, &heapinfos2[block2], sizeof(malloc_info));
947
948   if ((heapinfo1->type == MMALLOC_TYPE_FREE || heapinfo1->type==MMALLOC_TYPE_HEAPINFO)
949     && (heapinfo2->type == MMALLOC_TYPE_FREE || heapinfo2->type ==MMALLOC_TYPE_HEAPINFO)) {
950     /* Free block */
951     if (match_pairs)
952       state.match_equals(previous);
953     return 0;
954   }
955
956   if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED && heapinfo2->type == MMALLOC_TYPE_UNFRAGMENTED) {
957     /* Complete block */
958
959     // TODO, lookup variable type from block type as done for fragmented blocks
960
961     if (state.equals_to1_(block1, 0).valid_ && state.equals_to2_(block2, 0).valid_ &&
962         state.blocksEqual(block1, block2)) {
963       if (match_pairs)
964         state.match_equals(previous);
965       return 0;
966     }
967
968     if (type_size != -1 && type_size != (ssize_t)heapinfo1->busy_block.busy_size &&
969         type_size != (ssize_t)heapinfo2->busy_block.busy_size &&
970         (type->name.empty() || type->name == "struct s_smx_context")) {
971       if (match_pairs)
972         state.match_equals(previous);
973       return -1;
974     }
975
976     if (heapinfo1->busy_block.size != heapinfo2->busy_block.size)
977       return 1;
978     if (heapinfo1->busy_block.busy_size != heapinfo2->busy_block.busy_size)
979       return 1;
980
981     if (not previous->insert(simgrid::mc::makeHeapLocationPair(block1, -1, block2, -1)).second) {
982       if (match_pairs)
983         state.match_equals(previous);
984       return 0;
985     }
986
987     size = heapinfo1->busy_block.busy_size;
988
989     // Remember (basic) type inference.
990     // The current data structure only allows us to do this for the whole block.
991     if (type != nullptr && area1 == real_addr_block1)
992       state.types1_(block1, 0) = type;
993     if (type != nullptr && area2 == real_addr_block2)
994       state.types2_(block2, 0) = type;
995
996     if (size <= 0) {
997       if (match_pairs)
998         state.match_equals(previous);
999       return 0;
1000     }
1001
1002     if (heapinfo1->busy_block.ignore > 0
1003         && heapinfo2->busy_block.ignore == heapinfo1->busy_block.ignore)
1004       check_ignore = heapinfo1->busy_block.ignore;
1005
1006   } else if ((heapinfo1->type > 0) && (heapinfo2->type > 0)) {      /* Fragmented block */
1007
1008     // Fragment number:
1009     ssize_t frag1 = ((uintptr_t)(ADDR2UINT(area1) % (BLOCKSIZE))) >> heapinfo1->type;
1010     ssize_t frag2 = ((uintptr_t)(ADDR2UINT(area2) % (BLOCKSIZE))) >> heapinfo2->type;
1011
1012     // Process address of the fragment_:
1013     void* real_addr_frag1 = (void*)((char*)real_addr_block1 + (frag1 << heapinfo1->type));
1014     void* real_addr_frag2 = (void*)((char*)real_addr_block2 + (frag2 << heapinfo2->type));
1015
1016     // Check the size of the fragments against the size of the type:
1017     if (type_size != -1) {
1018       if (heapinfo1->busy_frag.frag_size[frag1] == -1 || heapinfo2->busy_frag.frag_size[frag2] == -1) {
1019         if (match_pairs)
1020           state.match_equals(previous);
1021         return -1;
1022       }
1023       // ?
1024       if (type_size != heapinfo1->busy_frag.frag_size[frag1]
1025           || type_size != heapinfo2->busy_frag.frag_size[frag2]) {
1026         if (match_pairs)
1027           state.match_equals(previous);
1028         return -1;
1029       }
1030     }
1031
1032     // Check if the blocks are already matched together:
1033     if (state.equals_to1_(block1, frag1).valid_ && state.equals_to2_(block2, frag2).valid_ && offset1 == offset2 &&
1034         state.fragmentsEqual(block1, frag1, block2, frag2)) {
1035       if (match_pairs)
1036         state.match_equals(previous);
1037       return 0;
1038     }
1039     // Compare the size of both fragments:
1040     if (heapinfo1->busy_frag.frag_size[frag1] != heapinfo2->busy_frag.frag_size[frag2]) {
1041       if (type_size == -1) {
1042         if (match_pairs)
1043           state.match_equals(previous);
1044         return -1;
1045       } else
1046         return 1;
1047     }
1048
1049     // Size of the fragment_:
1050     size = heapinfo1->busy_frag.frag_size[frag1];
1051
1052     // Remember (basic) type inference.
1053     // The current data structure only allows us to do this for the whole fragment_.
1054     if (type != nullptr && area1 == real_addr_frag1)
1055       state.types1_(block1, frag1) = type;
1056     if (type != nullptr && area2 == real_addr_frag2)
1057       state.types2_(block2, frag2) = type;
1058
1059     // The type of the variable is already known:
1060     if (type) {
1061       new_type1 = new_type2 = type;
1062     }
1063     // Type inference from the block type.
1064     else if (state.types1_(block1, frag1) != nullptr || state.types2_(block2, frag2) != nullptr) {
1065
1066       offset1 = (char*)area1 - (char*)real_addr_frag1;
1067       offset2 = (char*)area2 - (char*)real_addr_frag2;
1068
1069       if (state.types1_(block1, frag1) != nullptr && state.types2_(block2, frag2) != nullptr) {
1070         new_type1 = get_offset_type(real_addr_frag1, state.types1_(block1, frag1), offset1, size, snapshot1);
1071         new_type2 = get_offset_type(real_addr_frag2, state.types2_(block2, frag2), offset1, size, snapshot2);
1072       } else if (state.types1_(block1, frag1) != nullptr) {
1073         new_type1 = get_offset_type(real_addr_frag1, state.types1_(block1, frag1), offset1, size, snapshot1);
1074         new_type2 = get_offset_type(real_addr_frag2, state.types1_(block1, frag1), offset2, size, snapshot2);
1075       } else if (state.types2_(block2, frag2) != nullptr) {
1076         new_type1 = get_offset_type(real_addr_frag1, state.types2_(block2, frag2), offset1, size, snapshot1);
1077         new_type2 = get_offset_type(real_addr_frag2, state.types2_(block2, frag2), offset2, size, snapshot2);
1078       } else {
1079         if (match_pairs)
1080           state.match_equals(previous);
1081         return -1;
1082       }
1083
1084       if (new_type1 != nullptr && new_type2 != nullptr && new_type1 != new_type2) {
1085
1086         type = new_type1;
1087         while (type->byte_size == 0 && type->subtype != nullptr)
1088           type = type->subtype;
1089         new_size1 = type->byte_size;
1090
1091         type = new_type2;
1092         while (type->byte_size == 0 && type->subtype != nullptr)
1093           type = type->subtype;
1094         new_size2 = type->byte_size;
1095
1096       } else {
1097         if (match_pairs)
1098           state.match_equals(previous);
1099         return -1;
1100       }
1101     }
1102
1103     if (new_size1 > 0 && new_size1 == new_size2) {
1104       type = new_type1;
1105       size = new_size1;
1106     }
1107
1108     if (offset1 == 0 && offset2 == 0 &&
1109         not previous->insert(simgrid::mc::makeHeapLocationPair(block1, frag1, block2, frag2)).second) {
1110       if (match_pairs)
1111         state.match_equals(previous);
1112       return 0;
1113     }
1114
1115     if (size <= 0) {
1116       if (match_pairs)
1117         state.match_equals(previous);
1118       return 0;
1119     }
1120
1121     if ((heapinfo1->busy_frag.ignore[frag1] > 0) &&
1122         (heapinfo2->busy_frag.ignore[frag2] == heapinfo1->busy_frag.ignore[frag1]))
1123       check_ignore = heapinfo1->busy_frag.ignore[frag1];
1124
1125   } else
1126     return 1;
1127
1128
1129   /* Start comparison */
1130   int res_compare;
1131   if (type)
1132     res_compare = compare_heap_area_with_type(state, area1, area2, snapshot1, snapshot2, previous, type, size,
1133                                               check_ignore, pointer_level);
1134   else
1135     res_compare =
1136         compare_heap_area_without_type(state, area1, area2, snapshot1, snapshot2, previous, size, check_ignore);
1137
1138   if (res_compare == 1)
1139     return res_compare;
1140
1141   if (match_pairs)
1142     state.match_equals(previous);
1143   return 0;
1144 }
1145
1146 }
1147 }
1148
1149 /************************** Snapshot comparison *******************************/
1150 /******************************************************************************/
1151
1152 static int compare_areas_with_type(simgrid::mc::StateComparator& state, void* real_area1,
1153                                    simgrid::mc::Snapshot* snapshot1, simgrid::mc::Region* region1, void* real_area2,
1154                                    simgrid::mc::Snapshot* snapshot2, simgrid::mc::Region* region2,
1155                                    simgrid::mc::Type* type, int pointer_level)
1156 {
1157   simgrid::mc::RemoteClient* process = &mc_model_checker->process();
1158
1159   simgrid::mc::Type* subtype;
1160   simgrid::mc::Type* subsubtype;
1161   int elm_size;
1162   int i;
1163   int res;
1164
1165   do {
1166     xbt_assert(type != nullptr);
1167     switch (type->type) {
1168       case DW_TAG_unspecified_type:
1169         return 1;
1170
1171       case DW_TAG_base_type:
1172       case DW_TAG_enumeration_type:
1173       case DW_TAG_union_type:
1174         return MC_snapshot_region_memcmp(real_area1, region1, real_area2, region2, type->byte_size) != 0;
1175       case DW_TAG_typedef:
1176       case DW_TAG_volatile_type:
1177       case DW_TAG_const_type:
1178         // Poor man's TCO:
1179         type = type->subtype;
1180         continue; // restart
1181       case DW_TAG_array_type:
1182         subtype = type->subtype;
1183         switch (subtype->type) {
1184           case DW_TAG_unspecified_type:
1185             return 1;
1186
1187           case DW_TAG_base_type:
1188           case DW_TAG_enumeration_type:
1189           case DW_TAG_pointer_type:
1190           case DW_TAG_reference_type:
1191           case DW_TAG_rvalue_reference_type:
1192           case DW_TAG_structure_type:
1193           case DW_TAG_class_type:
1194           case DW_TAG_union_type:
1195             if (subtype->full_type)
1196               subtype = subtype->full_type;
1197             elm_size  = subtype->byte_size;
1198             break;
1199           case DW_TAG_const_type:
1200           case DW_TAG_typedef:
1201           case DW_TAG_volatile_type:
1202             subsubtype = subtype->subtype;
1203             if (subsubtype->full_type)
1204               subsubtype = subsubtype->full_type;
1205             elm_size     = subsubtype->byte_size;
1206             break;
1207           default:
1208             return 0;
1209         }
1210         for (i = 0; i < type->element_count; i++) {
1211           size_t off = i * elm_size;
1212           res = compare_areas_with_type(state, (char*)real_area1 + off, snapshot1, region1, (char*)real_area2 + off,
1213                                         snapshot2, region2, type->subtype, pointer_level);
1214           if (res == 1)
1215             return res;
1216         }
1217         break;
1218       case DW_TAG_pointer_type:
1219       case DW_TAG_reference_type:
1220       case DW_TAG_rvalue_reference_type: {
1221         void* addr_pointed1 = MC_region_read_pointer(region1, real_area1);
1222         void* addr_pointed2 = MC_region_read_pointer(region2, real_area2);
1223
1224         if (type->subtype && type->subtype->type == DW_TAG_subroutine_type)
1225           return (addr_pointed1 != addr_pointed2);
1226         if (addr_pointed1 == nullptr && addr_pointed2 == nullptr)
1227           return 0;
1228         if (addr_pointed1 == nullptr || addr_pointed2 == nullptr)
1229           return 1;
1230         if (not state.compared_pointers.insert(std::make_pair(addr_pointed1, addr_pointed2)).second)
1231           return 0;
1232
1233         pointer_level++;
1234
1235         // Some cases are not handled here:
1236         // * the pointers lead to different areas (one to the heap, the other to the RW segment ...)
1237         // * a pointer leads to the read-only segment of the current object
1238         // * a pointer lead to a different ELF object
1239
1240         if (addr_pointed1 > process->heap_address && addr_pointed1 < mc_snapshot_get_heap_end(snapshot1)) {
1241           if (not(addr_pointed2 > process->heap_address && addr_pointed2 < mc_snapshot_get_heap_end(snapshot2)))
1242             return 1;
1243           // The pointers are both in the heap:
1244           return simgrid::mc::compare_heap_area(state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, nullptr,
1245                                                 type->subtype, pointer_level);
1246
1247         } else if (region1->contain(simgrid::mc::remote(addr_pointed1))) {
1248           // The pointers are both in the current object R/W segment:
1249           if (not region2->contain(simgrid::mc::remote(addr_pointed2)))
1250             return 1;
1251           if (not type->type_id)
1252             return (addr_pointed1 != addr_pointed2);
1253           else
1254             return compare_areas_with_type(state, addr_pointed1, snapshot1, region1, addr_pointed2, snapshot2, region2,
1255                                            type->subtype, pointer_level);
1256         } else {
1257
1258           // TODO, We do not handle very well the case where
1259           // it belongs to a different (non-heap) region from the current one.
1260
1261           return (addr_pointed1 != addr_pointed2);
1262         }
1263       }
1264       case DW_TAG_structure_type:
1265       case DW_TAG_class_type:
1266         for (simgrid::mc::Member& member : type->members) {
1267           void* member1 = simgrid::dwarf::resolve_member(real_area1, type, &member, snapshot1);
1268           void* member2 = simgrid::dwarf::resolve_member(real_area2, type, &member, snapshot2);
1269           simgrid::mc::Region* subregion1 = snapshot1->get_region(member1, region1); // region1 is hinted
1270           simgrid::mc::Region* subregion2 = snapshot2->get_region(member2, region2); // region2 is hinted
1271           res = compare_areas_with_type(state, member1, snapshot1, subregion1, member2, snapshot2, subregion2,
1272                                         member.type, pointer_level);
1273           if (res == 1)
1274             return res;
1275         }
1276         break;
1277       case DW_TAG_subroutine_type:
1278         return -1;
1279       default:
1280         XBT_VERB("Unknown case: %d", type->type);
1281         break;
1282     }
1283
1284     return 0;
1285   } while (true);
1286 }
1287
1288 static int compare_global_variables(simgrid::mc::StateComparator& state, simgrid::mc::ObjectInformation* object_info,
1289                                     simgrid::mc::Region* r1, simgrid::mc::Region* r2, simgrid::mc::Snapshot* snapshot1,
1290                                     simgrid::mc::Snapshot* snapshot2)
1291 {
1292   xbt_assert(r1 && r2, "Missing region.");
1293
1294   std::vector<simgrid::mc::Variable>& variables = object_info->global_variables;
1295
1296   for (simgrid::mc::Variable const& current_var : variables) {
1297
1298     // If the variable is not in this object, skip it:
1299     // We do not expect to find a pointer to something which is not reachable
1300     // by the global variables.
1301     if ((char *) current_var.address < (char *) object_info->start_rw
1302         || (char *) current_var.address > (char *) object_info->end_rw)
1303       continue;
1304
1305     simgrid::mc::Type* bvariable_type = current_var.type;
1306     int res = compare_areas_with_type(state, (char*)current_var.address, snapshot1, r1, (char*)current_var.address,
1307                                       snapshot2, r2, bvariable_type, 0);
1308     if (res == 1) {
1309       XBT_VERB("Global variable %s (%p) is different between snapshots",
1310                current_var.name.c_str(),
1311                (char *) current_var.address);
1312       return 1;
1313     }
1314   }
1315
1316   return 0;
1317 }
1318
1319 static int compare_local_variables(simgrid::mc::StateComparator& state,
1320                                    simgrid::mc::Snapshot* snapshot1,
1321                                    simgrid::mc::Snapshot* snapshot2,
1322                                    mc_snapshot_stack_t stack1,
1323                                    mc_snapshot_stack_t stack2)
1324 {
1325   if (stack1->local_variables.size() != stack2->local_variables.size()) {
1326     XBT_VERB("Different number of local variables");
1327     return 1;
1328   }
1329
1330     unsigned int cursor = 0;
1331     local_variable_t current_var1;
1332     local_variable_t current_var2;
1333     while (cursor < stack1->local_variables.size()) {
1334       current_var1 = &stack1->local_variables[cursor];
1335       current_var2 = &stack1->local_variables[cursor];
1336       if (current_var1->name != current_var2->name
1337           || current_var1->subprogram != current_var2->subprogram
1338           || current_var1->ip != current_var2->ip) {
1339         // TODO, fix current_varX->subprogram->name to include name if DW_TAG_inlined_subprogram
1340         XBT_VERB
1341             ("Different name of variable (%s - %s) "
1342              "or frame (%s - %s) or ip (%lu - %lu)",
1343              current_var1->name.c_str(),
1344              current_var2->name.c_str(),
1345              current_var1->subprogram->name.c_str(),
1346              current_var2->subprogram->name.c_str(),
1347              current_var1->ip, current_var2->ip);
1348         return 1;
1349       }
1350       // TODO, fix current_varX->subprogram->name to include name if DW_TAG_inlined_subprogram
1351
1352         simgrid::mc::Type* subtype = current_var1->type;
1353         int res                    = compare_areas_with_type(state, current_var1->address, snapshot1,
1354                                           snapshot1->get_region(current_var1->address), current_var2->address,
1355                                           snapshot2, snapshot2->get_region(current_var2->address), subtype, 0);
1356
1357         if (res == 1) {
1358           // TODO, fix current_varX->subprogram->name to include name if DW_TAG_inlined_subprogram
1359           XBT_VERB("Local variable %s (%p - %p) in frame %s "
1360                    "is different between snapshots",
1361                    current_var1->name.c_str(), current_var1->address, current_var2->address,
1362                    current_var1->subprogram->name.c_str());
1363           return res;
1364       }
1365       cursor++;
1366     }
1367     return 0;
1368 }
1369
1370 namespace simgrid {
1371 namespace mc {
1372
1373 static std::unique_ptr<simgrid::mc::StateComparator> state_comparator;
1374
1375 int snapshot_compare(Snapshot* s1, Snapshot* s2)
1376 {
1377   // TODO, make this a field of ModelChecker or something similar
1378   if (state_comparator == nullptr)
1379     state_comparator.reset(new StateComparator());
1380   else
1381     state_comparator->clear();
1382
1383   RemoteClient* process = &mc_model_checker->process();
1384
1385   if (_sg_mc_hash) {
1386     if (s1->hash_ != s2->hash_) {
1387       XBT_VERB("(%d - %d) Different hash: 0x%" PRIx64 "--0x%" PRIx64, s1->num_state_, s2->num_state_, s1->hash_,
1388                s2->hash_);
1389       return 1;
1390     } else
1391       XBT_VERB("(%d - %d) Same hash: 0x%" PRIx64, s1->num_state_, s2->num_state_, s1->hash_);
1392   }
1393
1394   /* Compare enabled processes */
1395   if (s1->enabled_processes_ != s2->enabled_processes_) {
1396     XBT_VERB("(%d - %d) Different amount of enabled processes", s1->num_state_, s2->num_state_);
1397     return 1;
1398   }
1399
1400   /* Compare size of stacks */
1401   int is_diff = 0;
1402   for (unsigned long i = 0; i < s1->stacks_.size(); i++) {
1403     size_t size_used1 = s1->stack_sizes_[i];
1404     size_t size_used2 = s2->stack_sizes_[i];
1405     if (size_used1 != size_used2) {
1406       XBT_VERB("(%d - %d) Different size used in stacks: %zu - %zu", s1->num_state_, s2->num_state_, size_used1,
1407                size_used2);
1408       return 1;
1409     }
1410   }
1411   if (is_diff) // do not proceed if there is any stacks that don't match
1412     return 1;
1413
1414   /* Init heap information used in heap comparison algorithm */
1415   xbt_mheap_t heap1 = (xbt_mheap_t)s1->read_bytes(alloca(sizeof(struct mdesc)), sizeof(struct mdesc),
1416                                                   remote(process->heap_address), simgrid::mc::ReadOptions::lazy());
1417   xbt_mheap_t heap2 = (xbt_mheap_t)s2->read_bytes(alloca(sizeof(struct mdesc)), sizeof(struct mdesc),
1418                                                   remote(process->heap_address), simgrid::mc::ReadOptions::lazy());
1419   int res_init = state_comparator->initHeapInformation(heap1, heap2, &s1->to_ignore_, &s2->to_ignore_);
1420
1421   if (res_init == -1) {
1422     XBT_VERB("(%d - %d) Different heap information", s1->num_state_, s2->num_state_);
1423     return 1;
1424   }
1425
1426   /* Stacks comparison */
1427   // int diff_local = 0;
1428   for (unsigned int cursor = 0; cursor < s1->stacks_.size(); cursor++) {
1429     mc_snapshot_stack_t stack1 = &s1->stacks_[cursor];
1430     mc_snapshot_stack_t stack2 = &s2->stacks_[cursor];
1431
1432     if (compare_local_variables(*state_comparator, s1, s2, stack1, stack2) > 0) {
1433       XBT_VERB("(%d - %d) Different local variables between stacks %u", s1->num_state_, s2->num_state_, cursor + 1);
1434       return 1;
1435     }
1436   }
1437
1438   size_t regions_count = s1->snapshot_regions_.size();
1439   // TODO, raise a difference instead?
1440   xbt_assert(regions_count == s2->snapshot_regions_.size());
1441
1442   for (size_t k = 0; k != regions_count; ++k) {
1443     Region* region1 = s1->snapshot_regions_[k].get();
1444     Region* region2 = s2->snapshot_regions_[k].get();
1445
1446     // Preconditions:
1447     if (region1->region_type() != RegionType::Data)
1448       continue;
1449
1450     xbt_assert(region1->region_type() == region2->region_type());
1451     xbt_assert(region1->object_info() == region2->object_info());
1452     xbt_assert(region1->object_info());
1453
1454     /* Compare global variables */
1455     if (compare_global_variables(*state_comparator, region1->object_info(), region1, region2, s1, s2)) {
1456       std::string const& name = region1->object_info()->file_name;
1457       XBT_VERB("(%d - %d) Different global variables in %s", s1->num_state_, s2->num_state_, name.c_str());
1458       return 1;
1459     }
1460   }
1461
1462   /* Compare heap */
1463   if (mmalloc_compare_heap(*state_comparator, s1, s2) > 0) {
1464     XBT_VERB("(%d - %d) Different heap (mmalloc_compare)", s1->num_state_, s2->num_state_);
1465     return 1;
1466   }
1467
1468     XBT_VERB("(%d - %d) No difference found", s1->num_state_, s2->num_state_);
1469
1470     return 0;
1471 }
1472
1473 }
1474 }