Logo AND Algorithmique Numérique Distribuée

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