前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang程序调试常用方法

Golang程序调试常用方法

原创
作者头像
于顾而言SASE
发布2024-03-21 10:27:47
1290
发布2024-03-21 10:27:47
举报
文章被收录于专栏:golang与云原生golang与云原生

前言

一般来说,项目开发过程中,代码编写占开发总时间的40%,剩下的时间基本就是自测和联调的过程。程序出错很正常,关键是如何迅速的去定位它,修掉它。本文将介绍自己程序调试的一些常用方法,这边我以golang为例,总结为望问切闻---debug四部曲。

望--查看程序外部指标

第一步先看看程序的外部指标,如进程启动关系,系统调用的使用,消耗的内存,cpu,磁盘io,文件句柄连接数,网络连接情况等等资源是否符合预期。

内存,cpu部分我们可以用工具top查看,-d 指定每两次屏幕信息刷新之间的时间间隔(单位为秒);-H 显示所有线程的运行状态指标。如果没有该参数,会显示一个进程中所有线程的总和。在运行过程中,可以通过H命令进行交互控制:

进程启动关系可以用ps auxf查看,可以看到进程启动的时间以及进程调用的树形图:

跟踪程序的系统调用情况,可以使用strace。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间:

网络的使用情况可以用命令ss查看,ss 命令可以用来获取 socket 统计信息,它显示的内容和 netstat 类似。但 ss 的优势在于它能够显示更多更详细的有关 TCP 和连接状态的信息,而且比 netstat 更快。当服务器的 socket 连接数量变得非常大时,无论是使用 netstat 命令还是直接 cat /proc/net/tcp,执行速度都会很慢。

查看使用的文件句柄,ring队列,共享内存等等,可以使用lsof命令,它主要用来获取被进程打开文件的信息。

对于磁盘io的监控,我们可以采用iostat。它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况。iostat也有一个弱点,就是它不能对某个进程进行深入分析,仅对系统的整体情况进行分析。

问--询问近期改动

我们在工位中经常听到过如下的自言自语---"不可能,之前程序跑的还好好的啊?现在怎么会出问题呢?",每次听到这种情况,我都会安慰同事说一定是太阳黑子辐射的原因,导致程序跑错了,哈哈,开个玩笑。

一般出现这种情况,都是由于近期出现了改动导致的,你需要做的首先查看,是不是最近修改bug然后新引入了一个新的bug,如果确定不是自己的问题,那么一个是要问,关联的服务(上下游)是否最近合入过什么功能,另一个是问,是不是最近数据库发生了变动

切--查看程序内部指标

能否顺利定位出程序的bug所在,这步是最为关键的了。一般是看两个地方,一个是日志流(debug日志),一个是dlv attach进去调试,查看具体的调用栈。

那么如何打好日志呢,首先就是一定要做好分类,比如logrus库它有5个级别:

代码语言:javascript
复制
// 排查问题时打印,常见的用法是对函数的返回值做判断,非预期的返回值就打印debug日志
logrus.Debug("Useful debugging information.")

//info 出现关键事件时的打印,如程序初始化成功或者作为跟踪日志,
//一般在每个函数的入口和出口打印,用以跟踪程序的执行流
logrus.Info("Something noteworthy happened!")

// 当出现非预期的逻辑,但业务还是可以继续处理的情况,如web服务请求数超过指定的阈值
logrus.Warn("You should probably take a look at this.")

logrus.Error("Something failed but I'm not quitting.") 
logrus.Fatal("Bye.") //log之后会调用os.Exit(1)
logrus.Panic("I'm bailing.")

其次,打印日志结构体的时候不要用%+v或者在经常跑的地方不要加日志打印,这会导致刷屏,眼睛直接瞎啦。一般通过info级别的日志基本就能确定程序的数据流走向,变量和服务调用情况是否已经符合预期了。

dlv工具和C语言的gdb很像,也可以attach进去通过打断点的方式查看变量和调用栈,或者程序core dump的时候,可以通过dlv core的方式获取到所有的goroutine和frame上下文信息。golang产生core是需要在编译前配置的,配置方法如下:

代码语言:javascript
复制
unlimit -a unlimited

GOTRACEBACK=crash ./crash

当然有的时候出现段错误的时候,golang是会打印出崩溃处的堆栈,如果没有的话,一般就是被其他程序kill掉了,我们可以用mv大法,在调用/usr/bin/kill之前,先加一点信息重定向到文本

代码语言:javascript
复制
mv /usr/bin/kill /usr/bin/has.log.kill

#!/bin/sh
date >> /var/crash/kill.log
ps auxf >> /var/crash/kill.log
/usr/bin/has.log.kill $*

chmod +x usr/bin/has.log.kill
alias kill=/usr/bin/has.log.kill

dlv使用方法可以参考这篇文章:

GO语言调试利器dlv快速上手 - 博客园 (cnblogs.com)

闻--查看代码

结合以上几个步骤的定位,我们基本可以胸有成足了,这个时候打开代码,好好review一番。如果还是搞不定,这个时候只能用杀手锏pprof了,它是go性能分析神器,相当于在你的程序中暴露一个http服务端口,让你可以获得以下程序运行信息:

代码语言:javascript
复制
package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
)

func main() {
    go func() {
      // 你的业务代码
    }()

    http.ListenAndServe("0.0.0.0:7001", nil)
}

如果还是搞不定啊,那我建议就是摇人啦,拉个会议室,点几杯奶茶,叫上几个大佬一起review一下。

Reference

Linux top命令详解 - 小a玖拾柒 - 博客园 (cnblogs.com)

linux top swap 为0,Linux:top_逃命的饼干的博客-CSDN博客

Go进阶10:logrus日志使用教程 | Go&Rust (mojotv.cn)

Golang性能测试工具PProf应用详解 - 知乎 (zhihu.com)

linux中proc是什么-linux运维-PHP中文网

shell脚本中$0 $1 $# $@ $* $? $$ 的各种符号意义详解_一口Linux的博客-CSDN博客_shell脚本$@

linux 用strace查看系统调用 - jasononline - 博客园 (cnblogs.com)

linux ss命令详解 - 腾讯云开发者社区-腾讯云 (tencent.com)

Linux iostat命令详解 - 小a玖拾柒 - 博客园 (cnblogs.com)

Golang 大杀器之性能剖析 PProf - 简书 (jianshu.com)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 望--查看程序外部指标
  • 问--询问近期改动
  • 切--查看程序内部指标
  • 闻--查看代码
  • Reference
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档