中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

用 debugger 學(xué)習(xí) golang

2018-07-20    來(lái)源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

常見的工程語(yǔ)言可分為解釋型和編譯型兩種,比如寫 php 的,一般就不怎么在乎 debugger 之類的東西。為什么?如果真出了問(wèn)題,我可以臨時(shí)把出問(wèn)題的服務(wù)機(jī)器從線上服務(wù)中摘除出來(lái),甚至申請(qǐng)一個(gè)較高的權(quán)限去修改代碼,然后到處去 die/echo。雖然有人說(shuō)這么做不太好,或者一般公司也不給開權(quán)限。不過(guò)著急的時(shí)候,這個(gè)肯定是可行的。然而像 java/go 這種編譯型的就比較麻煩了。線上一般只有程序的運(yùn)行環(huán)境而沒有編譯環(huán)境。就算是在線下,每次去加一行 fmt.Println 或者 System.out.println 都去編譯一遍代碼也是會(huì)明顯降低幸福感的事情(當(dāng)然這里有人說(shuō)現(xiàn)在 java 支持 hotswap 之類的功能,不過(guò)你總還是會(huì)遇到需要重新編譯的場(chǎng)景。go 也是一樣的,項(xiàng)目大了,編譯時(shí)間還是可能會(huì)有個(gè)五六七八秒的。想要迅速地還原 bug 的現(xiàn)場(chǎng),那還是能用 debugger 為上。

除了拿 debugger 來(lái) debug。還可以用 debugger 來(lái)了解了解程序運(yùn)行的機(jī)制,或者用 disass 來(lái)查看程序運(yùn)行的匯編碼。這一點(diǎn)也很重要。應(yīng)用層的語(yǔ)言很多時(shí)候因?yàn)?runtime 事無(wú)巨細(xì)的封裝,已經(jīng)不是所見即所得的東西了,特別是像 go 這樣,你寫一個(gè) var a = 1 卻連最終這個(gè)變量會(huì)被分配到堆上還是棧上都不知道。而像應(yīng)用層的空 interface 和非空的 interface 實(shí)際的數(shù)據(jù)結(jié)構(gòu)完全不一樣,這些如果你想知道的話一方面可以通過(guò)閱讀源代碼,但 go 的源代碼到你的代碼之間始終還是有一個(gè)轉(zhuǎn)換過(guò)程。如果你可以通過(guò)匯編直接查看運(yùn)行時(shí)的結(jié)構(gòu)顯然要更為直觀。

這篇文章也不準(zhǔn)備寫得大而全,就簡(jiǎn)單地舉一些可以靠 debugger 來(lái)幫我們更清楚地認(rèn)識(shí)問(wèn)題的場(chǎng)景吧。

var a = new(T) 和 var a = &T{} 這兩種語(yǔ)法有區(qū)別么?

寫兩個(gè)差不多的程序,然后帶上 gcflags="-N -l" 來(lái) go build

-> 5    func main() {

di`main.main:
->  0x104f400 <+0>:  sub    rsp, 0x28
    0x104f404 <+4>:  mov    qword ptr [rsp + 0x20], rbp
    0x104f409 <+9>:  lea    rbp, [rsp + 0x20]

** 6        var a = &T{}

    0x104f40e <+14>: mov    qword ptr [rsp], 0x0
    0x104f416 <+22>: lea    rax, [rsp]
    0x104f41a <+26>: mov    qword ptr [rsp + 0x18], rax
    0x104f41f <+31>: test   al, byte ptr [rax]
    0x104f421 <+33>: mov    qword ptr [rsp], 0x0
    0x104f429 <+41>: mov    rax, qword ptr [rsp + 0x18]
    0x104f42e <+46>: mov    qword ptr [rsp + 0x10], rax

** 7        a.age += 1

    0x104f433 <+51>: test   al, byte ptr [rax]
    0x104f435 <+53>: mov    rax, qword ptr [rax]
    0x104f438 <+56>: mov    qword ptr [rsp + 0x8], rax
    0x104f43d <+61>: mov    rcx, qword ptr [rsp + 0x10]
    0x104f442 <+66>: test   al, byte ptr [rcx]
    0x104f444 <+68>: inc    rax
    0x104f447 <+71>: mov    qword ptr [rcx], rax
-> 5    func main() {

di2`main.main:
->  0x104f400 <+0>:  sub    rsp, 0x20
    0x104f404 <+4>:  mov    qword ptr [rsp + 0x18], rbp
    0x104f409 <+9>:  lea    rbp, [rsp + 0x18]

** 6        var a = new(T)

    0x104f40e <+14>: mov    qword ptr [rsp], 0x0
    0x104f416 <+22>: lea    rax, [rsp]
    0x104f41a <+26>: mov    qword ptr [rsp + 0x10], rax

** 7        a.age += 1

    0x104f41f <+31>: test   al, byte ptr [rax]
    0x104f421 <+33>: mov    rax, qword ptr [rsp]
    0x104f425 <+37>: mov    qword ptr [rsp + 0x8], rax
    0x104f42a <+42>: mov    rcx, qword ptr [rsp + 0x10]
    0x104f42f <+47>: test   al, byte ptr [rcx]
    0x104f431 <+49>: inc    rax
    0x104f434 <+52>: mov    qword ptr [rcx], rax

兩種代碼反編譯出來(lái)的匯編不一致,可以看到第一種比第二種多要了 8 個(gè)字節(jié)的棧空間。可以猜測(cè)實(shí)際上第一種寫法是分兩部走:

  1. T{};2.& 取地址

go build 不帶 gcflags 參數(shù)時(shí),兩者出來(lái)的匯編代碼就是完全一致的了。感興趣的同學(xué)可以自行驗(yàn)證。

查看 go 的 interface 的數(shù)據(jù)結(jié)構(gòu)

go 的 interface 一直是一個(gè)比較讓人糾結(jié)的數(shù)據(jù)結(jié)構(gòu),官方和信徒們從 14 年就一直在花不少篇幅跟你講,怎么判斷 interface 和 nil,我們這個(gè)設(shè)計(jì)是這樣的 blabla。不過(guò)我始終覺得 go 的 interface 設(shè)計(jì)是有點(diǎn)問(wèn)題的,只不過(guò)這幫 unix 老古董們不想承認(rèn)。。。

先來(lái)看一些例子吧:

package main

import (
    "bytes"
    "fmt"
    "io"
)

var (
    a *bytes.Buffer = nil
    b io.Writer
)

func set(v *bytes.Buffer) {
    if v == nil {
        fmt.Println("v is nil")
    }
    b = v
}

func get() {
    if b == nil {
        fmt.Println("b is nil")
    } else {
        fmt.Println("b is not nil")
    }
}

func main() {
    set(nil)
    get()
}

例子二(來(lái)自公司同事):

package main

import (
    "fmt"
    "io"
    "os"
    "unsafe"
)

var (
    v  interface{}
    r  io.Reader
    f  *os.File
    fn os.File
)

func main() {
    fmt.Println(v == nil)
    fmt.Println(r == nil)
    fmt.Println(f == nil)
    v = r
    fmt.Println(v == nil)
    v = fn
    fmt.Println(v == nil)
    v = f
    fmt.Println(v == nil)
    r = f
    fmt.Println(r == nil)
}

可以自己運(yùn)行一下看看結(jié)果。有很多文章會(huì)講,interface 包含有 type 和 data 兩個(gè)元素,只有兩者均為 nil 的時(shí)候才是真的 nil,然后再給你灌輸了很多理由為什么要這么設(shè)計(jì)。甚至還援引了 Rob Pike 的某個(gè) ppt。

對(duì)設(shè)計(jì)的吐槽先打住,我們看看 interface 在運(yùn)行期到底是一個(gè)什么樣的東西:

(lldb) p v
(interface {}) main.v = {
  _type = 0x0000000000000000
  data = 0x0000000000000000
}
(lldb) p r
(io.Reader) main.r = {
  tab = 0x0000000000000000
  data = 0x0000000000000000
}
(lldb) p f
(*os.File) main.f = 0x0000000000000000

這里可以看到,在 golang 中空 interface 和非空 interface 在數(shù)據(jù)結(jié)構(gòu)上也是有差別的? interface 就只有 runtime._type 和 void 指針組成。而非空 interface 則是 runtime.itab 和 void 指針組成。

把 *os.File 分別賦值給空 interface 和 io.Reader 類型的接口變量之后。我們看看這個(gè) runtime._type 和 runtime.itab 都變成什么樣了:

(lldb) p v
(interface {}) main.v = {
  _type = 0x00000000010be0a0
  data = 0x0000000000000000
}

(lldb) p *r.tab
(runtime.itab) *tab = {
  inter = 0x00000000010ad520
  _type = 0x00000000010be0a0
  link = 0x0000000000000000
  hash = 871609668
  bad = false
  inhash = true
  unused = ([0] = 0, [1] = 0)
  fun = ([0] = 0x000000000106d610)
}

非空 interface 的 _type 是存儲(chǔ)在 tab 字段里了。除此之外,非空 interface 本身的類型(這里是 io.Reader)存儲(chǔ)在 inter 字段中:

(runtime.interfacetype) *inter = {
  typ = {
    size = 0x0000000000000010
    ptrdata = 0x0000000000000010
    hash = 3769182245
    tflag = 7
    align = 8
    fieldalign = 8
    kind = 20
    alg = 0x000000000113cd80
    gcdata = 0x00000000010d55f6
    str = 12137
    ptrToThis = 45152
  }
  pkgpath = {
    bytes = 0x0000000001094538
  }
  mhdr = (len 1, cap 1) {
    [0] = (name = 1236, ityp = 90528)
  }
}

此外,非空 interface 還會(huì)在 itab 的 fun 數(shù)組里存儲(chǔ)函數(shù)列表。

這里會(huì)有一個(gè)非常蛋疼的地方,如果你把一個(gè)非空 interface 類型的 nil 值的 interface 變量賦值給一個(gè)空 interface 類型的變量,那么就會(huì)得到一個(gè)非空類型的非空 interface 變量。

這絕對(duì)是 go 的設(shè)計(jì)缺陷。。。

現(xiàn)在為了避免判斷時(shí)候的失誤,也有人會(huì)用 reflect.ValueOf(v) 來(lái)判斷一個(gè) interface 是否為 nil。但也會(huì)比較別扭。

學(xué)習(xí) go 的 channel

來(lái)一個(gè)簡(jiǎn)單的 demo:

package main

func main() {
    var a = make(chan int, 4)
    a <- 1
    a <- 1
    a <- 1
    a <- 1
    close(a)
    println()
}

打上斷點(diǎn),查看 a 的結(jié)構(gòu):

* 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* 類型,類似 c/c艸,這種類型需要用 x 指令來(lái)讀取內(nèi)容:

(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 中寫入一個(gè) 1 之后,a.buf 中的內(nèi)容發(fā)生了變化。同時(shí),a 中的 sendx 和 qcount 也都發(fā)生了變化:

(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)
}

這樣就可以非常方便地結(jié)合代碼,觀察 channel 的發(fā)送和接收行為。其實(shí)從 debugger 里得到的信息都非常的直觀,比看圖表要直觀得多。比如這里我們可以直接看到 lock 字段。這也說(shuō)明 channel 本身為了并發(fā)安全是帶鎖的。

recvq 和 sendq 是用來(lái)維護(hù)發(fā)送接收時(shí)被阻塞需要休眠的 goroutine 列表。

elemtype 是 runtime._type 類型,可以看到 channel 中的元素類型信息。

close(a) 以后再看看結(jié)構(gòu):

(chan int) a = 0x000000c42007a000
(lldb) p *a
(hchan<int>) *a = {
  qcount = 4
  dataqsiz = 4
  buf = 0x000000c42007a060
  elemsize = 8
  closed = 1 // 重點(diǎn)在這里
  elemtype = 0x0000000001055ee0
  sendx = 0
  recvx = 0
  recvq = {
    first = 0x0000000000000000
    last = 0x0000000000000000
  }
  sendq = {
    first = 0x0000000000000000
    last = 0x0000000000000000
  }
  lock = (key = 0x0000000000000000)
}

比畫一堆圖不知道高到哪里去了。

再嘗試在 a 上阻塞幾個(gè) 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 就是個(gè) sudog 的雙向鏈表,沒有什么難理解的~

確認(rèn) panic 的現(xiàn)場(chǎng)

程序里有時(shí)候會(huì)有這種代碼:

someFunction(r.A, *r.B, *r.C, *r.D, r.E, *r.F)

然后在這里 panic 了。但是 go 只會(huì)告訴你 nil pointer deference,卻不會(huì)告訴你是哪個(gè) nil pointer deference。著實(shí)蛋疼。

這個(gè)就是用 debugger 最基本斷點(diǎn)功能了。如果是用 delve,斷點(diǎn)可以用很多種方法來(lái)設(shè)置,比如 function+行號(hào),文件名+行號(hào),如果有歧義,delve 也會(huì)告訴你具體要怎么來(lái)消除歧義。

(lldb) n
Process 22595 stopped
* thread #1, stop reason = step over
    frame #0: 0x000000000104c344 nilPointer`main.main at nilPointer.go:16
   13   }
   14
   15   func main() {
-> 16       var t = T{A: 1}
   17       test(t.A, *t.B, *t.C, *t.D, t.E, *t.F)
   18   }
Target 0: (nilPointer) stopped.
(lldb) n
Process 22595 stopped
* thread #1, stop reason = step over
    frame #0: 0x000000000104c365 nilPointer`main.main at nilPointer.go:17
   14
   15   func main() {
   16       var t = T{A: 1}
-> 17       test(t.A, *t.B, *t.C, *t.D, t.E, *t.F)
   18   }
Target 0: (nilPointer) stopped.
(lldb) p t
(main.T) t = {
  A = 1
  B = 0x0000000000000000
  C = 0x0000000000000000
  D = 0x0000000000000000
  E = 0
  F = 0x0000000000000000
}

哪里是 nil 一目了然~

string 和 byte 之間到底有沒有進(jìn)行相互轉(zhuǎn)換

例子:

package main

func main() {

    var str = "abcde"
    var b = []byte("defg")

    println(str)
    println(string(b))

}

還是看反編譯的結(jié)果:


** 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

重點(diǎn)在這里的

    0x104cf35 <+101>: call   0x1038390                 ; runtime.stringtoslicebyte at string.go:146

runtime 里還有一個(gè)對(duì)應(yīng)的:

    0x104c624 <+196>: call   0x10378c0                 ; runtime.slicebytetostring at string.go:72

有了這樣的手段,如果別人和你說(shuō) go 會(huì)優(yōu)化 string 和 []byte 之間的轉(zhuǎn)換。你就可以隨時(shí)掏出 debugger 來(lái)打他的臉了。

我程序的 select 到底被翻譯成什么樣的執(zhí)行過(guò)程了

select 是 golang 提供的一種特權(quán)語(yǔ)法,實(shí)現(xiàn)的功能比較神奇。先不說(shuō)行為怎么樣。這種特權(quán)語(yǔ)法實(shí)際上最終一定會(huì)被翻譯成某種匯編指令或者 runtime 的內(nèi)置函數(shù)。

用反匯編來(lái)看一眼。

-> 6        select {

->  0x104e3d5 <+117>: mov    qword ptr [rsp + 0x38], 0x0
    0x104e3de <+126>: lea    rdi, [rsp + 0x40]
    0x104e3e3 <+131>: xorps  xmm0, xmm0
    0x104e3e6 <+134>: lea    rdi, [rdi - 0x10]
    0x104e3ea <+138>: mov    qword ptr [rsp - 0x10], rbp
    0x104e3ef <+143>: lea    rbp, [rsp - 0x10]
    0x104e3f4 <+148>: call   0x1048d5a                 ; runtime.duffzero + 250 at duff_amd64.s:87
    0x104e3f9 <+153>: mov    rbp, qword ptr [rbp]
    0x104e3fd <+157>: lea    rax, [rsp + 0x38]
    0x104e402 <+162>: mov    qword ptr [rsp], rax
    0x104e406 <+166>: mov    qword ptr [rsp + 0x8], 0xb8
    0x104e40f <+175>: mov    dword ptr [rsp + 0x10], 0x3
    0x104e417 <+183>: call   0x10305d0                 ; runtime.newselect at select.go:60

** 6        select {

    0x104e425 <+197>: mov    rax, qword ptr [rsp + 0x30]

** 6        select {

    0x104e445 <+229>: mov    rax, qword ptr [rsp + 0x28]

** 6        select {

    0x104e46a <+266>: lea    rax, [rsp + 0x38]
    0x104e46f <+271>: mov    qword ptr [rsp], rax
    0x104e473 <+275>: call   0x1030b10                 ; runtime.selectgo at select.go:202
    0x104e478 <+280>: mov    rax, qword ptr [rsp + 0x8]
    0x104e47d <+285>: mov    qword ptr [rsp + 0x20], rax

看起來(lái) select 被翻譯成了多段匯編代碼。說(shuō)明這個(gè)函數(shù)稍微復(fù)雜一些,不過(guò)反匯編過(guò)程已經(jīng)幫我們定位到了 select 被翻譯成的函數(shù)的位置。

實(shí)際上 select 的執(zhí)行過(guò)程為: newselect->selectsend/selectrecv->selectgo 這幾個(gè)過(guò)程。如果你的程序是下面這樣的:

for {
  select {
     case <-ch:
     case ch2<-1:
     default:
  }
}

在每次進(jìn)入 for 循環(huán)的時(shí)候,runtime 里的 hselect 結(jié)構(gòu)都會(huì)重新創(chuàng)建。也就是說(shuō)寫一個(gè)有 default case 的無(wú)限循環(huán),不僅僅是你知道的 cpu 占用爆炸,實(shí)際上還在不斷地在堆上分配、釋放、分配、釋放空間。感覺這里官方應(yīng)該是可以做一些優(yōu)化的,不知道為什么邏輯這么原始。(當(dāng)然,在 go 語(yǔ)言學(xué)習(xí)筆記里看到雨痕老師也吐槽他們的代碼寫得渣哈哈哈。

正在運(yùn)行的 goroutine 到底是阻塞在什么地方了

golang 中常見的內(nèi)存泄露套路是這樣的:


func main() {

   var ch chan int
   go func() {
      select {
         case <-ch:
      }
   }()
}

監(jiān)聽了一個(gè)永遠(yuǎn)阻塞的 channel,或者向一個(gè)沒有接收方的 channel 發(fā)數(shù)據(jù),如果這些事情沒有發(fā)生在主 goroutine 里的話,在 runtime 的 checkdead 函數(shù)中不會(huì)認(rèn)為這是個(gè) deadlock。而這樣的 goroutine 創(chuàng)建過(guò)程往往在 for 循環(huán)里。

公司內(nèi)的某個(gè)程序就曾經(jīng)在線下 debug 的時(shí)候發(fā)現(xiàn)每次來(lái)一個(gè)請(qǐng)求,就會(huì)導(dǎo)致 goroutine 總數(shù) +1。這顯然是不正常的。在 goroutine 達(dá)到一定數(shù)量之后,可以適用 delve attach 到你的進(jìn)程,然后運(yùn)行:

goroutines

一下就看到你泄露的 goroutine 都是卡在什么地方了。

當(dāng)然,如果你的程序開了 pprof,那通過(guò)網(wǎng)頁(yè)來(lái)看倒是更為方便。

之前公司內(nèi)的某個(gè)庫(kù)在找不到 disf 的 ip 的時(shí)候就會(huì)阻塞在 lib 的 channel 上。用這個(gè)辦法可以非?斓恼业絾(wèn)題根結(jié)。不用像某些程序員一樣到處加 fmt.Println 了。

程序的 cpu 占用非常高,似乎在哪里有死循環(huán)

這個(gè)問(wèn)題有兩個(gè)工具可以用,一個(gè)是 perf,一個(gè)是 debugger。

sudo perf top

可以找到死循環(huán)所處的位置,這個(gè)在之前寫的文章中有過(guò)涉及了。這里就不再贅述。

還有一種死循環(huán),但是程序本身沒死掉的,那就可以直接用 dlv attach 進(jìn)去了,基本上切換至可疑的 goroutine,跟個(gè)十幾步就可以找到問(wèn)題所在,當(dāng)然,結(jié)合 perf 來(lái)看更高效。這個(gè)可以參考之前定位 jsoniter 時(shí)候的步驟:https://github.com/gin-gonic/gin/issues/1086。

怎么一直觀察某一個(gè)變量的變化過(guò)程

也很簡(jiǎn)單,在希望觀察的地方打上斷點(diǎn),如果斷點(diǎn) id 是 13,那么用 delve 的 on 命令:

on 13 print xxx

即可

(dlv) n
> main.main() ./for.go:6 (hits goroutine(1):11 total:11) (PC: 0x44d694)
    count: 45
     1: package main
     2:
     3: func main() {
     4:     count:=0
     5:     for i:=0;i<10000;i++ {
=>   6:         count+=i
     7:     }
     8:     println(count)
     9: }
(dlv) n

我的程序只有運(yùn)行到 for 循環(huán)的第 1000 次疊代的時(shí)候才會(huì)出 bug,我怎么在第 1000 次循環(huán)的時(shí)候才設(shè)置這個(gè)斷點(diǎn)

用 delve 很簡(jiǎn)單:

[email protected]:~$ dlv exec ./for
Type 'help' for list of commands.
(dlv) b for.go:6
Breakpoint 1 set at 0x44d694 for main.main() ./for.go:6
(dlv) cond 1 i==1000 ////// => 重點(diǎn)在這里
(dlv) r
Process restarted with PID 29024
(dlv) c
> main.main() ./for.go:6 (hits goroutine(1):1 total:1) (PC: 0x44d694)
     1: package main
     2:
     3: func main() {
     4:     count:=0
     5:     for i:=0;i<10000;i++ {
=>   6:         count+=i
     7:     }
     8:     println(count)
     9: }
(dlv) p i
1000
(dlv) p count
499500

 

來(lái)自:https://gocn.io/article/657

標(biāo)簽: swap 安全 代碼 權(quán)限

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:未來(lái)Web開發(fā)趨勢(shì)報(bào)告

下一篇:你想知道的2018年軟件開發(fā)“10件事”