我们正在使用C在ARM内核(即嵌入式系统)上构建系统。问题是:我们如何才能以正式的方式避免再入问题,以便我们确信所有重新进入的错误都会被删除。这可能不是一个实际的愿望,但对于任何系统来说肯定都很重要,我想。
仅仅是为了讨论,我想绘制UML图或拥有一个完整的状态机将是一个良好的开端(但是如何在整个系统开发之后生成它?)。关于如何使用状态机/ UML图进行分析的任何建议?
答案 0 :(得分:4)
快速修复,如果您怀疑某事:
int some_func(int x, int y)
{
static volatile int do_not_enter_twice = 0;
assert(!(do_not_enter_twice++));
/* some_func continued */
do_not_enter_twice--;
return whatever;
}
更长的回答:
使用某个工具制作call graph并从那里手动继续。
答案 1 :(得分:4)
我不确定你想要解决的问题,但让我做出有根据的猜测。
第一点是确定可能存在问题的功能。重新进入可以通过递归调用发生,这可以通过几个嵌套调用,甚至可以通过回调/依赖注入或多个线程中使用的函数来隐藏。
您可以绘制定向调用图。假设函数A调用B和C,函数B调用D,E和F,函数C调用任何东西,依此类推。多线程时为每个线程绘制这个。如果图中有循环,则进行此循环的所有函数都需要重新进入。在这种情况下,您可以忽略子分支。在多个线程中使用的函数也需要是安全的,但现在包括所有子分支,因为您并不确切知道每个线程当前的位置。当使用锁时,事情会变得复杂和复杂,所以现在让我们忽略它。
此步骤肯定可以通过代码分析工具自动完成。
现在确定了功能,
答案 2 :(得分:2)
可以计算巨大调用图的工具是DMS Software Reengineering Toolkit及其C前端。 C前端用于解析C代码; DMS内置机器来计算控制和数据流分析,点到分析并提取呼叫直接和呼叫间接直通指针事实
DMS已被用于为 3500万行C代码(= 250,000个函数)的C源代码系统构建调用图,然后从该调用图中提取信息。构建这样的大图时的一个关键问题是尽可能准确地计算点到信息(理论上存在严格的理论限制),以便间接函数调用保守地针对最小数量的误报目标。
在您的情况下,正如其他作者所指出的那样,要提取的信息“是否存在循环?”在此调用图中。
在这种规模下,您不希望手动执行此操作,并且每次准备好生产构建时都需要重新执行此操作。所以机械化检查会很有意义。
答案 3 :(得分:0)
我会做两件事来检查你的代码。彻底的群体代码审查(目的是仅查找重入错误,而不是样式或其他错误)。其次,对问题进行实际攻击。
例如:
int myfunction(int x, int y) {
REENTRANCE_CHECK;
... body of function
}
现在你可以#define REENTRANCE_CHECK为空(用于生产)或者某些检查函数的代码永远不会被重新输入。运行您的测试(如果您没有测试,然后在附带调试器的设备上运行它)并启用这些检查,并查看是否有任何下降。
同样,您可以添加调试逻辑来检测对全局状态的错误更新。编写使用锁的代码(如果在已经持有的情况下获取它们则会断言。
这样的事情:
int my_global;
DEFINE_GLOBAL_LOCK(my_global);
void my_dangerous_function() {
...
LOCK_GLOBAL(my_global);
.. some critical section of code that uses my_global.
UNLOCK_GLOBAL(my_global);
...
}
同样,DECLARE_GLOBAL_LOCK,LOCK_GLOBAL和UNLOCK_GLOBAL可以#defined为真正的锁定代码(当然你必须编写)用于测试,对于生产它们可以#defined为无。
这种方法只有在找到并包装对全局状态的所有访问时才有效,但搜索很容易。