作者|陶建辉

原文首发于:

循迹追寻令人头秃的Crash

我们写 C 程序,常常碰到 Crash,绝大部分情况下都是空指针或野指针形成,从 call stack 来看,一般很简单github找出问题。但是有一类 Crasgiteeh 很难 dGitebgithub中文官网网页ug,那便是内存溢出。溢出的部分的内存giti轮胎是什么品牌空间正giti好掩盖其他一个线程拜访的数据(比如一个结构体),那么其他一个线程读取这块数据时,获取的数据便是无效的,往往导致不行预见的差错,甚至 Crash。但由于形成数git指令据溢出的线程现已脱离现场,导致问题很难定位。这是我在 2020 年 5 月写的一篇内部博客,以我其时碰到的一个差错为例,将处理这类问题的方法分享出来,供我们参看gitlab,触类旁通。

具体问题

在feature/query分支上,在cogitimmunity库房,实施以下脚本,呈现Crash。

./test.sh -f general/parser/col_arithmetigit指令c_operation.sim

重现问题

我登录到指定的github是干什么的机器,检查了 core dump, 确实如此。Call Stack 截图如下:

循迹追寻令人头秃的Crash,十倍程序员的Debug日常(2)

第一步:看哪个当地 crash。是 shash.c:250 行,运用 GDB 指令 “f 1″ 检查 stackgitlab 1,检查*pObj,一下就看到 hashFp 为 NULL,自然会 crash。但为什么会设置为空?其他参数呢?dataSize 设为 0 也必定是错的。因而能够判定,这个结构体是不对的。我们需求看上一层调用是否传入了正确的参数。

第二步:运用 GDB “f 2″检查 stack 2,rpcMain.c:605giti轮胎是什么品牌 行,检查*pRpc,这些结构体里的参数显得很正常,包git指令括指向 hagithub永久回家地址sh 的指针值看不出有什么问题。那因而能够判定调gitee用是 OK 的,gitee调用 taosGetStrHashData 应该供给了正确的参数。

第三步:已然参数对的,看 shash.c 的程序,那只或许是 SHashObj 这个结git教程构表现已被开释,拜访的时分,自然无gitee效。再看代码,只需一个或许,函数 taosCleanUpStgitirHash 被调用,因而我在改函数里立刻加上一行打印日志(留心 TDengine 的日志输出操控,系统配置文件 taos.cfg 里参数github asyncLog 要设置为 1,否则 crash 时,有或许日志不git指令打印出来)。从头作业脚本,检查日志,发现 taosCleanUpStrHash 没有被调用过。那么现在只需一个或许,这一块数据的内存被其他线程写坏。

第四步:万幸,我们有很棒的作业时内存检查东西 valgrind, 能够经过作业它来找找蛛丝马迹。一作业(valgringithub是干什么的d 有许多选项,我作业的是 valgrindgiti –leak-check=yes –track-origins=yes taosd -c test/dnode1/cfg),一下就发现有 invalid write,截图如下:

循迹追寻令人头秃的Crash,十倍程序员的Debug日常(2)

第五步:一github看 valgrind 输出,就知道 rpcMain.c:585 有 invalid write, 这儿是 mgiti轮胎是什么品牌emcpy。从编码上来看,这应该没有问题,由于仿制的是固定巨细的gitlab结构体 SRpcCogitinn,每次作业到这儿都会实施的。那么仅有的或许便是 pConn 指向的是无效的内存区,那 pgithub中文官网网页Conn 怎样或许是无效的?我们看一下程序:

循迹追寻令人头秃的Crash,十倍程序员的Debug日常(2)

看 584 行,pConn = pgitiRpc->connList + sid。这个 sid 是 taosAllocateId 分配gitee出来的。假定 sid 逾越 pRpc->sessions,那 pConn 毫无疑问就指向了无效的区域。那怎样承认呢?

第六步:加上日志 578 行,分配出的 ID 打印出来,编译,从头作业查验脚本。

第七步:crashgiti轮胎是什么品牌,看日志,能够看到 sid 能输出到 99(max 是 100),还一切正常,但之后就溃散。因而能够断言,Git便是由git教程于分配的 ID 逾越了 pRpc→session。

第八步gitlab检查分配 ID 的程序 tidpool.c,一看就知道原因,ID 分配是从 1 到 MAX,而 RPC 模块只能运用 1 到 Max-1。这样当 ID 回来为 max 时,RPC 模块自然会产生 invalid write。

处理方案

已然知道原因,那就很好办,有两套方法:

1. 在 tidpool.c,taosInitIdPool 里,将 maxId 减git教程一,这样分配的 ID 只会是 1 到 max-1。

2. 在giti rpcMain.c 的 rpcOpen() 函数里,将

pRpc->idPool = taosInitIdPool(pRpc->sessions);github

改为

pRpc->idPool =gitee taosInitIdPool(pRpc->sessions-1);

假定运用要求最多 100 个 session,这么改,RPC 至多创建 99 个,为保证 100 个,再github永久回家地址

pRpc->segit教程ssigit教程ons = pInit->sessions;

改为

pRpc->sessions = pInit→sessions+1;

验证

两种方法,都从头编译,作业查验脚本经过,crash 不再产生。

经验总结

遇到giti内存被写坏的场景,必定要git教程用 valgrind 跑一次,看是否有 invalid writegiti。由于它是一个动态检查东西,报的差错都应该是对的github中文官网网页。只需把 igitinvalid writegithub中文官网网页 先处理,再去看 crash 问题。

怎样防止类似问题

这个 BUG 的中心是由于 tidpool.c 分配的 ID 规划是 1 到 max,而 RPC 模块假定的 ID 分配是 1 到 max-1。因而是模块之间的约好出了问题。

怎样防止?每个模块对外 API 要做具体说明,假定 API 做了调整,要告诉咱giti们,而且要作业完整的查验例,git指令避免损坏github中文官网网页了某种约好。

本文为 TDengit教程gine 的一则实在事例,git commit id:89d9d62,欢迎我们重现。