2

I have following requirement:

  • Adding text at the entry and exit point of any function.
  • Not altering the source code, beside inserting from above (so no pre-processor or anything)

For example:

void fn(param-list)
{
    ENTRY_TEXT (param-list)
    //some code
    EXIT_TEXT
}

But not only in such a simple case, it'd also run with pre-processor directives!

Example:

void fn(param-list)
#ifdef __WIN__
{
  ENTRY_TEXT (param-list)
  //some windows code
  EXIT_TEXT
}
#else
{
    ENTRY_TEXT (param-list)
    //some any-os code

    if (condition)
    {
        return; //should become EXIT_TEXT
    }

    EXIT_TEXT
}

So my question is: Is there a proper way doing this?

I already tried some work with parsers used by compilers but since they all rely on running a pre-processor before parsing, they are useless to me.

Also some of the token generating parser, which do not need a pre-processor are somewhat useless because they generate a memory-mapping of tokens, which then leads to a complete new source code, instead of just inserting the text.

One thing I am working on is to try it with FLEX (or JFlex), if this is a valid option, I would appreciate some input on it. ;-)

EDIT: To clarify a little bit: The purpose is to allow something like a stack trace. I want to trace every function call, and in order to follow the call-hierachy, I need to place a macro at the entry-point of a function and at the exit point of a function. This builds a function-call trace. :-)

EDIT2: Compiler-specific options are not quite suitable since we have many different compilers to use, and many that are propably not well supported by any tools out there.

Markus
  • 43
  • 9

3 Answers3

3

Unfortunately, your idea is not only impractical (C++ is complex to parse), it's also doomed to fail.

The main issue you have is that exceptions will bypass your EXIT_TEXT macro entirely.


You have several solutions.

As has been noted, the first solution would be to use a platform dependent way of computing the stack trace. It can be somewhat imprecise, especially because of inlining: ie, small functions being inlined in their callers, they do not appear in the stack trace as no function call was generated at assembly level. On the other hand, it's widely available, does not require any surgery of the code and does not affect performance.

A second solution would be to only introduce something on entry and use RAII to do the exit work. Much better than your scheme as it automatically deals with multiple returns and exceptions, it suffers from the same issue: how to perform the insertion automatically. For this you will probably want to operate at the AST level, and modify the AST to introduce your little gem. You could do it with Clang (look up the c++11 migration tool for examples of rewrites at large) or with gcc (using plugins).

Finally, you also have manual annotations. While it may seem underpowered (and a lot of work), I would highlight that you do not leave logging to a tool... I see 3 advantages to doing it manually: you can avoid introducing this overhead in performance sensitive parts, you can retain only a "summary" of big arguments and you can customize the summary based on what's interesting for the current function.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Hi, I almost knew it would be kinda impossible to reach my requirements. As you say, the second option sounds quite perfect for what I need. (However, I would prefer #3, if i can get everyone to obey that ; ) ) - I give it a try and see what works best. - Thanks! – Markus May 17 '13 at 07:27
  • C++ is generally complex to parse, but... if you're willing to pass on code which doesn't meet reasonable coding guidelines (or your in house style), and you're only interested in finding the functions, it's not that difficult. (I know that I've got some simple code floating around that generates mapping tables for enums---it parses just enough C++ to find the enum constants, and their scope. And it wasn't that difficult.) – James Kanze May 17 '13 at 08:00
  • @JamesKanze: interesting, I took the other way round for enums, I use a macro (with some Boost.Preprocessor wizardry) to generate it and the mapping. – Matthieu M. May 17 '13 at 08:17
  • @JamesKanze At the moment its more or less what I am doing. I wrote my own little parser but it already is a big problem to actually FIND a function, since a function-signature has so many ways being declared. (Unlike the enum keyword functions dont have one ofc.) – Markus May 17 '13 at 08:52
  • @MatthieuM. I've seen that done as well. I needed it for existing sources, however. My solution is a lot more work than the macros, but now that I've got it, I find it preferable. – James Kanze May 17 '13 at 09:39
  • @user1071494 I forget the exact details, but IIRC: the key is that the function name is always followed by a `'('`. You have to keep track of nesting, and recognize when a statement starts (in order to recognize `class` and `namespace`), but it didn't require much more. The tricky part was, I think, handling things like `x = ...` in the enum: I requires skipping until the comma or the closing brace, correctly ignoring the comma in something like `aTemplate::value` but not in `a < b ? 10 : 20,`. (And I think I punted on this one, and require the second to be in `()`.) – James Kanze May 17 '13 at 09:45
1

I would suggest using LLVM libraries & Clang to get started.

You could also leverage the C++ language to simplify your process. If you just insert a small object into the code that is constructed on function scope entrance & rely on the fact that it will be destroyed on exit. That should massively simplify recording the 'exit' of the function.

Zac
  • 3,235
  • 4
  • 25
  • 30
  • I already checked LLVM and comparable stuff, but I always wanted to avoid these, since they seem to require a huge amount of time and work to get it right. - Thanks anyway, I'll give it a try. – Markus May 17 '13 at 07:28
0

This does not really answer you question, however, for your initial need, you may use the backtrace() function from execinfo.h (if you are using GCC).

How to generate a stacktrace when my gcc C++ app crashes

Community
  • 1
  • 1
Matthieu Rouget
  • 3,289
  • 18
  • 23
  • Note: unfortunately backtraces (though useful) do not provide the *values* for which the issues occurred... and really, this should probably be a comment. – Matthieu M. May 17 '13 at 07:04
  • Besides that, i have to say GCC is possible, but several compilers are used. (really MANY!) – Markus May 17 '13 at 07:12