Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
golang
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
golang
Commits
7a4ce23d
Commit
7a4ce23d
authored
Dec 15, 2010
by
Luuk van Dijk
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[68]l and runtime: GDB support for interfaces and goroutines.
R=rsc CC=golang-dev
https://golang.org/cl/3477041
parent
d96685ed
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
280 additions
and
34 deletions
+280
-34
dwarf.c
src/cmd/ld/dwarf.c
+28
-2
runtime-gdb.py
src/pkg/runtime/runtime-gdb.py
+237
-13
runtime_defs.go
src/pkg/runtime/runtime_defs.go
+15
-19
No files found.
src/cmd/ld/dwarf.c
View file @
7a4ce23d
...
...
@@ -11,6 +11,7 @@
// ptype struct '[]uint8' and qualifiers need to be quoted away
// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean.
// - file:line info for variables
// - make strings a typedef so prettyprinters can see the underlying string type
//
#include "l.h"
#include "lib.h"
...
...
@@ -280,8 +281,9 @@ static struct DWAbbrev {
/* IFACETYPE */
{
DW_TAG_
interface_type
,
DW_CHILDREN_no
,
DW_TAG_
typedef
,
DW_CHILDREN_yes
,
DW_AT_name
,
DW_FORM_string
,
DW_AT_type
,
DW_FORM_ref_addr
,
0
,
0
},
...
...
@@ -957,6 +959,14 @@ decodetype_structfieldoffs(Sym *s, int i)
return
decode_inuxi
(
s
->
p
+
10
*
PtrSize
+
0x10
+
i
*
5
*
PtrSize
,
4
);
// 0x38 / 0x60
}
// InterfaceTYpe.methods.len
static
vlong
decodetype_ifacemethodcount
(
Sym
*
s
)
{
return
decode_inuxi
(
s
->
p
+
6
*
PtrSize
+
8
,
4
);
}
// Fake attributes for slices, maps and channel
enum
{
DW_AT_internal_elem_type
=
250
,
// channels and slices
...
...
@@ -1095,6 +1105,12 @@ defgotype(Sym *gotype)
case
KindInterface
:
die
=
newdie
(
&
dwtypes
,
DW_ABRV_IFACETYPE
,
name
);
newattr
(
die
,
DW_AT_byte_size
,
DW_CLS_CONSTANT
,
bytesize
,
0
);
nfields
=
decodetype_ifacemethodcount
(
gotype
);
if
(
nfields
==
0
)
s
=
lookup
(
"type.runtime.eface"
,
0
);
else
s
=
lookup
(
"type.runtime.iface"
,
0
);
newrefattr
(
die
,
DW_AT_type
,
defgotype
(
s
));
break
;
case
KindMap
:
...
...
@@ -1425,8 +1441,13 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype)
return
;
if
(
strncmp
(
s
,
"string."
,
7
)
==
0
)
return
;
if
(
strncmp
(
s
,
"type."
,
5
)
==
0
)
if
(
strncmp
(
s
,
"type._."
,
7
)
==
0
)
return
;
if
(
strncmp
(
s
,
"type."
,
5
)
==
0
)
{
defgotype
(
sym
);
return
;
}
dv
=
nil
;
...
...
@@ -2291,6 +2312,11 @@ dwarfemitdebugsections(void)
newattr
(
die
,
DW_AT_encoding
,
DW_CLS_CONSTANT
,
DW_ATE_unsigned
,
0
);
newattr
(
die
,
DW_AT_byte_size
,
DW_CLS_CONSTANT
,
PtrSize
,
0
);
// Needed by the prettyprinter code for interface inspection.
defgotype
(
lookup
(
"type.runtime.commonType"
,
0
));
defgotype
(
lookup
(
"type.runtime.InterfaceType"
,
0
));
defgotype
(
lookup
(
"type.runtime.itab"
,
0
));
genasmsym
(
defdwsymb
);
writeabbrev
();
...
...
src/pkg/runtime/runtime-gdb.py
View file @
7a4ce23d
...
...
@@ -9,10 +9,20 @@ section in the compiled binary. The [68]l linkers emit this with a
path to this file based on the path to the runtime package.
"""
# Known issues:
# - pretty printing only works for the 'native' strings. E.g. 'type
# foo string' will make foo a plain struct in the eyes of gdb,
# circumventing the pretty print triggering.
# -
import
sys
,
re
print
>>
sys
.
stderr
,
"Loading Go Runtime support."
# allow to manually reload while developing
goobjfile
=
gdb
.
current_objfile
()
or
gdb
.
objfiles
()[
0
]
goobjfile
.
pretty_printers
=
[]
#
# Pretty Printers
#
...
...
@@ -95,7 +105,7 @@ class MapTypePrinter:
class
ChanTypePrinter
:
"""Pretty print chan[T] types.
Map
-typed go variables are really pointers. dereference them in gdb
Chan
-typed go variables are really pointers. dereference them in gdb
to inspect their contents with this pretty printer.
"""
...
...
@@ -117,18 +127,109 @@ class ChanTypePrinter:
ptr
=
ptr
[
'link'
]
#
# Register all the *Printer classes
# Register all the *Printer classes
above.
#
def
makematcher
(
klass
):
def
matcher
(
val
):
try
:
if
klass
.
pattern
.
match
(
str
(
val
.
type
)):
return
klass
(
val
)
except
:
pass
if
klass
.
pattern
.
match
(
str
(
val
.
type
)):
return
klass
(
val
)
except
:
pass
return
matcher
g
db
.
current_objfile
()
.
pretty_printers
.
extend
([
makematcher
(
k
)
for
k
in
vars
()
.
values
()
if
hasattr
(
k
,
'pattern'
)])
g
oobjfile
.
pretty_printers
.
extend
([
makematcher
(
k
)
for
k
in
vars
()
.
values
()
if
hasattr
(
k
,
'pattern'
)])
#
# For reference, this is what we're trying to do:
# eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string
# iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string
#
# interface types can't be recognized by their name, instead we check
# if they have the expected fields. Unfortunately the mapping of
# fields to python attributes in gdb.py isn't complete: you can't test
# for presence other than by trapping.
def
is_iface
(
val
):
try
:
return
str
(
val
[
'tab'
]
.
type
)
==
"struct runtime.itab *"
\
and
str
(
val
[
'data'
]
.
type
)
==
"void *"
except
:
pass
def
is_eface
(
val
):
try
:
return
str
(
val
[
'type_'
]
.
type
)
==
"runtime.Type *"
\
and
str
(
val
[
'data'
]
.
type
)
==
"void *"
except
:
pass
def
lookup_type
(
name
):
try
:
return
gdb
.
lookup_type
(
name
)
except
:
pass
try
:
return
gdb
.
lookup_type
(
'struct '
+
name
)
except
:
pass
try
:
return
gdb
.
lookup_type
(
'struct '
+
name
[
1
:])
.
pointer
()
except
:
pass
def
iface_dtype
(
obj
):
"Decode type of the data field of an eface or iface struct."
if
is_iface
(
obj
):
go_type_ptr
=
obj
[
'tab'
][
'Type'
]
elif
is_eface
(
obj
):
go_type_ptr
=
obj
[
'type_'
]
else
:
return
ct
=
gdb
.
lookup_type
(
"struct runtime.commonType"
)
.
pointer
()
dynamic_go_type
=
go_type_ptr
[
'data'
]
.
cast
(
ct
)
.
dereference
()
dtype_name
=
dynamic_go_type
[
'string'
]
.
dereference
()[
'str'
]
.
string
()
type_size
=
int
(
dynamic_go_type
[
'size'
])
uintptr_size
=
int
(
dynamic_go_type
[
'size'
]
.
type
.
sizeof
)
# size is itself an uintptr
dynamic_gdb_type
=
lookup_type
(
dtype_name
)
if
type_size
>
uintptr_size
:
dynamic_gdb_type
=
dynamic_gdb_type
.
pointer
()
return
dynamic_gdb_type
class
IfacePrinter
:
"""Pretty print interface values
Casts the data field to the appropriate dynamic type."""
def
__init__
(
self
,
val
):
self
.
val
=
val
def
display_hint
(
self
):
return
'string'
def
to_string
(
self
):
try
:
dtype
=
iface_dtype
(
self
.
val
)
except
:
return
"<bad dynamic type>"
try
:
return
self
.
val
[
'data'
]
.
cast
(
dtype
)
.
dereference
()
except
:
pass
return
self
.
val
[
'data'
]
.
cast
(
dtype
)
def
ifacematcher
(
val
):
if
is_iface
(
val
)
or
is_eface
(
val
):
return
IfacePrinter
(
val
)
goobjfile
.
pretty_printers
.
append
(
ifacematcher
)
#
# Convenience Functions
...
...
@@ -137,35 +238,158 @@ gdb.current_objfile().pretty_printers.extend([makematcher(k) for k in vars().val
class
GoLenFunc
(
gdb
.
Function
):
"Length of strings, slices, maps or channels"
how
=
((
StringTypePrinter
,
'len'
),
(
SliceTypePrinter
,
'len'
),
(
MapTypePrinter
,
'count'
),
(
ChanTypePrinter
,
'qcount'
))
how
=
((
StringTypePrinter
,
'len'
),
(
SliceTypePrinter
,
'len'
),
(
MapTypePrinter
,
'count'
),
(
ChanTypePrinter
,
'qcount'
))
def
__init__
(
self
):
super
(
GoLenFunc
,
self
)
.
__init__
(
"len"
)
def
invoke
(
self
,
obj
):
typename
=
str
(
obj
.
type
)
for
klass
,
fld
in
self
.
how
:
for
klass
,
fld
in
self
.
how
:
if
klass
.
pattern
.
match
(
typename
):
return
obj
[
fld
]
class
GoCapFunc
(
gdb
.
Function
):
"Capacity of slices or channels"
how
=
((
SliceTypePrinter
,
'cap'
),
(
ChanTypePrinter
,
'dataqsiz'
))
how
=
((
SliceTypePrinter
,
'cap'
),
(
ChanTypePrinter
,
'dataqsiz'
))
def
__init__
(
self
):
super
(
GoCapFunc
,
self
)
.
__init__
(
"cap"
)
def
invoke
(
self
,
obj
):
typename
=
str
(
obj
.
type
)
for
klass
,
fld
in
self
.
how
:
for
klass
,
fld
in
self
.
how
:
if
klass
.
pattern
.
match
(
typename
):
return
obj
[
fld
]
class
DTypeFunc
(
gdb
.
Function
):
"""Cast Interface values to their dynamic type.
For non-interface types this behaves as the identity operation.
"""
def
__init__
(
self
):
super
(
DTypeFunc
,
self
)
.
__init__
(
"dtype"
)
def
invoke
(
self
,
obj
):
try
:
return
obj
[
'data'
]
.
cast
(
iface_dtype
(
obj
))
except
:
pass
return
obj
#
# Commands
#
sts
=
(
'idle'
,
'runnable'
,
'running'
,
'syscall'
,
'waiting'
,
'moribund'
,
'dead'
,
'recovery'
)
def
linked_list
(
ptr
,
linkfield
):
while
ptr
:
yield
ptr
ptr
=
ptr
[
linkfield
]
class
GoroutinesCmd
(
gdb
.
Command
):
"List all goroutines."
def
__init__
(
self
):
super
(
GoroutinesCmd
,
self
)
.
__init__
(
"info goroutines"
,
gdb
.
COMMAND_STACK
,
gdb
.
COMPLETE_NONE
)
def
invoke
(
self
,
arg
,
from_tty
):
# args = gdb.string_to_argv(arg)
vp
=
gdb
.
lookup_type
(
'void'
)
.
pointer
()
for
ptr
in
linked_list
(
gdb
.
parse_and_eval
(
"'runtime.allg'"
),
'alllink'
):
if
ptr
[
'status'
]
==
6
:
# 'gdead'
continue
m
=
ptr
[
'm'
]
s
=
' '
if
m
:
pc
=
m
[
'sched'
][
'pc'
]
.
cast
(
vp
)
sp
=
m
[
'sched'
][
'sp'
]
.
cast
(
vp
)
s
=
'*'
else
:
pc
=
ptr
[
'sched'
][
'pc'
]
.
cast
(
vp
)
sp
=
ptr
[
'sched'
][
'sp'
]
.
cast
(
vp
)
blk
=
gdb
.
block_for_pc
(
long
((
pc
)))
print
s
,
ptr
[
'goid'
],
"
%8
s"
%
sts
[
long
((
ptr
[
'status'
]))],
blk
.
function
def
find_goroutine
(
goid
):
vp
=
gdb
.
lookup_type
(
'void'
)
.
pointer
()
for
ptr
in
linked_list
(
gdb
.
parse_and_eval
(
"'runtime.allg'"
),
'alllink'
):
if
ptr
[
'status'
]
==
6
:
# 'gdead'
continue
if
ptr
[
'goid'
]
==
goid
:
return
[(
ptr
[
'm'
]
or
ptr
)[
'sched'
][
x
]
.
cast
(
vp
)
for
x
in
'pc'
,
'sp'
]
return
None
,
None
class
GoroutineCmd
(
gdb
.
Command
):
"""Execute gdb command in the context of goroutine <goid>.
Switch PC and SP to the ones in the goroutine's G structure,
execute an arbitrary gdb command, and restore PC and SP.
Usage: (gdb) goroutine <goid> <gdbcmd>
Note that it is ill-defined to modify state in the context of a goroutine.
Restrict yourself to inspecting values.
"""
def
__init__
(
self
):
super
(
GoroutineCmd
,
self
)
.
__init__
(
"goroutine"
,
gdb
.
COMMAND_STACK
,
gdb
.
COMPLETE_NONE
)
def
invoke
(
self
,
arg
,
from_tty
):
goid
,
cmd
=
arg
.
split
(
None
,
1
)
pc
,
sp
=
find_goroutine
(
int
(
goid
))
if
not
pc
:
print
"No such goroutine: "
,
goid
return
save_frame
=
gdb
.
selected_frame
()
gdb
.
parse_and_eval
(
'$save_pc = $pc'
)
gdb
.
parse_and_eval
(
'$save_sp = $sp'
)
gdb
.
parse_and_eval
(
'$pc = 0x
%
x'
%
long
(
pc
))
gdb
.
parse_and_eval
(
'$sp = 0x
%
x'
%
long
(
sp
))
try
:
gdb
.
execute
(
cmd
)
finally
:
gdb
.
parse_and_eval
(
'$pc = $save_pc'
)
gdb
.
parse_and_eval
(
'$sp = $save_sp'
)
save_frame
.
select
()
class
GoIfaceCmd
(
gdb
.
Command
):
"Print Static and dynamic interface types"
def
__init__
(
self
):
super
(
GoIfaceCmd
,
self
)
.
__init__
(
"iface"
,
gdb
.
COMMAND_DATA
,
gdb
.
COMPLETE_SYMBOL
)
def
invoke
(
self
,
arg
,
from_tty
):
for
obj
in
gdb
.
string_to_argv
(
arg
):
try
:
#TODO fix quoting for qualified variable names
obj
=
gdb
.
parse_and_eval
(
"
%
s"
%
obj
)
except
Exception
,
e
:
print
"Can't parse "
,
obj
,
": "
,
e
continue
dtype
=
iface_dtype
(
obj
)
if
not
dtype
:
print
"Not an interface: "
,
obj
.
type
continue
print
"
%
s:
%
s"
%
(
obj
.
type
,
dtype
)
# TODO: print interface's methods and dynamic type's func pointers thereof.
#rsc: "to find the number of entries in the itab's Fn field look at itab.inter->numMethods
#i am sure i have the names wrong but look at the interface type and its method count"
# so Itype will start with a commontype which has kind = interface
#
# Register all convience functions and CLI commands
#
...
...
src/pkg/runtime/runtime_defs.go
View file @
7a4ce23d
...
...
@@ -55,19 +55,19 @@ type slice struct {
}
type
gobuf
struct
{
sp
*
byte
pc
*
byte
sp
unsafe
.
Pointer
pc
unsafe
.
Pointer
g
*
g_
}
type
g_
struct
{
stackguard
*
byte
stackbase
*
byte
stackguard
unsafe
.
Pointer
stackbase
unsafe
.
Pointer
defer_
*
defer_
panic_
*
panic_
sched
gobuf
stack0
*
byte
entry
*
byte
stack0
unsafe
.
Pointer
entry
unsafe
.
Pointer
alllink
*
g_
param
unsafe
.
Pointer
status
int16
...
...
@@ -168,31 +168,29 @@ const (
type
defer_
struct
{
siz
int32
sp
*
byte
pc
*
byte
fn
*
byte
sp
unsafe
.
Pointer
pc
unsafe
.
Pointer
fn
unsafe
.
Pointer
link
*
defer_
args
[
8
]
byte
// padded to actual size
}
type
panic_
struct
{
arg
eface
stackbase
*
byte
stackbase
unsafe
.
Pointer
link
*
panic_
recovered
bool
}
/*
* external data
// extern register G* g;
// extern register M* m;
* External data.
*/
var
(
algarray [amax]
A
lg
emptystring
S
tring
algarray
[
amax
]
a
lg
emptystring
s
tring
allg
*
g_
allm *
M
allm
*
m_
goidgen
int32
gomaxprocs
int32
panicking
int32
...
...
@@ -200,5 +198,3 @@ var (
gcwaiting
int32
goos
*
int8
)
*/
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment