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); }
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); }
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(); }
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); }
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); }
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(); } }
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)); } }
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(); } }