コード例 #1
0
 public static ErrorVariableInformation LogAndGetEvaluationError(
     NatvisDiagnosticLogger logger, string natvisType, string parentType, string displayName,
     string errorCause)
 {
     logger.Error(() => $"Failed to evaluate {natvisType} node" +
                  $" for {displayName}, type: {parentType}.");
     return(new ErrorVariableInformation(displayName, $"<Error> Reason: {errorCause}"));
 }
コード例 #2
0
        /// <summary>
        /// Loads Natvis files from string. This method is used in tests.
        /// </summary>
        /// <param name="natvisText">String with Natvis specification.</param>
        /// <param name="typeVisualizers">Type visualizers to initialize.</param>
        public void LoadFromString(string natvisText,
                                   ICollection <NatvisVisualizerScanner.FileInfo> typeVisualizers)
        {
            try
            {
                using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(natvisText)))
                {
                    LoadFromStream(stream, "<From String>", typeVisualizers);
                }

                using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(natvisText)))
                {
                    NatvisValidator validator = _validatorFactory.Create();
                    validator.Validate(stream);
                }
            }
            catch (InvalidOperationException ex)
            {
                // Handles invalid XML errors.
                // Don't allow natvis failures to stop debugging.
                var reason = ex.InnerException != null
                                 ? $"{ex.Message}: {ex.InnerException.Message}"
                                 : $"{ex.Message}";

                TraceWriteLine(NatvisLoggingLevel.ERROR,
                               $"Failed to load Natvis text. Reason: {reason}" +
                               $"{Environment.NewLine}Stacktrace:{ex.StackTrace}");
            }
            catch (Exception ex)
            {
                // TODO: Ensure 'unhandled' exceptions are logged at a higher level, such
                // as a global error handler.
                _logger.Error(
                    $"Failed to load Natvis text. Reason: {ex.Message}" +
                    $"{Environment.NewLine}Text:{Environment.NewLine}{natvisText}" +
                    $"{Environment.NewLine}Stacktrace:{Environment.NewLine}{ex.StackTrace}");
                throw;
            }
        }
コード例 #3
0
        /// <summary>
        /// Asynchronously returns a formatted string based on the format string context and
        /// variable provided.
        /// In case this method does not succeeded, it returns the fallback value specified.
        /// </summary>
        /// <param name="formatStringContext">The format string context that the formatted string
        /// should rely on</param>
        /// <param name="variable">The variable context used to evaluate expressions.</param>
        /// <param name="subexpressionFormatter">Delegate used to format subexpressions found
        /// within the string.</param>
        /// <param name="elementName">The Natvis element name that should be reported in logs.
        /// </param>
        /// <param name="fallbackValue">Fallback value used in case this method fails.</param>
        internal async Task <string> FormatStringAsync(
            FormatStringContext formatStringContext, IVariableInformation variable,
            Func <IVariableInformation, Task <string> > subexpressionFormatter,
            string elementName, Func <Task <string> > fallbackValue)
        {
            try
            {
                if (++_curFormatStringElementDepth > _maxFormatDepth)
                {
                    return("...");
                }

                foreach (var element in formatStringContext.StringElements)
                {
                    try
                    {
                        // e.g. <DisplayString>{{ size={_Mypair._Myval2._Mylast -
                        // _Mypair._Myval2._Myfirst} }}</DisplayString>
                        if (!NatvisViewsUtil.IsViewVisible(variable.FormatSpecifier,
                                                           element.IncludeView,
                                                           element.ExcludeView) ||
                            !await _evaluator.EvaluateConditionAsync(
                                element.Condition, variable, formatStringContext.NatvisScope))
                        {
                            continue;
                        }

                        return(await FormatValueAsync(element.Value, variable,
                                                      formatStringContext.NatvisScope,
                                                      subexpressionFormatter));
                    }
                    catch (ExpressionEvaluationFailed ex)
                    {
                        if (!element.Optional)
                        {
                            throw;
                        }

                        string expression = variable == null ? null : await variable.ValueAsync();

                        _logger.Verbose(
                            () => $"Failed to evaluate natvis {elementName} expression" +
                            $"  '{expression}' for type " +
                            $"'{variable?.TypeName}'. Reason: {ex.Message}");
                    }
                    catch (Exception ex) when(ex is NotSupportedException ||
                                              ex is InvalidOperationException)
                    {
                        _logger.Log(NatvisLoggingLevel.ERROR,
                                    $"Failed to format natvis {elementName}. " +
                                    $"Reason: {ex.Message}.");

                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(() =>
                                      $"Failed to format natvis {elementName} for type" +
                                      $" '{variable?.TypeName}'. " +
                                      $"Reason: {ex.Message}.{Environment.NewLine}" +
                                      $"Stacktrace:{Environment.NewLine}{ex.StackTrace}");

                        throw;
                    }
                }

                return(await fallbackValue.Invoke());
            }
            finally
            {
                --_curFormatStringElementDepth;
            }
        }
コード例 #4
0
        /// <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);
        }