How can I see if the GoLand debugger is running in the program?

后端 未结 3 1284
-上瘾入骨i
-上瘾入骨i 2021-01-14 15:52

In C# the executing program can detect if it\'s running in the debugger using:

System.Diagnostics.Debugger.IsAttached

Is there an equivalen

相关标签:
3条回答
  • 2021-01-14 15:57

    If you assume that the debugger used will be Delve, you can check on the Delve process(es). There are at least two cases to consider (maybe more).

    1. Delve launched your process. In this case, when you call os.Getppid() to get the pid of your parent process, that process will be Delve.
    2. Delve didn't launch your process, but did attach to it later. In this case, you'd need to look for all running Delve processes, look at their command lines, and see if any was launched with a command line including "attach ", where is the result of calling os.Getpid(). This relies on the assumption that you're not finding an old Delve, running with an older PID that happens to match yours. (I forget what the rules on on reuse of PIDs by the OS).

    Note that the os functions used by 1 and 2 are different. One gets the parent PID, the other gets your PID.

    Some very basic code to do 1 looks like this:

    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
    }
    
    0 讨论(0)
  • 2021-01-14 16:00

    As far as I know, there is no built-in way to do this in the manner you described. But you can do more or less the same using build tags to indicate that the delve debugger is running. You can pass build tags to dlv with the --build-flags argument. This is basically the same technique as I described in 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)
    }
    

    In Goland, you can enable this under 'Run/Debug Configurations', by adding the following into 'Go tool arguments:'

    -tags=delve
    


    If you are outside of Goland, running go run a.go will report delve false, if you want to run dlv on its own, use dlv debug --build-flags='-tags=delve' a.go; this will report delve true.


    Alternatively, you can use delve's set command to manually set a variable after starting the debugger.

    0 讨论(0)
  • 2021-01-14 16:00

    For case 2 we can set the program to wait for some signal (SIGUSR1) and attach debugger during this wait.
    The code of main.go can be like this:

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

    launch.json of vscode:

    {
        "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" 
    
    }
    

    Explanation: we launch the program with env WAIT_FOR_DEBUGGER=true, for example

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

    It will output dlv attach ... command and 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
    

    Run the dlv attach ... above
    Then go to VS Code and run myprog-remote-debug. Set breakpoints before
    Then give him kill -SIGUSR1 556127

    and breakpoints will work

    0 讨论(0)
提交回复
热议问题