Enhance printf debugging in Go and C with file, function and line information.
Whenever I have to debug big, difficult or tightly coupled programs, my style of debugging is usually:
- Reading and inspecting the code first, trying to identify potential call sites.
- Analyzing patterns in my head to see if what I read makes sense.
- If the code isn’t clear, I’ll write some tests that use the code under question.
- Inserting print statements and then doing a couple of runs with tests on the code.
- Rinse, repeat as I try to figure it out.
Lots of people would go for an actual debugger and insert break points then step through the code interactively. However, I find that’s a bit of a pain because that puts you in a box where one cannot perform a simple task without that one special tool in a special environment. Lots of systems don’t come pre-packaged with your external tools, you’ll have to explicitly install or download them. IMO you can definitely survive without using a debugger, in fact I have never once needed an external debugger because I find that my simple printf style is sufficient and effective.
I prefer to use simpler tools and simple methods that help me understand what I am doing effortlessly without fancy boilerplate. Almost every language that I’ve used is guaranteed to let you print to the screen, that’s more than enough for my simple needs, so let’s get to it!
In the Go world, we can access file, function name, and line number at runtime by invoking functions runtime.Caller(int) https://golang.org/pkg/runtime/#Caller whose return signature is:
(programCounter uintpr, file string, line int, ableToRecoverThisInfo bool)
Once we have the program counter, we can then further query and get the function for the program counter by invoking https://golang.org/pkg/runtime/#FuncForPC which then returns a runtime.Func object whose .Name() method gives the name of the function.
Runnable example below or here https://play.golang.org/p/pATI6RN6Es
which when run gives output
In the C world, depending on the compiler, we can use macros __FILE__, __LINE__ and then translator constant __func__. __FILE__ and __LINE__ are usually available at preprocessing/compile time and are inserted into the code by the pre-processor. __func__ doesn’t seem available to all compilers but at least gcc or llvm have them.
We can do something like this:
which when run gives output like this
Interestingly, as I mentioned early __FILE__, and __LINE__ are available at preprocessing and compile time, while __func__ is a magic constant available later at translator time https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html.
Let’s see what output we get by examining the output from the preprocessor stage:
If you made it this far, I think you have the tools to go into some uncertain code bases to try to bread crumb your way through unfamiliar territory by enhancing your print statements. I personally use this style of debugging and I’ve used it on embedded systems, smaller computers, many average sized machines, restricted computers. These are places where a programmer who relies on debuggers would have been blocked because they cannot install new packages.
Here is an example of the C style breadcrumbing incorporated into a project that I wrote 2 years ago when learning more about arithmetic https://github.com/odeke-em/utils/tree/master/arithmetic