XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Reflection;
using System.Runtime.Loader;
using Mono.Cecil;
using Mono.Cecil.Cil;

internal static class PeriodicSchedulerLogPatchChecks
{
    public static void Run(AssemblyDefinition before, AssemblyDefinition after, Action<string, bool> check)
    {
        var original = Target(before);
        var patched = Target(after);
        var oldInstructions = original.Body.Instructions;
        var newInstructions = patched.Body.Instructions;
        check("PeriodicSchedulerExactlyLogAndTerminalTimesliceInstructionsChanged", oldInstructions.Count == newInstructions.Count &&
            original.Body.CodeSize + 8 == patched.Body.CodeSize && original.Body.MaxStackSize == patched.Body.MaxStackSize &&
            original.Body.InitLocals == patched.Body.InitLocals &&
            original.Body.Variables.Select(local => local.VariableType.FullName).SequenceEqual(patched.Body.Variables.Select(local => local.VariableType.FullName)) &&
            original.Body.ExceptionHandlers.Count == 0 && patched.Body.ExceptionHandlers.Count == 0 &&
            oldInstructions.Where((instruction, index) => index != PeriodicSchedulerLogPatch.ChangedInstructionIndex && index != PeriodicSchedulerLogPatch.TimesliceInstructionIndex)
                .All(instruction => InstructionShape(instruction, original) == InstructionShape(newInstructions[oldInstructions.IndexOf(instruction)], patched)) &&
            oldInstructions[PeriodicSchedulerLogPatch.ChangedInstructionIndex].GetSize() == newInstructions[PeriodicSchedulerLogPatch.ChangedInstructionIndex].GetSize());
        var originalReachable = Reachable(original);
        var patchedReachable = Reachable(patched);
        check("PeriodicSchedulerOriginalSerialPrintsReachablePositiveControl", originalReachable.Count(IsSerialWrite) == 5);
        check("PeriodicSchedulerAllSerialPrintsUnreachable", patchedReachable.All(instruction => !IsSerialWrite(instruction)));
        check("PeriodicSchedulerTickCounterIncrementRemainsReachable", newInstructions.Take(4).All(patchedReachable.Contains) &&
            newInstructions[3].OpCode == OpCodes.Stsfld && newInstructions[3].Operand is FieldReference counter && counter.Name == "s_onTickLogCount");
        check("PeriodicSchedulerRuntimePassBoostAndGlobalAccountingRemainReachable", new[]
            { "set_TotalRuntime", "set_Pass", "set_IsBoosted", "UpdateGlobalPass" }.All(name =>
                patchedReachable.Any(instruction => instruction.Operand is MethodReference call && call.Name == name)));
        check("PeriodicSchedulerOtherMethodsUnchanged", original.DeclaringType.Methods.Where(method => method.Name != "OnTick").All(method =>
            Il(method) == Il(patched.DeclaringType.Methods.Single(candidate => candidate.FullName == method.FullName))));

        string prior = Il(patched);
        check("PeriodicSchedulerRepeatedApplyVerifiesWithoutMutation", PeriodicSchedulerLogPatch.Apply(after) && prior == Il(patched));
        using (var roundtrip = Clone(after))
            check("PeriodicSchedulerRoundtripIsIdempotent", PeriodicSchedulerLogPatch.Apply(roundtrip));
        using (var logOnly = Clone(after))
        {
            var terminal = Target(logOnly).Body.Instructions[PeriodicSchedulerLogPatch.TimesliceInstructionIndex];
            terminal.OpCode = OpCodes.Ldloc_2; terminal.Operand = null;
            check("PeriodicSchedulerVerifiedLogOnlyMigrationCompletesTimeslice", !PeriodicSchedulerLogPatch.Apply(logOnly) &&
                PeriodicSchedulerLogPatch.Apply(logOnly) && Il(Target(logOnly)) == Il(patched));
        }

        CheckRejected("PeriodicSchedulerChangedModuloRejected", before, assembly =>
            Target(assembly).Body.Instructions.Single(instruction => instruction.OpCode == OpCodes.Ldc_I4_S && Equals(instruction.Operand, (sbyte)100)).Operand = (sbyte)101);
        CheckRejected("PeriodicSchedulerChangedAccountingRejected", before, assembly =>
            Target(assembly).Body.Instructions[1].OpCode = OpCodes.Ldc_I4_2);
        CheckRejected("PeriodicSchedulerChangedAccountingDivisorRejected", before, assembly =>
            Target(assembly).Body.Instructions.Single(instruction => instruction.OpCode == OpCodes.Ldc_I4 && Equals(instruction.Operand, 10_000_000)).Operand = 1_000_000);
        CheckRejected("PeriodicSchedulerChangedTailThresholdRejected", after, assembly =>
            Target(assembly).Body.Instructions[PeriodicSchedulerLogPatch.TimesliceInstructionIndex].Operand = 2_000_000L);
        CheckRejected("PeriodicSchedulerChangedTailComparisonRejected", after, assembly =>
            Target(assembly).Body.Instructions[104].OpCode = OpCodes.Clt);
        CheckRejected("PeriodicSchedulerChangedDebugMessageRejected", before, assembly =>
            Target(assembly).Body.Instructions.Single(instruction => instruction.OpCode == OpCodes.Ldstr && Equals(instruction.Operand, " runQ=")).Operand = " changed=");
        CheckRejected("PeriodicSchedulerUnknownVersionRejected", before, assembly => assembly.Name.Version = new Version(3, 0, 95, 0));
        CheckRejected("PeriodicSchedulerChangedPatchedDestinationRejected", after, assembly =>
            Target(assembly).Body.Instructions[PeriodicSchedulerLogPatch.ChangedInstructionIndex].Operand = Target(assembly).Body.Instructions[83]);
        CheckRejected("PeriodicSchedulerPartialOrUnknownPatchRejected", after, assembly =>
        {
            var entry = Target(assembly).Body.Instructions[PeriodicSchedulerLogPatch.ChangedInstructionIndex];
            entry.OpCode = OpCodes.Nop; entry.Operand = null;
        });

        // Run the real original/patched callback IL on CoreCLR. Only referenced
        // data types and method tokens are replaced with these pure host doubles;
        // no scheduler, interrupt or hardware operations run on the host.
        using var originalFixture = ExecutableFixture(original);
        using var patchedFixture = ExecutableFixture(patched);
        using var originalRunner = new Runner(originalFixture);
        using var patchedRunner = new Runner(patchedFixture);
        Case[] cases =
        [
            new("BelowForcedTimeslice", 99, 999_999),
            new("ForcedTimesliceElapsed", 99, 1_000_000),
            new("QuantumElapsed", 99, 10_000_000),
            new("LowerPassPreempts", 99, 1_000_000, NextPass: 0),
            new("HigherPassKeepsCurrent", 99, 1_000_000, NextPass: 1_000_000),
            new("BoostDecay", 99, 1_000_000, Boosted: true),
            new("NullCurrent", 99, 1_000_000, HasCurrent: false),
            new("NullCpuData", 99, 1_000_000, HasCpu: false),
            new("ExitedThread", 99, 1_000_000, HasThreadData: false, NextPass: 0),
            new("CounterOverflow", uint.MaxValue, 1_000_000)
        ];
        var observations = cases.Select(test => (Test: test, Original: originalRunner.Run(test), Patched: patchedRunner.Run(test))).ToArray();
        foreach (var observation in observations)
            check("PeriodicSchedulerActualIl_" + observation.Test.Name, observation.Patched.SerialWrites == 0 &&
                observation.Patched with { Preempt = observation.Original.Preempt } == observation.Original with { SerialWrites = 0 } &&
                observation.Patched.Counter == unchecked(observation.Test.Counter + 1));
        check("PeriodicSchedulerExecutablePositiveControlActuallyPrinted", observations[1].Original.SerialWrites == 5 &&
            observations[1].Original.TotalRuntime == 1_000_100 && observations[1].Original.Pass == 602 &&
            !observations[1].Original.Preempt && observations[1].Original.GlobalUpdates == 1);
        check("PeriodicSchedulerOneMsForcesRotationButSubMsDoesNot", !observations[0].Patched.Preempt && observations[1].Patched.Preempt &&
            !observations[1].Original.Preempt && observations[4].Patched.Preempt && !observations[4].Original.Preempt);
        check("PeriodicSchedulerPreemptionAndBoostExecutableControl", observations[2].Patched.Preempt && observations[3].Patched.Preempt &&
            !observations[5].Patched.Boosted && !observations[6].Patched.Preempt &&
            !observations[7].Patched.Preempt && observations[8].Patched.Preempt);
        var oldFair = originalRunner.RunTwoRunnableThreads(0);
        var newFair = patchedRunner.RunTwoRunnableThreads(0);
        var oldGap = originalRunner.RunTwoRunnableThreads(1_000_000_000_000);
        var newGap = patchedRunner.RunTwoRunnableThreads(1_000_000_000_000);
        check("PeriodicSchedulerTwoRunnableThreadsBothProgressWithRealOneMsAccounting", newFair.FirstTicks == 50 && newFair.SecondTicks == 50 &&
            newFair.FirstRuntime == 50_000_000 && newFair.SecondRuntime == 50_000_000 && newFair.MaximumSameThreadRun == 1);
        check("PeriodicSchedulerLargeWallGapPassDifferenceCannotMonopolizeRunnablePair", oldGap.FirstTicks == 100 && oldGap.SecondTicks == 0 &&
            newGap.FirstTicks == 50 && newGap.SecondTicks == 50 && newGap.MaximumSameThreadRun == 1 && newGap.FirstRuntime + newGap.SecondRuntime == 100_000_000);
        check("PeriodicSchedulerOriginalFairPairPositiveControlProgresses", oldFair.FirstTicks > 0 && oldFair.SecondTicks > 0);

        void CheckRejected(string name, AssemblyDefinition source, Action<AssemblyDefinition> alter)
        {
            using var changed = Clone(source);
            alter(changed);
            string unchanged = Il(Target(changed));
            bool rejected;
            try { PeriodicSchedulerLogPatch.Apply(changed); rejected = false; }
            catch (InvalidOperationException) { rejected = true; }
            check(name, rejected && unchanged == Il(Target(changed)));
        }
    }

    private static MethodDefinition Target(AssemblyDefinition assembly) => assembly.MainModule.Types.Single(type =>
        type.FullName == PeriodicSchedulerLogPatch.SchedulerType).Methods.Single(method => method.Name == "OnTick");
    private static string Il(MethodDefinition method) => !method.HasBody ? "" : string.Join("\n", method.Body.Instructions.Select(instruction => instruction.ToString()));
    private static string InstructionShape(Instruction instruction, MethodDefinition method) => instruction.OpCode.Code + ":" + (instruction.Operand switch
    {
        Instruction branch => "#" + method.Body.Instructions.IndexOf(branch),
        VariableDefinition local => "$" + local.Index,
        MemberReference member => member.FullName,
        null => "",
        var operand => operand.ToString()
    });
    private static bool IsSerialWrite(Instruction instruction) => instruction.Operand is MethodReference call &&
        call.DeclaringType.FullName == "Cosmos.Kernel.Core.IO.Serial";
    private static AssemblyDefinition Clone(AssemblyDefinition assembly)
    {
        // Cecil reads method bodies lazily; the returned assembly retains this
        // backing stream until its own disposal.
        var stream = new MemoryStream(); assembly.Write(stream); stream.Position = 0;
        return AssemblyDefinition.ReadAssembly(stream, new ReaderParameters { InMemory = true, ReadingMode = ReadingMode.Immediate });
    }
    private static HashSet<Instruction> Reachable(MethodDefinition method)
    {
        var found = new HashSet<Instruction>();
        var queue = new Queue<Instruction>(); queue.Enqueue(method.Body.Instructions[0]);
        while (queue.TryDequeue(out var instruction))
        {
            if (!found.Add(instruction)) continue;
            if (instruction.Operand is Instruction target) queue.Enqueue(target);
            if (instruction.Operand is Instruction[] targets) foreach (var branch in targets) queue.Enqueue(branch);
            if (instruction.OpCode.FlowControl is not (FlowControl.Branch or FlowControl.Return or FlowControl.Throw) && instruction.Next is { } next)
                queue.Enqueue(next);
        }
        return found;
    }

    private static AssemblyDefinition ExecutableFixture(MethodDefinition source)
    {
        var fixture = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("SchedulerFixture_" + Guid.NewGuid().ToString("N"), new Version(1, 0)),
            "SchedulerFixture", ModuleKind.Dll);
        var module = fixture.MainModule;
        var type = new TypeDefinition("Fixture", "Callback", Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.Abstract | Mono.Cecil.TypeAttributes.Sealed,
            module.TypeSystem.Object); module.Types.Add(type);
        var counter = new FieldDefinition("Counter", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static, module.TypeSystem.UInt32);
        type.Fields.Add(counter);
        var method = new MethodDefinition("OnTick", Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.Static, module.TypeSystem.Boolean);
        foreach (var parameter in new[] { typeof(PeriodicSchedulerFixture.Scheduler), typeof(PeriodicSchedulerFixture.CpuState), typeof(PeriodicSchedulerFixture.SchedulerThread), typeof(ulong) })
            method.Parameters.Add(new("arg" + method.Parameters.Count, Mono.Cecil.ParameterAttributes.None, module.ImportReference(parameter)));
        type.Methods.Add(method); method.Body.InitLocals = source.Body.InitLocals; method.Body.MaxStackSize = source.Body.MaxStackSize;
        var locals = source.Body.Variables.ToDictionary(local => local, local => new VariableDefinition(local.VariableType.FullName switch
        {
            "Cosmos.Kernel.Core.Scheduler.Stride.StrideCpuData" => module.ImportReference(typeof(PeriodicSchedulerFixture.CpuData)),
            "Cosmos.Kernel.Core.Scheduler.Stride.StrideThreadData" => module.ImportReference(typeof(PeriodicSchedulerFixture.ThreadData)),
            "System.UInt64" => module.TypeSystem.UInt64,
            _ => throw new InvalidOperationException("Unknown scheduler fixture local type.")
        }));
        foreach (var local in locals.Values) method.Body.Variables.Add(local);
        var instructions = source.Body.Instructions.ToDictionary(instruction => instruction, _ => Instruction.Create(OpCodes.Nop));
        foreach (var instruction in source.Body.Instructions)
        {
            var clone = instructions[instruction]; clone.OpCode = instruction.OpCode;
            clone.Operand = instruction.Operand switch
            {
                Instruction branch => instructions[branch],
                Instruction[] branches => branches.Select(branch => instructions[branch]).ToArray(),
                VariableDefinition local => locals[local],
                ParameterDefinition parameter => method.Parameters[parameter.Index + 1],
                FieldReference field when field.Name == "s_onTickLogCount" => counter,
                MethodReference call => FixtureCall(call),
                null => null,
                sbyte or byte or int or long or string => instruction.Operand,
                _ => throw new InvalidOperationException("Unknown scheduler fixture operand.")
            };
            method.Body.Instructions.Add(clone);
        }
        return fixture;

        MethodReference FixtureCall(MethodReference call)
        {
            Type hostType = call.DeclaringType.FullName switch
            {
                PeriodicSchedulerLogPatch.SchedulerType when call.Name is "CpuDataOf" or "ThreadDataOf" => typeof(PeriodicSchedulerFixture),
                PeriodicSchedulerLogPatch.SchedulerType => typeof(PeriodicSchedulerFixture.Scheduler),
                "Cosmos.Kernel.Core.Scheduler.SchedulerThread" => typeof(PeriodicSchedulerFixture.SchedulerThread),
                "Cosmos.Kernel.Core.Scheduler.Stride.StrideThreadData" => typeof(PeriodicSchedulerFixture.ThreadData),
                "Cosmos.Kernel.Core.Scheduler.Stride.StrideCpuData" => typeof(PeriodicSchedulerFixture.CpuData),
                "System.Collections.Generic.List`1<Cosmos.Kernel.Core.Scheduler.SchedulerThread>" => typeof(List<PeriodicSchedulerFixture.SchedulerThread>),
                "Cosmos.Kernel.Core.IO.Serial" => typeof(PeriodicSchedulerFixture),
                _ => throw new InvalidOperationException("Unknown scheduler fixture call type: " + call.FullName)
            };
            return module.ImportReference(hostType.GetMethod(call.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                ?? throw new InvalidOperationException("Missing scheduler fixture method: " + call.FullName));
        }
    }

    private sealed class Runner : IDisposable
    {
        private readonly AssemblyLoadContext context = new("SchedulerCallbackControl", isCollectible: true);
        private readonly MethodInfo callback;
        private readonly FieldInfo counter;
        public Runner(AssemblyDefinition fixture)
        {
            using var bytes = new MemoryStream(); fixture.Write(bytes); bytes.Position = 0;
            var type = context.LoadFromStream(bytes).GetType("Fixture.Callback", throwOnError: true)!;
            callback = type.GetMethod("OnTick")!; counter = type.GetField("Counter")!;
        }
        public Observation Run(Case test)
        {
            var scheduler = new PeriodicSchedulerFixture.Scheduler { Now = 10_000_000 };
            var data = new PeriodicSchedulerFixture.ThreadData { Pass = 500, Stride = 1024, IsBoosted = test.Boosted };
            var current = test.HasCurrent ? new PeriodicSchedulerFixture.SchedulerThread { Id = 7, TotalRuntime = 100, Data = test.HasThreadData ? data : null } : null;
            var cpuData = test.HasCpu ? new PeriodicSchedulerFixture.CpuData() : null;
            if (cpuData is not null && test.NextPass is { } pass)
                cpuData.RunQueue.Add(new() { Id = 8, Data = new() { Pass = pass } });
            var cpu = new PeriodicSchedulerFixture.CpuState { Data = cpuData };
            PeriodicSchedulerFixture.SerialWrites = 0; counter.SetValue(null, test.Counter);
            bool preempt = (bool)callback.Invoke(null, [scheduler, cpu, current, test.Elapsed])!;
            return new(preempt, (uint)counter.GetValue(null)!, current?.TotalRuntime ?? 0, data.Pass, data.IsBoosted, scheduler.GlobalUpdates,
                PeriodicSchedulerFixture.SerialWrites);
        }
        public FairnessObservation RunTwoRunnableThreads(long secondInitialPass)
        {
            var scheduler = new PeriodicSchedulerFixture.Scheduler { Now = 10_000_000 };
            var first = new PeriodicSchedulerFixture.SchedulerThread { Id = 7, Data = new() { Stride = 1024, Pass = 0 } };
            var second = new PeriodicSchedulerFixture.SchedulerThread { Id = 8, Data = new() { Stride = 1024, Pass = secondInitialPass } };
            var data = new PeriodicSchedulerFixture.CpuData(); var cpu = new PeriodicSchedulerFixture.CpuState { Data = data };
            var current = first; data.RunQueue.Add(second);
            int firstTicks = 0, secondTicks = 0, consecutive = 0, maximumConsecutive = 0; uint previous = 0;
            for (int tick = 0; tick < 100; tick++)
            {
                if (current == first) firstTicks++; else secondTicks++;
                consecutive = previous == current.Id ? consecutive + 1 : 1; previous = current.Id;
                maximumConsecutive = Math.Max(maximumConsecutive, consecutive);
                bool preempt = (bool)callback.Invoke(null, [scheduler, cpu, current, 1_000_000UL])!;
                scheduler.Now += 1_000_000;
                if (!preempt) continue;
                // With exactly two runnable threads, pinned PickNext removes
                // the sole queued entry before OnThreadYield inserts prev.
                var next = data.RunQueue[0]; data.RunQueue.RemoveAt(0); data.RunQueue.Add(current); current = next;
            }
            return new(firstTicks, secondTicks, first.TotalRuntime, second.TotalRuntime, maximumConsecutive);
        }
        public void Dispose() => context.Unload();
    }

    private sealed record Case(string Name, uint Counter, ulong Elapsed, long? NextPass = null, bool Boosted = false,
        bool HasCurrent = true, bool HasCpu = true, bool HasThreadData = true);
    private sealed record Observation(bool Preempt, uint Counter, ulong TotalRuntime, long Pass, bool Boosted, int GlobalUpdates, int SerialWrites);
    private sealed record FairnessObservation(int FirstTicks, int SecondTicks, ulong FirstRuntime, ulong SecondRuntime, int MaximumSameThreadRun);
}

public static class PeriodicSchedulerFixture
{
    public static int SerialWrites;
    public static void WriteString(string text) => SerialWrites++;
    public static void WriteNumber(uint number, bool hex) => SerialWrites++;
    public static CpuData? CpuDataOf(CpuState cpu) => cpu.Data;
    public static ThreadData? ThreadDataOf(SchedulerThread thread) => thread.Data;
    public sealed class Scheduler
    {
        public ulong Now;
        public int GlobalUpdates;
        public ulong GetTimestamp() => Now;
        public void UpdateGlobalPass(CpuData cpu) => GlobalUpdates++;
    }
    public sealed class CpuState { public CpuData? Data; }
    public sealed class CpuData { public List<SchedulerThread> RunQueue { get; } = []; }
    public sealed class SchedulerThread
    {
        public uint Id { get; set; }
        public ulong TotalRuntime { get; set; }
        public ThreadData? Data;
    }
    public sealed class ThreadData
    {
        public long Pass { get; set; }
        public ulong Stride { get; set; }
        public bool IsBoosted { get; set; }
        public ulong LastWakeup { get; set; }
    }
}