gcflags
ldflags
更多參數(shù):
go tool 6g -h 或 [https://golang.org/cmd/gc/](https://golang.org/cmd/gc/)
go tool 6l -h 或 [https://golang.org/cmd/ld/](https://golang.org/cmd/ld/)
和 go build 參數(shù)相同,將生成文件拷貝到 bin、pkg 目錄。優(yōu)先使用 GOBIN 環(huán)境變量所指定目錄。
1.4 go get
下載并安裝擴(kuò)展包。默認(rèn)保存到 GOPATH 指定的第一個(gè)工作空間。
反匯編可執(zhí)行文件。
$ go tool objdump -s "main\.\w+" test
$ go tool objdump -s "main\.main" test
通過 runtime.GOOS/GOARCH 判斷,或使用編譯約束標(biāo)記。
// +build darwin linux
<--- 必須有空行,以區(qū)別包文檔。
package main
在源文件 (.go, .h, .c, .s 等) 頭部添加 "+build" 注釋,指示編譯器檢查相關(guān)環(huán)境變量。多個(gè)約束標(biāo)記會(huì)合并處理。其中空格表示 OR,逗號 AND,感嘆號 NOT。
// +build darwin linux --> 合并結(jié)果 (darwin OR linux) AND (amd64 AND (NOT cgo))
// +build amd64,!cgo
如果 GOOS、GOARCH 條件不符合,則編譯器會(huì)會(huì)忽略該文件。
還可使用文件名來表示編譯約束,比如 test_darwin_amd64.go。使用文件名拆分多個(gè)不同平臺(tái)源文件,更利于維護(hù)。
$ ls -l /usr/local/go/src/pkg/runtime
-rw-r--r--@ 1 yuhen admin 11545 11 29 05:38 os_darwin.c
-rw-r--r--@ 1 yuhen admin 1382 11 29 05:38 os_darwin.h
-rw-r--r--@ 1 yuhen admin 6990 11 29 05:38 os_freebsd.c
-rw-r--r--@ 1 yuhen admin 791 11 29 05:38 os_freebsd.h
-rw-r--r--@ 1 yuhen admin 644 11 29 05:38 os_freebsd_arm.c
-rw-r--r--@ 1 yuhen admin 8624 11 29 05:38 os_linux.c
-rw-r--r--@ 1 yuhen admin 1067 11 29 05:38 os_linux.h
-rw-r--r--@ 1 yuhen admin 861 11 29 05:38 os_linux_386.c
-rw-r--r--@ 1 yuhen admin 2418 11 29 05:38 os_linux_arm.c
支持:*_GOOS、*_GOARCH、*_GOOS_GOARCH、*_GOARCH_GOOS 格式。
可忽略某個(gè)文件,或指定編譯器版本號。更多信息參考標(biāo)準(zhǔn)庫 go/build 文檔。
// +build ignore
// +build go1.2 <--- 最低需要 go 1.2 編譯。
自定義約束條件,需使用 "go build -tags" 參數(shù)。
test.go
// +build beta,debug
package main
func init() {
println("test.go init")
}
輸出:
$ go build -tags "debug beta" && ./test
test.go init
$ go build -tags "debug" && ./test
$ go build -tags "debug \!cgo" && ./test
首先得生成與平臺(tái)相關(guān)的工具和標(biāo)準(zhǔn)庫。
$ cd /usr/local/go/src
$ GOOS=linux GOARCH=amd64 ./make.bash --no-clean
# Building C bootstrap tool.
cmd/dist
# Building compilers and Go bootstrap tool for host, darwin/amd64.
cmd/6l
cmd/6a
cmd/6c
cmd/6g
...
---
Installed Go for linux/amd64 in /usr/local/go
Installed commands in /usr/local/go/bin
說明:參數(shù) no-clean 避免清除其他平臺(tái)文件。
然后回到項(xiàng)目所在目錄,設(shè)定 GOOS、GOARCH 環(huán)境變量即可編譯目標(biāo)平臺(tái)文件。
$ GOOS=linux GOARCH=amd64 go build -o test
$ file test
learn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV)
$ uname -a
Darwin Kernel Version 12.5.0: RELEASE_X86_64 x86_64
簡單點(diǎn)說,go generate 掃描源代碼文件,找出所有 "//go:generate" 注釋,提取并執(zhí)行預(yù)處理命令。
不屬于 build 組成部分,設(shè)計(jì)目標(biāo)是提供給包開發(fā)者使用,因?yàn)榘脩艨赡懿痪邆涿顖?zhí)行環(huán)境。
//go:generate ls -l
//go:generate du
還可定義別名。須提前定義,僅在當(dāng)前文件內(nèi)有效。
//go:generate -command YACC go tool yacc
//go:generate YACC -o test.go -p parse test.y
可用條件編譯,讓 go build 忽略包含 generate 的文件。
// +build generate
$ go generate -tags generate
資源:Design Document Generating code
默認(rèn)情況下,編譯的二進(jìn)制文件已包含 DWARFv3 調(diào)試信息,只要 GDB 7.1 以上版本都可以調(diào)試。
相關(guān)選項(xiàng):
除了使用 GDB 的斷點(diǎn)命令外,還可以使用 runtime.Breakpoint 函數(shù)觸發(fā)中斷。另外,runtime/debug.PrintStack 可用來輸出調(diào)用堆棧信息。
某些時(shí)候,需要手工載入 Go Runtime support (runtime-gdb.py)。
.gdbinit
define goruntime
source /usr/local/go/src/runtime/runtime-gdb.py
end
set disassembly-flavor intel
set print pretty on
dir /usr/local/go/src/pkg/runtime
說明:OSX 環(huán)境下,可能需要以 sudo 方式啟動(dòng) gdb。
數(shù)據(jù)競爭 (data race) 是并發(fā)程序里不太容易發(fā)現(xiàn)的錯(cuò)誤,且很難捕獲和恢復(fù)錯(cuò)誤現(xiàn)場。Go 運(yùn)行時(shí)內(nèi)置了競爭檢測,允許我們使用編譯器參數(shù)打開這個(gè)功能。它會(huì)記錄和監(jiān)測運(yùn)行時(shí)內(nèi)存訪問狀態(tài),發(fā)出非同步訪問警告信息。
func main() {
var wg sync.WaitGroup
wg.Add(2)
x := 100
go func() {
defer wg.Done()
for {
x += 1
}
}()
go func() {
defer wg.Done()
for {
x += 100
}
}()
wg.Wait()
}
輸出:
$ GOMAXPROCS=2 go run -race main.go
==================
WARNING: DATA RACE
Write by goroutine 4:
main.func·002()
main.go:25 +0x59
Previous write by goroutine 3:
main.func·001()
main.go:18 +0x59
Goroutine 4 (running) created at:
main.main()
main.go:27 +0x16f
Goroutine 3 (running) created at:
main.main()
main.go:20 +0x100
==================
數(shù)據(jù)競爭檢測會(huì)嚴(yán)重影響性能,不建議在生產(chǎn)環(huán)境中使用。
func main() {
x := 100
for i := 0; i < 10000; i++ {
x += 1
}
fmt.Println(x)
}
輸出:
$ go build && time ./test
10100
real" 0m0.060s
user" 0m0.001s
sys" 0m0.003s
$ go build -race && time ./test
10100
real" 0m1.025s
user" 0m0.003s
sys" 0m0.009s
通常作為非性能測試項(xiàng)啟用。
$ go test -race
自帶代碼測試、性能測試、覆蓋率測試框架。
注: 不要將代碼放在名為 main 的目錄下,這會(huì)導(dǎo)致 go test "cannot import main" 錯(cuò)誤。
使用 testing.T 相關(guān)方法決定測試狀態(tài)。
testing.T
main_test.go
package main
import (
"testing"
"time"
)
func sum(n ...int) int {
var c int
for _, i := range n {
c += i
}
return c
}
func TestSum(t *testing.T) {
time.Sleep(time.Second * 2)
if sum(1, 2, 3) != 6 {
t.Fatal("sum error!")
}
}
func TestTimeout(t *testing.T) {
time.Sleep(time.Second * 5)
}
默認(rèn) go test 執(zhí)行所有單元測試函數(shù),支持 go build 參數(shù)。
$ go test -v -timeout 3s
=== RUN TestSum
--- PASS: TestSum (2.00 seconds)
=== RUN TestTimeout
panic: test timed out after 3s
FAIL" test" 3.044s
$ go test -v -run "(i)sum"
=== RUN TestSum
--- PASS: TestSum (2.00 seconds)
PASS
ok " test" 2.044s
可重寫 TestMain 函數(shù),處理一些 setup/teardown 操作。
func TestMain(m *testing.M) {
println("setup")
code := m.Run()
println("teardown")
os.Exit(code)
}
func TestA(t *testing.T) {}
func TestB(t *testing.T) {}
func BenchmarkC(b *testing.B) {}
輸出:
$ go test -v -test.bench .
setup
=== RUN TestA
--- PASS: TestA (0.00s)
=== RUN TestB
--- PASS: TestB (0.00s)
PASS
BenchmarkC" 2000000000" 0.00 ns/op
teardown
ok " test" 0.028s
性能測試需要運(yùn)行足夠多的次數(shù)才能計(jì)算單次執(zhí)行平均時(shí)間。
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
if sum(1, 2, 3) != 6 {
b.Fatal("sum")
}
}
}
默認(rèn)情況下,go test 不會(huì)執(zhí)行性能測試函數(shù),須使用 "-bench" 參數(shù)。
go test
$ go test -v -bench .
=== RUN TestSum
--- PASS: TestSum (2.00 seconds)
=== RUN TestTimeout
--- PASS: TestTimeout (5.00 seconds)
PASS
BenchmarkSum 100000000 11.0 ns/op
ok " test" 8.358s
$ go test -bench . -benchmem -cpu 1,2,4 -benchtime 30s
BenchmarkSum 5000000000 11.1 ns/op 0 B/op 0 allocs/op
BenchmarkSum-2 5000000000 11.4 ns/op 0 B/op 0 allocs/op
BenchmarkSum-4 5000000000 11.3 ns/op 0 B/op 0 allocs/op
ok " test" 193.246s
與 testing.T 類似,區(qū)別在于通過捕獲 stdout 輸出來判斷測試結(jié)果。
func ExampleSum() {
fmt.Println(sum(1, 2, 3))
fmt.Println(sum(10, 20, 30))
// Output:
// 6
// 60
}
不能使用內(nèi)置函數(shù) print/println,它們默認(rèn)輸出到 stderr。
$ go test -v
=== RUN: ExampleSum
--- PASS: ExampleSum (8.058us)
PASS
ok " test" 0.271s
Example 代碼可輸出到文檔,詳情參考包文檔章節(jié)。
除顯示代碼覆蓋率百分比外,還可輸出詳細(xì)分析記錄文件。
go test
$ go test -cover -coverprofile=cover.out -covermode=count
PASS
coverage: 80.0% of statements
ok " test" 0.043s
$ go tool cover -func=cover.out
test.go: Sum 100.0%
test.go: Add 0.0%
total:" (statements) 80.0%
用瀏覽器輸出結(jié)果,能查看更詳細(xì)直觀的信息。包括用不同顏色標(biāo)記覆蓋、運(yùn)行次數(shù)等。
$ go tool cover -html=cover.out
說明:將鼠標(biāo)移到代碼塊,可以看到具體的執(zhí)行次數(shù)。
監(jiān)控程序執(zhí)行,找出性能瓶頸。
import (
"os"
"runtime/pprof"
)
func main() {
// CPU
cpu, _ := os.Create("cpu.out")
defer cpu.Close()
pprof.StartCPUProfile(cpu)
defer pprof.StopCPUProfile()
// Memory
mem, _ := os.Create("mem.out")
defer mem.Close()
defer pprof.WriteHeapProfile(mem)
}
除調(diào)用 runtime/pprof 相關(guān)函數(shù)外,還可直接用測試命令輸出所需記錄文件。
go test
以 net/http 包為演示,先生成記錄文件。
$ go test -v -test.bench "." -cpuprofile cpu.out -memprofile mem.out net/http
進(jìn)入交互式查看模式。
$ go tool pprof http.test mem.out
(pprof) top5
2597.58kB of 2597.58kB total ( 100%)
Dropped 421 nodes (cum <= 12.99kB)
Showing top 5 nodes out of 28 (cum >= 1536.60kB)
flat flat% sum% cum cum%
1024.04kB 39.42% 39.42% 1024.04kB 39.42% encoding/asn1.parsePrintableString
548.84kB 21.13% 60.55% 548.84kB 21.13% mime.setExtensionType
512.56kB 19.73% 80.28% 1536.60kB 59.16% crypto/x509.parseCertificate
512.14kB 19.72% 100% 512.14kB 19.72% mcommoninit
0 0% 100% 1536.60kB 59.16% crypto/tls.(*Conn).Handshake
默認(rèn)輸出 inuse_space,可在命令行指定其他值,包括排序方式。
$ go tool pprof -alloc_space -cum http.test mem.out
可輸出函數(shù)調(diào)用的列表統(tǒng)計(jì)信息。
或者是更詳細(xì)的源碼模式。
除交互模式外,還可直接輸出統(tǒng)計(jì)結(jié)果。
$ go tool pprof -text http.test mem.out
2597.58kB of 2597.58kB total ( 100%)
Dropped 421 nodes (cum <= 12.99kB)
flat flat% sum% cum cum%
1024.04kB 39.42% 39.42% 1024.04kB 39.42% encoding/asn1.parsePrintableString
548.84kB 21.13% 60.55% 548.84kB 21.13% mime.setExtensionType
512.56kB 19.73% 80.28% 1536.60kB 59.16% crypto/x509.parseCertificate
512.14kB 19.72% 100% 512.14kB 19.72% mcommoninit
0 0% 100% 1536.60kB 59.16% crypto/tls.(*Conn).Handshake
0 0% 100% 1536.60kB 59.16% crypto/tls.(*Conn).clientHandshake
輸出圖形文件。
$ go tool pprof -web http.test mem.out
還可用 net/http/pprof 實(shí)時(shí)查看 runtime profiling 信息。
package main
import (
_ "net/http/pprof"
"net/http"
"time"
)
func main() {
go http.ListenAndServe("localhost:6060", nil)
for {
time.Sleep(time.Second)
}
}
在瀏覽器中查看 http://localhost:6060/debug/pprof/ 。
附: 自定義統(tǒng)計(jì)數(shù)據(jù),可用 expvar 導(dǎo)出,用瀏覽器訪問 /debug/vars 查看。
更多建議: