使用pybind11绑定Armadillo矩阵时出现堆损坏的原因及排查思路
我已经研究这个问题两周了,没法在代码库外构造出可复现的样例,因此需要大家的帮助。我暂时不确定问题出在pybind11还是Armadillo,可以排除是Carma的问题,因为错误在没有类型转换的场景下也会发生。
编辑补充:现在确认这是Carma的bug,我已经构造出了可复现的最小样例。
我一直尝试精简出最小可复现样例(MRE)但没有成功。我会在这里说明已知的信息,由于信息不足以复现bug,我最需要的是排查方向的建议。这是一个极难复现的内存损坏错误(堆损坏,Windows fatal exception: code 0xc0000374),触发场景为:未初始化的矩阵A被赋值为一个足够大的矩阵,使得Armadillo需要申请动态内存,在该内存被释放时就会发生崩溃。
我的运行环境为Windows 10,使用Armadillo 10.6.2、pybind11 v2.7.1,编译器为Clang 11。
// mat_container.cpp #include "mat_container.h" MatrixContainer::MatrixContainer(size_t d) { A = arma::Mat<double>(d, d, arma::fill::eye); std::cerr << "filled arma matrix\n"; }
绑定代码如下:
#include <pybind11/numpy.h> #include <pybind11/pybind11.h> #include <armadillo> #include <carma> #include "mat_container.h" PYBIND11_MODULE(example, m) { py::class_<MatrixContainer>(m, "MC").def(py::init<size_t, bool>()) .def_readwrite("A", &MatrixContainer::A); }
触发崩溃只需要在Python中调用example.MC(11),崩溃发生在成员变量(而非赋值的临时变量)的矩阵析构函数释放内存时。崩溃前的Armadillo调试日志如下:
@ __cdecl arma::Mat<double>::~Mat(void) [eT = double] [this = 000001569470DBA0] Mat::destructor: releasing memory
我尝试构造的MRE中,我复现了完全相同的结构,绑定代码使用的MatrixContainer类也在独立的库中编译,但没能复现bug,我不清楚遗漏了什么配置。
异常特征汇总
- 仅通过pybind11调用时触发,纯C++环境下不会出现
- 仅当包含矩阵的类的构造函数放在独立编译的源文件中时触发,构造函数放在头文件中时不会出现
- 仅当给对象的矩阵成员赋值非单位矩阵(比如
np.ones而非np.eye)时触发但该情况仅在我源码中的类上复现,我创建的小型测试类不会出现 - 仅当A的大小超过Armadillo局部内存阈值、需要申请动态内存时触发
- 赋值前调用
set_size()设置A的大小无法解决问题 - 该问题看起来是Armadillo特有,仅在Armadillo释放内存时触发(通过
ARMA_EXTRA_DEBUG确认),使用vector等其他动态内存分配的类不会出现类似错误
当使用动态内存的A被替换为使用局部内存的矩阵时,会触发崩溃:
mc = MC(11) mc.A = np.eye(3)
赋值mc.A时崩溃前的Armadillo调试日志如下:
@ void __cdecl arma::Mat<double>::init_warm(arma::uword, arma::uword) [eT = double] [in_n_rows = 3, in_n_cols = 3] Mat::init(): releasing memory
反向操作则不会触发崩溃,以下代码可以正常运行,A销毁时释放动态内存也不会崩溃:
mc = MC(3) mc.A = np.eye(11)
如果11x11的A被替换为另一个来自numpy的11x11矩阵,也会触发崩溃,调试日志和第一个无Python赋值的例子一致,为Mat::destructor: releasing memory。从上述示例我推断:当Carma(而非Armadillo直接)构造的动态内存矩阵被赋值给使用局部内存的A时,崩溃不会发生。
更新
目前还在排查中,但看起来错误的触发原因是arma_extra_code处于开启状态(我的MRE中没有开启该配置),问题根源似乎是Carma设置的ARMA_ALIEN_MEM相关函数。
内容的提问来源于stack exchange,提问作者kjohnsen

