探究vscode debug流程,解決無法運行go程序的問題

問題描述

vscode 無法以 run 模式運行 go 項目(只能以 debug 模式調試),並且有如下報錯。

圖中被遮蓋的部分是項目內的 package,並非第三方 package,也就是說在以 run 模式運行 go 項目時無法找到其他的 go 文件,只能找到入口文件。

初步排查

找不到其他文件,首先想到的是 GO_PATH 的問題,但是項目使用了 go mod,允許在 GO_PATH 之外的路徑創建項目,所以這個懷疑點排除。接下來懷疑 vscode 的配置有問題,每個 vscode 項目中都有 .launch.json 文件,配置運行代碼時的環境,下面是項目中的 .launch.json。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${workspaceRoot}/src/main.go",
            "env": {},
            "args": []
        }
    ]
}

可以看到 .launch.json 裏沒有指定程序的工作目錄,debug 模式和 run 模式會不會默認的工作路徑不同呢?於是在 main 函數裏使用 os.Getwd() 打印一下當前的路徑,結果如下:

  • debug 模式:項目所在目錄
  • run 模式:用戶目錄

基本可以確認,run 模式下的工作路徑設置不正確,導致找不到路徑。在 .launch.json 中加入 cwd 參數,手動填入項目路徑。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${workspaceRoot}/src/main.go",
            "cwd": "${workspaceRoot}",
            "env": {},
            "args": []
        }
    ]
}

但是修改 .launch.json 後運行程序,輸出的工作目錄仍然是用戶目錄,cwd 參數並沒有生效。

探究 vscode 的 debug 流程

至此,bug 的氣息越來越濃厚,cwd 參數沒有生效,肯定有問題!

一不做二不休,索性看看 vscode 的調試流程吧,用一個很暴力的方式,看看點擊運行按鈕後,vscode 到底是如何運行 go 程序的。

package main
import "time"
func main() {
    time.Sleep(10000000000)
}

運行程序後,使用 ps -ef|grep go 查看進程。

截圖中三個進程從上到下均是父子關係,也就是說在 vscode 中即便使用 run 模式運行,也不是直接執行 go run xxxx.go,這與 Goland 等其他 IDE 的行爲是不同的。vscode 首先調用了 language server 中的 node,執行了 go extention(vscode 的 go 擴展,安裝後才支持 go 語言項目)中的一個 goDebug.js,而後 goDebug.js 中調用了 go run xxxx.go。(圖中 /tmp 路徑下的 main 文件是 go run 執行過程中生成的二進制文件)

接下來查看 goDebug.js 的邏輯,找到了調用 go run 的代碼。

this.debugProcess = spawn(getBinPathWithPreferredGopath('go', []), runArgs, { env });

查看代碼上面幾行的邏輯,根據參數的命名,可以猜測出來,.launch.json 中的配置在這裏是可以獲取到的。接下來直接修改 js 文件,進行調試,證實上述的猜測,由於我們無法直接看到 node goDebug.js 的輸出,所以通過寫入文件的方式進行調試。

fs.writeFile('test.log', this.debugProcess.cwd(), function (err) {}

加入這句後再次運行,我們可以看到 test.log 文件中已經打印出了這個進程的工作路徑,也就是 go run 的工作路徑,是用戶目錄。至此,可以將問題縮小到:在 node 調用 go run 時沒有將 .launch.json 文件中的 cwd 傳給子進程(go run)。

spawn 是 nodejs 中的函數,看一下 spawn 的文檔可以發現,spawn 有三個參數 child_process.spawn(command[, args][, options]) 第三個參數 options 中可以指定 cwd 工作路徑。而 goDebug.js 這段啓動子進程的代碼並沒有設置 cwd,只設置了env 參數,這就是 run 模式無法運行 go 程序的原因。

解決方案

在發現這個問題時,vscode go extention的最新版本是0.13,這個問題暫時只能通過修改 goDebug.js 的源碼解決,如下圖所示加入註釋中的代碼,將 cwd 參數傳入子進程,就可以解決問題。

同時,這個 bug 已經被解決,可以參考 ISSUE #3096,程序員在解決另一個問題這個 ISSUE 的問題時,“順手”把 cwd 的問題修復了。在 vscode go extention 0.14版發佈後(已發佈),將 go extension 更新到最新版就可以正常運行和調試 go 項目了。

參考資料

Debugging in Visual Studio Code

Node.js v13.13.0 Documentation

Debug: add "go run ." support #3096

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章