The IIS Exception Monitor looks for exceptions and access violations in processes running on the IIS server, and monitors and diagnoses problems in both IIS and applications running under it. Last month, I discussed how to install the Exception Monitor and presented an overview of its operation. This month, I demonstrate how you can use this tool and other techniques to troubleshoot problems that frequently occur on an IIS server when applications are using COM objects.
Before I explain troubleshooting techniques, let's review what COM objects are and how they relate to Web applications. Microsoft COM is the hottest architecture in use today to build objects. COM objects, or components, are really applications that you build with a language such as C++ or Visual Basic (VB) to perform tasks such as database access, directory information, and security.
A COM application contains classes that become objects when another application uses components that a class exposes. A class is a module (or file) that contains subroutines and functions. A class' subroutines and functions make up its interface (COM objects don't have a visible user interface—UI). The interface consists of the class' methods (subroutines and functions—what the object can do) and properties (variables). You can often quickly assemble new applications from a collection of COM objects.
You can call the methods of an object from Active Server Pages (ASP) scripts, .exe files, and OLE .dll files. A method can return values and take any number of required or optional arguments—or none at all. Methods typically initiate an event within the object. Class methods can also use methods that exist in other classes. Thus, the class (or object) becomes reusable. For example, if a developer creates a Customer object that has the Add, Delete, and Update methods, any Windows application or Web application can use an ASP script to call these methods.
I created a class called CrashMe for testing. The class has the following methods:
This class facilitates testing various problem areas. For example, to test for an array-out-of-bounds condition, the code calls the GoForArrayBounds method.
Using a COM object in an ASP application is simple. You usually use the following syntax to create a variable for referencing the object, then instantiate the object with the CreateObject method:
<% dim oCrash set oCrash = server.Create Object("IISExceptionCauser .CrashMe") %>
In the previous syntax, IISExceptionCauser is a component that I created to test the Exception Monitor. CrashMe is a class in the component. CreateObject causes the CrashMe class to load into memory and returns a reference to that object. Notice that CrashMe becomes an object after the program loads it (i.e., instantiates it). Now you can use the oCrash variable to access the object's methods. For instance, you can use the following syntax to call the GoForStackSpace method:
<% oCrash.GoForStackSpace (0) %>
Finally, when you're finished using the object, you set the variable that references the object to Nothing, which releases the reference to the object and tells the OS to destroy it:
<% set oCrash = Nothing %>
When you compile an application that contains a class for use with COM, the application becomes a component. Components are the physical files (.dll or .exe) that serve as containers for classes. One component can have any number of classes.
You'll also see the terms in-process and out-of-process components frequently. In-process components (DLLs) execute in the memory space of the application executing a class' methods. Out-of-process components execute in a separate memory space and are therefore much slower than in-process components because they need to marshal (transport or convert) data between the application and the component.
You might think that this discussion doesn't apply to you because you don't use any COM objects in your applications; you use only ASP and ADO. However, ADO uses COM, so any data-driven site that is using ASP is most likely using COM. In summary, COM objects are simply applications that compile to .dll or .exe files and that have methods that other applications can call.
You can use several tools to track problems with COM objects: the Exception Monitor, Windows NT Performance Monitor, NT Event Viewer, and other tools. You can also use several tools together to monitor and track problems.
To illustrate how you can track down problems when you're working with COM objects, I created a simple VB component, which you see in Listing 1, that causes an error by recursively calling a function over and over. This loop eventually exhausts the stack space and triggers the following runtime error:
IISExceptionCauser error '800a001c' Out of stack space /IISExceptII/CrashMe.asp, line 16
If the Exception Monitor is running on the IIS server when this error occurs, the Exception Monitor doesn't pick up this type of error and doesn't generate a log. Instead, the VB runtime module traps this error and reports it to ASP, which delivers the error to the browser. VB receives reports about a variety of errors in this manner. If VB receives the error report, you might not receive information from the Exception Monitor.
To track down this error, note the line number in which the error occurred in CrashMe.asp (i.e., line 16), which reads
The application's developers will need to debug the application for you. The more information you can give them, the faster they can find the problem. For example, in this case the developers need to know that the program calls GoForStackSpace with a parameter of 0. If you have access to the source code, you can step through the problem method and debug it. In the routine in Listing 1, you need to limit the recursive call to prevent it from running out of stack space.
The VB runtime module will catch most errors that a VB component generates, such as the error I just described. Another example is if you exceed the capacity of an integer variable, VB will generate an overflow error, which will also show up in the browser.
Another sign of a COM problem is when an application attempts to use a component that won't execute. IIS 4.0 by default won't let you execute an out-of-process component (i.e., an .exe file) from an ASP application. When you try to execute a page that uses an out-of-process component, you'll see the following error:
Server object error 'ASP 0196 : 80040154' Cannot launch out of process component /myap/customer.asp, line 21
The last line includes the application and the ASP page that contains the component and the line number where the application launches the component.
To change IIS to allow the use of out-of-process components, enable the AspAllowOutOfProcComponents Metabase parameter. You set this parameter with the Metabase Editor in the Microsoft Internet Information Server Resource Kit or by using a VBScript routine. The Microsoft article "PRB: Cannot Launch Out of Process Component Under IIS 4" (http://support.microsoft.com/support/kb/articles/q184/6/82.asp) provides example VBScript code to implement this change.
Using the Exception Monitor
A third tool for tracing problems with COM objects is the Exception Monitor. You can use this tool when a component generates an exception error that the component or the runtime environment doesn't handle.
The Exception Monitor creates log files (i.e., .dbl files) when it encounters a problem or exception in a DLL running in an application or in the IIS process. You can examine the log files with the Log File Analyzer, which is part of the Exception Monitor.
I created several test methods in my VB component to determine how the Exception Monitor was trapping errors; I learned that it detects some errors and not others. For instance, the GoForOverflow method, which Listing 2 shows, loops through a For...Next loop and multiplies an integer until it generates an overflow. As I mentioned earlier, VB places checks for this type of error in the component's executable. However, if you tweak settings on the compiler to improve performance, VB might not catch the errors.
For example, you can use the Advanced Optimizations dialog from the Compile page of the Project's properties to tweak the code VB generates to get the highest level of performance from the code. However, this tweaking can also cause problems if your code isn't bulletproof.
I selected all but the first check box on the Advanced Optimizations page, then I went back to my test code. First, I recompiled the test DLL and moved it over to the server. Next, I started the Exception Monitor on the server. Then, I viewed the page that used the test DLL in a browser. This step generated an exception that the Exception Monitor picked up, and the monitor subsequently generated a log file. The error also caused the server to no longer process ASP or otherwise malfunction, and I had to reboot the server.
To troubleshoot this problem, I first looked in the NT event log, but none of the event log entries shed any light on he problem. Next, I started the Exception Monitor and selected the View Existing Log Files check box on the wizard's first page to display the Session Status window. To find the log file, I adjusted the borders of the Start Time column to a very narrow setting and looked at the End Time. I knew when the Exception Monitor generated the log, but not exactly when the error started. When you've selected the log to review, click View Log. In my test, the log didn't display for about 30 seconds, so be patient.
When the log reader loads the file and displays the main screen, the text in the top pane suggests that some type of exception occurred. The message also implies that C++ generated this exception. This error probably occurred because my VB component caused an exception that rippled up through some of the IIS or Windows DLLs. The bottom pane of the main screen shows the stack of executing code when the exception occurred.
Because I wasn't familiar with any of these DLLs, I needed more information. When I selected the Hide Microsoft Files check box to clean up the display, I saw the screen you see in Screen 1. Filtering out the Microsoft DLLs let me see more easily which DLLs the program had loaded when the crash occurred.
The top DLL in the list, crashcauser.dll, is my test DLL. Screen 1 informs me only that the program had loaded these DLLs when the problem occurred, not that my test DLL caused the problem. I didn't find any other indication that my DLL had caused the problem. This fact isn't surprising because many types of errors exist; the Exception Monitor reports only access violations, and the error in my program isn't an access error.
You can take the information that these tools have given you and use Performance Monitor or Task Manager to investigate further. For example, I learned that the application had loaded crashcauser.dll when the problem occurred. I could use Performance Monitor to observe how much CPU time crashcauser.dll is using. If some function in crashcauser.dll is consuming CPU time, a problem might exist in the code. I could then look at the ASP code to see what method it's calling and with what parameters. A developer could use that information to try to reproduce and fix the problem.
Use the Tools
As you use components in your applications more frequently, you need to understand them better. Tools such as Performance Monitor, Task Manager, and the Exception Monitor can help you troubleshoot the inevitable problems that occur. Learning which tools to use for which problem can save you time. Using the tools to observe your system when it's running well—and documenting the good readings—is also useful. You can use the good readings as baselines when problems occur.