Implement NatVis custom intrinsic function - Visual Studio (Windows) (original) (raw)

In this article, you learn about the guidelines for implementing a custom intrinsic function within a NatVis visualization. For more information about NatVis, see Create custom views of C++ objects.

Syntax

A NatVis file may define an intrinsic function using the following syntax:

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

Once the definition exists, any debugger expression may call the function, like any other function. For example, using the preceding NatVis definition, the expression Func(3) evaluates to 4.

An <Intrinsic> element may appear either on the file-level, or inside a <Type> element, before any other elements. Intrinsic functions defined within a <Type> define member functions of that type, while intrinsic functions defined outside a <Type> define global functions. For example:

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

In the preceding example, size() and capacity() are defined as member functions of the std::vector class, so whenever an expression evaluates vector.size(), it will actually evaluatevector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst, rather than perform a func-eval. Similarly, LOWORD(0x12345678) returns 0x5678, also without func-eval.

For another example, see the Intrinsic expansion.

Guidelines for using an intrinsic function

Be aware of the following guidelines when using an intrinsic function:

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

Guidelines to implement an Intrinsic function

Intrinsic functions support two possible forms of implementation:

To provide an extension-based implementation, the <Intrinsic> element should omit the Expression attribute and, instead, provide SourceId, LanguageId, Id, and ReturnType attributes, as provided in the following example:

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

To implement the function, the debugger extension must implement the IDkmIntrinsicFunctionEvaluator140 interface, using LanguageId andSourceId filters that match the corresponding values of the <Intrinsic> element in the NatVis file. When the function is called, the call translates into the component's Execute() method:

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

The component receives the bytes of each argument via the Arguments argument. If the function in question is a member function, the this pointer comes first, followed by the explicit arguments. The component should return the result by allocating a single-element array in pResults, storing the bytes of the return value.

Use the following guidelines to implement functions:

Customize the icon for calls to intrinsic functions.

By default, when you call an intrinsic function, the expression is given the pink diamond icon in the Watch window associated with function calls. You can override this behavior by specifying the Category attribute using one of the following values:

By combining intrinsic functions with the <Item> element, it's possible to author a NatVis file where item expressions have the wrench property icon:

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

Note

Placing the icon choice on the function level, rather than the <Item> level, avoids issues where the icon customization is lost when the full name is evaluated. Because the full name includes a call to the function, it has the same icon customization as the <Item> itself.