7

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

System.Diagnostics.Debugger.IsAttached

Go中是否有等价物?我有一些超时,我希望在单步执行代码时禁用这些超时。谢谢!

我正在使用 GoLand 调试器。

4

5 回答 5

9

据我所知,没有内置方法可以按照您描述的方式执行此操作。但是您可以或多或少地使用构建标签来指示 delve 调试器正在运行。您可以dlv使用--build-flags参数将构建标签传递给。这与我在如何检查是否在运行时启用竞争检测器中描述的技术基本相同?

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 工具参数”中:

-tags=delve

Goland 运行/调试配置窗口


如果你在 Goland 之外,runninggo run a.go会报错delve false,如果你想自己运行 dlv,使用 dlv debug --build-flags='-tags=delve' a.go; 这将报告delve true


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

于 2017-12-19T15:32:02.917 回答
3

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

  1. Delve 启动了您的进程。在这种情况下,当您调用os.Getppid()获取父进程的 pid 时,该进程将是 Delve。
  2. Delve 没有启动您的流程,但后来确实附加了它。在这种情况下,您需要查找所有正在运行的 Delve 进程,查看它们的命令行,并查看是否使用包含“attach”的命令行启动了任何进程,调用的结果在哪里os.Getpid()。这依赖于假设您没有找到旧的 Delve,它使用恰好与您匹配的旧 PID 运行。(我忘记了操作系统重用 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
}
于 2020-06-13T06:54:03.397 回答
1

在 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")
}
于 2021-07-01T12:50:34.263 回答
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

断点会起作用

于 2020-08-06T21:17:16.590 回答
0

这是一个非常简单的解决方案,如果您的进程由它运行,则检测 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
}
于 2022-02-03T10:36:27.540 回答