首页 > 解决方案 > 单独编译库源文件时单元测试中的 Seg 错误

问题描述

我正在编写一个小型库,直到现在直接使用add_executable命令中的单元测试添加库源文件,类似于以下内容:

CMakeLists.txt(生成的可执行文件OK)

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 17) 
find_package(Eigen3 REQUIRED)
find_package(GTSAM 4.0.2 REQUIRED)

add_executable(my_test test/unit_test.cpp src/my_lib.cpp)
target_include_directories(my_test PUBLIC include ${EIGEN3_INCLUDE_DIR} ${GTSAM_INCLUDE_DIR})
target_link_libraries(my_test PUBLIC gtsam)

一切正常,直到我创建了一个实际的库并链接了单元测试,类似于下面:

CMakeLists.txt(生成的可执行文件不正常)

cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 17) 

find_package(Eigen3 REQUIRED)
add_library(my_lib STATIC src/my_lib.cpp)
target_include_directories(my_lib PRIVATE include ${EIGEN3_INCLUDE_DIR})

find_package(GTSAM 4.0.2 REQUIRED)
add_executable(my_test test/unit_test.cpp)
target_include_directories(my_test PUBLIC include ${EIGEN3_INCLUDE_DIR} ${GTSAM_INCLUDE_DIR})
target_link_libraries(my_test PUBLIC gtsam my_lib)

但是,现在测试段出现故障。valgrind报告以下内容:

==18501== Memcheck, a memory error detector
==18501== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18501== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18501== Command: ./my_test
==18501==
==18501== Invalid read of size 8
==18501==    at 0x509CFBB: gtsam::noiseModel::Diagonal::Sigmas(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, bool) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C713: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==  Address 0x7241df8 is 8 bytes before a block of size 8 alloc'd
==18501==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18501==    by 0x4ED9FD: Eigen::internal::aligned_malloc(unsigned long) (Memory.h:159)
==18501==    by 0x507875B: Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::resize(long, long) [clone .constprop.1340] (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x5085698: Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::PlainObjectBase<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> > >(Eigen::DenseBase<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> > > const&) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x509CFA2: gtsam::noiseModel::Diagonal::Sigmas(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, bool) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C713: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==
==18501== Invalid read of size 8
==18501==    at 0x4F3C71D: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==  Address 0x7241da8 is 8 bytes before a block of size 8 alloc'd
==18501==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18501==    by 0x4ED9FD: Eigen::internal::aligned_malloc(unsigned long) (Memory.h:159)
==18501==    by 0x51492E1: Eigen::Matrix<double, -1, 1, 0, -1, 1>::Matrix<int>(int const&) [clone .constprop.1305] (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C6F4: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==
==18501== Invalid read of size 8
==18501==    at 0x509CDD1: gtsam::noiseModel::Diagonal::Variances(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, bool) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C77A: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==  Address 0x72420f8 is 8 bytes before a block of size 24 alloc'd
==18501==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18501==    by 0x4ED9FD: Eigen::internal::aligned_malloc(unsigned long) (Memory.h:159)
==18501==    by 0x509A53B: Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::resize(long, long) [clone .constprop.1216] (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x50A4A0A: Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::PlainObjectBase<Eigen::CwiseUnaryOp<Eigen::internal::scalar_sqrt_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> const> >(Eigen::DenseBase<Eigen::CwiseUnaryOp<Eigen::internal::scalar_sqrt_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> const> > const&) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x509CD63: gtsam::noiseModel::Diagonal::Variances(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, bool) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C77A: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501== Invalid read of size 8
==18501==    at 0x4F3C784: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==  Address 0x7242098 is 8 bytes before a block of size 24 alloc'd
==18501==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18501==    by 0x4ED9FD: Eigen::internal::aligned_malloc(unsigned long) (Memory.h:159)
==18501==    by 0x513A304: Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::DenseBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&) (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4F3C766: _GLOBAL__sub_I_lago.cpp (in /usr/local/lib/libgtsam.so.4.0.2)
==18501==    by 0x4010732: call_init (dl-init.c:72)
==18501==    by 0x4010732: _dl_init (dl-init.c:119)
==18501==    by 0x40010C9: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
==18501==
test_orient: /usr/local/include/eigen3/Eigen/src/Core/DenseStorage.h:128: Eigen::internal::plain_array<T, Size, MatrixOrArrayOptions, 32>::plain_array() [with T = double; int Size = 4; int MatrixOrArrayOptions = 0]: Assertion `(internal::UIntPtr(eigen_unaligned_array_assert_workaround_gcc47(array)) & (31)) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.
==18501==
==18501== Process terminating with default action of signal 6 (SIGABRT)
==18501==    at 0x5F95E97: raise (raise.c:51)
==18501==    by 0x5F97800: abort (abort.c:79)
==18501==    by 0x5F87399: __assert_fail_base (assert.c:92)
==18501==    by 0x5F87411: __assert_fail (assert.c:101)
==18501==    by 0x4D98AE: Eigen::internal::plain_array<double, 4, 0, 32>::plain_array() (DenseStorage.h:128)
==18501==    by 0x4D78AD: Eigen::DenseStorage<double, 4, 4, 1, 0>::DenseStorage() (DenseStorage.h:187)
==18501==    by 0x4D61C7: Eigen::PlainObjectBase<Eigen::Matrix<double, 4, 1, 0, 4, 1> >::PlainObjectBase() (PlainObjectBase.h:484)
==18501==    by 0x4D4DBB: Eigen::Matrix<double, 4, 1, 0, 4, 1>::Matrix() (Matrix.h:259)
==18501==    by 0x4ECF6A: orient::quaternionFromAngleAxis(Eigen::Matrix<double, 3, 1, 0, 3, 1> const&) (from_angle_axis.cpp:56)
==18501==    by 0x4D21C4: ____C_A_T_C_H____T_E_S_T____10() (from_angle_axis_test.cpp:95)
==18501==    by 0x3F5A43: Catch::TestInvokerAsFunction::invoke() const (catch2.hpp:14054)
==18501==    by 0x3F50A2: Catch::TestCase::invoke() const (catch2.hpp:13947)
==18501==
==18501== HEAP SUMMARY:
==18501==     in use at exit: 42,110 bytes in 347 blocks
==18501==   total heap usage: 4,871 allocs, 4,524 frees, 507,954 bytes allocated
==18501==
==18501== LEAK SUMMARY:
==18501==    definitely lost: 64 bytes in 4 blocks
==18501==    indirectly lost: 0 bytes in 0 blocks
==18501==      possibly lost: 0 bytes in 0 blocks
==18501==    still reachable: 42,046 bytes in 343 blocks
==18501==         suppressed: 0 bytes in 0 blocks
==18501== Rerun with --leak-check=full to see details of leaked memory
==18501==
==18501== For counts of detected and suppressed errors, rerun with: -v
==18501== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)

不幸的是,Eigen 断言中的链接让我变得更聪明。

我的问题是:seg 错误是由于错误地链接到我自己的库或 GTSAM 造成的吗?如果是这样,我该如何解决?如果不是,我是否应该假设我的库中存在仅在第二种情况下才表现出来的错误?

编辑:

重现的最小工作示例:

包括/my_lib.hpp

#pragma once
void f();

src/my_lib.cpp

#include <my_lib.hpp>
#include <unsupported/Eigen/KroneckerProduct>

void f() 
{
  Eigen::Matrix3d A = Eigen::Matrix3d::Random();
  Eigen::Matrix<double, 9, 3> B = Eigen::Matrix<double, 9, 3>::Random();
  Eigen::Matrix3d I = Eigen::Matrix3d::Identity();
  Eigen::Matrix<double, 9, 3> tmp = Eigen::kroneckerProduct(I, A) * B;
}

测试/unit_test.cpp

#include <my_lib.hpp>
#include <gtsam/base/numericalDerivative.h>

int main()
{
  f();
  return 0;
}

标签: c++cmake

解决方案


您的库使用 Eigen3 库,它也是测试中使用的 gtsam 库的一部分。您需要确保在这两种情况下都使用相同的Eigen3 库,否则可能会发生细微的错误(如您观察到的错误)。


从技术上讲,您的库和基于 gtsam 的测试是单独的对象文件,因此它们是独立编译的,并且它们都是正确的。

它是一个链接器,可以执行不需要的优化,例如从这些目标文件中合并函数。例如,如果这些函数具有相同的名称。如果您找到一种方法来禁止对链接器进行此类优化,那么即使在gtsam 用于您的库的 Eigen3 库不兼容的情况下,结果测试也可以正常工作。

但是,从项目稳定性的角度来看,禁用链接器优化是一种非常微妙的方式。仅将这种方式用作最后的手段。


推荐阅读