The art of debugging
The art of debugging
As impossible as it may seem to some of you, after years of programming I've never really felt the need to use a debugger. I did try messing around with GDB, but I'm still mostly mystified by the point of using it. And I get the feeling I've doing something horribly wrong all the time. So can some of you verse this poor newb in the proper purpose and way to use a debugger?
Re: The art of debugging
There are several methodologies for debugging code. To a large degree, it depends on your own personal knowledge of what kind of mistakes you make, and what language you are programming in. Debuggers are really nice if you are writing your code in ASM and/or the typical mistakes in coding that you make are conveniently found using singlestepping techniques.
If you code in C++, and you can always easily find your mistakes with just a few printf commands -- then a typical assembly-level debugger isn't going to do you a lot of good.
But this is rarely the case in OS development. Debugging a kernel with printfs is hard, because often you have no display to printf TO. And eventually you will run into a bug that is unbelievably mysterious, if you are just using printf-based techniques. A debugger allows you to see the entire state of the CPU, and to watch each opcode run. You have to keep your own mental image of what the CPU should be doing, in your head, constantly. The first instant that you notice the CPU doing something other than it should -- you stop singlestepping, examine every register to see that it contains what it should, examine any memory involved and make sure it has the right values -- and often you can spot the bug.
But as I say, if (in your real-life coding, in your IDE, with your HLL of choice) you never use breakpoints, or singlestepping -- and your code debugs itself easily ... then you may not need a debugger for your OS either.
If you code in C++, and you can always easily find your mistakes with just a few printf commands -- then a typical assembly-level debugger isn't going to do you a lot of good.
But this is rarely the case in OS development. Debugging a kernel with printfs is hard, because often you have no display to printf TO. And eventually you will run into a bug that is unbelievably mysterious, if you are just using printf-based techniques. A debugger allows you to see the entire state of the CPU, and to watch each opcode run. You have to keep your own mental image of what the CPU should be doing, in your head, constantly. The first instant that you notice the CPU doing something other than it should -- you stop singlestepping, examine every register to see that it contains what it should, examine any memory involved and make sure it has the right values -- and often you can spot the bug.
But as I say, if (in your real-life coding, in your IDE, with your HLL of choice) you never use breakpoints, or singlestepping -- and your code debugs itself easily ... then you may not need a debugger for your OS either.
Re: The art of debugging
I actually found a very nastly bug in one of my drivers (can't remember exactly which one) that way. The driver would work correctly, if there weren't any printf() function calls. As soon as printf() would be called in the driver initialization code, I would get an exception later on somewhere in the kernel (I was using my boot loader for quick testing of the drivers at that point, and the boot loader would display that the cpu generated an exception, then it would stop. The boot loader also included portions of the kernel). The bug turned out to be an uninitialized structure - Bochs initialized the contents of the memory with zeroes, but the arguments to printf() overwrote the memory range in the stack assigned to the structure.bewing wrote:But this is rarely the case in OS development. Debugging a kernel with printfs is hard, because often you have no display to printf TO. And eventually you will run into a bug that is unbelievably mysterious, if you are just using printf-based techniques.
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re: The art of debugging
My favourite bug was a panic in the initialization of my VGA driver. I ended up debugging it by moving some triple fault code around and seeing whether the VM reset or halted (i.e. -- whether the bug or my triple-fault code hit first). That took several hours.
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
Re: The art of debugging
There are some things you can do with GDB that you cannot accomplish with printf/log-style debugging.
Post-mortem debugging: GDB can take a coredump, and show you which function called which other function with which parameters, giving you an idea of what might have triggered the coredump.
Run-Time parameter fudging: Sometimes it is awfully hard to recreate an error, because it is hidden in some down-deep function you cannot call directly because it requires lots of run-time setup. GDB can be used to set a breakpoint just after function entry, and then "fudge" the parameters to whatever you like to trigger the desired behaviour.
Remote debugging: GDB can be used to debug processes running on a remote hardware. That might be a server, or an embedded device, which cannot give you printf()-output.
Fishing in the dark: If you have no idea what the call graph of a process might look like - e.g. because someone else wrote the code and "forgot" to document cleanly - stepping through the source can give you a good impression of what's going on. Hunting this kind of information down using printf() means numerous edit / compile / run cycles, while with GDB you can step through the whole thing in one go.
Besides, you can have both - the output of the printf()s you scattered through the code, and a look at the sources...
Post-mortem debugging: GDB can take a coredump, and show you which function called which other function with which parameters, giving you an idea of what might have triggered the coredump.
Run-Time parameter fudging: Sometimes it is awfully hard to recreate an error, because it is hidden in some down-deep function you cannot call directly because it requires lots of run-time setup. GDB can be used to set a breakpoint just after function entry, and then "fudge" the parameters to whatever you like to trigger the desired behaviour.
Remote debugging: GDB can be used to debug processes running on a remote hardware. That might be a server, or an embedded device, which cannot give you printf()-output.
Fishing in the dark: If you have no idea what the call graph of a process might look like - e.g. because someone else wrote the code and "forgot" to document cleanly - stepping through the source can give you a good impression of what's going on. Hunting this kind of information down using printf() means numerous edit / compile / run cycles, while with GDB you can step through the whole thing in one go.
Besides, you can have both - the output of the printf()s you scattered through the code, and a look at the sources...
Every good solution is obvious once you've found it.
-
- Member
- Posts: 566
- Joined: Tue Jun 20, 2006 9:17 am
Re: The art of debugging
Well said Solar . .
I sometimes to used make mistakes with pointers initially like freeing an already freed block etc and i had a tough time trying to find the bug . Once i learned the use of debuggers it was quite easy to find out exactly where i went wrong .
Regards
Shrek
I sometimes to used make mistakes with pointers initially like freeing an already freed block etc and i had a tough time trying to find the bug . Once i learned the use of debuggers it was quite easy to find out exactly where i went wrong .
Regards
Shrek
Re: The art of debugging
how can you use gdb to debug a kernel? Im not at that point in development yet, but if your kernel crashes under bochs, how can you get a core file, or how can you even run the kernel with gdb since theoretically gdb would need to be loaded first to debug?
-
- Member
- Posts: 391
- Joined: Wed Jul 25, 2007 8:45 am
- Libera.chat IRC: aejsmith
- Location: London, UK
- Contact:
Re: The art of debugging
You could implement a GDB stub in your kernel, or if you use QEMU you can attach GDB to it by starting QEMU with the -s option, then doing the following in GDB:
Code: Select all
target remote localhost:1234
symbol-file /path/to/my/kernel-binary