github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/runtime/runtime-gdb.py (about) 1 # Copyright 2010 The Go Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style 3 # license that can be found in the LICENSE file. 4 5 """GDB Pretty printers and convenience functions for Go's runtime structures. 6 7 This script is loaded by GDB when it finds a .debug_gdb_scripts 8 section in the compiled binary. The [68]l linkers emit this with a 9 path to this file based on the path to the runtime package. 10 """ 11 12 # Known issues: 13 # - pretty printing only works for the 'native' strings. E.g. 'type 14 # foo string' will make foo a plain struct in the eyes of gdb, 15 # circumventing the pretty print triggering. 16 17 18 from __future__ import print_function 19 import re 20 import sys 21 22 print("Loading Go Runtime support.", file=sys.stderr) 23 #http://python3porting.com/differences.html 24 if sys.version > '3': 25 xrange = range 26 # allow to manually reload while developing 27 goobjfile = gdb.current_objfile() or gdb.objfiles()[0] 28 goobjfile.pretty_printers = [] 29 30 # 31 # Pretty Printers 32 # 33 34 35 class StringTypePrinter: 36 "Pretty print Go strings." 37 38 pattern = re.compile(r'^struct string$') 39 40 def __init__(self, val): 41 self.val = val 42 43 def display_hint(self): 44 return 'string' 45 46 def to_string(self): 47 l = int(self.val['len']) 48 return self.val['str'].string("utf-8", "ignore", l) 49 50 51 class SliceTypePrinter: 52 "Pretty print slices." 53 54 pattern = re.compile(r'^struct \[\]') 55 56 def __init__(self, val): 57 self.val = val 58 59 def display_hint(self): 60 return 'array' 61 62 def to_string(self): 63 return str(self.val.type)[6:] # skip 'struct ' 64 65 def children(self): 66 if self.val["len"] > self.val["cap"]: 67 return 68 ptr = self.val["array"] 69 for idx in range(int(self.val["len"])): 70 yield ('[{0}]'.format(idx), (ptr + idx).dereference()) 71 72 73 class MapTypePrinter: 74 """Pretty print map[K]V types. 75 76 Map-typed go variables are really pointers. dereference them in gdb 77 to inspect their contents with this pretty printer. 78 """ 79 80 pattern = re.compile(r'^map\[.*\].*$') 81 82 def __init__(self, val): 83 self.val = val 84 85 def display_hint(self): 86 return 'map' 87 88 def to_string(self): 89 return str(self.val.type) 90 91 def children(self): 92 B = self.val['b'] 93 buckets = self.val['buckets'] 94 oldbuckets = self.val['oldbuckets'] 95 flags = self.val['flags'] 96 inttype = self.val['hash0'].type 97 cnt = 0 98 for bucket in xrange(2 ** int(B)): 99 bp = buckets + bucket 100 if oldbuckets: 101 oldbucket = bucket & (2 ** (B - 1) - 1) 102 oldbp = oldbuckets + oldbucket 103 oldb = oldbp.dereference() 104 if (oldb['overflow'].cast(inttype) & 1) == 0: # old bucket not evacuated yet 105 if bucket >= 2 ** (B - 1): 106 continue # already did old bucket 107 bp = oldbp 108 while bp: 109 b = bp.dereference() 110 for i in xrange(8): 111 if b['tophash'][i] != 0: 112 k = b['keys'][i] 113 v = b['values'][i] 114 if flags & 1: 115 k = k.dereference() 116 if flags & 2: 117 v = v.dereference() 118 yield str(cnt), k 119 yield str(cnt + 1), v 120 cnt += 2 121 bp = b['overflow'] 122 123 124 class ChanTypePrinter: 125 """Pretty print chan[T] types. 126 127 Chan-typed go variables are really pointers. dereference them in gdb 128 to inspect their contents with this pretty printer. 129 """ 130 131 pattern = re.compile(r'^struct hchan<.*>$') 132 133 def __init__(self, val): 134 self.val = val 135 136 def display_hint(self): 137 return 'array' 138 139 def to_string(self): 140 return str(self.val.type) 141 142 def children(self): 143 # see chan.c chanbuf(). et is the type stolen from hchan<T>::recvq->first->elem 144 et = [x.type for x in self.val['recvq']['first'].type.target().fields() if x.name == 'elem'][0] 145 ptr = (self.val.address + 1).cast(et.pointer()) 146 for i in range(self.val["qcount"]): 147 j = (self.val["recvx"] + i) % self.val["dataqsiz"] 148 yield ('[{0}]'.format(i), (ptr + j).dereference()) 149 150 151 # 152 # Register all the *Printer classes above. 153 # 154 155 def makematcher(klass): 156 def matcher(val): 157 try: 158 if klass.pattern.match(str(val.type)): 159 return klass(val) 160 except Exception: 161 pass 162 return matcher 163 164 goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) 165 166 # 167 # For reference, this is what we're trying to do: 168 # eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string 169 # iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string 170 # 171 # interface types can't be recognized by their name, instead we check 172 # if they have the expected fields. Unfortunately the mapping of 173 # fields to python attributes in gdb.py isn't complete: you can't test 174 # for presence other than by trapping. 175 176 177 def is_iface(val): 178 try: 179 return str(val['tab'].type) == "struct runtime.itab *" and str(val['data'].type) == "void *" 180 except gdb.error: 181 pass 182 183 184 def is_eface(val): 185 try: 186 return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *" 187 except gdb.error: 188 pass 189 190 191 def lookup_type(name): 192 try: 193 return gdb.lookup_type(name) 194 except gdb.error: 195 pass 196 try: 197 return gdb.lookup_type('struct ' + name) 198 except gdb.error: 199 pass 200 try: 201 return gdb.lookup_type('struct ' + name[1:]).pointer() 202 except gdb.error: 203 pass 204 205 _rctp_type = gdb.lookup_type("struct runtime.rtype").pointer() 206 207 208 def iface_commontype(obj): 209 if is_iface(obj): 210 go_type_ptr = obj['tab']['_type'] 211 elif is_eface(obj): 212 go_type_ptr = obj['_type'] 213 else: 214 return 215 216 return go_type_ptr.cast(_rctp_type).dereference() 217 218 219 def iface_dtype(obj): 220 "Decode type of the data field of an eface or iface struct." 221 # known issue: dtype_name decoded from runtime.rtype is "nested.Foo" 222 # but the dwarf table lists it as "full/path/to/nested.Foo" 223 224 dynamic_go_type = iface_commontype(obj) 225 if dynamic_go_type is None: 226 return 227 dtype_name = dynamic_go_type['string'].dereference()['str'].string() 228 229 dynamic_gdb_type = lookup_type(dtype_name) 230 if dynamic_gdb_type is None: 231 return 232 233 type_size = int(dynamic_go_type['size']) 234 uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr 235 if type_size > uintptr_size: 236 dynamic_gdb_type = dynamic_gdb_type.pointer() 237 238 return dynamic_gdb_type 239 240 241 def iface_dtype_name(obj): 242 "Decode type name of the data field of an eface or iface struct." 243 244 dynamic_go_type = iface_commontype(obj) 245 if dynamic_go_type is None: 246 return 247 return dynamic_go_type['string'].dereference()['str'].string() 248 249 250 class IfacePrinter: 251 """Pretty print interface values 252 253 Casts the data field to the appropriate dynamic type.""" 254 255 def __init__(self, val): 256 self.val = val 257 258 def display_hint(self): 259 return 'string' 260 261 def to_string(self): 262 if self.val['data'] == 0: 263 return 0x0 264 try: 265 dtype = iface_dtype(self.val) 266 except Exception: 267 return "<bad dynamic type>" 268 269 if dtype is None: # trouble looking up, print something reasonable 270 return "({0}){0}".format(iface_dtype_name(self.val), self.val['data']) 271 272 try: 273 return self.val['data'].cast(dtype).dereference() 274 except Exception: 275 pass 276 return self.val['data'].cast(dtype) 277 278 279 def ifacematcher(val): 280 if is_iface(val) or is_eface(val): 281 return IfacePrinter(val) 282 283 goobjfile.pretty_printers.append(ifacematcher) 284 285 # 286 # Convenience Functions 287 # 288 289 290 class GoLenFunc(gdb.Function): 291 "Length of strings, slices, maps or channels" 292 293 how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount')) 294 295 def __init__(self): 296 gdb.Function.__init__(self, "len") 297 298 def invoke(self, obj): 299 typename = str(obj.type) 300 for klass, fld in self.how: 301 if klass.pattern.match(typename): 302 return obj[fld] 303 304 305 class GoCapFunc(gdb.Function): 306 "Capacity of slices or channels" 307 308 how = ((SliceTypePrinter, 'cap'), (ChanTypePrinter, 'dataqsiz')) 309 310 def __init__(self): 311 gdb.Function.__init__(self, "cap") 312 313 def invoke(self, obj): 314 typename = str(obj.type) 315 for klass, fld in self.how: 316 if klass.pattern.match(typename): 317 return obj[fld] 318 319 320 class DTypeFunc(gdb.Function): 321 """Cast Interface values to their dynamic type. 322 323 For non-interface types this behaves as the identity operation. 324 """ 325 326 def __init__(self): 327 gdb.Function.__init__(self, "dtype") 328 329 def invoke(self, obj): 330 try: 331 return obj['data'].cast(iface_dtype(obj)) 332 except gdb.error: 333 pass 334 return obj 335 336 # 337 # Commands 338 # 339 340 sts = ('idle', 'runnable', 'running', 'syscall', 'waiting', 'moribund', 'dead', 'recovery') 341 342 343 def linked_list(ptr, linkfield): 344 while ptr: 345 yield ptr 346 ptr = ptr[linkfield] 347 348 349 class GoroutinesCmd(gdb.Command): 350 "List all goroutines." 351 352 def __init__(self): 353 gdb.Command.__init__(self, "info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 354 355 def invoke(self, _arg, _from_tty): 356 # args = gdb.string_to_argv(arg) 357 vp = gdb.lookup_type('void').pointer() 358 for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): 359 if ptr['status'] == 6: # 'gdead' 360 continue 361 s = ' ' 362 if ptr['m']: 363 s = '*' 364 pc = ptr['sched']['pc'].cast(vp) 365 # python2 will not cast pc (type void*) to an int cleanly 366 # instead python2 and python3 work with the hex string representation 367 # of the void pointer which we can parse back into an int. 368 # int(pc) will not work. 369 try: 370 #python3 / newer versions of gdb 371 pc = int(pc) 372 except gdb.error: 373 pc = int(str(pc), 16) 374 blk = gdb.block_for_pc(pc) 375 print(s, ptr['goid'], "{0:8s}".format(sts[int(ptr['status'])]), blk.function) 376 377 378 def find_goroutine(goid): 379 """ 380 find_goroutine attempts to find the goroutine identified by goid. 381 It returns a touple of gdv.Value's representing the stack pointer 382 and program counter pointer for the goroutine. 383 384 @param int goid 385 386 @return tuple (gdb.Value, gdb.Value) 387 """ 388 vp = gdb.lookup_type('void').pointer() 389 for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): 390 if ptr['status'] == 6: # 'gdead' 391 continue 392 if ptr['goid'] == goid: 393 return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp')) 394 return None, None 395 396 397 class GoroutineCmd(gdb.Command): 398 """Execute gdb command in the context of goroutine <goid>. 399 400 Switch PC and SP to the ones in the goroutine's G structure, 401 execute an arbitrary gdb command, and restore PC and SP. 402 403 Usage: (gdb) goroutine <goid> <gdbcmd> 404 405 Note that it is ill-defined to modify state in the context of a goroutine. 406 Restrict yourself to inspecting values. 407 """ 408 409 def __init__(self): 410 gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 411 412 def invoke(self, arg, _from_tty): 413 goid, cmd = arg.split(None, 1) 414 goid = gdb.parse_and_eval(goid) 415 pc, sp = find_goroutine(int(goid)) 416 if not pc: 417 print("No such goroutine: ", goid) 418 return 419 try: 420 #python3 / newer versions of gdb 421 pc = int(pc) 422 except gdb.error: 423 pc = int(str(pc), 16) 424 save_frame = gdb.selected_frame() 425 gdb.parse_and_eval('$save_pc = $pc') 426 gdb.parse_and_eval('$save_sp = $sp') 427 gdb.parse_and_eval('$pc = {0}'.format(str(pc))) 428 gdb.parse_and_eval('$sp = {0}'.format(str(sp))) 429 try: 430 gdb.execute(cmd) 431 finally: 432 gdb.parse_and_eval('$pc = $save_pc') 433 gdb.parse_and_eval('$sp = $save_sp') 434 save_frame.select() 435 436 437 class GoIfaceCmd(gdb.Command): 438 "Print Static and dynamic interface types" 439 440 def __init__(self): 441 gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) 442 443 def invoke(self, arg, _from_tty): 444 for obj in gdb.string_to_argv(arg): 445 try: 446 #TODO fix quoting for qualified variable names 447 obj = gdb.parse_and_eval(str(obj)) 448 except Exception as e: 449 print("Can't parse ", obj, ": ", e) 450 continue 451 452 if obj['data'] == 0: 453 dtype = "nil" 454 else: 455 dtype = iface_dtype(obj) 456 457 if dtype is None: 458 print("Not an interface: ", obj.type) 459 continue 460 461 print("{0}: {1}".format(obj.type, dtype)) 462 463 # TODO: print interface's methods and dynamic type's func pointers thereof. 464 #rsc: "to find the number of entries in the itab's Fn field look at 465 # itab.inter->numMethods 466 # i am sure i have the names wrong but look at the interface type 467 # and its method count" 468 # so Itype will start with a commontype which has kind = interface 469 470 # 471 # Register all convenience functions and CLI commands 472 # 473 GoLenFunc() 474 GoCapFunc() 475 DTypeFunc() 476 GoroutinesCmd() 477 GoroutineCmd() 478 GoIfaceCmd()