背景
在办理入职的第一天,遇到测试同事无奈且慌张的报出一个问题:拷机过程中,stTsp
进程重启了。可能因为大家都比较忙,也可能因为面试过程中,我说自己比较喜欢解决问题。领导就让我帮忙一起看看。
呃,此时我的内心…🤦🏻🤦🏻🤦🏻
毕竟刚进入团队,心想show一下power 似乎也可以。最终在两天内,解决了这个遗留问题。本文主要向大家分享我如何在快速定位问题,以及解决问题的思路,若对您有帮助,还请点赞收藏🫶🏻。
了解问题背景及复现流程
在分析问题前,一定要了解问题的现象,以及背景。于是我和测试人员经过详细沟通,该问题大致可以为这样描述。
- 在拷机的过程中,容易在夜间12点crash。
- 这个问题在年前就出现过,研发人员修改了一个版本后,依然存在。
通过与测试人员的沟通,根据经验,我大致有以下的排查思路。
- 容易在12点触发现象。通过该描述,我会怀疑是否存在类似这样的任务:每天凌晨做特定的操作,而该相关操作,导致进程crash。
- 判断方式:打开linux的coredump选项,若是进程代码导致crash,则会生成core文件。
- 拷机过程。一般拷机过程,都是伴随着运行时间的增加,因为运行时间增加,而导致进程异常的情况,第一个想到的就是内存泄漏了。
- 判断方式:通过系统日志查看,是否触发OOM机制,导致系统kill进程。
于是通过dmesg
命令,查看系统日志。经过分析的确是因为OOM机制导致的,如下图:
因此方向确定:进程内存泄漏,触发系统的OOM机制,将其kill。
验证流程
验证内存泄漏的最好方式,当然是用valgrind
工具。关于valgrind
工具的使用方式,可参考我的博客valgrind跨平台调试及其问题分析。经过使用valgrind 调试,的确存在内存泄漏的情况,但是并没有随着时间的增加而逐步增加,这就让我很奇怪。
并且随着与研发同事沟通,了解到了另一个背景:设备中有一个守护进程processMgr
,它的功能如下:
- 软看门狗。周期性等待应用程序喂狗,若超时,则认为进程阻塞,
kill -9
杀死进程。 - 守护进程。周期性查看列表进程是否存在,若不存在,则重启拉起。
- 资源监控。周期性查看进程使用内存容量,若超过预期值,则
kill -9
杀死进程。
注:其中周期为3秒。
这就基本推翻了我之前的猜想:随着时间的增加,导致stTsp
进程的内存泄漏越来越多,最终触发系统OOM机制。因为进程stTsp
的内存资源上限是8MB,最终触发OOM时,系统提示stTsp
的内存占用,已经高达72MB。
于是方向就改为:短时间内,执行了需要大内存的操作,比如申请了超大的内存。
继而之,咨询了相关同事,了解到一个周期删除数据表的功能,其需求流程大致如下:
- 将CAN通道上的数据,按照YY-MM—DD的规则,建立数据库表格,并保存表中。即将一天的can数据保存到数据库表中。
- 周期(一小时)判断数据库中的表,是否超过7个,若超过7个,则删除较早的表格。实现can数据保存7天的需求。
通过该功能描述,也就说明了为什么总是在凌晨12点多触发,并且也基本确认是删除数据库表的相关操作导致的内存急剧增加。
于是走读删除数据表的代码逻辑,大致流程如下:
- 删除过期的表
- 回收数据库空间
简单分析了代码,怀疑是回收数据库动作,导致内存急剧的增加。之后通过走读sqlite3开源库,发现其对VACUUM的命令注释如下:
/*
** The VACUUM command is used to clean up the database,
** collapse free space, etc. It is modelled after the VACUUM command
** in PostgreSQL. The VACUUM command works as follows:
**
** (1) Create a new transient database file
** (2) Copy all content from the database being vacuumed into
** the new transient database file
** (3) Copy content from the transient database back into the
** original database.
**
** The transient database requires temporary disk space approximately
** equal to the size of the original database. The copy operation of
** step (3) requires additional temporary disk space approximately equal
** to the size of the original database for the rollback journal.
** Hence, temporary disk space that is approximately 2x the size of the
** original database is required. Every page of the database is written
** approximately 3 times: Once for step (2) and twice for step (3).
** Two writes per page are required in step (3) because the original
** database content must be written into the rollback journal prior to
** overwriting the database with the vacuumed content.
**
** Only 1x temporary space and only 1x writes would be required if
** the copy of step (3) were replaced by deleting the original database
** and renaming the transient database as the original. But that will
** not work if other processes are attached to the original database.
** And a power loss in between deleting the original and renaming the
** transient would cause the database file to appear to be deleted
** following reboot.
由其注释可知,VACUMM命令会存在以下几点问题:
- 磁盘要求高。因为需要创建新的临时数据库,导致某时刻需要的最大磁盘空间是原始数据库的2倍。比如:要回收一个2GB的数据库,若要成功执行
VACUMM
命令,则至少需要剩余2GB的空余磁盘空间。 - 数据库拷贝过程,可能涉及大内存的申请,容易导致OOM。
最终经过讨论之后,我们决定不再执行回收数据的动作,似乎也是可以的。原因如下:
- 经过压测,一张表的CAN数据量大概是200MB,7天的表格也就是1.4BG。
- 当保存第8天的表格时,我们删除最早一天的表格,即使不做数据库回收命令。此时数据库会认为day1的表格已经被释放,其文件内容是可以被覆盖的。因此磁盘大小也不会一直增加。
通过测试验证,最终问题解决。
疑问:
其实到这里,问题似乎已经解决了。但是困扰了我一个问题:那就是数据库拷贝为什么会导致内存急剧增加呢?
按照我的理解,若是将数据库文件拷贝成另一个数据库,简单理解就是文件的拷贝,关于文件的拷贝,我们一般思路都是:
- 申请一块buff,一般为4KB
- 读取old 数据库,写入临时数据库
简单的逻辑为什么会引起内存的极度增加呢?于是怀着疑问,我走读了sqlite3RunVacuum
接口内容。发现了一个特殊的步骤,那就是sqlite是通过select
语句进行表的拷贝,如下:
因此,我怀疑内存的增加,应该就是select
语句导致的。因为数据库的每一张表的内容很大。若select
的查询集很大,那么就需要很大的内存用于保存查询结果,以及使用内存数据库等,最终导致stTsp
的内存超过系统承受范围。
因此使用sqlite3数据库时,应该根据系统资源的容量,通过sqlite3_hard_heap_limit64
接口设置数据库使用内存上限。
总结
通过本文的分享,希望能让大家遇到crash问题时,不至于手忙脚乱,任何问题都有套路可言,在于每个人去总结,以下便是我个人的经验:
- 确认是进程自己触发crash,还是系统触发。
- 若是程序自己触发,可以通过打开系统的coredump属性,待生成core文件时,进行gdb分析。关于gdb的使用,可以参考我的文章【小白进阶】Linux 调试大法——gdb。(打开程序的调试选项
-g
和not strip
) - 若是系统触发,一般是因为触发了OOM机制。如果内存会随着时间逐步增加,一般就是存在内存泄漏了,最简便的方式就是使用
valgrind
工具,运行一段时间,查看内存泄漏点;若不是逐步增加,一般是因为某个操作导致的内存急剧增加。这个可以根据日志打印,逐步定位相关代码,进行修改。
希望本文能够对您有所收获,不要忘记点赞收藏哦哦👍🏻。
若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途。