protected override void HandleVariablesRequestAsync(IRequestResponder <VariablesArguments, VariablesResponse> responder) { // Obtain relevant variable resolving information. int threadId = 0; int traceIndex = 0; bool isLocalVariableScope = false; bool isStateVariableScope = false; bool isParentVariableScope = false; UnderlyingVariableValuePair parentVariableValuePair = new UnderlyingVariableValuePair(null, null); // Try to obtain the variable reference as a local variable scope. isLocalVariableScope = ReferenceContainer.ResolveLocalVariable(responder.Arguments.VariablesReference, out threadId, out traceIndex); if (!isLocalVariableScope) { // Try to obtain the variable reference as a state variable scope. isStateVariableScope = ReferenceContainer.ResolveStateVariable(responder.Arguments.VariablesReference, out threadId, out traceIndex); if (!isStateVariableScope) { // Try to obtain the variable reference as a sub-variable scope. isParentVariableScope = ReferenceContainer.ResolveParentVariable(responder.Arguments.VariablesReference, out threadId, out parentVariableValuePair); } } // Using our thread id, obtain our thread state ThreadStates.TryGetValue(threadId, out var threadState); // Verify the thread state is valid if (threadState == null) { responder.SetResponse(new VariablesResponse()); return; } // Obtain the trace index for this scope. List <Variable> variableList = new List <Variable>(); try { ResolveVariables( variableList, responder.Arguments.VariablesReference, isLocalVariableScope, isStateVariableScope, isParentVariableScope, traceIndex, parentVariableValuePair, threadState); } catch (Exception ex) { LogException(ex, threadState); } // Respond with our variable list. responder.SetResponse(new VariablesResponse(variableList)); }
protected override void HandleExceptionInfoRequestAsync(IRequestResponder <ExceptionInfoArguments, ExceptionInfoResponse> responder) { // Obtain the current thread state bool success = ThreadStates.TryGetValue(responder.Arguments.ThreadId, out var threadState); if (success) { string exceptionMessage = threadState.ExecutionTraceAnalysis.GetException(threadState.CurrentStepIndex.Value).Message; responder.SetResponse(new ExceptionInfoResponse(exceptionMessage, ExceptionBreakMode.Always)); } }
protected override void HandleStepBackRequestAsync(IRequestResponder <StepBackArguments> responder) { // Set our response responder.SetResponse(new StepBackResponse()); // Obtain the current thread state bool success = ThreadStates.TryGetValue(responder.Arguments.ThreadId, out var threadState); if (success) { // Continue executing ContinueExecution(threadState, DesiredControlFlow.StepBackwards); } }
protected override void HandleContinueRequestAsync(IRequestResponder <ContinueArguments, ContinueResponse> responder) { // Set our response responder.SetResponse(new ContinueResponse()); // Obtain the current thread state bool success = ThreadStates.TryGetValue(responder.Arguments.ThreadId, out var threadState); if (success) { // Continue executing, taking one step before continuing, as evaluation occurs before steps occur, and we want // to ensure we advanced position from our last and don't re-evaluate the same trace point. We only do this on // startup since we want the initial trace point to be evaluated. After that, we want to force advancement by // at least one step before continuation/re-evaluation. ContinueExecution(threadState, DesiredControlFlow.Continue, 1); } }
protected override void HandleExceptionInfoRequestAsync(IRequestResponder <ExceptionInfoArguments, ExceptionInfoResponse> responder) { // Obtain the current thread state bool success = ThreadStates.TryGetValue(responder.Arguments.ThreadId, out var threadState); if (success) { var ex = threadState.ExecutionTraceAnalysis.GetException(threadState.CurrentStepIndex.Value); // Get the exception call stack lines. var exStackTrace = threadState.ExecutionTraceAnalysis.GetCallStackString(ex.TraceIndex.Value); responder.SetResponse(new ExceptionInfoResponse("Error", ExceptionBreakMode.Always) { Description = ex.Message, Details = new ExceptionDetails { Message = ex.Message, FormattedDescription = ex.Message, StackTrace = exStackTrace } }); } }
protected override void HandleVariablesRequestAsync(IRequestResponder <VariablesArguments, VariablesResponse> responder) { // Obtain relevant variable resolving information. int threadId = 0; int traceIndex = 0; bool isLocalVariableScope = false; bool isStateVariableScope = false; bool isParentVariableScope = false; UnderlyingVariableValuePair parentVariableValuePair = new UnderlyingVariableValuePair(null, null); // Try to obtain the variable reference as a local variable scope. isLocalVariableScope = ReferenceContainer.ResolveLocalVariable(responder.Arguments.VariablesReference, out threadId, out traceIndex); if (!isLocalVariableScope) { // Try to obtain the variable reference as a state variable scope. isStateVariableScope = ReferenceContainer.ResolveStateVariable(responder.Arguments.VariablesReference, out threadId, out traceIndex); if (!isStateVariableScope) { // Try to obtain the variable reference as a sub-variable scope. isParentVariableScope = ReferenceContainer.ResolveParentVariable(responder.Arguments.VariablesReference, out threadId, out parentVariableValuePair); } } // Using our thread id, obtain our thread state ThreadStates.TryGetValue(threadId, out var threadState); // Verify the thread state is valid if (threadState == null) { responder.SetResponse(new VariablesResponse()); return; } // Obtain the trace index for this scope. List <Variable> variableList = new List <Variable>(); // Obtain our local variables at this point in execution VariableValuePair[] variablePairs = Array.Empty <VariableValuePair>(); if (isLocalVariableScope) { variablePairs = threadState.ExecutionTraceAnalysis.GetLocalVariables(traceIndex); } else if (isStateVariableScope) { variablePairs = threadState.ExecutionTraceAnalysis.GetStateVariables(traceIndex); } 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(responder.Arguments.VariablesReference, variablePairReferenceId, threadId, underlyingVariableValuePair); } // Obtain the value string for this variable and add it to our list. string variableValueString = GetVariableValueString(underlyingVariableValuePair); variableList.Add(new Variable($"[{i}]", variableValueString, variablePairReferenceId)); } 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(new Variable($"[{i}]", bytes.Span[i].ToString(CultureInfo.InvariantCulture), 0)); } 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(responder.Arguments.VariablesReference, variablePairReferenceId, threadId, underlyingVariableValuePair); } // Obtain the value string for this variable and add it to our list. string variableValueString = GetVariableValueString(underlyingVariableValuePair); variableList.Add(new Variable(variablePair.Variable.Name, variableValueString, variablePairReferenceId)); } // Respond with our variable list. responder.SetResponse(new VariablesResponse(variableList)); }
protected override void HandleStackTraceRequestAsync(IRequestResponder <StackTraceArguments, StackTraceResponse> responder) { // Create a list of stack frames or try to get cached ones. List <StackFrame> stackFrames; bool cachedStackFrames = ReferenceContainer.TryGetStackFrames(responder.Arguments.ThreadId, out stackFrames); // Verify we have a thread state for this thread, and a valid step to represent in it. if (!cachedStackFrames && ThreadStates.TryGetValue(responder.Arguments.ThreadId, out var threadState) && threadState.CurrentStepIndex.HasValue) { // Initialize our stack frame list stackFrames = new List <StackFrame>(); // Obtain the callstack var callstack = threadState.ExecutionTraceAnalysis.GetCallStack(threadState.CurrentStepIndex.Value); // Loop through our scopes. for (int i = 0; i < callstack.Length; i++) { // Grab our current call frame var currentStackFrame = callstack[i]; // If the scope could not be resolved within a function, and no lines could be resolved, skip to the next frame. // as this is not a code section we can describe in any meaningful way. if (!currentStackFrame.ResolvedFunction && currentStackFrame.CurrentPositionLines.Length == 0) { continue; } // If we couldn't resolve the current position or there were no lines representing it if (currentStackFrame.Error || currentStackFrame.CurrentPositionLines.Length == 0) { continue; } // Obtain the method name we are executing in. string frameName = currentStackFrame.FunctionName; if (string.IsNullOrEmpty(frameName)) { frameName = "<undefined>"; } // Determine the bounds of our stack frame. int startLine = 0; int startColumn = 0; int endLine = 0; int endColumn = 0; // Loop through all of our lines for this position. for (int x = 0; x < currentStackFrame.CurrentPositionLines.Length; x++) { // Obtain our indexed line. SourceFileLine line = currentStackFrame.CurrentPositionLines[x]; // Set our start position if relevant. if (x == 0 || line.LineNumber <= startLine) { // Set our starting line number. startLine = line.LineNumber; // TODO: Determine our column start } // Set our end position if relevant. if (x == 0 || line.LineNumber >= endLine) { // Set our ending line number. endLine = line.LineNumber; // TODO: Determine our column endColumn = line.Length; } } // Format agnostic path to platform specific path var sourceFilePath = currentStackFrame.CurrentPositionLines[0].SourceFileMapParent.SourceFilePath; if (Path.DirectorySeparatorChar == '\\') { sourceFilePath = sourceFilePath.Replace('/', Path.DirectorySeparatorChar); } // Create our source object Source stackFrameSource = new Source() { Name = currentStackFrame.CurrentPositionLines[0].SourceFileMapParent.SourceFileName, Path = Path.Join(ConfigurationProperties.WorkspaceDirectory, _contractsDirectory, sourceFilePath) }; var stackFrame = new StackFrame() { Id = ReferenceContainer.GetUniqueId(), Name = frameName, Line = startLine, Column = startColumn, Source = stackFrameSource, EndLine = endLine, EndColumn = endColumn }; // Add the stack frame to our reference list ReferenceContainer.LinkStackFrame(threadState.ThreadId, stackFrame, currentStackFrame.CurrentPositionTraceIndex); // Add our stack frame to the result list stackFrames.Add(stackFrame); } } // Return our stack frames in our response. responder.SetResponse(new StackTraceResponse(stackFrames)); }