Logo AND Algorithmique Numérique Distribuée

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