是什么
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。
一般来说,GDB主要帮忙你完成下面四个方面的功能:
- 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
- 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
- 当程序被停住时,可以检查此时你的程序中所发生的事。
- 动态的改变你程序的执行环境。
示例1
package main
func main() {
var a = make(chan int, 4)
a <- 1
a <- 1
a <- 1
a <- 1
close(a)
println()
}
go build gcflags="-N -l" main.go
gdb main
打上断点,查看 a 的结构:
* thread #1, stop reason = step over
frame #0: 0x000000000104c354 normal_example`main.main at normal_example.go:5
2
3 func main() {
4 var a = make(chan int, 4)
-> 5 a <- 1
6 a <- 1
7 a <- 1
8 a <- 1
Target 0: (normal_example) stopped.
(lldb) p a
(chan int) a = 0x000000c42007a000
(lldb) p *a
(hchan<int>) *a = {
qcount = 0
dataqsiz = 4
buf = 0x000000c42007a060
elemsize = 8
closed = 0
elemtype = 0x0000000001055ee0
sendx = 0
recvx = 0
recvq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
sendq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
lock = (key = 0x0000000000000000)
}
a.buf 是 void* 类型,这种类型需要用 x 指令来读取内容:
(lldb) n
Process 21186 stopped
* thread #1, stop reason = step over
frame #0: 0x000000000104c369 normal_example`main.main at normal_example.go:6
3 func main() {
4 var a = make(chan int, 4)
5 a <- 1
-> 6 a <- 1
7 a <- 1
8 a <- 1
9 close(a)
Target 0: (normal_example) stopped.
(lldb) p a.buf
(void *) buf = 0x000000c42007a060
(lldb) x a.buf
0xc42007a060: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xc42007a070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
可以看到向 channel 中写入一个 1 之后,a.buf 中的内容发生了变化。同时,a 中的 sendx 和 qcount 也都发生了变化:
(lldb) p *a
(hchan<int>) *a = {
qcount = 1 // 这里这里
dataqsiz = 4
buf = 0x000000c42007a060
elemsize = 8
closed = 0
elemtype = 0x0000000001055ee0
sendx = 1 // 这里这里
recvx = 0
recvq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
sendq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
lock = (key = 0x0000000000000000)
}
recvq 和 sendq 是用来维护发送接收时被阻塞需要休眠的 goroutine 列表。
elemtype 是 runtime._type 类型,可以看到 channel 中的元素类型信息。
close(a) 以后再看看结构:
(chan int) a = 0x000000c42007a000
(lldb) p *a
(hchan<int>) *a = {
qcount = 4
dataqsiz = 4
buf = 0x000000c42007a060
elemsize = 8
closed = 1 // 重点在这里
elemtype = 0x0000000001055ee0
sendx = 0
recvx = 0
recvq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
sendq = {
first = 0x0000000000000000
last = 0x0000000000000000
}
lock = (key = 0x0000000000000000)
}
示例2
package main
import "sync"
func main() {
var wg sync.WaitGroup
var a = make(chan int, 2)
wg.Add(5)
for i := 0; i < 5; i++ {
go push(&wg, a)
}
wg.Wait()
close(a)
}
func push(wg *sync.WaitGroup, in chan<- int) {
in <- 2
defer wg.Done()
}
再尝试在 a 上阻塞几个 goroutine:
(lldb) p a.recvq
(waitq<int>) recvq = {
first = 0x000000c42007c000
last = 0x000000c42007c060
}
(lldb) p a.recvq.first
(*sudog<int>) first = 0x000000c42007c000
(lldb) p *a.recvq.first
(sudog<int>) *first = {
g = 0x000000c420000f00
isSelect = false
next = 0x000000c42007c060
prev = 0x0000000000000000
elem = 0x0000000000000000
acquiretime = 0
releasetime = 0
ticket = 0
parent = 0x0000000000000000
waitlink = 0x0000000000000000
waittail = 0x0000000000000000
c = 0x000000c42007a000
}
可以看到,channel 的 recvq 和 sendq 就是个 sudog 的双向链表
示例3
string 和 byte 之间到底有没有进行相互转换
package main
func main() {
var str = "abcde"
var b = []byte("defg")
println(str)
println(string(b))
}
disassemble来看汇编
** 6 var b = []byte("defg")
7
0x104cf17 <+71>: lea rax, [rsp + 0x30]
0x104cf1c <+76>: mov qword ptr [rsp], rax
0x104cf20 <+80>: lea rax, [rip + 0x1c95b] ; go.string.* + 210
0x104cf27 <+87>: mov qword ptr [rsp + 0x8], rax
0x104cf2c <+92>: mov qword ptr [rsp + 0x10], 0x4
0x104cf35 <+101>: call 0x1038390 ; runtime.stringtoslicebyte at string.go:146
0x104cf3a <+106>: mov rax, qword ptr [rsp + 0x20]
0x104cf3f <+111>: mov rcx, qword ptr [rsp + 0x18]
0x104cf44 <+116>: mov rdx, qword ptr [rsp + 0x28]
0x104cf49 <+121>: mov qword ptr [rsp + 0xa0], rcx
0x104cf51 <+129>: mov qword ptr [rsp + 0xa8], rax
0x104cf59 <+137>: mov qword ptr [rsp + 0xb0], rdx