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
30 changes: 17 additions & 13 deletions src/coreclr/debug/daccess/cdac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,31 @@ CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target)
{
HMODULE cdacLib;
if (!TryLoadCDACLibrary(&cdacLib))
return CDAC::Invalid();
return {};

return CDAC{cdacLib, descriptorAddr, target};
decltype(&cdac_reader_init) init = reinterpret_cast<decltype(&cdac_reader_init)>(::GetProcAddress(cdacLib, "cdac_reader_init"));
_ASSERTE(init != nullptr);

intptr_t handle;
if (init(descriptorAddr, &ReadFromTargetCallback, target, &handle) != 0)
{
::FreeLibrary(cdacLib);
return {};
}

return CDAC{cdacLib, handle, target};
}

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

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);
_ASSERTE(getSosInterface != nullptr);
getSosInterface(m_cdac_handle, &m_sos);
}

Expand Down
13 changes: 7 additions & 6 deletions src/coreclr/debug/daccess/cdac.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ class CDAC final
public: // static
static CDAC Create(uint64_t descriptorAddr, ICorDebugDataTarget *pDataTarget);

static CDAC Invalid()
{
return CDAC{nullptr, 0, nullptr};
}

public:
CDAC()
: m_module{NULL}
, m_cdac_handle{0}
, m_target{NULL}
{ }
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved

CDAC(const CDAC&) = delete;
CDAC& operator=(const CDAC&) = delete;

Expand Down Expand Up @@ -56,7 +57,7 @@ class CDAC final
IUnknown* SosInterface();

private:
CDAC(HMODULE module, uint64_t descriptorAddr, ICorDebugDataTarget* target);
CDAC(HMODULE module, intptr_t handle, ICorDebugDataTarget* target);

private:
HMODULE m_module;
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3038,7 +3038,6 @@ class DacStreamManager
//----------------------------------------------------------------------------

ClrDataAccess::ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget/*=0*/)
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
: m_cdac{CDAC::Invalid()}
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
{
SUPPORTS_DAC_HOST_ONLY; // ctor does no marshalling - don't check with DacCop

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
139 changes: 117 additions & 22 deletions src/native/managed/cdacreader/src/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Numerics;

namespace Microsoft.Diagnostics.DataContractReader;

Expand All @@ -15,7 +16,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 +30,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 +50,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 +158,17 @@ 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)
=> TryRead(address, isUnsigned: true, out value);

public uint ReadUInt32(ulong address)
{
if (!TryReadUInt32(address, out uint value))
Expand All @@ -147,21 +178,38 @@ public uint ReadUInt32(ulong address)
}

public bool TryReadUInt32(ulong address, out uint value)
=> TryReadUInt32(address, _config.IsLittleEndian, _reader, out value);
=> TryRead(address, isUnsigned: true, out value);

private static bool TryReadUInt32(ulong address, bool isLittleEndian, Reader reader, out uint value)
=> TryRead(address, isLittleEndian, isUnsigned: true, reader, out value);

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

return value;
}

Span<byte> buffer = stackalloc byte[sizeof(uint)];
public bool TryReadUInt64(ulong address, out ulong value)
=> TryRead(address, isUnsigned: true, out value);

private static bool TryReadUInt64(ulong address, bool isLittleEndian, Reader reader, out ulong value)
=> TryRead(address, isLittleEndian, isUnsigned: true, reader, out value);

private bool TryRead<T>(ulong address, bool isUnsigned, out T value) where T : unmanaged, IBinaryInteger<T>
=> TryRead(address, _config.IsLittleEndian, isUnsigned, _reader, out value);

private static bool TryRead<T>(ulong address, bool isLittleEndian, bool isUnsigned, Reader reader, out T value) where T : unmanaged, IBinaryInteger<T>
{
value = default;
Span<byte> buffer = stackalloc byte[sizeof(T)];
if (reader.ReadFromTarget(address, buffer) < 0)
return false;

value = isLittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
: BinaryPrimitives.ReadUInt32BigEndian(buffer);

return true;
return isLittleEndian
? T.TryReadLittleEndian(buffer, isUnsigned, out value)
: T.TryReadBigEndian(buffer, isUnsigned, out value);
}

public TargetPointer ReadPointer(ulong address)
Expand All @@ -183,21 +231,68 @@ private static bool TryReadPointer(ulong address, Configuration config, Reader r
if (reader.ReadFromTarget(address, buffer) < 0)
return false;

if (config.PointerSize == sizeof(uint))
if (config.PointerSize == sizeof(uint)
&& TryReadUInt32(address, config.IsLittleEndian, reader, out uint value32))
{
pointer = new TargetPointer(
config.IsLittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
: BinaryPrimitives.ReadUInt32BigEndian(buffer));
pointer = new TargetPointer(value32);
return true;
}
else if (config.PointerSize == sizeof(ulong))
else if (config.PointerSize == sizeof(ulong)
&& TryReadUInt64(address, config.IsLittleEndian, reader, out ulong value64))
{
pointer = new TargetPointer(
config.IsLittleEndian
? BinaryPrimitives.ReadUInt64LittleEndian(buffer)
: BinaryPrimitives.ReadUInt64BigEndian(buffer));
pointer = new TargetPointer(value64);
return true;
}

return false;
}

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;
}

Expand Down
Loading
Loading