Analyzing crash dumps can be complicated. Although Visual Studio supports viewing managed crash dumps, you often have to resort to more specialized tools like the SOS debugging extensions or WinDbg. In today’s post, Lee Culver, software developer on the .NET Runtime team, will introduce you to a new managed library that allows you to automate inspection tasks and access even more debugging information. –Immo
Today are we excited to announce the beta release of the Microsoft.Diagnostics.Runtime component (called ClrMD for short) through the NuGet Package Manager.
ClrMD is a set of advanced APIs for programmatically inspecting a crash dump of a .NET program much in the same way as the SOS Debugging Extensions (SOS). It allows you to write automated crash analysis for your applications and automate many common debugger tasks.
We understand that this API won’t be for everyone — hopefully debugging .NET crash dumps is a rare thing for you. However, our .NET Runtime team has had so much success automating complex diagnostics tasks with this API that we wanted to release it publicly.
One last, quick note, before we get started: The ClrMD managed library is a wrapper around CLR internal-only debugging APIs. Although those internal-only APIs are very useful for diagnostics, we do not support them as a public, documented release because they are incredibly difficult to use and tightly coupled with other implementation details of the CLR. ClrMD addresses this problem by providing an easy-to-use managed wrapper around these low-level debugging APIs.
Getting Started
Let’s dive right into an example of what can be done with ClrMD. The API was designed to be as discoverable as possible, so IntelliSense will be your primary guide. As an initial example, we will show you how to collect a set of heap statistics (objects, sizes, and counts) similar to what SOS reports when you run the command !dumpheap –stat.
The “root” object of ClrMD to start with is the DataTarget class. A DataTarget represents either a crash dump or a live .NET process. In this example, we will attach to a live process that has the name “HelloWorld.exe” with a timeout of 5 seconds to attempt to attach:
int pid = Process.GetProcessesByName("HelloWorld")[0].Id; using (DataTarget dataTarget = DataTarget.AttachToProcess(pid, 5000)) { string dacLocation = dataTarget.ClrVersions[0].TryGetDacLocation(); ClrRuntime runtime = dataTarget.CreateRuntime(dacLocation); // ... }
You may wonder what the TryGetDacLocation method does. The CLR is a managed runtime, which means that it provides additional abstractions, such as garbage collection and JIT compilation, over what the operating system provides. The bookkeeping for those abstractions is done via internal data structures that live within the process. Those data structures are specific to the CPU architecture and the CLR version. In order to decouple debuggers from the internal data structures, the CLR provides a data access component (DAC), implemented in mscordacwks.dll. The DAC has a standardized interface and is used by the debugger to obtain information about the state of those abstractions, for example, the managed heap. It is essential to use the DAC that matches the CLR version and the architecture of the process or crash dump you want to inspect. For a given CLR version, the TryGetDacLocation method tries to find a matching DAC on the same machine. If you need to inspect a process for which you do not have a matching CLR installed, you have another option: you can copy the DAC from a machine that has that version of the CLR installed. In that case, you provide the path to the alternate mscordacwks.dll to the CreateRuntime method manually. You can read more about the DAC on MSDN.
Note that the DAC is a native DLL and must be loaded into the program that uses ClrMD. If the dump or the live process is 32-bit, you must use the 32-bit version of the DAC, which, in turn, means that your inspection program needs to be 32-bit as well. The same is true for 64-bit processes. Make sure that your program’s platform matches what you are debugging.
Analyzing the Heap
Once you have attached to the process, you can use the runtime object to inspect the contents of the GC heap:
ClrHeap heap = runtime.GetHeap(); foreach (ulong obj in heap.EnumerateObjects()) { ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); Console.WriteLine("{0,12:X} {1,8:n0} {2}", obj, size, type.Name); }
This produces output similar to the following:
23B1D30 36 System.Security.PermissionSet 23B1D54 20 Microsoft.Win32.SafeHandles.SafePEFileHandle 23B1D68 32 System.Security.Policy.PEFileEvidenceFactory 23B1D88 40 System.Security.Policy.Evidence
However, the original goal was to output a set of heap statistics. Using the data above, you can use a LINQ query to group the heap by type and sort by total object size:
var stats = from o in heap.EnumerateObjects() let t = heap.GetObjectType(o) group o by t into g let size = g.Sum(o => (uint)g.Key.GetSize(o)) orderby size select new { Name = g.Key.Name, Size = size, Count = g.Count() }; foreach (var item in stats) Console.WriteLine("{0,12:n0} {1,12:n0} {2}", item.Size, item.Count, item.Name);
This will output data like the following — a collection of statistics about what objects are taking up the most space on the GC heap for your process:
564 11 System.Int32[] 616 2 System.Globalization.CultureData 680 18 System.String[] 728 26 System.RuntimeType 790 7 System.Char[] 5,788 165 System.String 17,252 6 System.Object[]
ClrMD Features and Functionality
Of course, there’s a lot more to this API than simply printing out heap statistics. You can also walk every managed thread in a process or crash dump and print out a managed callstack. For example, this code prints the managed stack trace for each thread, similar to what the SOS !clrstack command would report (and similar to the output in the Visual Studio stack trace window):
foreach (ClrThread thread in runtime.Threads) { Console.WriteLine("ThreadID: {0:X}", thread.OSThreadId); Console.WriteLine("Callstack:"); foreach (ClrStackFrame frame in thread.StackTrace) Console.WriteLine("{0,12:X} {1,12:X} {2}", frame.InstructionPointer, frame.StackPointer, frame.DisplayString); Console.WriteLine(); }
This produces output similar to the following:
ThreadID: 2D90 Callstack: 0 90F168 HelperMethodFrame 660E3365 90F1DC System.Threading.Thread.Sleep(Int32) C70089 90F1E0 HelloWorld.Program.Main(System.String[]) 0 90F36C GCFrame
Each ClrThread object also contains a CurrentException property, which may be null, but if not, contains the last thrown exception on this thread. This exception object contains the full stack trace, message, and type of the exception thrown.
ClrMD also provides the following features:
- Gets general information about the GC heap:
- Whether the GC is workstation or server
- The number of logical GC heaps in the process
- Data about the bounds of GC segments
- Walks the CLR’s handle table (similar to !gchandles in SOS).
- Walks the application domains in the process and identifies which modules are loaded into them.
- Enumerates threads, callstacks of those threads, the last thrown exception on threads, etc.
- Enumerates the object roots of the process (as the GC sees them for our mark-and-sweep algorithm).
- Walks the fields of objects.
- Gets data about the various heaps that the .NET runtime uses to see where memory is going in the process (see ClrRuntime.EnumerateMemoryRegions in the ClrMD package).
All of this functionality can generally be found on the ClrRuntime or the ClrHeap objects, as seen above. IntelliSense can help you explore the various properties and functions when you install the ClrMD package. In addition, you can also use the attached sample code.
Please use the comments under this post to let us know if you have any feedback!
To answer my own comment – it seems you can self-debug. Seems to work – would be nice to know if it is supported.
BTW, there is an error in the sample – I assume it should be:
// If we don't have the dac installed, we will use the long-name dac in the same folder.
if (string.IsNullOrEmpty(dacLocation)) // ***** without '!' ? ******
dacLocation = version.DacInfo.FileName;
@Hrvoje, yep that's an error, sorry about that, it should not have the '!'. 🙁
The self-debug case is not a supported scenario because there's not a sensible way to make it work. For example, you can attempt to inspect your own heap with it, the ClrMD api itself will be allocating objects, which will trigger GCs, which in turn will cause your heap walk to fail when a GC rearranges the heap as you were walking it.
This should always be used to inspect another process (or crash dump).
How can I call this to dump all objects under a given class or namespace from code? I want to dump all objects under a given dialogue window when that window is supposedly closed and deallocated. This would greatly help in finding objects that have not been garbage collected.
I also want to do a memory snapshot of allocated ojbects by full type name and object id and then at a later time compare that to the current memory snapshot. I'd want only the objects in the second snapshot that do not exist in the first one to be printed. This helps for code that should clean up all of its resources when it exits.
I've used this in C++ in the past to put in automatic debug only checks for memory leaks (e.g., snapshot, call method A, snapshot, compare snapshots, if snapshots differ, break in debug mode).
@Tom
ClrType instances have a .Name which contains the namespace as well as the typename. You can use this to separate out the heap by namespace (though I suppose it would be better to provide a Namespace property instead of making you parse out the name…that's not currently in the API).
As to your second question about doing heap diffs, the main obstacle to doing this is that the GC relocates objects, and an object can still be alive between two snapshots, but the object got moved…so you don't know the instance is the same. To solve this, we use a heuristic which basically does a diff of the type statistics (100 Foo objects in snapshot 1, 110 Foo objects in snapshot 2, 10 Foo objects difference).
In fact, perfview's memory diagnostics already does this today: http://www.microsoft.com/…/details.aspx
(Memory diagnostic in PerfView is actually built on top of ClrMD.)
is there any limitation on the kind of process we can attach to? e.g not runnning as admin and more
cause i have tried to attach to one of my own processes and got exception
Could not attach to pid 514, HRESULT: 0xd00000bb
hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
i write a dll in project ATL with a function witch function inclusive a input and output parameter is that byte* type
STDMETHODIMP CMSDllServer::sum22(BYTE* aa,SHORT len)
{
return S_OK;
}
in use during from this function in a windowes application c# doesnot exist no problem and array values returned to truth
//out put
1,2,3,4,5
but
the same function in a webservice returned to only first index array and exist very big problem
byte[] Packet = new byte[5];
//out put
1,0,0,0,0
help me pleaseeeeeeeeeeeeeeeeeeeeeeeeeeeee
thanx
Seems my original comment never made it through.
Our use case seems to be a bit simpler than this dll was intended to be used. We produce mission critical software (high availability and fault tolerance required, low installation count) which sometimes presents a challenge to monitor and diagnose.
I view ClrMD as a possibility to implement a miniature adtools-like component that would always be present with the deployment of our software to cover these use cases:
monitor the target process and take a "memory object count" if memory usage > X
monitor the target process (GUI application) and take a "call stack dump" if the UI thread is blocked for more than 250ms (there was a MS visual studio extension that did the same thing, but produced minidumps – there was a nice video about it on channel9, but I can't seem to find it. Basically, microsoft was using it to detect usability problems in production).
However, I would also like to see the following:
integrated ability to take usable minidump and full dumps of attached process, something like clrdump (google it, first result – seems the comments delete any of my posts that contains a link without warning…)
ability to take a snapshot of native call stacks – 95% of our threads are .net, but there are a couple of native threads we integrate with through C++/CLI and we would really like to see both native and managed callstacks. There should be an easy way to convert the call stack to a readable format with the matching symbols (symbol server support). I know this may not be your primary use case, but it would complete our needed feature set.
Hi,
Nice ! 🙂
It's really nice to see this kind of things released publicly since all analysis needs (automated or not) are not covered by SOS or even by other extensions like Steve Johnson's SOSEX (http://www.stevestechspot.com/SOSEXUpdatendashReadyForNET45.aspx).
Gaël
@Hrvoje
You can do this using the IDebug* interfaces provided by DbgEng (full details too long for a comment here, but you can search for IDebugControl::GetStackTrace). ClrMD provides a (mostly) complete wrapper around those interfaces:
using Microsoft.Diagnostics.Runtime;
…
IDebugControl control = (IDebugControl)dataTarget.DebuggerInterface;
control.GetStackTrace(…);
IDebugClient client = (IDebugClient)dataTarget.DebuggerInterface;
client.WriteDumpFile(…);
You can use the IDebug* interfaces to fully control your process (go, step, etc), but again…that's more detail than I can put in this comment. The API is still in beta too. =)
@li-raz
I should have pointed out in the post, attaching to a process requires debugger privileges (in this case, that almost certainly means running the program as administrator).
You do not need admin privileges to load a crash dump.
Is the clrCmd .net library on a path to be fully supported and part of .NET 4.x or later? We can use beta version code in our development environment but not in our production environment given the production environment has many different long running server processes.
Here is the quote from the blog post:
The ClrMD managed library is a wrapper around CLR internal-only debugging APIs. Although those internal-only APIs are very useful for diagnostics, we do not support them as a public, documented release because they are incredibly difficult to use and tightly coupled with other implementation details of the CLR. ClrMD addresses this problem by providing an easy-to-use managed wrapper around these low-level debugging APIs.
@Tom: The current version of ClrMD is a pre-release, which means its license doesn’t allow usage in production environments. Once we turn ClrMD into a stable release it will allow production use.
Is there any plan to provide one for .Net 3.5 framework (CLR 2)?
Thanks for this Immo. I've actually spent the past few months writing exactly the same library using the CorDebug, MetaData and Symbol server APIs. I've actually got it all up and running, although I've targeted crash dumps (full and partial) as opposed to a live process.
Do you have any thoughts on ClrMD vs CorDebug? Obviously CorDebug is geared towards debugging and happens to support crash dumps as an extra bonus, while ClrMD is focussed on analysis and not debugging.
It's great that ClrMD takes away all of the grunt labour I've had to do in order to get CorDebug up and running for crash dumps, like implementing ICorDebugDataTarget (great fun for partial dumps!) and parsing MetaData binary signatures which is a truely painful experience.
It's awesome to have an officially supported way of doing this now, but any thoughts on whether CorDebug will continue to support crash dumps? And any future plans for ClrMD, is this just the beginning ofClrMD ? Really excited by this so would love to hear anything you have to say 🙂
ps – is the team hiring? I've got experience in CorDebug, MetaData and the Symbol Server API's 😀
@kevinlo2: It's on our list but we don't have a timeline yet.
This is great! I can see this going pretty big in just overall debugging.
I still seem to be getting the "unable to attached to pid" error, though. Of course, I've only tried on the calc, notepad, and issexpress processes.
Keep up the awesome work!
Finally got past an issue attaching to a running process! I'm not seeing any stack traces in the managed threads though.
This is awesome stuff – I can't remember the last time I installed a framework and had so many "are you serious???" moments.
Very cool – keep up the good work!
This is awesome! Looking forward for further samples.
I was looking to automate IIS app pool process memory dump / analysis and make it as self-service tool on our shared web farm. This sounds very promising for that.
Very cool stuff – looking forward to reading more about this.
I'd be interested to see a way of grabbing more information about the objects or even the whole objects themselves.
I'm seeing odd behavior when trying to use retrieve native call stacks. When I try to use IDebugControl::GetStackTrace, it appears to not return all the frames in the stack.
For example, if I retrieve the stack for thread zero in a sample dump file via IDebugControl::GetStackTrace in ClrMD, I get the following frames:
############ Frames for thread 0 [B128] ############
[0]: 7C82845C ntdll!KiFastSystemCallRet
[1]: 77E61C8D kernel32!WaitForSingleObject
[2]: 5A364662 w3dt!IPM_MESSAGE_PIPE::operator=
[3]: 0100187C w3wp
[4]: 01001A27 w3wp
[5]: 77E6F23B kernel32!ProcessIdToSessionId
If I look at the stack in Visual Studio or WinDbg, or if I retrieve it using IDebugControl::GetStackTrace in a WinDbg extension, I get the following:
############ Frames for thread 0 [B128] ############
[0]: 7C82845C ntdll!KiFastSystemCallRet
[1]: 7C827B79 ntdll!ZwWaitForSingleObject <<<< Skipped by ClrMD
[2]: 77E61D1E kernel32!WaitForSingleObjectEx <<<< Skipped by ClrMD
[3]: 77E61C8D kernel32!WaitForSingleObject
[4]: 5A364662 w3dt!WP_CONTEXT::RunMainThreadLoop
[5]: 5A366E3F w3dt!UlAtqStartListen <<<< Skipped by ClrMD
[6]: 5A3AF42D w3core!W3_SERVER::StartListen <<<< Skipped by ClrMD
[7]: 5A3BC335 w3core!UlW3Start <<<< Skipped by ClrMD
[8]: 0100187C w3wp!wmain
[9]: 01001A27 w3wp!wmainCRTStartup
[10]: 77E6F23B kernel32!BaseProcessStart
Note that all the frames listed by ClrMD exist in the true call stack, but it has skipped the indicated frames in between. Have you seen this behavior before?
The code I'm using looks like this:
DataTarget dataTarget = DataTarget.LoadCrashDump(@"c:scratchmydump.dmp");
// Not actually using the ClrRuntime in this snippet, but my actual code is doing this,
// so I'm including it in case it matters.
ClrInfo clrInfo = dataTarget.ClrVersions[0];
string dacLocation = clrInfo.TryGetDacLocation();
ClrRuntime clrRuntime = dataTarget.CreateRuntime(dacLocation) ;
// Retrieve the required debugging interfaces
IDebugControl4 control = (IDebugControl4) dataTarget;
IDebugSystemObjects3 sysObjs = (IDebugSystemObjects3) dataTarget;
sysObjs.SetCurrentThreadId(0);
DEBUG_STACK_FRAME[] frames = new DEBUG_STACK_FRAME[100];
uint frameCount = 0;
control.GetStackTrace(0, 0, 0, frames, 100, out frameCount);
// Note: after the call, frameCount is set to 6, instead of 11, like it should be
As you may have noticed from my output above, I'm also having some issues with symbol resolution via IDebugSymbols::GetNameByOffset where occasionally I'm getting incorrect or incomplete names; but, I'm hoping that this is just something I have wrong in the code that sets up the symbol path.
Slight typo in the above code sample. These two lines:
IDebugControl4 control = (IDebugControl4) dataTarget;
IDebugSystemObjects3 sysObjs = (IDebugSystemObjects3) dataTarget;
should have read
IDebugControl4 control = (IDebugControl4) dataTarget.DebuggerInterface;
IDebugSystemObjects3 sysObjs = (IDebugSystemObjects3) dataTarget.DebuggerInterface;
Great… Love this. One question. I have a simple program that allocates a List<> and adds instances of a class called PayLoad (see code below). After the first time through the while loop, one Payload instance is allocated and added to the List<PayLoad>. When I dump all the heap allocated objects from my test program's namespace I see:
Name: Total Size: Total Number:
TestProgram.PayLoad[] 96 2
TestProgram.PayLoad 24 1
Does anybody have any idea where the TestProgram.PayLoad[] instances come from? I'm trying to measure memory usage of my namespace object and this seems to skew it a bit. Thanks.
class Program
{
static void Main(string[] args)
{
List<PayLoad> payloadList = new List<PayLoad>();
while (true)
{
Console.WriteLine("Adding new payload");
payloadList.Add(new PayLoad());
Console.ReadLine();
}
}
}
class PayLoad
{
public int a;
public int b;
}
I tried to run the sample to analyze .dmp file which was taken from a program running on the same machine as the sample.
but i keep getting the following exception when trying to create the runtime object:
Message: Failure loading DAC: CreateDacInstance failed 0x80131c30
at Microsoft.Diagnostics.Runtime.Desktop.DacLibrary.Init(String dll)
at Microsoft.Diagnostics.Runtime.Desktop.DacLibrary..ctor(DbgEngTarget dataTarget, String dll)
at Microsoft.Diagnostics.Runtime.DbgEngTarget.CreateRuntime(String dacFilename)
at DumpFetch.App..ctor()
at DumpFetch.App.Main()
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Any ideas?
I'm analyzing 11 GB dmp file and top size type is "Free". What does "Free" means?
Free is a pseudo object that represents a free space (a hole) in the GC heap. They exist when the GC happens but decides not to compact.
Having large amounts of fee space is not necessarily bad (especially if they are a few big chunks), since these to get reused. When these free areas get too numerous/small, the GC will compact. Large objects (> 85K), are treated differently, and placed in their own region of memory and currently are not ever compacted (however in V4.5.1 we have added the ability to do this explicitly)
Is any way to find using ClrMD that some object is reachable from roots or not reachable from roots?
Yes. In fact, this is how PerfView uses ClrMD, but you have to calculate the object graph manually to find that information. There's not a simple function call to do this.
The functions which you use to do most of the work are:
ClrHeap.GetObjectType – To get the type of the object in the root.
ClrType.EnumerateRefsOfObject – To enumerate what objects the current object references.
With these functions you build the full heap graph…and any object not in the graph is considered dead. Any object you do reach is considered live.
(There are false positives and negatives from this approach, but they are rare. We unfortunately aren't 100% accurate in the root reporting for all versions of the runtime.)
Thank you, Lee Culver!
It helped me find cause of memory leak.
@Alexey: Can you provide me your code for calculating the heap graph? (Mail: toni.wenzel@googlemail.com)
I'm currently investigating a memory leak of our own application. It would be interesting how you managed this.
THX!
What the ClrRoot.Address used for? Points this to the same as ClrRoot.Object?
How can I receive following informations:
The object which is pinned by the root (I guess ClrRoot.Object)
I would like to know which object prevent which object from being collected (GC relocated).
What is the ClrType.GetFieldForOffset() "inner" parameter used for?
Great work!
But when I tried it out on a production dump I don’t get the same answer from the sample code as I got from WinDbg for the command "!dump heap –stat".
Example for strings
The sample code returns: 16 318 082 199 815 System.String
But in WinDbg I get : 21004872 191564 System.String
I miss 3 Mb of string objects?!
And when I trying to search for “ClaimsPrincipal” objects it’s possible to locate 46 of them with WinDbg but none with ClrMD?
Is it something I have missed?
Wow, I wish I knew about this weeks ago. This is a fantastic little library and it's making my deep investigations into many millions of objects much more bearable. Thanks kindly 🙂
So, I might be doing something wrong, but I'm having a hard time working with array fields while trying to browse an object. I'm currently using ClrInstanceField.GetFieldValue(parentObjectAddress) which I was hoping would give me the address of the Array, since that is what it does for objects. Instead it seems to be returning something else? It also seems like it thinks the array in every generic List<T> is an Object[] but this would imply that Generic collections don't prevent boxing, which I know to be false.
I'm also curious that when I use GetFieldValue on an Object type, the address it gives back seems to work fine with field.Type, but heap.GetObjectType for the same address returns null or sometimes strange values. I only stumbled this way when trying to account for polymorphism while browsing referenced objects deeper than my starting point, since I figured ClrInstanceField.Type would reflect the general type definition, not necessarily the actual type stored in a particular instance (e.g. field definition type: IEnumerable, instance reference: ArrayList).
Maybe you could provide some more sample code now that this has been in the wild for a while? Without documentation it has been hard to infer how one might dig deep into an object graph, especially regarding fields that aren't primitive values (structs/arrays/objects/etc.). There are very few deep resources online, though the ScriptCs module and a few other blogs have been helpful, I am encountering plenty of things that require a lot of trial and error, which is costing me more time than I was hoping this tool would save me. I still think the knowledge will benefit me in the long run, but a followup would be nice. Maybe some of those internal automated diagnostics might be safe to share with the public?
On a positive note, I've had great success combining some work I did automating against dbgeng and SOS with this library and they appear to be complimenting each other well (since I already have some SOS parsing implemented).
I love this tool, but would also like to use an app written with it against some dumps containing unmanaged code from old legacy apps to automate large numbers of repetitive actions. I'm thinking the tool can do it because DebugDiag v2 uses ClrMD and it can open unmanaged dumps. But I can't figure out how to load the required unmanaged-compatible clr10sos from ClrMD-based code. The code seems to required the FindDAC step and, of course, there are no CLR versions in the dump at all.
How can I get ClrMd to use the Clr10Sos and let me use the ReadMemory, thread-related, and other convenient debugging commands?
Thanks!
-Bob
I realize now that I didn't put my name with my question, but I've further detailed the question above on StackOverflow. Sadly, I don't think there are many people using this extensively yet, so I'm concerned by the fact that the question is already well below the average number of viewers for a new question. I'm posting the link here both for experts that might see this as well as others who might have the same question:
stackoverflow.com/…/how-to-properly-work-with-non-primitive-clrinstancefield-values-using-clrmd
Hi,
We need to parse dictionary of type <string, List<objects>> using ClrMD. Dictionary are being stored as System.Collections.Hashtable+bucket[] into memory as per our understanding. We have tried to parse dictionary of type<string, List<objects>> by enumerating internal Hashtable+bucket[] objects, but we aren’t successful. We are able to parse dictionary values(List<objects>>) as individual arrays[]. But we aren’t able to correlate these individual arrays[] belongs to which Keys. To know this, we need to parse dictionary of type <string, List<objects>>.
Can you please provide us pointers/direction on how to parse dictionary using ClrMD ? Sample of code will be helpful.
This is amazing ! This is going to help me automating the debugging of W3WP on certain situations, I can't explain how thrilled I am, previously I would have had to create a memory dump using procdump, then pull up windbg and start issuing commands to gather the desired info, all of this manually and error prone !
Now I can make automatically from my APP, with a LIVE PROCESS !!
We do a lot of dump analysis where I work and we started to use your library a lot, it's awesome. I recently made an extension library for ClrMD which allows to use ClrMD with LINQPad; being able to interactively navigate in the dump with ClrMD and LINQPad is great for finding unknown issue. When we spot a particular issue pattern, we take the code we did in LINQPad and put it in an analysis rule in DebugDiag.
The project is on GitHub if you want to take a look: github.com/…/ClrMD.Extensions
It would be great to hear your thoughts about it, does it fit with your vision about where ClrMD is evolving? My main concern is about my 'ClrObject' class which take care of most of the LINQPad integration, I saw that you created one in the DebugDiag API (which is also available in the ClrMD samples). Do you plan to include the ClrObject class from DebugDiag directly in ClrMD? Do you plan to change ClrMD in a way that I would not be able to create my own ClrObject class?
Thanks for your time,
Jeff Cyr
There is a memory leak when calling the dataTarget.CreateRuntime method. Where can I report this?
Hi, I have posted a question about finding root of object using CLR MD at stackoverflow.com/…/trying-to-find-object-roots-using-clr-md
Can you please provide any suggestion about it.
Hi, maybe someone would be interested, I wrote an application that expose CLRMD library via GUI: github.com/…/LiveProcessInspector
Posted a question at stackoverflow.com/…/finding-a-types-instance-data-in-net-heap. Can you help?
How do I start a process using the debugger API?
I have been able to use CLRMD to attach to live proceesses that are already running and monitor them for exceptions and other debug events.
Now I want to be able to launch an app under the debugger API so I can capture debug events that occur during the startup sequence.
CLRMD does not expose this functionality so in a C++/CLI dll I wrote code that uses the unmanaged API to launch the process and then
use DataTarget::CreateFromDebuggerInterface() to be able to use the CLRMD functionality.
(error handling omitted):
PDEBUG_CLIENT debugClient = nullptr;
hr = DebugCreate( __uuidof( ::IDebugClient ), (void**)&debugClient );
System::Object^ obj = Marshal::GetObjectForIUnknown( ( System::IntPtr )debugClient );
Interop::IDebugClient^ pdc = (Interop::IDebugClient^)obj
Interop::DEBUG_CREATE_PROCESS createFlags = (Interop::DEBUG_CREATE_PROCESS)1; // this value seems to work, don't know why; other values failed
Interop::DEBUG_ATTACH attachFlags = Interop::DEBUG_ATTACH::INVASIVE_RESUME_PROCESS;
hr = pdc->CreateProcessAndAttach( 0, exePath, createFlags, 0, attachFlags );
Interop::IDebugControl^ idc = ( Interop::IDebugControl^ )pdc;
hr = idc->WaitForEvent( Interop::DEBUG_WAIT::DEFAULT, 1000 );
After calling WaitForEvent() the debugger should be attached and the process should be running. If I call idc->WaitForEvent( Interop::DEBUG_WAIT::DEFAULT, 1000 ); in a loop
it will display the UI and run normally.
However, when I try to connect the session to CLRMD I get errors.
DataTarget^ target = DataTarget::CreateFromDebuggerInterface( pdc );
This always throws an Microsoft::Diagnostics::Runtime::ClrDiagnosticsException
"Failed to get proessor type, HRESULT: 8000ffff""
at Microsoft.Diagnostics.Runtime.DbgEngDataReader.GetArchitecture() in c:workprojectsProjectsProcessMonitorsamplesdotnetsamplesMicrosoft.Diagnostics.RuntimeCLRMDClrMemDiagdbgengdatatarget.cs:line 164
at Microsoft.Diagnostics.Runtime.DataTargetImpl..ctor(IDataReader dataReader, IDebugClient client) in c:workprojectsProjectsProcessMonitorsamplesdotnetsamplesMicrosoft.Diagnostics.RuntimeCLRMDClrMemDiagdatatargetimpl.cs:line 30
at Microsoft.Diagnostics.Runtime.DataTarget.CreateFromDebuggerInterface(IDebugClient client) in c:workprojectsProjectsProcessMonitorsamplesdotnetsamplesMicrosoft.Diagnostics.RuntimeCLRMDClrMemDiagpublic.cs:line 2797
Inside the exception object it reports that the _HResult=0x81250002
I tried calling DataTarget::CreateFromDebuggerInterface() both before and after the target is connected and before and after WaitForEvent() is called – all fail the same way.
Any help getting this to work is appreciated.
Thanks.
Exception thrown: 'Microsoft.Diagnostics.Runtime.ClrDiagnosticsException' in Microsoft.Diagnostics.Runtime.dll
Additional information: This runtime is not initialized and contains no data.
Any ideas?