【问题标题】:How to get a warning for using recursion?如何获得使用递归的警告?
【发布时间】:2017-07-19 15:08:03
【问题描述】:

嵌入式软件开发的一些规则集和指南完全禁止递归。我将 arm-none-eabi-gcc 与基于 ARM Cortex-M4 的微控制器一起使用。

我正在寻找一个静态分析工具,它会警告我在代码中使用递归。我对这些工具的经验很少。是否可以为此使用 clang-tidy 或 clang 静态分析器?如果是,我该如何配置它们以警告递归?

(快速查看gcc option summary 告诉我,仅 gcc 无法做到这一点。)

注意:

  • 请不要告诉我“只是不要递归”。代码库很大,不是我一个人。我希望能够证明其中没有递归。
    (这就像说“不要取消引用空指针”。虽然没有人故意这样做,但存在工具是为了验证它不会发生。)

【问题讨论】:

  • 如果您需要有人监督您的 sholder 编程,请进行同行评审。否则,就不要编写递归代码!通过设计,编译器不能警告您递归 - 除非您只使用 static 函数而不使用函数指针。
  • @Olaf 如果您使用带有静态函数的递归(这是大多数时间都在执行的那种递归),它可能会警告您。
  • gcc 首先是一个编译器,可以但不做一些静态分析功能。但是,有许多可用的 3rd 方静态分析工具可以生成调用图等。如果您继承或必须分析大量源代码,这将是最有用的。如果是新代码,那么设计实践和同行评审是实现它的合适方法。
  • 正如所写,我认为这个问题要么太宽泛,要么要求工具推荐。当然,clang 库可用于实现静态分析,以检测 一些 递归调用。如果您有兴趣,它可能会成为一个很好的入门项目,如果您遇到麻烦,欢迎提出具体问题。
  • 我不明白。你是一个人格分裂的人吗?你早上醒来,注意到你在睡觉时写了一些东西。只是不要递归。

标签: c gcc recursion clang


【解决方案1】:

这可以使用 Clang 生成的调用图数据来解决。

步骤 1. 使用 clang 生成调用图信息:

clang -S -emit-llvm SourceFile.c -o - | opt -analyze -print-callgraph 

(来自Generate calling graph for C++ code,将-dot-callgraph替换为-print-callgraph。)

对于这样的输入:

void a(){}
void b(){a();}
void c(){a(); b();}
void d(){a(); c();}
void e(){e();}

这将产生:

CallGraph Root is: <<null function: 0x0x7fdef25036c0>>
Call graph node <<null function>><<0x7fdef25036c0>>  #uses=0
  CS<0x0> calls function 'a'
  CS<0x0> calls function 'b'
  CS<0x0> calls function 'c'
  CS<0x0> calls function 'd'

Call graph node for function: 'a'<<0x7fdef2503750>>  #uses=4

Call graph node for function: 'b'<<0x7fdef25037d0>>  #uses=2
  CS<0x7fdef2500a38> calls function 'a'

Call graph node for function: 'c'<<0x7fdef2503870>>  #uses=2
  CS<0x7fdef2500cb8> calls function 'a'
  CS<0x7fdef2500d28> calls function 'b'

Call graph node for function: 'd'<<0x7fdef2503970>>  #uses=1
  CS<0x7fdef2500fe8> calls function 'a'
  CS<0x7fdef2501058> calls function 'c'

Call graph node for function: 'e'<<0x7f8912d03c10>>  #uses=2
  CS<0x7f8912d01318> calls function 'e'

(在 C++ 中,可以使用 c++filt 清理损坏的函数名称;模板变得丑陋但可行。)有了这些数据,就可以勾勒出如何检测递归:

步骤 2. 将 callgraph 数据解析为喜欢的脚本语言以形成 callgraph 的表示。

class Graph(object):

  _callees = []

  def add_callee(self, f):
    self._callees.append(f)
    # etc

第 3 步。对于每个函数,遍历图表以查找对该函数的调用。有点像这样:

def walkGraph(node,f,stack):
  for callee in node._callees:
    if f == callee:
      print('Recursion!')
      dumpStack(stack,f)
    else:
      walkGraph(callee,f,stack.append(node))

【讨论】:

    【解决方案2】:

    clang-tidy 在版本 11 中学会了检测到这一点。

    https://clang.llvm.org/extra/clang-tidy/checks/misc-no-recursion.html

    【讨论】:

      【解决方案3】:

      您可以研究生成调用树的工具。他们可能有一个检测递归的选项,包括相互递归,否则你也许可以修补一个以获得你想要的功能。

      See this reponse

      Roguewave 软件的这个商业工具:TotalView

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-03
        • 1970-01-01
        • 2016-06-30
        相关资源
        最近更新 更多