Revisiting print debugging. Is it that bad?

preview_player
Показать описание
---

Revisiting print debugging. Is it that bad? // Last week's video inspired a lot of conversation, mostly because I didn't explore the details and corner cases of print debugging. And, a lot of you made some good points. So, this week, I decided we should talk more about the strengths and weaknesses of printf and its use in debugging, as well as some other options like logging and assert statements.

***

Welcome! I post videos that help you learn to program and become a more confident software developer. I cover beginner-to-advanced systems topics ranging from network programming, threads, processes, operating systems, embedded systems and others. My goal is to help you get under-the-hood and better understand how computers work and how you can use them to become stronger students and more capable professional developers.

About me: I'm a computer scientist, electrical engineer, researcher, and teacher. I specialize in embedded systems, mobile computing, sensor networks, and the Internet of Things. I teach systems and networking courses at Clemson University, where I also lead the PERSIST research lab.

More about me and what I do:

To Support the Channel:
+ like, subscribe, spread the word

Рекомендации по теме
Комментарии
Автор

I'd say it's kind of depends. When I develop algorithms, cout/printf is much easier for me to see if the algorithm works in the right way; once the structure/algorithm is fixed/done, when it comes to technical implementation, yeah, debugger may be easier to catch the wrong lines.

rnoro
Автор

Thanks Jacob. Another case for printf's is in embedded systems, where sometimes it's just not possible to use a nice debugger. I've worked on many systems were either there is no debugger, or it was very flaky to use and printf (e.g. to a serial port) or logging to an area of memory was a better solution.

sjdcknsn
Автор

Valgrind was a life changer for seg faults. Shows where the memory was allocated, where the memory was free'd, runtime call stack.. very helpful MOST of the time, and way faster than sticking print statements earlier and earlier in code blocks until you get a print out before the seg fault occurs. And not every memory violation will result in a seg fault (ie if you overrun a buffer into non-readonly memory that technically still belongs to your program, valgrind will show this but it wouldnt seg fault). But I use the old printf debugging from time to time. And debuggers too. Ive developed a situational feeling, I have a gut instinct about what to use in a particular scenario

Anonymous__Not_Found
Автор

This was great, thank you! Because of your argument on how sprinkling printf (or wrapper) calls bloats the real code I also don't see myself ever putting assert statements all over my code. I'd much rather spend a few minutes setting up a unit test or a bit longer to write my own unit test framework. When should I use asserts, if ever?

sanderbos
Автор

Polluting your code would be an issue if you weren't using source control where you can easily revert your debugging crap after you find the bug.

Printf or equivalent is super useful in video game development where you have to play them in real-time, then look back in the logs to see where things went wrong. When you identify the rough area then you can concoct a breakpoint condition and get to the exact problem in the debugger.

There are many tools. Practice using all of them!

razu
Автор

I may not be a "typical" listener as I've considered myself a professional C and C++ coder for 30-odd years. But I like to keep fresh and sometimes thinking about or arguing the basics gives new perspectives.

So ... for printf debugging --- I suppose I've mainly used it where options were limited. Platforms that I don't have a good debugger on (and am one-shotting a single project on it) and also on servers where debugging simply can't happen (coredump and restart is the reaction to problems) --- and while the debugger can look at the last state, it can't exactly give clarity on how you got there. Several server systems I run even use databases for logging... heh.

But one specific instance I'm currently working on is some "recreational" Unreal C++ development. Let me say this first: it's cool. You subclass classes that Unreal provides, your environment creates a DLL that the unreal editor loads on modify --- and thus you are using your subclasses in the Unreal editor. The sheer mountain of C++ code you're sitting on and tinkering with is fascinating.

Now... for the debugger --- you have source for the Unreal Engine. Wow. Stuff that in your debugger. Better have a big machine. But assuming that succeeds (it does on my desktop) ... debugging is _hard_ because you need to interact with the 3D world --- which is not so easy when the debugger has paused it.

randomscribblings
Автор

It's really a thing of maturity. Most bugs you can make sense of with prints, an experienced programmer would see by just looking at his code. At least thos where the amount of prints would still be considered ok.
The more complex ones then require breakpoints, stepping, etc. - a debugger. So yeah, if you're a student, give debuggers a try! It's worth it; not only in the short run.

pelic
Автор

Perhaps look at it like an adjustable wrench. Handy to use in a pinch for spur of the moment type of things, but if you're digging into doing actual intensive work, get the full set of correct sized wrenches and use the right tool for the job. Learn the tools and how to use them to be overall more efficient on a day-to-day basis. Learn the debugger and dedicate to sole use of it for a couple weeks and abstain from print statements. After you have sufficiently learned both methods, you will have better knowledge of both methods and make you're own decision on the job at hand.

rexjuggler
Автор

When write resource managers for QNX use macros exist only in DEBUG version of program, something like: DEBUG_ATC(flag, action). The flag up/down online through PPS, and the action is any action, include fprintf to log or console, set some value, call function or something else. And debugger can't help me in most situations, because system is real time and if I stop processing in the middle, I break everything.

end-ikend-ik
Автор

I've been using "printf" debugging for ages. Last week's admonition to stop doing that spurred me onto learning how to actually use GDB to debug instead.

Sure, I know the "cool" kids use fancy pants IDE tools with their integrated debuggers, but I have my reasons for using the editor I use, and am not about to switch to the IDEs.

The learning curve for GDB is a pain, and the lack of a persistent, separate display to show data as it mutates during the debug session is a drawback, but at the end of the day, the utility of debugging with GDB destroys the utility of debugging with "printf" for two reasons:

Reason 1: You have access to ALL of the data in the scope of the code while debugging. Sure, you have to be mentally focused enough to use the p command to print the data you want to see while holding the train of thought of debugging in your head, and this is a learning curve in and of itself, but you get ALL of the data, not just what you thought to include in the "printf" on the last run.

Reason 2: You quickly learn the discipline to carefully think of the code as it will execute line by line because of the need to display the data "While the iron is hot." This improves your understanding of the code and gives you insights into the refactoring process that inevitably occurs after the debugging is done and it all works perfectly.

So yeah, I'm totally sold. GDB over "printf" for debugging all day long.

BobSellers
Автор

That "experienced printf debugger" comment knocked me out. Cracked up 🤣🤣🤣

lean.drocalil
Автор

Jacob, what do you think with the case of multiple Threads? In my experience it's not go well with the debuggers... Maybe you have some tool that i do not know about?

And of course - Thanks for all your videos. You are perfect!
Really, thanks!

idxerpg
Автор

debugger is great. But 1 place a log file is better when debugging multithreaded application. Just write your thread safe logger func with thread ID.

wastedkafir
Автор

So, I just stumbled on an annoying new place where printf debugging (actually MessageBox debugging) turned out to be necessary.

These videos a few months ago convinced me to put more effort into learning gdb, and I'm glad I did. For finding mistakes in code, it's way better than the printf spiral. I still tend to use printf for "prebugging". That is, I'm working on an algorithm and I'll put in a printf to make sure something has the value I expect, then I'll replace the printf with code to manipulate the value.

Today's goal: parse the command line so a windows GUI application can open a file passed by double-clicking the file in explorer.

It turns out that if you start a program in windows from a command line, the windows API GetCommandLine() function returns what you would expect. The executable filename followed by any other text you add as arguments. This is the case in both Windows Command Prompt and MSYS2/bash. Since gdb runs on the command line, that's what it does. e.g.:

(gdb) p arg
$3 = 0x28112bd36c0 "wt.exe test.txt"

However if you start the program from Explorer, by double-clicking either the executable or a file, Windows encloses both pieces in quotes, like so:

(gdb) p arg
$5 = 0x18e06003750 "\"C:\\Users\\...\\wt.exe\"


Needless to say, this confused my attempts to parse the resulting string, which was different in gdb than when the program was run "normally".

I was eventually able to confirm all of this behavior by attaching gdb to running processes started in various ways. But I don't think I would have ever thought to try that if I hadn't seen the quotes popping up in the MessageBox (but only sometimes).

Oh, and if that's not enough, if you double-click just the executable, Windows includes a trailing space after the .exe. So if you want to test for the existence of a commandline argument by searching for a space... nope.

(gdb) p arg
$1 = 0x1e1d13336e0 "\"C:\\Users\\...\\wt.exe\" "

brianniece
Автор

I like to print debug for the simple reason that I hate my compilers, and they hate me.
"Oh, you specifically compiled with -g and -O0? Let me just OPTIMIZE OUT SOME VARIABLES"
Both gcc and clang do that shit to me and I'm tired of it, so I print the variables that they optimize out for no apparent reason

insu_na
Автор

Hey jacob can you do a video talking about the standars like c90 and c99, ?

javiervelazquez
Автор

For segfault issues I prefer to use write with a backtrace handler

mrcrackerist
Автор

Valgrind in combination with gdb (TUI version) are invaluable to use if you really want to maximize your C/C++ debugging for off-by-one, heap corruption, and segfault errors. Sure there's Radare2 and Qira, but would any student really invest that much time in learning those timeless debuggers?

SimGunther
Автор

I probably should have been more detailed. I know my debugging tools. Unfortunately using debugger in Unity programming is way slower than using Debug.Log() :(

lvqmfjz
Автор

You can easily remove all debug printf statements in all files if you master usage of sed, on linux at least, for example sed '/^\s*printf(\"DEBUG)/d' will remove all lines beginning with that pattern, and you can make it go through the entire source code recursively

damir