-
Notifications
You must be signed in to change notification settings - Fork 5.5k
[cdac] Make contracts tests use mock/placeholder target instead of actual target #110027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
elinor-fung
merged 5 commits into
dotnet:main
from
elinor-fung:cdac-test-use-placeholder-target
Nov 21, 2024
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
70155c1
Make contracts tests use mock target instead of actual target
elinor-fung 02f82ef
Move ExecutionManagerTestBuilder under MockDescriptors
elinor-fung f9b475b
Move helpers and tests for an actual contract descriptor (vs mock mem…
elinor-fung 4f91fd2
Update root namespaces of tests to match the assembly name
elinor-fung 6ce59fc
Move UseFunclets to local
elinor-fung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
173 changes: 173 additions & 0 deletions
173
src/native/managed/cdacreader/tests/ContractDescriptor/ContractDescriptorBuilder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
|
|
||
| namespace Microsoft.Diagnostics.DataContractReader.Tests.ContractDescriptor; | ||
|
|
||
| internal class ContractDescriptorBuilder : MockMemorySpace.Builder | ||
| { | ||
| // These addresses are arbitrary and are used to store the contract descriptor components. | ||
| // They should not overlap with any other heap fragment addresses. | ||
| private const ulong ContractDescriptorAddr = 0xaaaaaaaa; | ||
| private const uint JsonDescriptorAddr = 0xdddddddd; | ||
| private const uint ContractPointerDataAddr = 0xeeeeeeee; | ||
|
|
||
| private bool _created = false; | ||
|
|
||
| private IReadOnlyCollection<string> _contracts; | ||
| private IDictionary<DataType, Target.TypeInfo> _types; | ||
| private IReadOnlyCollection<(string Name, ulong? Value, uint? IndirectIndex, string? TypeName)> _globals; | ||
| private IReadOnlyCollection<ulong> _indirectValues; | ||
|
|
||
| public ContractDescriptorBuilder(TargetTestHelpers targetTestHelpers) | ||
| : base(targetTestHelpers) | ||
| { } | ||
|
|
||
| public ContractDescriptorBuilder SetContracts(IReadOnlyCollection<string> contracts) | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
| _contracts = contracts; | ||
| return this; | ||
| } | ||
|
|
||
| public ContractDescriptorBuilder SetTypes(IDictionary<DataType, Target.TypeInfo> types) | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
| _types = types; | ||
| return this; | ||
| } | ||
|
|
||
| public ContractDescriptorBuilder SetGlobals(IReadOnlyCollection<(string Name, ulong Value, string? TypeName)> globals) | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
| if (_globals != null) | ||
| throw new InvalidOperationException("Globals already set"); | ||
| _globals = globals.Select(g => (g.Name, (ulong?)g.Value, (uint?)null, g.TypeName)).ToArray(); | ||
| _indirectValues = null; | ||
| return this; | ||
| } | ||
|
|
||
| public ContractDescriptorBuilder SetGlobals(IReadOnlyCollection<(string Name, ulong? Value, uint? IndirectIndex, string? TypeName)> globals, IReadOnlyCollection<ulong> indirectValues) | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
| if (_globals != null) | ||
| throw new InvalidOperationException("Globals already set"); | ||
| _globals = globals; | ||
| _indirectValues = indirectValues; | ||
| return this; | ||
| } | ||
|
|
||
| private MockMemorySpace.HeapFragment CreateContractDescriptor(int jsonLength, int pointerDataCount) | ||
| { | ||
| byte[] descriptor = new byte[ContractDescriptorHelpers.Size(TargetTestHelpers.Arch.Is64Bit)]; | ||
| ContractDescriptorHelpers.Fill(descriptor, TargetTestHelpers.Arch, jsonLength, JsonDescriptorAddr, pointerDataCount, ContractPointerDataAddr); | ||
| return new MockMemorySpace.HeapFragment | ||
| { | ||
| Address = ContractDescriptorAddr, | ||
| Data = descriptor, | ||
| Name = "ContractDescriptor" | ||
| }; | ||
| } | ||
|
|
||
| private string MakeContractsJson() | ||
| { | ||
| if (_contracts.Count == 0) | ||
| return string.Empty; | ||
| StringBuilder sb = new(); | ||
| foreach (var c in _contracts) | ||
| { | ||
| sb.Append($"\"{c}\": 1,"); | ||
| } | ||
| Debug.Assert(sb.Length > 0); | ||
| sb.Length--; // remove trailing comma | ||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private (MockMemorySpace.HeapFragment json, MockMemorySpace.HeapFragment pointerData) CreateDataDescriptor() | ||
| { | ||
| string metadataTypesJson = _types is not null ? ContractDescriptorHelpers.MakeTypesJson(_types) : string.Empty; | ||
| string metadataGlobalsJson = _globals is not null ? ContractDescriptorHelpers.MakeGlobalsJson(_globals) : string.Empty; | ||
| string interpolatedContracts = _contracts is not null ? MakeContractsJson() : string.Empty; | ||
| byte[] jsonBytes = Encoding.UTF8.GetBytes($$""" | ||
| { | ||
| "version": 0, | ||
| "baseline": "empty", | ||
| "contracts": { {{interpolatedContracts}} }, | ||
| "types": { {{metadataTypesJson}} }, | ||
| "globals": { {{metadataGlobalsJson}} } | ||
| } | ||
| """); | ||
| MockMemorySpace.HeapFragment json = new() | ||
| { | ||
| Address = JsonDescriptorAddr, | ||
| Data = jsonBytes, | ||
| Name = "JsonDescriptor" | ||
| }; | ||
|
|
||
| MockMemorySpace.HeapFragment pointerData; | ||
| if (_indirectValues != null) | ||
| { | ||
| int pointerSize = TargetTestHelpers.PointerSize; | ||
| byte[] pointerDataBytes = new byte[_indirectValues.Count * pointerSize]; | ||
| int offset = 0; | ||
| foreach (var value in _indirectValues) | ||
| { | ||
| TargetTestHelpers.WritePointer(pointerDataBytes.AsSpan(offset, pointerSize), value); | ||
| offset += pointerSize; | ||
| } | ||
| pointerData = new MockMemorySpace.HeapFragment | ||
| { | ||
| Address = ContractPointerDataAddr, | ||
| Data = pointerDataBytes, | ||
| Name = "PointerData" | ||
| }; | ||
| } | ||
| else | ||
| { | ||
| pointerData = new MockMemorySpace.HeapFragment | ||
| { | ||
| Address = ContractPointerDataAddr, | ||
| Data = Array.Empty<byte>(), | ||
| Name = "PointerData" | ||
| }; | ||
| } | ||
| return (json, pointerData); | ||
| } | ||
|
|
||
| private ulong CreateDescriptorFragments() | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
|
|
||
| (var json, var pointerData) = CreateDataDescriptor(); | ||
| int pointerDataCount = pointerData.Data is null ? 0 : pointerData.Data.Length / TargetTestHelpers.PointerSize; | ||
| MockMemorySpace.HeapFragment descriptor = CreateContractDescriptor(json.Data.Length, pointerDataCount); | ||
|
|
||
| AddHeapFragment(descriptor); | ||
| AddHeapFragment(json); | ||
| if (pointerData.Data.Length > 0) | ||
| AddHeapFragment(pointerData); | ||
|
|
||
| _created = true; | ||
| return descriptor.Address; | ||
| } | ||
|
|
||
| public bool TryCreateTarget([NotNullWhen(true)] out ContractDescriptorTarget? target) | ||
| { | ||
| if (_created) | ||
| throw new InvalidOperationException("Context already created"); | ||
| ulong contractDescriptorAddress = CreateDescriptorFragments(); | ||
| MockMemorySpace.ReadContext context = GetReadContext(); | ||
| return ContractDescriptorTarget.TryCreate(contractDescriptorAddress, context.ReadFromTarget, out target); | ||
| } | ||
| } | ||
156 changes: 156 additions & 0 deletions
156
src/native/managed/cdacreader/tests/ContractDescriptor/ContractDescriptorHelpers.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Buffers.Binary; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| namespace Microsoft.Diagnostics.DataContractReader.Tests.ContractDescriptor; | ||
|
|
||
| internal unsafe class ContractDescriptorHelpers | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was all copied out of |
||
| { | ||
| public static int Size(bool is64Bit) => is64Bit ? sizeof(ContractDescriptor64) : sizeof(ContractDescriptor32); | ||
|
|
||
| public static void Fill(Span<byte> dest, MockTarget.Architecture arch, int jsonDescriptorSize, uint jsonDescriptorAddr, int pointerDataCount, uint pointerDataAddr) | ||
| { | ||
| if (arch.Is64Bit) | ||
| { | ||
| ContractDescriptor64.Fill(dest, arch.IsLittleEndian, jsonDescriptorSize, jsonDescriptorAddr, pointerDataCount, pointerDataAddr); | ||
| } | ||
| else | ||
| { | ||
| ContractDescriptor32.Fill(dest, arch.IsLittleEndian, jsonDescriptorSize, jsonDescriptorAddr, pointerDataCount, pointerDataAddr); | ||
| } | ||
| } | ||
|
|
||
| private struct ContractDescriptor32 | ||
| { | ||
| public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); | ||
| public uint Flags = 0x2 /*32-bit*/ | 0x1; | ||
| public uint DescriptorSize; | ||
| public uint Descriptor; | ||
| public uint PointerDataCount; | ||
| public uint Pad0 = 0; | ||
| public uint PointerData; | ||
|
|
||
| public ContractDescriptor32() { } | ||
|
|
||
| public static void Fill(Span<byte> dest, bool isLittleEndian, int jsonDescriptorSize, uint jsonDescriptorAddr, int pointerDataCount, uint pointerDataAddr) | ||
| { | ||
| ContractDescriptor32 descriptor = new() | ||
| { | ||
| DescriptorSize = (uint)jsonDescriptorSize, | ||
| Descriptor = jsonDescriptorAddr, | ||
| PointerDataCount = (uint)pointerDataCount, | ||
| PointerData = pointerDataAddr, | ||
| }; | ||
| if (BitConverter.IsLittleEndian != isLittleEndian) | ||
| descriptor.ReverseEndianness(); | ||
|
|
||
| MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); | ||
| } | ||
|
|
||
| private void ReverseEndianness() | ||
| { | ||
| Magic = BinaryPrimitives.ReverseEndianness(Magic); | ||
| Flags = BinaryPrimitives.ReverseEndianness(Flags); | ||
| DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); | ||
| Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); | ||
| PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); | ||
| Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); | ||
| PointerData = BinaryPrimitives.ReverseEndianness(PointerData); | ||
| } | ||
| } | ||
|
|
||
| private struct ContractDescriptor64 | ||
| { | ||
| public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); | ||
| public uint Flags = 0x1; | ||
| public uint DescriptorSize; | ||
| public ulong Descriptor; | ||
| public uint PointerDataCount; | ||
| public uint Pad0 = 0; | ||
| public ulong PointerData; | ||
|
|
||
| public ContractDescriptor64() { } | ||
|
|
||
| public static void Fill(Span<byte> dest, bool isLittleEndian, int jsonDescriptorSize, uint jsonDescriptorAddr, int pointerDataCount, uint pointerDataAddr) | ||
| { | ||
| ContractDescriptor64 descriptor = new() | ||
| { | ||
| DescriptorSize = (uint)jsonDescriptorSize, | ||
| Descriptor = jsonDescriptorAddr, | ||
| PointerDataCount = (uint)pointerDataCount, | ||
| PointerData = pointerDataAddr, | ||
| }; | ||
| if (BitConverter.IsLittleEndian != isLittleEndian) | ||
| descriptor.ReverseEndianness(); | ||
|
|
||
| MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); | ||
| } | ||
|
|
||
| private void ReverseEndianness() | ||
| { | ||
| Magic = BinaryPrimitives.ReverseEndianness(Magic); | ||
| Flags = BinaryPrimitives.ReverseEndianness(Flags); | ||
| DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); | ||
| Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); | ||
| PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); | ||
| Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); | ||
| PointerData = BinaryPrimitives.ReverseEndianness(PointerData); | ||
| } | ||
| } | ||
|
|
||
| #region JSON formatting | ||
| private static string GetTypeJson(string name, Target.TypeInfo info) | ||
| { | ||
| string ret = string.Empty; | ||
| List<string> fields = info.Size is null ? [] : [$"\"!\":{info.Size}"]; | ||
| fields.AddRange(info.Fields.Select(f => $"\"{f.Key}\":{(f.Value.TypeName is null ? f.Value.Offset : $"[{f.Value.Offset},\"{f.Value.TypeName}\"]")}")); | ||
| return $"\"{name}\":{{{string.Join(',', fields)}}}"; | ||
| } | ||
|
|
||
| public static string MakeTypesJson(IDictionary<DataType, Target.TypeInfo> types) | ||
| { | ||
| return string.Join(',', types.Select(t => GetTypeJson(t.Key.ToString(), t.Value))); | ||
| } | ||
|
|
||
| public static string MakeGlobalsJson(IEnumerable<(string Name, ulong Value, string? Type)> globals) | ||
| { | ||
| return MakeGlobalsJson(globals.Select(g => (g.Name, (ulong?)g.Value, (uint?)null, g.Type))); | ||
| } | ||
|
|
||
| public static string MakeGlobalsJson(IEnumerable<(string Name, ulong? Value, uint? IndirectIndex, string? Type)> globals) | ||
| { | ||
| return string.Join(',', globals.Select(FormatGlobal)); | ||
|
|
||
| static string FormatGlobal((string Name, ulong? Value, uint? IndirectIndex, string? Type) global) | ||
| { | ||
| if (global.Value is ulong value) | ||
| { | ||
| return $"\"{global.Name}\": {FormatValue(value, global.Type)}"; | ||
| } | ||
| else if (global.IndirectIndex is uint index) | ||
| { | ||
| return $"\"{global.Name}\": {FormatIndirect(index, global.Type)}"; | ||
| } | ||
| else | ||
| { | ||
| throw new InvalidOperationException("Global must have a value or indirect index"); | ||
| } | ||
|
|
||
| } | ||
| static string FormatValue(ulong value, string? type) | ||
| { | ||
| return type is null ? $"{value}" : $"[{value},\"{type}\"]"; | ||
| } | ||
| static string FormatIndirect(uint value, string? type) | ||
| { | ||
| return type is null ? $"[{value}]" : $"[[{value}],\"{type}\"]"; | ||
| } | ||
| } | ||
|
|
||
| #endregion JSON formatting | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was all copied out of
MockMemorySpace.cs