记一次神奇的debug经历:
注:本文针对单线程的场景
1、C++在编译时加上 两个编译选项,可以在运行时进行内存检测,发现内存访问异常会自动退出并打印内存访问异常的原因:
-fsanitize=address -fno-omit-frame-pointer
2、最近程序总是崩溃,运行上述内存检测后,发现是在退出一个函数时崩掉的,该函数的复杂局部变量,只有一个unordered_map,退出函数时析构栈上的变量,就报错了:
3、将工程代码最小化,发现只要很简单的逻辑就能复现问题,报错和上面一模一样:
#include <cstdio>
#include <unordered_map>
#include <vector>
typedef int int32_t;
class TestMap
{
public:
bool code_init(std::vector<int32_t>& code_list);
};
bool TestMap::code_init(std::vector<int32_t>& code_list)
{
std::unordered_map<int32_t, std::vector<int32_t>> map1;
for(const int32_t& code : code_list)
{
map1[code].push_back(code);
}
//return true;
}
int main(int argc,char **argv)
{
std::vector<int32_t> code_list;
for(int32_t i = 0; i < 2; i++)
code_list.push_back(i);
for(int32_t i = 0; i < 2; i++)
code_list.push_back(i+600000);
TestMap s;
s.code_init(code_list);
printf("code_init success!\n");
return 0;
}
4、经过控制变量测试,发现该问题的几个触发特征:
1)首先必须开启编译优化,-O0不会出问题,但**-Og以上**都会出问题;
2)unordered_map是栈变量,且所在的函数声明了返回值但并未返回任何值,void类型的函数不会出问题;
3)常见的gcc 4.8.5和gcc 7.5均未触发该问题,我是在gcc 11.2.1版本触发的
5、看了一下异常部分,unordered_map使用的hash_table在析构时,会先memset为0,然后再调用allocator进行析构,但开启编译优化了以后,变成先释放指针,然后再memset,会造成程序的隐患。