golang 运行时死锁排查和检测
当运行的系统发生goroutine等待获取锁时间超过预期时,判定为发生了死锁。因目前代码中使用了一些公开的锁实例,调用链也比较长,对问题排查带来了很大困扰。为了便于问题排查,需要借助工具来实现。
1. 发生死锁的判定依据和原因
1.1 判定依据
如下为使用Mutex锁产生的锁等待,并持续了222分钟。这类不合理现象,就判定为发生了死锁。(以下为cpu-pprof导出的stack信息)
goroutine 61508 [semacquire, 222 minutes]:
sync.runtime_SemacquireMutex(0xc00048f564, 0x4f4300, 0x1)
/opt/go/src/runtime/sema.go:71 +0x47
sync.(*Mutex).lockSlow(0xc00048f560)
/opt/go/src/sync/mutex.go:138 +0x105
sync.(*Mutex).Lock(...)
/opt/go/src/sync/mutex.go:81
......
1.2 产生死锁的原因
锁重入
多把锁加锁,并发访问时加锁顺序不一致
锁未释放
2. 死锁问题诊断
常用的标准库锁: sync.Mutex
, sync.RWMutex
,目标暂定为以检测这两类锁的死锁问题。
检测死锁的方法:分为静态代码分析、运行期分析。
2.1 静态代码分析
优势:
可以做到场景全覆盖。
劣势:
好像没有劣势,但开发难度较高,目前发现的一个开源工具 go-tools,验证下来无法检测互锁问题,但可以检测锁未释放的问题。
2.2 运行期分析
优势:
可以快速定位问题,检测相对容易。
劣势:
对代码有侵入性,需要封装标准库的锁,对性能会有一定影响,应采取条件编译方式,生产环境勿启用。
3. 运行期分析工具开发
3.1 目标
支持 Mutex 和 RWMutex
支持锁重入、互锁问题的检测
3.2 用法
import (
"github.com/berkaroad/detectlock-go"
)
// 应用启动时,设置启用调试
detectlock.EnableDebug()
// 声明 sync.Mutex、sync.RWMutex 替换为 detectlock.Mutex、detectlock.RWMutex
var locker1 *detectlock.Mutex = &detectlock.Mutex{}
var locker2 *detectlock.RWMutex = &detectlock.RWMutex{}
// 异步检测死锁
items := detectlock.Items()
fmt.Println(detectlock.DetectAcquired(items)) // 检测获得锁的goroutine列表
fmt.Println(detectlock.DetectReentry(items)) // 检测锁重入的goroutine列表
fmt.Println(detectlock.DetectLockedEachOther(items)) // 检测互锁的goroutine列表
// 关闭调试,并清理锁使用信息
detectlock.DisableDebug()
3.3 死锁示例数据
- 检测 sync.Mutex 多把锁顺序不一致导致的互锁
--- DetectAcquired ---
goroutine 29: [(0xc0000b4008, acquired, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:30)), (0xc0000b4000, wait, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:33))]
goroutine 30: [(0xc0000b4000, acquired, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:20)), (0xc0000b4008, wait, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:23))]
--- DetectLockedEachOther ---
goroutine 29: [(0xc0000b4008, acquired, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:30)), (0xc0000b4000, wait, main.B (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:33))]
goroutine 30: [(0xc0000b4000, acquired, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:20)), (0xc0000b4008, wait, main.A (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex01/main.go:23))]
- 检测 sync.Mutex 锁重入
--- DetectAcquired ---
goroutine 53: [(0xc0000160b8, acquired, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:19)), (0xc0000160b8, wait, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:22))]
--- DetectReentry ---
goroutine 53: [(0xc0000160b8, acquired, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:19)), (0xc0000160b8, wait, main.C (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex02/main.go:22))]
- 检测 sync.Mutex、sync.RWMutex 多把锁顺序不一致导致的互锁
--- DetectAcquired ---
goroutine 8: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 10: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 13: [(0xc0000160b8, acquired, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:30)), (0xc0000180c0, wait, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:33))]
goroutine 14: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 16: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 50: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 52: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 54: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 56: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 58: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
--- DetectLockedEachOther ---
goroutine 8: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 10: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 13: [(0xc0000160b8, acquired, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:30)), (0xc0000180c0, wait, main.E (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:33))]
goroutine 14: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 16: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 50: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 52: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 54: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 56: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
goroutine 58: [(0xc0000180c0, r-acquired, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:21)), (0xc0000160b8, wait, main.D (file: /home/berkaroad/github/berkaroad/detectlock-go/examples/mutex03/main.go:24))]
---------------------------------分割线-------------------------------------------------------------
我的detectlock-go项目: https://github.com/berkaroad/detectlock-go,已经挂在github上,有兴趣的可以去了解下。
使用中有何问题,欢迎在github上给我提issue,谢谢!
golang 运行时死锁排查和检测的更多相关文章
- gohook 一个支持运行时替换 golang 函数的库实现
运行时替换函数对 golang 这类静态语言来说并不是件容易的事情,语言层面的不支持导致只能从机器码层面做些奇怪 hack,往往艰难,但如能成功,那挣脱牢笼带来的成就感,想想就让人兴奋. gohook ...
- Unity运行时检测Altas使用情况
UI贴图在游戏中内存大小中占的分量非常非常大,尤其对于前期对UI没有规划的项目,无论是包量还是内存大小都是需要花费很多时间去优化.如果涉及到战斗场景和逻辑场景的情况下,常用的做法就是把两个场景使用的a ...
- [C++]C++中的运行时类型检测
Date:2014-1-3 Summary: 使用C++中的运行时类型检测.(文章重点在于记录本人的使用情况,并非深层讨论RTTI) Contents:写习惯C#的我,在C++依然存在哪些.NET的惯 ...
- [bug]”System.InvalidProgramException:公共语言运行时检测到无效程序“解决方案
Visual Studio 2017版本15.8.x运行某些程序会报这样的错误:“System.InvalidProgramException:公共语言运行时检测到无效程序” 此问题的临时解决方案: ...
- 第十九章 排查和调试Web程序 之 防止和排查运行时问题
1. 概述 常见的几种运行时问题包括 错误数据.慢于预期的响应.未知行为 或者 未处理的异常. Visual Studio 提供了 排查.跟踪 和 日志 等工具 来帮助排查系统的问题.有些情况还需要插 ...
- java中检测-在运行时指定对象是否是特定类的一个实例---关键字 instanceof
java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. if(requ ...
- SQL Server死锁排查
1. 死锁原理 根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件:互斥条件(Mutua ...
- 利用神器BTrace 追踪线上 Spring Boot应用运行时信息
概述 生产环境中的服务可能会出现各种问题,但总不能让服务下线来专门排查错误,这时候最好有一些手段来获取程序运行时信息,比如 接口方法参数/返回值.外部调用情况 以及 函数执行时间等信息以便定位问题.传 ...
- 介绍下Java内存区域(运行时数据区)
介绍下Java内存区域(运行时数据区) Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域.JDK 1.8 和之前的版本略有不同. 下图是 JDK 1.8 对JV ...
- Android权限管理之Android 6.0运行时权限及解决办法
前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以 ...
随机推荐
- 使用 .NET 8.0 和 OpenGL 创建一个简易的渲染器
前言我个人对三维渲染领域的开发有着浓厚的兴趣,尽管并未在相关行业工作过,我的了解还很片面.去年,在与群友聊天时,他们推荐了一本<Unity Shader入门精要>,说适合像我这样想自学的新 ...
- Android自动化测试框架uiautomator2详解
1 uiautomator2 简介 uiautomator2 是 一种 Android 自动化测试框架,提供了点击.长按.输入文本.滑动.拖拽.截屏等方法,能够模拟用户的各种动作.用户可以通过控件 ...
- od命令
od命令 od命令会读取所指定的文件的内容,并将其内容以八进制字节码呈现出来. 语法 od [OPTION]... [FILE]... od [-abcdfilosx]... [FILE] [[+]O ...
- Swoole从入门到入土(25)——多进程[进程间无锁计数器]
Atomic 是 Swoole 底层提供的原子计数操作类,可以方便整数的无锁原子增减.原子计数器有如下特点: - 使用共享内存,可以在不同的进程之间操作计数 - 基于 gcc/clang 提供的 CP ...
- Mysql错误消息 语言设置
今天操作数据库的时候,mysql错误返回语句 ,一直报的是非英语的语言 ,百般纠结 ,简单的还大致能猜出意思 , 复杂了就会实在看不懂的 ,举个简单的如下: [Err] 1064 - Erreur d ...
- 高效的PDF文字提取技术
无论是行政法规.学术论文还是企业合同,PDF文档为我们提供了一种便捷.稳定的信息传递方式.然而,从PDF文件中提取文本信息对于数据分析.内容编辑等后续处理来说至关重要. PDF文本提取技术是一种可以从 ...
- 【LeetCode二叉树#18】修剪二叉搜索树(涉及重构二叉树与递归回溯)
修剪二叉搜索树 力扣题目链接(opens new window) 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R.通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) .你 ...
- Gitlab 16.9.0 用Access Token注册Gitlab Runner
升级到当前最新版Gitlab之后 在"管理中心"的"CI/CD"--"Runners"下,提示以前的那种注册令牌的方式已经过时了. 点击右上 ...
- 前后端分离项目(八):后端报错Field 'id' doesn't have a default value
好家伙,又到了修bug的环节,(深叹一口气) 好了,来看报错 2022-10-29 23:27:52.155 WARN 15068 --- [nio-8011-exec-2] o.h.engine.j ...
- X86模拟龙芯与编译 .NET CoreCLR
目录 .NET 收到一台龙芯机器 编译 CoreCLR 环境要求 部署虚拟机与环境 Linux 安装 KVM 下载需要的文件 启动模拟器 下载 CoreCLR 尝试编译 CoreCLR 前段时间得知龙 ...