Debugger

Depure seu projeto com o Visual Studio Debugger (LLDB) ao usar a Android Game Development Extension.

Executar o depurador

Antes de executar o depurador, você precisa ser capaz de criar, implantar e executar seu jogo no Android. Consulte a seção Executar a amostra para ver mais detalhes.

Depois de verificar se é possível executar o jogo sem o depurador, use o depurador pressionando F5 ou selecionando o item Start Debugging no menu Debug. Você verá uma caixa de diálogo enquanto o depurador estiver sendo anexado ao jogo.

A inicialização do depurador leva de 10 segundos a 1 minuto ou mais, dependendo do tamanho do app e da quantidade de símbolos que precisam ser carregados na inicialização. A anexação a um novo dispositivo pela primeira vez é mais demorada porque o depurador precisa fazer o download de algumas bibliotecas do Android do dispositivo para a máquina host. Se demorar mais de um minuto nas primeiras tentativas com um novo dispositivo, cancele a sessão de depuração e reinicie-a.

Quando você executa o depurador dessa maneira, o jogo é iniciado no modo Waiting for Debugger e não executará nenhum código do seu jogo até que o depurador se conecte. Isso também permite depurar a seção de inicialização do jogo.

Leia a Documentação do Visual Studio para saber mais sobre recursos específicos desse depurador.

Como anexar a um processo

Se você quiser depurar um jogo que já está em execução em um dispositivo físico ou virtual, anexe o depurador ao processo no Visual Studio.

No Visual Studio, verifique se uma solução do Android está aberta e:

  1. acesse o menu Debug e selecione Attach to Process…;
  2. na lista suspensa Transport, selecione Android Game Development Extension;
  3. na lista suspensa Qualifier, selecione seu dispositivo Android;
  4. selecione o processo do jogo na lista de processos disponíveis e clique em Attach.

Anexar ao processo

Como executar comandos do LLDB.Shell

Com uma sessão de depuração ativa, use a janela de comando do Visual Studio para executar comandos do LLDB.Shell.

Formato do comando:

LLDB.Shell [command]

Exemplo:

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

Visualização de dados

Especificadores de formato

É possível mudar o formato em que um valor é exibido nas janelas Auto, Locals, Watch e variável DataTip usando especificadores de formato.

Os especificadores de formato são encontrados no final das expressões. Eles começam com uma vírgula seguida por uma string curta. Por exemplo, o especificador ,x na expressão _myInt,x formatará myInt como um hexadecimal em letras minúsculas.

Os especificadores de formato podem ser usados diretamente na janela Watch ou nas janelas Autos, Locals e DataTip adicionando-os às expressões Natvis. Para saber mais, consulte o Natvis.

Lista de especificadores compatíveis

Nome do formato Especificadores Descrição
booleano B mostre-o como um booleano verdadeiro/falso, usando a regra personalizada de que 0 é falso e todo o restante é verdadeiro
binário b mostre-o como uma sequência de bits
binário, sem 0b inicial bb mostre-o como uma sequência de bits sem o prefixo 0b
bytes y mostre os bytes, mas tente também exibi-los como caracteres ASCII
por exemplo, (int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
bytes com ASCII Y mostre os bytes, mas tente também exibi-los como caracteres ASCII
por exemplo, (int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
caractere c mostre os bytes como caracteres ASCII
por exemplo (int *) c.sp.x = P\xf8|xbf_\xff\x7f\0\0
caractere para impressão C mostre os bytes como caracteres ASCII para impressão
por exemplo, (int *) c.sp.x = P.._....
ponto flutuante complexo F interprete esse valor como a parte real e imaginária de um número de ponto flutuante complexo
por exemplo (int *) c.sp.x = 2.76658e+19 + 4.59163e-41i
decimal d, i mostre-o como um número inteiro com sinal (ele não realiza uma conversão, apenas mostra os bytes como um número inteiro com sinal)
enumeração E,en mostre-o como uma enumeração, imprimindo o nome do valor, se disponível, ou o valor inteiro, caso contrário
por exemplo (enum enumType) val_type = eValue2
hexadecimal – minúsculas x, h mostre-o em notação hexadecimal em letras minúsculas (ele não realiza uma conversão, apenas mostra os bytes como hexadecimais)
hexadecimal – maiúsculas X, H mostre-o em notação hexadecimal em letras maiúsculas (ele não realiza uma conversão, apenas mostra os bytes como hexadecimais)
hexadecimal – minúsculas, sem 0x inicial xb, hb mostre-o em notação hexadecimal em letras minúsculas sem o prefixo 0x (ele não realiza uma conversão, apenas mostra os bytes como hexadecimais)
hexadecimal – maiúsculas, sem 0x inicial Xb, Hb mostre-o em notação hexadecimal em letras maiúsculas sem o prefixo 0x (ele não realiza uma conversão, apenas mostra os bytes como hexadecimais)
ponto flutuante f mostre-o como um número de ponto flutuante (ele não realiza uma conversão, apenas interpreta os bytes como um valor de ponto flutuante IEEE754)
octal o mostre-o em notação octal
Tipo de SO O mostre-o como um OSType MacOS
por exemplo, (float) x = '\n\x1f\xd7\n'
string – C-string s mostre-o como uma string em C terminada em 0
por exemplo, "hello world"
string – C-string, sem aspas sb mostre-o como uma string em C terminada em 0, sem aspas
por exemplo, hello world
string – UTF-8 s8 mostre-o como uma string UTF-8 terminada em 0
por exemplo, u8"hello world ☕"
string – UTF-8, sem aspas s8b mostre-o como uma string UTF-8 terminada em 0, sem aspas
por exemplo, hello world ☕
string – UTF-16 su mostre-o como uma string UTF-16 terminada em 0
por exemplo, u"hello world ☕"
string – UTF-16, sem aspas sub mostre-o como uma string UTF-16 terminada em 0, sem aspas
por exemplo, hello world ☕
string – UTF-32 s32 mostre-o como uma string UTF-32 terminada em 0
por exemplo, U"hello world ☕"
string – UTF-32, sem aspas s32b mostre-o como uma string UTF-32 terminada em 0, sem aspas
por exemplo, hello world ☕
unicode16 U mostre-o como caracteres UTF-16
por exemplo, (float) x = 0xd70a 0x411f0x411f
unicode32 U32 mostre-o como caracteres UTF-32
por exemplo, (float) x = 0x411fd70a
decimal sem sinal u mostre-o como um número inteiro sem sinal (ele não realiza uma conversão, apenas mostra os bytes como um número inteiro sem sinal)
ponteiro p mostre-o como um ponteiro nativo (a menos que seja mesmo um ponteiro, o endereço resultante provavelmente será inválido)
inteiro complexo I interprete esse valor como a parte real e imaginária de um número inteiro complexo
por exemplo, (int *) pointer = 1048960 + 1i
matriz de caracteres a mostre-o como uma matriz de caracteres
por exemplo, (char) *c.sp.z = {X}
bruto ! formato bruto, ignorando qualquer personalização de visualizações de tipo de dados

Natvis

Com o framework Natvis, é possível personalizar a maneira como o Visual Studio exibe os tipos nativos nas janelas de variáveis do depurador. Por exemplo, use o Natvis para personalizar as exibições das janelas Watch, Locals e DataTips.

O recurso Natvis é ativado por padrão, mas pode ser desativado no Visual Studio configurando a sinalização Tools > Options > Android Game Development Extension > Natvis como Disabled.

Como carregar arquivos Natvis

O Visual Studio carrega arquivos Natvis dos três locais listados abaixo e os recarrega sempre que você inicia uma sessão de depuração. Os arquivos precisam aderir ao esquema Natvis do Visual Studio 2017.

  • Arquivos .natvis de um projeto carregado ou de um item da solução de nível superior
  • O diretório específico do usuário (%USERPROFILE%\Documents\Visual Studio 2017\Visualizers)
  • O diretório do sistema (%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers)
Como recarregar arquivos Natvis

Recarregue os arquivos Natvis durante uma sessão de depuração, avaliando .natvisreload na janela de comando ou de visualização.

Exemplo de arquivo Natvis

Este exemplo de arquivo Natvis inclui todas as tags e todos os atributos compatíveis no momento.

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

Criação de arquivos Natvis

O Visual Studio permite que você crie seus próprios arquivos Natvis. Para mais informações sobre como personalizar as janelas de variáveis do depurador, consulte MSDN.

Como depurar arquivos Natvis

Em alguns casos, erros são apresentados como o Valor de uma variável (por exemplo, nas janelas Auto, Watch etc.). Por exemplo: <error: use of undeclared identifier 'missingVar'>

Para ver mais detalhes sobre o erro, abra o arquivo GoogleAndroid.log na barra de ferramentas da Android Game Development Extension.

Limitações conhecidas

  • Caso seu atributo ou tag não estejam listados no exemplo de arquivo acima, ele não é compatível no momento. O Visual Studio ignora as tags e os atributos não compatíveis. Portanto, você pode deixá-los em um arquivo Natvis e o arquivo funcionará, desde que use nosso esquema.

  • O atributo Usage, embora exigido pelo esquema, não é compatível com <SmartPointer>. No entanto, o LLDB não restringe o acesso a operadores definidos em C++. Assim, qualquer operador necessário pode ser definido em C++.