Skip to content

Commit ac764af

Browse files
authored
Adding write capabilities and SetOtherNotificationFlags cDAC API (#118141)
* * adding target write capabilities to cDAC * implementing SetOtherNotificationFlags cDAC API * adding writebuffer * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> ---------
1 parent 6454b4c commit ac764af

31 files changed

+306
-70
lines changed

src/coreclr/debug/daccess/cdac.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ namespace
4343
return S_OK;
4444
}
4545

46+
int WriteToTargetCallback(uint64_t addr, const uint8_t* buff, uint32_t count, void* context)
47+
{
48+
ICorDebugMutableDataTarget* target = static_cast<ICorDebugMutableDataTarget*>(context);
49+
HRESULT hr = target->WriteVirtual((CORDB_ADDRESS)addr, buff, count);
50+
if (FAILED(hr))
51+
return hr;
52+
53+
return S_OK;
54+
}
55+
4656
int ReadThreadContext(uint32_t threadId, uint32_t contextFlags, uint32_t contextBufferSize, uint8_t* contextBuffer, void* context)
4757
{
4858
ICorDebugDataTarget* target = reinterpret_cast<ICorDebugDataTarget*>(context);
@@ -54,7 +64,7 @@ namespace
5464
}
5565
}
5666

57-
CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target, IUnknown* legacyImpl)
67+
CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugMutableDataTarget* target, IUnknown* legacyImpl)
5868
{
5969
HMODULE cdacLib;
6070
if (!TryLoadCDACLibrary(&cdacLib))
@@ -64,7 +74,7 @@ CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target, IUnknown
6474
_ASSERTE(init != nullptr);
6575

6676
intptr_t handle;
67-
if (init(descriptorAddr, &ReadFromTargetCallback, &ReadThreadContext, target, &handle) != 0)
77+
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContext, target, &handle) != 0)
6878
{
6979
::FreeLibrary(cdacLib);
7080
return {};

src/coreclr/debug/daccess/cdac.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class CDAC final
88
{
99
public: // static
10-
static CDAC Create(uint64_t descriptorAddr, ICorDebugDataTarget *pDataTarget, IUnknown* legacyImpl);
10+
static CDAC Create(uint64_t descriptorAddr, ICorDebugMutableDataTarget *pDataTarget, IUnknown* legacyImpl);
1111

1212
public:
1313
CDAC() = default;

src/coreclr/debug/daccess/daccess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7033,7 +7033,7 @@ CLRDataCreateInstance(REFIID iid,
70337033
HRESULT qiRes = pClrDataAccess->QueryInterface(IID_IUnknown, (void**)&thisImpl);
70347034
_ASSERTE(SUCCEEDED(qiRes));
70357035
CDAC& cdac = pClrDataAccess->m_cdac;
7036-
cdac = CDAC::Create(contractDescriptorAddr, pClrDataAccess->m_pTarget, thisImpl);
7036+
cdac = CDAC::Create(contractDescriptorAddr, pClrDataAccess->m_pMutableTarget, thisImpl);
70377037
if (cdac.IsValid())
70387038
{
70397039
// Get SOS interfaces from the cDAC if available.

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Target.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ public abstract class Target
7272
/// <param name="buffer">Destination to copy the bytes, the number of bytes to read is the span length</param>
7373
public abstract void ReadBuffer(ulong address, Span<byte> buffer);
7474

75+
/// <summary>
76+
/// Write some bytes to the target
77+
/// </summary>
78+
/// <param name="address">The address where to start writing</param>
79+
/// <param name="buffer">Source of the bytes to write, the number of bytes to write is the span length</param>
80+
public abstract void WriteBuffer(ulong address, Span<byte> buffer);
81+
7582
/// <summary>
7683
/// Read a null-terminated UTF-8 string from the target
7784
/// </summary>
@@ -133,6 +140,15 @@ public abstract class Target
133140
/// <returns>Value read from the target</returns>
134141
public abstract T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
135142

143+
/// <summary>
144+
/// Write a value to the target in target endianness
145+
/// </summary>
146+
/// <typeparam name="T">Type of value to write</typeparam>
147+
/// <param name="address">Address to start writing to</param>
148+
/// <param name="value">Value to write</param>
149+
/// <returns>True if the write was successful, false otherwise</returns>
150+
public abstract bool Write<T>(ulong address, T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
151+
136152
/// <summary>
137153
/// Read a target pointer from a span of bytes
138154
/// </summary>

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ private readonly struct Configuration
3232
}
3333

3434
private readonly Configuration _config;
35-
private readonly Reader _reader;
3635

36+
private readonly DataTargetDelegates _dataTargetDelegates;
3737
private readonly Dictionary<string, int> _contracts = [];
3838
private readonly IReadOnlyDictionary<string, GlobalValue> _globals = new Dictionary<string, GlobalValue>();
3939
private readonly Dictionary<DataType, Target.TypeInfo> _knownTypes = [];
@@ -43,6 +43,7 @@ private readonly struct Configuration
4343
public override DataCache ProcessedData { get; }
4444

4545
public delegate int ReadFromTargetDelegate(ulong address, Span<byte> bufferToFill);
46+
public delegate int WriteToTargetDelegate(ulong address, Span<byte> bufferToWrite);
4647
public delegate int GetTargetThreadContextDelegate(uint threadId, uint contextFlags, Span<byte> bufferToFill);
4748

4849
/// <summary>
@@ -56,18 +57,19 @@ private readonly struct Configuration
5657
public static bool TryCreate(
5758
ulong contractDescriptor,
5859
ReadFromTargetDelegate readFromTarget,
60+
WriteToTargetDelegate writeToTarget,
5961
GetTargetThreadContextDelegate getThreadContext,
6062
[NotNullWhen(true)] out ContractDescriptorTarget? target)
6163
{
62-
Reader reader = new Reader(readFromTarget, getThreadContext);
64+
DataTargetDelegates dataTargetDelegates = new DataTargetDelegates(readFromTarget, writeToTarget, getThreadContext);
6365
if (TryReadContractDescriptor(
6466
contractDescriptor,
65-
reader,
67+
dataTargetDelegates,
6668
out Configuration config,
6769
out ContractDescriptorParser.ContractDescriptor? descriptor,
6870
out TargetPointer[] pointerData))
6971
{
70-
target = new ContractDescriptorTarget(config, descriptor!, pointerData, reader);
72+
target = new ContractDescriptorTarget(config, descriptor!, pointerData, dataTargetDelegates);
7173
return true;
7274
}
7375

@@ -89,6 +91,7 @@ public static ContractDescriptorTarget Create(
8991
ContractDescriptorParser.ContractDescriptor contractDescriptor,
9092
TargetPointer[] globalPointerValues,
9193
ReadFromTargetDelegate readFromTarget,
94+
WriteToTargetDelegate writeToTarget,
9295
GetTargetThreadContextDelegate getThreadContext,
9396
bool isLittleEndian,
9497
int pointerSize)
@@ -97,15 +100,15 @@ public static ContractDescriptorTarget Create(
97100
new Configuration { IsLittleEndian = isLittleEndian, PointerSize = pointerSize },
98101
contractDescriptor,
99102
globalPointerValues,
100-
new Reader(readFromTarget, getThreadContext));
103+
new DataTargetDelegates(readFromTarget, writeToTarget, getThreadContext));
101104
}
102105

103-
private ContractDescriptorTarget(Configuration config, ContractDescriptorParser.ContractDescriptor descriptor, TargetPointer[] pointerData, Reader reader)
106+
private ContractDescriptorTarget(Configuration config, ContractDescriptorParser.ContractDescriptor descriptor, TargetPointer[] pointerData, DataTargetDelegates dataTargetDelegates)
104107
{
105108
Contracts = new CachingContractRegistry(this, this.TryGetContractVersion);
106109
ProcessedData = new DataCache(this);
107110
_config = config;
108-
_reader = reader;
111+
_dataTargetDelegates = dataTargetDelegates;
109112

110113
_contracts = descriptor.Contracts ?? [];
111114

@@ -187,7 +190,7 @@ private struct GlobalValue
187190
// See docs/design/datacontracts/contract-descriptor.md
188191
private static bool TryReadContractDescriptor(
189192
ulong address,
190-
Reader reader,
193+
DataTargetDelegates dataTargetDelegates,
191194
out Configuration config,
192195
out ContractDescriptorParser.ContractDescriptor? descriptor,
193196
out TargetPointer[] pointerData)
@@ -198,7 +201,7 @@ private static bool TryReadContractDescriptor(
198201

199202
// Magic - uint64_t
200203
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
201-
if (reader.ReadFromTarget(address, buffer) < 0)
204+
if (dataTargetDelegates.ReadFromTarget(address, buffer) < 0)
202205
return false;
203206

204207
address += sizeof(ulong);
@@ -209,7 +212,7 @@ private static bool TryReadContractDescriptor(
209212
return false;
210213

211214
// Flags - uint32_t
212-
if (!TryRead(address, isLittleEndian, reader, out uint flags))
215+
if (!TryRead(address, isLittleEndian, dataTargetDelegates, out uint flags))
213216
return false;
214217

215218
address += sizeof(uint);
@@ -220,19 +223,19 @@ private static bool TryReadContractDescriptor(
220223
config = new Configuration { IsLittleEndian = isLittleEndian, PointerSize = pointerSize };
221224

222225
// Descriptor size - uint32_t
223-
if (!TryRead(address, config.IsLittleEndian, reader, out uint descriptorSize))
226+
if (!TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint descriptorSize))
224227
return false;
225228

226229
address += sizeof(uint);
227230

228231
// Descriptor - char*
229-
if (!TryReadPointer(address, config, reader, out TargetPointer descriptorAddr))
232+
if (!TryReadPointer(address, config, dataTargetDelegates, out TargetPointer descriptorAddr))
230233
return false;
231234

232235
address += (uint)pointerSize;
233236

234237
// Pointer data count - uint32_t
235-
if (!TryRead(address, config.IsLittleEndian, reader, out uint pointerDataCount))
238+
if (!TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint pointerDataCount))
236239
return false;
237240

238241
address += sizeof(uint);
@@ -241,14 +244,14 @@ private static bool TryReadContractDescriptor(
241244
address += sizeof(uint);
242245

243246
// Pointer data - uintptr_t*
244-
if (!TryReadPointer(address, config, reader, out TargetPointer pointerDataAddr))
247+
if (!TryReadPointer(address, config, dataTargetDelegates, out TargetPointer pointerDataAddr))
245248
return false;
246249

247250
// Read descriptor
248251
Span<byte> descriptorBuffer = descriptorSize <= StackAllocByteThreshold
249252
? stackalloc byte[(int)descriptorSize]
250253
: new byte[(int)descriptorSize];
251-
if (reader.ReadFromTarget(descriptorAddr.Value, descriptorBuffer) < 0)
254+
if (dataTargetDelegates.ReadFromTarget(descriptorAddr.Value, descriptorBuffer) < 0)
252255
return false;
253256

254257
descriptor = ContractDescriptorParser.ParseCompact(descriptorBuffer);
@@ -259,7 +262,7 @@ private static bool TryReadContractDescriptor(
259262
pointerData = new TargetPointer[pointerDataCount];
260263
for (int i = 0; i < pointerDataCount; i++)
261264
{
262-
if (!TryReadPointer(pointerDataAddr.Value + (uint)(i * pointerSize), config, reader, out pointerData[i]))
265+
if (!TryReadPointer(pointerDataAddr.Value + (uint)(i * pointerSize), config, dataTargetDelegates, out pointerData[i]))
263266
return false;
264267
}
265268

@@ -280,7 +283,7 @@ private static DataType GetDataType(string type)
280283
public override bool TryGetThreadContext(ulong threadId, uint contextFlags, Span<byte> buffer)
281284
{
282285
// Underlying API only supports 32-bit thread IDs, mask off top 32 bits
283-
int hr = _reader.GetThreadContext((uint)(threadId & uint.MaxValue), contextFlags, buffer);
286+
int hr = _dataTargetDelegates.GetThreadContext((uint)(threadId & uint.MaxValue), contextFlags, buffer);
284287
return hr == 0;
285288
}
286289

@@ -292,24 +295,50 @@ public override bool TryGetThreadContext(ulong threadId, uint contextFlags, Span
292295
/// <returns>Value read from the target</returns>
293296
public override T Read<T>(ulong address)
294297
{
295-
if (!TryRead(address, _config.IsLittleEndian, _reader, out T value))
298+
if (!TryRead(address, _config.IsLittleEndian, _dataTargetDelegates, out T value))
296299
throw new InvalidOperationException($"Failed to read {typeof(T)} at 0x{address:x8}.");
297300

298301
return value;
299302
}
300303

301-
private static bool TryRead<T>(ulong address, bool isLittleEndian, Reader reader, out T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
304+
private static bool TryRead<T>(ulong address, bool isLittleEndian, DataTargetDelegates dataTargetDelegates, out T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
302305
{
303306
value = default;
304307
Span<byte> buffer = stackalloc byte[sizeof(T)];
305-
if (reader.ReadFromTarget(address, buffer) < 0)
308+
if (dataTargetDelegates.ReadFromTarget(address, buffer) < 0)
306309
return false;
307310

308311
return isLittleEndian
309312
? T.TryReadLittleEndian(buffer, !IsSigned<T>(), out value)
310313
: T.TryReadBigEndian(buffer, !IsSigned<T>(), out value);
311314
}
312315

316+
/// <summary>
317+
/// Write a value to the target in target endianness
318+
/// </summary>
319+
/// <typeparam name="T">Type of value to write</typeparam>
320+
/// <param name="address">Address to start writing to</param>
321+
/// <returns>True if the value is successfully written. Throws an InvalidOperationException otherwise.</returns>
322+
public override bool Write<T>(ulong address, T value)
323+
{
324+
if (!TryWrite(address, _config.IsLittleEndian, _dataTargetDelegates, value))
325+
throw new InvalidOperationException($"Failed to write {typeof(T)} at 0x{address:x8}.");
326+
return true;
327+
}
328+
329+
private static bool TryWrite<T>(ulong address, bool isLittleEndian, DataTargetDelegates dataTargetDelegates, T value) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
330+
{
331+
Span<byte> buffer = stackalloc byte[sizeof(T)];
332+
int bytesWritten = default;
333+
bool success = isLittleEndian
334+
? value.TryWriteLittleEndian(buffer, out bytesWritten)
335+
: value.TryWriteBigEndian(buffer, out bytesWritten);
336+
if (!success || bytesWritten != buffer.Length || dataTargetDelegates.WriteToTarget(address, buffer) < 0)
337+
return false;
338+
339+
return true;
340+
}
341+
313342
private static T Read<T>(ReadOnlySpan<byte> bytes, bool isLittleEndian) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
314343
{
315344
if (sizeof(T) != bytes.Length)
@@ -335,7 +364,18 @@ public override void ReadBuffer(ulong address, Span<byte> buffer)
335364

336365
private bool TryReadBuffer(ulong address, Span<byte> buffer)
337366
{
338-
return _reader.ReadFromTarget(address, buffer) >= 0;
367+
return _dataTargetDelegates.ReadFromTarget(address, buffer) >= 0;
368+
}
369+
370+
public override void WriteBuffer(ulong address, Span<byte> buffer)
371+
{
372+
if (!TryWriteBuffer(address, buffer))
373+
throw new InvalidOperationException($"Failed to write {buffer.Length} bytes at 0x{address:x8}.");
374+
}
375+
376+
private bool TryWriteBuffer(ulong address, Span<byte> buffer)
377+
{
378+
return _dataTargetDelegates.WriteToTarget(address, buffer) >= 0;
339379
}
340380

341381
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -351,7 +391,7 @@ private static bool IsSigned<T>() where T : struct, INumberBase<T>, IMinMaxValue
351391
/// <returns>Pointer read from the target</returns>}
352392
public override TargetPointer ReadPointer(ulong address)
353393
{
354-
if (!TryReadPointer(address, _config, _reader, out TargetPointer pointer))
394+
if (!TryReadPointer(address, _config, _dataTargetDelegates, out TargetPointer pointer))
355395
throw new InvalidOperationException($"Failed to read pointer at 0x{address:x8}.");
356396

357397
return pointer;
@@ -456,33 +496,33 @@ public override string ReadUtf16String(ulong address)
456496
/// <returns>Value read from the target</returns>
457497
public override TargetNUInt ReadNUInt(ulong address)
458498
{
459-
if (!TryReadNUInt(address, _config, _reader, out ulong value))
499+
if (!TryReadNUInt(address, _config, _dataTargetDelegates, out ulong value))
460500
throw new InvalidOperationException($"Failed to read nuint at 0x{address:x8}.");
461501

462502
return new TargetNUInt(value);
463503
}
464504

465-
private static bool TryReadPointer(ulong address, Configuration config, Reader reader, out TargetPointer pointer)
505+
private static bool TryReadPointer(ulong address, Configuration config, DataTargetDelegates dataTargetDelegates, out TargetPointer pointer)
466506
{
467507
pointer = TargetPointer.Null;
468-
if (!TryReadNUInt(address, config, reader, out ulong value))
508+
if (!TryReadNUInt(address, config, dataTargetDelegates, out ulong value))
469509
return false;
470510

471511
pointer = new TargetPointer(value);
472512
return true;
473513
}
474514

475-
private static bool TryReadNUInt(ulong address, Configuration config, Reader reader, out ulong value)
515+
private static bool TryReadNUInt(ulong address, Configuration config, DataTargetDelegates dataTargetDelegates, out ulong value)
476516
{
477517
value = 0;
478518
if (config.PointerSize == sizeof(uint)
479-
&& TryRead(address, config.IsLittleEndian, reader, out uint value32))
519+
&& TryRead(address, config.IsLittleEndian, dataTargetDelegates, out uint value32))
480520
{
481521
value = value32;
482522
return true;
483523
}
484524
else if (config.PointerSize == sizeof(ulong)
485-
&& TryRead(address, config.IsLittleEndian, reader, out ulong value64))
525+
&& TryRead(address, config.IsLittleEndian, dataTargetDelegates, out ulong value64))
486526
{
487527
value = value64;
488528
return true;
@@ -657,8 +697,9 @@ public void Clear()
657697
}
658698
}
659699

660-
private readonly struct Reader(
700+
private readonly struct DataTargetDelegates(
661701
ReadFromTargetDelegate readFromTarget,
702+
WriteToTargetDelegate writeToTarget,
662703
GetTargetThreadContextDelegate getThreadContext)
663704
{
664705
public int ReadFromTarget(ulong address, Span<byte> buffer)
@@ -673,5 +714,9 @@ public int GetThreadContext(uint threadId, uint contextFlags, Span<byte> buffer)
673714
{
674715
return getThreadContext(threadId, contextFlags, buffer);
675716
}
717+
public int WriteToTarget(ulong address, Span<byte> buffer)
718+
{
719+
return writeToTarget(address, buffer);
720+
}
676721
}
677722
}

src/native/managed/cdac/inc/cdac_reader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C"
1818
int cdac_reader_init(
1919
uint64_t descriptor,
2020
int(*read_from_target)(uint64_t, uint8_t*, uint32_t, void*),
21+
int(*write_to_target)(uint64_t, const uint8_t*, uint32_t, void*),
2122
int(*read_thread_context)(uint32_t, uint32_t, uint32_t, uint8_t*, void*),
2223
void* read_context,
2324
/*out*/ intptr_t* handle);

0 commit comments

Comments
 (0)