chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Tools.Profiler
|
||||
{
|
||||
internal static class MemorySnapshotOps
|
||||
{
|
||||
private static readonly Type MemoryProfilerType =
|
||||
Type.GetType("Unity.Profiling.Memory.MemoryProfiler, UnityEngine.CoreModule")
|
||||
?? Type.GetType("UnityEngine.Profiling.Memory.Experimental.MemoryProfiler, UnityEngine.CoreModule")
|
||||
?? Type.GetType("Unity.MemoryProfiler.MemoryProfiler, Unity.MemoryProfiler.Editor");
|
||||
|
||||
private static bool HasPackage => MemoryProfilerType != null;
|
||||
|
||||
internal static async Task<object> TakeSnapshotAsync(JObject @params)
|
||||
{
|
||||
if (!HasPackage)
|
||||
return PackageMissingError();
|
||||
|
||||
var p = new ToolParams(@params);
|
||||
string snapshotPath = p.Get("snapshot_path");
|
||||
|
||||
if (string.IsNullOrEmpty(snapshotPath))
|
||||
{
|
||||
string dir = Path.Combine(Application.temporaryCachePath, "MemoryCaptures");
|
||||
Directory.CreateDirectory(dir);
|
||||
snapshotPath = Path.Combine(dir, $"snapshot_{DateTime.Now:yyyyMMdd_HHmmss}.snap");
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
try
|
||||
{
|
||||
var debugScreenCaptureType = Type.GetType(
|
||||
"Unity.Profiling.DebugScreenCapture, UnityEngine.CoreModule")
|
||||
?? Type.GetType("UnityEngine.Profiling.Experimental.DebugScreenCapture, UnityEngine.CoreModule")
|
||||
?? Type.GetType("Unity.Profiling.Memory.Experimental.DebugScreenCapture, Unity.MemoryProfiler.Editor");
|
||||
var captureFlagsType = Type.GetType(
|
||||
"Unity.Profiling.Memory.CaptureFlags, UnityEngine.CoreModule")
|
||||
?? Type.GetType("UnityEngine.Profiling.Memory.Experimental.CaptureFlags, UnityEngine.CoreModule");
|
||||
|
||||
System.Reflection.MethodInfo takeMethod = null;
|
||||
|
||||
if (debugScreenCaptureType != null && captureFlagsType != null)
|
||||
{
|
||||
var screenshotCallbackType = typeof(Action<,,>).MakeGenericType(
|
||||
typeof(string), typeof(bool), debugScreenCaptureType);
|
||||
takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot",
|
||||
new[] { typeof(string), typeof(Action<string, bool>), screenshotCallbackType, captureFlagsType });
|
||||
}
|
||||
|
||||
if (takeMethod == null && captureFlagsType != null)
|
||||
{
|
||||
takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot",
|
||||
new[] { typeof(string), typeof(Action<string, bool>), captureFlagsType });
|
||||
}
|
||||
|
||||
if (takeMethod == null && debugScreenCaptureType != null)
|
||||
{
|
||||
var screenshotCallbackType = typeof(Action<,,>).MakeGenericType(
|
||||
typeof(string), typeof(bool), debugScreenCaptureType);
|
||||
takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot",
|
||||
new[] { typeof(string), typeof(Action<string, bool>), screenshotCallbackType, typeof(uint) });
|
||||
}
|
||||
|
||||
if (takeMethod == null)
|
||||
{
|
||||
takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot",
|
||||
new[] { typeof(string), typeof(Action<string, bool>) });
|
||||
}
|
||||
|
||||
if (takeMethod == null)
|
||||
return new ErrorResponse("Could not find TakeSnapshot method on MemoryProfiler. API may have changed.");
|
||||
|
||||
Action<string, bool> callback = (path, result) =>
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
var fi = new FileInfo(path);
|
||||
tcs.TrySetResult(new SuccessResponse("Memory snapshot captured.", new
|
||||
{
|
||||
path,
|
||||
size_bytes = fi.Exists ? fi.Length : 0,
|
||||
size_mb = fi.Exists ? Math.Round(fi.Length / (1024.0 * 1024.0), 2) : 0,
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.TrySetResult(new ErrorResponse($"Snapshot capture failed for path: {path}"));
|
||||
}
|
||||
};
|
||||
|
||||
var takeMethodParams = takeMethod.GetParameters();
|
||||
int paramCount = takeMethodParams.Length;
|
||||
if (paramCount == 4 && takeMethodParams[3].ParameterType == captureFlagsType)
|
||||
takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, Enum.ToObject(captureFlagsType, 0) });
|
||||
else if (paramCount == 3 && takeMethodParams[2].ParameterType == captureFlagsType)
|
||||
takeMethod.Invoke(null, new object[] { snapshotPath, callback, Enum.ToObject(captureFlagsType, 0) });
|
||||
else if (paramCount == 4 && takeMethodParams[3].ParameterType == typeof(uint))
|
||||
takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, 0u });
|
||||
else if (paramCount == 2)
|
||||
takeMethod.Invoke(null, new object[] { snapshotPath, callback });
|
||||
else
|
||||
return new ErrorResponse($"TakeSnapshot has unexpected {paramCount} parameters. API may have changed.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse($"Failed to take snapshot: {ex.Message}");
|
||||
}
|
||||
|
||||
var timeout = Task.Delay(TimeSpan.FromSeconds(30));
|
||||
var completed = await Task.WhenAny(tcs.Task, timeout);
|
||||
if (completed == timeout)
|
||||
return new ErrorResponse("Snapshot timed out after 30 seconds.");
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
internal static object ListSnapshots(JObject @params)
|
||||
{
|
||||
var p = new ToolParams(@params);
|
||||
string searchPath = p.Get("search_path");
|
||||
|
||||
var dirs = new List<string>();
|
||||
if (!string.IsNullOrEmpty(searchPath))
|
||||
{
|
||||
dirs.Add(searchPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirs.Add(Path.Combine(Application.temporaryCachePath, "MemoryCaptures"));
|
||||
dirs.Add(Path.Combine(Application.dataPath, "..", "MemoryCaptures"));
|
||||
}
|
||||
|
||||
var snapshots = new List<object>();
|
||||
foreach (string dir in dirs)
|
||||
{
|
||||
if (!Directory.Exists(dir)) continue;
|
||||
foreach (string file in Directory.GetFiles(dir, "*.snap"))
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
snapshots.Add(new
|
||||
{
|
||||
path = fi.FullName,
|
||||
size_bytes = fi.Length,
|
||||
size_mb = Math.Round(fi.Length / (1024.0 * 1024.0), 2),
|
||||
created = fi.CreationTimeUtc.ToString("o"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new SuccessResponse($"Found {snapshots.Count} snapshot(s).", new
|
||||
{
|
||||
snapshots,
|
||||
searched_dirs = dirs,
|
||||
});
|
||||
}
|
||||
|
||||
internal static object CompareSnapshots(JObject @params)
|
||||
{
|
||||
var p = new ToolParams(@params);
|
||||
var pathAResult = p.GetRequired("snapshot_a");
|
||||
if (!pathAResult.IsSuccess)
|
||||
return new ErrorResponse(pathAResult.ErrorMessage);
|
||||
|
||||
var pathBResult = p.GetRequired("snapshot_b");
|
||||
if (!pathBResult.IsSuccess)
|
||||
return new ErrorResponse(pathBResult.ErrorMessage);
|
||||
|
||||
string pathA = pathAResult.Value;
|
||||
string pathB = pathBResult.Value;
|
||||
|
||||
if (!File.Exists(pathA))
|
||||
return new ErrorResponse($"Snapshot file not found: {pathA}");
|
||||
if (!File.Exists(pathB))
|
||||
return new ErrorResponse($"Snapshot file not found: {pathB}");
|
||||
|
||||
var fiA = new FileInfo(pathA);
|
||||
var fiB = new FileInfo(pathB);
|
||||
|
||||
return new SuccessResponse("Snapshot comparison (file-level metadata).", new
|
||||
{
|
||||
snapshot_a = new
|
||||
{
|
||||
path = fiA.FullName,
|
||||
size_bytes = fiA.Length,
|
||||
size_mb = Math.Round(fiA.Length / (1024.0 * 1024.0), 2),
|
||||
created = fiA.CreationTimeUtc.ToString("o"),
|
||||
},
|
||||
snapshot_b = new
|
||||
{
|
||||
path = fiB.FullName,
|
||||
size_bytes = fiB.Length,
|
||||
size_mb = Math.Round(fiB.Length / (1024.0 * 1024.0), 2),
|
||||
created = fiB.CreationTimeUtc.ToString("o"),
|
||||
},
|
||||
delta = new
|
||||
{
|
||||
size_delta_bytes = fiB.Length - fiA.Length,
|
||||
size_delta_mb = Math.Round((fiB.Length - fiA.Length) / (1024.0 * 1024.0), 2),
|
||||
time_delta_seconds = (fiB.CreationTimeUtc - fiA.CreationTimeUtc).TotalSeconds,
|
||||
},
|
||||
note = "For detailed object-level comparison, open both snapshots in the Memory Profiler window.",
|
||||
});
|
||||
}
|
||||
|
||||
private static ErrorResponse PackageMissingError()
|
||||
{
|
||||
return new ErrorResponse(
|
||||
"Package com.unity.memoryprofiler is required. "
|
||||
+ "Install via Package Manager or: manage_packages action=add_package package_id=com.unity.memoryprofiler");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user