1import argparse 2from typing import Iterator, Union, Optional 3 4import gdb 5 6from .value import Value 7 8gdb.execute("set pagination off") 9gdb.write("set pagination off\n") 10gdb.execute("set python print-stack full") 11gdb.write("set python print-stack full\n") 12 13g_lvgl_instance = None 14 15 16class LVList(Value): 17 """LVGL linked list iterator""" 18 19 def __init__(self, ll: Value, nodetype: Union[gdb.Type, str] = None): 20 if not ll: 21 raise ValueError("Invalid linked list") 22 super().__init__(ll) 23 24 self.nodetype = ( 25 gdb.lookup_type(nodetype).pointer() 26 if isinstance(nodetype, str) 27 else nodetype 28 ) 29 self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer() 30 self.current = self.head 31 self._next_offset = self.n_size + self.lv_ll_node_t.sizeof 32 self._prev_offset = self.n_size 33 34 def _next(self, node): 35 next_value = Value(int(node) + self._next_offset) 36 return next_value.cast(self.lv_ll_node_t, ptr=True).dereference() 37 38 def _prev(self, node): 39 prev_value = Value(int(node) + self._prev_offset) 40 return prev_value.cast(self.lv_ll_node_t, ptr=True).dereference() 41 42 def __iter__(self): 43 return self 44 45 def __next__(self): 46 if not self.current: 47 raise StopIteration 48 49 nodetype = self.nodetype if self.nodetype else self.lv_ll_node_t 50 node = self.current.cast(nodetype) 51 52 self.current = self._next(self.current) 53 return node 54 55 @property 56 def len(self): 57 len = 0 58 node = self.head 59 while node: 60 len += 1 61 node = self._next(node) 62 return len 63 64 65class LVObject(Value): 66 """LVGL object""" 67 68 def __init__(self, obj: Value): 69 super().__init__(obj.cast("lv_obj_t", ptr=True)) 70 71 @property 72 def class_name(self): 73 name = self.class_p.name 74 return name.string() if name else "unknown" 75 76 @property 77 def x1(self): 78 return int(self.coords.x1) 79 80 @property 81 def y1(self): 82 return int(self.coords.y1) 83 84 @property 85 def x2(self): 86 return int(self.coords.x2) 87 88 @property 89 def y2(self): 90 return int(self.coords.y2) 91 92 @property 93 def child_count(self): 94 return self.spec_attr.child_cnt if self.spec_attr else 0 95 96 @property 97 def childs(self): 98 if not self.spec_attr: 99 return 100 101 for i in range(self.child_count): 102 child = self.spec_attr.children[i] 103 yield LVObject(child) 104 105 @property 106 def styles(self): 107 LV_STYLE_PROP_INV = 0 108 LV_STYLE_PROP_ANY = 0xFF 109 count = self.style_cnt 110 if count == 0: 111 return 112 113 styles = self.super_value("styles") 114 for i in range(count): 115 style = styles[i].style 116 prop_cnt = style.prop_cnt 117 values_and_props = style.values_and_props.cast("lv_style_const_prop_t", ptr=True) 118 for j in range(prop_cnt): 119 prop = values_and_props[j].prop 120 if prop == LV_STYLE_PROP_INV or prop == LV_STYLE_PROP_ANY: 121 continue 122 yield values_and_props[j] 123 124 def get_child(self, index: int): 125 return ( 126 self.spec_attr.children[index] if self.spec_attr else None 127 ) 128 129 130class LVDisplay(Value): 131 """LVGL display""" 132 133 def __init__(self, disp: Value): 134 super().__init__(disp) 135 136 @property 137 def screens(self): 138 screens = self.super_value("screens") 139 for i in range(self.screen_cnt): 140 yield LVObject(screens[i]) 141 142 143class LVGL: 144 """LVGL instance""" 145 146 def __init__(self, lv_global: Value): 147 self.lv_global = lv_global.cast("lv_global_t", ptr=True) 148 149 def displays(self) -> Iterator[LVDisplay]: 150 ll = self.lv_global.disp_ll 151 if not ll: 152 return 153 154 for disp in LVList(ll, "lv_display_t"): 155 yield LVDisplay(disp) 156 157 def screen_active(self): 158 disp = self.lv_global.disp_default 159 return disp.act_scr if disp else None 160 161 def draw_units(self): 162 unit = self.lv_global.draw_info.unit_head 163 164 # Iterate through all draw units 165 while unit: 166 yield unit 167 unit = unit.next 168 169 170def set_lvgl_instance(lv_global: Union[gdb.Value, Value, None]): 171 global g_lvgl_instance 172 173 if not lv_global: 174 try: 175 lv_global = Value(gdb.parse_and_eval("lv_global").address) 176 except gdb.error as e: 177 print(f"Failed to get lv_global: {e}") 178 return 179 180 if not isinstance(lv_global, Value): 181 lv_global = Value(lv_global) 182 183 inited = lv_global.inited 184 if not inited: 185 print("\x1b[31mlvgl is not initialized yet. Please call `set_lvgl_instance(None)` later.\x1b[0m") 186 return 187 188 g_lvgl_instance = LVGL(lv_global) 189 190 191def dump_obj_info(obj: LVObject): 192 clzname = obj.class_name 193 coords = f"{obj.x1},{obj.y1},{obj.x2},{obj.y2}" 194 print(f"{clzname}@{hex(obj)} {coords}") 195 196 197# Dump lv_style_const_prop_t 198def dump_style_info(style: Value): 199 prop = int(style.prop) 200 value = style.value 201 print(f"{prop} = {value}") 202 203 204class DumpObj(gdb.Command): 205 """dump obj tree from specified obj""" 206 207 def __init__(self): 208 super(DumpObj, self).__init__( 209 "dump obj", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION 210 ) 211 212 def dump_obj(self, obj: LVObject, depth=0, limit=None): 213 if not obj: 214 return 215 216 # dump self 217 print(" " * depth, end="") 218 dump_obj_info(obj) 219 220 if limit is not None and depth >= limit: 221 return 222 223 # dump children 224 for child in obj.childs: 225 self.dump_obj(child, depth + 1, limit=limit) 226 227 def invoke(self, args, from_tty): 228 parser = argparse.ArgumentParser(description="Dump lvgl obj tree.") 229 parser.add_argument( 230 "-L", 231 "--level", 232 type=int, 233 default=None, 234 help="Limit the depth of the tree.", 235 ) 236 parser.add_argument( 237 "root", 238 type=str, 239 nargs="?", 240 default=None, 241 help="Optional root obj to dump.", 242 ) 243 try: 244 args = parser.parse_args(gdb.string_to_argv(args)) 245 except SystemExit: 246 return 247 248 if args.root: 249 root = gdb.parse_and_eval(args.root) 250 root = LVObject(root) 251 self.dump_obj(root, limit=args.level) 252 else: 253 # dump all displays 254 depth = 0 255 for disp in g_lvgl_instance.displays(): 256 print(f"Display {hex(disp)}") 257 for screen in disp.screens: 258 print(f'{" " * (depth + 1)}Screen@{hex(screen)}') 259 self.dump_obj(screen, depth=depth + 1, limit=args.level) 260 261 262class InfoStyle(gdb.Command): 263 """dump obj style value for specified obj""" 264 265 def __init__(self): 266 super(InfoStyle, self).__init__( 267 "info style", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION 268 ) 269 270 def invoke(self, args, from_tty): 271 parser = argparse.ArgumentParser(description="Dump lvgl obj local style.") 272 parser.add_argument( 273 "obj", 274 type=str, 275 help="obj to show style.", 276 ) 277 278 try: 279 args = parser.parse_args(gdb.string_to_argv(args)) 280 except SystemExit: 281 return 282 283 obj = gdb.parse_and_eval(args.obj) 284 if not obj: 285 print("Invalid obj: ", args.obj) 286 return 287 288 obj = Value(obj) 289 290 # show all styles applied to this obj 291 for style in LVObject(obj).styles: 292 print(" ", end="") 293 dump_style_info(style) 294 295 296class InfoDrawUnit(gdb.Command): 297 """dump draw unit info""" 298 299 def __init__(self): 300 super(InfoDrawUnit, self).__init__( 301 "info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION 302 ) 303 304 def dump_draw_unit(self, draw_unit: Value): 305 # Dereference to get the string content of the name from draw_unit 306 name = draw_unit.name.string() 307 308 # Print draw_unit information and the name 309 print(f"Draw Unit: {draw_unit}, Name: {name}") 310 311 # Handle different draw_units based on the name 312 def lookup_type(name): 313 try: 314 return gdb.lookup_type(name) 315 except gdb.error: 316 return None 317 318 types = { 319 "DMA2D": lookup_type("lv_draw_dma2d_unit_t"), 320 "NEMA_GFX": lookup_type("lv_draw_nema_gfx_unit_t"), 321 "NXP_PXP": lookup_type("lv_draw_pxp_unit_t"), 322 "NXP_VGLITE": lookup_type("lv_draw_vglite_unit_t"), 323 "OPENGLES": lookup_type("lv_draw_opengles_unit_t"), 324 "DAVE2D": lookup_type("lv_draw_dave2d_unit_t"), 325 "SDL": lookup_type("lv_draw_sdl_unit_t"), 326 "SW": lookup_type("lv_draw_sw_unit_t"), 327 "VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"), 328 } 329 330 type = types.get(name, lookup_type("lv_draw_unit_t")) 331 print(draw_unit.cast(type, ptr=True).dereference().format_string(pretty_structs=True, symbols=True)) 332 333 def invoke(self, args, from_tty): 334 for unit in g_lvgl_instance.draw_units(): 335 self.dump_draw_unit(unit) 336