Skip to content
Merged
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)
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)
{
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