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