feat(cordis): 完成阶段5访问介导与诊断

This commit is contained in:
2026-08-17 01:09:32 +08:00
parent de5ab449cd
commit 8738e633ee
35 changed files with 1540 additions and 50 deletions
@@ -15,6 +15,7 @@ namespace ShrinkModFramework
public string Path;
public string Revision;
public Assembly Assembly;
public long LoadedBytes;
}
internal sealed class RevisionState
@@ -27,10 +28,11 @@ namespace ShrinkModFramework
private static readonly Dictionary<string, ExternalAssemblyRevision> CurrentAssemblyRevisions =
new(StringComparer.OrdinalIgnoreCase);
private static readonly HashSet<Assembly> ExternalAssemblyHistory = new();
private static readonly Dictionary<Assembly, ExternalAssemblyRevision> ExternalAssemblyHistory = new();
private static readonly Dictionary<string, string> KnownAssemblyFiles = new(StringComparer.OrdinalIgnoreCase);
private static readonly object ResolveLock = new();
private static bool _resolveRegistered;
private static int _lastWarnedResidentCount;
public static IReadOnlyList<Assembly> LoadExternalAssemblies(ShrinkModFrameworkSettings settings, bool verboseLogging)
{
@@ -82,18 +84,22 @@ namespace ShrinkModFramework
}
var pdbPath = Path.ChangeExtension(dllPath, ".pdb");
var assembly = File.Exists(pdbPath)
? Assembly.Load(bytes, File.ReadAllBytes(pdbPath))
byte[] pdbBytes = null;
if (File.Exists(pdbPath))
pdbBytes = File.ReadAllBytes(pdbPath);
var assembly = pdbBytes != null
? Assembly.Load(bytes, pdbBytes)
: Assembly.Load(bytes);
var loadedRevision = new ExternalAssemblyRevision
{
Path = dllPath,
Revision = revision,
Assembly = assembly
Assembly = assembly,
LoadedBytes = bytes.LongLength + (pdbBytes?.LongLength ?? 0L)
};
CurrentAssemblyRevisions[dllPath] = loadedRevision;
ExternalAssemblyHistory.Add(assembly);
ExternalAssemblyHistory[assembly] = loadedRevision;
if (verboseLogging)
Debug.Log($"[ShrinkModFramework] 已加载外部模组程序集:{assembly.GetName().Name} ({revision[..12]})");
@@ -104,6 +110,8 @@ namespace ShrinkModFramework
}
}
WarnIfResidentLimitExceeded(settings);
foreach (var missingPath in CurrentAssemblyRevisions.Keys
.Where(path => !presentPaths.Contains(path))
.ToArray())
@@ -124,7 +132,30 @@ namespace ShrinkModFramework
}
internal static bool IsExternalAssembly(Assembly assembly) =>
assembly != null && ExternalAssemblyHistory.Contains(assembly);
assembly != null && ExternalAssemblyHistory.ContainsKey(assembly);
internal static ShrinkExternalAssemblyDiagnostic CaptureDiagnostic(int softLimit)
{
var normalizedLimit = Math.Max(1, softLimit);
var currentAssemblies = new HashSet<Assembly>(
CurrentAssemblyRevisions.Values.Select(item => item.Assembly));
var revisions = ExternalAssemblyHistory.Values
.OrderBy(item => item.Path, StringComparer.OrdinalIgnoreCase)
.ThenBy(item => item.Revision, StringComparer.Ordinal)
.Select(item => new ShrinkExternalAssemblyRevisionDiagnostic(
item.Path,
item.Revision,
item.Assembly.GetName().Name ?? string.Empty,
item.LoadedBytes,
currentAssemblies.Contains(item.Assembly)))
.ToArray();
return new ShrinkExternalAssemblyDiagnostic(
CurrentAssemblyRevisions.Count,
revisions,
revisions.Sum(item => item.LoadedBytes),
normalizedLimit);
}
internal static RevisionState CaptureState()
{
@@ -193,6 +224,21 @@ namespace ShrinkModFramework
{
ResetForTesting();
ExternalAssemblyHistory.Clear();
_lastWarnedResidentCount = 0;
}
private static void WarnIfResidentLimitExceeded(ShrinkModFrameworkSettings settings)
{
var limit = Math.Max(1, settings != null ? settings.externalAssemblyRevisionSoftLimit : 16);
var residentCount = ExternalAssemblyHistory.Count;
if (residentCount < limit || residentCount == _lastWarnedResidentCount)
return;
_lastWarnedResidentCount = residentCount;
var bytes = ExternalAssemblyHistory.Values.Sum(item => item.LoadedBytes);
Debug.LogWarning(
$"[ShrinkModFramework] 外部 DLL 常驻 revision 已达到 {residentCount} 个(载入文件约 {bytes} bytes" +
$"软阈值 {limit})。Mono 无法卸载这些 Assembly;建议在维护窗口执行 Domain Reload 或重启进程。");
}
private static string ComputeSha256(byte[] bytes)