记录一次崩溃问题排查过程(gtsam库相关)
- 有同学在使用 gtsam库(4.1.1)的时候,出现了崩溃问题,位置在
Eigen::Quaternion赋值操作上operator=; - 由于堆栈打印信息不全,让他用 debug 模式编译(
-g -O0) , 再次测试得到的堆栈信息如下:
bash
libMapping3d.so!_mm256_store_pd(__m256d__A, double *__P) (\usr\lib\gcc\x86_64-linux-gnu\9\include\avxintrin.h:868)
libMapping3d.so!Eigen::internal::pstore<double, double__vector(4)>(double*, double __vector(4) const&)(double *to, const Eigen::internal::Packet4d & from) (\usr\include\eigen3\Eigen\src\Core\arch\AVX\PacketMath.h:252)
libMapping3d.so!Eigen::internal::pstoret<double, double__vector(4), 32>(double*, double __vector(4) const&)(const double (&) __attribute__ ((vector_size(4))) from, double *to) (\usr\include\eigen3\Eigen\src\Core\GenericPacketMath.h:474)
libMapping3d.so!Eigen::internal::assign_op<double, double>::assignPacket<32, double__vector(4)>(double*, double __vector(4) const&) const(const Eigen::internal::assign_op<double, double> *const this, double* a, const double (&) __attribute__ ((vector_size(4))) b) (\usr\include\eigen3\Eigen\src\Core\functors\AssignmentFunctors.h:28)
libMapping3d.so!Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>::assignPacket<32, 32, double__vector(4)>(long, long)(Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0> *const this, Eigen::Index row, Eigen::Index col) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:652)
libMapping3d.so!Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>::assignPacketByOuterInner<32, 32, double __vector(4)>(long, long)(Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>* const this, Eigen::Index outer, Eigen::Index inner) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:666)
libMapping3d.so!Eigen::internal::copy_using_evaluator_innervec_CompleteUnrolling<Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>, 0, 4>::run(Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0> & kernel) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:274)
libMapping3d.so!Eigen::internal::dense_assignment_loop<Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0>, 2, 2>::run(Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::evaluator<Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Eigen::internal::assign_op<double, double>, 0> & kernel) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:468)
libMapping3d.so!Eigen::internal::call_dense_assignment_loop<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::internal::assign_op<double, double> >(Eigen::Matrix<double, 4, 1, 0, 4, 1> & dst, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & src, const Eigen::internal::assign_op<double, double> & func) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:741)
libMapping3d.so!Eigen::internal::Assignment<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::internal::assign_op<double, double>, Eigen::internal::Dense2Dense, void>::run(Eigen::Matrix<double, 4, 1, 0, 4, 1> & dst, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & src, const Eigen::internal::assign_op<double, double> & func) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:879)
libMapping3d.so!Eigen::internal::call_assignment_no_alias<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::internal::assign_op<double, double> >(Eigen::Matrix<double, 4, 1, 0, 4, 1> & dst, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & src, const Eigen::internal::assign_op<double, double> & func) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:836)
libMapping3d.so!Eigen::internal::call_assignment<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::internal::assign_op<double, double> >(Eigen::Matrix<double, 4, 1, 0, 4, 1>&, Eigen::Matrix<double, 4, 1, 0, 4, 1> const&, Eigen::internal::assign_op<double, double> const&, Eigen::internal::enable_if<!Eigen::internal::evaluator_assume_aliasing<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::internal::evaluator_traits<Eigen::Matrix<double, 4, 1, 0, 4, 1> >::Shape>::value, void*>::type)(Eigen::Matrix<double, 4, 1, 0, 4, 1> & dst, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & src, const Eigen::internal::assign_op<double, double> & func) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:804)
libMapping3d.so!Eigen::internal::call_assignment<Eigen::Matrix<double, 4, 1, 0, 4, 1>, Eigen::Matrix<double, 4, 1, 0, 4, 1> >(Eigen::Matrix<double, 4, 1, 0, 4, 1> & dst, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & src) (\usr\include\eigen3\Eigen\src\Core\AssignEvaluator.h:782)
libMapping3d.so!Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 0, 4, 1> >::_set<Eigen::Matrix<double, 4, 1, 0, 4, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 0, 4, 1> >* const this, const Eigen::DenseBase<Eigen::Matrix<double, 4, 1, 0, 4, 1> > & other) (\usr\include\eigen3\Eigen\src\Core\PlainObjectBase.h:714)
libMapping3d.so!Eigen::Matrix<double, 4, 1, 0, 4, 1>::operator=(Eigen::Matrix<double, 4, 1, 0, 4, 1> *const this, const Eigen::Matrix<double, 4, 1, 0, 4, 1> & other) (\usr\include\eigen3\Eigen\src\Core\Matrix.h:208)
libMapping3d.so!Eigen::QuaternionBase<Eigen::Quaternion<double, 0> >::operator=(Eigen::QuaternionBase<Eigen::Quaternion<double, 0> >* const this, const Eigen::QuaternionBase<Eigen::Quaternion<double, 0> > & other) (\usr\include\eigen3\Eigen\src\Geometry\Quaternion.h:493)
libMapping3d.so!Eigen::Quaternion<double, 0>::operator=(Eigen::Quaternion<double, 0> *const this, const Eigen::Quaternion<double, 0> & other) (\usr\include\eigen3\Eigen\src\Geometry\Quaternion.h:243)
libMapping3d.so!ModelParam::operator=(ModelParam* const this) (\home\source\dataprocess\Mapping3d\internal_include\common\data_process.h:78)
libMapping3d.so!Mapping3DParam::operator=(Mapping3DParam *const this) (\home\source\dataprocess\Mapping3d\internal_include\common\data_process.h:323)
libMapping3d.so!mapping3d::Mapping3DAlg::init(mapping3d::Mapping3DAlg* const this, const Mapping3DParam & param)
......
-
看着挺多的,实际关键的信息有一下两点:
- 一是
Eigen::Quaternion<double, 0>::operator=,确定是 eigen 四元数类赋值报错 - 二是
_mm256_store_pd(__m256d__A, double *__P) (\usr\lib\gcc\x86_64-linux-gnu\9\include\avxintrin.h:868)- 这个是 底层 AVX 指令:将 256 位向量寄存器中的数据,存储到内存地址__P 。AVX 是 x86_64 架构的 SIMD 指令集,用于向量运算加速(如矩阵 / 四元数计算)。
- 一是
-
系统中安装的eigen库,一般来说是没开加速优化的,于是排查发现 gtsam库 源码居然包了一个 eigen库 ,在
gtsam/3rdparty/Eigen目录 ; -
实际程序运行的时候是用的这个 eigen库(开了加速优化)。
-
解决方案有两个:
- 一是 按规范进行
字节对齐 - 二是 重新编译gtsam库,使用系统 eigen库 (未开加速优化)
- 一是 按规范进行
方案一 :按规范进行 字节对齐
- 字节对齐写法如下:
c++
#include <Eigen/Core>
#include <Eigen/Geometry>
// 1. 用 __attribute__((aligned(32))) 强制结构体整体 32 bit对齐(GCC 专用)
// 2. 重载 new 运算符,确保堆上分配时对齐(Eigen 提供的宏)
struct __attribute__((aligned(32))) ModelParam {
// Eigen 成员:需放在结构体前部(减少内存碎片,确保对齐)
Eigen::Quaterniond rotation; // 四元数(8 bit * 4 = 32 bit,需 32 bit对齐)
Eigen::Vector4d translation; // 4 维平移向量(8 bit * 4 = 32 bit)
//
double scale;
// 关键:Eigen 提供的宏,重载 new/delete,确保堆上分配时 32 bit对齐
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
// 自定义赋值运算符(若需手动实现,需确保 Eigen 成员正确赋值)
ModelParam& operator=(const ModelParam& other) {
if (this != &other) {
rotation = other.rotation; // Eigen 成员自动处理对齐赋值
translation = other.translation;
scale = other.scale;
}
return *this;
}
};
// 用重载后的 new 分配对象内存
ModelParam* param_ptr1 = new ModelParam(); // 自动 32 bit对齐
- 这样 修改后 就可以正常运行了,但是由于他应该是 找的一个开源算法库,里面涉及用到 Eigen 的类 较多,修改起来不方便;
方案二:重新编译gtsam库,使用系统 eigen库
-
使用的docker容器 系统环境是 ubuntu22.04 , boost_1.71.0 , eigen_3.4.0 , gcc-11, cmake_3.30.2
-
满足 gtsam 的编译要求.
-
下载 gtsam 库:
bash
git clone https://github.com/borglab/gtsam.git
git checkout 4.1.1
- 大概研究了下 gtsam 库 cmake 文件结构,发现里面配置不少,我就不需要的都关了,并配置使用系统 eigen 库,最终的编译脚本如下:
bash
mkdir -p build && cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DGTSAM_USE_SYSTEM_EIGEN=ON \
-DGTSAM_BUILD_UNSTABLE=OFF \
-DGTSAM_BUILD_PYTHON=OFF \
-DGTSAM_WITH_TBB=OFF \
-DGTSAM_WITH_EIGEN_MKL=OFF \
-DGTSAM_WITH_EIGEN_MKL_OPENMP=OFF \
-DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF \
-DGTSAM_BUILD_TESTS=OFF \
-DGTSAM_BUILD_TIMING_ALWAYS=OFF \
-DCMAKE_INSTALL_PREFIX=/opt/tong/prefix/install
make -j4
make install
-DCMAKE_BUILD_TYPE=Release: 设置构建类型为 Release。-DGTSAM_USE_SYSTEM_EIGEN=ON:使用系统上已安装的 Eigen 库 。-DGTSAM_BUILD_UNSTABLE=OFF:关闭构建 GTSAM 中标记为 (unstable) 的功能或模块。-DGTSAM_BUILD_PYTHON=OFF: 关闭构建 GTSAM 的 Python 接口(封装)。-DGTSAM_WITH_TBB=OFF: 关闭 Intel Threading Building Blocks (TBB) 支持。-DGTSAM_WITH_EIGEN_MKL=OFF: 关闭使用 Intel Math Kernel Library (MKL) 来加速 Eigen 库的计算。-DGTSAM_WITH_EIGEN_MKL_OPENMP=OFF: 关闭使用 MKL 的 OpenMP 后端来加速 Eigen。-DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF: 关闭构建示例程序。-DGTSAM_BUILD_TESTS=OFF:关闭构建 GTSAM 的单元测试和测试程序。(验证库的功能是否正确可以打开,但在部署安装时通常不需要)-DCMAKE_INSTALL_PREFIX=/opt/tong/prefix/install: 设置安装目录。
- 以上构建一个精简、优化、不包含额外功能 的 GTSAM 发布版本 (使用系统
Eigen库,并禁用了并行计算库的支持(TBB/MKL))。 - 使用以上编译好的库替换原来gtsam库就可以正常运行了。
- 如果想用计算加速库,必须遵循其对应的规则和要求。
- 编出来库 结构如下:
bash
root@ac41c3b78c6c:/opt/tong/prefix/install# tree -L 2
.
|-- include
| |-- CppUnitLite
| `-- gtsam
`-- lib
|-- cmake
|-- libCppUnitLite.a
|-- libgtsam.so -> libgtsam.so.4
|-- libgtsam.so.4 -> libgtsam.so.4.1.1
|-- libgtsam.so.4.1.1
`-- libmetis-gtsam.so
- 查看库的依赖:
bash
root@ac41c3b78c6c:/opt/tong/prefix/install# ldd lib/libgtsam.so
linux-vdso.so.1 (0x00007fffc3dc7000)
libboost_serialization.so.1.71.0 => /usr/local/lib/libboost_serialization.so.1.71.0 (0x00007f0e2dd9c000)
libboost_filesystem.so.1.71.0 => /usr/local/lib/libboost_filesystem.so.1.71.0 (0x00007f0e2dd7d000)
libboost_timer.so.1.71.0 => /usr/local/lib/libboost_timer.so.1.71.0 (0x00007f0e2dd72000)
libmetis-gtsam.so => not found
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f0e2db46000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0e2da5d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0e2da3d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e2d814000)
libboost_chrono.so.1.71.0 => /usr/local/lib/libboost_chrono.so.1.71.0 (0x00007f0e2d807000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0e2e39f000)
root@ac41c3b78c6c:/opt/tong/prefix/install# ldd lib/libmetis-gtsam.so
linux-vdso.so.1 (0x00007ffe5a122000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007efc926a6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efc9247d000)
/lib64/ld-linux-x86-64.so.2 (0x00007efc92825000)
- eigen 相关函数已经编译进去了,运行时只需再依赖 boost 库就可以。
- 这样库就可以打包(deb)使用了,要注意的是cmake里面路径可能需要修改(如果库位置发生变化)。
总结
- 主要原因还是引入了外部库,且对其使用不熟悉。
- 遇到了 崩溃问题,先把其 详细堆栈信息(debug)保存下来,以供分析。
- 在使用外部库时,需要明确其依赖关系,确保不与其他库冲突。