gdb笔记

213 阅读5分钟

是什么

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

linuxtools-rst.readthedocs.io/zh_CN/lates…