调试器

使用 Android 游戏开发扩展时,通过 Visual Studio 调试器 (LLDB) 调试您的项目。

运行调试器

在运行调试器之前,您必须能够在 Android 上构建、部署和运行您的游戏。有关详细信息,请参阅“运行示例”部分。

确认您可以在不使用调试器的情况下运行游戏后,您可以按 F5 或在调试菜单中选择开始调试项来使用调试器。调试器附加到游戏时,您应该会看到一个对话框。

启动调试器需要 10 秒到 1 分钟或更长时间,具体取决于应用大小以及启动时需要加载的符号数量。首次附加到新设备时会花费更多时间,因为调试器必须将一些 Android 库从设备下载到主机。如果首次在新设备上尝试时花费了 1 分钟以上,请考虑取消调试会话,然后重新启动。

以这种方式运行调试器时,游戏会以等待调试器模式启动,并且在调试器连接之前,游戏不会执行任何代码。这使您还可以调试游戏的初始化部分。

您可以通过阅读 Visual Studio 文档,找到有关特定 Visual Studio 调试器功能的更多信息。

附加到进程

如果要调试已在物理设备或虚拟设备上运行的游戏,可以从 Visual Studio 将调试器附加到该进程。

在 Visual Studio 中,确保已打开 Android 解决方案,然后

  1. 转到调试菜单,然后选择附加到进程...
  2. 传输下拉列表中,选择 Android 游戏开发扩展
  3. 限定符下拉列表中,选择您的 Android 设备。
  4. 从可用进程列表中选择游戏进程,然后点击附加

Attach to process

执行 LLDB.Shell 命令

在调试会话处于活动状态时,使用 Visual Studio 的命令窗口运行 LLDB.Shell 命令。

命令格式

LLDB.Shell [command]

示例

>LLDB.Shell expr myIntVariable = 9
Status:  Success
Output Message:
(int) $2 = 9

数据可视化

格式说明符

您可以使用格式说明符更改自动局部变量监视和变量数据提示窗口中值的显示格式。

格式说明符位于表达式的末尾。它们以逗号开头,后跟一个短字符串。例如,表达式 _myInt,x 中的 ,x 说明符会将 myInt 格式化为小写十六进制。

格式说明符可以直接在监视窗口中使用,也可以通过将其添加到 Natvis 表达式中,在自动局部变量数据提示窗口中使用。有关详细信息,请参阅 Natvis

支持的说明符列表

格式名称 说明符 说明
布尔值 B 将此显示为 true/false 布尔值,通常规则是 0 为 false,其他所有值都为 true
二进制 b 将此显示为位序列
二进制,无前导 0b bb 将此显示为不带 0b 前缀的位序列
字节 y 显示字节,但同时尝试将其显示为 ASCII 字符
例如:(int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
带 ASCII 的字节 Y 显示字节,但同时尝试将其显示为 ASCII 字符
例如:(int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
字符 c 将字节显示为 ASCII 字符
例如:(int *) c.sp.x = P\xf8\xbf_\xff\x7f\0\0
可打印字符 C 将字节显示为可打印 ASCII 字符
例如:(int *) c.sp.x = P.._....
复浮点数 F 将此值解释为复浮点数的实部和虚部
例如:(int *) c.sp.x = 2.76658e+19 + 4.59163e-41i
十进制 d, i 将此显示为带符号整数(不执行类型转换,仅将字节显示为带符号整数)
枚举 E,en 将此显示为枚举,如果可用则打印值的名称,否则打印整数值
例如:(enum enumType) val_type = eValue2
十六进制 - 小写 x, h 以小写十六进制表示法显示(不执行类型转换,仅将字节显示为十六进制)
十六进制 - 大写 X, H 以大写十六进制表示法显示(不执行类型转换,仅将字节显示为十六进制)
十六进制 - 小写,无前导 0x xb, hb 以不带 0x 前缀的小写十六进制表示法显示(不执行类型转换,仅将字节显示为十六进制)
十六进制 - 大写,无前导 0x Xb, Hb 以不带 0x 前缀的大写十六进制表示法显示(不执行类型转换,仅将字节显示为十六进制)
浮点数 f 将此显示为浮点数(不执行类型转换,仅将字节解释为 IEEE754 浮点值)
八进制 o 以八进制表示法显示
操作系统类型 O 将此显示为 MacOS OSType
例如:(float) x = '\n\x1f\xd7\n'
字符串 - C 字符串 s 将此显示为以 0 终止的 C 字符串
例如:“hello world”
字符串 - C 字符串,无引号 sb 将此显示为不带引号的以 0 终止的 C 字符串,
例如:hello world
字符串 - UTF-8 s8 将此显示为以 0 终止的 UTF-8 字符串
例如:u8"hello world ☕"
字符串 - UTF-8,无引号 s8b 将此显示为不带引号的以 0 终止的 UTF-8 字符串
例如:hello world ☕
字符串 - UTF-16 su 将此显示为以 0 终止的 UTF-16 字符串
例如:u"hello world ☕"
字符串 - UTF-16,无引号 sub 将此显示为不带引号的以 0 终止的 UTF-16 字符串
例如:hello world ☕
字符串 - UTF-32 s32 将此显示为以 0 终止的 UTF-32 字符串
例如:U"hello world ☕"
字符串 - UTF-32,无引号 s32b 将此显示为不带引号的以 0 终止的 UTF-32 字符串
例如:hello world ☕
Unicode16 U 将此显示为 UTF-16 字符
例如:(float) x = 0xd70a 0x411f
Unicode32 U32 将此显示为 UTF-32 字符
例如:(float) x = 0x411fd70a
无符号十进制 u 将此显示为无符号整数(不执行类型转换,仅将字节显示为无符号整数)
指针 p 将此显示为原生指针(除非这确实是一个指针,否则结果地址可能无效)
复整数 I 将此值解释为复整数的实部和虚部
例如:(int *) pointer = 1048960 + 1i
字符数组 a 将此显示为字符数组
例如:(char) *c.sp.z = {X}
原始格式 ! 原始格式,忽略任何数据类型视图自定义

Natvis

Natvis 框架允许您自定义 Visual Studio 在调试器变量窗口中显示原生类型的方式。例如,可以使用 Natvis 自定义监视局部变量数据提示窗口的显示。

Natvis 功能默认启用,但您可以通过在 Visual Studio 中将工具 > 选项 > Android 游戏开发扩展 > Natvis 标志设置为已停用来将其停用。

加载 Natvis 文件

Visual Studio 从下面列出的三个位置加载 Natvis 文件,并在每次启动调试会话时重新加载它们。文件必须符合 Visual Studio 2017 Natvis 架构。

  • 作为已加载项目或顶级解决方案项一部分的 .natvis 文件。
  • 用户特定目录 (%USERPROFILE%\Documents\Visual Studio 2017\Visualizers)
  • 系统范围目录 (%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers)
重新加载 Natvis 文件

在调试会话期间,通过在命令窗口或监视窗口中评估 .natvisreload 来重新加载 Natvis 文件。

Natvis 示例文件

此 Natvis 示例文件包含目前支持的所有标签和属性。

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

  <Type Name="demo::Vector&lt;*&gt;">
    <AlternativeType Name="MySimilarVectorType&lt;*&gt;"/>

    <!-- Included to show the <SmartPointer> feature is supported. -->
    <SmartPointer Optional="true" Usage="Minimal">ptr</SmartPointer>

    <!-- Included to show the <DisplayString> feature is supported. -->
    <DisplayString Condition="_size == 0" Optional="true">()</DisplayString>
    <DisplayString Condition="_size == 1">(x={_items[0]})</DisplayString>
    <DisplayString Condition="_size == 2">(x={_items[0]}, y={_items[1]})</DisplayString>
    <DisplayString Condition="_size == 3">(x={_items[0]}, y={_items[1]}, z={_items[2]})</DisplayString>
    <DisplayString>[Size={_size,x}] (x={_items[0]}, y={_items[1]}, z={_items[2]}, ...)</DisplayString>

    <!-- Included to show the <StringView> feature is supported. -->
    <StringView Condition="true" Optional="true">_stringViewText</StringView>

    <Expand HideRawView="false">
      <!-- Included to show the <Item> feature is supported. -->
      <Item Name="X" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 1" Optional="true">_items[0]</Item>
      <Item Name="Y" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 2" Optional="true">_items[1]</Item>
      <Item Name="Z" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 3" Optional="true">_items[2]</Item>

      <!-- Included to show the <ArrayItems> feature is supported. -->
      <ArrayItems Condition="_size >= 4" Optional="true">
        <Size Condition="true" Optional="true">_size</Size>
        <ValuePointer Condition="true">_items</ValuePointer>
      </ArrayItems>

      <!-- Included to show the <IndexListItems> feature is supported. -->
      <IndexListItems Condition="true" Optional="true">
        <Size Condition="true" Optional="true">_listSize</Size>
        <ValueNode Condition="true">_list[%i]</ValueNode>
      </IndexListItems>

      <!-- Included to show the <LinkedListItems> feature is supported. -->
      <LinkedListItems Condition="true" Optional="true">
        <Size Optional="true">_listSize</Size>
        <HeadPointer>_head</HeadPointer>
        <NextPointer>_next</NextPointer>
        <ValueNode>_value</ValueNode>
      </LinkedListItems>

      <!-- Included to show the <ExpandedItem> feature is supported. -->
      <ExpandedItem Condition="true" Optional="true">_childVar</ExpandedItem>

      <!-- Included to show the <Synthetic> feature is supported. -->
      <Synthetic Name="[Size]" Condition="true" Optional="true">
        <DisplayString>_size</DisplayString>
        <Expand HideRawView="true">
          <!-- Any supported <Expand> sub-tags. -->
        </Expand>
      </Synthetic>

      <!-- Included to show the <TreeItems> feature is supported. -->
      <TreeItems Condition="true" Optional="true">
        <Size>_treeSize</Size>
        <HeadPointer>_head</HeadPointer>
        <LeftPointer>_left</LeftPointer>
        <RightPointer>_right</RightPointer>
        <ValueNode>_value</ValueNode>
      </TreeItems>

      <!-- Included to show format specifiers are supported. -->
      <Item Name="[Hex Dump at {_index,x}]">myInt[_index],x</Item>
    </Expand>
  </Type>
</AutoVisualizer>

编写 Natvis 文件

Visual Studio 支持自行编写 Natvis 文件。有关自定义调试器变量窗口的详细信息,请参阅 MSDN

调试 Natvis 文件

在某些情况下,错误会以变量的形式显示(例如,在自动监视等窗口中)。例如:<error: use of undeclared identifier 'missingVar'>

您可以通过从 Android 游戏开发扩展工具栏中打开 GoogleAndroid.log 文件来访问有关错误的更多详细信息。

已知限制

  • 如果您的标签或属性未在上面的示例文件中列出,则表示目前不支持。Visual Studio 会忽略不受支持的标签和属性,因此您可以将其保留在现有的 Natvis 文件中,只要该文件使用我们的架构,它就会正常工作。

  • 尽管架构要求 Usage 属性,但它不受 <SmartPointer> 支持。然而,LLDB 不限制对 C++ 中定义的操作符的访问,因此任何所需的操作符都可以在 C++ 中定义。