Logo AND Algorithmique Numérique Distribuée

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