/// <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);
        }
Beispiel #2
0
        async Task <RemoteValue> CreateValueFromExpressionAsync(string expression)
        {
            var stepsRecorder = new ExpressionEvaluationRecorder.StepsRecorder(_timeSource);

            long        startTimestampUs = _timeSource.GetTimestampUs();
            RemoteValue remoteValue      =
                await CreateValueFromExpressionWithMetricsAsync(expression, stepsRecorder);

            long endTimestampUs = _timeSource.GetTimestampUs();

            _expressionEvaluationRecorder.Record(_expressionEvaluationStrategy,
                                                 _expressionEvaluationContext, stepsRecorder,
                                                 startTimestampUs, endTimestampUs);

            return(remoteValue);
        }
        public void RecordSingleEventWithTwoStepsTest()
        {
            const ExpressionEvaluationStrategy strategySource = ExpressionEvaluationStrategy.LLDB;
            const ExpressionEvaluationContext  contextSource  = ExpressionEvaluationContext.FRAME;

            var stepsRecorder = new ExpressionEvaluationRecorder.StepsRecorder(_timeSource);

            using (Step step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB_VARIABLE_PATH))
            {
                step.Finalize(LLDBErrorCode.ERROR);
            }

            using (Step step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB))
            {
                step.Finalize(LLDBErrorCode.OK);
            }

            const long startTimestampUs = 750;
            const long endTimestampUs   = 21562;

            _expressionEvaluationRecorder.Record(strategySource, contextSource, stepsRecorder,
                                                 startTimestampUs, endTimestampUs);

            // Get a copy of the batch summary sent to batchEventAggregator so we can verify
            // that it matches the one being sent to metrics.
            ExpressionEvaluationBatchSummary batchSummary = null;

            _batchEventAggregator.BatchSummaryReady += (_, newSummary) => batchSummary = newSummary;

            _timer.Increment(_minimumBatchSeparationMilliseconds);
            _eventScheduler.Increment(_minimumBatchSeparationMilliseconds);

            const ExpressionEvaluation.Types.Strategy strategyExpected =
                ExpressionEvaluation.Types.Strategy.Lldb;
            const ExpressionEvaluation.Types.Context contextExpected = ExpressionEvaluation.Types
                                                                       .Context.Frame;

            Assert.AreEqual(1, batchSummary.Proto.ExpressionEvaluations.Count);
            ExpressionEvaluation received = batchSummary.Proto.ExpressionEvaluations[0];

            Assert.Multiple(() =>
            {
                Assert.AreEqual(strategyExpected, received.Strategy);
                Assert.AreEqual(contextExpected, received.Context);
                Assert.NotNull(received.EvaluationSteps);
                Assert.AreEqual(2, received.EvaluationSteps.Count);
                Assert.AreEqual(startTimestampUs, received.StartTimestampMicroseconds);
                Assert.AreEqual(endTimestampUs, received.EndTimestampMicroseconds);
                Assert.Null(received.NatvisValueId);
            });

            const ExpressionEvaluationStep.Types.Engine firstStepEngineExpected =
                ExpressionEvaluationStep.Types.Engine.LldbVariablePath;
            const ExpressionEvaluationStep.Types.EngineResult firstStepEngineResultExpected =
                ExpressionEvaluationStep.Types.EngineResult.LldbError;
            const ExpressionEvaluationStep.Types.Engine secondStepEngineExpected =
                ExpressionEvaluationStep.Types.Engine.Lldb;
            const ExpressionEvaluationStep.Types.EngineResult secondStepEngineResultExpected =
                ExpressionEvaluationStep.Types.EngineResult.LldbOk;

            ExpressionEvaluationStep firstStep  = received.EvaluationSteps[0];
            ExpressionEvaluationStep secondStep = received.EvaluationSteps[1];

            Assert.Multiple(() =>
            {
                Assert.AreEqual(firstStepEngineExpected, firstStep.Engine);
                Assert.AreEqual(firstStepEngineResultExpected, firstStep.Result);
                Assert.AreEqual(1, firstStep.DurationMicroseconds);
                Assert.AreEqual(secondStepEngineExpected, secondStep.Engine);
                Assert.AreEqual(secondStepEngineResultExpected, secondStep.Result);
                Assert.AreEqual(1, secondStep.DurationMicroseconds);
            });

            _metrics.Received(1)
            .RecordEvent(DeveloperEventType.Types.Type.VsiDebugExpressionEvaluationBatch,
                         new DeveloperLogEvent
            {
                DebugExpressionEvaluationBatch = batchSummary.Proto,
                StatusCode = DeveloperEventStatus.Types.Code.Success
            });
        }