There are several factors to consider in an implicit solution:
- C is barely type aware. For example if you used @Jens Gustedt's suggestion of instrumenting functions, you still have the problem of determining what exactly you're looking at on the stack, and how to appropriately print out the values. IIRC, instrumenting functions won't give you the prototype of the upcoming function, nor will it provide a handy struct with pointers to those values. This would be more akin to C++ templating. You'd have to write an enter/exit function that was aware of the prototypes of all the functions, and have inside knowledge of the precise calling convention and packing arrangements for your variables.
- You need to compile with debugging symbols, in order to identify what memory was associated with a variable defined in your code, and when those variables are being modified.
- More complex types do not have a standard way to be printed (structs for example), even in C++. You'd need to have printing functions that conform to some kind of interface.
- Pointers and arrays of indeterminate length will be very difficult to handle. You'd need to test for NULL pointers, and know array sizes in advance to correctly print their values.
I've attempted something similar to your "printf at function entry" in an existing project I'm working on. It's kind of useful for important functions, but I've gotten far more go out of asserting values are in the expected ranges at function entry and exit. Here's the logging header and source, which demonstrate how to greatly simplify the printing of lines, functions and other useful information around your manual tracing. You'll notice many of my types have a corresponding *_valid()
function, and assertions surrounding their use. As soon as my program runs off the tracks (which I assure you is quite frequent during debugging), the asserts will fire and I can analyse the situation via the stack trace.
You may also find this answer useful regarding both the difficulty of doing this stuff with C alone, and how to work around it using macros.
Your best bet if you need this done implicitly is via GDB, presumably you can write scripts to analyse changes after each instruction, and intelligently determine and print types using the debugging info provided by -g
.