//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // file="TestL4" // Project: StgSharp // AuthorGroup: Nitload // Copyright (c) Nitload. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using StgSharp.HighPerformance.Memory; using System; using System.Runtime.CompilerServices; namespace StgSharp { // ==================================================================== // SequentialPredictor // 辅助类:给定一批地址,每次 Predict 返回全部地址(幂等); // Prefetch 将缓存行填充为 0xAB,WriteBack 为空操作。 // 仅供 L4 内存安全测试使用。 // ==================================================================== internal sealed unsafe class SequentialPredictor : IL4Predict { private readonly nuint[] _addresses; public SequentialPredictor( nuint[] addresses ) { _addresses = addresses; } public bool Predict( Span node, out int count ) { count = Math.Min(_addresses.Length, node.Length); for (int i = 0; i < count; i++) { node[i] = new CacheLinePrediction { Address = _addresses[i], MapPolicy = 0, }; } return count > 0; } // 将缓存行填充 0xAB,便于数据完整性断言 public void Prefetch( nuint origin, ulong policy, Span cache ) { cache.Fill(0xAB); } // 只读路径测试:无需回写 public void WriteBack( nuint origin, ulong policy, ReadOnlySpan cache ) { } } // ==================================================================== // L4MemorySafetyTest — 挂载到 Program partial class // ==================================================================== internal static partial class Program { // ---------------------------------------------------------------- // 统一的断言辅助:条件为 false 时打印 FAIL 并记录失败数 // ---------------------------------------------------------------- private static int _l4FailCount; // ================================================================ // 入口:从 Main 调用 TestL4MemorySafety() // ================================================================ public static void TestL4MemorySafety() { _l4FailCount = 0; Console.WriteLine("=== L4 Memory Safety Tests (P0) ==="); Console.WriteLine(); L4Test_Dispose_NoLeak(); L4Test_Map_CacheHit_DataIntegrity(); L4Test_CacheLine_Dispose_DecrementsCounts(); L4Test_CacheLine_Dispose_Idempotent(); Console.WriteLine(); if (_l4FailCount == 0) { Console.WriteLine("=== ALL P0 TESTS PASSED ==="); } else { Console.WriteLine($"=== {_l4FailCount} TEST(S) FAILED ==="); } } private static void L4Assert( bool condition, string message, [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0 ) { if (!condition) { Console.WriteLine($" [FAIL] {caller}:{line} {message}"); _l4FailCount++; } } // ================================================================ // P0-3 CacheLine_Dispose_DecrementsCounts // Dispose 后 refCount/activeCount 正确递减: // 连续 Map → Dispose 同一地址 3 轮,每轮都能拿到有效句柄。 // 若 refCount 未递减,驱逐时 CAS 永远失败,缓存满后 // Evict 超时会抛出 OverflowException。 // ================================================================ private static void L4Test_CacheLine_Dispose_DecrementsCounts() { Console.Write("[P0-3] CacheLine_Dispose_DecrementsCounts ... "); try { nuint[] addresses = MakeL4FakeAddresses(1); using L4 l4 = new L4(); l4.PrefetchPredict = new SequentialPredictor(addresses); nuint target = addresses[0]; bool passed = true; for (int round = 0; round < 3; round++) { L4.CacheLine line = l4.Map(target); if (line.Address == nuint.Zero) { L4Assert(false, $"Round {round}: Map returned zero address"); passed = false; line.Dispose(); break; } line.Dispose(); } if (passed) { Console.WriteLine("PASS"); } } catch (Exception ex) { Console.WriteLine($"FAIL ({ex.GetType().Name}: {ex.Message})"); _l4FailCount++; } } // ================================================================ // P0-4 CacheLine_Dispose_Idempotent // 对同一 CacheLine 连续 Dispose 两次: // - 不抛异常 // - 计数未被双减:之后再次 Map 同地址仍能成功 // ================================================================ private static void L4Test_CacheLine_Dispose_Idempotent() { Console.Write("[P0-4] CacheLine_Dispose_Idempotent ... "); try { nuint[] addresses = MakeL4FakeAddresses(1); using L4 l4 = new L4(); l4.PrefetchPredict = new SequentialPredictor(addresses); nuint target = addresses[0]; // 第一轮:Map → Dispose × 2 L4.CacheLine line = l4.Map(target); line.Dispose(); // 正常释放 line.Dispose(); // 幂等:_handle 已为 0,直接返回 // 第二轮:计数未双减,应仍能成功 Map L4.CacheLine line2 = l4.Map(target); L4Assert(line2.Address != nuint.Zero, "After double-dispose, Map should still return valid address"); line2.Dispose(); if (_l4FailCount == 0) { Console.WriteLine("PASS"); } } catch (Exception ex) { Console.WriteLine($"FAIL ({ex.GetType().Name}: {ex.Message})"); _l4FailCount++; } } // ================================================================ // P0-1 Dispose_NoLeak // 构造后立即 Dispose,不抛异常;连续 Dispose 两次幂等。 // ================================================================ private static void L4Test_Dispose_NoLeak() { Console.Write("[P0-1] Dispose_NoLeak ... "); try { L4 l4 = new L4(); l4.PrefetchPredict = new SequentialPredictor(Array.Empty()); l4.Dispose(); // 第一次:正常释放 l4.Dispose(); // 第二次:_disposed 保护,应幂等 Console.WriteLine("PASS"); } catch (Exception ex) { Console.WriteLine($"FAIL ({ex.GetType().Name}: {ex.Message})"); _l4FailCount++; } } // ================================================================ // P0-2 Map_CacheHit_DataIntegrity // 经由 SequentialPredictor 预取后,Map() 命中的缓存行数据 // 应与 Prefetch() 写入的内容一致(全 0xAB)。 // // 注意:需要 CacheLine 暴露 public nuint Address 才能读取内容。 // 若该属性尚未暴露,本测试会编译报错,届时先添加该属性。 // ================================================================ private static unsafe void L4Test_Map_CacheHit_DataIntegrity() { Console.Write("[P0-2] Map_CacheHit_DataIntegrity ... "); try { nuint[] addresses = MakeL4FakeAddresses(4); using L4 l4 = new L4(); l4.PrefetchPredict = new SequentialPredictor(addresses); // Map 触发内部同步 Prefetch,命中第一个地址 L4.CacheLine line = l4.Map(addresses[0]); try { // 读取缓存行内容,应全为 0xAB ReadOnlySpan data = new ReadOnlySpan( (byte*)line.Address, 8192 // CacheLineData.Data 固定 8192 字节 ); bool allAb = true; for (int i = 0; i < data.Length; i++) { if (data[i] != 0xAB) { allAb = false; L4Assert(false, $"data[{i}] == 0x{data[i]:X2}, expected 0xAB"); break; } } if (allAb) { Console.WriteLine("PASS"); } } finally { line.Dispose(); } } catch (Exception ex) { Console.WriteLine($"FAIL ({ex.GetType().Name}: {ex.Message})"); _l4FailCount++; } } // ---------------------------------------------------------------- // 生成 count 个互不相同的假地址(仅用作 SwissTable key, // 不会被解引用;偏移足够大以避开地址 0) // ---------------------------------------------------------------- private static nuint[] MakeL4FakeAddresses( int count ) { nuint[] result = new nuint[count]; for (int i = 0; i < count; i++) { result[i] = (nuint)(0x1_0000_0000UL + (((ulong)i) * 0x1000)); } return result; } } }