Skip to content

Instantly share code, notes, and snippets.

@smourier
Created April 10, 2024 12:55
Show Gist options
  • Save smourier/989a8a02a6a132a1fdc3f06b72a8e3f4 to your computer and use it in GitHub Desktop.
Save smourier/989a8a02a6a132a1fdc3f06b72a8e3f4 to your computer and use it in GitHub Desktop.
C# implementation of IMallocSpy
public class ConsoleMallocSpy : MallocSpy
{
protected override void Log(object message, [CallerMemberName] string methodName = null) => Console.WriteLine(methodName + ": " + message);
private readonly ConcurrentDictionary<IntPtr, IntPtr> _blocks = new();
private IntPtr _allocRequest;
protected override IntPtr PreAlloc(IntPtr cbRequest)
{
_allocRequest = cbRequest;
return base.PreAlloc(cbRequest);
}
protected override IntPtr PostAlloc(IntPtr pActual)
{
var ret = base.PostAlloc(pActual);
_blocks[ret] = _allocRequest;
return ret;
}
protected override IntPtr PreFree(IntPtr pRequest, bool fSpyed)
{
if (!fSpyed)
return base.PreFree(pRequest, fSpyed);
if (_blocks.TryRemove(pRequest, out var size))
{
Console.WriteLine(ToHexaDump(pRequest, size.ToInt32()));
}
return base.PreFree(pRequest, fSpyed);
}
}
public abstract class MallocSpy : IDisposable, MallocSpy.IMallocSpy
{
private bool _disposedValue;
protected MallocSpy()
{
CoRegisterMallocSpy(this);
}
protected abstract void Log(object message, [CallerMemberName] string methodName = null);
protected virtual string ToString(IntPtr ptr)
{
if (IntPtr.Size == 8)
return "0x" + ptr.ToInt64().ToString("X16");
return "0x" + ptr.ToInt32().ToString("X8");
}
IntPtr IMallocSpy.PreAlloc(IntPtr cbRequest) => PreAlloc(cbRequest);
protected virtual IntPtr PreAlloc(IntPtr cbRequest)
{
Log("cbRequest: " + ToString(cbRequest));
return cbRequest;
}
IntPtr IMallocSpy.PostAlloc(IntPtr pActual) => PostAlloc(pActual);
protected virtual IntPtr PostAlloc(IntPtr pActual)
{
Log("pActual: " + ToString(pActual));
return pActual;
}
IntPtr IMallocSpy.PreFree(IntPtr pRequest, bool fSpyed) => PreFree(pRequest, fSpyed);
protected virtual IntPtr PreFree(IntPtr pRequest, bool fSpyed)
{
Log("pRequest: " + ToString(pRequest) + " fSpyed: " + fSpyed);
return pRequest;
}
void IMallocSpy.PostFree(bool fSpyed) => PostFree(fSpyed);
protected virtual void PostFree(bool fSpyed)
{
Log("fSpyed: " + fSpyed);
}
IntPtr IMallocSpy.PreRealloc(IntPtr pRequest, IntPtr cbRequest, out IntPtr ppNewRequest, bool fSpyed) => PreRealloc(pRequest, cbRequest, out ppNewRequest, fSpyed);
protected virtual IntPtr PreRealloc(IntPtr pRequest, IntPtr cbRequest, out IntPtr ppNewRequest, bool fSpyed)
{
Log("pRequest: " + ToString(pRequest) + " cbRequest " + ToString(cbRequest) + " fSpyed: " + fSpyed);
ppNewRequest = pRequest;
return cbRequest;
}
IntPtr IMallocSpy.PostRealloc(IntPtr pActual, bool fSpyed) => PostRealloc(pActual, fSpyed);
protected virtual IntPtr PostRealloc(IntPtr pActual, bool fSpyed)
{
Log("pActual: " + ToString(pActual) + " fSpyed: " + fSpyed);
return pActual;
}
IntPtr IMallocSpy.PreGetSize(IntPtr pRequest, bool fSpyed) => PreGetSize(pRequest, fSpyed);
protected virtual IntPtr PreGetSize(IntPtr pRequest, bool fSpyed)
{
Log("pRequest: " + ToString(pRequest) + " fSpyed: " + fSpyed);
return pRequest;
}
IntPtr IMallocSpy.PostGetSize(IntPtr pActual, bool fSpyed) => PostGetSize(pActual, fSpyed);
protected virtual IntPtr PostGetSize(IntPtr pActual, bool fSpyed)
{
Log("pActual: " + ToString(pActual) + " fSpyed: " + fSpyed);
return pActual;
}
IntPtr IMallocSpy.PreDidAlloc(IntPtr pRequest, bool fSpyed) => PreDidAlloc(pRequest, fSpyed);
protected virtual IntPtr PreDidAlloc(IntPtr pRequest, bool fSpyed)
{
Log("pRequest: " + ToString(pRequest) + " fSpyed: " + fSpyed);
return pRequest;
}
int IMallocSpy.PostDidAlloc(IntPtr pRequest, bool fSpyed, int fActual) => PostDidAlloc(pRequest, fSpyed, fActual);
protected virtual int PostDidAlloc(IntPtr pRequest, bool fSpyed, int fActual)
{
Log("pRequest: " + ToString(pRequest) + " fSpyed: " + fSpyed + " fActual: " + fActual);
return fActual;
}
void IMallocSpy.PreHeapMinimize() => PreHeapMinimize();
protected virtual void PreHeapMinimize()
{
Log(string.Empty);
}
void IMallocSpy.PostHeapMinimize() => PostHeapMinimize();
protected virtual void PostHeapMinimize()
{
Log(string.Empty);
}
[DllImport("Ole32")]
private static extern int CoRegisterMallocSpy(IMallocSpy pMallocSpy);
[DllImport("Ole32")]
private static extern int CoRevokeMallocSpy();
[ComImport, Guid("0000001d-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMallocSpy
{
[PreserveSig]
IntPtr PreAlloc(IntPtr cbRequest);
[PreserveSig]
IntPtr PostAlloc(IntPtr pActual);
[PreserveSig]
IntPtr PreFree(IntPtr pRequest, bool fSpyed);
[PreserveSig]
void PostFree(bool fSpyed);
[PreserveSig]
IntPtr PreRealloc(IntPtr pRequest, IntPtr cbRequest, out IntPtr ppNewRequest, bool fSpyed);
[PreserveSig]
IntPtr PostRealloc(IntPtr pActual, bool fSpyed);
[PreserveSig]
IntPtr PreGetSize(IntPtr pRequest, bool fSpyed);
[PreserveSig]
IntPtr PostGetSize(IntPtr pActual, bool fSpyed);
[PreserveSig]
IntPtr PreDidAlloc(IntPtr pRequest, bool fSpyed);
[PreserveSig]
int PostDidAlloc(IntPtr pRequest, bool fSpyed, int fActual);
[PreserveSig]
void PreHeapMinimize();
[PreserveSig]
void PostHeapMinimize();
}
protected static string ToHexaDump(string text, Encoding encoding = null)
{
if (text == null)
return null;
if (encoding == null)
{
encoding = Encoding.Unicode;
}
return ToHexaDump(encoding.GetBytes(text));
}
protected static string ToHexaDump(byte[] bytes, string prefix = null)
{
if (bytes == null)
return null;
return ToHexaDump(bytes, 0, bytes.Length, prefix, true);
}
protected static string ToHexaDump(IntPtr ptr, int count) => ToHexaDump(ptr, 0, count);
protected static string ToHexaDump(IntPtr ptr, int offset, int count, string prefix = null, bool addHeader = true)
{
if (ptr == IntPtr.Zero)
return null;
var bytes = new byte[count];
Marshal.Copy(ptr, bytes, offset, count);
return ToHexaDump(bytes, 0, count, prefix, addHeader);
}
protected static string ToHexaDump(byte[] bytes, int count) => ToHexaDump(bytes, 0, count);
protected static string ToHexaDump(byte[] bytes, int offset, int count, string prefix = null, bool addHeader = true)
{
if (bytes == null)
return null;
if (offset < 0)
{
offset = 0;
}
if (count < 0)
{
count = bytes.Length;
}
if ((offset + count) > bytes.Length)
{
count = bytes.Length - offset;
}
var sb = new StringBuilder();
if (addHeader)
{
sb.Append(prefix);
//+ 0 1 2 3 4 5 6 7
//+ 01234567890123456789012345678901234567890123456789012345678901234567890123456789
sb.AppendLine("Offset 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF");
sb.AppendLine("-------- ----------------------------------------------- ----------------");
}
for (var i = 0; i < count; i += 16)
{
sb.Append(prefix);
sb.AppendFormat("{0:X8} ", i + offset);
int j;
for (j = 0; (j < 16) && ((i + j) < count); j++)
{
sb.AppendFormat("{0:X2} ", bytes[i + j + offset]);
}
sb.Append(" ");
if (j < 16)
{
sb.Append(new string(' ', 3 * (16 - j)));
}
for (j = 0; j < 16 && (i + j) < count; j++)
{
var b = bytes[i + j + offset];
if (b > 31 && b < 128)
{
sb.Append((char)b);
}
else
{
sb.Append('.');
}
}
sb.AppendLine();
}
return sb.ToString();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// dispose managed state (managed objects)
CoRevokeMallocSpy();
}
// free unmanaged resources (unmanaged objects) and override finalizer
// set large fields to null
_disposedValue = true;
}
}
// // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~MalllocSpy()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment