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