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

[wasm][debugger] Inspect static class #56740

Merged
merged 3 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 68 additions & 61 deletions src/mono/mono/component/debugger-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -7617,67 +7617,67 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
g_free (name);
break;
}
case CMD_ASSEMBLY_GET_METADATA_BLOB: {
MonoImage* image = ass->image;
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
buffer_add_byte_array (buf, (guint8*)image->raw_data, image->raw_data_len);
break;
}
case CMD_ASSEMBLY_GET_IS_DYNAMIC: {
buffer_add_byte (buf, ass->dynamic);
break;
}
case CMD_ASSEMBLY_GET_PDB_BLOB: {
MonoImage* image = ass->image;
MonoDebugHandle* handle = mono_debug_get_handle (image);
if (!handle) {
return ERR_INVALID_ARGUMENT;
}
MonoPPDBFile* ppdb = handle->ppdb;
if (ppdb) {
image = mono_ppdb_get_image (ppdb);
buffer_add_byte_array (buf, (guint8*)image->raw_data, image->raw_data_len);
} else {
buffer_add_byte_array (buf, NULL, 0);
}
break;
}
case CMD_ASSEMBLY_GET_TYPE_FROM_TOKEN: {
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
guint32 token = decode_int (p, &p, end);
ERROR_DECL (error);
error_init (error);
MonoClass* mono_class = mono_class_get_checked (ass->image, token, error);
if (!is_ok (error)) {
add_error_string (buf, mono_error_get_message (error));
mono_error_cleanup (error);
return ERR_INVALID_ARGUMENT;
}
buffer_add_typeid (buf, domain, mono_class);
mono_error_cleanup (error);
break;
}
case CMD_ASSEMBLY_GET_METHOD_FROM_TOKEN: {
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
guint32 token = decode_int (p, &p, end);
ERROR_DECL (error);
error_init (error);
MonoMethod* mono_method = mono_get_method_checked (ass->image, token, NULL, NULL, error);
if (!is_ok (error)) {
add_error_string (buf, mono_error_get_message (error));
mono_error_cleanup (error);
return ERR_INVALID_ARGUMENT;
}
buffer_add_methodid (buf, domain, mono_method);
mono_error_cleanup (error);
break;
}
case CMD_ASSEMBLY_GET_METADATA_BLOB: {
MonoImage* image = ass->image;
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
buffer_add_byte_array (buf, (guint8*)image->raw_data, image->raw_data_len);
break;
}
case CMD_ASSEMBLY_GET_IS_DYNAMIC: {
buffer_add_byte (buf, ass->dynamic);
break;
}
case CMD_ASSEMBLY_GET_PDB_BLOB: {
MonoImage* image = ass->image;
MonoDebugHandle* handle = mono_debug_get_handle (image);
if (!handle) {
return ERR_INVALID_ARGUMENT;
}
MonoPPDBFile* ppdb = handle->ppdb;
if (ppdb) {
image = mono_ppdb_get_image (ppdb);
buffer_add_byte_array (buf, (guint8*)image->raw_data, image->raw_data_len);
} else {
buffer_add_byte_array (buf, NULL, 0);
}
break;
}
case CMD_ASSEMBLY_GET_TYPE_FROM_TOKEN: {
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
guint32 token = decode_int (p, &p, end);
ERROR_DECL (error);
error_init (error);
MonoClass* mono_class = mono_class_get_checked (ass->image, token, error);
if (!is_ok (error)) {
add_error_string (buf, mono_error_get_message (error));
mono_error_cleanup (error);
return ERR_INVALID_ARGUMENT;
}
buffer_add_typeid (buf, domain, mono_class);
mono_error_cleanup (error);
break;
}
case CMD_ASSEMBLY_GET_METHOD_FROM_TOKEN: {
if (ass->dynamic) {
return ERR_NOT_IMPLEMENTED;
}
guint32 token = decode_int (p, &p, end);
ERROR_DECL (error);
error_init (error);
MonoMethod* mono_method = mono_get_method_checked (ass->image, token, NULL, NULL, error);
if (!is_ok (error)) {
add_error_string (buf, mono_error_get_message (error));
mono_error_cleanup (error);
return ERR_INVALID_ARGUMENT;
}
buffer_add_methodid (buf, domain, mono_method);
mono_error_cleanup (error);
break;
}
case CMD_ASSEMBLY_HAS_DEBUG_INFO: {
buffer_add_byte (buf, !ass->dynamic && mono_debug_image_has_debug_info (ass->image));
break;
Expand Down Expand Up @@ -8377,6 +8377,13 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
}
break;
}
case MDBGPROT_CMD_TYPE_INITIALIZE: {
MonoVTable *vtable = mono_class_vtable_checked (klass, error);
goto_if_nok (error, loader_error);
mono_runtime_class_init_full (vtable, error);
goto_if_nok (error, loader_error);
break;
}
default:
err = ERR_NOT_IMPLEMENTED;
goto exit;
Expand Down
3 changes: 2 additions & 1 deletion src/mono/mono/component/debugger-protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ typedef enum {
MDBGPROT_CMD_TYPE_CREATE_INSTANCE = 19,
MDBGPROT_CMD_TYPE_GET_VALUE_SIZE = 20,
MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21,
MDBGPROT_CMD_TYPE_GET_PARENTS = 22
MDBGPROT_CMD_TYPE_GET_PARENTS = 22,
MDBGPROT_CMD_TYPE_INITIALIZE = 23
} MdbgProtCmdType;

typedef enum {
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/object-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -1599,7 +1599,7 @@ mono_class_try_get_vtable (MonoClass *klass);
gboolean
mono_runtime_run_module_cctor (MonoImage *image, MonoError *error);

gboolean
MONO_COMPONENT_API gboolean
mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error);

void
Expand Down
10 changes: 6 additions & 4 deletions src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,14 +453,16 @@ public VarInfo[] GetLiveVarsAt(int offset)

internal class TypeInfo
{
private AssemblyInfo assembly;
internal AssemblyInfo assembly;
private TypeDefinition type;
private List<MethodInfo> methods;
public int Token { get; }

public TypeInfo(AssemblyInfo assembly, TypeDefinition type)
public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type)
{
this.assembly = assembly;
var metadataReader = assembly.asmMetadataReader;
Token = MetadataTokens.GetToken(metadataReader, typeHandle);
this.type = type;
methods = new List<MethodInfo>();
Name = metadataReader.GetString(type.Name);
Expand Down Expand Up @@ -594,7 +596,7 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName)
{
var typeDefinition = asmMetadataReader.GetTypeDefinition(type);

var typeInfo = new TypeInfo(this, typeDefinition);
var typeInfo = new TypeInfo(this, type, typeDefinition);
typesByName[typeInfo.FullName] = typeInfo;
if (pdbMetadataReader != null)
{
Expand Down Expand Up @@ -876,7 +878,7 @@ public object ToScriptSource(int executionContextId, object executionContextAuxD

internal class DebugStore
{
private List<AssemblyInfo> assemblies = new List<AssemblyInfo>();
internal List<AssemblyInfo> assemblies = new List<AssemblyInfo>();
private readonly HttpClient client;
private readonly ILogger logger;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,57 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
}
return null;
}

public async Task<JObject> TryToRunOnLoadedClasses(string varName, CancellationToken token)
{
string classNameToFind = "";
string[] parts = varName.Split(".");
var typeId = -1;
foreach (string part in parts)
{
if (classNameToFind.Length > 0)
classNameToFind += ".";
classNameToFind += part.Trim();
if (typeId != -1)
{
var fields = await proxy.SdbHelper.GetTypeFields(sessionId, typeId, token);
foreach (var field in fields)
{
if (field.Name == part.Trim())
{
var isInitialized = await proxy.SdbHelper.TypeIsInitialized(sessionId, typeId, token);
if (isInitialized == 0)
{
isInitialized = await proxy.SdbHelper.TypeInitialize(sessionId, typeId, token);
}
var valueRet = await proxy.SdbHelper.GetFieldValue(sessionId, typeId, field.Id, token);
return await GetValueFromObject(valueRet, token);
}
}
var methodId = await proxy.SdbHelper.GetPropertyMethodIdByName(sessionId, typeId, part.Trim(), token);
if (methodId != -1)
{
var commandParamsObj = new MemoryStream();
var commandParamsObjWriter = new MonoBinaryWriter(commandParamsObj);
commandParamsObjWriter.Write(0); //param count
var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
return await GetValueFromObject(retMethod, token);
}
}
var store = await proxy.LoadStore(sessionId, token);
foreach (var asm in store.assemblies)
{
var type = asm.GetTypeByName(classNameToFind);
if (type != null)
{
var assemblyId = await proxy.SdbHelper.GetAssemblyId(sessionId, type.assembly.Name, token);
typeId = await proxy.SdbHelper.GetTypeIdFromToken(sessionId, assemblyId, type.Token, token);
}
}
}
return null;
}

// Checks Locals, followed by `this`
public async Task<JObject> Resolve(string varName, CancellationToken token)
{
Expand Down Expand Up @@ -140,7 +191,8 @@ public async Task<JObject> Resolve(string varName, CancellationToken token)
}
else
{
return null;
rootObject = await TryToRunOnLoadedClasses(varName, token);
return rootObject;
}
}
}
Expand Down Expand Up @@ -177,8 +229,8 @@ public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary
var typeName = await proxy.SdbHelper.GetTypeName(sessionId, typeId[0], token);
throw new Exception($"Method '{methodName}' not found in type '{typeName}'");
}
var command_params_obj = new MemoryStream();
var commandParamsObjWriter = new MonoBinaryWriter(command_params_obj);
var commandParamsObj = new MemoryStream();
var commandParamsObjWriter = new MonoBinaryWriter(commandParamsObj);
commandParamsObjWriter.WriteObj(objectId, proxy.SdbHelper);
if (method.ArgumentList != null)
{
Expand All @@ -197,7 +249,7 @@ public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary
return null;
}
}
var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, command_params_obj.ToArray(), methodId, "methodRet", token);
var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
return await GetValueFromObject(retMethod, token);
}
}
Expand Down
73 changes: 72 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ internal enum CmdType {
CreateInstance = 19,
GetValueSize = 20,
GetValuesICorDbg = 21,
GetParents = 22
GetParents = 22,
Initialize = 23,
}

internal enum CmdArray {
Expand Down Expand Up @@ -929,6 +930,41 @@ public async Task<bool> ClearSingleStep(SessionId sessionId, int req_id, Cancell
return false;
}

public async Task<JObject> GetFieldValue(SessionId sessionId, int typeId, int fieldId, CancellationToken token)
{
var ret = new List<FieldTypeClass>();
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write(typeId);
commandParamsWriter.Write(1);
commandParamsWriter.Write(fieldId);

var retDebuggerCmdReader = await SendDebuggerAgentCommand<CmdType>(sessionId, CmdType.GetValues, commandParams, token);
return await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, "", false, -1, token);
}

public async Task<int> TypeIsInitialized(SessionId sessionId, int typeId, CancellationToken token)
{
var ret = new List<FieldTypeClass>();
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write(typeId);

var retDebuggerCmdReader = await SendDebuggerAgentCommand<CmdType>(sessionId, CmdType.IsInitialized, commandParams, token);
return retDebuggerCmdReader.ReadInt32();
}

public async Task<int> TypeInitialize(SessionId sessionId, int typeId, CancellationToken token)
{
var ret = new List<FieldTypeClass>();
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write(typeId);

var retDebuggerCmdReader = await SendDebuggerAgentCommand<CmdType>(sessionId, CmdType.Initialize, commandParams, token);
return retDebuggerCmdReader.ReadInt32();
}

public async Task<List<FieldTypeClass>> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token)
{
var ret = new List<FieldTypeClass>();
Expand Down Expand Up @@ -1128,6 +1164,17 @@ public async Task<string> GetClassNameFromObject(SessionId sessionId, int object
return await GetTypeName(sessionId, type_id[0], token);
}

public async Task<int> GetTypeIdFromToken(SessionId sessionId, int assemblyId, int typeToken, CancellationToken token)
{
var ret = new List<string>();
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write((int)assemblyId);
commandParamsWriter.Write((int)typeToken);
var retDebuggerCmdReader = await SendDebuggerAgentCommand<CmdAssembly>(sessionId, CmdAssembly.GetTypeFromToken, commandParams, token);
return retDebuggerCmdReader.ReadInt32();
}

public async Task<int> GetMethodIdByName(SessionId sessionId, int type_id, string method_name, CancellationToken token)
{
var ret = new List<string>();
Expand Down Expand Up @@ -1191,6 +1238,30 @@ public async Task<JObject> InvokeMethod(SessionId sessionId, byte[] valueTypeBuf
retDebuggerCmdReader.ReadByte(); //number of objects returned.
return await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, varName, false, -1, token);
}

public async Task<int> GetPropertyMethodIdByName(SessionId sessionId, int typeId, string propertyName, CancellationToken token)
{
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.Write(typeId);

var retDebuggerCmdReader = await SendDebuggerAgentCommand<CmdType>(sessionId, CmdType.GetProperties, commandParams, token);
var nProperties = retDebuggerCmdReader.ReadInt32();
for (int i = 0 ; i < nProperties; i++)
{
retDebuggerCmdReader.ReadInt32(); //propertyId
string propertyNameStr = retDebuggerCmdReader.ReadString();
var getMethodId = retDebuggerCmdReader.ReadInt32();
retDebuggerCmdReader.ReadInt32(); //setmethod
var attrs = retDebuggerCmdReader.ReadInt32(); //attrs
if (propertyNameStr == propertyName)
{
return getMethodId;
}
}
return -1;
}

public async Task<JArray> CreateJArrayForProperties(SessionId sessionId, int typeId, byte[] object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token)
{
JArray ret = new JArray();
Expand Down
Loading