Welcome to MSDN Blogs Sign in | Join | Help

Getting where the exception is thrown in Windows Error Report for managed application without a dump

A while ago, an internal thread shows how to get where the exception is thrown in Windows Error Report for managed application without a dump. The thread is very insightful. Shared below:

Subject: RE: How to investigate Windows Error Report from managed app?

 

FYI

 

I actually found that there is way to find what method caused this error and where without the dump. See below:

 

Problem signature

Problem Event Name:             CLR20r3

Problem Signature 01:            consoleapplication35.exe

Problem Signature 02:            1.0.0.0

Problem Signature 03:            48ed4a20

Problem Signature 04:            ConsoleApplication35

Problem Signature 05:            1.0.0.0

Problem Signature 06:            48ed4a20

Problem Signature 07:            1

Problem Signature 08:            2e

Problem Signature 09:            System.InvalidOperationException

OS Version:                              6.0.6001.2.1.0.272.7

Locale ID:                                1033

 

The Problem Signature 07 is MethodDef token. And SOS provides !Token2EE command to find the token. The trick here is that it trimmed “06” from the top, so its original value should be 0600000 + the value you see from Problem Signature 07 which is 06000001 in this example.

 

Now use !Token2EE to figure out the method:

 

0:003> !Token2EE ConsoleApplication35 06000001

Module: 00112c5c (ConsoleApplication35.exe)

Token: 0x06000001

MethodDesc: 00112ff0

Name: ConsoleApplication35.Program.Main(System.String[])

JITTED Code Address: 001b0070

 

Now you use !dumpil to list all IL for the method and check the IL offset to find which line actually failed.

 

0:003> !dumpil 00112ff0

ilAddr = 00ee2050

IL_0000: nop

IL_0001: ldstr "notepad.exe"

IL_0006: ldnull

IL_0007: call System.Diagnostics.Process::Start

IL_000c: stloc.0

.try

{

  IL_000d: nop

  IL_000e: ldloc.0

  IL_000f: callvirt System.Diagnostics.Process::WaitForExit

  IL_0014: nop

  IL_0015: nop

  IL_0016: leave.s IL_0028

} // end .try

.finally

{

  IL_0018: ldloc.0

  IL_0019: ldnull

  IL_001a: ceq

  IL_001c: stloc.1

  IL_001d: ldloc.1

  IL_001e: brtrue.s IL_0027

  IL_0020: ldloc.0

  IL_0021: callvirt System.IDisposable::Dispose

  IL_0026: nop

  IL_0027: endfinally

} // end .finally

IL_0028: nop

IL_0029: newobj System.InvalidOperationException::.ctor

IL_002e: throw

 

That’s it.

Posted by junfeng | 0 Comments
Filed under:

Internal Manifest vs External Manifest

Internal manifest is the RT_MANIFEST resource in the executable. 

External manifest is App.exe.manifest.

In Windows XP, Sxs searches external manifest before internal manifest. If an external manifest is found, the internal manifest is ignored.

 In Windows Server 2003 and later, the order is reversed.  Internal manifest is preferred over external manifest.

If you use external manifest, and your scenario works in Windows XP, but not Windows Server 2003 and later, please double check the executable does not have an internal manifest

Posted by junfeng | 3 Comments
Filed under:

CLR HRESULT

CLR HRESULT’ facility code is 0x13. If an HRESULT error is 0x8013xxxx, it is likely a CLR error.

All CLR errors are defined in CorError.h. You can find CorError.h in your Visual Studio install (or Microsoft Platform SDK).

C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\CorError.h

For example,

#define FUSION_E_REF_DEF_MISMATCH       EMAKEHR(0x1040)     // The located assembly's manifest definition does not match the assembly reference.

That is, 0x80131040 is FUSION_E_REF_DEF_MISMATCH.

Posted by junfeng | 3 Comments

Be careful about exception after resource allocation

The following is a common code pattern

        Resource resource = GetResource();

        DoWork();           

        return resource;

If DoWork() throws exception, the resource will be leaked. We need to guard against this.

For example

        bool success = false;

        Resource resource = GetResource();

        try

        {

            DoWork();

            success = true;

            return resource;

        }

        finally

        {

            if (!success)

            {

                DisposeResource(resource);

            }

        }

Posted by junfeng | 6 Comments
Filed under:

Exception Filter

C# does not support exception filter. However, VB and IL support it. To add exception filter to C#, we can build a function in VB or IL, then call it in C#.

The example below is one way we may implement the function to support exception filter. It asks the caller to provide four delegates, and call them at the right places.

Imports Microsoft.VisualBasic

 

Namespace Utility

 

    Public Delegate Sub TryDelegate()

    Public Delegate Function FilterDelegate(ByVal exception As System.Exception) As Boolean

    Public Delegate Sub CatchDelegate(ByVal exception As System.Exception)

    Public Delegate Sub FinallyDelegate()

 

    Public Class ExceptionFilter

        Inherits System.Object

 

        Public Shared Sub TryFilterCatch(ByVal tryDelegate As TryDelegate, ByVal filterDelegate As FilterDelegate, ByVal catchDelegate As CatchDelegate, ByVal finallyDelegate As FinallyDelegate)

            Try

                tryDelegate()

            Catch ex As System.Exception When catchDelegate <> Nothing And filterDelegate(ex)

                catchDelegate(ex)

            Finally

                If (finallyDelegate <> Nothing) Then

                    finallyDelegate()

                End If

            End Try

        End Sub

 

    End Class

 

End Namespace

 

In C# we can call Utility.TryFilterCatch as the following:

using System;

using Utility;

 

namespace ExceptionFilterUsage

{

    class Program

    {

        static void Main(string[] args)

        {

            TryDelegate tryDelegate = delegate()

            {

                Console.WriteLine("In try.");

                throw new ApplicationException();

            };

 

            FilterDelegate filterDelegate = delegate(Exception e)

            {

                Console.WriteLine("In exception filter.");

 

                if (e is ApplicationException)

                {

                    Console.WriteLine("ApplicationException is thrown, which should never happen.");

                    return false;

                }

                return true;

            };

 

            CatchDelegate catchDelegate = delegate(Exception e)

            {

                Console.WriteLine("In Catch: Exception: {0}", e);

            };

 

            FinallyDelegate finallyDelegate = delegate()

            {

                Console.WriteLine("In finally.");

            };

 

            ExceptionFilter.TryFilterCatch(tryDelegate, filterDelegate, catchDelegate, finallyDelegate);

        }

    }

}

 

Posted by junfeng | 7 Comments
Filed under:

ThreadPool.BindHandle

I mentioned that we can use ThreadPool.BindHandle to implement asynchronous IO. Here are roughly the steps necessary to make it happen:

1.       Create an overlapped file handle

            SafeFileHandle handle = CreateFile(

                                filename,

                                Win32.GENERIC_READ_ACCESS,

                                Win32.FILE_SHARE_READ | Win32.FILE_SHARE_WRITE | Win32.FILE_SHARE_DELETE,

                                (IntPtr)null,

                                Win32.OPEN_EXISTING,

                                Win32.FILE_FLAG_OVERLAPPED,

                                new SafeFileHandle(IntPtr.Zero, false));

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        private static extern SafeFileHandle CreateFile(

           string lpFileName,

           uint dwDesiredAccess,

           uint dwShareMode,

            //SECURITY_ATTRIBUTES lpSecurityAttributes,

           IntPtr lpSecurityAttributes,

           uint dwCreationDisposition,

           int dwFlagsAndAttributes,

           SafeFileHandle hTemplateFile);

2.       Bind the handle to thread pool.

            if (!ThreadPool.BindHandle(handle))

            {

                Console.WriteLine("Fail to BindHandle to threadpool.");

                return;

        }

3.       Prepare your asynchronous IO callback.

                byte[] bytes = new byte[0x8000];

 

                IOCompletionCallback iocomplete = delegate(uint errorCode, uint numBytes, NativeOverlapped* _overlapped)

                {

                    unsafe

                    {

                        try

                        {

                            if (errorCode == Win32.ERROR_HANDLE_EOF)

                                Console.WriteLine("End of file in callback.");

 

                            if (errorCode != 0 && numBytes != 0)

                            {

                                Console.WriteLine("Error {0} when reading file.", errorCode);

                            }

                            Console.WriteLine("Read {0} bytes.", numBytes);

                        }

                        finally

                        {

                            Overlapped.Free(pOverlapped);

                        }

                    }

                };   

 

4.       Create a NativeOverlapped* pointer.

                    Overlapped overlapped = new Overlapped();

 

                    NativeOverlapped* pOverlapped = overlapped.Pack(iocomplete, bytes);

 

                pOverlapped->OffsetLow = (int)offset;

5.       Call the asynchronous IO API and pass the NativeOverlapped * to it.

                    fixed (byte* p = bytes)

                    {

                        r = ReadFile(handle, p, bytes.Length, IntPtr.Zero, pOverlapped);

                        if (r == 0)

                        {

                            r = Marshal.GetLastWin32Error();

                            if (r == Win32.ERROR_HANDLE_EOF)

                            {

                                Console.WriteLine("Done.");

                                break;

                            }

 

                            if (r != Win32.ERROR_IO_PENDING)

                            {

                                Console.WriteLine("Failed to read file. LastError is {0}", Marshal.GetLastWin32Error());

                                Overlapped.Free(pOverlapped);

                                return;

                            }

                        }

                    }

 

        [DllImport("KERNEL32.dll", SetLastError = true)]

        unsafe internal static extern int ReadFile(

            SafeFileHandle handle,

            byte* bytes,

            int numBytesToRead,

            IntPtr numBytesRead_mustBeZero,

            NativeOverlapped* overlapped);

 

Your IO callback will be invoked by CLR thread when the IO completed.

 

So when should you use ThreadPool.BindHandle? The answer is almost *Never*. .Net Framework's FileStream class internally uses ThreadPool.BindHandle to implement the async IO. You should always use FileStream if possible.

Posted by junfeng | 8 Comments
Filed under:

ThreadPool.UnsafeQueueNativeOverlapped

CLR’s thread pool has two pools of threads. The first pool is used by ThreadPool.QueueUserWorkItem. The second pool is an IoCompletionPort thread pool used by ThreadPool.BindHandle and ThreadPool.UnsafeQueueNativeOverlapped.

ThreadPool.BindHandle is used by CLR to implement asynchronous IO. For example, FileStream uses it to implement BeginRead/BeginWrite. Developers can take advantage of it too. We will talk about that in a separate article.

ThreadPool.UnsafeQueueNativeOverlapped can be used to queue a non IO work item to the IoCompletionPort thread pool, just like ThreadPool.QueueUserWorkItem.

Why will you want to use ThreadPool.UnsafeQueueNativeOverlapped instead of ThreadPool.QueueUserWorkItem?

In our development, we discover an inefficiency of ThreadPool.QueueUserWorkItem. If we have some alternate high and low number of work items, some of the threads may do busy waiting, artificially increase the CPU usage of our application.

If you have the same pattern, and you have observed high CPU usage when it should not, you can try ThreadPool.UnsafeQueueNativeOverlapped.

The following is an example how to ThreadPool.UnsafeQueueNativeOverlapped.

using System;

using System.Runtime.InteropServices;

using System.Threading;

 

namespace UQNO

{

    internal class AsyncHelper

    {

        WaitCallback callback;

        object state;

 

        internal AsyncHelper(WaitCallback callback, object state)

        {

            this.callback = callback;

            this.state = state;

        }

 

        unsafe internal void Callback(uint errorCode, uint numBytes, NativeOverlapped* _overlapped)

        {

            try

            {

                this.callback(this.state);

            }

            finally

            {

                Overlapped.Free(_overlapped);

            }

        }

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            ManualResetEvent wait = new ManualResetEvent(false);

 

            WaitCallback callback = delegate(object state)

            {

                Console.WriteLine("callback is executed in thread id {0} name {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);

                ManualResetEvent _wait = (ManualResetEvent)state;

                _wait.Set();

            };

 

            AsyncHelper ah = new AsyncHelper(callback, wait);

 

            unsafe

            {

                Overlapped overlapped = new Overlapped();

                NativeOverlapped* pOverlapped = overlapped.Pack(ah.Callback, null);

                ThreadPool.UnsafeQueueNativeOverlapped(pOverlapped);

                wait.WaitOne();

            }

        }

    }

}

Posted by junfeng | 3 Comments
Filed under:

Conversion between System.String and char *

We can convert a char * to System.String with System.String’s constructor

string str = new string((char*)p);

And for the reverse:

fixed(char *p = str){}

Why do we care about conversion between System.String and char *?

From this article, this is the fastest way to marshal strings between managed and native boundary.

Posted by junfeng | 3 Comments
Filed under:

XXX is not a valid Win32 application

If you have used your Vista SP1-based computer for extended period, you may experience some problems starting large applications, for example, Office 2007 applications. Specifically, you may receive a message “XXX is not a valid Win32 application”.

If you do experience this problem, you can install hotfix KB952709.

http://support.microsoft.com/kb/952709/

Posted by junfeng | 1 Comments
Filed under:

Managed ThreadPool vs Win32 ThreadPool (pre-Vista)

The following is a conversation between me and a CLR dev. The conversation is very informative so I quote it here.

From: 
Sent:
To:
Subject: RE: ThreadPool.QueueUserWorkItem

 

There might be some confusion here around the meaning of the term "I/O Thread."  In the Windows thread pool (the old one, not the new Vista thread pool), an "I/O thread" is one that processes APCs queued by other threads, or by I/O initiated from the I/O threads.  The "non-I/O" threads get their work from a completion port, either as a result of QueueUserWorkItem, or I/O initiated on a handle bound to the threadpool with BinIoCompletionCallback.  So they are both geared toward processing I/O completions, but they just use different mechanisms.

 

In the managed ThreadPool, we use the terms "worker thread" and "I/O thread."  In our case, an I/O thread is one that waits on a completion port; i.e., it's exactly equivalent to Windows' non-I/O thread.  How confusing!  Our "worker threads" wait on a simple user-space work queue, and never enter an alertable state (unless user code does so), and so explicitly do not process APCs.  Managed "worker threads" have no equivalent in the Windows thread pool, just as Windows "I/O threads" have no managed equivalent.

 

The managed QueueUserWorkItem queues work to the "worker threads" only.  UnsafeQueueNativeOverlapped queues to the I/O threads, as do completions on handles that have been bound to the ThreadPool via BindHandle.

 

Why don't we support APCs as a completion mechanism?  APCs are really not a good general-purpose completion mechanism for user code.  Managing the reentrancy introduced by APCs is nearly impossible; any time you block on a lock, for example, some arbitrary I/O completion might take over your thread.  And they don't scale well, except in certain very constrained scenarios, because there's no load-balancing of completions across threads.  You can, of course, implement your own load balancing, but you'll never do better in user-space than the kernel does with completion ports.  So we provide a rich async I/O infrastructure based on completion ports, and nothing else.

 

From: 
Sent:
To:
Subject: RE: ThreadPool.QueueUserWorkItem

 

With ThreadPool.QueueUserWorkItem, the callback is not called on an I/O thread. You might be looking for ThreadPool.UnsafeQueueNativeOverlapped.

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.unsafequeuenativeoverlapped.aspx


From: Junfeng Zhang
Sent:
To:
Subject: ThreadPool.QueueUserWorkItem

The native kernel32 QueueuserWorkItem has a flag to indicate to schedule the callback in I/O thread or not. The flag is missing in the managed ThreadPool.QueueUserWorkItem.

 

Why is so? Is it because the callback is always scheduled on a I/O thread?

 

 

Posted by junfeng | 5 Comments
Filed under:

Managed Watson Dump

From .Net Framework 2.0, Dr. Watson is able to generate dump compatible with .Net framework. This means, dumps with heap data generated by Dr. Watson contains information about managed heap so they can be analyzed with sos.dll.

Some update to Dr.Watson is necessary to make this happen. Dumps generated by older Dr.Watson is not compatible with .Net framework.

To see if a dump is compatible with .Net framework, make sure the mini dump's version is equal or higher than 6400.

 

0:003> .dumpdebug

----- User Mini Dump Analysis

 

MINIDUMP_HEADER:

Version         A793 (6407)

NumberOfStreams 8

Flags           120

                0020 MiniDumpWithUnloadedModules

                0100 MiniDumpWithProcessThreadData

 

Posted by junfeng | 1 Comments
Filed under:

Inspect a 32 bit Process Dump Generated by a 64 bit Debugger

64 bit Windows can run both 32 bit process and 64 bit process. For debugging though, you want to use 32 bit debugger to debug 32 bit process, and 64 bit debugger for 64 bit process. Otherwise it won’t be pretty.

Occasionally, I receive a 32 bit process dump generated by a 64 bit debugger.

When load some dump in the debugger, this is how it looks like:

0:020> .sympath SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols

  20  Id: 1994.15f4 Suspend: 1 Teb: 00000000`7efa4000 Unfrozen
RetAddr           : Args to Child                                                           : Call Site
00000000`78b84191 : 00000023`7d61c918 00000000`00000023 00000000`00000202 00000000`0518fffc : wow64cpu!CpupSyscallStub+0x9
00000000`6b006a5a : 00000000`00000003 00000000`00000000 00000000`00000000 00000000`051cf7d0 : wow64cpu!Thunk2ArgNSpNSpReloadState+0x21
00000000`6b005e0d : 00000000`051cfd00 00000000`051cf1d0 00000000`051cf7d0 00000000`00000000 : wow64!RunCpuSimulation+0xa
00000000`77f109f0 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`7efdf000 : wow64!Wow64LdrpInitialize+0x2ed
00000000`77ef30a5 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrpInitialize+0x2aa
00000000`7d4d1504 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!KiUserApcDispatcher+0x15
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadStartThunk
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 0001002f`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 0001002f`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 0001002f`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 0001002f`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
0001002f`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x1002f`00000000
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x0

I do not know if you can make sense of it. I cannot.

Fortunately, Windows debugger allows you to change the target processor type. Here is how it works:

0:020> .effmach x86
Effective machine: x86 compatible (x86)
0:020:x86> .load wow64exts

  20  Id: 1994.15f4 Suspend: 1 Teb: 00000000`7efa4000 Unfrozen
ChildEBP          RetAddr           Args to Child                                        
0518ff0c 7d4d0ec5 00000000 0518ff4c 7d4d8bfa ntdll_7d600000!ZwDelayExecution+0x15
0518ff74 7d4d14ef 0000ea60 00000000 0518ffac kernel32!SleepEx+0x68
0518ff84 776bbb0f 0000ea60 083e6ca8 776bbab4 kernel32!Sleep+0xf
0518ff90 776bbab4 00000000 00000000 083e6ca8 ole32!CROIDTable::WorkerThreadLoop+0x14
0518ffac 776b1704 00000000 0518ffec 7d4dfe21 ole32!CRpcThread::WorkerLoop+0x26
0518ffb8 7d4dfe21 083e6ca8 00000000 00000000 ole32!CRpcThreadCache::RpcWorkerThreadEntry+0x20
0518ffec 00000000 776b16e4 083e6ca8 00000000 kernel32!BaseThreadStart+0x34

Much better, isn’t it?

For best result though, please use 32 bit debugger to generate the dump for a 32 bit process.

Posted by junfeng | 2 Comments
Filed under:

Getting a Full Memory Dump for a Process

To diagnose a problem for a remote customer, sometimes the easiest way is to have the customer generate a full memory dump for the process, and share the memory dump.

In Vista, task manager can generate a full memory dump from the Processes tab.

image

In Windows XP , this functionality does not exist. However, Windows XP ships a debugger ntsd.exe in the box. We can use it to generate a full memory dump.

C:\temp>ntsd -p 316
...
0:002> .dump /f c:\temp\foo.dmp
Creating c:\temp\foo.dmp - user full dump
0:002>

Posted by junfeng | 0 Comments
Filed under:

Event Handles “leak”

On our stress run, we saw our process’ handle count steadily increases until certain point, then it stabilizes. However the number of handles is high. Most of those handles are Event handles. We are concerned about it. So we went off and did some investigation.

Turns out the Event handles are coming from the use of Monitor.

When there is contention on the lock object, CLR internally creates an Event handle, presumably to facilitate the thread scheduling. The event handle is not cleaned up until the object is garbage collected.

It appears we were using Monitor in a lot of places, and we had lock contentions, which triggers CLR to allocate a lot of Event handles.

So if you have a lot of long lived objects, be careful about the usage of Monitor.

Posted by junfeng | 3 Comments
Filed under: ,

Use !htrace to debug handle leak

Windbg Debugger’s !htrace extension is very handy to debug handle leak.

The process essentially boils down to the following simple steps:

1.       Enable trace

2.       Take a snapshot

3.       Run scenario

4.       Show the diff

On step 4, !htrace will show all the extra opened handles after the last snapshot, along with the callstack if available. This greatly helps to debug what handles are leak, and by whom.

Like any other resource leak detection tool, there will be false positives. You need to understand what is a real leak, and what is just a transient allocation.

 

 

!htrace

The !htrace extension displays stack trace information for one or more handles.

Syntax

User-Mode Syntax

!htrace [Handle [Max_Traces]] 
!htrace -enable [Max_Traces]
!htrace -snapshot
!htrace -diff
!htrace -disable
!htrace -? 

Kernel-Mode Syntax

!htrace [Handle [Process [Max_Traces]]] 
!htrace -? 

Parameters

Handle

Specifies the handle whose stack trace will be displayed. If Handle is 0 or omitted, stack traces for all handles in the process will be displayed.

Process

(Kernel mode only) Specifies the process whose handles will be displayed. If Process is 0 or omitted, then the current process is used. In user mode, the current process is always used.

Max_Traces

Specifies the maximum number of stack traces to display. In user mode, if this parameter is omitted, then all the stack traces for the target process will be displayed.

-enable

(User mode only) Enables handle tracing and takes the first snapshot of the handle information to use as the initial state by the -diff option.

-snapshot

(User mode only) Takes a snapshot of the current handle information to use as the initial state by the -diff option.

-diff

(User mode only) Compares current handle information with the last snapshot of handle information that was taken. Displays all handles that are still open.

-disable

(User mode only; Windows Server 2003 and later only) Disables handle tracing. In Windows XP, handle tracing can be disabled only by terminating the target process.

-?

Displays some brief Help text for this extension in the Debugger Command window.

DLL

Windows NT 4.0

Unavailable

Windows 2000

Unavailable

Windows XP and later

Kdexts.dll
Ntsdexts.dll

Comments

Before !htrace can be used, Application Verifier must be activated for the target process, and the Detect invalid handle usage option must be selected. By activating Application Verifier, stack trace information is saved each time the process opens a handle, closes a handle, or references an invalid handle. It is this stack trace information that !htrace displays. For more information, see Application Verifier.

The following example displays information about all handles in process 0x81400300:

kd> !htrace 0 81400300
Process 0x81400300
ObjectTable 0xE10CCF60

--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D

--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x5 stack traces.


Additional Information

For information about handles, see the Microsoft Windows SDK documentation and Microsoft Windows Internals by Mark Russinovich and David Solomon. To display further information about a specific handle, use the !handle extension.


© 2007 Microsoft Corporation
Send feedback on this topic

 

Posted by junfeng | 3 Comments
Filed under:
More Posts Next page »
 
Page view tracker