9
9
path to this file based on the path to the runtime package.
10
10
"""
11
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
+
12
18
import sys , re
13
19
14
20
print >> sys .stderr , "Loading Go Runtime support."
15
21
22
+ # allow to manually reload while developing
23
+ goobjfile = gdb .current_objfile () or gdb .objfiles ()[0 ]
24
+ goobjfile .pretty_printers = []
25
+
16
26
#
17
27
# Pretty Printers
18
28
#
@@ -95,7 +105,7 @@ def traverse_hash(self, stab):
95
105
class ChanTypePrinter :
96
106
"""Pretty print chan[T] types.
97
107
98
- Map -typed go variables are really pointers. dereference them in gdb
108
+ Chan -typed go variables are really pointers. dereference them in gdb
99
109
to inspect their contents with this pretty printer.
100
110
"""
101
111
@@ -117,18 +127,109 @@ def children(self):
117
127
ptr = ptr ['link' ]
118
128
119
129
#
120
- # Register all the *Printer classes
130
+ # Register all the *Printer classes above.
121
131
#
122
132
123
133
def makematcher (klass ):
124
134
def matcher (val ):
125
135
try :
126
- if klass .pattern .match (str (val .type )): return klass (val )
127
- except : pass
136
+ if klass .pattern .match (str (val .type )):
137
+ return klass (val )
138
+ except :
139
+ pass
128
140
return matcher
129
141
130
- gdb . current_objfile () .pretty_printers .extend ([makematcher (k ) for k in vars ().values () if hasattr (k , 'pattern' )])
142
+ goobjfile .pretty_printers .extend ([makematcher (k ) for k in vars ().values () if hasattr (k , 'pattern' )])
131
143
144
+ #
145
+ # For reference, this is what we're trying to do:
146
+ # eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string
147
+ # iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string
148
+ #
149
+ # interface types can't be recognized by their name, instead we check
150
+ # if they have the expected fields. Unfortunately the mapping of
151
+ # fields to python attributes in gdb.py isn't complete: you can't test
152
+ # for presence other than by trapping.
153
+
154
+
155
+ def is_iface (val ):
156
+ try :
157
+ return str (val ['tab' ].type ) == "struct runtime.itab *" \
158
+ and str (val ['data' ].type ) == "void *"
159
+ except :
160
+ pass
161
+
162
+ def is_eface (val ):
163
+ try :
164
+ return str (val ['type_' ].type ) == "runtime.Type *" \
165
+ and str (val ['data' ].type ) == "void *"
166
+ except :
167
+ pass
168
+
169
+ def lookup_type (name ):
170
+ try :
171
+ return gdb .lookup_type (name )
172
+ except :
173
+ pass
174
+ try :
175
+ return gdb .lookup_type ('struct ' + name )
176
+ except :
177
+ pass
178
+ try :
179
+ return gdb .lookup_type ('struct ' + name [1 :]).pointer ()
180
+ except :
181
+ pass
182
+
183
+
184
+ def iface_dtype (obj ):
185
+ "Decode type of the data field of an eface or iface struct."
186
+
187
+ if is_iface (obj ):
188
+ go_type_ptr = obj ['tab' ]['Type' ]
189
+ elif is_eface (obj ):
190
+ go_type_ptr = obj ['type_' ]
191
+ else :
192
+ return
193
+
194
+ ct = gdb .lookup_type ("struct runtime.commonType" ).pointer ()
195
+ dynamic_go_type = go_type_ptr ['data' ].cast (ct ).dereference ()
196
+ dtype_name = dynamic_go_type ['string' ].dereference ()['str' ].string ()
197
+ type_size = int (dynamic_go_type ['size' ])
198
+ uintptr_size = int (dynamic_go_type ['size' ].type .sizeof ) # size is itself an uintptr
199
+ dynamic_gdb_type = lookup_type (dtype_name )
200
+ if type_size > uintptr_size :
201
+ dynamic_gdb_type = dynamic_gdb_type .pointer ()
202
+ return dynamic_gdb_type
203
+
204
+
205
+ class IfacePrinter :
206
+ """Pretty print interface values
207
+
208
+ Casts the data field to the appropriate dynamic type."""
209
+
210
+ def __init__ (self , val ):
211
+ self .val = val
212
+
213
+ def display_hint (self ):
214
+ return 'string'
215
+
216
+ def to_string (self ):
217
+ try :
218
+ dtype = iface_dtype (self .val )
219
+ except :
220
+ return "<bad dynamic type>"
221
+ try :
222
+ return self .val ['data' ].cast (dtype ).dereference ()
223
+ except :
224
+ pass
225
+ return self .val ['data' ].cast (dtype )
226
+
227
+
228
+ def ifacematcher (val ):
229
+ if is_iface (val ) or is_eface (val ):
230
+ return IfacePrinter (val )
231
+
232
+ goobjfile .pretty_printers .append (ifacematcher )
132
233
133
234
#
134
235
# Convenience Functions
@@ -137,35 +238,158 @@ def matcher(val):
137
238
class GoLenFunc (gdb .Function ):
138
239
"Length of strings, slices, maps or channels"
139
240
140
- how = ((StringTypePrinter , 'len' ),
141
- (SliceTypePrinter , 'len' ),
142
- (MapTypePrinter , 'count' ),
143
- (ChanTypePrinter , 'qcount' ))
241
+ how = ((StringTypePrinter , 'len' ),
242
+ (SliceTypePrinter , 'len' ),
243
+ (MapTypePrinter , 'count' ),
244
+ (ChanTypePrinter , 'qcount' ))
144
245
145
246
def __init__ (self ):
146
247
super (GoLenFunc , self ).__init__ ("len" )
147
248
148
249
def invoke (self , obj ):
149
250
typename = str (obj .type )
150
- for klass , fld in self .how :
251
+ for klass , fld in self .how :
151
252
if klass .pattern .match (typename ):
152
253
return obj [fld ]
153
254
154
255
class GoCapFunc (gdb .Function ):
155
256
"Capacity of slices or channels"
156
257
157
- how = ((SliceTypePrinter , 'cap' ),
158
- (ChanTypePrinter , 'dataqsiz' ))
258
+ how = ((SliceTypePrinter , 'cap' ),
259
+ (ChanTypePrinter , 'dataqsiz' ))
159
260
160
261
def __init__ (self ):
161
262
super (GoCapFunc , self ).__init__ ("cap" )
162
263
163
264
def invoke (self , obj ):
164
265
typename = str (obj .type )
165
- for klass , fld in self .how :
266
+ for klass , fld in self .how :
166
267
if klass .pattern .match (typename ):
167
268
return obj [fld ]
168
269
270
+ class DTypeFunc (gdb .Function ):
271
+ """Cast Interface values to their dynamic type.
272
+
273
+ For non-interface types this behaves as the identity operation.
274
+ """
275
+
276
+ def __init__ (self ):
277
+ super (DTypeFunc , self ).__init__ ("dtype" )
278
+
279
+ def invoke (self , obj ):
280
+ try :
281
+ return obj ['data' ].cast (iface_dtype (obj ))
282
+ except :
283
+ pass
284
+ return obj
285
+
286
+ #
287
+ # Commands
288
+ #
289
+
290
+ sts = ( 'idle' , 'runnable' , 'running' , 'syscall' , 'waiting' , 'moribund' , 'dead' , 'recovery' )
291
+
292
+ def linked_list (ptr , linkfield ):
293
+ while ptr :
294
+ yield ptr
295
+ ptr = ptr [linkfield ]
296
+
297
+
298
+ class GoroutinesCmd (gdb .Command ):
299
+ "List all goroutines."
300
+
301
+ def __init__ (self ):
302
+ super (GoroutinesCmd , self ).__init__ ("info goroutines" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
303
+
304
+ def invoke (self , arg , from_tty ):
305
+ # args = gdb.string_to_argv(arg)
306
+ vp = gdb .lookup_type ('void' ).pointer ()
307
+ for ptr in linked_list (gdb .parse_and_eval ("'runtime.allg'" ), 'alllink' ):
308
+ if ptr ['status' ] == 6 : # 'gdead'
309
+ continue
310
+ m = ptr ['m' ]
311
+ s = ' '
312
+ if m :
313
+ pc = m ['sched' ]['pc' ].cast (vp )
314
+ sp = m ['sched' ]['sp' ].cast (vp )
315
+ s = '*'
316
+ else :
317
+ pc = ptr ['sched' ]['pc' ].cast (vp )
318
+ sp = ptr ['sched' ]['sp' ].cast (vp )
319
+ blk = gdb .block_for_pc (long ((pc )))
320
+ print s , ptr ['goid' ], "%8s" % sts [long ((ptr ['status' ]))], blk .function
321
+
322
+ def find_goroutine (goid ):
323
+ vp = gdb .lookup_type ('void' ).pointer ()
324
+ for ptr in linked_list (gdb .parse_and_eval ("'runtime.allg'" ), 'alllink' ):
325
+ if ptr ['status' ] == 6 : # 'gdead'
326
+ continue
327
+ if ptr ['goid' ] == goid :
328
+ return [(ptr ['m' ] or ptr )['sched' ][x ].cast (vp ) for x in 'pc' , 'sp' ]
329
+ return None , None
330
+
331
+
332
+ class GoroutineCmd (gdb .Command ):
333
+ """Execute gdb command in the context of goroutine <goid>.
334
+
335
+ Switch PC and SP to the ones in the goroutine's G structure,
336
+ execute an arbitrary gdb command, and restore PC and SP.
337
+
338
+ Usage: (gdb) goroutine <goid> <gdbcmd>
339
+
340
+ Note that it is ill-defined to modify state in the context of a goroutine.
341
+ Restrict yourself to inspecting values.
342
+ """
343
+
344
+ def __init__ (self ):
345
+ super (GoroutineCmd , self ).__init__ ("goroutine" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
346
+
347
+ def invoke (self , arg , from_tty ):
348
+ goid , cmd = arg .split (None , 1 )
349
+ pc , sp = find_goroutine (int (goid ))
350
+ if not pc :
351
+ print "No such goroutine: " , goid
352
+ return
353
+ save_frame = gdb .selected_frame ()
354
+ gdb .parse_and_eval ('$save_pc = $pc' )
355
+ gdb .parse_and_eval ('$save_sp = $sp' )
356
+ gdb .parse_and_eval ('$pc = 0x%x' % long (pc ))
357
+ gdb .parse_and_eval ('$sp = 0x%x' % long (sp ))
358
+ try :
359
+ gdb .execute (cmd )
360
+ finally :
361
+ gdb .parse_and_eval ('$pc = $save_pc' )
362
+ gdb .parse_and_eval ('$sp = $save_sp' )
363
+ save_frame .select ()
364
+
365
+
366
+ class GoIfaceCmd (gdb .Command ):
367
+ "Print Static and dynamic interface types"
368
+
369
+ def __init__ (self ):
370
+ super (GoIfaceCmd , self ).__init__ ("iface" , gdb .COMMAND_DATA , gdb .COMPLETE_SYMBOL )
371
+
372
+ def invoke (self , arg , from_tty ):
373
+ for obj in gdb .string_to_argv (arg ):
374
+ try :
375
+ #TODO fix quoting for qualified variable names
376
+ obj = gdb .parse_and_eval ("%s" % obj )
377
+ except Exception , e :
378
+ print "Can't parse " , obj , ": " , e
379
+ continue
380
+
381
+ dtype = iface_dtype (obj )
382
+ if not dtype :
383
+ print "Not an interface: " , obj .type
384
+ continue
385
+
386
+ print "%s: %s" % (obj .type , dtype )
387
+
388
+ # TODO: print interface's methods and dynamic type's func pointers thereof.
389
+ #rsc: "to find the number of entries in the itab's Fn field look at itab.inter->numMethods
390
+ #i am sure i have the names wrong but look at the interface type and its method count"
391
+ # so Itype will start with a commontype which has kind = interface
392
+
169
393
#
170
394
# Register all convience functions and CLI commands
171
395
#
0 commit comments