delve
delve copied to clipboard
Allow users to list methods of struct/interface
It would be nice for developers to list all methods of a struct/interface in addition to its fields in dlv like python dir(object)
.
PS: though this expr doc says that dlv shows the concrete type of a interface, I also want to list all methods of a interface.
Additional information:
- What version of Delve are you using (
dlv version
)?
Delve Debugger
Version: 1.5.0
In the meantime, the funcs
command lists all methods on types.
(dlv) funcs sync.atomic\.\(\*Value\)
sync/atomic.(*Value).Load
sync/atomic.(*Value).Store
What about a doc
command which shows the godoc for the values type?
I'm also interested. And I have found myself wanting this feature on more than one occasion.
Specifically:
Proposal:
Add ability to list the public API of any variable/type and also the public api of any import.
This will involve adding a new command(lets call it ptype
for proposal purposes although I would be happy for any other name.)
Problem statement:
Whenever you are debugging using delve you want to know a number of things; (a) What variables are available? (b) What are the types of these variables? (c) What are the values of these variables? (d) What can I do with these variables(How can I use them)? You ideally want to answer these questions without leaving the comfort of your debugging session/terminal.
For (a) we have locals
, args
, funcs
and vars
For (b) we have print
For (c) we have whatis
and types
For (d) however, delve is a bit lacking. When you have a variable/type and you want to know how you can use it; there is no delve command to do that. You have to use whatis
to get the type AND then lookup that type either in godoc
or https://pkg.go.dev/
or some other place to find the public API. This process involves leaving the debug session to go and use another tool; I think we can do better.
Usage:
Let's say you have a Go file like
package main
import (
"compress/flate"
"net/http"
"time"
)
func main() {
_ = flate.BestSpeed
tomorrow := time.Hour * 24
req, _ := http.NewRequest("GET", "https://google.com", nil)
}
You can then use the ptype
delve command while debugging the above code like this
(dlv) ptype tomorrow
(dlv) ptype req
(dlv) ptype "compress/flate"
And when you do so, the public API of the requested variables/types or package import will be printed.
(dlv) ptype tomorrow
[
NAME: time.Duration
KIND: int64
SIGNATURE: [time.Duration]
FIELDS: []
METHODS: [
Hours func(time.Duration) float64
Microseconds func(time.Duration) int64
Milliseconds func(time.Duration) int64
Minutes func(time.Duration) float64
Nanoseconds func(time.Duration) int64
Round func(time.Duration, time.Duration) time.Duration
Seconds func(time.Duration) float64
String func(time.Duration) string
Truncate func(time.Duration, time.Duration) time.Duration
]
]
(dlv) ptype req
[
NAME: net/http.Request
KIND: struct
SIGNATURE: [*http.Request http.Request]
FIELDS: [
Method string
URL *url.URL
Proto string
ProtoMajor int
ProtoMinor int
... <truncated> ...
]
METHODS: [
AddCookie func(*http.Request, *http.Cookie)
BasicAuth func(*http.Request) (string, string, bool)
Clone func(*http.Request, context.Context) *http.Request
... <truncated> ...
]
]
(dlv) ptype "compress/flate"
[
NAME: compress/flate
CONSTANTS: [
BestCompression untyped int
BestSpeed untyped int
... <truncated> ...
]
VARIABLES: []
FUNCTIONS: [
NewReader(r io.Reader) io.ReadCloser
NewReaderDict(r io.Reader, dict []byte) io.ReadCloser
... <truncated> ...
]
TYPES: [
... <truncated> ...
Writer struct
(*Writer) Close() error
(*Writer) Flush() error
(*Writer) Reset(dst io.Writer)
(*Writer) Write(data []byte) (n int, err error)
CorruptInputError int64
(CorruptInputError) Error() string]
]
Precedent:
The gdb
debugger has a ptype
command. See; https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_109.html
Which works like;
struct complex {double real; double imag;} v;
(gdb) whatis v
type = struct complex
(gdb) ptype v
type = struct complex {
double real;
double imag;
}
Implementation:
The implementation would use the reflect
package with no third party dependency.
If this proposal were to be accepted, I'm willing to do most of the work to get this in. I already have a package that uses reflect
to print out public API of variables/types/imports; see https://github.com/komuw/kama#usage
The implementation in delve would not use that package but would instead only rely on reflect
.
This proposal can be broken into two parts:
(i) Add ability to list the public API of any variable/type.
(ii) Add ability to list the public api of any import.
I feel certain that we should do (i) but I'm not certain about (ii). The two parts can be accepted/rejected independently.
I can avail any other information required to help people be able to evaluate this proposal.
Thanks,
I don't know whether to make my proposal an independent issue?
Also a bit related to: https://github.com/go-delve/delve/issues/55
@komuw perhaps the new functionality could be a flag passed to whatis
struct complex {double real; double imag;} v;
(gdb) whatis v
type = struct complex
(gdb) whatis -verbose v
type = struct complex {
double real;
double imag;
}
@icholy I would also be fine with that idea.
I'm also interested. And I have found myself wanting this feature on more than one occasion.
Specifically:
Proposal:
Add ability to list the public API of any variable/type and also the public api of any import. This will involve adding a new command(lets call it
ptype
for proposal purposes although I would be happy for any other name.)Problem statement:
Whenever you are debugging using delve you want to know a number of things; (a) What variables are available? (b) What are the types of these variables? (c) What are the values of these variables? (d) What can I do with these variables(How can I use them)? You ideally want to answer these questions without leaving the comfort of your debugging session/terminal.
For (a) we have
locals
,args
,funcs
andvars
For (b) we havewhatis
andtypes
For (d) however, delve is a bit lacking. When you have a variable/type and you want to know how you can use it; there is no delve command to do that. You have to usewhatis
to get the type AND then lookup that type either ingodoc
orhttps://pkg.go.dev/
or some other place to find the public API. This process involves leaving the debug session to go and use another tool; I think we can do better.Usage:
Let's say you have a Go file like
package main import ( "compress/flate" "net/http" "time" ) func main() { _ = flate.BestSpeed tomorrow := time.Hour * 24 req, _ := http.NewRequest("GET", "https://google.com", nil) }
You can then use the
ptype
delve command while debugging the above code like this(dlv) ptype tomorrow (dlv) ptype req (dlv) ptype "compress/flate"
And when you do so, the public API of the requested variables/types or package import will be printed.
(dlv) ptype tomorrow [ NAME: time.Duration KIND: int64 SIGNATURE: [time.Duration] FIELDS: [] METHODS: [ Hours func(time.Duration) float64 Microseconds func(time.Duration) int64 Milliseconds func(time.Duration) int64 Minutes func(time.Duration) float64 Nanoseconds func(time.Duration) int64 Round func(time.Duration, time.Duration) time.Duration Seconds func(time.Duration) float64 String func(time.Duration) string Truncate func(time.Duration, time.Duration) time.Duration ] ]
(dlv) ptype req [ NAME: net/http.Request KIND: struct SIGNATURE: [*http.Request http.Request] FIELDS: [ Method string URL *url.URL Proto string ProtoMajor int ProtoMinor int ... <truncated> ... ] METHODS: [ AddCookie func(*http.Request, *http.Cookie) BasicAuth func(*http.Request) (string, string, bool) Clone func(*http.Request, context.Context) *http.Request ... <truncated> ... ] ]
(dlv) ptype "compress/flate" [ NAME: compress/flate CONSTANTS: [ BestCompression untyped int BestSpeed untyped int ... <truncated> ... ] VARIABLES: [] FUNCTIONS: [ NewReader(r io.Reader) io.ReadCloser NewReaderDict(r io.Reader, dict []byte) io.ReadCloser ... <truncated> ... ] TYPES: [ ... <truncated> ... Writer struct (*Writer) Close() error (*Writer) Flush() error (*Writer) Reset(dst io.Writer) (*Writer) Write(data []byte) (n int, err error) CorruptInputError int64 (CorruptInputError) Error() string] ]
Precedent:
The
gdb
debugger has aptype
command. See; https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_109.html Which works like;struct complex {double real; double imag;} v; (gdb) whatis v type = struct complex (gdb) ptype v type = struct complex { double real; double imag; }
Implementation:
The implementation would use the
reflect
package with no third party dependency. If this proposal were to be accepted, I'm willing to do most of the work to get this in. I already have a package that usesreflect
to print out public API of variables/types/imports; see https://github.com/komuw/kama#usage The implementation in delve would not use that package but would instead only rely onreflect
.This proposal can be broken into two parts: (i) Add ability to list the public API of any variable/type. (ii) Add ability to list the public api of any import. I feel certain that we should do (i) but I'm not certain about (ii). The two parts can be accepted/rejected independently.
I can avail any other information required to help people be able to evaluate this proposal.
Thanks,
good idea, I found this issue for almost the same purpose, to print a complex data structure without the souce code
I need this feature, and I am trying to implement this. Now I have a problem to solve.
Following is my testing:
$ dlv debug main.go
Type 'help' for list of commands.
(dlv) b main.main
Breakpoint 1 set at 0x10cb87b for main.main() ./main.go:17
(dlv) c
> main.main() ./main.go:17 (hits goroutine(1):1 total:1) (PC: 0x10cb87b)
12: def int
13: }
14:
15: var _ Student = Student{}
16:
=> 17: func main() {
18: ss := Student{Name: "xxxx"}
19: fmt.Println(ss)
20: }
(dlv) types Student
*main.Student
main.Student
(dlv) ptype main.Student
struct main.Student <nil>
(dlv) ptype int
int <nil>
(dlv)
main.Student is defined and printed by ptype
, it outputs its type is a struct, but the inner details is <nil>
. This is useless.
I just use BinaryInfo.findType(name)
to return the godwarf.Type
, then call the godwarf.Type.String()
to return the description of this type, but it's <nil>
.
I expect godwarf.Type.String()
would return the complete type description. Please let me know if anyone knows how to solve this problem.
I think I can do a type switch
to handle case by case. For example, convert godwarf.Type to godwarf.StructType, then I get the struct fields. So it works like this now.
$ dlv debug main.go
Type 'help' for list of commands.
(dlv) b main.main
Breakpoint 1 set at 0x10cb87b for main.main() ./main.go:17
(dlv) ptype main.Student
struct main.Student
fields
0 Name string
1 Age int
2 Sex int
3 Address string
4 School string
5 abc int
6 def int
Problem is godwarf.StructType
may not contain field to access its methods. Correct me if I misunderstand.
I have 2 solutions which maybe not so good. method1: We can traverse all the functions to find
main.(*Student)*
to list. method2: The generated .[z]debug_info,main.(*Student).String()
is DIEDW_TAG_subprogram
, this DIE's first sibling is its receiver type, which points to*main.Student
with tagDW_TAG_pointer_type
. I think when parsing dwarf, if we use some datastructure to store the mappings between the receiver type and its methods. That will accelerate the query speed compared to traversing all BinaryInfo.Functions.
todo:
- how to list the methods.
- Printing it recursively should be considered, like
print <variable>
, we also consider the recursive levels. - other godwarf.*Type should be supported.
Next, I want to see how print
works, maybe there's some code that can be reused.
This modification is here: https://github.com/hitzhangjie/delve/tree/feat/ptype.
using method1 mentioned above, now I can list the method's name (without parameter list).
(dlv) ptype main.Student
struct main.Student
fields
0 Name string
1 Age int
2 Sex int
3 Address string
4 School string
5 abc int
6 def int
methods
String
Sing
Learn
<nil>
After writing some code about DWARF, now it can list the parameter list (receiverType var is dropped) and return list:
$ dlv debug main.go
Type 'help' for list of commands.
(dlv) ptype main.Student
struct main.Student
fields
0 Name string
1 Age int
2 Sex int
3 Address string
4 School string
5 abc int
6 def int
methods
func Learn(a int,b int) (~r2 error)
<nil>
I had given this a shot one year ago and got it somehow working as a proof of concept with bad code.
The code can be found here: https://github.com/komuw/delve/tree/issues/2249-A
And instructions on how to run the demo are; https://github.com/komuw/delve/blob/issues/2249-A/example/main.go#L11-L36
basically;
A. Get the code and drop inside a docker container.
git clone [email protected]:komuw/delve.git
cd delve
git checkout issues/2249-A
docker-compose run app
B. build delve and debug the example application
rm -rf example/example /go/bin/dlv && \
go mod tidy && \
make install && \
go build -x -gcflags="all=-N -l" -ldflags='all=-linkshared' -o example/example example/main.go && \
/go/bin/dlv exec example/example
C. set breakpoint and execute various `whatis` commands
break example/main.go:86
(dlv) continue
(dlv) whatis -v f
(dlv) whatis -v hReq
when you do so, you get output like:
(dlv) whatis -v f
===printVar:===
struct main.Fire {
fields:
HH main.Health
Age int64
Name string
Dist main.Distance
Day time.Time
methods:
Hello func(i int) string
MethodTwo func() int64
}
===printVar:===
main.Fire
whatis -v hReq
===printVar:===
struct *net/http.Request {
fields:
Method string
URL *net/url.URL
Proto string
....
TLS *crypto/tls.ConnectionState
Cancel <-chan struct {}
Response *net/http.Response
methods:
BasicAuth func() (username string, password string, ok bool)
SetBasicAuth func(username string, password string)
...
Clone func(ctx context.Context) *net/http.Request
PostFormValue func(key string) string
Write func(w io.Writer) error
ParseMultipartForm func(maxMemory int64) error
Cookies func() []*net/http.Cookie
}
===printVar:===
*net/http.Request
(dlv) whatis -v MyFn
===printVar:===
func(int, string, *uint64) void {
methods:
MethodOnFunc func() int64
}
===printVar:===
main.fn
I don't have time to take it further than I did, so I'm just sharing whatever I have.
@komuw Thanks for your sharing, I'm willing to learn your code later...and contribute this.