Welcome to MSDN Blogs Sign in | Join | Help

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/pl

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 | 4 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:

Consider Creating a new class for locking

C# provides keyword lock for synchronized access.  

A good practice is to create a private object for locking purpose. For example,

public class LockExample

{

    private object syncObject = new object();

 

    public void SynchronizedMethod()

    {

        lock (syncObject)

        {

        }

}
}

It may make sense to create a class just for locking purpose:

using System;

 

class LockClass : object { };

 

public class LockExample

{

    private object syncObject = new LockClass();

 

    public void SynchronizedMethod()

    {

        lock (syncObject)

        {

        }

    }

}

The advantage of the second approach, is that you can easily tell how many lock objects you have used in your project using SOS command “!dumpheap -mt”.

Posted by junfeng | 5 Comments
Filed under:

A case study of a NullReferenceException

We are seeing a NullReferenceException in our stress program. The investigation process may be helpful to some folks.

 

Thread 22 is showing a Watson dialog.

 

This is a register corruption.

 

0:022> kp

ChildEBP RetAddr 

091bc9a0 76961220 ntdll!ZwWaitForSingleObject(void)+0x15

091bca10 76961188 kernel32!WaitForSingleObjectEx(void * hHandle = 0x00001034, unsigned long dwMilliseconds = 0x4e20, int bAlertable = 0)+0xbe

091bca24 7bb1fca4 kernel32!WaitForSingleObject(void * hHandle = 0x00001034, unsigned long dwMilliseconds = 0x4e20)+0x12

091bca54 7bb202b4 mscorwks!ClrWaitForSingleObject(void * handle = 0x00001034, unsigned long timeout = 0x4e20)+0x24

091bcf10 7bb23570 mscorwks!RunWatson(void * hWatsonSharedMemory = 0x000015f4, void * hEventAlive = 0x00001034, void * hEventDone = 0x00000d7c, void * hMutex = 0x00001378)+0x18f

091bd658 7bb23b43 mscorwks!DoFaultReportWorker(struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac, class TypeOfReportedError tore = class TypeOfReportedError, class Thread * pThread = 0x09b1d0b0, unsigned long dwThreadID = 0x300)+0xb52

091bd6a0 7bb317d3 mscorwks!DoFaultReport(struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac, class TypeOfReportedError tore = class TypeOfReportedError)+0x13d

091bd6c8 7bb361fb mscorwks!WatsonLastChance(class Thread * pThread = 0x09b1d0b0, struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac, class TypeOfReportedError tore = class TypeOfReportedError)+0x51

091bd728 7bb36387 mscorwks!InternalUnhandledExceptionFilter_Worker(struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac)+0x179

091bd734 7bb7578c mscorwks!ThreadBaseExceptionAppDomainFilter(struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac, void * pvParam = 0x091bf0f4)+0x1a

091bd76c 7baf167a mscorwks!ThreadBaseRedirectingFilter(struct _EXCEPTION_POINTERS * pExceptionInfo = 0x091bd7ac, void * _pCallState = 0x00000002)+0x7b

091bd774 7b9d2540 mscorwks!ManagedThreadBase_DispatchOuter(struct ManagedThreadCallState * pCallState = 0x091bf0f4)+0x38

091bd788 7ba6bf39 mscorwks!_EH4_CallFilterFunc(void)+0x12

091bd7b0 77e6b6d1 mscorwks!_except_handler4(struct _EXCEPTION_RECORD * ExceptionRecord = <Memory access error>, struct _EXCEPTION_REGISTRATION_RECORD * EstablisherFrame = <Memory access error>, struct _CONTEXT * ContextRecord = <Memory access error>, void * DispatcherContext = <Memory access error>)+0x8e

091bd7d4 77e6b6a3 ntdll!ExecuteHandler2(void)+0x26

091bd87c 77e4ee57 ntdll!ExecuteHandler(void)+0x24

091bd87c 06b2d7f3 ntdll!KiUserExceptionDispatcher(void)+0xf

091bea00 06b2d796 mscorlib!System.Collections.Generic.LinkedList`1[[System.__Canon, mscorlib]].Find(<HRESULT 0x80004001>)+0x43

091bea38 06b2d6b6 mscorlib!System.Collections.Generic.LinkedList`1[[System.__Canon, mscorlib]].Contains(<HRESULT 0x80004001>)+0x6

 

 

The first thing we should do is to find the right exception context. As pointed out in this article, we should look for _EXCEPTION_POINTERS, and the frame mscorwks!DoFaultReport gives us that (and along with many other frames). Let’s use it.

 

0:022> dc 0x091bd7ac

091bd7ac  091bd894 091bd8e4 77e6b6d1 091bd894  ...........w....

091bd7bc  091beed4 091bd8e4 091bd870 091bdc64  ........p...d...

091bd7cc  77e6b6e5 091beed4 091bd87c 77e6b6a3  ...w....|......w

091bd7dc  091bd894 091beed4 091bd8e4 091bd870  ............p...

091bd7ec  7ba6beb4 00000000 091bd894 091beed4  ...{............

091bd7fc  77e6b648 091bd894 091beed4 091bd8e4  H..w............

091bd80c  091bd870 7ba6beb4 00000002 091bd894  p......{........

091bd81c  091be6e8 7bb83bd0 035db834 00000004  .....;.{4.].....

 

The highlighted DWORD is the exception context. Let’s move to it.

 

0:022> .cxr 091bd8e4

eax=00000000 ebx=03339044 ecx=033310a8 edx=00000000 esi=00000001 edi=03312bc0

eip=06b2d7f3 esp=091be9f0 ebp=091bea00 iopl=0         nv up ei ng nz ac pe cy

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010297

mscorlib!System.Collections.Generic.LinkedList`1[[System.__Canon, mscorlib]].Find(System.__Canon)+0x43:

06b2d7f3 8b5610          mov     edx,dword ptr [esi+10h] ds:002b:00000011=????????

 

So this is access invalid memory. This is very strange. LinkedList is pure managed code. I have never seen manage code accessing invalid memory.

 

Let’s look at the assembly code and see what happened.

 

0:022> !u 06b2d7f3

Normal JIT generated code

System.Collections.Generic.LinkedList`1[[System.__Canon, mscorlib]].Find(System.__Canon)

Begin 06b2d7b0, size 83

06b2d7b0 55              push    ebp

06b2d7b1 8bec            mov     ebp,esp

06b2d7b3 57              push    edi

06b2d7b4 56              push    esi

06b2d7b5 53              push    ebx

06b2d7b6 50              push    eax

06b2d7b7 8bf9            mov     edi,ecx

06b2d7b9 8bda            mov     ebx,edx

06b2d7bb 8b7704          mov     esi,dword ptr [edi+4]

06b2d7be 8b0f            mov     ecx,dword ptr [edi]

06b2d7c0 8b4120          mov     eax,dword ptr [ecx+20h]

06b2d7c3 8b10            mov     edx,dword ptr [eax]

06b2d7c5 83c208          add     edx,8

06b2d7c8 8b02            mov     eax,dword ptr [edx]

06b2d7ca 85c0            test    eax,eax