다음을 통해 공유


C++용 NatVis 사용자 지정 내장 함수 구현

이 문서에서는 NatVis 시각화 내에서 사용자 지정 내장 함수를 구현하기 위한 지침에 대해 알아봅니다. NatVis에 대한 자세한 내용은 C++ 개체의 사용자 지정 보기 만들기를 참조하세요.

통사론

NatVis 파일은 다음 구문을 사용하여 내장 함수를 정의할 수 있습니다.

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

정의가 있으면 모든 디버거 식이 다른 함수와 마찬가지로 함수를 호출할 수 있습니다. 예를 들어 앞의 NatVis 정의를 사용하면 식 Func(3) 4로 계산됩니다.

<Intrinsic> 요소는 파일 수준이나 <Type> 요소 내부에 다른 요소 앞에 나타날 수 있습니다. <Type> 내에 정의된 내장 함수는 해당 형식의 멤버 함수를 정의하고, <Type> 외부에서 정의된 내장 함수는 전역 함수를 정의합니다. 예를 들어:

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

앞의 예제에서 size()capacity()std::vector 클래스의 멤버 함수로 정의되어 있기 때문에, 매번 vector.size()이 평가될 때 func-eval을 수행하는 대신 사실상 vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst이 평가됩니다. 마찬가지로 LOWORD(0x12345678)는 func-eval 없이 0x5678을 반환합니다.

또 다른 예제는 내장 확장참조하세요.

내장 함수 사용에 대한 지침

내장 함수를 사용하는 경우 다음 지침에 유의하세요.

  • 내장 함수는 PDB에서 정의한 함수와 결합하여 오버로드되거나, 다른 내장 함수와 서로 오버로드될 수 있습니다.

  • 내장 함수가 이름과 인수 목록이 같은 PDB 정의 함수와 충돌하면 내장 함수가 승리합니다. 해당하는 내장 함수가 있는 경우 PDB 함수를 func-eval할 수 없습니다.

  • 내장 함수의 주소를 사용할 수 없습니다. 당신은 그것을 호출 할 수 있습니다.

  • 식에서 평가되려면 내장 멤버 함수는 해당 형식의 기본 뷰에 포함되어야 합니다. 예를 들어 IncludeView 제약 조건이 있는 형식 항목은 내장 함수를 지정하지 않을 수 있습니다.

  • NatVis 식을 비롯한 모든 식에서 내장 함수를 호출할 수 있습니다. 내장 함수는 서로 호출할 수도 있습니다. 그러나 재귀를 사용하는 내장 함수는 현재 지원되지 않습니다. 예를 들어:

    <!-- 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>
    
  • 기본적으로 내장 함수는 부작용이 없는 것으로 간주됩니다. 즉, 부작용을 허용하지 않는 컨텍스트에서 호출할 수 있으며 구현 식은 부작용을 포함할 수 없습니다.

    정의는 선언에서 SideEffect 특성을 지정하여 이 동작을 재정의할 수 있습니다. 함수가 부작용이 있는 것으로 표시되면 구현 식의 부작용이 허용됩니다. 그러나 부작용이 금지된 컨텍스트에서 함수를 호출하는 것은 더 이상 허용되지 않습니다.

  • 두 내장 함수의 정의가 서로 충돌하는 경우(동일한 이름, 동일한 서명), 마지막 함수가 동일한 파일 내에서 우선합니다.

    파일 간 우선 순위가 높은 파일의 인스턴스가 우선합니다(프로젝트가 사용자 디렉터리보다 높고, 그 다음은 설치 디렉터리입니다). 우선 순위가 높은 정의에 구문 분석하지 않는 식이 포함된 경우 해당 정의는 무시되고 다음으로 높은 우선 순위 정의가 대신 사용됩니다.

내장 함수를 구현하기 위한 지침

내장 함수는 두 가지 가능한 구현 형식을 지원합니다.

  • 표현 기반

    NatVis 파일은 함수의 반환 값으로 계산되는 식을 정의합니다. 식은 <Parameter> 요소로 선언된 어떤 인수도 사용할 수 있습니다. 클래스 내에 정의된 함수도 "인스턴스" 함수로 간주되며 "this" 포인터에도 액세스할 수 있습니다.

  • 확장 기반

    NatVis 파일은 실제로 함수를 평가하기 위해 디버거 확장을 호출하는 지침을 제공합니다. 디버거 확장은 Concord API에 대한 모든 권한을 가지며 NatVis 식 내에서는 불가능한 작업을 수행할 수 있습니다.

확장 기반 구현을 제공하려면 <Intrinsic> 요소는 Expression 특성을 생략하고 대신 다음 예제와 같이 SourceId, LanguageId, IdReturnType 특성을 제공해야 합니다.

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

함수를 구현하려면 디버거 확장이 natVis 파일에서 <Intrinsic> 요소의 해당 값과 일치하는 LanguageIdSourceId 필터를 사용하여 IDkmIntrinsicFunctionEvaluator140 인터페이스를 구현해야 합니다. 함수가 호출되면 호출이 구성 요소의 Execute() 메서드로 변환됩니다.

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

구성 요소는 Arguments 인수를 통해 각 인수의 바이트를 받습니다. 문제의 함수가 멤버 함수인 경우 this 포인터가 먼저 오고 그 뒤에 명시적 인수가 옵니다. 구성 요소는 pResults에 단일 요소 배열을 할당하고 반환 값의 바이트를 저장하여 결과를 반환해야 합니다.

다음 지침을 사용하여 함수를 구현합니다.

  • 두 구현 형태를 섞어 사용하는 것은 불법입니다. 즉, 식과 소스 ID를 모두 포함할 수 없습니다.

  • 식 기반 구현에 대한 반환 형식 지정은 허용되지만 필수는 아닙니다. 반환 형식을 지정하면 식의 반환 형식이 정확히 일치해야 합니다(암시적 캐스팅이 허용되지 않음). 반환 형식을 지정하지 않으면 반환 형식이 식에서 유추됩니다. 모든 확장 기반 구현은 NatVis 파일에 반환 형식을 명시해야 합니다.

  • 다른 내장 함수에 대한 비귀적 호출이 허용됩니다. 재귀는 허용되지 않습니다.

  • 함수에 부작용이 있는 경우 선언에서 SideEffect="true" 지정해야 합니다. 식 기반 구현이 함수의 부작용을 선언하지 않고 식 자체에 부작용을 갖는 것은 불법입니다. 함수를 부작용이 있는 것으로 선언하지 않고 부작용이 발생하도록 확장 기반 구현을 호출하는 것은 정의되지 않은 동작이므로 피해야 합니다.

  • Varargs 내장 함수가 허용됩니다. varargs 함수를 선언하려면 선언에 Varargs="true" 지정합니다. 식 기반 구현에서 vararg 함수를 선언하는 것은 합법적이지만 현재는 확장 기반 구현만 변수 인수에 액세스할 수 있습니다. 확장 기반 구현을 사용하면 Execute() 함수는 선언된 인수뿐만 아니라 실제로 전달되는 모든 인수를 받습니다.

  • 클래스/구조체/공용 구조체 형식을 인수 형식으로 사용하는 내장 함수는 지원되지 않습니다. 클래스/구조체/유니언 타입을 반환하는 것은 괜찮습니다. (클래스/구조체/공용 구조체 유형의 포인터 또는 참조는 인수 유형으로 사용할 수 있음).

내장 함수 호출 아이콘을 사용자 지정합니다.

기본적으로, 내장 함수를 호출하면 함수 호출과 연결된 Watch 창에서 식에 핑크 다이아몬드 아이콘이 표시됩니다. 다음 값 중 하나를 사용하여 Category 특성을 지정하여 이 동작을 재정의할 수 있습니다.

  • 메서드. 일반적으로 메서드 호출(기본값)과 함께 사용되는 분홍색 다이아몬드 아이콘을 사용합니다.
  • 재산. 일반적으로 속성과 함께 사용되는 검은색 렌치 아이콘을 사용합니다.
  • 데이터. 일반적으로 데이터와 함께 사용되는 파란색 다이아몬드 아이콘을 사용합니다.

내장 함수를 <Item> 요소와 결합하여 항목 식에 렌치 속성 아이콘이 있는 NatVis 파일을 작성할 수 있습니다.

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

메모

<Item> 수준이 아닌 함수 수준에서 아이콘 선택을 배치하면 전체 이름을 평가할 때 아이콘 사용자 지정이 손실되는 문제를 방지할 수 있습니다. 전체 이름에는 함수에 대한 호출이 포함되므로 <Item> 자체와 동일한 아이콘 사용자 지정이 있습니다.