Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Debugging with NTSD and Application Verifier


Patrick is a senior software engineer at LANSA Pty Ltd. He can be contacted at [email protected].


Many Windows developers are unaware that Windows ships with its own built-in debugger—the Microsoft NT Symbolic Debugger (ntsd). In this article, I describe how to use ntsd to debug a few straightforward problems. I also describe the Microsoft Application Verifier (AppVerif) tool and present some examples that illustrate both a strength and limitation of AppVerif when finding buffer overruns on the heap.

The ntsd command-line debugger is not as pretty as Visual Studio's integrated debugger. Despite this (or perhaps because of this), ntsd.exe and its cousins are arguably the debuggers of choice for developers at Microsoft who build the core of the Windows operating system

Although ntsd has historically shipped in-box with Windows NT right up through Windows XP, Microsoft is continually improving it. Consequently, I recommend downloading the most recent version of the Microsoft Debugging Tools for Windows package (www.microsoft.com/whdc/devtools/debugging), which includes ntsd, the Windows Debugger WinDbg, the Kernel Debugger KD, an SDK for writing debugger extensions, and the debugger.chm help file.

Example

Here's an example that illustrates how to use ntsd to debug a typical application crash. I use the word "crash" to refer to any situation where the application was terminated abnormally by the operating system. The most common cause of application crashes is where the application attempts to read from or write to an invalid memory location. This is called an "access violation" (AV). For example, the application may attempt to dereference a NULL pointer. Example 1 is designed to do just this.

void main(void)
{
    char *p = 0;
    *p = 123;
}

Example 1: C program designed to dereference a NULL pointer.

When I run this program (t1.exe), Windows terminates it at the point of the access violation and issues the expected message that there was a problem with my program. To debug this, I run ntsd.exe from the command line, passing the name of my application as an argument; for example, ntsd.exe -g t1.exe. With the -g option, ntsd.exe loads and immediately runs the application. Without the -g option, ntsd.exe loads the application, then immediately breaks before the application runs, requiring the g command to let the application continue. When the AV occurs, ntsd.exe breaks in and presents me with a debugger command window like Example 2.

Microsoft (R) Windows Debugger  Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: t1.exe
Symbol search path is: SRV*c:\Files\websymbols*http://msdl.microsoft.com/downloa
d/symbols
Executable search path is:
ModLoad: 00400000 0040f000   t1.exe
ModLoad: 7c900000 7c9b0000   ntdll.dll
ModLoad: 7c800000 7c8f4000   C:\WINDOWS\system32\kernel32.dll
(ce4.ddc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffdb000 ecx=00320758 edx=00320000 esi=7c9118f1  	edi=00011970
eip=0040101e esp=0012ff7c ebp=0012ff80 iopl=0     nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000        efl=00010206
*** WARNING: Unable to verify checksum for t1.exe
t1!main+0xe:
0040101e c6007b    mov     byte ptr [eax],7Bh     ds:0023:00000000=??
0:000>

Example 2: Contents of an ntsd command window at the point where the application t1.exe has caused an AV.

The initial debugger output reveals useful information. After displaying the command line used to start the application, it shows the search path for finding symbol files (PDBs). The symbol search path is important and, in this case, I had specified it beforehand by setting the _NT_SYMBOL_PATH system variable before running ntsd.exe. The symbol path shown here is SRV*c:\Files\websymbols*http://msdl.microsoft.com/download/symbols, which identifies a symbol server URL and the location where downloaded symbol files may be cached. That is, when ntsd needs to load symbols for a binary, it connects to the symbol server at the specified URL, and requests PDBs based on unique characteristics of the binary such as the name of the binary and its timestamp. The URL here is for the Microsoft public symbol server, which provides public Windows symbols for many versions of Windows. Of course, I also need to make sure that ntsd can find the symbols for my application. If I wish to add, say, the location "C:\Files" to the symbol path, then I can use the debugger command .sympath+ C:\Files. The command .symfix+ adds the URL for the Microsoft public symbol server if is not on the path. After changing the symbol search path, I always use the .reload command to force the debugger to reload all symbols.

When the debugger broke in, it displayed the state of the CPU registers, and the instruction that was being executed. The line t1!main() indicates the name of the executing module and the C function that was being executed, while mov byte ptr [eax], 7Bh indicates the faulting assembler instruction. The eax register currently contains a value of zero, so the program triggered an access violation by attempting to write into memory address zero.

So far, the debugger has provided me with useful information about what went wrong before I have even issued any commands. Example 3 shows a few debugger commands typically used after a break. The first command, the Display Stack Backtrace command k, shows the call stack for the current thread. The Toggle Source Line Support command .lines causes the debugger to switch between showing line numbers and not showing them in the output of future commands. Issuing the k command again gives a stack, but this time it references the relevant source file and line number for the code for which it has private symbols. The call stack shows that the AV occurred near line 4 in source file t1.c. The Display Local Variables command dv shows the value of variable p as 0x00000000. The last command in Example 3 is the Display Type command dt p, which shows the address in which the local variable p is stored, and that p is of type char *. If a variable references a C struct data type or a C++ class, then the dt command also attempts to display the names and values for each field.

0:000> k
ChildEBP RetAddr
0012ff80 004010de t1!main+0xe
0012ffc0 7c816fd7 t1!mainCRTStartup+0xb4
0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000> .lines
Line number information will be loaded
0:000> k
ChildEBP RetAddr
0012ff80 004010de t1!main+0xe [t1.c @ 4]
0012ffc0 7c816fd7 t1!mainCRTStartup+0xb4
0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000> dv
              p = 0x00000000 ""
0:000> dt p
Local var @ 0x12ff7c Type char*
(null)

Example 3: Results of several basic ntsd commands.

Arguably the most useful feature of the ntsd debugger is the !analyze debugger extension. In general, this is one of the first commands I issue after a crash because it often does a lot of the initial debugging work for me. Example 4 (available at www.ddj.com/code/) shows the results of using !analyze -v where the -v option triggers verbose output. This command extension analyzes, summarizes, and displays the cause of the break. In this case, the debugger has access to full symbolic debugging information; therefore, !analyze also shows a snippet of the original source code, with an arrow pointing at the line where the break occurred.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.