V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
douyacun
V2EX  ›  Go 编程语言

golang 如何远程调试代码?

  •  
  •   douyacun ·
    douyacun · 2021-01-18 10:32:06 +08:00 · 2295 次点击
    这是一个创建于 1401 天前的主题,其中的信息可能已经有所发展或是发生改变。

    博客地址: https://www.douyacun.com

    原文链接: https://www.douyacun.com/article/65021b4fee1f73b55ba77b46bb18b3a7

    问题: epoll 这只能运行在 linux,有问题没发调试,蛋疼吗?

    首先想到装个 docker,运行个 centos,然后对着 docker 又是一顿撸。还是没发调试,

    gdb 也是可以调试 go 程序的,问题是,这 tm 一个项目几百个文件都有了,我 gdb 怎么断点啊,大概的思路就是从函数调用的地方开始入手,这啥时候是个头啊,简单的还行,果断放弃了

    delve dlv 其实很不错的,也能像 gdb 一样支持,对 goroutine 支持也很完美,问题来了,docker 运行的 centos,我就是装个 dlv, go get -u github.com/go-delve/delve/cmd/dlv 装不动,只能心里默默诅咒那个下决定对 github 限速的人。google 我能理解,但是 github 这个真理解不了。只能走代理了。关于[怎么在 centos 上使用代理](/linux/centos 使用 ss 代理.md)

    goland 配置

    Run/Edit configurations

    host 配置

    docker 运行的 centos,创建 container 的时候,指定 -p 2345:2345 , 把容器的端口映射到宿主机器上就 ok 了

    编译调试

    goland 里面介绍的很明白,有一个坑 exec ./main的时候后面怎么传参数给调试进程,正确的姿势: 后面跟 --,告诉 dlv,这是给调试进程用的。

    dlv --listen=:9004 --headless=true --api-version=2 --accept-multiclient exec ./main -- start --env debug
    

    dlv listen 以后怎么退出来?

    不好意思,我也没有找到正确的姿势,万能的方式,新启动一个终端 kill 掉他,然后会问,docker 怎么 attach 以后还是当前窗口,这个....

    docker exec -it centos7 bash
    

    看一下我调试解决的 bug

    func start() {
    	for {
    		clients, err := hub.Wait()
    		if err != nil {
    			logger.Debugf("epoll wait %v", err)
    			continue
    		}
    		for _, client := range clients {
          // 这里是 68 行,下面这一行会报 空指针 的错误
    			bt, opCode, err := wsutil.ReadClientData(client.conn)
    			if err != nil {
    				log.Printf("read message error: %v", err)
    				continue
    			}
    			// 处理 ping/pong/close
    			if opCode.IsControl() {
    				err := wsutil.HandleClientControlMessage(client.conn, wsutil.Message{
    					OpCode:  opCode,
    					Payload: bt,
    				})
    				if err != nil {
    					if _, ok := err.(wsutil.ClosedError); ok {
    						hub.unregister <- *client
    					}
    					continue
    				}
    				continue
    			}
    			cmsg := ClientMessage{}
    			if err := json.Unmarshal(bt, cmsg); err != nil {
    				logger.Errorf("json unmarshal error: %v", err)
    				continue
    			}
    			hub.broadcast <- NewDefaultMsg(client, cmsg.Content, cmsg.ChannelId)
    		}
    	}
    }
    

    报错信息:

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0xdf08d4]
    
    goroutine 20 [running]:
    dyc/internal/module/chat.start()
    	/root/github/api.douyacun.com/internal/module/chat/client.go:68 +0x64
    created by dyc/internal/module/chat.init.0
    	/root/github/api.douyacun.com/internal/module/chat/client.go:26 +0x82
    

    可以看出来是 client 为 nil 了,那就出现hub.Wait(), 调用 epoll wait 的时候返回的 client 有 nil,debug 一下

    wait 函数的代码:

    func (e *epoll) Wait() ([]*Client, error) {
    	events := make([]syscall.EpollEvent, 100)
    	n, err := syscall.EpollWait(e.Fd, events, 100)
    	if err != nil {
    		return nil, err
    	}
    	connections := make([]*Client, n)
    	for i := 0; i < n; i++ {
    		conn := e.connections[int(events[i].Fd)]
    		connections = append(connections, conn)
    	}
    	return connections, nil
    }
    

    next 一直运行,看一下有连接进来的情况:

    这里应该是对syscall.EpollWait 第二个参数 events, 第一个返回值 n 的理解有问题,但这不是关键,关键是远程调试姿势有了。

    2 条回复    2021-01-18 19:48:30 +08:00
    yjhatfdu2
        1
    yjhatfdu2  
       2021-01-18 17:34:54 +08:00
    go get 用 GOPROXY 不行嘛
    douyacun
        2
    douyacun  
    OP
       2021-01-18 19:48:30 +08:00
    @yjhatfdu2
    go get 可以用 GOPROXY

    不过这是在本地调试 linux 上的代码,因为用到了 epoll,mac 上无法执行的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1069 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 22:33 · PVG 06:33 · LAX 14:33 · JFK 17:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.