Skip to content
R. Bernstein edited this page Jul 14, 2013 · 6 revisions

Not much here other than the fact that this debugger exists and it does in large part to the cool work of the go-tools ssa interpreter. I am curious how this fares with other debuggers, so let me know what cool features they have and maybe I'll add them.

Showing all goroutines

When in the debugger, a lock is set so basically all threads and go routines are stopped. You can get a list of goroutines and their call stack using the goroutines (or gore) command.

When there is more than a single goroutine, that is displayed in the prompt after an "@" sign. For example

gub[4@2]: 

The "4" refers to the number of command prompts that have been entered. The "2" refers to the fact that you are stopped in goroutine 2. The main goroutine is always 0, and if that is running, it is not shown. That is you will see:

gub[4]: 

when the fourth command is about to be entered.

Fine-grained locations

Thanks to go/token's fine-grained line/column position handling, we are able to reflect that inside the debugger. This means that you can distinguish which statement we are stopped at in a multi-statement line or distinguish between the initialization, condition and iteration parts of a for statement which are on the same line. And you can set breakpoints using both line and column number. The command locs will show all the locations we have registered.

Change Program counter

You can change the program counter inside the bacic block with the debugger jump command.

Using as a run-time inspector (whatis)

The go.tools/ssa package collects lots of detailed and interesting information about Go programs and packages. One can have (large) bulk lists printed for you using various. But right now it doesn't give one a way of narrowing the information dumped.

With the debugger, not only can one narrow this information, one can also adapt queries based on previous queries, because the debugger is interactive.

Here is an example.

First get in the debugger:

$ ./gub.sh example/square.go 
Running....
Gub version 0.1
Type 'h' for help
->  main() 
example/square.go:15:6
gub[0] 

Ok. Now let's see what we have regarding the package os:

os is a package: at /home/rocky/go/google-code/go/src/pkg/os
Members
Args           Hostname         O_CREATE           Stdout           
Chdir          Interrupt        O_EXCL             Symlink          
Chmod          IsExist          O_RDONLY           SyscallError     
Chown          IsNotExist       O_RDWR             TempDir          
Chtimes        IsPathSeparator  O_SYNC             Truncate         
Clearenv       IsPermission     O_TRUNC            atime            
...
gub[1] 

Not only do you get where the package is located on my computer, in case I want to browse the source, but thanks to my columnize package, the list is nicley arranged compactly and sorted.

Ok, but now let's dill down based on this information.

gub[2] whatis os.Chown
os.Chown is a function at:
    /home/rocky/go/google-code/go/src/pkg/os/file_posix.go:98:6-103:2
    parameter name : string
    parameter uid : int
    parameter gid : int
Locals:
  0:	name~ string
  1:	uid~ int
  2:	gid~ int
  3:	e error

Again we get where this is located and very precise information about where in the file this function is. Under locals we see that in addition to the parameters, there is a local variable defined called e.

Want to know what os.O_RDWR is?

gub[2] whatis os.O_RDWR
Constant O_RDWR is a constant at:
  /home/rocky/go/google-code/go/src/pkg/os/file.go:60:2
  int 2

It is a integer constant with value 2.

Calls, Returns, and Events, oh my!

Of course we don't make a common debugger mistake of mistaking the first statement of a call for the call itself. The event that you are stopped at is shown in the as a little icon.

???    Unknown
:=     Assignment statement
}      Block end
<-X    go "break" statement
xxx    user-set breakpoint
->     call entry 
<-     call return
(.)    Expression
if:    go "if" initialization
if?    go "if" condition
lo:    go "for" loop initialization
lo?    go "for" condition
lo+    go "for" interation
m()    start of go main() 
oX     panic (My lame attempt at skull and cross bones)
...    range statement
sel    select type
sw?    switch condition
---    beginning of a new statement

Because we have such fine grain control, stepping may be a little bit tedious. If you are familiar with my other debuggers that will be addressed, via set different, step suffixes, and possibly event masks.

Don't like my debugger?

Well, with the hooks added to the interpreter, you can write your own.

I've added a call-stack frame type which one can use to introspect about the call stack. You can register a function to get called tracing is enabled.

The main interface is:

type TraceHookFunc func(*interp.Frame, *ssa2.Instruction, ssa2.TraceEvent)
interp.SetTraceHook(hook TraceHookFunc)