如何查看GoLand调试器是否在程序中运行?

时间:2017-12-19 02:07:06

标签: go delve goland

在C#中,执行程序可以使用以下命令检测它是否在调试器中运行:

System.Diagnostics.Debugger.IsAttached

Go中是否有同等效力?我有一些超时,我想在我踩过代码时被禁用。谢谢!

我正在使用GoLand调试器。

4 个答案:

答案 0 :(得分:3)

据我所知,没有内置的方式以您描述的方式执行此操作。但是您可以使用构建标记来执行或多或少相同的操作,以指示delve调试器正在运行。您可以使用dlv参数将构建标记传递给--build-flags。这与我在How can I check if the race detector is enabled at runtime?

中描述的技术基本相同

isdelve/delve.go

// +build delve

package isdelve

const Enabled = true

isdelve/nodelve.go

// +build !delve

package isdelve

const Enabled = false

a.go

package main

import (
    "isdelve"
    "fmt"
)

func main() {
    fmt.Println("delve", isdelve.Enabled)
}

正在运行go run a.go将报告delve false并运行
dlv debug --build-flags='-tags=delve' a.go会报告delve true

您必须在GoLand用户界面的某处传递该标志。我对GoLand并不熟悉,所以我无法专门帮助你解决这个问题。

或者,您可以使用delve的set命令在启动调试器后手动设置变量。

答案 1 :(得分:1)

如果您假定使用的调试器是Delve,则可以检查Delve进程。至少要考虑两种情况(可能更多)。

  1. Delve启动了您的流程。在这种情况下,当您调用os.Getppid()来获取父进程的pid时,该进程将是Delve。
  2. Delve并未启动您的流程,但稍后进行了附加。在这种情况下,您需要查找所有正在运行的Delve进程,查看它们的命令行,并查看是否有任何使用包含“ attach”的命令行启动的,这是调用os.Getpid()的结果。这是基于以下假设:您没有找到运行与您的PID相匹配的旧PID的旧Delve。 (我忘记了操作系统重用PID的规则)。

请注意,1和2使用的os函数不同。一个获取父PID,另一个获取您的PID。

一些非常基本的代码要做1看起来像这样:

func isLaunchedByDebugger() bool {
    // gops executable must be in the path. See https://github.com/google/gops
    gopsOut, err := exec.Command("gops", strconv.Itoa(os.Getppid())).Output()
    if err == nil && strings.Contains(string(gopsOut), "\\dlv.exe") {
        // our parent process is (probably) the Delve debugger
        return true
    }
    return false
}

答案 2 :(得分:0)

对于情况2,我们可以将程序设置为等待某些信号(SIGUSR1),并在此等待期间连接调试器。
main.go的代码可以像这样:

package main

import (
    "os"
    "os/signal"
    "syscall"
    "fmt"
    "github.com/my/repo/cmd"
)

const (
    waitForSignalEnv       = "WAIT_FOR_DEBUGGER"
    debuggerPort           = "4321"
)

func main() {
    // Waiting for debugger attach in case if waitForSignalEnv!=""
    if os.Getenv(waitForSignalEnv) != "" {
        sigs := make(chan os.Signal, 1)
        goOn := make(chan bool, 1)
        signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR1)

        go func() {
            sig := <-sigs
            if sig == syscall.SIGUSR1 {
                goOn <- true
            } else if (sig == syscall.SIGTERM || sig == syscall.SIGINT ){
                fmt.Printf("Exiting ...")
                os.Exit(0)
            }
        }()     
            
        fmt.Printf("%s env is set, waiting SIGUSR1.\nYou can run remote debug in vscode and attach dlv debugger:\n\n", waitForSignalEnv)
    
        pid := os.Getpid()
        fmt.Printf("dlv attach --continue --accept-multiclient --headless --listen=:%s %d\n", debuggerPort, pid)
        fmt.Printf("\nLaunch remote debugger in vscode to port %d and then give SIGUSR1 to the process\n", debuggerPort)
        fmt.Printf("kill -SIGUSR1 %d\n", pid)
        
        <-goOn
        fmt.Printf("Continue ...")
    }
    cmd.Execute()
}
vscode的

launch.json:

{
    "name": "myprog-remote-debug",
    "type": "go",
    "request": "launch",
    "remotePath": "${env:GOPATH}/src/github.com/my/repo",
    "mode": "remote",
    "port": 4321,
    "host": "127.0.0.1",
    "program": "${env:GOPATH}/src/github.com/my/repo",   
    "showLog": true,
    "trace": "verbose" 

}

说明: 例如,我们以env WAIT_FOR_DEBUGGER = true启动程序

export WAIT_FOR_DEBUGGER=true
./myprog -f values.yaml

它将输出dlv attach ...命令和kill -SIGUSR <pid>

WAIT_FOR_DEBUGGER env is set, waiting SIGUSR1.
You can run remote debug in vscode and attach dlv debugger:

dlv attach --continue --accept-multiclient --headless --listen=:4321 556127

Launch remote debugger in vscode to port 4321 and then give SIGUSR1 to the process
kill -SIGUSR1 556127

运行上面的dlv attach ...
然后转到VS Code并运行myprog-remote-debug。在之前设置断点
然后给他kill -SIGUSR1 556127

断点将起作用

答案 3 :(得分:0)

在 Linux 上,您可以读取 /proc/self/status 文件以检索 TracerPid 字段,即调试器的 PID(如果有)。

func GetTracerPid() (int, error) {
    file, err := os.Open("/proc/self/status")
    if err != nil {
        return -1, fmt.Errorf("can't open process status file: %w", err)
    }
    defer file.Close()

    for {
        var tpid int
        num, err := fmt.Fscanf(file, "TracerPid: %d\n", &tpid)
        if err == io.EOF {
            break
        }
        if num != 0 {
            return tpid, nil
        }
    }

    return -1, errors.New("unknown format of process status file")
}

使用方法:

tpid, err := GetTracerPid()
if err != nil {
    log.Println("something went wrong", err)
} else if tpid != 0 {
    fmt.Println("we're under debugging: tracer_pid", tpid)
} else {
    fmt.Println("we're free of tracing")
}