diff --git a/README.md b/README.md index b7687842..0bf69e79 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,15 @@ To enter the heap profile, run: $ gops pprof-heap ``` -#### 4. gc +#### 4. trace + +gops allows you to start the runtime tracer for 5 seconds and examine the results. + +```sh +$ gops trace +``` + +#### 5. gc If you want to force run garbage collection on the target program, run the following command. It will block until the GC is completed. @@ -100,7 +108,7 @@ It will block until the GC is completed. $ gops gc ``` -#### 5. version +#### 6. version gops reports the Go version the target program is built with, if you run the following: @@ -108,7 +116,7 @@ gops reports the Go version the target program is built with, if you run the fol $ gops version ``` -#### 6. stats +#### 7. stats To print the runtime statistics such as number of goroutines and `GOMAXPROCS`, run the following: diff --git a/agent/agent.go b/agent/agent.go index 46c085ae..ab9571b6 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -14,6 +14,7 @@ import ( gosignal "os/signal" "runtime" "runtime/pprof" + "runtime/trace" "strconv" "sync" "time" @@ -189,6 +190,10 @@ func handle(conn net.Conn, msg []byte) error { fmt.Fprintf(conn, "OS threads: %v\n", pprof.Lookup("threadcreate").Count()) fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0)) fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU()) + case signal.Trace: + trace.Start(conn) + time.Sleep(5 * time.Second) + trace.Stop() } return nil } diff --git a/cmd.go b/cmd.go index 60673a20..46d28114 100644 --- a/cmd.go +++ b/cmd.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "io" "io/ioutil" "net" "os" @@ -21,6 +22,7 @@ var cmds = map[string](func(pid int) error){ "pprof-heap": pprofHeap, "pprof-cpu": pprofCPU, "stats": stats, + "trace": trace, } func stackTrace(pid int) error { @@ -49,6 +51,31 @@ func pprofCPU(pid int) error { return pprof(pid, signal.CPUProfile) } +func trace(pid int) error { + fmt.Println("Tracing now, will take 5 secs...") + out, err := cmd(pid, signal.Trace) + if err != nil { + return err + } + if len(out) == 0 { + return errors.New("nothing has traced") + } + tmpfile, err := ioutil.TempFile("", "trace") + if err != nil { + return err + } + defer os.Remove(tmpfile.Name()) + if err := ioutil.WriteFile(tmpfile.Name(), out, 0); err != nil { + return err + } + cmd := exec.Command("go", "tool", "trace", tmpfile.Name()) + cmd.Env = os.Environ() + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + func pprof(pid int, p byte) error { out, err := cmd(pid, p) if err != nil { @@ -96,20 +123,28 @@ func cmdWithPrint(pid int, c byte) error { } func cmd(pid int, c byte) ([]byte, error) { - port, err := internal.GetPort(pid) + conn, err := cmdLazy(pid, c) if err != nil { return nil, err } - conn, err := net.Dial("tcp", "127.0.0.1:"+port) + all, err := ioutil.ReadAll(conn) if err != nil { return nil, err } - if _, err := conn.Write([]byte{c}); err != nil { + return all, nil +} + +func cmdLazy(pid int, c byte) (io.Reader, error) { + port, err := internal.GetPort(pid) + if err != nil { return nil, err } - all, err := ioutil.ReadAll(conn) + conn, err := net.Dial("tcp", "127.0.0.1:"+port) if err != nil { return nil, err } - return all, nil + if _, err := conn.Write([]byte{c}); err != nil { + return nil, err + } + return conn, nil } diff --git a/main.go b/main.go index a5ac05f1..b467ad9f 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,7 @@ Commands: help Prints this help text. Profiling commands: + trace Runs the runtime tracer for 5 secs and launches "go tool trace". pprof-heap Reads the heap profile and launches "go tool pprof". pprof-cpu Reads the CPU profile and launches "go tool pprof". diff --git a/signal/signal.go b/signal/signal.go index aefeaa69..430e2b09 100644 --- a/signal/signal.go +++ b/signal/signal.go @@ -26,4 +26,7 @@ const ( // Stats returns Go runtime statistics such as number of goroutines, GOMAXPROCS, and NumCPU. Stats = byte(0x7) + + // Trace starts the Go execution tracer, waits 5 seconds and launches the trace tool. + Trace = byte(0x8) )