Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Home HOME
Site Map SITE MAP
Index INDEX
Full-text search SEARCH
Forum FORUM
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG IN
                         
 
 
  Register for FREE Daily Updates:  
  Email:  
 
  The ASPToday Article
August 28, 2001
      Previous article -
August 27, 2001
   
 
   
   
   
DNA 2000 Debug Tracing   Salim Naim  
by Salim Naim
 
CATEGORY:  DNA 2000  
ARTICLE TYPE: Tutorial Reader Comments
   
    ABSTRACT  
 

Debugging a Distributed Internet Application (DNA) has never been an easy task. Due to the distributed nature, errors can rise from different layers. Current examples tend to focus on the debugging process of stepping through identified error-causing objects. While Your initial and most frustrated efforts are mostly spent on gathering enough information to identify the error causing tier or object. In this article Salim Naim provides a mode for outputting information from within the COM+ application to a centralized location (Console window) shared among all components within the package, thus helping to solve this problem.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

Debugging a Distributed Internet Application ( DNA) has never been an easy task. Due to the distributed nature, errors can rise from different layers such as: data service, business logic, or presentation, which lead more often than not, to problems that surface in vulnerable parts of your application. Your initial and most frustrated efforts are mostly spent on gathering enough information to identify the error causing tier or object. Numerous Examples are available online that can guide you through the process of debugging an object, primarily; the examples tend to focus on the debugging process of stepping through identified error-causing objects.For example: Debugging Compiled Visual Basic Components

The biggest obstacles faced by distributed application programmers isnˇ¦t debugging a single object but rather identifying the operability that is taking place between the objects in their respective tiers at runtime. Other major factors that add to the complexity experienced are the systems configurations, which vary from development to production environments, and the various miscellaneous bugs that can arise as a result. A method of gathering enough information, without disturbing the applications runtime environment, is needed to simplify the process. This helps to solve errors, which the programmer is faced with.

The object that will be written in this article, is referred to as the DNA.DEBUGGER, this is a C++ ATL COM+ object. This object provides a mode for outputting information from within the COM+ application to a centralized location (Console window) shared among all components within the package. This information can be used by the programmer to identify the execution taking place at runtime. This helps identify and determine what course of action to take. This process can often be simply identified by the "Call Stack" or internal memory state at certain traced stages. DNA DEBUGGER is best compared to the Debug.Print method of the immediate window in Visual Basic or TRACE macro of MFC in Visual C++.

DNA.DEBUGGER is a C++/ATL object; this article assumes that its audience are familiar with ATL and C++. If you are new to writing C++/ATL objects then please refer to msdn.microsoft.com for tutorials for getting started with developing objects using ATL. If you are a DNA Application programmer and your primary language for development is not C++/ATL. Then the details of the internal architecture of DNA.DEBUGGER can be ignored; you may proceed to using DNA.DEBUGGER and its runtime tracing functionality in the sample provided.

IDEBUGGER

COM+ and the dllhost

Before moving on to explain how DNA.DEBUGGER provides centralized debugging for all objects in a COM+ package, it is important to first understand how COM+ Applications work and how they are activated.

Every COM+ application or package created contains one or more INPROC servers. These objects include a surrogate process, which governs their activation and the other functionality that COM+ provides such as contexts, transaction processing object pooling, Just In Time (JIT) activation and security. The surrogate process dllhost.exe is sometimes mistaken in its behavior for being a service. Programmers make this assumption since COM+ applications can be started and shutdown, such that effectively their COM+ applications run as service. Dllhost is a process launched from a service using the CreateProcess API itˇ¦s not a service and should not be referred to as a service.

From the entire configuration that COM+ Application provides, the most interesting property is the identity configuration.

Figure 1: This dialog box is the Properties dialog for a COM+ 1.0 application in Component Services.

Dllhost.exe is created via a CreateProcess API call. One parameter that can alter the outcome of a COM+ package is the CreateProcess APIˇ¦s STARTUPINFO structure (please refer to the WIN32 SDK documentation). The LPSTR lpDesktop member of the structure specifies which window station and desktop the process will be associated with. This level of detail is usually hidden from the COM+ programmer, but is the fundamental nature of how an application can be traced.

Both Windows NT and Windows 2000 contain one visible window station (WinSta0), window stations and desktops are securable objects like any OS object (Events, Semaphore, etc...). Window stations are associated with zero or more desktop objects, among other things like clipboard global atoms, display, keyboard and mouse devices that display information to the end user. As a user have you come across three of the default window stations, WinSta0 (interactive) desktops: Default, Winlogon and the screen saver. Default is the Explorer or shell that you run all your processes in, Winlogon is the Ctrl + Alt + Del screen and Screen saver is the desktop that displays your screen saver.

COM+ Identity gives the user the options of running the application as (refer to Figure 1 )

These two options determine what behavior the application will inherit and what can and cannot be seen at runtime when the application is activated.

Interactive User

When an application is configured to run as an " Interactive User ", the first activating server process will be used to service all requests. This server instance will belong to the interactive window station WinSta0\Default and hence the console window associated with dllhost will be visible to the end user.

Note: the disadvantage of running an interactive user is that a user must be logged on locally or else the COM+ application will fail to activate and service requests.

This User

When an application is configured to run as a "This User", the first server process will be used to activate and service all requests, however the first server process will have its own window station created as part of the first process creation. COM never uses the interactive window station WinSta0 for Applications configured to run as "This User" and hence the interaction with this mode of identity is not visible by the executing systems interactive desktop WinSta0\Default. Itˇ¦s interesting to note that even if the specified user is the same as the logged on (interactive user), COM will not use the interactive station.

Note: "This User" is the recommended mode of running COM+ application. This mode doesnˇ¦t require a user to be logged on to the executing system and is the preferred option when you are not debugging an application.

The Object

HKEY_LOCAL_MACHINE\SOFTWARE\DNADEBUGGER\debug

settings.

Initialize ()

This Method sets up the Console for the surrogate process (dllhost.exe) and redirects the executables STDOUT, STDIN and STDERR to the console window. This method can be called many times by objects within the application however only the initial call will allocate the console window.

To avoid constantly querying the registry upon every instantiation of DNA.DEBUGGER, the object stores its setting in COM+ ObjectContexts SharedPropertyManager.

SharedPropertyManager

The SharedPropertyManager is a resource dispenser that allows objects within a COM+ Application to share data among each other. The SharedPropertyManager contains SharedPropertyGroups and Shared Properties in which data is stored. The SharedPropertyManager avoids name collision of variables with unique namespaces and also handles concurrency for property access.

Figure 2: The Relationship between Shared Properties to the SharedPropertyManager.

Note: this figure was obtained from msdn. For additional information about the SharedPropertyManager and storing state in COM+ application, refer to msdn.microsoft.com and search using the following criteria "COM+ shared property".

DNA.DEBUGGER stores the queried debug registry setting within a SharedPropertyGroup named DNA_DEBUGGER_SETTING in a SharedProperty called DEBUG .

For a complete listing of the code that stores and queries the SharedPorpertyManager in C++, please refer to the SharedProp.CPP file accompanying the article.

STDMETHODIMP CDEBUGGER::Initialize()
{
FILE,  *hFile;
       HANDLE hSTDINConsole,hSTDOUTConsole,hSTDERRConsole; 
       HRESULT       HR = S_OK;
       CHAR          szStreamOpenMode[] = "w";
       INT           hFileHandleCRT;
       INT           i;
       ...

              //only carry on and create the console if debugTracing
              //is enabled
              if(m_DebuggTracing == FALSE)
                     return S_OK;
              
              //Alocate a console for the calling process
              AllocConsole();
              
              //Get the Handle for the STDIN,STDOUT and STDERR devices
              ...

              hSTDOUTConsole = GetStdHandle(STD_OUTPUT_HANDLE);
              if(hSTDOUTConsole == INVALID_HANDLE_VALUE) 
              return HRESULT_FROM_WIN32(GetLastError());
              ...

              //associate the OS HANDLE with a C Runtime
              //hande for the C Run time io streams
              
              //############ S T D O U T ############//
              hFileHandleCRT = 
_& _open_osfhandle((long)hSTDOUTConsole,_O_TEXT);
              if(hFileHandleCRT == -1) return E_FAIL;

              hFile = _fdopen(hFileHandleCRT,szStreamOpenMode);
              if(hFile == NULL) return
_& HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);

              *stdout = *hFile;
              i = setvbuf(stdout,NULL,_IONBF,0);
              cout.sync_with_stdio();
              ...
}

The Initializing process starts with a call to the Win32 AllocConsole, this API allocates a new console window for the calling process, if one doesnˇ¦t exist. The remainder of the code retrieves the Handles for the STDIN , STDOUT and the STDOUT and synchronizes the handles with the C Runtime handles of cin , cout and cerr .

With the STDOUT for the process now being redirected to the newly allocated console the process of tracing data is as simple as printf . To create a language independent IO stream wrapper, DNA.DEBUGGER wraps a wprintf function call with the DebugTrace Method.

DebugTrace ()

This method is a simple wrap for a wprintf statement. Additional information is added to the traced text within this method and a timestamp of the trace is added for additional reference.

Note: objects written in Java, VB or C++ are not bound to using DebugTrace for outputting information to the standard output. These objects can use their language specific implementation of IO streams. The main benefit that DebugTrace provides is that it contains additional runtime checking that will only output information if tracing is enabled. This feature is particularity useful if you decide to build an object with DebugTrace method calls that are compiled with the object. In practice, run-time / release build tracing, especially of large apps, is being increasingly recommended to make technical support easier.

A Snippet from the DebugTrace Method implementation:

...
//Get time in seconds
time( &_SystemClock);

//Convert time to struct 
pnewtime = localtime( &_SystemClock); 

//output the message and makes sure that we timestamp in
//_wasctime always return 26 characters with the last two characters
//being \n\0 so we trim those two characters.
wprintf(L"<DEBUGTRACE @ %.24s> %s\n",_wasctime(pnewtime),(wchar_t*)_bstr_t(lpvValue));
...

MemoryTrace ()

Memory leaks are one of the hardest things to eliminate in DNA applications; this task becomes even more complicated as applications rely on many objects to provide the entire functionality that an application is providing. Leaky objects can slowly exhaust server resources over time, causing the surrogate dllhost to misbehave and jeopardize the applications stability. To assist in addressing memory leaks, DNA.DEBUGGER implements MemoryTrace . This method output traces Win32 GlobalMemoryStatus calls to give the programmer an indication of those resources are being consumed in, with traced MemoryTrace calls, an application can quickly pinpoint resource leaks.

Sample Debug Tracing

DNA.DEBUGGER relies on one registry setting that determines whether to create a Console window for debug tracing or not, this registry entry located in

HKEY_LOCAL_MACHINE\SOFTWARE\DNADEBUGGER\debug

Figure 3: Screenshot of the registry setting.

This variable is of type REG_DWORD and has the following settings.

Note: This registry setting must be set manually to enable tracing.

To demonstrate the use of DNA.DEBUGGER, a hypothetical sample that relies on two objects. One written in VB and the other in ATL will be used to simulate a typical DNA application.

Figure 4

IIS and COM+

IIS Applications that require a higher level of application isolation such as MEDIUM (default setting in IIS5) and HIGH application isolation can take full advantage of DNA.DEBUGGER.

The DebugTrace method when used with this level of application protection outputs traced text from ASP providing the added benefit of debugging your site at runtime without Response.Write being sprinkled throughout the code. This comes as a result of the COM+ integration that is built into IIS.

Figure 5:Screenshot of an IIS application, property settings

To Start the Debug tracing process, ASPApplication.asp a sample ASP page will Instantiate an object written in VB Sample.VBCOM and call one of its method. This object will subsequently call another object written in C++ Sample.ATLCOM. The entire process will be traced via the messages that get outputted from the object and at the initiating stage in ASP.

Note: attached with the code accompanying the article is the source code for both objects used (Sample.VBCOM and Sample.ATLCOM) and the ASPApplication.asp.

ASPApplication.asp

<%
   Dim objVBCOM

   Call DebugTrace("Starting Object Call from ASP")
   ...

   Set objVBCOM = Server.CreateObject("Sample.VBCOM")
   if IsObject(objVBCOM) Then
       Call objVBCOM.VBMethodCall()
       Set objVBCOM = Nothing
   End if
%>

The DebugTrace Function is nothing but a function wrapper to DNA.DEBUGGER, this function traces its text arguments, to the surrogates console window.

Function DebugTrace(lpszText)
    Dim obj
    Set obj = Server.CreateObject("DNA.DEBUGGER")

   'No error checking
    Call obj.Initialize()
    Call obj.DebugTrace(lpszText)
    
   'Free
   Set obj = Nothing
End Function

The first object called from ASP Sample.VBCOM, contains one sample method called VBMethodCall , this method subsequently creates another object that is written in C++. Information from within both objects is traced to the shared console of the Application.

Sample.VBCOM object

Public Function VBMethodCall()
    '... some operation    
    DebugTrace "This is data traced from VBMethodCall"
    
    'Calling an ATL object
    Call ATLObjectMethod    
    '... some other code
End Function

Note: The implementation of the DebugTrace function within this object again is a function wrapper to DNA.DEBUGGER.

Sample.ATLCOM object

This object contains a single method called ATLMethodCall; this method traces information to the debug session Console.

//no error checking, just and example.
spDebugTracer.CreateInstance(__uuidof(DEBUGGER));
	
if(spDebugTracer)
spDebugTracer->DebugTrace(&_variant_t("ATLMethodCall() Method Call from ATL"));

The sample debug application shows ways in which objects can add additional debug tracing information that will be available when the debug registry setting is set for debug tracing.

Sample Output

The result of the entire procedure is shown below within the surrogates console window:

Figure 6: Screenshot of sample debug trace.

Summing it up

DNA. DEBUGGER is a valuable and rewarding resource for locating bugs and memory leaks. The object provides great runtime support that can take place without altering the runtime systems environment. This object lacks one possible enhancement, which is the ability to log traces to a log file as well as to the console window. The overhead of using object is the addition of DebugTrace method calls compiled into the consuming object. DNA.DEBUGGER attempts to minimize the overhead of having its methods used by the SharedPropertyManager , by only performing an operation when debug tracing is enabled. The final decision on whether to use debug tracing is specific to each DNA application but will guarantee a successful method of debugging an application.

Information on working with the sample is specified in the readme accompanying the code.

System Requirements

To work with the sample provided in this article, the following Servers/Applications are required.