A brief intro of delve

delve is a debugger developed in Golang and also dedicated to help trouble-shooting Golang programs (Home page is here). Though it is still in pre-1.0 release, I think it is stable enough for daily use. BTW, if you find some bugs, you can report it to developers and help to make delve more stronger! P.S., Albeit fmt.Print buddies are useful in most cases, I strongly recommend you try to usedelve to inspect the internal mechanism of your code, because it can help you know Golang deeper, not just superficial stuff.

Installing delve is very simple: taking Linux platform as an example, it is no different from setting up other Golang projects, just “go get” is enough:

go get github.com/derekparker/delve/cmd/dlv 

Now, in $GOPATH/bin, there is an extra dlv executable binary file (Notice: the project is named delve, while the executable file is calleddlv. I even made a foolish mistake when began to use it).Run dlv command, and it will show you a detailed manual of delve:

# dlv
Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.

Usage:
  dlv [command]

Available Commands:
  version     Prints version.
  run         Deprecated command. Use 'debug' instead.
  debug       Compile and begin debugging program.
......

Let’s check this artificial Hello.go program:

package main

import "fmt"

func main() {
        var s []byte
        s = append(s, []byte("Hello, Debugging!")...)
        fmt.Println(string(s))
}

Use delve to debug it:

# dlv debug Hello.go
Type 'help' for list of commands.
(dlv) help
The following commands are available:
    help (alias: h) ------------- Prints the help message.
    break (alias: b) ------------ Sets a breakpoint.
    trace (alias: t) ------------ Set tracepoint.
    restart (alias: r) ---------- Restart process.
    continue (alias: c) --------- Run until breakpoint or program termination.
    step (alias: s) ------------- Single step through program.
    step-instruction (alias: si)  Single step a single cpu instruction.
    next (alias: n) ------------- Step over to next source line.
    threads --------------------- Print out info for every traced thread.
    thread (alias: tr) ---------- Switch to the specified thread.
......

If you are familiar with gdb, you will find the commands are very similar, and I promise you can master delve soon.

An interesting thing is that the delve doesn’t provide start command which gdb offers, so you should try to set breakpoints first, then run continue command:

(dlv) b Hello.go:8
Breakpoint 1 set at 0x4011ea for main.main() ./Hello.go:8
(dlv) c
> main.main() ./Hello.go:8 (hits goroutine(1):1 total:1) (PC: 0x4011ea)
     3: import "fmt"
     4:
     5: func main() {
     6:         var s []byte
     7:         s = append(s, []byte("Hello, Debugging!")...)
=>   8:         fmt.Println(string(s))
     9: }
(dlv) p s
[]uint8 len: 17, cap: 32, [72,101,108,108,111,44,32,68,101,98,117,103,103,105,110,103,33]
(dlv) goroutines
[4 goroutines]
* Goroutine 1 - User: ./Hello.go:8 main.main (0x4011ea)
  Goroutine 2 - User: /usr/local/go/src/runtime/proc.go:263 runtime.gopark (0x42a153)
  Goroutine 3 - User: /usr/local/go/src/runtime/proc.go:263 runtime.gopark (0x42a153)
  Goroutine 4 - User: /usr/local/go/src/runtime/mfinal.go:144 runtime.runfinq (0x413f80)

Cool! Isn’t it? Now You can observe almost everything you want to know about your program.

Happy Debugging! Happy delving!

 

The tips of using gdb to debug Golang program

Although “GDB does not understand Go programs well.” (from Debugging Go Code with GDB), sometimes gdb is still a useful tool for debugging Golang program. In this post, I will show some small tips.

(1)

My OS is Ubuntu 14.04. Launching gdb, it prompts following things:

......
warning: File "/usr/local/go/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
line to your configuration file "/home/nan/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/nan/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
......

So I add “add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py” in my .gdbinit file. My preferred .gdbinit file is like this:

$ cat ${HOME}/.gdbinit
add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
set confirm off
set print pretty on

(2)

If you want to set breakpoint on the start of main function, you should use “b main.main“:

......
(gdb) b main.main
Breakpoint 1 at 0x4021e0: file /home/nan/kubernetes/_output/local/go/src/k8s.io/kubernetes/cmd/kubectl/kubectl.go, line 26.
(gdb) r
Starting program: /home/nan/kubernetes/_output/local/go/bin/kubectl
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77f6700 (LWP 957)]
[New Thread 0x7ffff6fb5700 (LWP 958)]
[New Thread 0x7ffff5fb3700 (LWP 960)]
[New Thread 0x7ffff67b4700 (LWP 959)]

Breakpoint 1, main.main () at /home/nan/kubernetes/_output/local/go/src/k8s.io/kubernetes/cmd/kubectl/kubectl.go:26
26      func main() {
......

Not “b main“, except you want to read mysterious assembly code:

......
(gdb) b main
Breakpoint 1 at 0x45e830: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 63.
(gdb) r
Starting program: /home/nan/kubernetes/_output/local/go/bin/kubectl
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at /usr/local/go/src/runtime/rt0_linux_amd64.s:63
63              MOVQ    $runtime·rt0_go(SB), AX
(gdb)
......

(3)

If you don’t know the symbol name of a function, you can use readelf tool. E.g.:

$ readelf -s -W /home/nan/kubernetes/_output/local/go/bin/kubectl | grep NewCmdConfig
 ......
 14350: 0000000000766da0  3168 FUNC    LOCAL  DEFAULT   14 k8s.io/kubernetes/pkg/kubectl/cmd/config.NewCmdConfig
 ......
 14404: 00000000007745d0    48 FUNC    LOCAL  DEFAULT   14 k8s.io/kubernetes/pkg/kubectl/cmd/config.NewCmdConfig.func1

The config.NewCmdConfig.func1 is the function which is defined in NewCmdConfig function:

func NewCmdConfig(pathOptions *PathOptions, out io.Writer) *cobra.Command {
    ......
        Run: func(cmd *cobra.Command, args []string) {
            cmd.Help()
    ......
}

Then you can set breakpoint for wanted function:

......
(gdb) b k8s.io/kubernetes/pkg/kubectl/cmd/config.NewCmdConfig
Breakpoint 1 at 0x766da0: file /home/nan/kubernetes/_output/local/go/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go, line 63.
(gdb) r
Starting program: /home/nan/kubernetes/_output/local/go/bin/kubectl
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77f6700 (LWP 1416)]
[New Thread 0x7ffff6fb5700 (LWP 1417)]
[New Thread 0x7ffff67b4700 (LWP 1418)]
[New Thread 0x7ffff5fb3700 (LWP 1419)]

Breakpoint 1, k8s.io/kubernetes/pkg/kubectl/cmd/config.NewCmdConfig (pathOptions=0xc820178b90, out=..., ~r2=0x1)
    at /home/nan/kubernetes/_output/local/go/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go:63
63      func NewCmdConfig(pathOptions *PathOptions, out io.Writer) *cobra.Command {
(gdb)
......

 

Use Intellij IDEA as a Golang IDE

My Intellij IDEA is v15.0.1 community edition, and has been installed with golang-plugin. My Golang workspace is like this:

GOPATH=C:\Work\gocode

(1) Select “Create New Project“:

1

(2) Select “Go“, then “Next“:

2

(3) Select “SDK“, then “Next“:

3

(4) The important step coming! The up setting is for storing IDEA project files, while the down is for Golang workspace, then “Finish“:

4

(5) Now, use Intellij IDEA as a Golang IDE:

5
Enjoy it!

Fix “unsupported protocol scheme” issue in golang

When using “http” package in golang, you should use the full domain name, For example:

package main
import (
    "net/http"
    "fmt"
    "io/ioutil"
    "os"
)

func main() {
    resp, err := http.Get("www.google.com")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    text, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Println(string(text))
}

Executing it, the error will be:

Get www.google.com: unsupported protocol scheme ""

Add “http://” before “www.google.com“, then executing it. The result is OK:

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en"><head><meta co.... 

P.S., the full code is here.

Use sync.WaitGroup in Golang

sync.WaitGroup provides a goroutine synchronization mechanism in Golang, and is used for waiting for a collection of goroutines to finish.

The sync.WaitGroup structure is like this:

type WaitGroup struct {
    m       Mutex
    counter int32
    waiters int32
    sema    *uint32
}

There is a counter member which indicates how many goroutines need to be waited are living now.

sync.WaitGroup also provides 3 methods: Add, Done and Wait. Add method is used to identify how many goroutines need to be waited. When a goroutine exits, it must call Done. The main goroutine blocks on Wait, Once the counter becomes 0, the Wait will return, and main goroutine can continue to run.

Let’s see an example:

package main

import (
    "sync"
    "time"
    "fmt"
)

func sleepFun(sec time.Duration, wg *sync.WaitGroup) {
    defer wg.Done()
    time.Sleep(sec * time.Second)
    fmt.Println("goroutine exit")
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go sleepFun(1, &wg)
    go sleepFun(3, &wg)
    wg.Wait()
    fmt.Println("Main goroutine exit")

}

Because the main goroutine need to wait 2 goroutines, so the argument for wg.Add is 2. The execution result is like this:

goroutine exit
goroutine exit
Main goroutine exit

Please notice, the Add must go ahead of Done. For detailed inforamtion, you can refer this link: Example for sync.WaitGroup correct?.

P.S., the full code is here.