Thursday, January 13, 2011

Introduction to Debugging Tools

note: this was reposted from my wordpress (w/c i did several months ago).

Out of boredom and not having anything remotely interesting thing to do at the office, I’ve written this lengthy guide for ‘newbies’ to the world of debugging tools. I’m not really sure if anyone would be using that document within the group since the members aren’t really used to documents (so unlike, the previous company I’ve worked at). And also, considering that the info in that document could be a help to someone out there, I figured might as well post it in a blog.

So, here it is: my own introduction and tutorial to debugging tools.

Note: Consider this as just a port to the document I mentioned earlier. I might have missed some things. Just tell me so in the comments and if I’m not too lazy, I might update it. Hehe

1. What are Debugging Tools?

Debugging tools are commonly stand alone applications that either attaches to a running application or loads and analyzes dump files. These tools allow a developer to inspect the processes; memory allocations and pertinent debugging information about an application (i.e. call stacks, loaded modules, data types and values).

2. Some Warning

Debugging tools are never foolproof or sure way to find and detect problems in an application. You should not depend on just one tool or one approach, particularly for bugs that are complicated. Often, the symptoms of a bug don’t point directly to its cause.

Use the tools introduced here in combination of other tools, analysis of log or data files and other means of acquiring information to help ensure that your analysis is as complete as possible.

Also, be aware of false-positives. These are errors or issues that a debugging tool would report as an issue that must be dealt with but is actually ignorable. This is a common occurrence, typically if the error goes down to the level of OS messages (or MFC), but it doesn’t mean that MFC has no bugs.

First Stop: WinDBG

1. What is WinDbg?

WinDbg (or WinDebug) is a free tool used in debugging applications. WinDbg has a myriad of features that could help a developer, features like:

  1. Generate and Read dump files
  2. Attach to and debug a running process
  3. Analyze memory and call stacks for processes
  4. Check the data structures and values of a process
  5. Display the associated source code for a process

By the way, this is a typical WinDbg workspace.

clip_image002

This is a terrible example of a workspace, I know. I was too lazy to fix up the settings. Hopefully, you’d do better. The point is to get idea regarding the windows / tools built in WinDbg. Don’t mind their functionalities yet, we can deal with it later.

The main targets in using WinDbg would fall into these two types: memory issues (i.e. memory leaks, memory hogging) and performance issues (i.e. CPU hogging, deadlocks, infinite loops).

For the purpose of simplicity (and my feeble attempts at organization), I’ll separate the explanation of Memory Issues and Performance Issues. For actual debugging targets, the techniques and operations are often combined and overlapped.

There are basically two ways to use WinDbg for analyzing or debugging an application:

clip_image004

However, before going through the above routes of analysis, we need to setup WinDbg first for its debugging functionalities to work properly. So, follow the steps in the next sections.

2. Install and Setup WinDbg

WinDbg is available for free from Microsoft website. Check this link for the download. It is part of the Windows SDK’s Debugging tools.

clip_image006

The Windows SDK would usually come as an ISO. You need to either burn it to a disk or mount it to a virtual drive to install the applications. Once installed, Windbg should normally appear at this Start Menu path:

Programs -> Debugging Tools for Windows (x86) -> WinDbg

Now it is time to setup the files we need to debug our applications. Start with setting the debug symbols by going through WinDbg’s menu: File → Symbol File Path… and set it to the following illustration.

clip_image008

path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols; d:\My_Projects\Location_where_i_put_my_debug_files

Okay, let me explain what this setting is for.

Remember that when we build an application in our IDE (say, Visual Studio), there’s always the default options to build in Debug or Release mode. While we’re in the process of creating, debugging or updating our code we normally build in Debug mode. On the other hand, we use the Release mode when we are about to deploy or release our application. One of the main differences in these modes is that in Debug mode, a symbol file is generated.

Note: You can actually generate a symbol file even in Release mode. It is after all just a configuration. However, by default, symbol files are generated only on Debug.

Symbol files (*.pdb files) are the Debugging information (symbols) for applications and related binaries (dll’s). This is important for debuggers (and the developers) since we need as much information as we need if we are analyzing a certain operation or component of an application. This information would include human readable data type names, function names and information that could link it a binary’s operations directly to its source code. If we could all read and understand machine language, then this would be of little use. Thankfully, ‘most of us’ can’t.

Therefore, during a debugging operation, it’s best to get our symbol files. Then, we let WinDbg (or any other debugging tool) know about it. This information would help in most of the operations of WinDbg.

Since, our application is running on Windows, we also need the symbols for Windows libraries. These symbols are available for download from the Microsoft website. To avoid downloading it every time we debug, we point WinDbg to a cache folder where the symbols will be saved and reloaded the next time Windbg runs. Hence, we have the Cache folder in our illustration.

Okay, now that WinDbg knows our symbols (thru our symbols file), let’s tell it where to found our source code. Setting this up, would allow WinDbg (thru symbol info) to point to the actual code that executed the operation of an application.

We just need to point to the folder where we save our source files. Just go to: File → Source File Path... and set it as follows.

image

Note: it is important that the symbols and source files always match what you are actually running. You NEED to change these settings if you’re using another version of your application.

3. Using WinDbg for Memory Debugging

WinDbg can be used to view memory (i.e. heap allocations) of a running process or processes saved in a dump file. This allows a developer to inspect the data structures and their values. This capability is helpful in identifying and analyzing memory leaks in an application.

While there’s a plethora of ways one can investigate memory leaks, the following is a rough guide of how one can use the functionalities of WinDbg for this purpose.

3.1. If debugging a running program

Analyzing memory leaks would be much faster if the problem can be recreated in a development environment. This would allow you to run the program and debug it in real time. Note however, most memory leaks may not seem to be apparent at first and the symptoms might not direct it to one as easily.

If the problem can be recreated or you opted to check directly with a running process then follow this procedure.

  1. 1. Set the gflags for the the application. At the command prompt, type in:
    gflags.exe name_of_application.exe +ust
    Setting the gflags for an application before it is executed allows debuggers (not just Windbg) to access more debug information for that program. Again, we want as much debug info as we can get.
    The gflags.exe program is a tool that came with Windows SDK, tells the system to add user dump information when running an application.
  2. Run the application to debug.
  3. Run WinDbg.
  4. Go to the [File → Attach to a Process..] menu. Select your application from the Window that will appear.
    clip_image012
    Once WinDbg is attached to a process, it will immediately put that process on a debug break as indicated in the Command Window that would appear next.
    clip_image014
    This is the Command Window of WinDbg. You type in the commands at the bottom of the Window and the results show up in the text area above it. From time to time the text at the bottom left of the window would show *BUSY* indicating that Windbg is still processing, simply wait for it to complete the task or hit CTRL+BREAK to abort that task.
  5. From here you can issue commands to start analyzing the memory for the application.

Note: Attaching WinDbg to a process would give you the ability to put breaks in its execution. You can use the commands in WinDbg to control when to start and stop the execution:

clip_image016

It is necessary to start (break) and stop (run) the application from time to time to switch between analyzing the process/memory data and operating your applications. You can only issue WinDbg commands for most of its operations while your application is stopped (break).

3.2. If debugging with a User Dump file

Most often, memory leaks are reported from a deployment site wherein you only have the log files and an attached dump file. If the problem cannot be immediately recreated on the development site and you can’t debug in the deployment site (which you normally can’t), just use the dump file to debug the application.

To start analyzing a dump file, use the following steps:

1. Go to [File → Open Crash Dump…] menu and browse to your dump file.
Once selected, WinDbg will begin loading the dump file, after which you would be greeted by a similar view if you were to attach to a running process (see previous section).
clip_image018

2. From here on, you may issue commands and begin analyzing the data for that user dump at the time it was generated.

3.3. Common Operations related to Memory

Once you’ve loaded WinDbg with either the dump file or attached to a process (see 3.1 and 3.2), use the following operations to detect the memory leaks.

Operation / Command Description / Purpose
!heap –s Display a table summarizing the allocations in the heap.
!heap –stat –h <heap_address> Display detailed information about the allocations for the specified <heap_address> showing their sizes.

Get the <heap_address> from the [!heap –s] command.

!heap -flt s <size> Displays a filtered list of heap allocations for the specified <size>.

Use the size from [!heap –stat –h] command.

This might produce a very long list. You may have to CTRL+BREAK at times. Try to check for a particular heap address area only then get the user pointer.

!heap –p –a <userptr_address> Display the call stack for the specified <userptr_address>.

Get the from the [!heap –flt s] command.

This may not be available if the symbols are not correct, incomplete or if the GFLAGS were not set.

!heap -l

note: it’s small letter “L”

Displays a list of ‘potential’ unreachable blocks’.

Potential unreachable blocks are areas in memory that were previously allocated but are not being pointed to anymore. Since, there are no more pointers to it; it might not be possible to de-allocate its memory.

The detected unreachable blocks might be false-positives and most applications have a few of these particularly those that use DLLs.

If available, you may use the [!heap –p –a] command to check these unreachable blocks.

Sections 3.3.1 and 3.3.2 are a sample usage of the above operations.

3.3.1. Check the heap for large allocations.

1. In the command window, enter:
!heap –s
This would show something similar to this (in the command window):
clip_image020

2. If you are analyzing a log file, the typical approach is to take note of the largest allocations.
Or, if you are debugging to an attached process:
2.1 Get the heap summary from Step 1;
2.2 Do some operations with your process
2.3 get another heap summary.
2.4 Compare the two summaries and take note of the modified allocations – this can be your suspect allocations.

3. In the command window again, issue the [!heap –stat] command similar below:
clip_image022

4. You may proceed to analyze the heap for this specific size through the [!heap –flt] command. One of the targets of doing this is to get a User Pointer. Issue the command similar below:
clip_image024
There might be too many of the allocations of the specified size, you may pick randomly as chances are: these allocations come from the same source in the code.

5. If the GFlags and WinDbg source files were properly set (and especially if you’re debugging a running process), proceed with checking the call stack with one of the User Pointers above. Issue the [!heap -p –a] similar below:
clip_image026

3.3.2. Check the heap for potential unreachable blocks

1. In the command window, enter: !heap –l
clip_image028

2. Based on the results of the [!heap –l] command, you can pick-up your analysis using the User column. You may check the call stack form here using the User pointer value if it is available.
clip_image030

3. You may at times wish to check out the actual data in the memory for a specific addresses. To do so, you need to open up the Memory Window. In this window, type in the User Pointer address that we acquired in [!heap –l] command.
clip_image032
In the above example, we can proceed investigating for possible leaks with string allocations in the SnmpExtensionClose() and SnmpExtensionInit() functions.

SAMPLE Resolution (in my sample case):

Continuing the investigation from the info we got from WinDbg, I’ve noticed that in the code of SnmpExtensionInit() function: there were BSTR’s that we’re allocated but not freed. It was related to this post in Microsoft:

http://msdn.microsoft.com/en-us/library/xda6xzx7(VS.71).aspx

Check out the second rule in that page.

clip_image034

And there you go! This is an example on how I got to solve a particular memory leak problem.

My target on the next post is using WinDbg with the focus on threads and processing. Hope, I don’t get too lazy or too busy with something else.

0 comments:

Post a Comment