Example #1
0
        async Task GetDetails(MessageId msg_id, MonoCommands cmd, CancellationToken token)
        {
            var res = await SendMonoCommand(msg_id, cmd, token);

            //if we fail we just buble that to the IDE (and let it panic over it)
            if (res.IsErr)
            {
                SendResponse(msg_id, res, token);
                return;
            }

            try {
                var values   = res.Value?["result"]?["value"]?.Values <JObject>().ToArray() ?? Array.Empty <JObject>();
                var var_list = new List <JObject>();

                // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
                // results in a "Memory access out of bounds", causing 'values' to be null,
                // so skip returning variable values in that case.
                for (int i = 0; i < values.Length; i += 2)
                {
                    string fieldName = FormatFieldName((string)values[i]["name"]);
                    var    value     = values [i + 1]? ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(JObject.FromObject(new {
                        name = fieldName,
                        value
                    }));
                }
                var response = JObject.FromObject(new
                {
                    result = var_list
                });

                SendResponse(msg_id, Result.Ok(response), token);
            } catch (Exception e) {
                Log("verbose", $"failed to parse {res.Value} - {e.Message}");
                SendResponse(msg_id, Result.Exception(e), token);
            }
        }
Example #2
0
        async Task <Result> EnableBreakPoint(SessionId sessionId, Breakpoint bp, CancellationToken token)
        {
            var asm_name     = bp.Location.CliLocation.Method.Assembly.Name;
            var method_token = bp.Location.CliLocation.Method.Token;
            var il_offset    = bp.Location.CliLocation.Offset;

            var res = await SendMonoCommand(sessionId, MonoCommands.SetBreakpoint(asm_name, method_token, il_offset), token);

            var ret_code = res.Value? ["result"]? ["value"]?.Value <int> ();

            if (ret_code.HasValue)
            {
                bp.RemoteId = ret_code.Value;
                bp.State    = BreakpointState.Active;
                //Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
            }

            return(res);
        }
Example #3
0
        async Task <bool> Step(MessageId msg_id, StepKind kind, CancellationToken token)
        {
            var context = GetContext(msg_id);

            if (context.CallStack == null)
            {
                return(false);
            }

            var res = await SendMonoCommand(msg_id, MonoCommands.StartSingleStepping(kind), token);

            SendResponse(msg_id, Result.Ok(new JObject()), token);

            context.CallStack = null;

            await SendCommand(msg_id, "Debugger.resume", new JObject(), token);

            return(true);
        }
Example #4
0
        async Task <DebugStore> LoadStore(SessionId sessionId, CancellationToken token)
        {
            var context = GetContext(sessionId);

            if (Interlocked.CompareExchange(ref context.store, new DebugStore(logger), null) != null)
            {
                return(await context.Source.Task);
            }

            try {
                var loaded_pdbs = await SendMonoCommand(sessionId, MonoCommands.GetLoadedFiles(), token);

                var the_value = loaded_pdbs.Value? ["result"]? ["value"];
                var the_pdbs  = the_value?.ToObject <string[]> ();

                await foreach (var source in context.store.Load(sessionId, the_pdbs, token).WithCancellation(token))
                {
                    var scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
                    Log("verbose", $"\tsending {source.Url} {context.Id} {sessionId.sessionId}");

                    SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);

                    foreach (var req in context.BreakpointRequests.Values)
                    {
                        if (req.TryResolve(source))
                        {
                            await SetBreakpoint(sessionId, context.store, req, token);
                        }
                    }
                }
            } catch (Exception e) {
                context.Source.SetException(e);
            }

            if (!context.Source.Task.IsCompleted)
            {
                context.Source.SetResult(context.store);
            }
            return(context.store);
        }
Example #5
0
        async Task <DebugStore> RuntimeReady(SessionId sessionId, CancellationToken token)
        {
            var context = GetContext(sessionId);

            if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource <DebugStore> (), null) != null)
            {
                return(await context.ready.Task);
            }

            var clear_result = await SendMonoCommand(sessionId, MonoCommands.ClearAllBreakpoints(), token);

            if (clear_result.IsErr)
            {
                Log("verbose", $"Failed to clear breakpoints due to {clear_result}");
            }

            var store = await LoadStore(sessionId, token);

            context.ready.SetResult(store);
            SendEvent(sessionId, "Mono.runtimeReady", new JObject(), token);
            return(store);
        }
Example #6
0
        async Task GetScopeProperties(MessageId msg_id, int scope_id, CancellationToken token)
        {
            try {
                var scope = GetContext(msg_id).CallStack.FirstOrDefault(s => s.Id == scope_id);
                var vars  = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);

                var var_ids = vars.Select(v => v.Index).ToArray();
                var res     = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token);

                //if we fail we just buble that to the IDE (and let it panic over it)
                if (res.IsErr)
                {
                    SendResponse(msg_id, res, token);
                    return;
                }

                var values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();

                if (values == null)
                {
                    SendResponse(msg_id, Result.OkFromObject(new { result = Array.Empty <object> () }), token);
                }

                var var_list = new List <object> ();
                int i        = 0;
                // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
                // results in a "Memory access out of bounds", causing 'values' to be null,
                // so skip returning variable values in that case.
                while (i < vars.Length && i < values.Length)
                {
                    var value = values [i] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(new {
                        name = vars [i].Name,
                        value
                    });
                    i++;
                }
                //Async methods are special in the way that local variables can be lifted to generated class fields
                //value of "this" comes here either
                while (i < values.Length)
                {
                    String name = values [i] ["name"].ToString();

                    if (name.IndexOf(">", StringComparison.Ordinal) > 0)
                    {
                        name = name.Substring(1, name.IndexOf(">", StringComparison.Ordinal) - 1);
                    }

                    var value = values [i + 1] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(new {
                        name,
                        value
                    });
                    i = i + 2;
                }

                SendResponse(msg_id, Result.OkFromObject(new { result = var_list }), token);
            } catch (Exception exception) {
                Log("verbose", $"Error resolving scope properties {exception.Message}");
                SendResponse(msg_id, Result.Exception(exception), token);
            }
        }
Example #7
0
        //static int frame_id=0;
        async Task <bool> OnBreakpointHit(SessionId sessionId, JObject args, CancellationToken token)
        {
            //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
            var res = await SendMonoCommand(sessionId, MonoCommands.GetCallStack(), token);

            var orig_callframes = args? ["callFrames"]?.Values <JObject> ();
            var context         = GetContext(sessionId);

            if (res.IsErr)
            {
                //Give up and send the original call stack
                return(false);
            }

            //step one, figure out where did we hit
            var res_value = res.Value? ["result"]? ["value"];

            if (res_value == null || res_value is JValue)
            {
                //Give up and send the original call stack
                return(false);
            }

            Log("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}");
            var bp_id = res_value? ["breakpoint_id"]?.Value <int> ();

            Log("verbose", $"We just hit bp {bp_id}");
            if (!bp_id.HasValue)
            {
                //Give up and send the original call stack
                return(false);
            }
            var bp = context.Breakpoints.FirstOrDefault(b => b.RemoteId == bp_id.Value);

            var store = context.Store;
            var src   = bp == null ? null : store.GetFileById(bp.Location.Id);

            var callFrames = new List <object> ();

            foreach (var frame in orig_callframes)
            {
                var function_name = frame ["functionName"]?.Value <string> ();
                var url           = frame ["url"]?.Value <string> ();
                if ("mono_wasm_fire_bp" == function_name || "_mono_wasm_fire_bp" == function_name)
                {
                    var frames          = new List <Frame> ();
                    int frame_id        = 0;
                    var the_mono_frames = res.Value? ["result"]? ["value"]? ["frames"]?.Values <JObject> ();

                    foreach (var mono_frame in the_mono_frames)
                    {
                        var il_pos        = mono_frame ["il_pos"].Value <int> ();
                        var method_token  = mono_frame ["method_token"].Value <int> ();
                        var assembly_name = mono_frame ["assembly_name"].Value <string> ();

                        var asm = store.GetAssemblyByName(assembly_name);
                        if (asm == null)
                        {
                            Log("info", $"Unable to find assembly: {assembly_name}");
                            continue;
                        }

                        var method = asm.GetMethodByToken(method_token);

                        if (method == null)
                        {
                            Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
                            continue;
                        }

                        var location = method?.GetLocationByIl(il_pos);

                        // When hitting a breakpoint on the "IncrementCount" method in the standard
                        // Blazor project template, one of the stack frames is inside mscorlib.dll
                        // and we get location==null for it. It will trigger a NullReferenceException
                        // if we don't skip over that stack frame.
                        if (location == null)
                        {
                            continue;
                        }

                        Log("info", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
                        Log("info", $"\tmethod {method.Name} location: {location}");
                        frames.Add(new Frame(method, location, frame_id));

                        callFrames.Add(new {
                            functionName     = method.Name,
                            callFrameId      = $"dotnet:scope:{frame_id}",
                            functionLocation = method.StartLocation.AsLocation(),

                            location = location.AsLocation(),

                            url = store.ToUrl(location),

                            scopeChain = new [] {
                                new {
                                    type    = "local",
                                    @object = new {
                                        @type       = "object",
                                        className   = "Object",
                                        description = "Object",
                                        objectId    = $"dotnet:scope:{frame_id}",
                                    },
                                    name          = method.Name,
                                    startLocation = method.StartLocation.AsLocation(),
                                    endLocation   = method.EndLocation.AsLocation(),
                                }
                            }
                        });

                        ++frame_id;
                        context.CallStack = frames;
                    }
                }
                else if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) ||
                           url.StartsWith("wasm://wasm/", StringComparison.Ordinal)))
                {
                    callFrames.Add(frame);
                }
            }

            var bp_list = new string [bp == null ? 0 : 1];

            if (bp != null)
            {
                bp_list [0] = bp.StackId;
            }

            var o = JObject.FromObject(new {
                callFrames,
                reason         = "other",         //other means breakpoint
                hitBreakpoints = bp_list,
            });

            SendEvent(sessionId, "Debugger.paused", o, token);
            return(true);
        }
Example #8
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            switch (method)
            {
            case "Debugger.getScriptSource": {
                var script = args? ["scriptId"]?.Value <string> ();
                return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null)
                {
                    return(GetPossibleBreakpoints(id, start, end, token));
                }
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                Log("info", $"BP req {args}");
                var bp_req = BreakpointRequest.Parse(args, GetContext(id).Store);
                if (bp_req != null)
                {
                    await SetBreakPoint(id, bp_req, token);

                    return(true);
                }
                break;
            }

            case "Debugger.removeBreakpoint": {
                return(await RemoveBreakpoint(id, args, token));
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Runtime.getProperties": {
                var objId = args? ["objectId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts[1])
                    {
                    case "scope": {
                        await GetScopeProperties(id, int.Parse(parts[2]), token);

                        break;
                    }

                    case "object": {
                        await GetDetails(id, MonoCommands.GetObjectProperties(int.Parse(parts[2])), token);

                        break;
                    }

                    case "array": {
                        await GetDetails(id, MonoCommands.GetArrayValues(int.Parse(parts [2])), token);

                        break;
                    }
                    }
                    return(true);
                }
                break;
            }
            }

            return(false);
        }
Example #9
0
 internal Task <Result> SendMonoCommand(SessionId id, MonoCommands cmd, CancellationToken token)
 => SendCommand(id, "Runtime.evaluate", JObject.FromObject(cmd), token);
Example #10
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            switch (method)
            {
            case "Debugger.enable": {
                    var resp = await SendCommand(id, method, args, token);

                    GetContext(id).DebuggerId = resp.Value ["debuggerId"]?.ToString();

                    if (await IsRuntimeAlreadyReadyAlready(id, token))
                    {
                        await RuntimeReady(id, token);
                    }

                    SendResponse(id, resp, token);
                    return(true);
            }

            case "Debugger.getScriptSource": {
                    var script = args? ["scriptId"]?.Value <string> ();
                    return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var resp = await SendCommand(id, method, args, token);

                if (resp.IsOk && resp.Value["locations"].HasValues)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null && await GetPossibleBreakpoints(id, start, end, token))
                {
                    return(true);
                }

                SendResponse(id, resp, token);
                return(true);
            }

            case "Debugger.setBreakpoint": {
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                var context = GetContext(id);
                var resp    = await SendCommand(id, method, args, token);

                if (!resp.IsOk)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var bpid    = resp.Value["breakpointId"]?.ToString();
                var request = BreakpointRequest.Parse(bpid, args);
                context.BreakpointRequests[bpid] = request;
                if (await IsRuntimeAlreadyReadyAlready(id, token))
                {
                    var store = await RuntimeReady(id, token);

                    Log("verbose", $"BP req {args}");
                    await SetBreakpoint(id, store, request, token);
                }

                SendResponse(id, Result.OkFromObject(request.AsSetBreakpointByUrlResponse()), token);
                return(true);
            }

            case "Debugger.removeBreakpoint": {
                return(await RemoveBreakpoint(id, args, token));
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Runtime.getProperties": {
                var objId = args? ["objectId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts[1])
                    {
                    case "scope": {
                        await GetScopeProperties(id, int.Parse(parts[2]), token);

                        break;
                    }

                    case "object": {
                        await GetDetails(id, MonoCommands.GetObjectProperties(int.Parse(parts[2])), token);

                        break;
                    }

                    case "array": {
                        await GetDetails(id, MonoCommands.GetArrayValues(int.Parse(parts [2])), token);

                        break;
                    }
                    }
                    return(true);
                }
                break;
            }
            }

            return(false);
        }
Example #11
0
        async Task <bool> IsRuntimeAlreadyReadyAlready(SessionId sessionId, CancellationToken token)
        {
            var res = await SendMonoCommand(sessionId, MonoCommands.IsRuntimeReady(), token);

            return(res.Value? ["result"]? ["value"]?.Value <bool> () ?? false);
        }