如何观察 Go map 的初始化过程

huangapple go评论107阅读模式
英文:

How to watch the init of the go map

问题

我在VSCode中使用Delve来调试Golang代码。
我想使用Delve来查看Golang地图的创建过程,并在使用make函数初始化地图时创建一个断点,但是它无法进入断点。

英文:

I use the delve to debug galang code in vscode.
I want to use the delve to see the the creation of the golang map,and I create a breakpoint when use the make function to init the map,but it just cannot step into.

答案1

得分: 1

你可以使用delve来调试运行时,但是你需要熟悉dlv的命令行界面(不使用图形界面)。

我将使用以下程序作为示例:

package main

func main() {
	abc := make(map[string]int)
	abc["a"] = 1
	abc["b"] = 2
	abc["c"] = 2
	fmt.Println(abc)
}

我们可以使用dlv debug命令让delve构建和调试我们的程序。这将进入交互式调试器。

使map调试变得“有趣”的一件事是编译器可以以不同的方式创建map。我们首先需要知道编译器用于创建map的函数(根据map的大小可能有多个)。我们将使用disassemble -l main.main命令询问delve对main函数进行反汇编:

(dlv) disassemble -l main.main
TEXT main.main(SB) /home/caveman/Downloads/test/main.go
        main.go:7       0x4b6860        64488b0c25f8ffffff              mov rcx, qword ptr fs:[0xfffffff8]
        main.go:7       0x4b6869        488d4424f0                      lea rax, ptr [rsp-0x10]
        main.go:7       0x4b686e        483b4110                        cmp rax, qword ptr [rcx+0x10]
        main.go:7       0x4b6872        0f8668010000                    jbe 0x4b69e0
        main.go:7       0x4b6878        4881ec90000000                  sub rsp, 0x90
        main.go:7       0x4b687f        4889ac2488000000                mov qword ptr [rsp+0x88], rbp
        main.go:7       0x4b6887        488dac2488000000                lea rbp, ptr [rsp+0x88]
        main.go:8       0x4b688f        e84c86f5ff                      call $runtime.makemap_small
        main.go:8       0x4b6894        488b0424                        mov rax, qword ptr [rsp]
        main.go:8       0x4b6898        4889442430                      mov qword ptr [rsp+0x30], rax
        ...

现在看起来很复杂,但我们只需要寻找对runtime的调用。在这种情况下,是对makemap_small函数的调用:call $runtime.makemap_small

既然我们知道了这一点,我们需要在这个runtime函数中设置一个断点。我们使用break mkSmallMap runtime.makemap_small命令来实现:

(dlv) break mkSmallMap runtime.makemap_small
Breakpoint mkSmallMap set at 0x40eeef for runtime.makemap_small() /usr/local/go/src/runtime/map.go:292

现在,我们可以通过执行c(continue)命令来启动我们的程序:

(dlv) c
> [mkSmallMap] runtime.makemap_small() /usr/local/go/src/runtime/map.go:292 (hits goroutine(1):1 total:1) (PC: 0x40eeef)
Warning: debugging optimized function
   287: }
   288:
   289: // makemap_small implements Go map creation for make(map[k]v) and
   290: // make(map[k]v, hint) when hint is known to be at most bucketCnt
   291: // at compile time and the map needs to be allocated on the heap.
=> 292: func makemap_small() *hmap {
   293:         h := new(hmap)
   294:         h.hash0 = fastrand()
   295:         return h
   296: }
   297:

我们在runtime函数本身中断。help命令将帮助您开始使用调试所需的所有命令。以下是一些基本命令:

  • continue(别名:c)--------- 运行直到断点或程序终止。
  • next(别名:n)------------- 跳过到下一行源代码。
  • step(别名:s)------------- 逐行执行程序。
  • stepout(别名:so)--------- 从当前函数中跳出。
  • list(别名:ls | l)------- 显示源代码。
  • args ----------------- 打印函数参数。
  • locals --------------- 打印局部变量。

如果我们将map的创建更改为abc := make(map[string]int, 100000),您将在反汇编中看到runtime函数已更改为runtime.makemap,这可能更符合您的兴趣。

希望这能帮助您入门,请在需要澄清的地方留下评论。

英文:

You can use delve to debug the runtime, however you will need to get familiar with the dlv command line interface(without using a GUI).

I will be using the following program as an example:

package main

func main() {
	abc := make(map[string]int)
	abc["a"] = 1
	abc["b"] = 2
	abc["c"] = 2
	fmt.Println(abc)
}

We can ask delve to build and debug our program with the dlv debug command. This will drop us into the interactive debugger.

One of the things that makes maps "fun" to debug is that the compiler can create maps in different ways. We first need to know which functions the compiler is using to create a map(can be several depending on map size). We will ask delve to disassemble the main func with the disassemble -l main.main command:

(dlv) disassemble -l main.main
TEXT main.main(SB) /home/caveman/Downloads/test/main.go
        main.go:7       0x4b6860        64488b0c25f8ffffff              mov rcx, qword ptr fs:[0xfffffff8]
        main.go:7       0x4b6869        488d4424f0                      lea rax, ptr [rsp-0x10]
        main.go:7       0x4b686e        483b4110                        cmp rax, qword ptr [rcx+0x10]
        main.go:7       0x4b6872        0f8668010000                    jbe 0x4b69e0
        main.go:7       0x4b6878        4881ec90000000                  sub rsp, 0x90
        main.go:7       0x4b687f        4889ac2488000000                mov qword ptr [rsp+0x88], rbp
        main.go:7       0x4b6887        488dac2488000000                lea rbp, ptr [rsp+0x88]
        main.go:8       0x4b688f        e84c86f5ff                      call $runtime.makemap_small
        main.go:8       0x4b6894        488b0424                        mov rax, qword ptr [rsp]
        main.go:8       0x4b6898        4889442430                      mov qword ptr [rsp+0x30], rax
        ...

Now this looks complicated, but we just have to look for calls to the runtime. In this case call $runtime.makemap_small a call to the makemap_small function.

Now that we know this, we need to make a breakpoint in this runtime function. We do this using the break mkSmallMap runtime.makemap_small command:

(dlv) break mkSmallMap runtime.makemap_small
Breakpoint mkSmallMap set at 0x40eeef for runtime.makemap_small() /usr/local/go/src/runtime/map.go:292

Now we can start our program by executing the c(continue) command:

(dlv) c
> [mkSmallMap] runtime.makemap_small() /usr/local/go/src/runtime/map.go:292 (hits goroutine(1):1 total:1) (PC: 0x40eeef)
Warning: debugging optimized function
   287: }
   288:
   289: // makemap_small implements Go map creation for make(map[k]v) and
   290: // make(map[k]v, hint) when hint is known to be at most bucketCnt
   291: // at compile time and the map needs to be allocated on the heap.
=> 292: func makemap_small() *hmap {
   293:         h := new(hmap)
   294:         h.hash0 = fastrand()
   295:         return h
   296: }
   297:

We break in the runtime function itself. The help command will help you get started with all the commands you need for debugging. A few basic ones:

  • continue (alias: c) --------- Run until breakpoint or program termination.
  • next (alias: n) ------------- Step over to next source line.
  • step (alias: s) ------------- Single step through program.
  • stepout (alias: so) --------- Step out of the current function.
  • list (alias: ls | l) ------- Show source code.
  • args ----------------- Print function arguments.
  • locals --------------- Print local variables.

If we change the map creation to abc := make(map[string]int, 100000) you will see in the disassembly that the runtime function has changed to runtime.makemap which is likely of more interest to you.

I hope this gets you going, please leave a comment if you need clarification on anything.

huangapple
  • 本文由 发表于 2021年12月9日 22:25:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/70291596.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定