Text file
src/runtime/runtime-gdb.py
Documentation: runtime
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 # G state (runtime2.go)
31
32 def read_runtime_const(varname, default):
33 try:
34 return int(gdb.parse_and_eval(varname))
35 except Exception:
36 return int(default)
37
38
39 G_IDLE = read_runtime_const("'runtime._Gidle'", 0)
40 G_RUNNABLE = read_runtime_const("'runtime._Grunnable'", 1)
41 G_RUNNING = read_runtime_const("'runtime._Grunning'", 2)
42 G_SYSCALL = read_runtime_const("'runtime._Gsyscall'", 3)
43 G_WAITING = read_runtime_const("'runtime._Gwaiting'", 4)
44 G_MORIBUND_UNUSED = read_runtime_const("'runtime._Gmoribund_unused'", 5)
45 G_DEAD = read_runtime_const("'runtime._Gdead'", 6)
46 G_ENQUEUE_UNUSED = read_runtime_const("'runtime._Genqueue_unused'", 7)
47 G_COPYSTACK = read_runtime_const("'runtime._Gcopystack'", 8)
48 G_SCAN = read_runtime_const("'runtime._Gscan'", 0x1000)
49 G_SCANRUNNABLE = G_SCAN+G_RUNNABLE
50 G_SCANRUNNING = G_SCAN+G_RUNNING
51 G_SCANSYSCALL = G_SCAN+G_SYSCALL
52 G_SCANWAITING = G_SCAN+G_WAITING
53
54 sts = {
55 G_IDLE: 'idle',
56 G_RUNNABLE: 'runnable',
57 G_RUNNING: 'running',
58 G_SYSCALL: 'syscall',
59 G_WAITING: 'waiting',
60 G_MORIBUND_UNUSED: 'moribund',
61 G_DEAD: 'dead',
62 G_ENQUEUE_UNUSED: 'enqueue',
63 G_COPYSTACK: 'copystack',
64 G_SCAN: 'scan',
65 G_SCANRUNNABLE: 'runnable+s',
66 G_SCANRUNNING: 'running+s',
67 G_SCANSYSCALL: 'syscall+s',
68 G_SCANWAITING: 'waiting+s',
69 }
70
71
72 #
73 # Value wrappers
74 #
75
76 class SliceValue:
77 "Wrapper for slice values."
78
79 def __init__(self, val):
80 self.val = val
81
82 @property
83 def len(self):
84 return int(self.val['len'])
85
86 @property
87 def cap(self):
88 return int(self.val['cap'])
89
90 def __getitem__(self, i):
91 if i < 0 or i >= self.len:
92 raise IndexError(i)
93 ptr = self.val["array"]
94 return (ptr + i).dereference()
95
96
97 #
98 # Pretty Printers
99 #
100
101
102 class StringTypePrinter:
103 "Pretty print Go strings."
104
105 pattern = re.compile(r'^struct string( \*)?$')
106
107 def __init__(self, val):
108 self.val = val
109
110 def display_hint(self):
111 return 'string'
112
113 def to_string(self):
114 l = int(self.val['len'])
115 return self.val['str'].string("utf-8", "ignore", l)
116
117
118 class SliceTypePrinter:
119 "Pretty print slices."
120
121 pattern = re.compile(r'^struct \[\]')
122
123 def __init__(self, val):
124 self.val = val
125
126 def display_hint(self):
127 return 'array'
128
129 def to_string(self):
130 return str(self.val.type)[6:] # skip 'struct '
131
132 def children(self):
133 sval = SliceValue(self.val)
134 if sval.len > sval.cap:
135 return
136 for idx, item in enumerate(sval):
137 yield ('[{0}]'.format(idx), item)
138
139
140 class MapTypePrinter:
141 """Pretty print map[K]V types.
142
143 Map-typed go variables are really pointers. dereference them in gdb
144 to inspect their contents with this pretty printer.
145 """
146
147 pattern = re.compile(r'^map\[.*\].*$')
148
149 def __init__(self, val):
150 self.val = val
151
152 def display_hint(self):
153 return 'map'
154
155 def to_string(self):
156 return str(self.val.type)
157
158 def children(self):
159 B = self.val['B']
160 buckets = self.val['buckets']
161 oldbuckets = self.val['oldbuckets']
162 flags = self.val['flags']
163 inttype = self.val['hash0'].type
164 cnt = 0
165 for bucket in xrange(2 ** int(B)):
166 bp = buckets + bucket
167 if oldbuckets:
168 oldbucket = bucket & (2 ** (B - 1) - 1)
169 oldbp = oldbuckets + oldbucket
170 oldb = oldbp.dereference()
171 if (oldb['overflow'].cast(inttype) & 1) == 0: # old bucket not evacuated yet
172 if bucket >= 2 ** (B - 1):
173 continue # already did old bucket
174 bp = oldbp
175 while bp:
176 b = bp.dereference()
177 for i in xrange(8):
178 if b['tophash'][i] != 0:
179 k = b['keys'][i]
180 v = b['values'][i]
181 if flags & 1:
182 k = k.dereference()
183 if flags & 2:
184 v = v.dereference()
185 yield str(cnt), k
186 yield str(cnt + 1), v
187 cnt += 2
188 bp = b['overflow']
189
190
191 class ChanTypePrinter:
192 """Pretty print chan[T] types.
193
194 Chan-typed go variables are really pointers. dereference them in gdb
195 to inspect their contents with this pretty printer.
196 """
197
198 pattern = re.compile(r'^struct hchan<.*>$')
199
200 def __init__(self, val):
201 self.val = val
202
203 def display_hint(self):
204 return 'array'
205
206 def to_string(self):
207 return str(self.val.type)
208
209 def children(self):
210 # see chan.c chanbuf(). et is the type stolen from hchan<T>::recvq->first->elem
211 et = [x.type for x in self.val['recvq']['first'].type.target().fields() if x.name == 'elem'][0]
212 ptr = (self.val.address + 1).cast(et.pointer())
213 for i in range(self.val["qcount"]):
214 j = (self.val["recvx"] + i) % self.val["dataqsiz"]
215 yield ('[{0}]'.format(i), (ptr + j).dereference())
216
217
218 #
219 # Register all the *Printer classes above.
220 #
221
222 def makematcher(klass):
223 def matcher(val):
224 try:
225 if klass.pattern.match(str(val.type)):
226 return klass(val)
227 except Exception:
228 pass
229 return matcher
230
231 goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')])
232
233
234 #
235 # Utilities
236 #
237
238 def pc_to_int(pc):
239 # python2 will not cast pc (type void*) to an int cleanly
240 # instead python2 and python3 work with the hex string representation
241 # of the void pointer which we can parse back into an int.
242 # int(pc) will not work.
243 try:
244 # python3 / newer versions of gdb
245 pc = int(pc)
246 except gdb.error:
247 # str(pc) can return things like
248 # "0x429d6c <runtime.gopark+284>", so
249 # chop at first space.
250 pc = int(str(pc).split(None, 1)[0], 16)
251 return pc
252
253
254 #
255 # For reference, this is what we're trying to do:
256 # eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string
257 # iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string
258 #
259 # interface types can't be recognized by their name, instead we check
260 # if they have the expected fields. Unfortunately the mapping of
261 # fields to python attributes in gdb.py isn't complete: you can't test
262 # for presence other than by trapping.
263
264
265 def is_iface(val):
266 try:
267 return str(val['tab'].type) == "struct runtime.itab *" and str(val['data'].type) == "void *"
268 except gdb.error:
269 pass
270
271
272 def is_eface(val):
273 try:
274 return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *"
275 except gdb.error:
276 pass
277
278
279 def lookup_type(name):
280 try:
281 return gdb.lookup_type(name)
282 except gdb.error:
283 pass
284 try:
285 return gdb.lookup_type('struct ' + name)
286 except gdb.error:
287 pass
288 try:
289 return gdb.lookup_type('struct ' + name[1:]).pointer()
290 except gdb.error:
291 pass
292
293
294 def iface_commontype(obj):
295 if is_iface(obj):
296 go_type_ptr = obj['tab']['_type']
297 elif is_eface(obj):
298 go_type_ptr = obj['_type']
299 else:
300 return
301
302 return go_type_ptr.cast(gdb.lookup_type("struct reflect.rtype").pointer()).dereference()
303
304
305 def iface_dtype(obj):
306 "Decode type of the data field of an eface or iface struct."
307 # known issue: dtype_name decoded from runtime.rtype is "nested.Foo"
308 # but the dwarf table lists it as "full/path/to/nested.Foo"
309
310 dynamic_go_type = iface_commontype(obj)
311 if dynamic_go_type is None:
312 return
313 dtype_name = dynamic_go_type['string'].dereference()['str'].string()
314
315 dynamic_gdb_type = lookup_type(dtype_name)
316 if dynamic_gdb_type is None:
317 return
318
319 type_size = int(dynamic_go_type['size'])
320 uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr
321 if type_size > uintptr_size:
322 dynamic_gdb_type = dynamic_gdb_type.pointer()
323
324 return dynamic_gdb_type
325
326
327 def iface_dtype_name(obj):
328 "Decode type name of the data field of an eface or iface struct."
329
330 dynamic_go_type = iface_commontype(obj)
331 if dynamic_go_type is None:
332 return
333 return dynamic_go_type['string'].dereference()['str'].string()
334
335
336 class IfacePrinter:
337 """Pretty print interface values
338
339 Casts the data field to the appropriate dynamic type."""
340
341 def __init__(self, val):
342 self.val = val
343
344 def display_hint(self):
345 return 'string'
346
347 def to_string(self):
348 if self.val['data'] == 0:
349 return 0x0
350 try:
351 dtype = iface_dtype(self.val)
352 except Exception:
353 return "<bad dynamic type>"
354
355 if dtype is None: # trouble looking up, print something reasonable
356 return "({0}){0}".format(iface_dtype_name(self.val), self.val['data'])
357
358 try:
359 return self.val['data'].cast(dtype).dereference()
360 except Exception:
361 pass
362 return self.val['data'].cast(dtype)
363
364
365 def ifacematcher(val):
366 if is_iface(val) or is_eface(val):
367 return IfacePrinter(val)
368
369 goobjfile.pretty_printers.append(ifacematcher)
370
371 #
372 # Convenience Functions
373 #
374
375
376 class GoLenFunc(gdb.Function):
377 "Length of strings, slices, maps or channels"
378
379 how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount'))
380
381 def __init__(self):
382 gdb.Function.__init__(self, "len")
383
384 def invoke(self, obj):
385 typename = str(obj.type)
386 for klass, fld in self.how:
387 if klass.pattern.match(typename):
388 return obj[fld]
389
390
391 class GoCapFunc(gdb.Function):
392 "Capacity of slices or channels"
393
394 how = ((SliceTypePrinter, 'cap'), (ChanTypePrinter, 'dataqsiz'))
395
396 def __init__(self):
397 gdb.Function.__init__(self, "cap")
398
399 def invoke(self, obj):
400 typename = str(obj.type)
401 for klass, fld in self.how:
402 if klass.pattern.match(typename):
403 return obj[fld]
404
405
406 class DTypeFunc(gdb.Function):
407 """Cast Interface values to their dynamic type.
408
409 For non-interface types this behaves as the identity operation.
410 """
411
412 def __init__(self):
413 gdb.Function.__init__(self, "dtype")
414
415 def invoke(self, obj):
416 try:
417 return obj['data'].cast(iface_dtype(obj))
418 except gdb.error:
419 pass
420 return obj
421
422 #
423 # Commands
424 #
425
426 def linked_list(ptr, linkfield):
427 while ptr:
428 yield ptr
429 ptr = ptr[linkfield]
430
431
432 class GoroutinesCmd(gdb.Command):
433 "List all goroutines."
434
435 def __init__(self):
436 gdb.Command.__init__(self, "info goroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
437
438 def invoke(self, _arg, _from_tty):
439 # args = gdb.string_to_argv(arg)
440 vp = gdb.lookup_type('void').pointer()
441 for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
442 if ptr['atomicstatus'] == G_DEAD:
443 continue
444 s = ' '
445 if ptr['m']:
446 s = '*'
447 pc = ptr['sched']['pc'].cast(vp)
448 pc = pc_to_int(pc)
449 blk = gdb.block_for_pc(pc)
450 status = int(ptr['atomicstatus'])
451 st = sts.get(status, "unknown(%d)" % status)
452 print(s, ptr['goid'], "{0:8s}".format(st), blk.function)
453
454
455 def find_goroutine(goid):
456 """
457 find_goroutine attempts to find the goroutine identified by goid.
458 It returns a tuple of gdb.Value's representing the stack pointer
459 and program counter pointer for the goroutine.
460
461 @param int goid
462
463 @return tuple (gdb.Value, gdb.Value)
464 """
465 vp = gdb.lookup_type('void').pointer()
466 for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
467 if ptr['atomicstatus'] == G_DEAD:
468 continue
469 if ptr['goid'] == goid:
470 break
471 else:
472 return None, None
473 # Get the goroutine's saved state.
474 pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
475 status = ptr['atomicstatus']&~G_SCAN
476 # Goroutine is not running nor in syscall, so use the info in goroutine
477 if status != G_RUNNING and status != G_SYSCALL:
478 return pc.cast(vp), sp.cast(vp)
479
480 # If the goroutine is in a syscall, use syscallpc/sp.
481 pc, sp = ptr['syscallpc'], ptr['syscallsp']
482 if sp != 0:
483 return pc.cast(vp), sp.cast(vp)
484 # Otherwise, the goroutine is running, so it doesn't have
485 # saved scheduler state. Find G's OS thread.
486 m = ptr['m']
487 if m == 0:
488 return None, None
489 for thr in gdb.selected_inferior().threads():
490 if thr.ptid[1] == m['procid']:
491 break
492 else:
493 return None, None
494 # Get scheduler state from the G's OS thread state.
495 curthr = gdb.selected_thread()
496 try:
497 thr.switch()
498 pc = gdb.parse_and_eval('$pc')
499 sp = gdb.parse_and_eval('$sp')
500 finally:
501 curthr.switch()
502 return pc.cast(vp), sp.cast(vp)
503
504
505 class GoroutineCmd(gdb.Command):
506 """Execute gdb command in the context of goroutine <goid>.
507
508 Switch PC and SP to the ones in the goroutine's G structure,
509 execute an arbitrary gdb command, and restore PC and SP.
510
511 Usage: (gdb) goroutine <goid> <gdbcmd>
512
513 Note that it is ill-defined to modify state in the context of a goroutine.
514 Restrict yourself to inspecting values.
515 """
516
517 def __init__(self):
518 gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
519
520 def invoke(self, arg, _from_tty):
521 goid, cmd = arg.split(None, 1)
522 goid = gdb.parse_and_eval(goid)
523 pc, sp = find_goroutine(int(goid))
524 if not pc:
525 print("No such goroutine: ", goid)
526 return
527 pc = pc_to_int(pc)
528 save_frame = gdb.selected_frame()
529 gdb.parse_and_eval('$save_sp = $sp')
530 gdb.parse_and_eval('$save_pc = $pc')
531 gdb.parse_and_eval('$sp = {0}'.format(str(sp)))
532 gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
533 try:
534 gdb.execute(cmd)
535 finally:
536 gdb.parse_and_eval('$sp = $save_sp')
537 gdb.parse_and_eval('$pc = $save_pc')
538 save_frame.select()
539
540
541 class GoIfaceCmd(gdb.Command):
542 "Print Static and dynamic interface types"
543
544 def __init__(self):
545 gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
546
547 def invoke(self, arg, _from_tty):
548 for obj in gdb.string_to_argv(arg):
549 try:
550 #TODO fix quoting for qualified variable names
551 obj = gdb.parse_and_eval(str(obj))
552 except Exception as e:
553 print("Can't parse ", obj, ": ", e)
554 continue
555
556 if obj['data'] == 0:
557 dtype = "nil"
558 else:
559 dtype = iface_dtype(obj)
560
561 if dtype is None:
562 print("Not an interface: ", obj.type)
563 continue
564
565 print("{0}: {1}".format(obj.type, dtype))
566
567 # TODO: print interface's methods and dynamic type's func pointers thereof.
568 #rsc: "to find the number of entries in the itab's Fn field look at
569 # itab.inter->numMethods
570 # i am sure i have the names wrong but look at the interface type
571 # and its method count"
572 # so Itype will start with a commontype which has kind = interface
573
574 #
575 # Register all convenience functions and CLI commands
576 #
577 GoLenFunc()
578 GoCapFunc()
579 DTypeFunc()
580 GoroutinesCmd()
581 GoroutineCmd()
582 GoIfaceCmd()
View as plain text