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 | |
Hana Kim | ef17573 | 2018-01-16 15:21:34 -0500 | [diff] [blame] | 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 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 72 | # |
Austin Clements | 5456868 | 2015-02-16 21:56:10 -0500 | [diff] [blame] | 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 | # |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 98 | # Pretty Printers |
| 99 | # |
| 100 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 101 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 102 | class StringTypePrinter: |
| 103 | "Pretty print Go strings." |
| 104 | |
Lee Packham | c45751e | 2015-03-30 17:36:49 +0100 | [diff] [blame] | 105 | pattern = re.compile(r'^struct string( \*)?$') |
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 'string' |
| 112 | |
| 113 | def to_string(self): |
Luuk van Dijk | db22e23 | 2011-02-20 18:53:23 +0100 | [diff] [blame] | 114 | l = int(self.val['len']) |
| 115 | return self.val['str'].string("utf-8", "ignore", l) |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 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): |
Austin Clements | 98651d6 | 2015-02-16 22:04:24 -0500 | [diff] [blame] | 133 | sval = SliceValue(self.val) |
| 134 | if sval.len > sval.cap: |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 135 | return |
Austin Clements | 98651d6 | 2015-02-16 22:04:24 -0500 | [diff] [blame] | 136 | for idx, item in enumerate(sval): |
| 137 | yield ('[{0}]'.format(idx), item) |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 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 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 147 | pattern = re.compile(r'^map\[.*\].*$') |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 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): |
Jan Kratochvil | 1c82e23 | 2015-02-21 18:18:33 +0100 | [diff] [blame] | 159 | B = self.val['B'] |
Keith Randall | fb3ed16 | 2013-03-29 11:04:07 -0700 | [diff] [blame] | 160 | buckets = self.val['buckets'] |
| 161 | oldbuckets = self.val['oldbuckets'] |
| 162 | flags = self.val['flags'] |
| 163 | inttype = self.val['hash0'].type |
| 164 | cnt = 0 |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 165 | for bucket in xrange(2 ** int(B)): |
Keith Randall | fb3ed16 | 2013-03-29 11:04:07 -0700 | [diff] [blame] | 166 | bp = buckets + bucket |
| 167 | if oldbuckets: |
| 168 | oldbucket = bucket & (2 ** (B - 1) - 1) |
| 169 | oldbp = oldbuckets + oldbucket |
| 170 | oldb = oldbp.dereference() |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 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 |
Keith Randall | fb3ed16 | 2013-03-29 11:04:07 -0700 | [diff] [blame] | 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() |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 185 | yield str(cnt), k |
| 186 | yield str(cnt + 1), v |
Keith Randall | fb3ed16 | 2013-03-29 11:04:07 -0700 | [diff] [blame] | 187 | cnt += 2 |
| 188 | bp = b['overflow'] |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 189 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 190 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 191 | class ChanTypePrinter: |
| 192 | """Pretty print chan[T] types. |
| 193 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 194 | Chan-typed go variables are really pointers. dereference them in gdb |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 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): |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 210 | # 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] | 211 | 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] | 212 | ptr = (self.val.address + 1).cast(et.pointer()) |
| 213 | for i in range(self.val["qcount"]): |
Luuk van Dijk | dd93df3 | 2011-04-14 15:32:20 +0200 | [diff] [blame] | 214 | j = (self.val["recvx"] + i) % self.val["dataqsiz"] |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 215 | yield ('[{0}]'.format(i), (ptr + j).dereference()) |
Luuk van Dijk | dd93df3 | 2011-04-14 15:32:20 +0200 | [diff] [blame] | 216 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 217 | |
| 218 | # |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 219 | # Register all the *Printer classes above. |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 220 | # |
| 221 | |
| 222 | def makematcher(klass): |
| 223 | def matcher(val): |
| 224 | try: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 225 | if klass.pattern.match(str(val.type)): |
| 226 | return klass(val) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 227 | except Exception: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 228 | pass |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 229 | return matcher |
| 230 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 231 | 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] | 232 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 233 | # |
| 234 | # For reference, this is what we're trying to do: |
Russ Cox | 1120982 | 2012-11-13 13:06:29 -0500 | [diff] [blame] | 235 | # eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string |
| 236 | # iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 237 | # |
| 238 | # interface types can't be recognized by their name, instead we check |
| 239 | # if they have the expected fields. Unfortunately the mapping of |
| 240 | # fields to python attributes in gdb.py isn't complete: you can't test |
| 241 | # for presence other than by trapping. |
| 242 | |
| 243 | |
| 244 | def is_iface(val): |
| 245 | try: |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 246 | return str(val['tab'].type) == "struct runtime.itab *" and str(val['data'].type) == "void *" |
| 247 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 248 | pass |
| 249 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 250 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 251 | def is_eface(val): |
| 252 | try: |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 253 | return str(val['_type'].type) == "struct runtime._type *" and str(val['data'].type) == "void *" |
| 254 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 255 | pass |
| 256 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 257 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 258 | def lookup_type(name): |
| 259 | try: |
| 260 | return gdb.lookup_type(name) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 261 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 262 | pass |
| 263 | try: |
| 264 | return gdb.lookup_type('struct ' + name) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 265 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 266 | pass |
| 267 | try: |
| 268 | return gdb.lookup_type('struct ' + name[1:]).pointer() |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 269 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 270 | pass |
| 271 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 272 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 273 | def iface_commontype(obj): |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 274 | if is_iface(obj): |
Luuk van Dijk | 7400be8 | 2011-01-31 12:27:28 +0100 | [diff] [blame] | 275 | go_type_ptr = obj['tab']['_type'] |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 276 | elif is_eface(obj): |
Luuk van Dijk | 7400be8 | 2011-01-31 12:27:28 +0100 | [diff] [blame] | 277 | go_type_ptr = obj['_type'] |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 278 | else: |
| 279 | return |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 280 | |
Jan Kratochvil | 02d80b9 | 2015-02-21 17:35:01 +0100 | [diff] [blame] | 281 | 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] | 282 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 283 | |
| 284 | def iface_dtype(obj): |
| 285 | "Decode type of the data field of an eface or iface struct." |
Russ Cox | 1120982 | 2012-11-13 13:06:29 -0500 | [diff] [blame] | 286 | # known issue: dtype_name decoded from runtime.rtype is "nested.Foo" |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 287 | # but the dwarf table lists it as "full/path/to/nested.Foo" |
| 288 | |
| 289 | dynamic_go_type = iface_commontype(obj) |
| 290 | if dynamic_go_type is None: |
| 291 | return |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 292 | dtype_name = dynamic_go_type['string'].dereference()['str'].string() |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 293 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 294 | dynamic_gdb_type = lookup_type(dtype_name) |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 295 | if dynamic_gdb_type is None: |
| 296 | return |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 297 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 298 | type_size = int(dynamic_go_type['size']) |
| 299 | uintptr_size = int(dynamic_go_type['size'].type.sizeof) # size is itself an uintptr |
| 300 | if type_size > uintptr_size: |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 301 | dynamic_gdb_type = dynamic_gdb_type.pointer() |
| 302 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 303 | return dynamic_gdb_type |
| 304 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 305 | |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 306 | def iface_dtype_name(obj): |
| 307 | "Decode type name of the data field of an eface or iface struct." |
| 308 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 309 | dynamic_go_type = iface_commontype(obj) |
| 310 | if dynamic_go_type is None: |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 311 | return |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 312 | return dynamic_go_type['string'].dereference()['str'].string() |
| 313 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 314 | |
| 315 | class IfacePrinter: |
| 316 | """Pretty print interface values |
| 317 | |
| 318 | Casts the data field to the appropriate dynamic type.""" |
| 319 | |
| 320 | def __init__(self, val): |
| 321 | self.val = val |
| 322 | |
| 323 | def display_hint(self): |
| 324 | return 'string' |
| 325 | |
| 326 | def to_string(self): |
Luuk van Dijk | 43512e6 | 2011-03-28 17:34:22 +0200 | [diff] [blame] | 327 | if self.val['data'] == 0: |
| 328 | return 0x0 |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 329 | try: |
| 330 | dtype = iface_dtype(self.val) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 331 | except Exception: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 332 | return "<bad dynamic type>" |
Luuk van Dijk | 46ed89b | 2011-09-23 10:28:02 +0200 | [diff] [blame] | 333 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 334 | if dtype is None: # trouble looking up, print something reasonable |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 335 | 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] | 336 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 337 | try: |
| 338 | return self.val['data'].cast(dtype).dereference() |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 339 | except Exception: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 340 | pass |
| 341 | return self.val['data'].cast(dtype) |
| 342 | |
| 343 | |
| 344 | def ifacematcher(val): |
| 345 | if is_iface(val) or is_eface(val): |
| 346 | return IfacePrinter(val) |
| 347 | |
| 348 | goobjfile.pretty_printers.append(ifacematcher) |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 349 | |
| 350 | # |
| 351 | # Convenience Functions |
| 352 | # |
| 353 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 354 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 355 | class GoLenFunc(gdb.Function): |
| 356 | "Length of strings, slices, maps or channels" |
| 357 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 358 | how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount')) |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 359 | |
| 360 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 361 | gdb.Function.__init__(self, "len") |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 362 | |
| 363 | def invoke(self, obj): |
| 364 | typename = str(obj.type) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 365 | for klass, fld in self.how: |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 366 | if klass.pattern.match(typename): |
| 367 | return obj[fld] |
| 368 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 369 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 370 | class GoCapFunc(gdb.Function): |
| 371 | "Capacity of slices or channels" |
| 372 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 373 | how = ((SliceTypePrinter, 'cap'), (ChanTypePrinter, 'dataqsiz')) |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 374 | |
| 375 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 376 | gdb.Function.__init__(self, "cap") |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 377 | |
| 378 | def invoke(self, obj): |
| 379 | typename = str(obj.type) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 380 | for klass, fld in self.how: |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 381 | if klass.pattern.match(typename): |
| 382 | return obj[fld] |
| 383 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 384 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 385 | class DTypeFunc(gdb.Function): |
| 386 | """Cast Interface values to their dynamic type. |
| 387 | |
| 388 | For non-interface types this behaves as the identity operation. |
| 389 | """ |
| 390 | |
| 391 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 392 | gdb.Function.__init__(self, "dtype") |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 393 | |
| 394 | def invoke(self, obj): |
| 395 | try: |
| 396 | return obj['data'].cast(iface_dtype(obj)) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 397 | except gdb.error: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 398 | pass |
| 399 | return obj |
| 400 | |
| 401 | # |
| 402 | # Commands |
| 403 | # |
| 404 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 405 | def linked_list(ptr, linkfield): |
| 406 | while ptr: |
| 407 | yield ptr |
| 408 | ptr = ptr[linkfield] |
| 409 | |
| 410 | |
| 411 | class GoroutinesCmd(gdb.Command): |
| 412 | "List all goroutines." |
| 413 | |
| 414 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 415 | 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] | 416 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 417 | def invoke(self, _arg, _from_tty): |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 418 | # args = gdb.string_to_argv(arg) |
| 419 | vp = gdb.lookup_type('void').pointer() |
Austin Clements | 5456868 | 2015-02-16 21:56:10 -0500 | [diff] [blame] | 420 | for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): |
Hana Kim | ef17573 | 2018-01-16 15:21:34 -0500 | [diff] [blame] | 421 | if ptr['atomicstatus'] == G_DEAD: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 422 | continue |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 423 | s = ' ' |
Luuk van Dijk | 43512e6 | 2011-03-28 17:34:22 +0200 | [diff] [blame] | 424 | if ptr['m']: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 425 | s = '*' |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 426 | pc = ptr['sched']['pc'].cast(vp) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 427 | # python2 will not cast pc (type void*) to an int cleanly |
| 428 | # instead python2 and python3 work with the hex string representation |
| 429 | # of the void pointer which we can parse back into an int. |
| 430 | # int(pc) will not work. |
| 431 | try: |
| 432 | #python3 / newer versions of gdb |
| 433 | pc = int(pc) |
| 434 | except gdb.error: |
Austin Clements | 5456868 | 2015-02-16 21:56:10 -0500 | [diff] [blame] | 435 | # str(pc) can return things like |
| 436 | # "0x429d6c <runtime.gopark+284>", so |
| 437 | # chop at first space. |
| 438 | pc = int(str(pc).split(None, 1)[0], 16) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 439 | blk = gdb.block_for_pc(pc) |
Hana Kim | ef17573 | 2018-01-16 15:21:34 -0500 | [diff] [blame] | 440 | status = int(ptr['atomicstatus']) |
| 441 | st = sts.get(status, "unknown(%d)" % status) |
| 442 | print(s, ptr['goid'], "{0:8s}".format(st), blk.function) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 443 | |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 444 | |
| 445 | def find_goroutine(goid): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 446 | """ |
| 447 | find_goroutine attempts to find the goroutine identified by goid. |
Shenghou Ma | ca6be91 | 2014-05-20 14:42:07 -0400 | [diff] [blame] | 448 | It returns a touple of gdv.Value's representing the stack pointer |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 449 | and program counter pointer for the goroutine. |
| 450 | |
| 451 | @param int goid |
| 452 | |
| 453 | @return tuple (gdb.Value, gdb.Value) |
| 454 | """ |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 455 | vp = gdb.lookup_type('void').pointer() |
Austin Clements | 5456868 | 2015-02-16 21:56:10 -0500 | [diff] [blame] | 456 | for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): |
Hana Kim | ef17573 | 2018-01-16 15:21:34 -0500 | [diff] [blame] | 457 | if ptr['atomicstatus'] == G_DEAD: |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 458 | continue |
| 459 | if ptr['goid'] == goid: |
Austin Clements | 2465971 | 2017-06-07 10:30:49 -0400 | [diff] [blame] | 460 | break |
| 461 | else: |
| 462 | return None, None |
| 463 | # Get the goroutine's saved state. |
| 464 | pc, sp = ptr['sched']['pc'], ptr['sched']['sp'] |
Hana Kim | dc3bef3 | 2018-01-16 15:31:12 -0500 | [diff] [blame] | 465 | status = ptr['atomicstatus']&~G_SCAN |
| 466 | # Goroutine is not running nor in syscall, so use the info in goroutine |
| 467 | if status != G_RUNNING and status != G_SYSCALL: |
Austin Clements | 2465971 | 2017-06-07 10:30:49 -0400 | [diff] [blame] | 468 | return pc.cast(vp), sp.cast(vp) |
Hana Kim | dc3bef3 | 2018-01-16 15:31:12 -0500 | [diff] [blame] | 469 | |
Austin Clements | 2465971 | 2017-06-07 10:30:49 -0400 | [diff] [blame] | 470 | # If the goroutine is in a syscall, use syscallpc/sp. |
| 471 | pc, sp = ptr['syscallpc'], ptr['syscallsp'] |
| 472 | if sp != 0: |
| 473 | return pc.cast(vp), sp.cast(vp) |
| 474 | # Otherwise, the goroutine is running, so it doesn't have |
| 475 | # saved scheduler state. Find G's OS thread. |
| 476 | m = ptr['m'] |
| 477 | if m == 0: |
| 478 | return None, None |
| 479 | for thr in gdb.selected_inferior().threads(): |
| 480 | if thr.ptid[1] == m['procid']: |
| 481 | break |
Austin Clements | a7d7d7a | 2017-06-08 12:05:31 -0400 | [diff] [blame] | 482 | else: |
Austin Clements | 2465971 | 2017-06-07 10:30:49 -0400 | [diff] [blame] | 483 | return None, None |
| 484 | # Get scheduler state from the G's OS thread state. |
| 485 | curthr = gdb.selected_thread() |
| 486 | try: |
| 487 | thr.switch() |
| 488 | pc = gdb.parse_and_eval('$pc') |
| 489 | sp = gdb.parse_and_eval('$sp') |
| 490 | finally: |
| 491 | curthr.switch() |
| 492 | return pc.cast(vp), sp.cast(vp) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 493 | |
| 494 | |
| 495 | class GoroutineCmd(gdb.Command): |
| 496 | """Execute gdb command in the context of goroutine <goid>. |
| 497 | |
| 498 | Switch PC and SP to the ones in the goroutine's G structure, |
| 499 | execute an arbitrary gdb command, and restore PC and SP. |
| 500 | |
| 501 | Usage: (gdb) goroutine <goid> <gdbcmd> |
| 502 | |
| 503 | Note that it is ill-defined to modify state in the context of a goroutine. |
| 504 | Restrict yourself to inspecting values. |
| 505 | """ |
| 506 | |
| 507 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 508 | gdb.Command.__init__(self, "goroutine", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 509 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 510 | def invoke(self, arg, _from_tty): |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 511 | goid, cmd = arg.split(None, 1) |
Christian Himpel | ca8aac6 | 2012-11-19 10:22:47 -0800 | [diff] [blame] | 512 | goid = gdb.parse_and_eval(goid) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 513 | pc, sp = find_goroutine(int(goid)) |
| 514 | if not pc: |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 515 | print("No such goroutine: ", goid) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 516 | return |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 517 | try: |
| 518 | #python3 / newer versions of gdb |
| 519 | pc = int(pc) |
| 520 | except gdb.error: |
Derek Buitenhuis | 53840ad | 2015-04-10 15:13:04 -0400 | [diff] [blame] | 521 | pc = int(str(pc).split(None, 1)[0], 16) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 522 | save_frame = gdb.selected_frame() |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 523 | gdb.parse_and_eval('$save_sp = $sp') |
Cherry Zhang | cbc2686 | 2016-06-08 22:22:35 -0400 | [diff] [blame] | 524 | gdb.parse_and_eval('$save_pc = $pc') |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 525 | gdb.parse_and_eval('$sp = {0}'.format(str(sp))) |
Cherry Zhang | cbc2686 | 2016-06-08 22:22:35 -0400 | [diff] [blame] | 526 | gdb.parse_and_eval('$pc = {0}'.format(str(pc))) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 527 | try: |
| 528 | gdb.execute(cmd) |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 529 | finally: |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 530 | gdb.parse_and_eval('$sp = $save_sp') |
Cherry Zhang | cbc2686 | 2016-06-08 22:22:35 -0400 | [diff] [blame] | 531 | gdb.parse_and_eval('$pc = $save_pc') |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 532 | save_frame.select() |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 533 | |
| 534 | |
| 535 | class GoIfaceCmd(gdb.Command): |
| 536 | "Print Static and dynamic interface types" |
| 537 | |
| 538 | def __init__(self): |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 539 | gdb.Command.__init__(self, "iface", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 540 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 541 | def invoke(self, arg, _from_tty): |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 542 | for obj in gdb.string_to_argv(arg): |
| 543 | try: |
| 544 | #TODO fix quoting for qualified variable names |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 545 | obj = gdb.parse_and_eval(str(obj)) |
| 546 | except Exception as e: |
| 547 | print("Can't parse ", obj, ": ", e) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 548 | continue |
| 549 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 550 | if obj['data'] == 0: |
| 551 | dtype = "nil" |
| 552 | else: |
| 553 | dtype = iface_dtype(obj) |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 554 | |
Luuk van Dijk | fb27061 | 2012-02-29 16:42:25 +0100 | [diff] [blame] | 555 | if dtype is None: |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 556 | print("Not an interface: ", obj.type) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 557 | continue |
| 558 | |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 559 | print("{0}: {1}".format(obj.type, dtype)) |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 560 | |
| 561 | # TODO: print interface's methods and dynamic type's func pointers thereof. |
Shane Hansen | f12a167 | 2014-02-24 10:13:27 -0500 | [diff] [blame] | 562 | #rsc: "to find the number of entries in the itab's Fn field look at |
| 563 | # itab.inter->numMethods |
| 564 | # i am sure i have the names wrong but look at the interface type |
| 565 | # and its method count" |
Luuk van Dijk | 7a4ce23 | 2010-12-15 12:00:43 +0100 | [diff] [blame] | 566 | # so Itype will start with a commontype which has kind = interface |
| 567 | |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 568 | # |
Robert Hencke | 3fbd478 | 2011-05-30 18:02:59 +1000 | [diff] [blame] | 569 | # Register all convenience functions and CLI commands |
Luuk van Dijk | 9a71bb0 | 2010-12-03 19:19:33 +0100 | [diff] [blame] | 570 | # |
Alexis Imperial-Legrand | 927b7ac | 2013-09-10 13:00:08 -0400 | [diff] [blame] | 571 | GoLenFunc() |
| 572 | GoCapFunc() |
| 573 | DTypeFunc() |
| 574 | GoroutinesCmd() |
| 575 | GoroutineCmd() |
| 576 | GoIfaceCmd() |