Dela via


Implementera anpassad NatVis-funktion för C++

I den här artikeln får du lära dig mer om riktlinjerna för att implementera en anpassad inbyggd funktion i en NatVis-visualisering. Mer information om NatVis finns i Skapa anpassade vyer av C++-objekt.

Syntax

En NatVis-fil kan definiera en inbyggd funktion med hjälp av följande syntax:

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

När definitionen finns kan alla felsökningsuttryck anropa funktionen, precis som andra funktioner. Med hjälp av den föregående NatVis-definitionen utvärderas uttrycket Func(3) till 4.

Ett <Intrinsic> element kan visas antingen på filnivå eller inuti ett <Type>-element före andra element. Inbyggda funktioner som definieras i en <Type> definiera medlemsfunktioner av den typen, medan inbyggda funktioner som definieras utanför en <Type> definiera globala funktioner. Till exempel:

<?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>

I föregående exempel definieras size() och capacity() som medlemsfunktioner i klassen std::vector, så när ett uttryck utvärderas vector.size()utvärderas faktiskt vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirsti stället för att utföra en func-eval. På samma sätt returnerar LOWORD(0x12345678)0x5678, även utan func-eval.

Ett annat exempel finns i Intrinsisk expansion.

Riktlinjer för att använda en inbyggd funktion

Tänk på följande riktlinjer när du använder en inbyggd funktion:

  • Inbyggda funktioner kan vara överbelastade, antingen med PDB-definierade funktioner eller med varandra.

  • När en inbyggd funktion står i konflikt med en PDB-definierad funktion med samma namn och argumentlista vinner den inbyggda funktionen. Du kan inte utvärdera PDB-funktionen om det finns en motsvarande inbyggd funktion.

  • Du kan inte ta adressen till en inbyggd funktion. du kan bara anropa den.

  • Inbyggda medlemsfunktioner måste tillhöra standardvyn för respektive typ för att kunna utvärderas i ett uttryck. En typpost med en IncludeView begränsning kanske till exempel inte anger någon inbyggd funktion.

  • Inbyggda funktioner kan anropas från valfritt uttryck, inklusive NatVis-uttryck. Inbyggda funktioner kan också anropa varandra. Inbyggda funktioner som använder rekursion stöds dock för närvarande inte. Till exempel:

    <!-- 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>
    
  • Som standard antas inbyggda funktioner vara sidoeffektfria. Det betyder att de kan anropas i kontexter som inte tillåter biverkningar, och implementeringsuttrycket får inte innehålla biverkningar.

    Definitionen kan åsidosätta det här beteendet genom att ange attributet SideEffect i deklarationen. Om funktionen är markerad för att ha sidoeffekter, tillåts sidoeffekter i implementeringsuttrycket. Det är dock inte längre tillåtet att anropa funktionen i sammanhang där biverkningar är förbjudna.

  • När definitionerna av två inbyggda funktioner är i konflikt med varandra (samma namn, samma signatur) vinner den sista (i samma fil).

    I olika filer vinner instansen i filen med högre prioritet (projektet är högre än användarkatalogen, vilket är högre än installationskatalogen). Om en definition med högre prioritet innehåller ett uttryck som inte parsas ignoreras definitionen och den näst högsta prioritetsdefinitionen används i stället.

Riktlinjer för att implementera en inbyggd funktion

Inbyggda funktioner stöder två möjliga former av implementering:

  • Uttrycksbaserad

    NatVis-filen definierar ett uttryck som utvärderas till funktionens returvärde. Uttrycket kan använda alla argument som skickas in, och som deklareras som <Parameter>-element. Funktioner som definieras i en klass antas också vara "instansfunktioner" och kan komma åt "this-pekaren".

  • Tilläggsbaserad

    NatVis-filen innehåller instruktioner för att anropa ett felsökningstillägg för att faktiskt utvärdera funktionen. Ett felsökningstillägg har fullständig åtkomst till Concord API och har möjlighet att utföra uppgifter som inte är möjliga i ett NatVis-uttryck.

För att tillhandahålla en tilläggsbaserad implementering bör <Intrinsic>-elementet utelämna attributet Expression och i stället ange attributen SourceId, LanguageId, Idoch ReturnType enligt följande exempel:

<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>

För att implementera funktionen måste felsökningstillägget implementera IDkmIntrinsicFunctionEvaluator140-gränssnittet med hjälp av LanguageId och SourceId filter som matchar motsvarande värden för elementet <Intrinsic> i NatVis-filen. När funktionen anropas översätts anropet till komponentens Execute() metod:

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

Komponenten tar emot byteen för varje argument via argumentet Arguments. Om funktionen i fråga är en medlemsfunktion kommer pekaren this först, följt av de explicita argumenten. Komponenten ska returnera resultatet genom att allokera en matris med ett element i pResultsoch lagra byte för returvärdet.

Använd följande riktlinjer för att implementera funktioner:

  • Det är olagligt att "blanda och matcha" de två implementeringsformulären. Du kan alltså inte inkludera både ett uttryck och ett käll-ID.

  • Det är tillåtet att ange en returtyp för en uttrycksbaserad implementering, men krävs inte. Om en returtyp anges måste returtypen för uttrycket exakt matcha det (ingen implicit gjutning tillåts). Om en returtyp inte har angetts härleds returtypen från uttrycket. Alla tilläggsbaserade implementeringar måste ange en returtyp i NatVis-filen.

  • Icke-rekursiva anrop till andra inbyggda funktioner tillåts. Rekursion tillåts inte.

  • Om funktionen har biverkningar måste du ange SideEffect="true" i deklarationen. Det är olagligt för en uttrycksbaserad implementering att ha biverkningar i uttrycket utan att deklarera att funktionen har biverkningar. Att anropa en tilläggsbaserad implementering för att få biverkningar utan att deklarera funktionen som att ha biverkningar är odefinierat beteende och bör undvikas.

  • Varargs inbyggda funktioner tillåts. Om du vill deklarera en varargs-funktion anger du Varargs="true" i deklarationen. Det är lagligt för en uttrycksbaserad implementering att deklarera en vararg funktion, men för närvarande är det bara tilläggsbaserade implementeringar som kan komma åt variabelargumenten. Med en tilläggsbaserad implementering tar funktionen Execute() emot alla argument som faktiskt skickas in, inte bara de deklarerade argumenten.

  • Inbyggda funktioner som använder en klass/struct/union-typ som argumenttyp stöds inte. Att returnera en klass/struct/union-typ är OK. (En pekare eller referens till en klass/struct/union-typ är OK som argumenttyp).

Anpassa ikonen för anrop till inbyggda funktioner.

När du anropar en inbyggd funktion får uttrycket som standard den rosa rombikonen i fönstret Watch som är associerat med funktionsanrop. Du kan åsidosätta det här beteendet genom att ange attributet Category med något av följande värden:

  • Metod. Använd den rosa rombikonen, som vanligtvis används med metodanrop (standard).
  • Egenskap. Använd den svarta skiftnyckelikonen, som vanligtvis används med inställningar.
  • Data. Använd den blå rombikonen, som vanligtvis används med data.

Genom att kombinera interna funktioner med elementet <Item> är det möjligt att skapa en NatVis-fil där uttryck för objekt har egenskapsikonen för skiftnyckel:

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

Not

Om du placerar ikonalternativet på funktionsnivå i stället för <Item> nivå undviker du problem där ikonanpassningen går förlorad när det fullständiga namnet utvärderas. Eftersom det fullständiga namnet innehåller ett anrop till funktionen har det samma ikonanpassning som <Item> i sig.