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<*>">
<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._Myfirst
i 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->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->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 <= 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
, Id
och 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 pResults
och 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 envararg
funktion, men för närvarande är det bara tilläggsbaserade implementeringar som kan komma åt variabelargumenten. Med en tilläggsbaserad implementering tar funktionenExecute()
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->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.