Files
stgsharp/StgSharpDebug/TestL4.cs
T

323 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// 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<CacheLineDescription> node,
out int count
)
{
count = Math.Min(_addresses.Length, node.Length);
for (int i = 0; i < count; i++) {
node[i] = new CacheLineDescription
{
Address = _addresses[i],
MapPolicy = 0,
};
}
return count > 0;
}
// 将缓存行填充 0xAB,便于数据完整性断言
public void Prefetch(
nuint origin,
ulong policy,
Span<byte> cache
)
{
cache.Fill(0xAB);
}
// 只读路径测试:无需回写
public void WriteBack(
nuint origin,
ulong policy,
ReadOnlySpan<byte> 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<nuint>());
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<byte> data = new ReadOnlySpan<byte>(
(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;
}
}
}