Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье описаны рекомендации по реализации пользовательской встроенной функции в визуализации 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<*>">
<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(), оно фактически вычисляет vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst, а не выполняет func-eval.
Аналогично, LOWORD(0x12345678) возвращает 0x5678, также без func-eval.
В другом примере см. Встроенное расширение.
Рекомендации по использованию встроенной функции
Помните о следующих рекомендациях при использовании встроенной функции:
Встроенные функции могут быть перегружены, либо с функциями, определяемыми в PDB, либо между собой.
Если встроенная функция конфликтует с определяемой PDB функцией с тем же именем и списком аргументов, встроенная функция выиграет. Вы не можете вычислять функцию PDB, если существует эквивалентная встроенная функция.
Невозможно получить адрес встроенной функции; её можно только вызвать.
Функции-члены, являющиеся встроенными, должны принадлежать виду по умолчанию для соответствующего типа, чтобы быть вычисленными в выражении. Например, запись типа с ограничением
IncludeViewможет не указывать встроенную функцию.Встроенные функции могут вызываться из любого выражения, включая выражения NatVis. Встроенные функции также могут вызывать друг друга. Однако встроенные функции, использующие рекурсию, в настоящее время не поддерживаются. Например:
<!-- 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>По умолчанию встроенные функции считаются не имеющими побочных эффектов. То есть их можно вызывать в контекстах, которые запрещают побочные эффекты, и выражение реализации не допускается содержать побочные эффекты.
Определение может переопределить это поведение, указав атрибут
SideEffectв объявлении. Если функция помечена как имеющая побочные эффекты, побочные эффекты в выражении реализации становятся допустимыми. Однако вызов функции в контекстах, в которых запрещены побочные эффекты, больше не разрешен.Когда определения двух встроенных функций конфликтуют друг с другом (одно и то же имя, одна и та же сигнатура), последняя имеет приоритет (в пределах одного файла).
В разных файлах экземпляр в файле с более высоким приоритетом имеет приоритет (проект имеет более высокий приоритет, чем директория пользователя, а она, в свою очередь, выше директории установки). Если определение с более высоким приоритетом содержит выражение, которое не анализируется, это определение игнорируется, а вместо этого используется следующее определение с наивысшим приоритетом.
Рекомендации по реализации встроенной функции
Встроенные функции поддерживают две возможные формы реализации:
На основе выражений
Файл NatVis определяет выражение, которое оценивает возвращаемое значение функции. Выражение может использовать любые аргументы, переданные в него, объявленные как элементы
<Parameter>. Функции, определенные в классе, также считаются функциями экземпляра, а также могут получить доступ к указателю "this".Основывается на расширениях
Файл NatVis предоставляет инструкции по вызову расширения отладчика для фактической оценки функции. Расширение отладчика имеет полный доступ к API Конкорда и имеет возможность выполнять задачи, которые недоступны в выражении NatVis.
Чтобы обеспечить реализацию на основе расширений, элемент <Intrinsic> должен опустить атрибут Expression, а вместо этого указать SourceId, LanguageId, Idи ReturnType атрибуты, как показано в следующем примере:
<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>
Чтобы реализовать функцию, расширение отладчика должно реализовать интерфейс IDkmIntrinsicFunctionEvaluator140, используя LanguageId и фильтры SourceId, соответствующие соответствующим значениям элемента <Intrinsic> в файле NatVis. При вызове функции это переводится в метод 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, сохраняя байты возвращаемого значения.
Используйте следующие рекомендации для реализации функций:
Использовать обе формы реализации вместе — незаконно. То есть нельзя включать как выражение, так и исходный идентификатор.
Указание возвращаемого типа для реализации на основе выражений разрешено, но не обязательно. Если указан тип возвращаемого значения, возвращаемый тип выражения должен точно совпадать с ним (не допускается неявное приведение). Если возвращаемый тип не указан, возвращаемый тип выводится из выражения. Любая реализация на основе расширений должна указывать тип возвращаемого значения в файле NatVis.
Разрешены не рекурсивные вызовы к другим встроенным функциям. Рекурсия не допускается.
Если функция имеет побочные эффекты, необходимо указать
SideEffect="true"в объявлении. Незаконно, чтобы реализация, основанная на выражениях, имела побочные эффекты, не объявив функцию таковой. Использование реализации на основе расширения для создания побочных эффектов без объявления функции имеющей побочные эффекты является неопределённым поведением и его следует избегать.Разрешены встроенные функции Varargs. Чтобы объявить функцию varargs, укажите
Varargs="true"в объявлении. Хотя реализация на основе выражений является законной для объявления функцииvararg, в настоящее время только реализации на основе расширений имеют способ доступа к аргументам переменной. При реализации на основе расширения функцияExecute()получает все аргументы, которые фактически передаются, а не только объявленные аргументы.Встроенные функции, потребляющие тип класса/ структуры или объединения в качестве типа аргумента, не поддерживаются. Возврат типа класса/структуры или объединения является приемлемым. (Указатель или ссылка на тип класса/структуры или объединения — "ОК" в качестве типа аргумента).
Настройте значок для вызовов встроенных функций.
По умолчанию при вызове встроенной функции выражение получает розовый значок бриллианта в окне Watch, связанном с вызовами функций. Это поведение можно переопределить, указав атрибут Category с помощью одного из следующих значений:
- Метод. Используйте значок розового бриллианта, обычно используемый с вызовами методов (по умолчанию).
- Свойство. Используйте черный значок гаечного ключа, который обычно используется для работы со свойствами.
- Данные. Используйте синий значок бриллианта, обычно используемый с данными.
Сочетая встроенные функции с элементом <Item>, можно создать файл NatVis, где выражения элементов имеют значок свойства wrench:
<Type Name="MyClass">
<Intrinsic Name="GetValue" ReturnType="int" Expression="m_value" Category="Property" />
<Expand>
<Item Name="Value">this->GetValue()</Item>
</Expand>
</Type>
Заметка
Размещение выбора значка на уровне функции, а не на уровне <Item>, позволяет избежать проблем, при которых настройка значка теряется при оценке полного имени. Поскольку полное имя включает вызов функции, оно имеет ту же настройку значка, что и сам <Item>.