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