Skip to content

Commit b6bfc5a

Browse files
authored
[cdac] Add helper methods to Target for reading UTF-8/16 strings (#106483)
1 parent 1f9e91d commit b6bfc5a

File tree

6 files changed

+87
-60
lines changed

6 files changed

+87
-60
lines changed

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ partial interface IRuntimeTypeSystem : IContract
125125
// generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
126126
// Or something else similar.
127127
// A no metadata method is also a StoredSigMethodDesc
128-
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan<byte> methodName);
128+
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out string methodName);
129129

130130
// A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata.
131131
public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan<byte> signature);
@@ -708,9 +708,9 @@ And the various apis are implemented with the following algorithms
708708
public uint GetMethodToken(MethodDescHandle methodDescHandle)
709709
{
710710
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
711-
711+
712712
TargetPointer methodDescChunk = // Using ChunkIndex from methodDesc, compute the wrapping MethodDescChunk
713-
713+
714714
ushort Flags3AndTokenRemainder = // Read Flags3AndTokenRemainder field from MethodDesc contract using address methodDescHandle.Address
715715
716716
ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk

src/native/managed/cdacreader/src/Contracts/Loader_1.cs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Collections.Generic;
6-
using System.Runtime.InteropServices;
75

86
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
97

@@ -39,26 +37,7 @@ ModuleFlags ILoader.GetFlags(ModuleHandle handle)
3937
string ILoader.GetPath(ModuleHandle handle)
4038
{
4139
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
42-
43-
// TODO: [cdac] Add/use APIs on Target for reading strings in target endianness
44-
TargetPointer addr = module.Path;
45-
while (true)
46-
{
47-
// Read characters until we find the null terminator
48-
char nameChar = _target.Read<char>(addr);
49-
if (nameChar == 0)
50-
break;
51-
52-
addr += sizeof(char);
53-
}
54-
55-
int length = (int)(addr.Value - module.Path.Value);
56-
if (length == 0)
57-
return string.Empty;
58-
59-
Span<byte> span = stackalloc byte[length];
60-
_target.ReadBuffer(module.Path, span);
61-
return new string(MemoryMarshal.Cast<byte, char>(span));
40+
return _target.ReadUtf16String(module.Path);
6241
}
6342

6443
TargetPointer ILoader.GetLoaderAllocator(ModuleHandle handle)

src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Reflection.Metadata.Ecma335;
65

76
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
87

@@ -148,11 +147,11 @@ static IContract IContract.Create(Target target, int version)
148147
// An array method is also a StoredSigMethodDesc
149148
public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType) => throw new NotImplementedException();
150149

151-
// Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically
150+
// Return true if a MethodDesc represents a method without metadata, either an IL Stub dynamically
152151
// generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
153152
// Or something else similar.
154153
// A no metadata method is also a StoredSigMethodDesc
155-
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan<byte> methodName) => throw new NotImplementedException();
154+
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out string methodName) => throw new NotImplementedException();
156155
// A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata.
157156
public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan<byte> signature) => throw new NotImplementedException();
158157

src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.Reflection.Metadata.Ecma335;
7-
using Microsoft.Diagnostics.DataContractReader.Data;
88
using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS;
9-
using System.Diagnostics;
10-
using System.Text;
11-
using System.Reflection;
9+
using Microsoft.Diagnostics.DataContractReader.Data;
1210

1311
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
1412

@@ -195,34 +193,16 @@ private class DynamicMethodDesc : IData<DynamicMethodDesc>
195193
private DynamicMethodDesc(Target target, TargetPointer methodDescPointer)
196194
{
197195
_address = methodDescPointer;
198-
List<byte> nameBytes = new();
199196
_desc = target.ProcessedData.GetOrAdd<Data.DynamicMethodDesc>(methodDescPointer);
200197

201-
if (_desc.MethodName != TargetPointer.Null)
202-
{
203-
TargetPointer currentNameAddress = _desc.MethodName;
204-
do
205-
{
206-
byte nameByte = target.Read<byte>(currentNameAddress);
207-
208-
if (nameByte == 0)
209-
break;
210-
211-
nameBytes.Add(nameByte);
212-
currentNameAddress++;
213-
} while (true);
214-
215-
MethodName = nameBytes.ToArray();
216-
}
217-
else
218-
{
219-
MethodName = System.Array.Empty<byte>();
220-
}
198+
MethodName = _desc.MethodName != TargetPointer.Null
199+
? target.ReadUtf8String(_desc.MethodName)
200+
: string.Empty;
221201

222202
_storedSigDesc = target.ProcessedData.GetOrAdd<Data.StoredSigMethodDesc>(methodDescPointer);
223203
}
224204

225-
public byte[] MethodName { get; }
205+
public string MethodName { get; }
226206
public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags;
227207

228208
public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
@@ -712,13 +692,13 @@ public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionTy
712692
return true;
713693
}
714694

715-
public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> methodName)
695+
public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out string methodName)
716696
{
717697
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
718698

719699
if (methodDesc.Classification != MethodClassification.Dynamic)
720700
{
721-
methodName = default;
701+
methodName = string.Empty;
722702
return false;
723703
}
724704

src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder,
6565
{
6666
IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem;
6767
ILoader loader = target.Contracts.Loader;
68-
ReadOnlySpan<byte> methodNameSpan;
68+
string methodName;
6969
TypeHandle th = default;
7070
Contracts.ModuleHandle module = default;
7171

72-
bool isNoMetadataMethod = runtimeTypeSystem.IsNoMetadataMethod(method, out methodNameSpan);
72+
bool isNoMetadataMethod = runtimeTypeSystem.IsNoMetadataMethod(method, out methodName);
7373
if (isNoMetadataMethod)
7474
{
7575
if (runtimeTypeSystem.IsDynamicMethod(method))
@@ -91,7 +91,7 @@ public static void AppendMethodImpl(Target target, StringBuilder stringBuilder,
9191

9292
if (isNoMetadataMethod)
9393
{
94-
stringBuilder.Append(Encoding.UTF8.GetString(methodNameSpan));
94+
stringBuilder.Append(methodName);
9595
}
9696
else if (runtimeTypeSystem.IsArrayMethod(method, out ArrayFunctionType functionType))
9797
{

src/native/managed/cdacreader/src/Target.cs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.Diagnostics.CodeAnalysis;
8-
using System.Net;
98
using System.Numerics;
109
using System.Runtime.CompilerServices;
10+
using System.Text;
1111
using Microsoft.Diagnostics.DataContractReader.Data;
1212

1313
namespace Microsoft.Diagnostics.DataContractReader;
@@ -265,6 +265,12 @@ private static DataType GetDataType(string type)
265265

266266
public int PointerSize => _config.PointerSize;
267267

268+
/// <summary>
269+
/// Read a value from the target in target endianness
270+
/// </summary>
271+
/// <typeparam name="T">Type of value to read</typeparam>
272+
/// <param name="address">Address to start reading from</param>
273+
/// <returns>Value read from the target</returns>
268274
public T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
269275
{
270276
if (!TryRead(address, _config.IsLittleEndian, _reader, out T value))
@@ -319,6 +325,11 @@ private static bool IsSigned<T>() where T : struct, INumberBase<T>, IMinMaxValue
319325
return T.IsNegative(T.MinValue);
320326
}
321327

328+
/// <summary>
329+
/// Read a pointer from the target in target endianness
330+
/// </summary>
331+
/// <param name="address">Address to start reading from</param>
332+
/// <returns>Pointer read from the target</returns>}
322333
public TargetPointer ReadPointer(ulong address)
323334
{
324335
if (!TryReadPointer(address, _config, _reader, out TargetPointer pointer))
@@ -352,6 +363,64 @@ public void ReadPointers(ulong address, Span<TargetPointer> buffer)
352363
}
353364
}
354365

366+
/// <summary>
367+
/// Read a null-terminated UTF-8 string from the target
368+
/// </summary>
369+
/// <param name="address">Address to start reading from</param>
370+
/// <returns>String read from the target</returns>}
371+
public string ReadUtf8String(ulong address)
372+
{
373+
// Read characters until we find the null terminator
374+
ulong end = address;
375+
while (Read<byte>(end) != 0)
376+
{
377+
end += sizeof(byte);
378+
}
379+
380+
int length = (int)(end - address);
381+
if (length == 0)
382+
return string.Empty;
383+
384+
Span<byte> span = length <= StackAllocByteThreshold
385+
? stackalloc byte[length]
386+
: new byte[length];
387+
ReadBuffer(address, span);
388+
return Encoding.UTF8.GetString(span);
389+
}
390+
391+
/// <summary>
392+
/// Read a null-terminated UTF-16 string from the target in target endianness
393+
/// </summary>
394+
/// <param name="address">Address to start reading from</param>
395+
/// <returns>String read from the target</returns>}
396+
public string ReadUtf16String(ulong address)
397+
{
398+
// Read characters until we find the null terminator
399+
ulong end = address;
400+
while (Read<char>(end) != 0)
401+
{
402+
end += sizeof(char);
403+
}
404+
405+
int length = (int)(end - address);
406+
if (length == 0)
407+
return string.Empty;
408+
409+
Span<byte> span = length <= StackAllocByteThreshold
410+
? stackalloc byte[length]
411+
: new byte[length];
412+
ReadBuffer(address, span);
413+
string result = _config.IsLittleEndian
414+
? Encoding.Unicode.GetString(span)
415+
: Encoding.BigEndianUnicode.GetString(span);
416+
return result;
417+
}
418+
419+
/// <summary>
420+
/// Read a native unsigned integer from the target in target endianness
421+
/// </summary>
422+
/// <param name="address">Address to start reading from</param>
423+
/// <returns>Value read from the target</returns>
355424
public TargetNUInt ReadNUInt(ulong address)
356425
{
357426
if (!TryReadNUInt(address, _config, _reader, out ulong value))

0 commit comments

Comments
 (0)