blob: eedac7cf44cf7768ed7f766e14e2df4701dab380 [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001# 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
7This script is loaded by GDB when it finds a .debug_gdb_scripts
8section in the compiled binary. The [68]l linkers emit this with a
9path 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
18from __future__ import print_function
19import re
20import sys
21
22print("Loading Go Runtime support.", file=sys.stderr)
23#http://python3porting.com/differences.html
24if sys.version > '3':
25 xrange = range
26# allow to manually reload while developing
27goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
28goobjfile.pretty_printers = []
29
30#
31# Pretty Printers
32#
33
34
35class 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
51class 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
73class 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
124class 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
155def 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
164goobjfile.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
177def 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
184def 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
191def 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
208def 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
219def 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
241def 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
250class 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
279def ifacematcher(val):
280 if is_iface(val) or is_eface(val):
281 return IfacePrinter(val)
282
283goobjfile.pretty_printers.append(ifacematcher)
284
285#
286# Convenience Functions
287#
288
289
290class 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
305class 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
320class 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
340sts = ('idle', 'runnable', 'running', 'syscall', 'waiting', 'moribund', 'dead', 'recovery')
341
342
343def linked_list(ptr, linkfield):
344 while ptr:
345 yield ptr
346 ptr = ptr[linkfield]
347
348
349class 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
378def 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
397class 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
437class 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#
473GoLenFunc()
474GoCapFunc()
475DTypeFunc()
476GoroutinesCmd()
477GoroutineCmd()
478GoIfaceCmd()