8

我希望能够检测我的函数(或它调用的任何其他函数)是否最终会在我的单元测试中调用某些特定函数(例如,mallocfree):我的软件的一些小部分具有硬实时要求,并且我想确保没有人在这些函数中添加会意外触发分配的内容(并让我的 CI 管道自动检查它)。

我知道我可以在 gdb 上放一个断点,但理想情况下我想做类似的事情:

void my_unit_test() {
    my_object obj; // perform some initialization that will allocate

    START_CHECKING_FUNCTION(malloc); // also, operator new or std::allocate would be nice
    obj.perform_realtime_stuff();
    STOP_CHECKING_FUNCTION(malloc);
}

std::abort理想情况下,如果在两次检查之间调用了 malloc ,则测试将以不太脏的方式(例如 not )失败。

理想情况下,这可以在任何系统上运行,但我现在可以接受只能在 linux 上运行的东西。这在某种程度上可能吗?也许通过替换 LD_PRELOAD hack malloc,但我宁愿不必为我感兴趣的所有功能这样做。

4

4 回答 4

3

单元测试调用他们测试的函数。您想知道单元测试调用的函数 F 是否最终可以调用 malloc(或 new 或 ...)。似乎您真正想要做的是为整个系统构建一个调用图,然后在调用图中询问关键函数 F 是否可以到达 malloc 等。一旦有了调用图,这很容易计算。

获取调用图并不是那么容易。如果您有一个真正的语言前端来进行名称解析,那么发现模块 A 直接调用模块 B 是“技术上容易的”。找出 A 间接调用的内容并不容易。您需要一个(函数指针)指向分析,而这些很难。而且,当然,您已经决定是否要深入库(例如,std::) 例程。

面对函数指针和方法调用,你的调用图需要保守(这样你就不会错过潜在的调用)并且相当精确(这样你就不会被误报淹没)。

这个 Doxygen 支持声称构建调用图:http ://clang.llvm.org/doxygen/CallGraph_8cpp.html 我不知道它是否处理间接/方法调用或者它有多精确;我对它不是很熟悉,文档似乎很薄。Doxygen 过去在处理间接或精确方面并不享有盛誉,但过去的版本并非基于 Clang。在http://stackoverflow.com/questions/5373714/generate-calling-graph-for-c-code上对此有一些进一步的讨论

您的问题已标记为 c/c++,但似乎与 C++ 有关。对于 C,我们的DMS Software Reengineering Toolkit及其通用流分析和调用图生成支持,与 DMS 的C 前端相结合,已用于分析大约 1600 万行/50,000 个函数的 C 系统,通过间接调用生成保守正确的调用图.

我们没有专门尝试为大型系统构建 C++ 调用图,但与 DMS 的C++ 前端一起使用的相同 DMS 通用流分析和调用图生成将是“技术上简单的” 。在构建正确且大规模运行的静态分析时,没有什么是微不足道的。

于 2018-01-01T08:48:54.733 回答
1

如果您正在使用调用 malloc 的库,那么您可能需要查看Joint Strike Fighter C++ Coding Standards。这是一种针对关键任务软件的编码风格。一个建议是编写自己的分配器。另一个建议是使用类似jemalloc具有统计信息的东西,但由于它面向性能,因此更加不可预测。


你想要的是一个具有间谍功能的模拟库。每个框架的工作方式会有所不同,但这里有一个使用 Google 的示例:

static std::function<void*(size_t)> malloc_bridge;

struct malloc_mock {
    malloc_mock() { malloc_bridge = std::bind(&malloc_mock::mock_, this, _1); }
    MOCK_METHOD1(mock_, void*(size_t));
}

void* malloc_cheat(size_t size) {
    return malloc_bridge(size);
}

#define malloc malloc_cheat

struct fixture {
    void f() { malloc(...); }
};

struct CustomTest : ::testing::test {
    malloc_mock mock_;
};

TEST_F(CustomTest, ShouldMallocXBytes) {
    EXPECT_CALL(mock_, mock_(X))
      .WillOnce(::testing::Return(static_cast<void*>(0)));
    Fixture fix;
    fix.f();
}

#undef malloc

警告:编译器未触及代码。但你明白了。

于 2017-12-31T21:21:36.067 回答
1

如果您使用的是 GNU C 库,则可以使用_malloc_hook ()和类似的函数来调用用户定义的函数,只要malloc使用该系列的函数之一。

这样的钩子函数可以分析调用跟踪(使用backtrace()),以找出malloc在这个调用链中是否被允许,如果不是,则打印有关罪魁祸首的消息。

于 2018-01-13T20:49:04.137 回答
1

这不是一个完整的答案,但您可以尝试使用 Valgrind 来计算分配和释放。默认情况下,默认的 Valgrind 工具 memcheck 计算分配和释放的数量,并在 中打印结果报告HEAP SUMMARY,这是一个示例输出:

$ valgrind ./a.out
==2653== Memcheck, a memory error detector
==2653== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2653== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2653== Command: ./a.out
==2653== 
==2653== 
==2653== HEAP SUMMARY:
==2653==     in use at exit: 0 bytes in 0 blocks
==2653==   total heap usage: 2 allocs, 2 frees, 72,716 bytes allocated
==2653== 
==2653== All heap blocks were freed -- no leaks are possible
==2653== 
==2653== For counts of detected and suppressed errors, rerun with: -v
==2653== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

您可以再添加一个测试来计算基线分配数:

void my_unit_test_baseline() {
    my_object obj; // perform some initialization that will allocate
}

现在您可以运行实际测试并将分配数量与基线测试进行比较。如果它们不等于您测试的代码中发生的某些分配。您可以记录此事实或以其他任何您想要的方式向它发出信号。

于 2018-01-05T19:01:29.120 回答