I'm developing an add-on for AutoCAD 2009. The project output is a class library. When I attempt to debug and load the class library, I get this "LoaderLock was detected message." I've been writing these add-ons for awhile and this is the first message of this type I've seen.
Where do I start trying to figure this out?
What is LoaderLock and why is it bothering me now?
LoaderLock was detected
Message: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.
I went to Debug -> Exceptions -> "Managed Debugging Assistants", found "LoaderLock" and unchecked the "Thrown" checkbox.
I can debug again but what did I do and why did I have to do it? Will this cause other problems for me?
The loader lock is a process-wide lock used by the system to synchronize access to loading DLL's into a process address space. Functions that load DLL's, free DLL's, query DLL info, etc., all acquire the loader lock. What typically impacts developers the most is that the loader lock is held while DllMain is running as well - this means that an OS lock that you aren't normally aware of can be held while running your code.
The loader lock can be viewed as being at a very low level in the lock-hierarchy. Code running under the loader lock during DllMain can be the cause of deadlocks. For instance, the CLR has its own set of internal locks which it could hold while loading DLL's. If you call managed code from within your DllMain, you could cause the CLR on your thread to acquire one of these locks while holding the loader lock. If the CLR on another thread had acquired that lock (causing the origin thread in DllMain to block) and then tried to load a DLL which would acquire the loader lock, your process would deadlock.
It sounds like the CLR is trying to preemptively detect running managed code under the loader lock. When you see the stack from this failure in the debugger, identify what is causing your managed code to be running from within a DllMain and remove it.
In my experience with AutoCAD, the LoaderLock warning can be safely ignored. It's not a sign of your code doing something wrong, but rather the warning is raised because of the way AutoCAD is loading and initializing your application.
This a bug in Visual Studio 2005. Read this article for more details: http://support.microsoft.com/kb/913996
Related
I'm looking at an application which hangs on Server 2016 but runs find on Server 2008 R2. I have traced it to a hang caused by deadlock when a particular DLL loads. Analysing the DLL (I don't have the source code) I can see that it violates the guideline here
Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either
directly or indirectly). This can cause a deadlock or a crash
Specifically it calls GetStringTypeW from DllMain.
I'm trying to understand how this function can cause deadlocks in DllMain.
Your DllMain function runs inside the loader lock, one of the few
times the OS lets you run code while one of its internal locks is
held. This means that you must be extra careful not to violate a lock
hierarchy in your DllMain; otherwise, you are asking for a deadlock. (Refer to "Another reason not to do anything scary in your DllMain: Inadvertent deadlock")
DllMain is called while the loader-lock is held, so if GetStringType acquires the loader lock, deadlock appears.
Significant restrictions are imposed on the functions that can be
called within DllMain. As such, DllMain is designed to perform minimal
initialization tasks, by using a small subset of the Microsoft®
Windows® API. You cannot call any function in DllMain that directly or
indirectly tries to acquire the loader lock. Otherwise, you will
introduce the possibility that your application deadlocks or crashes.
We can't see source code of GetStringType function. It is suggested to follow Dynamic-Link Library Best Practices.
Calling a function from DllMain that will (directly or indirectly) attempt to load another module is going to deadlock.
Module loading is serialized. The system uses a lock to ensure that at any given time no more than a single DLL entry point is entered. If you are calling LoadLibrary (directly or indirectly) from your DLL entry point, you are holding the loader lock. LoadLibrary will attempt to acquire the loader lock itself prior to calling into the module's entry point. So while your code is waiting for LoadLibrary to return, LoadLibrary is waiting for the lock you are holding. That's a deadlock.
This is likely the reason why calling GetStringType from DllMain deadlocks. Without access to source code I cannot verify this, though. You may be able to find evidence by enabling loader snaps.
Is there a way to create a crash dump file automatically on application crash (on Windows OS), same as if I could save with Visual Studio debugger attached? That is, I want to be able to debug my application in Visual Studio with automatically created crash dump files.
Update: Debug Diag 2.0 is now available. This release has native support for .NET dumps.
Yes it is possible using DebugDiag 1.2.
The Debug Diagnostic Tool (DebugDiag) is designed to assist in
troubleshooting issues such as hangs, slow performance, memory leaks
or fragmentation, and crashes in any user-mode process. The tool
includes additional debugging scripts focused on Internet Information
Services (IIS) applications, web data access components, COM+ and
related Microsoft technologies.
It even allows you to run crash/hang analysis on the dump and give you a nice report about the callstack and thread that are in sleep state (for hang dumps). It also allows you to take on the fly dumps too. The dumps taken by DebugDiag can be analyzed in DebugDiag, Visual Studio and WinDbg.
EDIT: The MSDN link to how to use DebugDiag is here.
Use SetUnhandledExceptionFilter to catch exceptions. And in this global exception handler, use MiniDumpWriteDump function to create a dump file.
There is a lot around exception handling this way, like you won't be able to catch all exceptions (exception from STL, pure-virtual function, abort C runtime call etc). Also, you may be in problem if more than one thread causes some exception. You either need to suspend all other running threads when you get exception in your global exception handler, or use some logic so that other threads won't interfere with your dump-generation code.
To handle all cases, you need to tweak around linker settings (like /EHsc flag), so that ALL exceptions can be handled by try-catch, enable debugging information even for release build so that .PDB is generated and you can get call stack. Use API hooking, so that C-runtime calls won't disable your global-exception handler and lot!
See these:
SetUnhandledExceptionFilter and the C/C++ Runtime Library
Own Crash Minidump with Call Stack
My only recommendation is you start with simpler approach, and don't bother about more complex scenarios. In short, just use SetUnhandledExceptionFilter, in a single threaded application, cause an Access Violation, and in global-exception-handler, use MinidumpWriteDump to create a MINI dump (i.e. without memory-dump).
I have a crashdump created by DrWatson, the exception code is 0x80000007 STATUS_WAKE_SYSTEM_DEBUGGER and the message is "{Kernel Debugger Awakened} The system debugger was awakened by an interrupt." (from here: 2.3.1 NTSTATUS values http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx )
I cannot find any documentation about it. What is its meaning?
A quick trip to Google brought up this forum post. Basically some DLL crashed inside DllMain and thus the loader lock was abandoned. The injected thread that the debugger creates then gets stuck during its DllMain(THREAD_ATTACH) call. After some time, the operating system uses a "wake debugger" approach and that is what the debugger ends up seeing instead of the original exception. Sounds plausible.
I'm trying to understand in a bit more detail how a OS loaderlock is used in relation to the loading and unloading of DLL's in Windows.
I understand that every loaded DLL get notified when a new thread is created/destroyed and or a new DLL is loaded/unloaded.
So does that mean that the DllMain function is run inside a lock and no other thread can access it while it is running, and if you were to create another thread in that function, you could hang the process or even the OS?
Is my understanding correct?
Is there some article somewhere that explain this?
A deadlock can happen when two threads try to acquire two locks in different sequence.
Thread A gets lock A and then tries to get lock B
Meanwhile thread B gets lock B and then tries to get lock A
A thread that's running DllMain has already acquired an implicit O/S lock: therefore they (Microsoft) reckon that it may be unsafe for that thread to try to acquire any other, second lock (e.g. because a different thread might already own that lock and be currently blocked on the implicit O/S lock).
that is correct.
Any such execution is illegal because
it can lead to deadlocks and to use of
DLLs before they have been initialized
by the operating system's loader.
More information can be found here: LoaderLock MDA (MSDN Website)
I have a customer who is getting a 100% reproduceable crash that I can't replicate in my program compiled in Visual Studio 2005. I sent them a debug build of my program and kept all the PDB and DLL files handy. They sent me the minidump file, but when I open it I get:
"Unhandled exception at 0x00000000 in MiniDump.dmp: 0xC0000005: Access violation reading location 0x00000000."
Then the call stack shows only "0x00000000()" and the disassembly shows me a dump of the memory at 0x0. I've set up the symbol server, loaded my PDB symbols, etc. But I can't see any way of knowing which of the many DLLs actually caused the jump to null. This is a large project with many dependencies, and some of them are binaries that I don't have the source or PDBs for, as I am using an API as a 3rd party.
So how on earth is this minidump useful? How do I see which DLL caused the crash? I've never really used minidumps for debugging before, but all the tutorials I have read seem to at least display a function name or something else that gives you a clue in the call stack. I just get the one line pointing to null.
I also tried using "Depends" to see if there was some DLL dependency that was unresolved; however on my three test machines with various Windows OS's, I seem to get three different sets of OS DLL dependencies (and yet can't replicate the crash); so this doesn't seem a particularly reliable method either to diagnose the problem.
What other methods are available to determine the cause of this problem? Is there some way to step back one instruction to see which DLL jumped to null?
Well it looks like the answer in this instance was "Use WinDbg instead of Visual Studio for debugging minidumps". I couldn't get any useful info out of VS, but WinDbg gave me a wealth of info on the chain of function calls that led to the crash.
In this instance it still didn't help solve my problem, as all of the functions were in the 3rd party library I am using, so it looks like the only definitive answer to my specific problem is to use log files to trace the state of my application that leads to the crash.
I guess if anyone else sees a similar problem with an unhelpful call stack when debugging a minidump, the best practice is to open it with WinDgb rather than Visual Studio. Seems odd that the best tool for the job is the free Microsoft product, not the commerical one.
The other lesson here is probably "any program that uses a third party library needs to write a log file".
The whole idea behind all 'simple' ways of post mortem debugging is the capture of a stack trace. If your application overwrites the stack there is no way for such analysis. Only very sophisticated methods, that record the whole program execution in dedicated hardware could help.
The way to go in such a case are log files. Spread some log statements very wide around the area where the fault occurs and transmit that version to the customer. After the crash you'll see the last log statement in your log file. Add more log statements between that point and the next log statement that has not been recorded in the log file, ship that version again. Repeat until you found the line causing the problem.
I wrote a two part article about this at ddj.com:
About Log Files Part 1
About Log Files Part 2
Just an observation, but the the stack is getting truncated or over-written, might this be a simple case of using an uninitialised field, or perhaps a buffer overrun ?
That might be fairly easy to locate.
Have you tried to set WinDbg on a customer's computer and use it as a default debugger for any application that causes a crash? You just need to add pdb files to the folder where your application resides. When a crush happens WinDbg starts and you can try to get call stack.
Possibly you already know this, but here are some points about minidump debugging:
1. You need to have exactly the same executables and PDB files, as on the client computer where minidump was created, and they should be placed exactly in the same directories. Just rebuilding the same version doesn't help.
2. Debugger must be connected to MS Symbols server.
3. When debugger starts, it prints process loading log in the Output window. Generally, all libraries should be successfully loaded with debug information. Libraries without debug information are loaded as well, but "no debug info" is printed. Learn this log - it can give you some information.
If executable stack contains frames from a library without debug information, it may be not shown. This happens, for example, if your code is running as third-party library callback.
Try to create minidump on your own computer, by adding some code which creates unhandled exception, and debug it immediately. Does this work? Compare loading log in successful and unsuccessful debugging sessions.
You may have called null function pointer. Current executing function information is needed to show call stack information. Force set instruction pointer to start of any simple function, then you'll see call stack information again.
void SimpleFunc()
{ // <- set next statement here
}