1

In order to debug a recursive program, I find it useful to visualize how deeply nested my function calls are. I'd love to have something like __func__, but for how deep my stack trace is instead of what my function name is. I understand that it would be impossible for a compiler to simply know this, because how nested you are is a dynamically generated value. But it wouldn't be hard for a compiler to add capabilities to implement this, you could simply add 1 to a global counter before each call and subtract 1 before eat ret.


I'm using the following debug statement (found here):

#define printdbg(Level, formatString, ...)                          \
    do {                                                            \
        fprintf(stderr, "%s:%d %s: " formatString "\n",             \
            __FILE__, __LINE__, __func__, ##__VA_ARGS__);           \
        if (Level == LEVEL_ERROR) { printf("quitting\n"); exit(1); }\
    } while (0)

I'd love to add an additional Predefined Identifier at the start, where I can leverage something of the form printf("%*s", __NEST__+1, ":"), to print a total of __NEST__ spaces at the beginning of each debug statement, to let me visualize how deep into the stack each debug statement was made from.


I know I could simply have a global counter that I ++ at the start of each function, and -- at the end, but I just learned about predefined identifiers and they're so cool! Also, no need to re-invent the wheel.

I can't find a list of supported predefined identifiers anywhere online. All I've found is this and this, neither of which claim to be comprehensive. If the equivalent of __NEST__ exists, somebody here probably knows the one word I'm looking for. If it doesn't exist, then where can I find a well-documented list of all the predefined identifiers?

Ari Sweedler
  • 807
  • 7
  • 25

1 Answers1

1

C does not provide a predefined identifier that will give you the function call nesting level. If a runtime system supported such an identifier it would add some overhead to all function calls. The overhead would be a price that everybody would pay and only the few using the identifier would benefit from. This is against the spirit of the C programming language, in which you expect to pay only for the features you're using.

On most current CPU architectures and C compilers you can obtain a number that increases with each function invocation by looking at the address of a local variable. Here is an example.

#include <stdio.h>

// Base nesting level (must be initialized by main)
static char *main_nesting;

// Return an integer corresponding to the current function call nesting level
int
nesting_level(void)
{
    int a;
    return (main_nesting - (char *)&a);
}

void
nest3(void)
{
    printf("%s=%d\n", __func__, nesting_level());
}

void
nest2(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest3();
}

void
nest1(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest2();
}

int
main(int argc, char *argv[])
{
    main_nesting = (char *)&argc;
    printf("%s=%d\n", __func__, nesting_level());
    nest1();
}

When you run this program, you will get output such as the following.

main=20
nest1=68
nest2=116
nest3=164

The set of predefined macro names is specified in §6.10.8 of the ISO/IEC 9899:2018 C standard. In addition, for GCC-like compilers you can obtain a list of all predefined macros by running the following command on a Unix system.

cpp -dM /dev/null
Diomidis Spinellis
  • 18,734
  • 5
  • 61
  • 83
  • 1
    Awesome! Thank you for the detailed answer. Your solution can be used to track nest-level, as long as I am recursing into functions with the same depth. My hope was, just as gcc supports -fPIC for position independent code (which also has a small overhead per function call), I was hoping that there could be a compiler flag to support tracking the nest level (this would not be as simple as PIC, however, so I see why it wouldn’t be implemented.) – Ari Sweedler Sep 10 '18 at 21:44
  • 1
    With `-fprofile-arcs` GCC inserts some code at the beginning and end of each function. As an alternative, you can modify that code to make it adjust a counter. – Diomidis Spinellis Sep 12 '18 at 05:31