8

Some rule sets and guidelines of embedded software development forbid recursion entirely. I use arm-none-eabi-gcc with an ARM Cortex-M4 based microcontroller.

I'm looking for a static analysis tool which would warn me about the use of recursion in the code. I have little experience with such tools. Is it possible to use clang-tidy or the clang static analyzer for this? If yes, how can I configure them to warn about recursion?

(A quick look at the gcc option summary tells me that gcc alone can't do this.)

NOTE:

  • Please don't tell me "just don't recurse". The code base is big and not mine alone. I'd like to be able to prove that there is no recursion in it.
    (That is like saying "just don't dereference a null pointer". While nobody does on purpose, tools exist for a reason to verify that it doesn't happen.)
9
  • 3
    If you need someone watching over your sholder programming, do a peer-review. Otherwise, just don't write recursive code! By desing, the compiler cannot warn you about recursion - unless you only use static functions and no function pointers. Commented Jul 19, 2017 at 15:09
  • 2
    @Olaf well it could warn you if you use recursion with static functions (which is the kind of recursion that is done most of the time). Commented Jul 19, 2017 at 15:13
  • 2
    gcc is first and foremost a compiler, and could but does not do a number of static analysis functions. There are, however, a number of available 3rd party static analysis tools that generate call graphs, etc. This would be most useful if you've inherited or must analyze a large body of source code. If it's new code, then design practice and peer reviews is the appropriate way to achieve it. Commented Jul 19, 2017 at 15:15
  • As written, I think this question is either too broad or requesting a tool recommendation. Certainly, the clang library could be used to implement a static analysis which would detect some recursive calls. If you are interested, it might make for a good starter project, and if you run into trouble, specific questions would be welcome. Commented Jul 19, 2017 at 15:49
  • I don't understand. Are you a split personality guy? You wake up in the morning and notice that the second you has written something when you were sleeping. Just do not recurse. Commented Jul 19, 2017 at 15:52

3 Answers 3

5

This is solvable using callgraph data generated by Clang.

Step 1. Generate call graph information using clang:

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

(From Generate calling graph for C++ code, replacing -dot-callgraph with -print-callgraph.)

For an input like:

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

this will produce:

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'

(In C++, mangled function names can be cleaned up with c++filt; templates get ugly but are doable.) With that data, it's possible to sketch out how one detects recursion:

Step 2. Parse callgraph data into favorite scripting language to form representation of callgraph.

class Graph(object):

  _callees = []

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

Step 3. For each function, walk graph looking for calls to that function. Something kind of like this:

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))
Sign up to request clarification or add additional context in comments.

Comments

4

clang-tidy learned to detect this in version 11.

https://releases.llvm.org/11.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc-no-recursion.html


Note the limitation documented in the PR of the feature:

Here we detect both direct and indirect recursive calls,
although since clang-tidy (unlike clang static analyzer)
is CTU-unaware, if the recursion transcends a single standalone TU,
we will naturally not find it :/

Comments

0

You can investigate tools to produce call trees. They might have an option to detect recursion, including mutual recursion, otherwise you might be able to patch one to get the functionality you want.

See this reponse

A this commercial tool from Roguewave software: TotalView

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.