private bool HandleExceptions(MeadowDebugAdapterThreadState threadState)
        {
            // Try to obtain an exception at this point
            if (threadState.CurrentStepIndex.HasValue)
            {
                // Obtain an exception at the current point.
                ExecutionTraceException traceException = threadState.ExecutionTraceAnalysis.GetException(threadState.CurrentStepIndex.Value);

                // If we have an exception, throw it and return the appropriate status.
                if (traceException != null)
                {
                    // Send our exception event.
                    var stoppedEvent = new StoppedEvent(StoppedEvent.ReasonValue.Exception)
                    {
                        Text     = traceException.Message,
                        ThreadId = threadState.ThreadId
                    };
                    Protocol.SendEvent(stoppedEvent);
                    return(true);
                }
            }

            // We did not find an exception here, return false
            return(false);
        }
        private bool HandleBreakpoint(MeadowDebugAdapterThreadState threadState)
        {
            // Verify we have a valid step at this point.
            if (!threadState.CurrentStepIndex.HasValue)
            {
                return(false);
            }

            // Loop through all the source lines at this step.
            var sourceLines = threadState.ExecutionTraceAnalysis.GetSourceLines(threadState.CurrentStepIndex.Value);

            foreach (var sourceLine in sourceLines)
            {
                // Verify our source path.
                var sourceFilePath = sourceLine.SourceFileMapParent?.SourceFilePath;

                // Resolve relative path properly so it can simply be looked up.
                bool success = _sourceBreakpoints.TryGetValue(sourceFilePath, out var breakpointLines);

                // If we have a breakpoint at this line number..
                bool containsBreakpoint = success && breakpointLines.Any(x => x == sourceLine.LineNumber);
                if (containsBreakpoint)
                {
                    // Signal our breakpoint event has occurred for this thread.
                    Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Breakpoint)
                    {
                        ThreadId = threadState.ThreadId
                    });
                    return(true);
                }
            }

            return(false);
        }
예제 #3
0
        void LogException(Exception ex, MeadowDebugAdapterThreadState threadState)
        {
            // Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.ThreadEvent
            var msg = new StringBuilder();

            msg.AppendLine(ex.ToString());
            msg.AppendLine();
            msg.AppendLine("Solidity call stack: ");
            try
            {
                msg.AppendLine(threadState.ExecutionTraceAnalysis.GetCallStackString(threadState.CurrentStepIndex.Value));
            }
            catch (Exception callstackResolveEx)
            {
                msg.AppendLine("Exception resolving callstack: " + callstackResolveEx.ToString());
            }

            var exceptionEvent = new OutputEvent
            {
                Category = OutputEvent.CategoryValue.Stderr,
                Output   = msg.ToString()
            };

            Protocol.SendEvent(exceptionEvent);
        }
예제 #4
0
        public async Task ProcessExecutionTraceAnalysis(ExecutionTraceAnalysis traceAnalysis)
        {
            // Obtain our thread ID
            int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;

            // Create a thread state for this thread
            MeadowDebugAdapterThreadState threadState = new MeadowDebugAdapterThreadState(traceAnalysis, threadId);

            // Acquire the semaphore for processing a trace.
            await _processTraceSemaphore.WaitAsync();

            // Set the thread state in our lookup
            ThreadStates[threadId] = threadState;

            // Send an event that our thread has exited.
            Protocol.SendEvent(new ThreadEvent(ThreadEvent.ReasonValue.Started, threadState.ThreadId));

            // Continue our execution.
            ContinueExecution(threadState, DesiredControlFlow.Continue);

            // Lock execution is complete.
            await threadState.Semaphore.WaitAsync();

            // Remove the thread from our lookup.
            ThreadStates.Remove(threadId, out _);

            // Unlink our data for our thread id.
            ReferenceContainer.UnlinkThreadId(threadId);

            // Send an event that our thread has exited.
            Protocol.SendEvent(new ThreadEvent(ThreadEvent.ReasonValue.Exited, threadState.ThreadId));

            // Release the semaphore for processing a trace.
            _processTraceSemaphore.Release();
        }
예제 #5
0
        private bool CheckBreakpointExists(MeadowDebugAdapterThreadState threadState)
        {
            // Verify we have a valid step at this point.
            if (!threadState.CurrentStepIndex.HasValue)
            {
                return(false);
            }

            // Loop through all the source lines at this step.
            var sourceLines = threadState.ExecutionTraceAnalysis.GetSourceLines(threadState.CurrentStepIndex.Value);

            foreach (var sourceLine in sourceLines)
            {
                // Verify our source path.
                var sourceFilePath = sourceLine.SourceFileMapParent?.SourceFilePath;

                // Resolve relative path properly so it can simply be looked up.
                bool success = _sourceBreakpoints.TryGetValue(sourceFilePath, out var breakpointLines);

                // If we have a breakpoint at this line number..
                bool containsBreakpoint = success && breakpointLines.Any(x => x == sourceLine.LineNumber);
                if (containsBreakpoint)
                {
                    return(true);
                }
            }

            return(false);
        }
예제 #6
0
        private bool HandleExceptions(MeadowDebugAdapterThreadState threadState)
        {
            bool ShouldProcessException()
            {
                lock (_exceptionBreakpointFilters)
                {
                    if (_exceptionBreakpointFilters.Contains(EXCEPTION_BREAKPOINT_FILTER_ALL))
                    {
                        return(true);
                    }

                    if (!threadState.ExpectingException && _exceptionBreakpointFilters.Contains(EXCEPTION_BREAKPOINT_FILTER_UNHANDLED))
                    {
                        return(true);
                    }

                    return(false);
                }
            }

            // Try to obtain an exception at this point
            if (ShouldProcessException() && threadState.CurrentStepIndex.HasValue)
            {
                // Obtain an exception at the current point.
                ExecutionTraceException traceException = threadState.ExecutionTraceAnalysis.GetException(threadState.CurrentStepIndex.Value);

                // If we have an exception, throw it and return the appropriate status.
                if (traceException != null)
                {
                    // Send our exception event.
                    var stoppedEvent = new StoppedEvent(StoppedEvent.ReasonValue.Exception)
                    {
                        Text     = traceException.Message,
                        ThreadId = threadState.ThreadId
                    };
                    Protocol.SendEvent(stoppedEvent);
                    return(true);
                }
            }

            // We did not find an exception here, return false
            return(false);
        }
예제 #7
0
        public async Task ProcessExecutionTraceAnalysis(IJsonRpcClient rpcClient, ExecutionTraceAnalysis traceAnalysis, bool expectingException)
        {
            // We don't have real threads here, only a unique execution context
            // per RPC callback (eth_call or eth_sendTransactions).
            // So just use a rolling ID for threads.
            int threadId = System.Threading.Interlocked.Increment(ref _threadIDCounter);

            // Create a thread state for this thread
            MeadowDebugAdapterThreadState threadState = new MeadowDebugAdapterThreadState(rpcClient, traceAnalysis, threadId, expectingException);

            // Acquire the semaphore for processing a trace.
            await _processTraceSemaphore.WaitAsync();

            // Set the thread state in our lookup
            ThreadStates[threadId] = threadState;

            // If we're not exiting, step through our
            if (!Exiting)
            {
                // Send an event that our thread has exited.
                Protocol.SendEvent(new ThreadEvent(ThreadEvent.ReasonValue.Started, threadState.ThreadId));

                // Continue our execution.
                ContinueExecution(threadState, DesiredControlFlow.Continue);

                // Lock execution is complete.
                await threadState.Semaphore.WaitAsync();

                // Send an event that our thread has exited.
                Protocol.SendEvent(new ThreadEvent(ThreadEvent.ReasonValue.Exited, threadState.ThreadId));
            }

            // Remove the thread from our lookup.
            ThreadStates.Remove(threadId, out _);

            // Unlink our data for our thread id.
            ReferenceContainer.UnlinkThreadId(threadId);

            // Release the semaphore for processing a trace.
            _processTraceSemaphore.Release();
        }
 private bool EvaluateCurrentStep(MeadowDebugAdapterThreadState threadState, bool exceptions = true, bool breakpoints = true)
 {
     // Evaluate exceptions and breakpoints at this point in execution.
     return((exceptions && HandleExceptions(threadState)) || (breakpoints && HandleBreakpoint(threadState)));
 }
        private void ContinueExecution(MeadowDebugAdapterThreadState threadState, DesiredControlFlow controlFlowAction = DesiredControlFlow.Continue, int stepsPriorToAction = 0)
        {
            // Unlink our data for our thread id.
            ReferenceContainer.UnlinkThreadId(threadState.ThreadId);

            // Create a variable to track if we have finished stepping through the execution.
            bool finishedExecution = false;

            // Determine the direction to take steps prior to any evaluation.
            if (stepsPriorToAction >= 0)
            {
                // Loop for each step to take forward.
                for (int i = 0; i < stepsPriorToAction; i++)
                {
                    // Take a step forward, if we could not, we finished execution, so we can stop looping.
                    if (!threadState.IncrementStep())
                    {
                        finishedExecution = true;
                        break;
                    }
                }
            }
            else
            {
                // Loop for each step to take backward
                for (int i = 0; i > stepsPriorToAction; i--)
                {
                    // Take a step backward, if we could not, we can stop early as we won't be able to step backwards anymore.
                    if (!threadState.DecrementStep())
                    {
                        break;
                    }
                }
            }

            // If we haven't finished execution,
            if (!finishedExecution)
            {
                switch (controlFlowAction)
                {
                case DesiredControlFlow.StepOver:
                // TODO: Implement

                case DesiredControlFlow.StepInto:
                {
                    // Increment our step
                    finishedExecution = !threadState.IncrementStep();

                    // If we stepped successfully, we evaluate, and if an event is encountered, we stop.
                    if (!finishedExecution && EvaluateCurrentStep(threadState))
                    {
                        return;
                    }

                    // Signal our breakpoint event has occurred for this thread.
                    Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Step)
                        {
                            ThreadId = threadState.ThreadId
                        });
                    break;
                }

                case DesiredControlFlow.StepOut:
                // TODO: Implement

                case DesiredControlFlow.StepBackwards:
                {
                    // Decrement our step
                    bool decrementedStep = threadState.DecrementStep();

                    // If we stepped successfully, we evaluate, and if an event is encountered, we stop.
                    if (decrementedStep && EvaluateCurrentStep(threadState))
                    {
                        return;
                    }

                    // Signal our breakpoint event has occurred for this thread.
                    Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Step)
                        {
                            ThreadId = threadState.ThreadId
                        });

                    // TODO: Check if we couldn't decrement step. Disable step backward if we can.
                    break;
                }

                case DesiredControlFlow.Continue:
                {
                    // Process the execution trace analysis
                    while (threadState.CurrentStepIndex.HasValue && !Exiting)
                    {
                        // If we encountered an event at this point, stop
                        if (EvaluateCurrentStep(threadState))
                        {
                            return;
                        }

                        // If we couldn't step forward, this trace has been fully processed.
                        if (!threadState.IncrementStep())
                        {
                            break;
                        }
                    }

                    // If we exited this way, our execution has concluded because we could not step any further (or there were never any steps).
                    finishedExecution = true;
                    break;
                }
                }
            }

            // If we finished execution, signal our thread
            if (finishedExecution)
            {
                threadState.Semaphore.Release();
            }
        }
예제 #10
0
        void ResolveVariables(
            List <Variable> variableList,
            int variablesReference,
            bool isLocalVariableScope,
            bool isStateVariableScope,
            bool isParentVariableScope,
            int traceIndex,
            UnderlyingVariableValuePair parentVariableValuePair,
            MeadowDebugAdapterThreadState threadState)
        {
            // Obtain our local variables at this point in execution
            VariableValuePair[] variablePairs = Array.Empty <VariableValuePair>();
            if (isLocalVariableScope)
            {
                variablePairs = threadState.ExecutionTraceAnalysis.GetLocalVariables(traceIndex, threadState.RpcClient);
            }
            else if (isStateVariableScope)
            {
                variablePairs = threadState.ExecutionTraceAnalysis.GetStateVariables(traceIndex, threadState.RpcClient);
            }
            else if (isParentVariableScope)
            {
                // We're loading sub-variables for a variable.
                switch (parentVariableValuePair.Variable.GenericType)
                {
                case VarGenericType.Struct:
                {
                    // Cast our to an enumerable type.
                    variablePairs = ((IEnumerable <VariableValuePair>)parentVariableValuePair.Value).ToArray();
                    break;
                }

                case VarGenericType.Array:
                {
                    // Cast our variable
                    var arrayVariable = ((VarArray)parentVariableValuePair.Variable);

                    // Cast to an object array.
                    var arrayValue = (object[])parentVariableValuePair.Value;

                    // Loop for each element
                    for (int i = 0; i < arrayValue.Length; i++)
                    {
                        // Create an underlying variable value pair for this element
                        var underlyingVariableValuePair = new UnderlyingVariableValuePair(arrayVariable.ElementObject, arrayValue[i]);

                        // Check if this is a nested variable type
                        bool nestedType = IsNestedVariableType(arrayVariable.ElementObject.GenericType);
                        int  variablePairReferenceId = 0;
                        if (nestedType)
                        {
                            // Create a new reference id for this variable if it's a nested type.
                            variablePairReferenceId = ReferenceContainer.GetUniqueId();

                            // Link our reference for any nested types.
                            ReferenceContainer.LinkSubVariableReference(variablesReference, variablePairReferenceId, threadState.ThreadId, underlyingVariableValuePair);
                        }

                        // Obtain the value string for this variable and add it to our list.
                        string variableValueString = GetVariableValueString(underlyingVariableValuePair);
                        variableList.Add(CreateVariable($"[{i}]", variableValueString, variablePairReferenceId, underlyingVariableValuePair.Variable.BaseType));
                    }


                    break;
                }

                case VarGenericType.ByteArrayDynamic:
                case VarGenericType.ByteArrayFixed:
                {
                    // Cast our to an enumerable type.
                    var bytes = (Memory <byte>)parentVariableValuePair.Value;
                    for (int i = 0; i < bytes.Length; i++)
                    {
                        variableList.Add(CreateVariable($"[{i}]", bytes.Span[i].ToString(CultureInfo.InvariantCulture), 0, "byte"));
                    }

                    break;
                }

                case VarGenericType.Mapping:
                {
                    // Obtain our mapping's key-value pairs.
                    var mappingKeyValuePairs = (MappingKeyValuePair[])parentVariableValuePair.Value;
                    variablePairs = new VariableValuePair[mappingKeyValuePairs.Length * 2];

                    // Loop for each key and value pair to add.
                    int variableIndex = 0;
                    for (int i = 0; i < mappingKeyValuePairs.Length; i++)
                    {
                        // Set our key and value in our variable value pair enumeration.
                        variablePairs[variableIndex++] = mappingKeyValuePairs[i].Key;
                        variablePairs[variableIndex++] = mappingKeyValuePairs[i].Value;
                    }

                    break;
                }
                }
            }

            // Loop for each local variables
            foreach (VariableValuePair variablePair in variablePairs)
            {
                // Create an underlying variable value pair for this pair.
                var underlyingVariableValuePair = new UnderlyingVariableValuePair(variablePair);

                // Check if this is a nested variable type
                bool nestedType = IsNestedVariableType(variablePair.Variable.GenericType);
                int  variablePairReferenceId = 0;
                if (nestedType)
                {
                    // Create a new reference id for this variable if it's a nested type.
                    variablePairReferenceId = ReferenceContainer.GetUniqueId();

                    // Link our reference for any nested types.
                    ReferenceContainer.LinkSubVariableReference(variablesReference, variablePairReferenceId, threadState.ThreadId, underlyingVariableValuePair);
                }

                // Obtain the value string for this variable and add it to our list.
                string variableValueString = GetVariableValueString(underlyingVariableValuePair);


                variableList.Add(CreateVariable(variablePair.Variable.Name, variableValueString, variablePairReferenceId, variablePair.Variable.BaseType));
            }
        }
예제 #11
0
        private void ContinueExecution(MeadowDebugAdapterThreadState threadState, DesiredControlFlow controlFlow = DesiredControlFlow.Continue)
        {
            // Unlink our data for our thread id.
            ReferenceContainer.UnlinkThreadId(threadState.ThreadId);

            // Verify we don't have an exception at this point that we haven't already handled, if we do, break out.
            if (threadState.LastExceptionTraceIndex != threadState.CurrentStepIndex && HandleExceptions(threadState))
            {
                return;
            }

            // Determine how to continue execution.
            bool finishedExecution = false;

            switch (controlFlow)
            {
            case DesiredControlFlow.StepOver:
            // TODO: Implement

            case DesiredControlFlow.StepInto:
            {
                // Increment our step
                finishedExecution = !threadState.IncrementStep();

                // Signal our breakpoint event has occurred for this thread.
                Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Step)
                    {
                        ThreadId = threadState.ThreadId
                    });
                break;
            }

            case DesiredControlFlow.StepOut:
            // TODO: Implement

            case DesiredControlFlow.StepBackwards:
            {
                // Decrement our step
                threadState.DecrementStep();

                // Signal our breakpoint event has occurred for this thread.
                Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Step)
                    {
                        ThreadId = threadState.ThreadId
                    });

                // TODO: Check if we couldn't decrement step. Disable step backward if we can.
                break;
            }

            case DesiredControlFlow.Continue:
            {
                // Process the execution trace analysis
                while (threadState.CurrentStepIndex.HasValue)
                {
                    // Obtain our breakpoints.
                    bool hitBreakpoint = CheckBreakpointExists(threadState);

                    // If we hit a breakpoint, we can signal our breakpoint and exit this execution method.
                    if (hitBreakpoint)
                    {
                        // Signal our breakpoint event has occurred for this thread.
                        Protocol.SendEvent(new StoppedEvent(StoppedEvent.ReasonValue.Breakpoint)
                            {
                                ThreadId = threadState.ThreadId
                            });
                        return;
                    }

                    // Increment our position
                    bool successfulStep = threadState.IncrementStep();

                    // If we couldn't step, break out of our loop
                    if (!successfulStep)
                    {
                        break;
                    }
                }

                // If we exited this way, our execution has concluded because we could not step any further (or there were never any steps).
                finishedExecution = true;
                break;
            }
            }

            // If we finished execution, signal our thread
            if (finishedExecution)
            {
                threadState.Semaphore.Release();
            }
        }