Skip to content

Commit

Permalink
[cdac] Read contract descriptor from target (dotnet#101208)
Browse files Browse the repository at this point in the history
- Get `DotNetRuntimeContractDescriptor` address from the target
- Read contract descriptor to determine endianness and pointer size
- Parse JSON descriptor and store contracts
  • Loading branch information
elinor-fung authored and matouskozak committed Apr 30, 2024
1 parent 5f779a0 commit 18602ea
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 46 deletions.
20 changes: 8 additions & 12 deletions src/coreclr/debug/daccess/cdac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ namespace

int ReadFromTargetCallback(uint64_t addr, uint8_t* dest, uint32_t count, void* context)
{
CDAC* cdac = reinterpret_cast<CDAC*>(context);
return cdac->ReadFromTarget(addr, dest, count);
ICorDebugDataTarget* target = reinterpret_cast<ICorDebugDataTarget*>(context);
HRESULT hr = ReadFromDataTarget(target, addr, dest, count);
if (FAILED(hr))
return hr;

return S_OK;
}
}

Expand All @@ -52,11 +56,12 @@ CDAC::CDAC(HMODULE module, uint64_t descriptorAddr, ICorDebugDataTarget* target)
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, this, &m_cdac_handle);
init(descriptorAddr, &ReadFromTargetCallback, m_target, &m_cdac_handle);
getSosInterface(m_cdac_handle, &m_sos);
}

Expand All @@ -77,12 +82,3 @@ IUnknown* CDAC::SosInterface()
{
return m_sos;
}

int CDAC::ReadFromTarget(uint64_t addr, uint8_t* dest, uint32_t count)
{
HRESULT hr = ReadFromDataTarget(m_target, addr, dest, count);
if (FAILED(hr))
return hr;

return S_OK;
}
7 changes: 3 additions & 4 deletions src/coreclr/debug/daccess/cdac.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CDAC final
CDAC(CDAC&& other)
: m_module{ other.m_module }
, m_cdac_handle{ other.m_cdac_handle }
, m_target{ other.m_target }
, m_target{ other.m_target.Extract() }
, m_sos{ other.m_sos.Extract() }
{
other.m_module = NULL;
Expand All @@ -34,7 +34,7 @@ class CDAC final
{
m_module = other.m_module;
m_cdac_handle = other.m_cdac_handle;
m_target = other.m_target;
m_target = other.m_target.Extract();
m_sos = other.m_sos.Extract();

other.m_module = NULL;
Expand All @@ -54,15 +54,14 @@ class CDAC final

// This does not AddRef the returned interface
IUnknown* SosInterface();
int ReadFromTarget(uint64_t addr, uint8_t* dest, uint32_t count);

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

private:
HMODULE m_module;
intptr_t m_cdac_handle;
ICorDebugDataTarget* m_target;
NonVMComHolder<ICorDebugDataTarget> m_target;
NonVMComHolder<IUnknown> m_sos;
};

Expand Down
10 changes: 7 additions & 3 deletions src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <dactablerva.h>
#else
extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress);
// cDAC depends on symbol lookup to find the contract descriptor
#define CAN_USE_CDAC
#endif

#include "dwbucketmanager.hpp"
Expand Down Expand Up @@ -5493,15 +5495,16 @@ ClrDataAccess::Initialize(void)
IfFailRet(GetDacGlobalValues());
IfFailRet(DacGetHostVtPtrs());

// TODO: [cdac] TryGetSymbol is only implemented for Linux, OSX, and Windows.
#ifdef CAN_USE_CDAC
CLRConfigNoCache enable = CLRConfigNoCache::Get("ENABLE_CDAC");
if (enable.IsSet())
{
DWORD val;
if (enable.TryAsInteger(10, val) && val == 1)
{
// TODO: [cdac] Get contract descriptor from exported symbol
uint64_t contractDescriptorAddr = 0;
//if (TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr))
if (TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr))
{
m_cdac = CDAC::Create(contractDescriptorAddr, m_pTarget);
if (m_cdac.IsValid())
Expand All @@ -5514,6 +5517,7 @@ ClrDataAccess::Initialize(void)
}
}
}
#endif

//
// DAC is now setup and ready to use
Expand Down Expand Up @@ -6946,7 +6950,7 @@ GetDacTableAddress(ICorDebugDataTarget* dataTarget, ULONG64 baseAddress, PULONG6
return E_INVALIDARG;
}
#endif
// On MacOS, FreeBSD or NetBSD use the RVA include file
// On FreeBSD, NetBSD, or SunOS use the RVA include file
*dacTableAddress = baseAddress + DAC_TABLE_RVA;
#else
// Otherwise, try to get the dac table address via the export symbol
Expand Down
21 changes: 18 additions & 3 deletions src/native/managed/cdacreader/src/ContractDescriptorParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand All @@ -23,13 +24,15 @@ public partial class ContractDescriptorParser
/// <summary>
/// Parses the "compact" representation of a contract descriptor.
/// </summary>
// Workaround for https://github.com/dotnet/runtime/issues/101205
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Root))]
public static ContractDescriptor? ParseCompact(ReadOnlySpan<byte> json)
{
return JsonSerializer.Deserialize(json, ContractDescriptorContext.Default.ContractDescriptor);
}

[JsonSerializable(typeof(ContractDescriptor))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(int?))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Dictionary<string, int>))]
[JsonSerializable(typeof(Dictionary<string, TypeDescriptor>))]
Expand All @@ -38,11 +41,17 @@ public partial class ContractDescriptorParser
[JsonSerializable(typeof(TypeDescriptor))]
[JsonSerializable(typeof(FieldDescriptor))]
[JsonSerializable(typeof(GlobalDescriptor))]
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
[JsonSourceGenerationOptions(AllowTrailingCommas = true,
DictionaryKeyPolicy = JsonKnownNamingPolicy.Unspecified, // contracts, types and globals are case sensitive
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
ReadCommentHandling = JsonCommentHandling.Skip)]
ReadCommentHandling = JsonCommentHandling.Skip,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement,
Converters = [typeof(TypeDescriptorConverter),
typeof(FieldDescriptorConverter),
typeof(GlobalDescriptorConverter)])]
internal sealed partial class ContractDescriptorContext : JsonSerializerContext
{
}
Expand All @@ -58,7 +67,13 @@ public class ContractDescriptor
public Dictionary<string, GlobalDescriptor>? Globals { get; set; }

[JsonExtensionData]
public Dictionary<string, object?>? Extras { get; set; }
public Dictionary<string, JsonElement>? Extras { get; set; }

public override string ToString()
{
return $"Version: {Version}, Baseline: {Baseline}, Contracts: {Contracts?.Count}, Types: {Types?.Count}, Globals: {Globals?.Count}";
}

}

[JsonConverter(typeof(TypeDescriptorConverter))]
Expand Down
5 changes: 4 additions & 1 deletion src/native/managed/cdacreader/src/Entrypoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ internal static class Entrypoints
[UnmanagedCallersOnly(EntryPoint = $"{CDAC}init")]
private static unsafe int Init(ulong descriptor, delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget, void* readContext, IntPtr* handle)
{
Target target = new(descriptor, readFromTarget, readContext);
// TODO: [cdac] Better error code/details
if (!Target.TryCreate(descriptor, readFromTarget, readContext, out Target? target))
return -1;

GCHandle gcHandle = GCHandle.Alloc(target);
*handle = GCHandle.ToIntPtr(gcHandle);
return 0;
Expand Down
12 changes: 12 additions & 0 deletions src/native/managed/cdacreader/src/Root.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;

namespace Microsoft.Diagnostics.DataContractReader;

internal static class Root
{
// https://github.com/dotnet/runtime/issues/101205
public static JsonDerivedTypeAttribute[] R1 = new JsonDerivedTypeAttribute[] { null! };
}
Loading

0 comments on commit 18602ea

Please sign in to comment.