Created
April 10, 2024 12:55
-
-
Save smourier/989a8a02a6a132a1fdc3f06b72a8e3f4 to your computer and use it in GitHub Desktop.
C# implementation of IMallocSpy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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