是否要运行此应用程序_使用Delve调试Go应用程序
調(diào)試器
任何編程語言中最簡(jiǎn)單的調(diào)試形式是使用打印語句或日志來寫入標(biāo)準(zhǔn)輸出。這肯定沒有問題,但是當(dāng)我們的應(yīng)用程序規(guī)模增加并且邏輯變得更加復(fù)雜時(shí),這種方式變得極其困難。將打印語句添加到應(yīng)用程序的每個(gè)代碼路徑并不容易。這是調(diào)試器派上用場(chǎng)的地方。調(diào)試器可幫助我們使用斷點(diǎn)和許多其他功能來跟蹤程序的執(zhí)行路徑。Delve是Go的一種此類調(diào)試器。在本教程中,我們將學(xué)習(xí)如何使用Delve調(diào)試Go應(yīng)用程序。
安裝Delve
首先請(qǐng)確保您位于一個(gè)不包含go.mod文件的目錄中。我比較喜歡我的Documents目錄。
cd ~/Documents/接下來,讓我們?cè)O(shè)置GOBIN環(huán)境變量。此環(huán)境變量指定Delve二進(jìn)制文件的安裝位置。如果GOBIN已經(jīng)設(shè)置,請(qǐng)?zhí)^此步驟。您可以通過運(yùn)行以下命令檢查是否設(shè)置了GOBIN。
go env | grep GOBIN如果以上命令顯示GOBIN="",則表示GOBIN未設(shè)置。請(qǐng)運(yùn)行export GOBIN=~/go/bin/命令設(shè)置GOBIN。
讓我們通過運(yùn)行添加GOBIN到PATH export PATH=$PATH:~/go/bin
對(duì)于macOS,需要使用Xcode命令行開發(fā)人員工具來運(yùn)行Delve。請(qǐng)運(yùn)行xcode-select --install以安裝命令行工具。Linux用戶可以跳過此步驟。
現(xiàn)在我們開始安裝Delve。請(qǐng)運(yùn)行
go get github.com/go-delve/delve/cmd/dlv安裝delve。運(yùn)行此命令后,請(qǐng)通過運(yùn)行來測(cè)試安裝dlv version。成功安裝后,它將打印Delve的版本。
Delve Debugger Version: 1.4.0 Build: $Id: 67422e6f7148fa1efa0eac1423ab5594b223d93b開始使用Delve
讓我們編寫一個(gè)簡(jiǎn)單的程序,然后使用Delve開始對(duì)其進(jìn)行調(diào)試。
讓我們使用以下命令為示例程序創(chuàng)建目錄。
mkdir ~/Documents/debugSample在我們剛剛創(chuàng)建的目錄內(nèi)創(chuàng)建一個(gè)文件main.go,內(nèi)容如下。
package mainimport ( "fmt")func main() { arr := []int{101, 95, 10, 188, 100} max := arr[0] for _, v := range arr { if v > max { max = v } } fmt.Printf("Max element is %d", max)}上面的程序?qū)⒋蛴∏衅琣rr的最大元素。運(yùn)行上面的程序?qū)⑤敵?/p>Max element is 188
現(xiàn)在我們準(zhǔn)備調(diào)試程序。讓我們轉(zhuǎn)到debugSample目錄cd ~/Documents/debugSample。之后,鍵入以下命令以啟動(dòng)Delve。
dlv debug上面的命令將開始調(diào)試當(dāng)前目錄中的main軟件包。鍵入上面的命令后,您可以看到終端已更改為(dlv)提示。如果您看到此更改,則表明調(diào)試器已成功啟動(dòng)并等待我們的命令:)。
讓我們啟動(dòng)第一個(gè)命令。
在dlv提示符下,鍵入continue。
(dlv) continue該continue命令將運(yùn)行程序,直到出現(xiàn)斷點(diǎn)或程序完成為止。由于我們沒有定義任何斷點(diǎn),因此該程序?qū)⒁恢边\(yùn)行到完成。
Max element is 188 Process 1733 has exited with status 0如果看到以上輸出,則調(diào)試器已運(yùn)行,程序已完成:)。但這對(duì)我們沒有任何用處。讓我們繼續(xù)添加幾個(gè)斷點(diǎn),并觀察調(diào)試器如何發(fā)揮作用。
創(chuàng)建斷點(diǎn)
斷點(diǎn)在指定的行處暫停程序的執(zhí)行。當(dāng)執(zhí)行暫停時(shí),我們可以將命令發(fā)送到調(diào)試器以打印變量的值,查看程序的堆棧跟蹤,等等。
下面提供了創(chuàng)建斷點(diǎn)的語法
(dlv) break filename:lineno上面的命令將在filename文件中的lineno行創(chuàng)建一個(gè)斷點(diǎn)。
讓我們?cè)谛刑?hào)上添加一個(gè)斷點(diǎn)。我們的main.go第9行
(dlv) break main.go:9運(yùn)行上述命令后,您將看到輸出Process 1733 has exited with status 0。實(shí)際上沒有添加斷點(diǎn)。這是因?yàn)閏ontinue當(dāng)時(shí)沒有斷點(diǎn),所以我們?cè)谳^早運(yùn)行時(shí)就退出了程序。讓我們重新啟動(dòng)程序,然后嘗試再次設(shè)置斷點(diǎn)。
(dlv) restartProcess restarted with PID 2028 (dlv) break main.go:9Breakpoint 1 set at 0x10c16e4 for main.main() ./main.go:9該restart命令重新啟動(dòng)程序,然后該break命令設(shè)置斷點(diǎn)。上面的輸出確認(rèn)斷點(diǎn)1設(shè)置在main.go中的第9行。
現(xiàn)在讓continue為我們繼續(xù)程序,并檢查調(diào)試器是否在斷點(diǎn)處暫停程序。
(dlv) continue> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x10c16e4) 4: "fmt" 5: ) 6: 7: func main() { 8: arr := []int{101, 95, 10, 188, 100}=> 9: max := arr[0] 10: for _, v := range arr { 11: if v > max { 12: max = v 13: } 14: }continue執(zhí)行完之后,我們可以看到調(diào)試器已在第9行暫停了我們的程序。
列出斷點(diǎn)
(dlv) breakpoints上面的命令列出了應(yīng)用程序的當(dāng)前斷點(diǎn)。
(dlv) breakpointsBreakpoint runtime-fatal-throw at 0x102de10 for runtime.fatalthrow() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:820 (0) Breakpoint unrecovered-panic at 0x102de80 for runtime.fatalpanic() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:847 (0) print runtime.curg._panic.argBreakpoint 1 at 0x10c16e4 for main.main() ./main.go:9 (1)您可能會(huì)驚訝地發(fā)現(xiàn),除了我們添加的斷點(diǎn)之外,還有另外兩個(gè)斷點(diǎn)。delve會(huì)添加其他兩個(gè)斷點(diǎn),以確保當(dāng)碰到?jīng)]有使用restore處理的運(yùn)行時(shí)緊急情況時(shí),調(diào)試會(huì)話不會(huì)突然結(jié)束。
打印變量
程序的執(zhí)行已在第9行暫停。print是用于打印變量值的命令。讓我們使用print并在切片的第0個(gè)索引處打印元素。
(dlv) print arr[0]運(yùn)行上面的命令將打印101。
請(qǐng)注意,如果嘗試打印max,則會(huì)得到一個(gè)垃圾值。
(dlv) print max824634294736這是因?yàn)槌绦蛟谛刑?hào)9之前已暫停。因此打印會(huì)max打印一些隨機(jī)的垃圾值。要打印max的實(shí)際值,我們應(yīng)該移至程序的下一行。可以使用next命令來完成。
(dlv) next調(diào)試器將移至下一行,并輸出
> main.main() ./main.go:10 (PC: 0x10c16ee) 5: ) 6: 7: func main() { 8: arr := []int{101, 95, 10, 188, 100} 9: max := arr[0]=> 10: for _, v := range arr { 11: if v > max { 12: max = v 13: } 14: } 15: fmt.Printf("Max element is %d", max)現(xiàn)在,如果我們嘗試(dlv) print max,可以看到輸出101。
next命令可用于逐行瀏覽程序。
如果繼續(xù)輸入next,則可以看到調(diào)試器在程序中逐行運(yùn)行。當(dāng)循環(huán)中的第一個(gè)for循環(huán)重復(fù)一次時(shí)。第10行結(jié)束了, next將引導(dǎo)我們完成下一個(gè)迭代,最終程序?qū)⒔K止。
打印表達(dá)式
print也可以用于打印表達(dá)式。例如,如果我們要查找的值為max + 10,則可以使用print。
讓我們?cè)谕瓿蒮or循環(huán)外添加另一個(gè)斷點(diǎn)。
(dlv) break main.go:15上面的命令在行號(hào)15上添加了另一個(gè)斷點(diǎn)。此時(shí)max的計(jì)算已完成。
鍵入continue,程序?qū)⒃诖藬帱c(diǎn)處停止。
print max + 10命令將輸出198。
清除斷點(diǎn)
clear是清除單個(gè)斷點(diǎn)的命令,clearall是清除程序中所有斷點(diǎn)的命令。
首先讓我們列出應(yīng)用程序中的斷點(diǎn)。
(dlv) breakpointsBreakpoint runtime-fatal-throw at 0x102de10 for runtime.fatalthrow() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:820 (0) Breakpoint unrecovered-panic at 0x102de80 for runtime.fatalpanic() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:847 (0) print runtime.curg._panic.argBreakpoint 1 at 0x10c16e4 for main.main() ./main.go:9 (1) Breakpoint 2 at 0x10c1785 for main.main() ./main.go:15 (1)我們有兩個(gè)斷點(diǎn)分別是1和2
如果我們運(yùn)行clear 1,它將刪除斷點(diǎn)1。
(dlv) clear 1Breakpoint 1 cleared at 0x10c16e4 for main.main() ./main.go:9如果運(yùn)行clearall,它將刪除所有的斷點(diǎn)。現(xiàn)在我們只剩下一個(gè)斷點(diǎn)2。
(dlv) clearallBreakpoint 2 cleared at 0x10c1785 for main.main() ./main.go:15從上面的輸出中,我們可以看到剩余的一個(gè)斷點(diǎn)也被清除了。如果我們現(xiàn)在執(zhí)行continue命令,程序?qū)⒋蛴ax的值并終止。
(dlv) continueMax element is 188 Process 3095 has exited with status 0踏入和退出功能
可以使用Delve進(jìn)入或離開函數(shù)。讓我們嘗試借助示例來理解這一點(diǎn)。
package mainimport ( "fmt")func max(arr []int) int { max := arr[0] for _, v := range arr { if v > max { max = v } } return max}func main() { arr := []int{101, 95, 10, 188, 100} m := max(arr) fmt.Printf("Max element is %d", m)}我已經(jīng)修改了到目前為止一直在使用的程序,并將查找切片最大元素的邏輯移到了名為max的函數(shù)中。
使用退出命令Delve (dlv) q退出調(diào)試,把main.go替換為上面的程序,然后使用命令再次開始調(diào)試dlv debug。
讓我們?cè)?8行上添加一個(gè)斷點(diǎn)。
b是添加斷點(diǎn)的簡(jiǎn)寫。
(dlv) b main.go:18(dlv) continue我們?cè)诘?8行添加了斷點(diǎn),并繼續(xù)執(zhí)行程序。運(yùn)行以上命令將打印,
> main.main() ./main.go:18 (hits goroutine(1):1 total:1) (PC: 0x10c17ae) 13: } 14: return max 15: } 16: func main() { 17: arr := []int{101, 95, 10, 188, 100}=> 18: m := max(arr) 19: fmt.Printf("Max element is %d", m) 20: }程序執(zhí)行已在第18行暫停。跟我們預(yù)期的一樣。現(xiàn)在我們有兩個(gè)選擇。
- 繼續(xù)深入調(diào)試max函數(shù)的功能
- 跳過max函數(shù),然后移至下一行。
根據(jù)我們的要求,我們可以選擇其中一種。
首先,讓我們跳過max函數(shù),移至下一行。為此,您可以運(yùn)行next,調(diào)試器將自動(dòng)移至下一行。默認(rèn)情況下,Delve不會(huì)更深入地調(diào)試函數(shù)調(diào)用。
(dlv) next> main.main() ./main.go:19 (PC: 0x10c17d3) 14: return max 15: } 16: func main() { 17: arr := []int{101, 95, 10, 188, 100} 18: m := max(arr)=> 19: fmt.Printf("Max element is %d", m) 20: }您可以從上面的輸出中看到調(diào)試器已移至下一行。
鍵入continue,程序?qū)⑼瓿蓤?zhí)行。
讓我們學(xué)習(xí)如何更深入地了解max函數(shù)。
鍵入restart和continue,我們可以看到程序在已經(jīng)存在的斷點(diǎn)處再次暫停。
(dlv) restartProcess restarted with PID 5378 (dlv) continue> main.main() ./main.go:18 (hits goroutine(1):1 total:1) (PC: 0x10c17ae) 13: } 14: return max 15: } 16: func main() { 17: arr := []int{101, 95, 10, 188, 100}=> 18: m := max(arr) 19: fmt.Printf("Max element is %d", m) 20: }現(xiàn)在輸入step,我們可以看到調(diào)試器已經(jīng)移入max函數(shù)中了。
(dlv) step> main.max() ./main.go:7 (PC: 0x10c1650) 2: 3: import ( 4: "fmt" 5: ) 6: => 7: func max(arr []int) int { 8: max := arr[0] 9: for _, v := range arr { 10: if v > max { 11: max = v 12: }鍵入next,控件將移至max函數(shù)的第一行。
(dlv) next> main.max() ./main.go:8 (PC: 0x10c1667) 3: import ( 4: "fmt" 5: ) 6: 7: func max(arr []int) int {=> 8: max := arr[0] 9: for _, v := range arr { 10: if v > max { 11: max = v 12: } 13: }如果繼續(xù)輸入next,則可以逐步執(zhí)行max函數(shù)的執(zhí)行路徑。
您可能想知道是否可以不通過max函數(shù)中的每一行而返回到main。使用stepout命令可以做到這一點(diǎn)。
(dlv) stepout> main.main() ./main.go:18 (PC: 0x10c17c9)Values returned: ~r1: 188 13: } 14: return max 15: } 16: func main() { 17: arr := []int{101, 95, 10, 188, 100}=> 18: m := max(arr) 19: fmt.Printf("Max element is %d", m) 20: }鍵入stepout后,控件將返回到main。現(xiàn)在您可以在main中繼續(xù)調(diào)試。
打印堆棧跟蹤
調(diào)試時(shí)需要的一個(gè)非常重要的功能是打印程序的當(dāng)前堆棧跟蹤。這對(duì)于查找當(dāng)前代碼執(zhí)行路徑很有用。stack是用于打印當(dāng)前堆棧跟蹤的命令。
讓我們清除所有斷點(diǎn),在第11行處添加一個(gè)新的斷點(diǎn),并打印程序的當(dāng)前堆棧跟蹤。
(dlv) restart(dlv) clearall(dlv) b main.go:11(dlv) continue當(dāng)程序在斷點(diǎn)處暫停時(shí),鍵入
(dlv) stack它將輸出程序的當(dāng)前堆棧跟蹤。
0 0x00000000010c16e8 in main.max at ./main.go:111 0x00000000010c17c9 in main.main at ./main.go:182 0x000000000102f754 in runtime.main at /usr/local/Cellar/go/1.13.7/libexec/src/runtime/proc.go:2033 0x000000000105acc1 in runtime.goexit at /usr/local/Cellar/go/1.13.7/libexec/src/runtime/asm_amd64.s:1357到目前為止,我們已經(jīng)介紹了Delve的基本命令,來幫助使用Delve調(diào)試應(yīng)用程序。在接下來的教程中,我們將介紹Delve的高級(jí)功能,例如調(diào)試goroutine,將調(diào)試器附加到現(xiàn)有進(jìn)程,遠(yuǎn)程調(diào)試以及使用VSCode編輯器中的Delve。
謝謝閱讀,請(qǐng)留下您的意見和反饋。
總結(jié)
以上是生活随笔為你收集整理的是否要运行此应用程序_使用Delve调试Go应用程序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1000新债中签能赚多少?附:提高新债收
- 下一篇: 招商银行信用卡退款算还款吗