示例#1
0
        public async Task <EvaluationResult> EvaluateExpressionAsync()
        {
            VsExpression vsExpression =
                await _vsExpressionCreator.CreateAsync(_text, EvaluateSizeSpecifierExpressionAsync);

            IDebugProperty2 result;

            if (vsExpression.Value.StartsWith("."))
            {
                EvaluateCommand(vsExpression.Value, out result);
                return(EvaluationResult.FromResult(result));
            }

            RemoteValue remoteValue = await CreateValueFromExpressionAsync(vsExpression.Value);

            if (remoteValue == null)
            {
                return(EvaluationResult.Fail());
            }

            string displayName           = vsExpression.ToString();
            IVariableInformation varInfo =
                _varInfoBuilder.Create(remoteValue, displayName, vsExpression.FormatSpecifier);

            result = _createPropertyDelegate.Invoke(varInfo);

            return(EvaluationResult.FromResult(result));
        }
        /// <summary>
        /// Tries to resolve the given expression assuming it is a member of the given variable. If
        /// the expression is not a member of the given variable, null is returned. Otherwise, a
        /// variable resulting from evaluating the expression is returned.
        /// </summary>
        IVariableInformation GetValueForMemberAccessExpression(IVariableInformation variable,
                                                               VsExpression vsExpression,
                                                               string displayName)
        {
            if (!vsExpression.Value.StartsWith("["))
            {
                vsExpression = vsExpression.Clone((variable.IsPointer
                                                      ? "->"
                                                      : ".") + vsExpression.Value);
            }

            if (!NatvisTextMatcher.IsExpressionPath(vsExpression.Value))
            {
                return(null);
            }

            var value = variable.GetValueForExpressionPath(vsExpression);

            if (value == null || value.Error)
            {
                return(null);
            }

            if (displayName != null)
            {
                return(new NamedVariableInformation(value, displayName));
            }

            return(value);
        }
        /// <summary>
        /// Evaluates an LLDB expression. It decides which expression evaluation method to use
        /// (e.g. LLDB, lldb-eval, path expression, etc.) depending on the Stadia SDK settings and
        /// the input |expression|. It doesn't support format specifiers, only expressions that
        /// can be directly evaluated in the LLDB environment.
        /// </summary>
        /// <param name="expression">The expression to be evaluated.</param>
        /// <param name="variable">The evaluation context.</param>
        /// <param name="natvisScope">The Natvis tokens to be resolved before evaluation.</param>
        /// <param name="displayName">The display name given to the result. If null the underlying
        /// debugger's context specific name is used.</param>
        /// <returns>The expression result.</returns>
        async Task <IVariableInformation> EvaluateLldbExpressionAsync(VsExpression expression,
                                                                      IVariableInformation variable,
                                                                      NatvisScope natvisScope,
                                                                      string displayName)
        {
            ExpressionEvaluationStrategy strategy = _extensionOptions.ExpressionEvaluationStrategy;
            var stepsRecorder = new ExpressionEvaluationRecorder.StepsRecorder(_timeSource);

            long startTimestampUs = _timeSource.GetTimestampUs();

            IVariableInformation variableInformation =
                await EvaluateLldbExpressionWithMetricsAsync(
                    expression, variable, natvisScope, displayName, strategy, stepsRecorder);

            // Evaluating a context variable will just return the reference to it. Because of
            // deferred evaluation of display values, some values could be incorrectly displayed
            // (in the case a context variable was changed in between two expression evaluations).
            // In order to prevent this, we create a copy of result if the expression was simply
            // a context variable.
            if (natvisScope.IsContextVariable(expression.Value))
            {
                variableInformation = variableInformation.Clone(expression.FormatSpecifier);
            }

            long endTimestampUs = _timeSource.GetTimestampUs();

            _expressionEvaluationRecorder.Record(strategy, ExpressionEvaluationContext.VALUE,
                                                 stepsRecorder, startTimestampUs, endTimestampUs,
                                                 variable.Id);

            return(variableInformation);
        }
        /// <summary>
        /// Try to evaluate the given expression in the context of the variable.
        /// Imagine the variable is a class and the expression is in a method of that class.
        /// Member variables can be accessed and take precedence over global variables of the same name.
        /// Globals can be accessed as well. Returns a variable representing the result of the evaluation
        /// or null if the expression can't be evaluated.
        /// </summary>
        Task <IVariableInformation> EvaluateExpressionInVariableScopeAsync(
            IVariableInformation variable, VsExpression vsExpression, string displayName)
        {
            if (variable.IsPointer || variable.IsReference)
            {
                variable = variable.Dereference();
            }

            return(variable.EvaluateExpressionAsync(displayName, vsExpression));
        }
示例#5
0
        public async Task <IVariableInformation> EvaluateExpressionAsync(string displayName,
                                                                         VsExpression vsExpression)
        {
            RemoteValue resultValue =
                await _remoteValue.EvaluateExpressionAsync(vsExpression.Value);

            return(resultValue == null
                       ? null
                       : _varInfoBuilder.Create(resultValue, displayName,
                                                formatSpecifier: vsExpression.FormatSpecifier));
        }
示例#6
0
        public async Task <IVariableInformation> EvaluateExpressionLldbEvalAsync(
            string displayName, VsExpression vsExpression,
            IDictionary <string, RemoteValue> contextVariables = null)
        {
            RemoteValue resultValue = await _remoteValue.EvaluateExpressionLldbEvalAsync(
                vsExpression.Value, contextVariables);

            return(resultValue == null
                       ? null
                       : _varInfoBuilder.Create(resultValue, displayName,
                                                formatSpecifier: vsExpression.FormatSpecifier));
        }
示例#7
0
        public IVariableInformation GetValueForExpressionPath(VsExpression vsExpression)
        {
            RemoteValue expressionValue =
                _remoteValue.GetValueForExpressionPath(vsExpression.Value);

            if (expressionValue == null)
            {
                return(null);
            }

            return(_varInfoBuilder.Create(expressionValue,
                                          formatSpecifier: vsExpression.FormatSpecifier));
        }
示例#8
0
        public async Task <IVariableInformation> CreateValueFromExpressionAsync(string displayName,
                                                                                VsExpression
                                                                                vsExpression)
        {
            RemoteValue expressionValue =
                await _remoteValue.CreateValueFromExpressionAsync(displayName, vsExpression.Value);

            if (expressionValue == null)
            {
                return(null);
            }

            return(_varInfoBuilder.Create(expressionValue,
                                          formatSpecifier: vsExpression.FormatSpecifier));
        }
        /// <summary>
        /// Declare a variable in using the given variable scope to execute the value expression.
        /// Token replacement using scopedNames is done against both the variable name and the
        /// value expression.
        /// </summary>
        /// <exception cref="ExpressionEvaluationFailed">
        /// Expression to declare the variable failed to evaluate.
        /// </exception>
        public async Task DeclareVariableAsync(IVariableInformation variable, string variableName,
                                               string valueExpression, NatvisScope natvisScope)
        {
            string scratchVar =
                ReplaceScopedNames(variableName, natvisScope?.ScopedNames, out bool ignore);
            VsExpression vsExpression = _vsExpressionCreator.Create(valueExpression, "")
                                        .MapValue(e => ReplaceScopedNames(e, natvisScope?.ScopedNames, out ignore));

            // Declare variable and return it. Pure declaration expressions will always return
            // error because these expressions don't return a valid value.
            VsExpression createExpression =
                vsExpression.MapValue(e => $"auto {scratchVar}={e}; {scratchVar}");

            if (variable.IsPointer || variable.IsReference)
            {
                variable = variable.Dereference();
                if (variable == null)
                {
                    string failMsg = $"Failed to dereference pointer: Name: {variableName}";
                    _logger.Error(failMsg);
                    throw new ExpressionEvaluationFailed(failMsg);
                }
            }

            // TODO: Split the logic for LLDB and lldb-eval. Currently, LLDB is always
            // used to create a scratch variable (even if lldb-eval is the selected engine).
            IVariableInformation result =
                await variable.EvaluateExpressionAsync(variableName, createExpression);

            if (result != null && !result.Error && natvisScope != null)
            {
                // Result of 'auto {scratchVar}={e}; {scratchVar}' creates a copy of the scratch
                // variable. Evaluating '{scratchVar}' returns the reference to the original
                // variable. By using the original variable we make sure that the we always use its
                // up-to-date value.
                // TODO: Use RemoteFrame.FindValue to get the scratch variable.
                // EvaluateExpression method already is optimised for the case of fetching scratch
                // variables, but it isn't a convenient one.
                result = await variable.EvaluateExpressionAsync(
                    variableName, _vsExpressionCreator.Create($"{scratchVar}", ""));

                if (result != null && !result.Error)
                {
                    natvisScope.AddContextVariable(scratchVar, result.GetRemoteValue());
                    return;
                }
            }

            string msg = $"Failed to declare variable: Name: {variableName}, " +
                         $"Expression: {valueExpression}";

            string resultMessage = result?.ErrorMessage;

            if (!string.IsNullOrEmpty(resultMessage))
            {
                msg += $", Info: {{{resultMessage}}}";
            }

            _logger.Error(msg);
            throw new ExpressionEvaluationFailed(msg);
        }
        async Task <IVariableInformation> EvaluateLldbExpressionWithMetricsAsync(
            VsExpression expression, IVariableInformation variable, NatvisScope natvisScope,
            string displayName, ExpressionEvaluationStrategy strategy,
            ExpressionEvaluationRecorder.StepsRecorder stepsRecorder)
        {
            bool variableReplaced = false;

            expression = expression.MapValue(
                v => ReplaceScopedNames(v, natvisScope?.ScopedNames, out variableReplaced));

            var lldbErrors = new List <string>();

            // A helper lambda function to construct an exception given the list of lldb errors.
            Func <IList <string>, ExpressionEvaluationFailed> createExpressionEvaluationException =
                errors =>
            {
                var exceptionMsg =
                    $"Failed to evaluate expression, display name: {displayName}, " +
                    $"expression: {expression}";

                errors = errors.Where(error => !string.IsNullOrEmpty(error)).ToList();
                if (errors.Any())
                {
                    exceptionMsg += $", info: {{{string.Join("; ", errors)}}}";
                }

                return(new ExpressionEvaluationFailed(exceptionMsg));
            };

            if (strategy == ExpressionEvaluationStrategy.LLDB_EVAL ||
                strategy == ExpressionEvaluationStrategy.LLDB_EVAL_WITH_FALLBACK)
            {
                IVariableInformation value;
                LldbEvalErrorCode    errorCode;

                using (var step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB_EVAL))
                {
                    value = await variable.EvaluateExpressionLldbEvalAsync(
                        displayName, expression, natvisScope.ContextVariables);

                    errorCode = (LldbEvalErrorCode)Enum.ToObject(typeof(LldbEvalErrorCode),
                                                                 value.ErrorCode);

                    step.Finalize(errorCode);
                }

                if (errorCode == LldbEvalErrorCode.Ok)
                {
                    value.FallbackValueFormat = variable.FallbackValueFormat;
                    return(value);
                }

                lldbErrors.Add(value?.ErrorMessage);

                if (errorCode == LldbEvalErrorCode.InvalidNumericLiteral ||
                    errorCode == LldbEvalErrorCode.InvalidOperandType ||
                    errorCode == LldbEvalErrorCode.UndeclaredIdentifier)
                {
                    // In the case of a well-known error, there's no need to fallback to
                    // LLDB, as it will fail with the same error.
                    throw createExpressionEvaluationException(lldbErrors);
                }
            }

            if (strategy == ExpressionEvaluationStrategy.LLDB)
            {
                // If lldb-eval is not enabled, try to interpret the expression as member access
                // before using LLDB to evaluate the expression in the context of the variable.
                IVariableInformation value;
                LLDBErrorCode        errorCode;

                using (var step =
                           stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB_VARIABLE_PATH))
                {
                    value     = GetValueForMemberAccessExpression(variable, expression, displayName);
                    errorCode = value != null && !value.Error
                        ? LLDBErrorCode.OK
                        : LLDBErrorCode.ERROR;

                    step.Finalize(errorCode);
                }

                if (errorCode == LLDBErrorCode.OK)
                {
                    value.FallbackValueFormat = variable.FallbackValueFormat;
                    return(value);
                }

                lldbErrors.Add(value?.ErrorMessage);
            }

            if (strategy == ExpressionEvaluationStrategy.LLDB ||
                strategy == ExpressionEvaluationStrategy.LLDB_EVAL_WITH_FALLBACK)
            {
                IVariableInformation value;
                LLDBErrorCode        errorCode;

                using (var step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB))
                {
                    value = await EvaluateExpressionInVariableScopeAsync(
                        variable, expression, displayName);

                    errorCode = value != null && !value.Error
                        ? LLDBErrorCode.OK
                        : LLDBErrorCode.ERROR;

                    step.Finalize(errorCode);
                }

                if (errorCode == LLDBErrorCode.OK)
                {
                    value.FallbackValueFormat = variable.FallbackValueFormat;
                    return(value);
                }

                lldbErrors.Add(value?.ErrorMessage);
            }

            throw createExpressionEvaluationException(lldbErrors);
        }