ryml-gdbtypes.py (12652B)
1 # To make this file known to Qt Creator using: 2 # Tools > Options > Debugger > Locals & Expressions > Extra Debugging Helpers 3 # Any contents here will be picked up by GDB, LLDB, and CDB based 4 # debugging in Qt Creator automatically. 5 6 7 # Example to display a simple type 8 # template<typename U, typename V> struct MapNode 9 # { 10 # U key; 11 # V data; 12 # } 13 # 14 # def qdump__MapNode(d, value): 15 # d.putValue("This is the value column contents") 16 # d.putExpandable() 17 # if d.isExpanded(): 18 # with Children(d): 19 # # Compact simple case. 20 # d.putSubItem("key", value["key"]) 21 # # Same effect, with more customization possibilities. 22 # with SubItem(d, "data") 23 # d.putItem("data", value["data"]) 24 25 # Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html 26 # for more details or look at qttypes.py, stdtypes.py, boosttypes.py 27 # for more complex examples. 28 29 # to try parsing: 30 # env PYTHONPATH=/usr/share/qtcreator/debugger/ python src/ryml-gdbtypes.py 31 32 33 import dumper 34 #from dumper import Dumper, Value, Children, SubItem 35 #from dumper import SubItem, Children 36 from dumper import * 37 38 import sys 39 import os 40 41 42 # ----------------------------------------------------------------------------- 43 # ----------------------------------------------------------------------------- 44 # ----------------------------------------------------------------------------- 45 46 # QtCreator makes it really hard to figure out problems in this code. 47 # So here are some debugging utilities. 48 49 50 # FIXME. this decorator is not working; find out why. 51 def dbglog(func): 52 """a decorator that logs entry and exit of functions""" 53 if not _DBG: 54 return func 55 def func_wrapper(*args, **kwargs): 56 _dbg_enter(func.__name__) 57 ret = func(*args, **kwargs) 58 _dbg_exit(func.__name__) 59 return ret 60 return func_wrapper 61 62 63 _DBG = False 64 _dbg_log = None 65 _dbg_stack = 0 66 def _dbg(*args, **kwargs): 67 global _dbg_log, _dbg_stack 68 if not _DBG: 69 return 70 if _dbg_log is None: 71 filename = os.path.join(os.path.dirname(__file__), "dbg.txt") 72 _dbg_log = open(filename, "w") 73 kwargs['file'] = _dbg_log 74 kwargs['flush'] = True 75 print(" " * _dbg_stack, *args, **kwargs) 76 77 78 def _dbg_enter(name): 79 global _dbg_stack 80 _dbg(name, "- enter") 81 _dbg_stack += 1 82 83 84 def _dbg_exit(name): 85 global _dbg_stack 86 _dbg_stack -= 1 87 _dbg(name, "- exit!") 88 89 90 91 # ----------------------------------------------------------------------------- 92 # ----------------------------------------------------------------------------- 93 # ----------------------------------------------------------------------------- 94 95 96 NPOS = 18446744073709551615 97 MAX_SUBSTR_LEN_DISPLAY = 80 98 MAX_SUBSTR_LEN_EXPAND = 1000 99 100 101 def get_str_value(d, value, limit=0): 102 # adapted from dumper.py::Dumper::putCharArrayValue() 103 m_str = value["str"].pointer() 104 m_len = value["len"].integer() 105 if m_len == NPOS: 106 _dbg("getstr... 1", m_len) 107 m_str = "!!!!!<npos>!!!!!" 108 m_len = len(m_str) 109 return m_str, m_len 110 if limit == 0: 111 limit = d.displayStringLimit 112 elided, shown = d.computeLimit(m_len, limit) 113 mem = bytes(d.readRawMemory(m_str, shown)) 114 mem = mem.decode('utf8') 115 return mem, m_len 116 117 118 def __display_csubstr(d, value, limit=0): 119 m_str, m_len = get_str_value(d, value) 120 safe_len = min(m_len, MAX_SUBSTR_LEN_DISPLAY) 121 disp = m_str[0:safe_len] 122 # ensure the string escapes characters like \n\r\t etc 123 disp = disp.encode('unicode_escape').decode('utf8') 124 # WATCHOUT. quotes in the string will make qtcreator hang!!! 125 disp = disp.replace('"', '\\"') 126 disp = disp.replace('\'', '\\') 127 if m_len <= MAX_SUBSTR_LEN_DISPLAY: 128 d.putValue(f"[{m_len}] '{disp}'") 129 else: 130 d.putValue(f"[{m_len}] '{disp}'...") 131 return m_str, m_len 132 133 134 def qdump__c4__csubstr(d, value): 135 m_str, m_len = __display_csubstr(d, value) 136 d.putExpandable() 137 if d.isExpanded(): 138 with Children(d): 139 safe_len = min(m_len, MAX_SUBSTR_LEN_EXPAND) 140 for i in range(safe_len): 141 ct = d.createType('char') 142 d.putSubItem(safe_len, d.createValue(value["str"].pointer() + i, ct)) 143 d.putSubItem("len", value["len"]) 144 d.putPtrItem("str", value["str"].pointer()) 145 146 147 def qdump__c4__substr(d, value): 148 qdump__c4__csubstr(d, value) 149 150 151 def qdump__c4__basic_substring(d, value): 152 qdump__c4__csubstr(d, value) 153 154 155 def qdump__c4__yml__NodeScalar(d, value): 156 alen = value["anchor"]["len"].integer() 157 tlen = value["tag" ]["len"].integer() 158 m_str, m_len = get_str_value(d, value["scalar"]) 159 if alen == 0 and tlen == 0: 160 d.putValue(f'\'{m_str}\'') 161 elif alen == 0 and tlen > 0: 162 d.putValue(f'\'{m_str}\' [Ta]') 163 elif alen > 0 and tlen == 0: 164 d.putValue(f'\'{m_str}\' [tA]') 165 elif alen > 0 and tlen > 0: 166 d.putValue(f'\'{m_str}\' [TA]') 167 d.putExpandable() 168 if d.isExpanded(): 169 with Children(d): 170 d.putSubItem("[scalar]", value["scalar"]) 171 if tlen > 0: 172 d.putSubItem("[tag]", value["tag"]) 173 if alen > 0: 174 d.putSubItem("[anchor or ref]", value["anchor"]) 175 176 177 def _format_enum_value(int_value, enum_map): 178 str_value = enum_map.get(int_value, None) 179 display = f'{int_value}' if str_value is None else f'{str_value} ({int_value})' 180 return display 181 182 183 def _format_bitmask_value(int_value, enum_map): 184 str_value = enum_map.get(int_value, None) 185 if str_value: 186 return f'{str_value} ({int_value})' 187 else: 188 out = "" 189 orig = int_value 190 # do in reverse to get compound flags first 191 for k, v in reversed(enum_map.items()): 192 if (k != 0): 193 if (int_value & k) == k: 194 if len(out) > 0: 195 out += '|' 196 out += v 197 int_value &= ~k 198 else: 199 if len(out) == 0 and int_value == 0: 200 return v 201 if out == "": 202 return f'{int_value}' 203 return f"{out} ({orig})" 204 205 206 def _c4bit(*ints): 207 ret = 0 208 for i in ints: 209 ret |= 1 << i 210 return ret 211 212 213 node_types = { 214 0: "NOTYPE", 215 _c4bit(0): "VAL" , 216 _c4bit(1): "KEY" , 217 _c4bit(2): "MAP" , 218 _c4bit(3): "SEQ" , 219 _c4bit(4): "DOC" , 220 _c4bit(5,3): "STREAM", 221 _c4bit(6): "KEYREF" , 222 _c4bit(7): "VALREF" , 223 _c4bit(8): "KEYANCH" , 224 _c4bit(9): "VALANCH" , 225 _c4bit(10): "KEYTAG" , 226 _c4bit(11): "VALTAG" , 227 _c4bit(12): "VALQUO" , 228 _c4bit(13): "KEYQUO" , 229 _c4bit(1,0): "KEYVAL", 230 _c4bit(1,3): "KEYSEQ", 231 _c4bit(1,2): "KEYMAP", 232 _c4bit(4,2): "DOCMAP", 233 _c4bit(4,3): "DOCSEQ", 234 _c4bit(4,0): "DOCVAL", 235 # 236 _c4bit(14): "STYLE_FLOW_SL", 237 _c4bit(15): "STYLE_FLOW_ML", 238 _c4bit(16): "STYLE_BLOCK", 239 # 240 _c4bit(17): "KEY_LITERAL", 241 _c4bit(18): "VAL_LITERAL", 242 _c4bit(19): "KEY_FOLDED", 243 _c4bit(20): "VAL_FOLDED", 244 _c4bit(21): "KEY_SQUO", 245 _c4bit(22): "VAL_SQUO", 246 _c4bit(23): "KEY_DQUO", 247 _c4bit(24): "VAL_DQUO", 248 _c4bit(25): "KEY_PLAIN", 249 _c4bit(26): "VAL_PLAIN", 250 } 251 node_types_rev = {v: k for k, v in node_types.items()} 252 253 254 def _node_type_has_all(node_type_value, type_name): 255 exp = node_types_rev[type_name] 256 return (node_type_value & exp) == exp 257 258 259 def _node_type_has_any(node_type_value, type_name): 260 exp = node_types_rev[type_name] 261 return (node_type_value & exp) != 0 262 263 264 def qdump__c4__yml__NodeType_e(d, value): 265 v = _format_bitmask_value(value.integer(), node_types) 266 d.putValue(v) 267 268 269 def qdump__c4__yml__NodeType(d, value): 270 qdump__c4__yml__NodeType_e(d, value["type"]) 271 272 273 def qdump__c4__yml__NodeData(d, value): 274 d.putValue("wtf") 275 ty = _format_bitmask_value(value.integer(), node_types) 276 t = value["m_type"]["type"].integer() 277 k = value["m_key"]["scalar"] 278 v = value["m_val"]["scalar"] 279 sk, lk = get_str_value(d, k) 280 sv, lv = get_str_value(d, v) 281 if _node_type_has_all(t, "KEYVAL"): 282 d.putValue(f"'{sk}': '{sv}' {ty}") 283 elif _node_type_has_any(t, "KEY"): 284 d.putValue(f"'{sk}': {ty}") 285 elif _node_type_has_any(t, "VAL"): 286 d.putValue(f"'{sv}' {ty}") 287 else: 288 d.putValue(f"{ty}") 289 d.putExpandable() 290 if d.isExpanded(): 291 with Children(d): 292 d.putSubItem("m_type", value["m_type"]) 293 # key 294 if _node_type_has_any(t, "KEY"): 295 d.putSubItem("m_key", value["m_key"]) 296 if _node_type_has_any(t, "KEYREF"): 297 with SubItem(d, "m_key.ref"): 298 s_, _ = get_str_value(d, value["m_key"]["anchor"]) 299 d.putValue(f"'{s_}'") 300 if _node_type_has_any(t, "KEYANCH"): 301 with SubItem(d, "m_key.anchor"): 302 s_, _ = get_str_value(d, value["m_key"]["anchor"]) 303 d.putValue(f"'{s_}'") 304 if _node_type_has_any(t, "KEYTAG"): 305 with SubItem(d, "m_key.tag"): 306 s_, _ = get_str_value(d, value["m_key"]["tag"]) 307 d.putValue(f"'{s_}'") 308 # val 309 if _node_type_has_any(t, "VAL"): 310 d.putSubItem("m_val", value["m_val"]) 311 if _node_type_has_any(t, "VALREF"): 312 with SubItem(d, "m_val.ref"): 313 s_, _ = get_str_value(d, value["m_val"]["anchor"]) 314 d.putValue(f"'{s_}'") 315 if _node_type_has_any(t, "VALANCH"): 316 with SubItem(d, "m_val.anchor"): 317 s_, _ = get_str_value(d, value["m_val"]["anchor"]) 318 d.putValue(f"'{s_}'") 319 if _node_type_has_any(t, "VALTAG"): 320 with SubItem(d, "m_val.tag"): 321 s_, _ = get_str_value(d, value["m_val"]["tag"]) 322 d.putValue(f"'{s_}'") 323 # hierarchy 324 _dump_node_index(d, "m_parent", value) 325 _dump_node_index(d, "m_first_child", value) 326 _dump_node_index(d, "m_last_child", value) 327 _dump_node_index(d, "m_next_sibling", value) 328 _dump_node_index(d, "m_prev_sibling", value) 329 330 331 def _dump_node_index(d, name, value): 332 if int(value[name].integer()) == NPOS: 333 pass 334 #with SubItem(d, name): 335 # d.putValue("-") 336 else: 337 d.putSubItem(name, value[name]) 338 339 340 # c4::yml::Tree 341 def qdump__c4__yml__Tree(d, value): 342 m_size = value["m_size"].integer() 343 m_cap = value["m_cap"].integer() 344 d.putExpandable() 345 if d.isExpanded(): 346 #d.putArrayData(value["m_buf"], m_size, value["m_buf"].dereference()) 347 with Children(d): 348 with SubItem(d, f"[nodes]"): 349 d.putItemCount(m_size) 350 d.putArrayData(value["m_buf"].pointer(), m_size, value["m_buf"].type.dereference()) 351 d.putPtrItem("m_buf", value["m_buf"].pointer()) 352 d.putIntItem("m_size", value["m_size"]) 353 d.putIntItem("m_cap (capacity)", value["m_cap"]) 354 d.putIntItem("[slack]", m_cap - m_size) 355 d.putIntItem("m_free_head", value["m_free_head"]) 356 d.putIntItem("m_free_tail", value["m_free_tail"]) 357 d.putSubItem("m_arena", value["m_arena"]) 358 359 360 def qdump__c4__yml__detail__stack(d, value): 361 T = value.type[0] 362 N = value.type[0] 363 m_size = value["m_size"].integer() 364 m_capacity = value["m_capacity"].integer() 365 d.putItemCount(m_size) 366 if d.isExpanded(): 367 with Children(d): 368 with SubItem(d, f"[nodes]"): 369 d.putItemCount(m_size) 370 d.putArrayData(value["m_stack"].pointer(), m_size, T) 371 d.putIntItem("m_size", value["m_size"]) 372 d.putIntItem("m_capacity", value["m_capacity"]) 373 #d.putIntItem("[small capacity]", N) 374 d.putIntItem("[is large]", value["m_buf"].address() == value["m_stack"].pointer()) 375 d.putPtrItem("m_stack", value["m_stack"].pointer()) 376 d.putPtrItem("m_buf", value["m_buf"].address()) 377 378 379 def qdump__c4__yml__detail__ReferenceResolver__refdata(d, value): 380 node = value["node"].integer() 381 ty = _format_bitmask_value(value["type"].integer(), node_types) 382 d.putValue(f'{node} {ty}') 383 d.putExpandable() 384 if d.isExpanded(): 385 with Children(d): 386 d.putSubItem("type", value["type"]) 387 d.putSubItem("node", value["node"]) 388 _dump_node_index(d, "prev_anchor", value) 389 _dump_node_index(d, "target", value) 390 _dump_node_index(d, "parent_ref", value) 391 _dump_node_index(d, "parent_ref_sibling", value)