1import lldb
2
3_map_capping_size = 255
4
5
6class libcxx_hash_table_SynthProvider:
7
8    def __init__(self, valobj, dict):
9        self.valobj = valobj
10        self.num_elements = None
11        self.next_element = None
12        self.bucket_count = None
13
14    def update(self):
15        logger = lldb.formatters.Logger.Logger()
16        self.num_elements = None
17        self.next_element = None
18        self.bucket_count = None
19        try:
20            # unordered_map is made up of a hash_map, which has 4 pieces in it:
21            #   bucket list :
22            #      array of buckets
23            #   p1 (pair):
24            #      first - pointer to first loaded element
25            #   p2 (pair):
26            #      first - number of elements
27            #      second - hash function
28            #   p3 (pair):
29            #      first - max_load_factor
30            #      second - equality operator function
31            #
32            # For display, we actually don't need to go inside the buckets, since 'p1' has a way to iterate over all
33            # the elements directly.
34            #
35            # We will calculate other values about the map because they will be useful for the summary.
36            #
37            table = self.valobj.GetChildMemberWithName('__table_')
38
39            bl_ptr = table.GetChildMemberWithName(
40                '__bucket_list_').GetChildMemberWithName('__ptr_')
41            self.bucket_array_ptr = bl_ptr.GetChildMemberWithName(
42                '__first_').GetValueAsUnsigned(0)
43            self.bucket_count = bl_ptr.GetChildMemberWithName('__second_').GetChildMemberWithName(
44                '__data_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
45            logger >> "Bucket count = %r" % self.bucket_count
46
47            self.begin_ptr = table.GetChildMemberWithName('__p1_').GetChildMemberWithName(
48                '__first_').GetChildMemberWithName('__next_')
49
50            self.num_elements = table.GetChildMemberWithName(
51                '__p2_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
52            self.max_load_factor = table.GetChildMemberWithName(
53                '__p3_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
54            logger >> "Num elements = %r" % self.num_elements
55
56            # save the pointers as we get them
57            #   -- don't access this first element if num_element==0!
58            self.elements_cache = []
59            if self.num_elements:
60                self.next_element = self.begin_ptr
61            else:
62                self.next_element = None
63        except Exception as e:
64            logger >> "Caught exception: %r" % e
65            pass
66
67    def num_children(self):
68        global _map_capping_size
69        num_elements = self.num_elements
70        if num_elements is not None:
71            if num_elements > _map_capping_size:
72                num_elements = _map_capping_size
73        return num_elements
74
75    def has_children(self):
76        return True
77
78    def get_child_index(self, name):
79        logger = lldb.formatters.Logger.Logger()
80        try:
81            return int(name.lstrip('[').rstrip(']'))
82        except:
83            return -1
84
85    def get_child_at_index(self, index):
86        logger = lldb.formatters.Logger.Logger()
87        logger >> "Retrieving child " + str(index)
88        if index < 0:
89            return None
90        if index >= self.num_children():
91            return None
92
93        # extend
94        logger >> " : cache size starts with %d elements" % len(
95            self.elements_cache)
96        while index >= len(self.elements_cache):
97            # if we hit the end before we get the index, give up:
98            if not self.next_element:
99                logger >> " : hit end of list"
100                return None
101
102            node = self.next_element.Dereference()
103
104            value = node.GetChildMemberWithName('__value_')
105            hash_value = node.GetChildMemberWithName(
106                '__hash_').GetValueAsUnsigned()
107            self.elements_cache.append((value, hash_value))
108
109            self.next_element = node.GetChildMemberWithName('__next_')
110            if not self.next_element.GetValueAsUnsigned(0):
111                self.next_element = None
112
113        # hit the index! so we have the value
114        logger >> " : cache size ends with %d elements" % len(
115            self.elements_cache)
116        value, hash_value = self.elements_cache[index]
117        return self.valobj.CreateValueFromData(
118            '[%d] <hash %d>' %
119            (index, hash_value), value.GetData(), value.GetType())
120
121
122def __lldb_init_module(debugger, dict):
123    debugger.HandleCommand(
124        'type synthetic add -l unordered_multi.libcxx_hash_table_SynthProvider -x "^(std::__1::)unordered_(multi)?(map|set)<.+> >$" -w libcxx')
125