我希望能够检测我的函数(或它调用的任何其他函数)是否最终会在我的单元测试中调用某些特定函数(例如grid_columnconfigure
和sticky='nsew'
):我的软件中的一小部分具有硬实时要求,并且我想确保没有人添加会在这些函数中偶然触发分配的东西(并让我的CI管道自动检查它)。
我知道我可以在gdb上设置一个断点,但理想情况下我想做一些类似的事情:
malloc
理想情况下,如果在两次检查之间调用malloc,测试将以不太脏的方式失败(例如,不是free
)。
理想情况下,这可以在任何系统上运行,但我现在可以使用只在Linux上运行的东西。这有可能吗?也许通过LD_PRELOAD hack来取代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);
}
,但我不想为我感兴趣的所有函数做这个。
答案 0 :(得分:3)
单元测试他们测试的调用函数。您想知道单元测试调用的函数F是否最终可以调用malloc(或new或...)。看起来你真正想要做的是为整个系统构建一个调用图,然后询问关键函数F 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 Front End,已用于analyze C systems of some 16 million lines/50,000 functions with indirect calls生成保守的正确调用图。
我们还没有专门为大型系统构建C ++调用图,但DMS的C++ Front End使用相同的DMS通用流分析和调用图生成“技术上简单”。当构建一个正确且大规模运行的静态分析时,没有什么是微不足道的。
答案 1 :(得分: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
警告:编译器手没有触及代码。但是你明白了。
答案 2 :(得分:1)
这不是一个完整的答案,但您可以尝试使用Valgrind来计算alloc和frees。默认情况下,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
}
现在您可以运行实际测试并将分配数量与基线测试进行比较。如果它们不等于测试代码中发生的某些分配。你可以记录这个事实,或者用其他任何你想要的方式发出信号。
答案 3 :(得分:1)
如果您使用的是GNU C库,只要使用_malloc_hook ()
系列的某个函数,就可以使用malloc
和类似的函数调用用户定义的函数。 / p>
这样的钩子函数可以分析调用跟踪(使用backtrace()
),以便查找是否在此调用链中允许malloc
,如果没有,则打印有关罪魁祸首的消息。