1# Copyright (c) 2020 Google LLC. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# The following Skia types can be natively visualized in LLDB:
6# - SkAutoTArray, SkAutoSTArray
7# - SkString
8# - SkTArray, SkSTArray
9# - sk_sp
10#
11# To enable LLDB debugging support, run the following command at the (lldb) prompt:
12#
13#      command script import (your-skia-local-path)/platform_tools/debugging/lldb/skia.py
14#
15# This can be automatically enabled at the start of every debugging session by creating a
16# ~/.lldbinit file which contains this command.
17
18import lldb
19
20def SkString_SummaryProvider(valobj, dict):
21    fRec = valobj.GetChildMemberWithName('fRec')
22    # The fPtr inside fRec is automatically consumed by sk_sp_SynthProvider.
23    fLength = fRec.GetChildMemberWithName('fLength')
24    if fLength.GetValueAsUnsigned(0) <= 0:
25        return '""'
26    fBeginningOfData = fRec.GetChildMemberWithName('fBeginningOfData')
27
28    # Fetch string contents into an SBData.
29    string = fBeginningOfData.AddressOf().GetPointeeData(0, fLength.GetValueAsUnsigned(0))
30    # Zero terminate the SBData. (This actually adds four zero bytes, but that's harmless.)
31    string.Append(lldb.SBData.CreateDataFromInt(0))
32    # Convert our SBData into a string.
33    error = lldb.SBError()
34    string = string.GetString(error, 0)
35    if error.Fail():
36        return '<error: ' + error.GetCString() + '>'
37    else:
38        return '"' + string + '"'
39
40
41class SkTArray_SynthProvider:
42
43    def __init__(self, valobj, dict):
44        self.valobj = valobj
45
46    def num_children(self):
47        try:
48            count = self.fCount.GetValueAsSigned(0)
49            count = max(count, 0)
50            count = min(count, 10000)
51            return count
52        except:
53            return 0
54
55    def get_child_index(self, name):
56        try:
57            return int(name.lstrip('[').rstrip(']'))
58        except:
59            return -1
60
61    def get_child_at_index(self, index):
62        if index < 0:
63            return None
64        if index >= self.num_children():
65            return None
66
67        try:
68            offset = index * self.dataSize
69            return self.fItemArray.CreateChildAtOffset('[' + str(index) + ']',
70                                                       offset, self.dataType)
71        except:
72            return None
73
74    def update(self):
75        try:
76            self.fItemArray = self.valobj.GetChildMemberWithName('fItemArray')
77            self.fCount = self.valobj.GetChildMemberWithName('fCount')
78            self.dataType = self.fItemArray.GetType().GetPointeeType()
79            self.dataSize = self.dataType.GetByteSize()
80        except:
81            pass
82
83    def has_children(self):
84        return True
85
86
87class SkAutoTArray_SynthProvider:
88
89    def __init__(self, valobj, dict):
90        self.valobj = valobj
91
92    def num_children(self):
93        try:
94            count = self.fCount.GetValueAsSigned(0)
95            count = max(count, 0)
96            count = min(count, 10000)
97            return count
98        except:
99            return 0
100
101    def get_child_index(self, name):
102        try:
103            return int(name.lstrip('[').rstrip(']'))
104        except:
105            return -1
106
107    def get_child_at_index(self, index):
108        if index < 0:
109            return None
110        if index >= self.num_children():
111            return None
112
113        try:
114            offset = index * self.dataSize
115            return self.fValue.CreateChildAtOffset('[' + str(index) + ']',
116                                                   offset, self.dataType)
117        except:
118            return None
119
120    def update(self):
121        try:
122            self.fCount = self.valobj.GetChildMemberWithName('fCount')
123            fArray = self.valobj.GetChildMemberWithName('fArray')
124            # These lookups rely on implementation details of unique_ptr and __compressed_pair.
125            ptr = fArray.GetChildMemberWithName('__ptr_')
126            self.fValue = ptr.GetChildMemberWithName('__value_')
127            self.dataType = self.fValue.GetType().GetPointeeType()
128            self.dataSize = self.dataType.GetByteSize()
129        except:
130            pass
131
132    def has_children(self):
133        return True
134
135
136class SkSpan_SynthProvider:
137
138    def __init__(self, valobj, dict):
139        self.valobj = valobj
140
141    def num_children(self):
142        try:
143            count = self.fSize.GetValueAsSigned(0)
144            count = max(count, 0)
145            count = min(count, 10000)
146            return count
147        except:
148            return 0
149
150    def get_child_index(self, name):
151        try:
152            return int(name.lstrip('[').rstrip(']'))
153        except:
154            return -1
155
156    def get_child_at_index(self, index):
157        if index < 0:
158            return None
159        if index >= self.num_children():
160            return None
161
162        try:
163            offset = index * self.dataSize
164            return self.fPtr.CreateChildAtOffset('[' + str(index) + ']',
165                                                 offset, self.dataType)
166        except:
167            return None
168
169    def update(self):
170        try:
171            self.fPtr = self.valobj.GetChildMemberWithName('fPtr')
172            self.fSize = self.valobj.GetChildMemberWithName('fSize')
173            self.dataType = self.fPtr.GetType().GetPointeeType()
174            self.dataSize = self.dataType.GetByteSize()
175        except:
176            pass
177
178    def has_children(self):
179        return True
180
181
182
183class sk_sp_SynthProvider:
184
185    def __init__(self, valobj, dict):
186        self.valobj = valobj
187
188    def num_children(self):
189        return self.fPtr.GetNumChildren()
190
191    def get_child_at_index(self, index):
192        try:
193            return self.fPtr.GetChildAtIndex(index)
194        except:
195            return None
196
197    def get_child_index(self, name):
198        return self.fPtr.GetIndexOfChildWithName(name)
199
200    def update(self):
201        try:
202            self.fPtr = self.valobj.GetChildMemberWithName('fPtr')
203        except:
204            pass
205
206
207def __lldb_init_module(debugger, dict):
208    debugger.HandleCommand(
209        'type summary add -F skia.SkString_SummaryProvider "SkString" -w skia')
210    debugger.HandleCommand(
211        'type synthetic add -l skia.sk_sp_SynthProvider -x "^sk_sp<.+>$" -w skia')
212    debugger.HandleCommand(
213        'type summary add --summary-string "fPtr = ${var.fPtr}" -x "^sk_sp<.+>$" -w skia')
214    debugger.HandleCommand(
215        'type synthetic add -l skia.SkTArray_SynthProvider -x "^SkS?TArray<.+>$" -w skia')
216    debugger.HandleCommand(
217        'type synthetic add -l skia.SkSpan_SynthProvider -x "^SkSpan<.+>$" -w skia')
218    debugger.HandleCommand(
219        'type summary add --summary-string "size=${svar%#}" -e -x "^SkS?TArray<.+>$" -w skia')
220    debugger.HandleCommand(
221        'type synthetic add -l skia.SkAutoTArray_SynthProvider -x "^SkAutoS?TArray<.+>$" -w skia')
222    debugger.HandleCommand(
223        'type summary add --summary-string "size=${svar%#}" -e -x "^SkAutoS?TArray<.+>$" -w skia')
224    debugger.HandleCommand("type category enable skia")
225