Go工具和调试详解
工具集go build-gcflags: 传递给编译器的参数 -ldflags: 传递给链接器的参数 -work: 查看编译临时目录 -race: 允许数据竞争检测(仅支持amd64) -n: 查看但不执行编译指令 -x: 查看并执行编译命令 -a: 强制重新编译所有依赖包 -v: 查看被编译的包名,包括依赖包 -p n:并行编译所使用的CPU数,默认为全部 -o:输出文件名 gcflags: -B 禁用边界检查 -N 禁用优化 -l 禁用函数内联 -u 禁用unsafe代码 -m 输出优化信息 -S 输出汇编代码 ldflags: -w 禁用DRAWF调试信息,但不包括符号表 -s 禁用符号表 -X 修改字符串符号值 -X main.VER ‘0.99’ -X main.S ‘abc’ -H 链接文件类型,其中包括windowsgui. cmd/ld/doc.go
更多参数: go tool compile -h go tool link -h go install和go build参数相同,将生成文件拷贝到bin,pkg目录,优先使用GOBIN环境变量指定目录 go clean -n 查看但不执行清理命令 -x 查看并执行清理命令 -i 删除bin,pkg目录下的二进制文件 -r 清理所有依赖包临时目录 go get-d 仅下载,不执行安装命令 -t 下载测试所需依赖包 -u 更新包包括其依赖 -v 查看并执行命令 go tool objdump 反汇编可执行文件 go tool objdump -s “main.w+” test 条件编译(一)GOOS GOARCH 通过runtime.GOOS/GOARCH判断,或使用编译约束标记 // +build darwin linux ?-----必须有空行,以区别包文档。 package main 在源文件(.go,.h,.c,.s等)头部添加”+build”注释,指示编译器检查相关环境变量。 多个约束标记会合并处理。其中空格表示OR,逗号AND,感叹号NOT。 // +build darwin linux -------à合并结果(darwin OR linux)AND (amd64 AND (NOT cgo)) // +build amd64,!cgo 如果GOOS,GOARCH不符号,编译器会忽略该文件 (二)文件名 还可使用文件名来表示编译约束,比如test_darwin_amd64.go.使用文件名拆分多个不同平台源文件,更利于维护。 -rw-r--r--@1 zhangxiaoan admin 11545 11 29 05:38 os_darwin.c -rw-r--r--@1 zhangxiaoan admin 1382 11 29 05:38 os_darwin.h -rw-r--r--@1 zhangxiaoan admin 6990 11 29 05:38 os_freebsd.c -rw-r--r--@1 zhangxiaoan admin 791 11 29 05:38 os_freebsd.h -rw-r--r--@1 zhangxiaoan admin 644 11 29 05:38 os_freebsd_arm.c -rw-r--r--@1 zhangxiaoan admin 8624 11 29 05:38 os_linux.c -rw-r--r--@1 zhangxiaoan admin 1067 11 29 05:38 os_linux.h -rw-r--r--@1 zhangxiaoan admin 861 11 29 05:38 os_linux_386.c -rw-r--r--@ 1zhangxiaoan admin 2418 11 29 05:38 os_linux_arm.c ?支持:*_GOOS、*_GOARCH、*_GOOS_GOARCH、*_GOARCH_GOOS 格式。 可忽略某个文件,或指定编译器版本号。更多信息参考标准库go/build文档。 // +build ignore // +build go1.2 ?------最低需要go1.2编译 (三)自定义约束条件 需使用”go build -tags”参数。 跨平台编译首先得生成与平台相关的工具和标准库 $ 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 ... --- InstalledGo for linux/amd64 in /usr/local/go Installed commands in/usr/local/go/bin --no-clean参数避免清除其他平台文件 然后回到项目所在目录,设定GOOS,GOARCH环境变量即可编译目标平台文件 $GOOS=linux GOARCH=amd64 go build -o test $ filetest learn:ELF 64-bit LSB executable,x86-64,version 1 (SYSV) $ uname-a Darwin Kernel Version12.5.0: RELEASE_X86_64 x86_64 GO GDB语言调试gdb不能很好的理解GO程序.即使使用gccgo,栈管理,线程和运行时与GDB期望的执行模式也不完全一样。因此GDB对GO程序并不完全可靠,尤其是在调试并发程序时。 GO程序包含DWARF3调试信息,GDB7.1以上版本可以进行调试。. 传递”-w”选项给链接器会去除调试信息。(比如:go build -ldflags “-w” prog.go) gc编译器生成的代码包含内联函数和寄存器变量优化,可以通过-gcflags “-N -l”去掉优化 常用操作·显示代码文件和行号信息,设置断点和查看反汇编: ·(gdb) list ·(gdb) list line ·(gdb) list file.go:line ·(gdb) break line ·(gdb) break file.go:line (gdb) disas ·显示backtraces和stack frames: ·(gdb) bt (gdb) frame n ·显示名称,类型和栈上的本地变量,参数和返回值: ·(gdb) info locals ·(gdb) info args ·(gdb) p variable (gdb) whatis variable ·显示名称,类型和全局变量的位置: (gdb) info variables regexp go扩展GDB最新的扩展机制允许为二进制程序加载扩展脚本。工具链使用这个机制为GDB扩展一些命令来监控运行代码的内部(如goroutines)和内置map,slice,和通道的清晰打印. 如果GDB不能找到扩展脚本,可以手工执行: (gdb) source/usr/lib/golang/src/runtime/runtime-gdb.py ·打印string,map,channel orinterface: (gdb) p var ·strings,slices和maps的$len()和$cap() 函数: (gdb) p $len(var) ·转换接口为它们的动态类型: ·(gdb) p $dtype(var) (gdb) iface var ·查看协程: ·(gdb) info goroutines ·(gdb) goroutine n cmd (gdb) help goroutine 举例: (gdb) goroutine 12 bt 已知问题字符串的清晰打印只对原始字符串类型有效,从其派生的类型无效 运行库的C代码部分没有类型信息 GDB不理解GO的命名方式,包中的类型的函数形式为 所有的全局变量集中到包”main” 教程通过regexp包的单元测试程序来演示GDB的使用。 构建程序:cd $GOROOT/src/regexp; go test -c 生成可执行程序regexp.test $ gdb regexp.test GNU gdb (GDB) 7.2-gg8 Copyright (C) 2010 Free Software Foundation,Inc. License GPLv 3+: GNU GPL version 3or later <http://gnu.org/licenses/gpl.html> Type "show copying" and "show warranty" forlicensing/warranty details. This GDB was configured as "x86_64-linux".
Reading symbols from/home/user/go/src/regexp/regexp.test... done. Loading Go Runtime support. (gdb)
如果没有自动加载,可以手工加载:
(gdb) source/usr/lib/go/src/runtime/runtime-gdb.py Loading Go Runtime support. 查看代码使用"l"或"list"命令查看源代码. (gdb) l 通过函数名显示源代码的特定部分(必须包含其包名). (gdb) l main.main 显示特定文件和行号 (gdb) l regexp.go:1 (gdb) # Hit enter to repeat last command. Here,this lists next 10lines. 关于命名变量和函数名必须携带其所属的包名。regexp包中的Compile函数被GDB当作’regexp.Compile’ 方法必须包含其接收着类型。比如,*Regexp类型的String方法必须写在’regexp.(*Regexp).String’ 引用其它变量的类型在调试信息中被加上了一个数字后缀,被闭包引用的变量将会显示为一个添加&前缀的指针 设置断点为TestFind函数设置断点: (gdb) b 'regexp.TestFind' Breakpoint 1 at 0x424908: file /home/user/go/src/regexp/find_test.go,line148. 运行程序: (gdb) run Starting program: /home/user/go/src/regexp/regexp.test Breakpoint 1,regexp.TestFind (t=0xf8404a89c0) at/home/user/go/src/regexp/find_test.go:148 148 func TestFind(t *testing.T) {
执行在断点处暂停。查看哪个协程正在运行: (gdb) info goroutines 1waiting runtime.gosched * 13 running runtime.goexit 标记着*的为当前正在运行的协程
查看栈. 在程序暂停的地方查看栈回溯: (gdb) bt # backtrace #0 regexp.TestFind (t=0xf8404a89c0)at /home/user/go/src/regexp/find_test.go:148 #1 0x000000000042f60b intesting.tRunner (t=0xf8404a89c0,test=0x573720) at/home/user/go/src/testing/testing.go:156 #2 0x000000000040df64 inruntime.initdone () at /home/user/go/src/runtime/proc.c:242 #3 0x000000f8404a89c0 in ?? () #4 0x0000000000573720 in ?? () #5 0x0000000000000000 in ?? ()
另外一个协程1,阻塞在通道接收: (gdb) goroutine 1 bt #0 0x000000000040facb inruntime.gosched () at /home/user/go/src/runtime/proc.c:873 #1 0x00000000004031c9 inruntime.chanrecv (c=void,ep=void,selected=void,received=void) at/home/user/go/src/runtime/chan.c:342 #2 0x0000000000403299 inruntime.chanrecv1 (t=void,c=void) at/home/user/go/src/runtime/chan.c:423 #3 0x000000000043075b intesting.RunTests (matchString={void (struct string,struct string,bool *,error *)} 0x7ffff7f9ef60,tests= []testing.InternalTest = {...}) at/home/user/go/src/testing/testing.go:201 #4 0x00000000004302b1 in testing.Main(matchString={void (struct string,error *)} 0x7ffff7f9ef80,tests=[]testing.InternalTest = {...},benchmarks= []testing.InternalBenchmark ={...}) at /home/user/go/src/testing/testing.go:168 #5 0x0000000000400dc1 in main.main() at /home/user/go/src/regexp/_testmain.go:98 #6 0x00000000004022e7 inruntime.mainstart () at /home/user/go/src/runtime/amd64/asm.s:78 #7 0x000000000040ea6f inruntime.initdone () at /home/user/go/src/runtime/proc.c:243 #8 0x0000000000000000 in ?? () 栈桢显示我们当前在执行regexp.TestFind函数. (gdb) info frame Stack level 0,frame at 0x7ffff7f9ff88: rip = 0x425530 in regexp.TestFind(/home/user/go/src/regexp/find_test.go:148); saved rip 0x430233 called by frame at 0x7ffff7f9ffa8 source language minimal. Arglist at 0x7ffff7f9ff78,args:t=0xf840688b60 Locals at 0x7ffff7f9ff78,Previousframe's sp is 0x7ffff7f9ff88 Saved registers: rip at 0x7ffff7f9ff80 命令info locals显示函数的所有本地变量,但是这个命令有一定风险,因为它会尝试打印未初始化的变量。可能造成gdb打印很大的数组. 查看函数参数: (gdb) info args t = 0xf840688b60 打印参数时显示的时一个指向Regexp值的指针. 注意GDB错误地将*放在了类型名的右边. (gdb) p re (gdb) p t $1 = (struct testing.T *) 0xf840688b60 (gdb) p *t $2 = {errors = "",failed = false,ch = 0xf8406f5690} (gdb) p *t->ch $3 = struct hchan<*testing.T> struct hchan<*testing.T>是通道运行时的内部表示. 它当前为空, 否则gdb会清晰地打印其内容. 继续执行: (gdb) n # execute nextline 149 for _,test := rangefindTests { (gdb) # enter is repeat 150 re :=MustCompile(test.pat) (gdb) p test.pat $4 = "" $5 = (struct regexp.Regexp *) 0xf84068d070 (gdb) p *re $6 = {expr = "",prog = 0xf840688b80,prefix = "",prefixBytes = []uint8,prefixComplete =true, prefixRune = 0,cond = 0 ' 00',numSubexp = 0,longest = false,mu = {state = 0,sema = 0},239)"> machine = []*regexp.machine} (gdb) p *re->prog $7 = {Inst = []regexp/syntax.Inst ={{Op = 5 ' 05',Out = 0,Arg = 0,Rune =[]int},{Op = 6 ' 06',Out = 2,Rune= []int},{Op = 4 ' 04',Arg =0,Rune = []int}},239)"> Start = 1,NumCap = 2} 我们能够跟进String函数通过调用”s”: (gdb) s regexp.(*Regexp).String (re=0xf84068d070,noname=void) at/home/user/go/src/regexp/regexp.go:97 97 func (re *Regexp) String()string { 通过bt查看我们执行到哪里: (gdb) bt #0 regexp.(*Regexp).String(re=0xf84068d070,noname=void) at/home/user/go/src/regexp/regexp.go:97 #1 0x0000000000425615 inregexp.TestFind (t=0xf840688b60) at/home/user/go/src/regexp/find_test.go:151 #2 0x0000000000430233 intesting.tRunner (t=0xf840688b60,test=0x5747b8) at/home/user/go/src/testing/testing.go:156 #3 0x000000000040ea6f inruntime.initdone () at /home/user/go/src/runtime/proc.c:243 .... 查看源代码: (gdb) l 92 mu sync.Mutex 93 machine []*machine 94 } 95 96 // String returns the sourcetext used to compile the regular expression. 97 func (re *Regexp) String()string { 98 return re.expr 99 } 100 101 // Compile parses a regularexpression and returns,if successful, 清晰打印GDB's pretty printing mechanism is triggered by regexpmatches on type names. An example for slices: (gdb) p utf $22 = []uint8 = {0 ' 00',0' 00',0 ' 00',0 ' 00'} 因为slices,arrays 和 strings 不是C指针,GDB不能解释带下标的操作,但是你可以通过下面的方式查看其内容 (使用tab帮助完成): (gdb) p slc $11 = []int = {0,0} (gdb) p slc-><TAB> array slc len (gdb) p slc->array $12 = (int *) 0xf84057af00 (gdb) p slc->array[1] $13 = 0 扩展函数$len 和 $cap支持strings,arrays 和 slices: (gdb) p $len(utf) $23 = 4 (gdb) p $cap(utf) $24 = 4 通道和maps是引用类型,gdb显示为类似C++的指针类型hash<int,string>*.解引用操作将会触发清晰打印 接口在运行时表示为一个指向类型描述的指针和一个指向值的指针. Go GDB运行扩展对运行时类型解码并自动触发清晰打印. 扩展函数$dtype为你解码运行时类型 (例子在regexp.go的293行.) (gdb) p i $4 = {str = "cbb"} (gdb) whatis i type = regexp.input (gdb) p $dtype(i) $26 = (struct regexp.inputBytes *) 0xf8400b4930 (gdb) iface i regexp.input: struct regexp.inputBytes * (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |