I did a more general thing: At work we have a small "scripting" language (sort of a DSL), this DSL is translated into C++ and then the code is compiled before loading the program. Users work through a dedicated IDE.
During the translation phase, we inject a call between every 2 lines (of the original script language) similar in spirit to the macro above, that updates a global array with a counter. This global array is also built during the translation phase.
There's a separate thread which polls this array and sends updates to the IDE, so users can see in real-time what code was run and when, without needing to stop and debug the code or insert printing statement.
There's a somewhat complicated algorithm which during translation gives a unique ID to each callee in the call tree, so that thread sends to the IDE basically just a number with a counter. This doesn't deal with recursion BTW, I just limited reporting on recursion to a shallow depth. It still runs but just isn't reported.
It's not a complete replacement for a debugger (we also have a debugger) but it's good enough for most simple cases.
There's a similar hack for variables (including local variables).
Recently, some of my less technical users have been disappointed to discover this feature isn't present in other languages / IDEs...
You can also sort of do the same trick in any language, if compiled without optimizations the compiler will usually insert NOP assembly statements between each source line (disclaimer: not all compilers, not all languages, depends on a lot of factors).
This gives you both runtime visualization of running code and potentially time-travel debugging.
So one might be able to run a post-build patching phase and replace these NOPs with a call to a reporting function as above.
Out of curiosity I actually did this with C#, replacing NOPs in the MSIL level, since MSIL is easier to reason about than pure assembly, and it worked very nicely, I got a full "log" of program execution, including all lines executed, when, and all values of local and global variables.
I used the Cecil library to make it slightly more pleasant to read and manipulate IL.
Didn't go on with it other than writing a basic proof of concept.
There are of course tools which do the same for C++, like undo.io and RR, but I'm not aware of any tool doing this for C# / dotnet code. I'm not sure why since while not trivial, it isn't very difficult (and I'm not an expert by any means in this sort of thing).
Roslyn (C# compiler infrastructure) has code generators now, which is nicer than working with IL, but as far as I was able to see they don't support this scenario.