记一次valgrind引发的打桩失败问题的定位

96
Jiang阿涵
2018.06.16 15:04* 字数 587

Valgrind是Linux下用来检查程序是否有内存泄漏的利器。现在每次运行完UT之后,都会用valgrind跑一下程序,看看有没有内存泄漏的问题。如果你的程序从来没有用valgrind跑过,也没有在代码中置入内存泄漏相关的检查,那么使用valgrind跑一下吧,相信我,你可能会非常惊讶的。

执行UT的时候,常常需要打桩,什么意思?就是用一个“桩函数”,把原来的函数替换掉,使得我们的程序可以跑到一些特定的分支中,来提高代码的覆盖率。这个替换的过程,我就不赘述了。

在我的测试中,发现一个特别奇怪的现象。如果用valgrind执行我的测试代码,当我把原函数old_func替换为new_func_a后,再多替换一次为new_func_b后,发现程序执行的还是new_func_a,也就是第二次的替换没有起任何作用!更神奇的是,当我不用valgrind运行测试程序的时候,没有任何问题!

一开始我也非常怀疑,工具怎么会出错?大概率是我代码的问题。于是开始加日志、各种调试,观察到的结果都是一样的:

  1. 我正确地把原函数替换成new_func_b了,这是没有问题的。
  2. 程序就是执行了new_func_a,也就是我第一次替换的函数。

于是我开始怀疑,难道valgrind有什么缓存?把new_func_a给缓存起来了?于是我抱着最后的希望,看了一下valgrind的参数,有没有类似的开关,可以把这种缓存给关闭。看了几遍之后,我猛然发现,有一个参数叫“smc-check”,虽然看起来和缓存无关,但是我看到了“self-modifying code”这样的字眼,所谓打桩不就是一种“self-modifying code”吗?

然后我立刻搜索了一个这个参数,OK,这个参数正是我需要的:

--smc-check=<none|stack|all|all-non-file> [default: all-non-file for x86/amd64/s390x, stack for other archs]

This option controls Valgrind's detection of self-modifying code. If no checking is done, 
when a program executes some code, then overwrites it with new code, and executes the new code, 
Valgrind will continue to execute the translations it made for the old code. 
This will likely lead to incorrect behaviour and/or crashes.

加了这个参数,再次运行程序,搞定了!

工程实践
Web note ad 1