Skip to content

Commit

Permalink
Use ClrMDExports (from Kevin Gosse) to allow lldb/WinDBG extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Christophe Nasarre committed Feb 24, 2019
1 parent f48122e commit ae0de2b
Show file tree
Hide file tree
Showing 18 changed files with 209 additions and 156 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The few "debugging extensions" that have been created at Criteo to help post-mor
- as a [stand alone tool](./Documentation/ClrMDStudio.md) to load a .NET application memory dump and start automatic thread, thread pool, tasks and timer analysis.
[zip](./binaries/ClrMDStudio-1.5.1_x64.zip)
- as a [WinDBG extension](./Documentation/gsose.md) to get the same level of details plus more commands such as getting a method signature based on its address.
[zip](./binaries/gsose-1.5.1_x64.zip)
[zip](./binaries/gsose-1.5.2_x64.zip)

More analyzers and commands will be added as needed.

Expand Down
Binary file removed binaries/gsose-1.5.1_x64.zip
Binary file not shown.
Binary file added binaries/gsose-1.5.2_x64.zip
Binary file not shown.
19 changes: 8 additions & 11 deletions src/gsose/ConcurrentDictionary.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using ClrMDExports;
using ClrMDStudio;
using Microsoft.Diagnostics.Runtime;
using RGiesecke.DllExport;
Expand All @@ -12,21 +13,17 @@ public partial class DebuggerExtensions
[DllExport("dcd")]
public static void dcd(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnDumpConcurrentDictionary(client, args);
DebuggingContext.Execute(client, args, OnDumpConcurrentDictionary);
}

[DllExport("DumpConcurrentDictionary")]
public static void DumpConcurrentDictionary(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnDumpConcurrentDictionary(client, args);
DebuggingContext.Execute(client, args, OnDumpConcurrentDictionary);
}

public static void OnDumpConcurrentDictionary(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
public static void OnDumpConcurrentDictionary(ClrRuntime runtime, string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

// parse the command argument
if (args.StartsWith("0x"))
{
Expand All @@ -44,12 +41,12 @@ public static void OnDumpConcurrentDictionary(IntPtr client, [MarshalAs(Unmanage
return;
}

ShowConcurrentDictionary(address);
ShowConcurrentDictionary(runtime, address);
}

private static void ShowConcurrentDictionary(ulong address)
private static void ShowConcurrentDictionary(ClrRuntime runtime, ulong address)
{
var heap = Runtime.Heap;
var heap = runtime.Heap;
ClrType t = heap.GetObjectType(address);
if (t == null)
{
Expand All @@ -60,7 +57,7 @@ private static void ShowConcurrentDictionary(ulong address)
try
{
// different implementations between .NET Core and .NET Framework
var helper = new ClrMDHelper(Runtime);
var helper = new ClrMDHelper(runtime);

var cd = heap.GetProxy(address);
Console.WriteLine($"{cd.GetClrType().Name}");
Expand Down
19 changes: 8 additions & 11 deletions src/gsose/ConcurrentQueue.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using ClrMDExports;
using ClrMDStudio;
using Microsoft.Diagnostics.Runtime;
using RGiesecke.DllExport;
Expand All @@ -12,21 +13,17 @@ public partial class DebuggerExtensions
[DllExport("dcq")]
public static void dcq(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnDumpConcurrentQueue(client, args);
DebuggingContext.Execute(client, args, OnDumpConcurrentQueue);
}

[DllExport("DumpConcurrentQueue")]
public static void DumpConcurrentQueue(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnDumpConcurrentQueue(client, args);
DebuggingContext.Execute(client, args, OnDumpConcurrentQueue);
}

public static void OnDumpConcurrentQueue(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
public static void OnDumpConcurrentQueue(ClrRuntime runtime, string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

// parse the command argument
if (string.IsNullOrEmpty(args))
{
Expand Down Expand Up @@ -60,12 +57,12 @@ public static void OnDumpConcurrentQueue(IntPtr client, [MarshalAs(UnmanagedType
{
showItemType = arguments.Any(arg => arg == "-t");
}
ShowConcurrentQueue(reference, showItemType);
ShowConcurrentQueue(runtime, reference, showItemType);
}

private static void ShowConcurrentQueue(ulong address, bool showItemType)
private static void ShowConcurrentQueue(ClrRuntime runtime, ulong address, bool showItemType)
{
var heap = Runtime.Heap;
var heap = runtime.Heap;
ClrType t = heap.GetObjectType(address);
if (t == null)
{
Expand All @@ -76,7 +73,7 @@ private static void ShowConcurrentQueue(ulong address, bool showItemType)
try
{
// different implementations between .NET Core and .NET Framework
var helper = new ClrMDHelper(Runtime);
var helper = new ClrMDHelper(runtime);
var cq = heap.GetProxy(address);
int count = 0;
foreach (var item in ClrMDHelper.EnumerateConcurrentQueue(cq, helper.IsNetCore()))
Expand Down
31 changes: 16 additions & 15 deletions src/gsose/DumpHeap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using ClrMDExports;

namespace gsose
{
Expand All @@ -11,27 +12,27 @@ public partial class DebuggerExtensions
[DllExport("heapstat")]
public static void HeapStat(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

DebuggingContext.Execute(client, args, OnHeapStat);
}

public static void OnHeapStat(ClrRuntime runtime, string args)
{
// Use ClrMD as normal, but ONLY cache the copy of ClrRuntime (this.Runtime). All other
// types you get out of ClrMD (such as ClrHeap, ClrTypes, etc) should be discarded and
// reobtained every run.
ClrHeap heap = Runtime.Heap;
ClrHeap heap = runtime.Heap;

var stats = from obj in heap.EnumerateObjectAddresses()
let t = heap.GetObjectType(obj)
group obj by t into g
let size = g.Sum(p => (uint)g.Key.GetSize(p))
orderby size
select new
{
Size = size,
Count = g.Count(),
Name = g.Key.Name
};
let t = heap.GetObjectType(obj)
group obj by t into g
let size = g.Sum(p => (uint)g.Key.GetSize(p))
orderby size
select new
{
Size = size,
Count = g.Count(),
Name = g.Key.Name
};

// Console.WriteLine now writes to the debugger.
foreach (var entry in stats)
Expand Down
16 changes: 7 additions & 9 deletions src/gsose/GarbageCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using ClrMDExports;
using Microsoft.Diagnostics.Runtime;

namespace gsose
{
Expand All @@ -13,26 +15,22 @@ public partial class DebuggerExtensions
[DllExport("gci")]
public static void gci(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnGCInfo(client, args);
DebuggingContext.Execute(client, args, OnGCInfo);
}
[DllExport("gcinfo")]
public static void gcinfo(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnGCInfo(client, args);
DebuggingContext.Execute(client, args, OnGCInfo);
}
[DllExport("GCInfo")]
public static void GCInfo(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnGCInfo(client, args);
DebuggingContext.Execute(client, args, OnGCInfo);
}

private static void OnGCInfo(IntPtr client, string args)
private static void OnGCInfo(ClrRuntime runtime, string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

var helper = new ClrMDHelper(Runtime);
var helper = new ClrMDHelper(runtime);
var segments = helper.ComputeGCSegments();

ListGenerations(segments);
Expand Down
12 changes: 5 additions & 7 deletions src/gsose/Help.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Runtime.InteropServices;
using ClrMDExports;
using Microsoft.Diagnostics.Runtime;
using RGiesecke.DllExport;

namespace gsose
Expand All @@ -9,12 +11,12 @@ public partial class DebuggerExtensions
[DllExport("Help")]
public static void Help(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnHelp(client, args);
DebuggingContext.Execute(client, args, OnHelp);
}
[DllExport("help")]
public static void help(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnHelp(client, args);
DebuggingContext.Execute(client, args, OnHelp);
}

const string _help =
Expand Down Expand Up @@ -251,12 +253,8 @@ public static void help(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string a
" 5 - 0x000001D10DF5B480 | NetCoreConsoleApp.InstanceInConcurrentDataStructures\r\n" +
"\r\n";
//
private static void OnHelp(IntPtr client, string args)
private static void OnHelp(ClrRuntime runtime, string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

string command = args;
if (args != null)
command = args.ToLower();
Expand Down
66 changes: 66 additions & 0 deletions src/gsose/Init.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ClrMDExports
{
internal class Init
{
[RGiesecke.DllExport.DllExport("DebugExtensionInitialize")]
public static int DebugExtensionInitialize(ref uint version, ref uint flags)
{
var extensionFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

try
{
var file = Path.Combine(extensionFolder, "ClrMDExports.dll");

var assemblyName = AssemblyName.GetAssemblyName(file);

ForceAssemblyLoad(assemblyName);
}
catch (Exception ex)
{
Console.WriteLine("Could not load ClrMDExports: " + ex);
}

InitializeDebuggingContext();

// Set the extension version to 1, which expects exports with this signature:
// void _stdcall function(IDebugClient *client, const char *args)
version = DEBUG_EXTENSION_VERSION(1, 0);
flags = 0;
return 0;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void InitializeDebuggingContext()
{
Private.Initialization.IsWinDbg = true;
}

private static void ForceAssemblyLoad(AssemblyName assemblyName)
{
var codeBase = assemblyName.CodeBase;

if (codeBase.StartsWith("file://"))
{
codeBase = codeBase.Substring(8).Replace('/', '\\');
}

ResolveEventHandler assemblyResolve = (sender, args) => args.Name == assemblyName.FullName ? Assembly.LoadFrom(codeBase) : null;

AppDomain.CurrentDomain.AssemblyResolve += assemblyResolve;

Assembly.Load(assemblyName.FullName);

AppDomain.CurrentDomain.AssemblyResolve -= assemblyResolve;
}

static uint DEBUG_EXTENSION_VERSION(uint major, uint minor)
{
return ((((major) & 0xffff) << 16) | ((minor) & 0xffff));
}
}
}
17 changes: 7 additions & 10 deletions src/gsose/PinnedObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using ClrMDExports;

namespace gsose
{
Expand All @@ -12,37 +13,33 @@ public partial class DebuggerExtensions
[DllExport("po")]
public static void po(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnPinnedObjects(client, args);
DebuggingContext.Execute(client, args, OnPinnedObjects);
}
[DllExport("pinnedobjects")]
public static void pinnedobjects(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnPinnedObjects(client, args);
DebuggingContext.Execute(client, args, OnPinnedObjects);
}
[DllExport("PinnedObjects")]
public static void PinnedObjects(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
OnPinnedObjects(client, args);
DebuggingContext.Execute(client, args, OnPinnedObjects);
}

private static void OnPinnedObjects(IntPtr client, string args)
private static void OnPinnedObjects(ClrRuntime runtime, string args)
{
// Must be the first thing in our extension.
if (!InitApi(client))
return;

int minInstanceCount;
if (!int.TryParse(args, out minInstanceCount))
minInstanceCount = 1;

// Use ClrMD as normal, but ONLY cache the copy of ClrRuntime (this.Runtime). All other
// types you get out of ClrMD (such as ClrHeap, ClrTypes, etc) should be discarded and
// reobtained every run.
ClrHeap heap = Runtime.Heap;
ClrHeap heap = runtime.Heap;

// Console.WriteLine now writes to the debugger.

ClrMDHelper helper = new ClrMDHelper(Runtime);
ClrMDHelper helper = new ClrMDHelper(runtime);

try
{
Expand Down
10 changes: 8 additions & 2 deletions src/gsose/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.5.1.0")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyVersion("1.5.2.0")]
[assembly: AssemblyFileVersion("1.5.2.0")]
//
// version 1.5.2
// -----------------------------------------
// - use ClrMDExports (from Kevin Gosse) to allow lldb/WinDBG extension
//
//
// version 1.5.1
// -----------------------------------------
// - bug fix for .NET Core CLR 2.0/2.1 --> 2.2 structure changes for thread pool work item callback
// - use UnmanagedExports.Repack nuget package (from Kevin Gosse)
//
//
// version 1.5
// -----------------------------------------
// - add dcq (Dump concurrent queue) command
Expand Down
Loading

0 comments on commit ae0de2b

Please sign in to comment.