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?
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