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