Partilhar via


Implementar função intrínseca personalizada NatVis para C++

Neste artigo, você aprenderá sobre as diretrizes para implementar uma função intrínseca personalizada em uma visualização NatVis. Para obter mais informações sobre o NatVis, consulte Criar exibições personalizadas de objetos C++.

Sintaxe

Um arquivo NatVis pode definir uma função intrínseca usando a seguinte sintaxe:

<Intrinsic Name="Func" Expression="arg + 1">
  <Parameter Name="arg" Type="int" />
</Intrinsic>

Uma vez que a definição existe, qualquer expressão do depurador pode chamar a função, como qualquer outra função. Por exemplo, usando a definição NatVis anterior, a expressão Func(3) é avaliada como 4.

Um elemento <Intrinsic> pode aparecer no nível do arquivo ou dentro de um elemento <Type>, antes de quaisquer outros elementos. As funções intrínsecas definidas dentro de um <Type> definem funções de membro desse tipo, enquanto as funções intrínsecas definidas fora de um <Type> definem funções globais. Por exemplo:

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="std::vector&lt;*&gt;">
    <Intrinsic Name="size" Expression="_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst" />
    <Intrinsic Name="capacity" Expression="_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst" />
  </Type>
  <Intrinsic Name="LOWORD" Expression="arg & 0xFFFF">
    <Parameter Name="arg" Type="unsigned int" />
  </Intrinsic>
</AutoVisualizer>

No exemplo anterior, size() e capacity() são definidos como funções de membro da classe std::vector, portanto, sempre que uma expressão avalia vector.size(), ela realmente avaliará vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst, em vez de executar um func-eval. Da mesma forma, LOWORD(0x12345678) retorna 0x5678, também sem func-eval.

Para outro exemplo, consulte a expansão intrínseca .

Orientações para a utilização de uma função intrínseca

Esteja ciente das seguintes diretrizes ao usar uma função intrínseca:

  • As funções intrínsecas podem ser sobrecarregadas, seja com funções definidas pelo PDB, seja entre si.

  • Quando uma função intrínseca entra em conflito com uma função definida pelo PDB, com o mesmo nome e lista de argumentos, a função intrínseca prevalecerá. Não é possível avaliar a função PDB se existir uma função intrínseca equivalente.

  • Você não pode obter o endereço de uma função intrínseca; você só pode chamá-la.

  • As funções de membro intrínsecas devem pertencer à visualização padrão do respetivo tipo para serem avaliadas em uma expressão. Por exemplo, uma entrada de tipo com uma restrição de IncludeView pode não especificar uma função intrínseca.

  • As funções intrínsecas podem ser chamadas a partir de qualquer expressão, incluindo expressões NatVis. As funções intrínsecas também podem se chamar umas às outras. No entanto, as funções intrínsecas que usam recursão não são suportadas no momento. Por exemplo:

    <!-- OK -->
    <Intrinsic Name="Func2" Expression="this-&gt;Func3(arg + 1)">
      <Parameter Name="arg" Type="int" />
    </Intrinsic>
    <Intrinsic Name="Func3" Expression="arg + 1">
      <Parameter Name="arg" Type="int"/>
    </Intrinsic>
    
    <!-- Unsupported -->
    <Intrinsic Name="Func2" Expression="this-&gt;Func3(arg + 1)">
      <Parameter Name="arg" Type="int" />
    </Intrinsic>
    <Intrinsic Name="Func3" Expression="Func2(arg + 1)">
      <Parameter Name="arg" Type="int"/>
    </Intrinsic>
    
    <!-- Also unsupported-->
    <Intrinsic Name="Fib" Expression="(n &lt;= 1) ? 1 : Fib(n - 1) + Fib(n - 2)">
      <Parameter Name="n" Type="int"/>
    </Intrinsic>
    
  • Por padrão, as funções intrínsecas são assumidas como livres de efeitos colaterais. Ou seja, eles podem ser chamados em contextos que não permitem efeitos colaterais, e a expressão de implementação não pode conter efeitos colaterais.

    A definição pode substituir esse comportamento especificando o atributo SideEffect na declaração. Se a função estiver marcada como tendo efeitos secundários, os efeitos secundários na expressão de implementação tornam-se permitidos. No entanto, invocar a função em contextos onde os efeitos secundários são proibidos já não é permitido.

  • Quando as definições de duas funções intrínsecas entram em conflito entre si (mesmo nome, mesma assinatura), a última ganha (dentro do mesmo arquivo).

    Dentro de arquivos diferentes, a instância no arquivo com prioridade mais alta ganha (o projeto é maior do que o diretório do usuário, que é maior do que o diretório de instalação). Se uma definição de prioridade mais alta contiver uma expressão que não possa ser processada, essa definição será ignorada, e a definição com a próxima maior prioridade será usada.

Diretrizes para implementar uma função intrínseca

As funções intrínsecas suportam duas formas possíveis de implementação:

  • Baseado em expressões

    O arquivo NatVis define uma expressão que avalia o valor de retorno da função. A expressão pode usar quaisquer argumentos passados nela declarados como elementos <Parameter>. As funções definidas dentro de uma classe também são assumidas como funções de "instância" e podem acessar o ponteiro "this" também.

  • Baseado em extensões

    O arquivo NatVis fornece instruções para invocar uma extensão do depurador para realmente avaliar a função. Uma extensão do depurador tem acesso total à API do Concord e tem a capacidade de executar tarefas que não são possíveis dentro de uma expressão NatVis.

Para fornecer uma implementação baseada em extensão, o elemento <Intrinsic> deve omitir o atributo Expression e, em vez disso, fornecer atributos SourceId, LanguageId, Ide ReturnType, conforme fornecido no exemplo a seguir:

<Intrinsic Name="MyFunc" SourceId="a665fa54-6e7d-480e-a80b-1fc1202e9646" LanguageId="3a12d0b7-c26c-11d0-b442-00a0244a1dd2" Id="1000" ReturnType="double">
  <Parameter Type="int" />
  <Parameter Type="int" />
  <Parameter Type="int" />
</Intrinsic>

Para implementar a função, a extensão do depurador deve implementar a interface IDkmIntrinsicFunctionEvaluator140, usando filtros LanguageId e SourceId que correspondam aos valores correspondentes do elemento <Intrinsic> no arquivo NatVis. Quando a função é chamada, a chamada se traduz no método Execute() do componente:

STDMETHOD(Execute)(
    _In_ Evaluation::IL::DkmILExecuteIntrinsic* pExecuteIntrinsic,
    _In_ Evaluation::DkmILContext* pILContext,
    _In_ Evaluation::IL::DkmCompiledILInspectionQuery* pInspectionQuery,
    _In_ const DkmArray<Evaluation::IL::DkmILEvaluationResult*>& Arguments,
    _In_opt_ DkmReadOnlyCollection<Evaluation::DkmCompiledInspectionQuery*>* pSubroutines,
    _Out_ DkmArray<Evaluation::IL::DkmILEvaluationResult*>* pResults,
    _Out_ Evaluation::IL::DkmILFailureReason* pFailureReason
    );

O componente recebe os bytes de cada argumento por meio do argumento Arguments. Se a função em questão for uma função membro, o ponteiro this vem primeiro, seguido pelos argumentos explícitos. O componente deve retornar o resultado alocando uma matriz de elemento único em pResults, armazenando os bytes do valor de retorno.

Use as seguintes diretrizes para implementar funções:

  • É ilegal "misturar e combinar" as duas formas de implementação. Ou seja, você não pode incluir uma expressão e um ID de origem.

  • Especificar um tipo de retorno para uma implementação baseada em expressão é permitido, mas não obrigatório. Se um tipo de retorno for especificado, o tipo de retorno da expressão deverá corresponder exatamente a ele (nenhuma transmissão implícita permitida). Se um tipo de retorno não for especificado, o tipo de retorno será inferido da expressão. Qualquer implementação baseada em extensão deve indicar um tipo de retorno no arquivo NatVis.

  • São permitidas chamadas não recursivas para outras funções intrínsecas. A recursão não é permitida.

  • Se a função tiver efeitos colaterais, você deve especificar SideEffect="true" na declaração. É ilegal para uma implementação baseada em expressão ter efeitos colaterais na expressão sem declarar que a função tem efeitos colaterais. Invocar uma implementação baseada em extensão para ter efeitos colaterais sem declarar a função como tendo efeitos colaterais é um comportamento indefinido e deve ser evitado.

  • As funções intrínsecas de Varargs são permitidas. Para declarar uma função varargs, especifique Varargs="true" na declaração. Embora seja legal para uma implementação baseada em expressão declarar uma função vararg, atualmente, apenas implementações baseadas em extensão têm uma maneira de acessar os argumentos variáveis. Com uma implementação baseada em extensão, a função Execute() recebe todos os argumentos que são realmente passados, não apenas os argumentos declarados.

  • Não há suporte para funções intrínsecas que consomem um tipo de classe, estrutura ou união como argumento. Retornar um tipo de classe/struct/union é OK. (Um ponteiro ou referência a um tipo de classe/estrutura/união é OK como um tipo de argumento).

Personalize o ícone para chamadas para funções intrínsecas.

Por padrão, quando você chama uma função intrínseca, a expressão recebe o ícone de diamante rosa na janela de observação do associada a chamadas de função. Você pode substituir esse comportamento especificando o atributo Category usando um dos seguintes valores:

  • Método. Use o ícone de diamante rosa, normalmente usado com chamadas de método (padrão).
  • Propriedade. Use o ícone de chave preta, normalmente usado com propriedades.
  • Dados. Use o ícone de diamante azul, normalmente usado com dados.

Ao combinar funções intrínsecas com o elemento <Item>, é possível criar um arquivo NatVis onde as expressões de item têm o ícone da propriedade wrench:

<Type Name="MyClass">
  <Intrinsic Name="GetValue" ReturnType="int" Expression="m_value" Category="Property" />
  <Expand>
    <Item Name="Value">this-&gt;GetValue()</Item>
  </Expand>
</Type>

Observação

Colocar a escolha do ícone no nível da função, em vez do nível <Item>, evita problemas em que a personalização do ícone é perdida quando o nome completo é avaliado. Como o nome completo inclui uma chamada para a função, ele tem a mesma personalização de ícone que o próprio <Item>.