Compartilhar via


Implementar a função intrínseca personalizada do 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>

Depois que a definição existir, qualquer expressão de depurador poderá 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 qualquer outro elemento. Funções intrínsecas definidas em um <Type> definem funções membro desse tipo, enquanto 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 membro da classe std::vector, portanto, sempre que uma expressão avaliar vector.size(), ela na realidade avaliará vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst, em vez de executar uma avaliação de função. Da mesma forma, LOWORD(0x12345678) retorna 0x5678, também sem avaliação de função.

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

Diretrizes para usar uma função intrínseca

Lembre-se das seguintes diretrizes ao usar uma função intrínseca:

  • As funções intrínsecas podem ser sobrecarregadas, com funções definidas pelo PDB ou 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 será ganha. Você não poderá 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 à exibição padrão do respectivo tipo para serem avaliadas em uma expressão. Por exemplo, uma entrada de tipo com uma restrição IncludeView pode não especificar uma função intrínseca.

  • Funções intrínsecas podem ser chamadas a partir de quaisquer expressões, incluindo expressões NatVis. Funções intrínsecas também podem chamar umas às outras. No entanto, no momento, não há suporte para funções intrínsecas que usam recursão. 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 consideradas livres de efeito colateral. Ou seja, eles podem ser chamados em contextos que não permitem efeitos colaterais e a expressão de implementação não tem permissão para 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 colaterais, os efeitos colaterais na expressão de implementação serão permitidos. No entanto, invocar a função em contextos em que efeitos colaterais são proibidos não é mais permitido.

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

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

Diretrizes para implementar uma função intrínseca

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

  • Baseado em expressão

    O arquivo NatVis define uma expressão que é avaliada como o valor retornado da função. A expressão pode usar quaisquer argumentos passados para ela declarados como elementos <Parameter>. As funções definidas em uma classe são consideradas funções de "instance" e podem acessar o ponteiro "this".

  • Baseado em extensões

    O arquivo NatVis fornece instruções para invocar uma extensão de depurador para realmente avaliar a função. Uma extensão de depurador tem acesso total à API concord e tem a capacidade de executar tarefas que não são possíveis em 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 será o 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 retornado.

Use as seguintes diretrizes para implementar funções:

  • É ilegal "combinar os dois tipos" de formulários de implementação. Ou seja, você não pode incluir uma expressão e uma ID de origem.

  • Especificar um tipo de retorno para uma implementação baseada em expressão é permitido, mas não é necessário. Se um tipo de retorno for especificado, o tipo de retorno da expressão deverá corresponder exatamente a ela (nenhuma conversã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.

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

  • Se a função tiver efeitos colaterais, você deverá especificar SideEffect="true" na declaração. É ilegal que uma implementação baseada em expressão tenha 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.

  • Funções intrínsecas varargs são permitidas. Para declarar uma função varargs, especifique Varargs="true" na declaração. Embora seja legal que uma implementação baseada em expressão declare uma função vararg, atualmente, somente implementações baseadas em extensão têm uma maneira de acessar os argumentos da variável. 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/struct/união como um tipo de argumento. Retornar um tipo de classe/struct/união é aceitável. (Um ponteiro ou referência a um tipo de classe/struct/união pode ser usado 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 Inspeção 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 inglesa preta, geralmente usado com as propriedades.
  • Dados. Use o ícone de diamante azul, normalmente usado com dados.

Combinando funções intrínsecas com o elemento <Item>, é possível criar um arquivo NatVis em que 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>

Nota

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