代码之家  ›  专栏  ›  技术社区  ›  Philip Beber

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

  •  8
  • Philip Beber  · 技术社区  · 7 年前

    在C语言中,执行程序可以使用以下方法检测是否在调试器中运行:

    System.Diagnostics.Debugger.IsAttached
    

    Go中是否有等效项?我有一些超时,我想被禁用,而我正在逐步通过代码。谢谢

    我正在使用GoLand调试器。

    5 回复  |  直到 7 年前
        1
  •  9
  •   cwash    5 年前

    据我所知,没有一种内在的方法可以像你描述的那样做到这一点。但您可以使用构建标记来指示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)
    }
    

    在Goland中,您可以在“运行/调试配置”下启用此功能,方法是将以下内容添加到“Go tool参数”:

    -tags=delve
    

    Goland Run/Debug Configurations window


    如果你在Goland以外,跑步 go run a.go 将报告 delve false ,如果要单独运行dlv,请使用 dlv debug --build-flags='-tags=delve' a.go ; 这将报告 delve true .


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

        2
  •  3
  •   John Rusk - MSFT    4 年前

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

    1. Delve启动了您的流程。在这种情况下,当你打电话时 os.Getppid() 要获取父进程的pid,将对该进程进行深入研究。
    2. Delve没有启动您的流程,但稍后会附加到该流程。在这种情况下,您需要查找所有正在运行的Delve进程,查看它们的命令行,并查看是否有任何进程是使用包含“attach”的命令行启动的,其中是调用 os.Getpid() . 这取决于这样一种假设,即您没有找到一个旧的Delve,而是使用一个与您的PID匹配的旧PID运行。(我忘记了操作系统重用PID的规则)。

    请注意,1和2使用的操作系统功能不同。一个获取父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
    }
    
        3
  •  1
  •   mymedia    3 年前

    在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")
    }
    
        4
  •  0
  •   kosta    4 年前

    对于案例2,我们可以将程序设置为等待某个信号(SIGUSR1),并在此等待期间附加调试器。
    main的代码。围棋可以是这样的:

    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的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\u FOR\u 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代码并运行myprog远程调试。在之前设置断点
    那就给他 kill -SIGUSR1 556127

    断点也会起作用

        5
  •  0
  •   FGM    2 年前

    如果您的进程是由Delve运行的,而不是事后附加的,那么这里有一个非常简单的解决方案来检测Delve。至少到目前为止,这是大多数用例

    package isdebugging
    
    import (
        "os"
    
        "github.com/mitchellh/go-ps"
    )
    
    // IsDebugging will return true if the process was launched from Delve or the
    // gopls language server debugger.
    //
    // It does not detect situations where a debugger attached after process start.
    func IsDebugging() bool {
        pid := os.Getppid()
    
        // We loop in case there were intermediary processes like the gopls language server.
        for pid != 0 {
            switch p, err := ps.FindProcess(pid); {
            case err != nil:
                return false
            case p.Executable() == "dlv":
                return true
            default:
                pid = p.PPid()
            }
        }
        return false
    }