未验证 提交 5e3dc062 编写于 作者: T Thays Grazia 提交者: GitHub

[mono][debugger] Support debug inline array (#90026)

* Support debug inline array for wasm

* Removing extra tab

* Changing behavior to use the same protocol version on wasm and on debugger-libs.

* Revert last changes

* Adding changes for debugger-libs support

* Change where we send the information about inline array.

* fix wasm compilation

* Addressing @radical comments
Fixing other tests.

* Fix wrong change.

* Adding more tests as suggested by @radical
上级 e35c8214
......@@ -5269,6 +5269,13 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
continue;
nfields ++;
}
if (CHECK_PROTOCOL_VERSION(2, 65)) {
if (m_class_is_inlinearray (klass) && nfields == 1)
buffer_add_int (buf, m_class_inlinearray_value (klass));
else
buffer_add_int (buf, -1);
}
buffer_add_int (buf, nfields);
iter = NULL;
......@@ -5290,6 +5297,14 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
}
buffer_add_value_full (buf, f->type, mono_vtype_get_field_addr (addr, f), domain, FALSE, parent_vtypes, len_fixed_array != 1 ? len_fixed_array : isFixedSizeArray(f));
if (CHECK_PROTOCOL_VERSION(2, 65)) {
if (m_class_is_inlinearray (klass) && nfields == 1) {
int element_size = mono_class_instance_size (mono_class_from_mono_type_internal (f->type)) - MONO_ABI_SIZEOF (MonoObject);
int array_size = m_class_inlinearray_value (klass);
for (int i = 1; i < array_size; i++)
buffer_add_value_full (buf, f->type, ((char*)mono_vtype_get_field_addr (addr, f)) + (i*element_size), domain, FALSE, parent_vtypes, len_fixed_array != 1 ? len_fixed_array : isFixedSizeArray(f));
}
}
}
if (boxed_vtype) {
......@@ -5349,6 +5364,8 @@ decode_vtype (MonoType *t, MonoDomain *domain, gpointer void_addr, gpointer void
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit);
klass = decode_typeid (buf, &buf, limit, &d, &err);
if (CHECK_PROTOCOL_VERSION(2, 65))
decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
return err;
......@@ -5453,6 +5470,8 @@ decode_vtype_compute_size (MonoType *t, MonoDomain *domain, gpointer void_buf, g
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit);
klass = decode_typeid (buf, &buf, limit, &d, &err);
if (CHECK_PROTOCOL_VERSION(2, 65))
decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
goto end;
......@@ -5580,6 +5599,8 @@ decode_value_compute_size (MonoType *t, int type, MonoDomain *domain, guint8 *bu
decode_byte (buf, &buf, limit);
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit); //ignore is boxed
if (CHECK_PROTOCOL_VERSION(2, 65))
decode_int (buf, &buf, limit); //ignore inline array
decode_typeid (buf, &buf, limit, &d, &err);
ret += decode_vtype_compute_size (NULL, domain, buf, &buf, limit, from_by_ref_value_type);
} else {
......@@ -5757,6 +5778,8 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit); //ignore is boxed
klass = decode_typeid (buf, &buf, limit, &d, &err);
if (CHECK_PROTOCOL_VERSION(2, 65))
decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
return err;
......@@ -9155,6 +9178,11 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
buffer_add_int (buf, header->code_size);
}
}
if (CHECK_PROTOCOL_VERSION (2, 65)) {
for (int i = 0; i < num_locals; ++i) {
buffer_add_int (buf, locals->locals [i].index);
}
}
}
mono_metadata_free_mh (header);
......@@ -9914,7 +9942,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
pos = - pos - 1;
cmd_stack_frame_get_parameter (frame, sig, pos, buf, jit);
} else {
if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from newer protocol versions it's sent the pdb index
if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from older protocol versions it's sent the pdb index
MonoDebugLocalsInfo *locals;
locals = mono_debug_lookup_locals (frame->de.method);
if (locals) {
......@@ -9970,7 +9998,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
var = &jit->params [pos];
is_arg = TRUE;
} else {
if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from newer protocol versions it's sent the pdb index
if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from older protocol versions it's sent the pdb index
MonoDebugLocalsInfo *locals;
locals = mono_debug_lookup_locals (frame->de.method);
if (locals) {
......
......@@ -11,7 +11,7 @@
*/
#define MAJOR_VERSION 2
#define MINOR_VERSION 64
#define MINOR_VERSION 65
typedef enum {
MDBGPROT_CMD_COMPOSITE = 100
......
......@@ -317,6 +317,10 @@ private async Task<JObject> ReadAsObjectValue(MonoBinaryReader retDebuggerCmdRea
var isBoxed = retDebuggerCmdReader.ReadByte() == 1;
var typeId = retDebuggerCmdReader.ReadInt32();
var className = await _sdbAgent.GetTypeName(typeId, token);
var inlineArraySize = -1;
(int MajorVersion, int MinorVersion) = await _sdbAgent.GetVMVersion(token);
if (MajorVersion == 2 && MinorVersion >= 65)
inlineArraySize = retDebuggerCmdReader.ReadInt32();
var numValues = retDebuggerCmdReader.ReadInt32();
if (className.StartsWith("System.Nullable<", StringComparison.Ordinal)) //should we call something on debugger-agent to check???
......@@ -347,6 +351,7 @@ private async Task<JObject> ReadAsObjectValue(MonoBinaryReader retDebuggerCmdRea
typeId,
isEnum,
includeStatic,
inlineArraySize,
token);
_valueTypes[valueType.Id.Value] = valueType;
return await valueType.ToJObject(_sdbAgent, forDebuggerDisplayAttribute, token);
......
......@@ -395,6 +395,15 @@ public async Task<JObject> Resolve(ElementAccessExpressionSyntax elementAccess,
switch (objectId.Scheme)
{
case "valuetype": //can be an inlined array
{
if (!context.SdbAgent.ValueCreator.TryGetValueTypeById(objectId.Value, out ValueTypeClass valueType))
throw new InvalidOperationException($"Cannot apply indexing with [] to an expression of scheme '{objectId.Scheme}'");
var typeInfo = await context.SdbAgent.GetTypeInfo(valueType.TypeId, token);
if (int.TryParse(elementIdxInfo.ElementIdxStr, out elementIdx) && elementIdx >= 0 && elementIdx < valueType.InlineArray.Count)
return (JObject)valueType.InlineArray[elementIdx]["value"];
throw new InvalidOperationException($"Index is outside the bounds of the inline array");
}
case "array":
rootObject["value"] = await context.SdbAgent.GetArrayValues(objectId.Value, token);
if (!elementIdxInfo.IsMultidimensional)
......
......@@ -818,7 +818,7 @@ internal sealed partial class MonoSDBHelper
private static int debuggerObjectId;
private static int cmdId = 1; //cmdId == 0 is used by events which come from runtime
private const int MINOR_VERSION = 62;
private const int MINOR_VERSION = 65;
private const int MAJOR_VERSION = 2;
private int VmMinorVersion { get; set; }
......
......@@ -24,13 +24,13 @@ internal sealed class ValueTypeClass
private bool fieldsExpanded;
private readonly string className;
private JArray fields;
public List<JObject> InlineArray { get; init; }
public DotnetObjectId Id { get; init; }
public byte[] Buffer { get; init; }
public int TypeId { get; init; }
public bool IsEnum { get; init; }
public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId, bool isEnum)
public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId, bool isEnum, List<JObject> inlineArray = null)
{
var valueTypeId = MonoSDBHelper.GetNewObjectId();
var objectId = new DotnetObjectId("valuetype", valueTypeId);
......@@ -42,6 +42,7 @@ public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId
autoExpand = ShouldAutoExpand(className);
Id = objectId;
IsEnum = isEnum;
InlineArray = inlineArray;
}
public override string ToString() => $"{{ ValueTypeClass: typeId: {TypeId}, Id: {Id}, Id: {Id}, fields: {fields} }}";
......@@ -54,6 +55,7 @@ public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId
int typeId,
bool isEnum,
bool includeStatic,
int inlineArraySize,
CancellationToken token)
{
var typeInfo = await sdbAgent.GetTypeInfo(typeId, token);
......@@ -63,6 +65,8 @@ public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId
IReadOnlyList<FieldTypeClass> fieldTypes = await sdbAgent.GetTypeFields(typeId, token);
JArray fields = new();
List<JObject> inlineArray = null;
JObject lastWritableFieldValue = null;
if (includeStatic)
{
IEnumerable<FieldTypeClass> staticFields =
......@@ -77,20 +81,30 @@ public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId
IEnumerable<FieldTypeClass> writableFields = fieldTypes
.Where(f => !f.Attributes.HasFlag(FieldAttributes.Literal)
&& !f.Attributes.HasFlag(FieldAttributes.Static));
foreach (var field in writableFields)
{
var fieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, true, field.TypeId, false);
fields.Add(GetFieldWithMetadata(field, fieldValue, isStatic: false));
lastWritableFieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, isOwn: true, field.TypeId, forDebuggerDisplayAttribute: false);
fields.Add(GetFieldWithMetadata(field, lastWritableFieldValue, isStatic: false));
}
if (inlineArraySize > 0)
{
inlineArray = new(inlineArraySize+1);
inlineArray.Add(lastWritableFieldValue);
var firstFieldtypeId = writableFields.First().TypeId;
for (int i = 1; i < inlineArraySize; i++)
{
//the valuetype has a single instance field in inline-arrays
var inlineArrayItem = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, $"{i}", token, isOwn: true, firstFieldtypeId, forDebuggerDisplayAttribute: false);
inlineArray.Add(inlineArrayItem);
}
}
long endPos = cmdReader.BaseStream.Position;
cmdReader.BaseStream.Position = initialPos;
byte[] valueTypeBuffer = new byte[endPos - initialPos];
cmdReader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos));
cmdReader.BaseStream.Position = endPos;
return new ValueTypeClass(valueTypeBuffer, className, fields, typeId, isEnum);
return new ValueTypeClass(valueTypeBuffer, className, fields, typeId, isEnum, inlineArray);
JObject GetFieldWithMetadata(FieldTypeClass field, JObject fieldValue, bool isStatic)
{
......
......@@ -670,5 +670,100 @@ public async Task InspectPrimitiveTypeMultiArrayLocals(bool use_cfo)
CheckNumber(int_arr_3, "1, 2, 1", 121);
CheckNumber(int_arr_3, "1, 2, 2", 122);
}
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData("s")]
[InlineData("classWithInlineArrayField.myInlineArray")]
[InlineData("classWithInlineArrayField.InlineArrayProp")]
[InlineData("classWithInlineArrayField.myStructWithInlineArray.myInlineArray")]
public async Task InspectInlineArray(string varName)
{
var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs";
var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
$"'[debugger-test] DebuggerTests.InlineArray:run'" +
"); }, 1);";
var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, 441, 12, "DebuggerTests.InlineArray.run");
var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
await EvaluateOnCallFrameAndCheck(id,
($"{varName}[0]", TObject("DebuggerTests.InlineArray.E")),
($"{varName}[a]", TObject("DebuggerTests.InlineArray.E")),
($"{varName}[b]", TObject("DebuggerTests.InlineArray.E")),
($"{varName}[1]", TObject("DebuggerTests.InlineArray.E")));
var (_, res) = await EvaluateOnCallFrame(id,$"{varName}[43]", expect_ok: false);
Assert.Equal($"Unable to evaluate element access '{varName}[43]': Index is outside the bounds of the inline array", res.Error["result"]?["description"]?.Value<string>());
var (s_zero, _) = await EvaluateOnCallFrame(id, $"{varName}[0]");
await CheckValue(s_zero, TObject("DebuggerTests.InlineArray.E"), nameof(s_zero));
var s_zero_props = await GetProperties(s_zero["objectId"]?.Value<string>());
await CheckProps(s_zero_props, new
{
x = TNumber(1),
y = TNumber(2),
o = TObject("DebuggerTests.InlineArray.One")
}, "s_zero_props#1");
var (s_one, _) = await EvaluateOnCallFrame(id, $"{varName}[1]");
await CheckValue(s_one, TObject("DebuggerTests.InlineArray.E"), nameof(s_one));
var ss_one_props = await GetProperties(s_one["objectId"]?.Value<string>());
await CheckProps(ss_one_props, new
{
x = TNumber(3),
y = TNumber(4),
o = TObject("DebuggerTests.InlineArray.Two")
}, "s_one_props#1");
}
[Fact]
public async Task InspectInlineArray2()
{
var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs";
var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
$"'[debugger-test] DebuggerTests.InlineArray:run2'" +
"); }, 1);";
var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, 459, 12, "DebuggerTests.InlineArray.run2");
var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
await EvaluateOnCallFrameAndCheck(id,
($"a2[0]", TNumber(1)),
($"a3[0]", TNumber(2)),
($"a4[0]", TObject("DebuggerTests.InlineArray.E")),
($"Arr4.myStaticField", TNumber(50))
);
var (_, res) = await EvaluateOnCallFrame(id,$"a3[1]", expect_ok: false);
Assert.Equal($"Unable to evaluate element access 'a3[1]': Index is outside the bounds of the inline array", res.Error["result"]?["description"]?.Value<string>());
var (s_zero, _) = await EvaluateOnCallFrame(id, $"a4[0]");
await CheckValue(s_zero, TObject("DebuggerTests.InlineArray.E"), nameof(s_zero));
var s_zero_props = await GetProperties(s_zero["objectId"]?.Value<string>());
await CheckProps(s_zero_props, new
{
x = TNumber(1),
y = TNumber(2),
o = TObject("DebuggerTests.InlineArray.One")
}, "s_zero_props#1");
var (s_one, _) = await EvaluateOnCallFrame(id, $"a4[1]");
await CheckValue(s_one, TObject("DebuggerTests.InlineArray.E"), nameof(s_one));
var ss_one_props = await GetProperties(s_one["objectId"]?.Value<string>());
await CheckProps(ss_one_props, new
{
x = TNumber(3),
y = TNumber(4),
o = TObject("DebuggerTests.InlineArray.Two")
}, "s_one_props#1");
var (s_two, _) = await EvaluateOnCallFrame(id, $"a4");
await CheckValue(s_two, TObject("DebuggerTests.InlineArray.Arr4"), nameof(s_two));
var s_two_props = await GetProperties(s_two["objectId"]?.Value<string>());
await CheckProps(s_two_props, new
{
myStaticField = TNumber(50),
e = TObject("DebuggerTests.InlineArray.E"),
Length = TNumber(42)
}, "s_two_props#1");
}
}
}
......@@ -345,4 +345,119 @@ public static void run()
Console.WriteLine($"int_arr: {int_arr_3.Length}");
}
}
public class InlineArray
{
struct StructWithInlineArray
{
public Arr1 myInlineArray;
}
class ClassWithInlineArrayField
{
public Arr1 myInlineArray;
public Arr1 InlineArrayProp => myInlineArray;
public StructWithInlineArray myStructWithInlineArray;
}
class One {}
class Two {}
class Three {}
class Four {}
struct E
{
public int x;
public int y;
public object o;
}
[System.Runtime.CompilerServices.InlineArray(Length)]
struct Arr1
{
public const int Length = 42;
public E e;
}
[System.Runtime.CompilerServices.InlineArray(Length)]
struct Arr2
{
public const int Length = 42;
public int e;
}
[System.Runtime.CompilerServices.InlineArray(1)]
struct Arr3
{
public int e;
}
[System.Runtime.CompilerServices.InlineArray(Length)]
struct Arr4
{
public static int myStaticField = 50;
public const int Length = 42;
public E e;
}
private static Arr1 Initialize(Arr1 s)
{
s[0].o = new One();
s[0].x = 1;
s[0].y = 2;
s[1].o = new Two();
s[1].x = 3;
s[1].y = 4;
s[2].o = new Three();
s[2].x = 5;
s[2].y = 6;
s[3].o = new Four();
s[3].x = 7;
s[3].y = 8;
return s;
}
public static void run()
{
int a = 0;
int b = 1;
Arr1 s = default;
s = Initialize(s);
ClassWithInlineArrayField classWithInlineArrayField = new ();
s = Initialize(classWithInlineArrayField.myInlineArray);
classWithInlineArrayField.myInlineArray[0].o = new One();
classWithInlineArrayField.myInlineArray[0].x = 1;
classWithInlineArrayField.myInlineArray[0].y = 2;
classWithInlineArrayField.myInlineArray[1].o = new Two();
classWithInlineArrayField.myInlineArray[1].x = 3;
classWithInlineArrayField.myInlineArray[1].y = 4;
//classWithInlineArrayField.InlineArrayProp[0].o = new One();
//classWithInlineArrayField.InlineArrayProp[0].x = 1;
//classWithInlineArrayField.InlineArrayProp[0].y = 2;
//classWithInlineArrayField.InlineArrayProp[1].o = new Two();
//classWithInlineArrayField.InlineArrayProp[1].x = 3;
//classWithInlineArrayField.InlineArrayProp[1].y = 4;
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].o = new One();
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].x = 1;
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].y = 2;
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].o = new Two();
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].x = 3;
classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].y = 4;
System.Diagnostics.Debugger.Break();
Console.WriteLine(s[0].o.GetType().Name);
}
public static void run2()
{
Arr2 a2 = default; //test with primitive type
Arr3 a3 = default; //test with length==1
Arr4 a4 = default; //test with static field
a2[0] = 1;
a3[0] = 2;
a4[0].o = new One();
a4[0].x = 1;
a4[0].y = 2;
a4[1].o = new Two();
a4[1].x = 3;
a4[1].y = 4;
Console.WriteLine($"olha thays - {Arr4.myStaticField}");
System.Diagnostics.Debugger.Break();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册