Logo AND Algorithmique Numérique Distribuée

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