File: chromium_visualizers.py

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (245 lines) | stat: -rw-r--r-- 7,717 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
    Provides visualizers for common types for debugging in LLDB.

    To make these available, add the following to your ~/.lldbinit or your
    .vscode/launch.json, or run after launching lldb:

    command script import {Path to SRC Root}/tools/lldb/chromium_visualizers.py
"""

import traceback

import lldb


def lazy_unsigned_global(name, child=None):
  sb_value = None
  uint_value = 0

  def getter(fromValue):
    nonlocal sb_value
    nonlocal uint_value
    if sb_value is None:
      sb_value = fromValue.GetTarget().FindFirstGlobalVariable(name)
      if child:
        sb_value = sb_value.GetChildMemberWithName(child)
      uint_value = sb_value.GetValueAsUnsigned()
    return uint_value

  return getter


kPointerCompressionShift_getter = lazy_unsigned_global(
    'cppgc::internal::api_constants::kPointerCompressionShift')
CageBaseGlobal_getter = lazy_unsigned_global(
    'cppgc::internal::CageBaseGlobal::g_base_', 'base')

PLACEHOLDER_VALUE = None


class SingleChildProvider:
  """A base class for providers that create one child."""

  def __init__(self, valobj):
    self.child = None
    self.valobj = valobj

  def update(self):
    # By default, reevaluate
    self.child = None

  def ensure_populated(self):
    if self.child is None:
      try:
        self.populate()
      except:
        print(traceback.format_exc())
    if self.child is None or not self.child.IsValid():
      global PLACEHOLDER_VALUE
      if PLACEHOLDER_VALUE is None:
        PLACEHOLDER_VALUE = self.valobj.CreateValueFromExpression(
            '<failed to load child>', 'nullptr')
      self.child = PLACEHOLDER_VALUE

  def populate(self):
    raise NotImplementedError()

  def num_children(self):
    self.ensure_populated()
    return 1

  def has_children(self):
    return True

  def get_child_index(self, name):
    self.ensure_populated()
    return 0

  def get_child_at_index(self, index):
    self.ensure_populated()
    return self.child


class VectorProvider(SingleChildProvider):
  """Provides children for a vector."""

  def __init__(self, valobj, internal_dict):
    super().__init__(valobj)

  def populate(self):
    lldbtype = self.valobj.GetType()
    addr = self.valobj.GetValueAsAddress() if lldbtype.IsPointerType(
    ) else self.valobj.GetLoadAddress()
    data_pointer = self.valobj.GetChildMemberWithName('buffer_')
    size = self.valobj.GetChildMemberWithName('size_').GetValueAsUnsigned()
    self.child = data_pointer.Cast(
        data_pointer.GetType().GetPointeeType().GetArrayType(
            size).GetPointerType())


class SmartPointerProvider(SingleChildProvider):
  """
    A base class for providers that create one child generated by calling a
    method on the object.
  """

  def __init__(self,
               raw_name,
               valobj,
               internal_dict,
               child_name='$$dereference$$'):
    super().__init__(valobj)
    self.raw_name = raw_name
    self.child_name = child_name

  def update(self):
    global PLACEHOLDER_VALUE
    if self.child is PLACEHOLDER_VALUE:
      self.child = None

  def populate(self):
    self.child = self.valobj.GetChildMemberWithName(self.raw_name).Cast(
        self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType())


class ScopedRefProvider(SmartPointerProvider):

  def __init__(self, valobj, internal_dict):
    super().__init__('ptr_', valobj, internal_dict)


class MemberProvider(SingleChildProvider):

  def __init__(self, valobj, internal_dict):
    super().__init__(valobj)
    self.last_child = None
    self.last_addr = None
    self.raw_storage = None
    self.pointer_type = None
    self.compressed = None

  def update(self):
    global PLACEHOLDER_VALUE
    if self.compressed is not None or self.child is PLACEHOLDER_VALUE:
      self.child = None

  def populate(self):
    if self.raw_storage is None:
      self.raw_storage = self.valobj.GetChildMemberWithName('raw_')
      self.pointer_type = self.raw_storage.GetType().GetCanonicalType().GetName(
      )
    pointee_type = self.valobj.GetType().GetTemplateArgumentType(0)
    if self.pointer_type == 'cppgc::internal::RawPointer':
      data_pointer = self.raw_storage.GetChildMemberWithName('ptr_').Cast(
          pointee_type.GetPointerType())
    elif self.pointer_type == 'cppgc::internal::CompressedPointer':
      # Need to reproduce the behavior of CompressedPointer::Decompress()
      # because it is optimized away.
      if self.compressed is None:
        self.compressed = self.raw_storage.GetChildMemberWithName('value_')
      compressed = self.compressed.GetValueAsUnsigned()
      if self.last_child and self.last_addr == compressed:
        # Last child is still valid
        self.child = self.last_child
        return
      pointer_shift = kPointerCompressionShift_getter(self.raw_storage)
      cage_base = CageBaseGlobal_getter(self.raw_storage)
      sign_bit = 0x80000000
      decompressed = ((
          (compressed ^ sign_bit) - sign_bit) << pointer_shift) & cage_base
      data_pointer = self.valobj.CreateValueFromAddress('$$dereference$$',
                                                        decompressed,
                                                        pointee_type)
      self.last_addr = compressed
    else:
      # This seems to happen when the pointer is null. Ignore.
      return
    self.child = data_pointer
    self.last_child = self.child


class MethodProvider(SingleChildProvider):
  """
    A base class for providers that create one child generated by calling a
    method on the object.
  """

  def __init__(self,
               method,
               valobj,
               internal_dict,
               child_name='$$dereference$$'):
    super().__init__(valobj)
    self.method = method
    self.child_name = child_name
    self.last_child = None
    self.last_addr = None

  def populate(self):
    lldbtype = self.valobj.GetType()
    addr = self.valobj.GetValueAsAddress() if lldbtype.IsPointerType(
    ) else self.valobj.GetLoadAddress()
    if self.last_child and self.last_addr == addr:
      # Last child is still valid
      self.child = self.last_child
      return
    self.child = self.valobj.CreateValueFromExpression(
        self.child_name,
        f'(({lldbtype.GetCanonicalType().GetName()} *){addr})->{self.method}')
    self.last_child = self.child
    self.last_addr = addr


class WTFStringProvider(MethodProvider):

  def __init__(self, valobj, internal_dict):
    super().__init__('Utf8(WTF::Utf8ConversionMode::kLenient)', valobj,
                     internal_dict, 'utf8_')


def __lldb_init_module(debugger, unused_dict):
  debugger.HandleCommand(
      'type synthetic add -l chromium_visualizers.ScopedRefProvider -x "^scoped_refptr<.*>$"'
  )
  debugger.HandleCommand(
      'type synthetic add -p -r -l chromium_visualizers.MemberProvider -x "^cppgc::internal::BasicMember<.*>$"'
  )
  debugger.HandleCommand(
      'type synthetic add -l chromium_visualizers.WTFStringProvider -x "^WTF::String$"'
  )
  debugger.HandleCommand(
      'type synthetic add -l chromium_visualizers.VectorProvider -x "^WTF::Vector<.*>$"'
  )
  debugger.HandleCommand(
      'type summary add --summary-string "${svar.utf8_}" WTF::String')
  debugger.HandleCommand(
      'type summary add --summary-string "${var.string_}" WTF::AtomicString')
  debugger.HandleCommand(
      'type summary add --summary-string "size = ${var.size_}" -x "^WTF::Vector<.*>$"'
  )
  debugger.HandleCommand(
      'type summary add --summary-string "${var.raw_}" -x "^cppgc::internal::BasicMember<.*>$"'
  )