After my last two book reviews were rather negative, I'm happy to say that this review is going to be positive again. Mario Herwardt's and Daniel Pravat's book Advanced Windows Debugging (
) keeps what the title promises. It's a book for people that need to find bugs in Windows programs that are for whatever reason (too) difficult to find with the "normal" developer tools like the integrated Visual C++ debugger. That's not what the authors say in the introduction of course. They say it's a book for everyone that does Windows development. And that's probably true because the book is a real eye-opener for what's possible with the debugging tools provided by Microsoft. Nevertheless I guess that most developers will probably rarely if ever leave the cushy environment of their IDE's debugger. But just in case you do, this book prepares you well.
The book itself is approximately 800 pages long and divided into three sections called "Overview", "Applied Debugging", and "Advanced Topics". Each section is once again divided into four to six chapters of strongly varying length.
The "Overview" section is divided into four chapters. The first chapter is called "Introduction to the Tools". It tells the user which tools will be used throughout the book, where to find them and how to install them. Most of the tools mentioned are available from the Microsoft website as part of packages like the Driver Development Kit (DDK). The one exception is Ethereal which is curiously used in the book instead of the forked and actively developed Wireshark.
The second chapter is called "Introduction to the Debuggers". It is the more advanced sibling of the first chapter. Here, the user is guided through the installation and use of the debugging tools Microsoft provides in the Debugging Tools for Windows package. The focus on this chapter (like the focus of the entire book) lies on the WinDbg debugger. To acquaint the reader with WinDbg, many of the standard WinDbg commands are presented and explained.
The next chapter, "Debuggers Uncovered", is a very nice detour from the rest of the book. While the book is actually about debugging programs, this chapter tells the reader how debuggers actually work. The whole explanation from attaching to processes to setting up a debug loop and handling debug events is explained. In theory, the information from this chapter is enough to write a simple debugger should you ever feel like it. Further information about issues like exception handling, setting software and hardware breakpoints, or how attached debuggers influence event dispatching in the target process is given too.
"Managing Symbol and Source Files" is the last chapter of the first section. In this brief chapter, the authors offer some best practices on how to get and organize symbol- and source files you want to use during your debugging sessions.
The second section of the book ("Applied Debugging") is the main section of the book. On approximately 350 pages many different bug categories are introduced. Each bug class is explained using one or more small example applications. The authors walk the reader through the debug process for each of the buggy applications and tell him what information from the debugger is important. I really liked how they emphasize that the reader is not supposed to look at the source code before debugging the applications because very often you won't have the luxury of knowing where to look at in the source code of your real applications. If you knew beforehand where the heap is corrupted or where a handle is leaked you probably won't need WinDbg anymore and the exercise is useless.
The first chapter of the second section is called "Memory Corruption Part I - Stacks". The authors explain how the stack works and how it is used to transfer control and arguments between functions. Then they explain what bugs can screw with the stack (common stuff like buffer overflows, trickier stuff like calling conventions mismatches, and so on) and how to find these bugs with WinDbg.
The sixth chapter is the continuation of the fifth chapter. Instead of focusing on the stack, "Memory Corruption Part II - Heaps" focuses on the heaps used by normal Win32 programs. The general layout of this chapter equals the one from the chapter before. At first, purpose and structure of the heap is explained and how it behaves when memory is allocated or freed. Following this, common bugs that lead to heap corruptions are presented and the reader is given a few example applications and walkthroughs on how to find the bugs in these applications.
The seventh chapter "Security" is all about Windows Security concepts like access control and user authentication and how the improper use of these concepts can lead to problems in applications. The relevant security concepts are explained both on a high level in general terms and on a low level by looking at the relevant data structures in memory using WinDbg. Once again, sample applications are included where the user can debug common bugs related to the content of this chapter.
The eighth chapter deals with "Interprocess Communication" and continues where the seventh chapter left off. Assuming you've successfully set up user privileges in your application using the information from chapter 7, chapter 8 now tells you how to find bugs in interprocess-communication code. The focus is on DCOM and Remote Procedure Calls (RPCs) here. This is also the chapter where Ethereal is used to analyze the traffic sent between different processes in order to find out whether the transmitted packets indicate an error in the communication of some kind or if the error is elsewhere.
Chapter nine is my favourite chapter of the second section. It deals with recognizing and finding "Resource Leaks". In this chapter, resource leaks are defined as any kind of situation where something that was previously allocated is never freed again. This includes memory allocation as well as forgetting to call CloseHandle on previously created HANDLEs. Resource leaks always worried me. There are the obvious ones which you can spot after a few seconds (for example if you forget to close an allocated HANDLE in your WM_PAINT handler and your window isn't drawn properly anymore) but then there are the devious ones which semi-randomly fill up the memory over a long time in programs which are active for weeks, months, or even years. This chapter deals with the devious ones and once again the user is advised to debug first and look at the source of the example application later.
The tenth chapter, "Synchronization", is the last chapter of the second section. It introduces various methods for thread synchronization that are available in Windows and to what kinds of bugs thread synchronization can lead. Due to the difficulty of multithreaded design in traditional languages this is probably another one of these chapters where most people will often have some kind of gotcha moment when faced with something they never considered before.
The third and last section of the book is called "Advanced Topics". In this section you can find all the stuff that just didn't fit into the second section anymore but still deserves a mention.
The 11th chapter is called "Writing Custom Debugger Extensions". While there are lots of built-in commands in WinDbg, sometimes they are just not enough. This chapter describes an example where a custom debugger extension is useful. The example application is a buggy application that deals with binary trees. In some function of the tree code, the tree structure is corrupted and the tree loses its shape. Debugging a complex data structure like a tree is tedious because of the pointers that are used to walk through the nodes. A custom WinDbg extension is written that walks over the trees and dumps their content into the WinDbg window.
The 12th chapter is all about "64-Bit Debugging". While the rest of the book deals exclusively with 32-Bit debugging, the newer 64-Bit architecture is given some consideration too. The 12th chapter revisits the chapters 1-11 and details all relevant changes in the techniques presented in the first 11 chapters that are relevant for debugging 64-Bit applications.
The 13th chapter is called "Postmortem Debugging". If - for whatever reason - you can't debug a running process, you have to work with the dump files produced by an application crash (or otherwise). This chapter tells the reader how to set up the debugging tools to work with these dump files and how to debug the dumped programs.
The 14th chapter ("Power Tools") introduces two tools that make it easier to automize the debugging process. One of them is the Debug Diagnostic Tool (DebugDiag), the other one is WinDbg's !analyze command.
The 15th chapter is comparable to the 12th chapter. While the 12th chapter explained the differences between 32-Bit and 64-Bit debugging, the 15th chapter explains the differences between the pre-Vista versions of Windows (which are used in the chapters 1-11) and Windows Vista. Once again the earlier chapters are revisited and the most significant differences between Windows Vista and earlier versions of Windows are mentioned.
Now that I've described the content of the book, I want to say something about the editing of the book. This book is one of the most well-edited tech books I've ever seen. The screenshots are sharp and readable, the figures easily convey whatever information they want to convey, the tables are well-designed and the page layout just makes sense. Even the small things were done right. One example concerns the debugger dumps and the code listings. Since both debugger dumps and code listings can be rather lengthy at times, the authors made sure that the relevant parts of both are always printed in boldface. That way you don't waste time trying to read whole debugger dumps. Your eyes can always jump straight to the relevant parts instead.
The content of the book is just as good as the layout. If you combined this book with Windows Internals and Windows via C/C++ you probably have some kind of awesome Windows Programming trifecta in your bookshelf. At this point it's maybe time to get over my silent tears for SoftICE because WinDbg is apparently much, much better than I thought.