Skip to content
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

[cdac] Read/store globals from contract descriptor #101450

Merged
merged 12 commits into from
Apr 26, 2024
4 changes: 2 additions & 2 deletions docs/design/datacontracts/contract-descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ reserved bits should be written as zero. Diagnostic tooling may ignore non-zero

The `descriptor` is a pointer to a UTF-8 JSON string described in [data descriptor physical layout](./data_descriptor.md#Physical_JSON_descriptor). The total number of bytes is given by `descriptor_size`.

The auxiliary data for the JSON descriptor is stored at the location `aux_data` in `aux_data_count` pointer-sized slots.
The auxiliary data for the JSON descriptor is stored at the location `pointer_data` in `pointer_data_count` pointer-sized slots.

### Architecture properties

Expand Down Expand Up @@ -83,7 +83,7 @@ a JSON integer constant.
"globals":
{
"FEATURE_COMINTEROP": 0,
"s_pThreadStore": [ 0 ] // indirect from aux data offset 0
"s_pThreadStore": [ 0 ] // indirect from pointer data offset 0
},
"contracts": {"Thread": 1, "GCHandle": 1, "ThreadStore": 1}
}
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/debug/daccess/cdac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,21 @@ CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target)
}

CDAC::CDAC(HMODULE module, uint64_t descriptorAddr, ICorDebugDataTarget* target)
: m_module(module)
: m_module{module}
, m_cdac_handle{0}
, m_target{target}
{
if (m_module == NULL)
{
m_cdac_handle = 0;
return;
}

m_target->AddRef();
decltype(&cdac_reader_init) init = reinterpret_cast<decltype(&cdac_reader_init)>(::GetProcAddress(m_module, "cdac_reader_init"));
decltype(&cdac_reader_get_sos_interface) getSosInterface = reinterpret_cast<decltype(&cdac_reader_get_sos_interface)>(::GetProcAddress(m_module, "cdac_reader_get_sos_interface"));
_ASSERTE(init != nullptr && getSosInterface != nullptr);

init(descriptorAddr, &ReadFromTargetCallback, m_target, &m_cdac_handle);
if (init(descriptorAddr, &ReadFromTargetCallback, m_target, &m_cdac_handle) != 0)
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
return;

getSosInterface(m_cdac_handle, &m_sos);
}

Expand Down
13 changes: 13 additions & 0 deletions src/native/managed/cdacreader/src/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader;

internal static class Constants
{
internal static class Globals
{
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
}
}
3 changes: 1 addition & 2 deletions src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public SOSDacImpl(Target target)

public int GetBreakingChangeVersion()
{
// TODO: Return non-hard-coded version
return 4;
return _target.ReadGlobalUInt8(Constants.Globals.SOSBreakingChangeVersion);
}

public unsafe int GetCCWData(ulong ccw, void* data) => HResults.E_NOTIMPL;
Expand Down
99 changes: 95 additions & 4 deletions src/native/managed/cdacreader/src/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public struct TargetPointer
public TargetPointer(ulong value) => Value = value;
}

internal sealed unsafe class Target
public sealed unsafe class Target
{
private const int StackAllocByteThreshold = 1024;

Expand All @@ -29,7 +29,7 @@ private readonly struct Configuration
private readonly Reader _reader;

private readonly IReadOnlyDictionary<string, int> _contracts = new Dictionary<string, int>();
private readonly TargetPointer[] _pointerData = [];
private readonly IReadOnlyDictionary<string, (ulong Value, string? Type)> _globals = new Dictionary<string, (ulong, string?)>();

public static bool TryCreate(ulong contractDescriptor, delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget, void* readContext, out Target? target)
{
Expand All @@ -49,11 +49,30 @@ private Target(Configuration config, ContractDescriptorParser.ContractDescriptor
_config = config;
_reader = reader;

// TODO: [cdac] Read globals and types
// TODO: [cdac] Read types
// note: we will probably want to store the globals and types into a more usable form
_contracts = descriptor.Contracts ?? [];

_pointerData = pointerData;
// Read globals and map indirect values to pointer data
if (descriptor.Globals is not null)
{
Dictionary<string, (ulong Value, string? Type)> globals = [];
foreach ((string name, ContractDescriptorParser.GlobalDescriptor global) in descriptor.Globals)
{
ulong value = global.Value;
if (global.Indirect)
{
if (value >= (ulong)pointerData.Length)
throw new InvalidOperationException($"Invalid pointer data index {value}.");

value = pointerData[value].Value;
}

globals[name] = (value, global.Type);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just using the (optional) type string for now. I expect we'll do some mapping that isn't just string-based once we also read in the types.

}

_globals = globals;
}
}

// See docs/design/datacontracts/contract-descriptor.md
Expand Down Expand Up @@ -138,6 +157,29 @@ private static bool TryReadContractDescriptor(
return true;
}

public byte ReadUInt8(ulong address)
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
{
if (!TryReadUInt8(address, out byte value))
throw new InvalidOperationException($"Failed to read uint8 at 0x{address:x8}.");

return value;
}

public bool TryReadUInt8(ulong address, out byte value)
=> TryReadUInt8(address, _reader, out value);

private static bool TryReadUInt8(ulong address, Reader reader, out byte value)
{
value = 0;
fixed (byte* ptr = &value)
{
if (reader.ReadFromTarget(address, ptr, 1) < 0)
return false;
}

return true;
}

public uint ReadUInt32(ulong address)
{
if (!TryReadUInt32(address, out uint value))
Expand Down Expand Up @@ -201,6 +243,55 @@ private static bool TryReadPointer(ulong address, Configuration config, Reader r
return true;
}

public byte ReadGlobalUInt8(string name)
{
if (!TryReadGlobalUInt8(name, out byte value))
throw new InvalidOperationException($"Failed to read global uint8 '{name}'.");

return value;
}

public bool TryReadGlobalUInt8(string name, out byte value)
{
value = 0;
if (!TryReadGlobalValue(name, out ulong globalValue, "uint8"))
return false;

value = (byte)globalValue;
return true;
}

public TargetPointer ReadGlobalPointer(string name)
{
if (!TryReadGlobalPointer(name, out TargetPointer pointer))
throw new InvalidOperationException($"Failed to read global pointer '{name}'.");

return pointer;
}

public bool TryReadGlobalPointer(string name, out TargetPointer pointer)
{
pointer = TargetPointer.Null;
if (!TryReadGlobalValue(name, out ulong globalValue, "pointer", "nint", "nuint"))
return false;

pointer = new TargetPointer(globalValue);
return true;
}

private bool TryReadGlobalValue(string name, out ulong value, params string[] expectedTypes)
{
value = 0;
if (!_globals.TryGetValue(name, out (ulong Value, string? Type) global))
return false;

if (global.Type is not null && Array.IndexOf(expectedTypes, global.Type) == -1)
return false;

value = global.Value;
return true;
}

private sealed class Reader
{
private readonly delegate* unmanaged<ulong, byte*, uint, void*, int> _readFromTarget;
Expand Down
Loading
Loading