Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Para o Windows 8, o depurador e o compilador do Windows foram aprimorados para que você possa depurar código otimizado e depurar funções embutidas. O depurador exibe parâmetros e variáveis locais, independentemente de estarem armazenados em registos ou na pilha. O depurador também exibe funções embutidas na pilha de chamadas. Para funções embutidas, o depurador exibe variáveis locais, mas não parâmetros.
Quando o código é otimizado, ele é transformado para ser executado mais rápido e usar menos memória. Às vezes, as funções são removidas como resultado da remoção de código morto, código sendo mesclado ou funções sendo colocadas em linha. As variáveis e parâmetros locais também podem ser removidos. Muitas otimizações de código removem variáveis locais que não são necessárias ou usadas; Outras otimizações removem variáveis de indução em loops. A eliminação comum de subexpressões mescla variáveis locais.
As compilações de retalho do Windows são otimizadas. Portanto, se estiveres a executar uma versão de retalho do Windows, é especialmente útil ter um depurador concebido para funcionar bem com código otimizado. Para tornar a depuração de código otimizado eficaz, dois recursos principais são necessários: 1) exibição precisa de variáveis locais e 2) exibição de funções embutidas na pilha de chamadas.
Exibição precisa de variáveis e parâmetros locais
Para facilitar a exibição precisa de variáveis e parâmetros locais, o compilador registra informações sobre os locais de variáveis locais e parâmetros em arquivos de símbolo (PDB). Esses registros de localização rastreiam os locais de armazenamento das variáveis e os intervalos de códigos específicos em que esses locais são válidos. Esses registros não só ajudam a rastrear os locais (em registros ou em slots de pilha) das variáveis, mas também o movimento das variáveis. Por exemplo, um parâmetro pode estar primeiro no registro RCX, mas é movido para um slot de pilha para liberar o RCX, depois movido para o registro R8 quando é muito usado num loop, e então movido para um slot de pilha diferente quando o código está fora do loop. O depurador do Windows consome os registos de localização detalhada nos arquivos PDB e usa o ponteiro de instrução atual para selecionar os registos de localização apropriados para as variáveis e parâmetros locais.
Esta captura de tela da janela Locais no Visual Studio mostra os parâmetros e variáveis locais para uma função em um aplicativo otimizado de 64 bits. A função não é em linha, por isso vemos tanto parâmetros como variáveis locais.
Você pode usar o comando dv -v para ver os locais dos parâmetros e variáveis locais.
Observe que a janela Locais exibe os parâmetros corretamente, mesmo que eles estejam armazenados em registros.
Além de rastrear variáveis com tipos primitivos, os registros de localização rastreiam dados de membros de estruturas e classes locais. A saída do depurador a seguir exibe estruturas locais.
0:000> dt My1
Local var Type _LocalStruct
+0x000 i1 : 0n0 (edi)
+0x004 i2 : 0n1 (rsp+0x94)
+0x008 i3 : 0n2 (rsp+0x90)
+0x00c i4 : 0n3 (rsp+0x208)
+0x010 i5 : 0n4 (r10d)
+0x014 i6 : 0n7 (rsp+0x200)
0:000> dt My2
Local var @ 0xefa60 Type _IntSum
+0x000 sum1 : 0n4760 (edx)
+0x004 sum2 : 0n30772 (ecx)
+0x008 sum3 : 0n2 (r12d)
+0x00c sum4 : 0n0
Aqui estão algumas observações sobre a saída anterior do depurador.
- A estrutura local My1 ilustra que o compilador pode espalhar membros de dados da estrutura local nos registos e slots de pilha não contíguos.
- A saída do comando dt My2 será diferente da saída do comando dt _IntSum 0xefa60. Você não pode assumir que a estrutura local ocupará um bloco contíguo de memória de stack. No caso do My2, apenas
sum4permanece no bloco de pilha original; os outros três membros de dados são movidos para registos. - Alguns elementos de dados podem ter várias localizações. Por exemplo, My2.sum2 tem dois locais: um é o registo ECX (que o depurador do Windows escolhe) e o outro é 0xefa60+0x4 (o slot de pilha original). Isso pode acontecer para variáveis locais de tipo primitivo também, e o depurador do Windows impõe heurísticas precedentes para determinar qual local usar. Por exemplo, as posições de registo têm sempre prioridade sobre as posições de pilha.
Exibição de funções embutidas na pilha de chamadas
Durante a otimização de código, algumas funções são colocadas em linha. Ou seja, o corpo da função é colocado diretamente no código como uma expansão de macro. Não há chamada de função nem retorno ao chamador. Para facilitar a exibição de funções embutidas, o compilador armazena dados nos arquivos PDB que ajudam a decodificar os blocos de código para as funções embutidas (ou seja, sequências de blocos de código em funções de chamador que pertencem às funções de chamada que estão sendo colocadas embutidas), bem como as variáveis locais (variáveis locais com escopo nesses blocos de código). Esses dados ajudam o depurador a incluir funções embutidas como parte do desenrolar da pilha.
Suponha que você compile um aplicativo e force uma função nomeada func1 para estar embutida.
__forceinline int func1(int p1, int p2, int p3)
{
int num1 = 0;
int num2 = 0;
int num3 = 0;
...
}
Você pode usar o comando bm para definir um ponto de interrupção em func1.
0:000> bm MyApp!func1
1: 000007f6`8d621088 @!"MyApp!func1" (MyApp!func1 inlined in MyApp!main+0x88)
0:000> g
Breakpoint 1 hit
MyApp!main+0x88:
000007f6`8d621088 488d0d21110000 lea rcx,[MyApp!`string' (000007f6`8d6221b0)]
Depois de dar um passo no func1, você pode usar o comando k para ver func1 na pilha de chamadas. Você pode usar o comando dv para ver as variáveis locais do func1. Observe que a variável num3 local é mostrada como indisponível. Uma variável local pode não estar disponível no código otimizado por vários motivos. Pode ser que a variável não exista no código otimizado. Pode ser que a variável ainda não tenha sido inicializada ou que a variável não esteja mais sendo usada.
0:000> p
MyApp!func1+0x7:
000007f6`8d62108f 8d3c33 lea edi,[rbx+rsi]
0:000> knL
# Child-SP RetAddr Call Site
00 (Inline Function) --------`-------- MyApp!func1+0x7
01 00000000`0050fc90 000007f6`8d6213f3 MyApp!main+0x8f
02 00000000`0050fcf0 000007ff`c6af0f7d MyApp!__tmainCRTStartup+0x10f
03 00000000`0050fd20 000007ff`c7063d6d KERNEL32!BaseThreadInitThunk+0xd
04 00000000`0050fd50 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
0:000> dv -v
00000000`0050fcb0 num1 = 0n0
00000000`0050fcb4 num2 = 0n0
<unavailable> num3 = <value unavailable>
Se olhares para o quadro 1 no rastreamento de pilha, podes ver as variáveis locais para a função main. Observe que duas das variáveis são armazenadas em registros.
0:000> .frame 1
01 00000000`0050fc90 000007f6`8d6213f3 MyApp!main+0x8f
0:000> dv -v
00000000`0050fd08 c = 0n7
@ebx b = 0n13
@esi a = 0n6
O depurador do Windows agrega dados de arquivos PDB para encontrar todos os locais onde uma função específica foi colocada em linha. Você pode usar o comando x para listar todos os locais de chamada de uma função inline.
0:000> x simple!MoreCalculate
00000000`ff6e1455 simple!MoreCalculate = (inline caller) simple!wmain+8d
00000000`ff6e1528 simple!MoreCalculate = (inline caller) simple!wmain+160
0:000> x simple!Calculate
00000000`ff6e141b simple!Calculate = (inline caller) simple!wmain+53
Como o depurador do Windows pode enumerar todos os pontos de chamada de uma função inline, ele pode definir um ponto de interrupção dentro da função inline calculando os deslocamentos a partir dos pontos de chamada. Você pode usar o comando bm (que é usado para definir pontos de interrupção que correspondem a padrões de expressão regular) para definir pontos de interrupção para funções embutidas.
O depurador do Windows agrupa todos os pontos de interrupção definidos para uma função inline específica em um contêiner de ponto de interrupção. Você pode manipular o contêiner de ponto de interrupção como um todo usando comandos como be, bd, bc. Veja os seguintes exemplos de comandos bd 3 e bc 3 . Você também pode manipular pontos de interrupção individuais. Veja o seguinte exemplo de comando be 2 .
0:000> bm simple!MoreCalculate
2: 00000000`ff6e1455 @!"simple!MoreCalculate" (simple!MoreCalculate inlined in simple!wmain+0x8d)
4: 00000000`ff6e1528 @!"simple!MoreCalculate" (simple!MoreCalculate inlined in simple!wmain+0x160)
0:000> bl
0 e 00000000`ff6e13c8 [n:\win7\simple\simple.cpp @ 52] 0001 (0001) 0:**** simple!wmain
3 e <inline function> 0001 (0001) 0:**** {simple!MoreCalculate}
2 e 00000000`ff6e1455 [n:\win7\simple\simple.cpp @ 58] 0001 (0001) 0:**** simple!wmain+0x8d (inline function simple!MoreCalculate)
4 e 00000000`ff6e1528 [n:\win7\simple\simple.cpp @ 72] 0001 (0001) 0:**** simple!wmain+0x160 (inline function simple!MoreCalculate)
0:000> bd 3
0:000> be 2
0:000> bl
0 e 00000000`ff6e13c8 [n:\win7\simple\simple.cpp @ 52] 0001 (0001) 0:**** simple!wmain
3 d <inline function> 0001 (0001) 0:**** {simple!MoreCalculate}
2 e 00000000`ff6e1455 [n:\win7\simple\simple.cpp @ 58] 0001 (0001) 0:**** simple!wmain+0x8d (inline function simple!MoreCalculate)
4 d 00000000`ff6e1528 [n:\win7\simple\simple.cpp @ 72] 0001 (0001) 0:**** simple!wmain+0x160 (inline function simple!MoreCalculate)
0:000> bc 3
0:000> bl
0 e 00000000`ff6e13c8 [n:\win7\simple\simple.cpp @ 52] 0001 (0001) 0:**** simple!wmain
Como não há instruções explícitas de chamada ou retorno para funções embutidas, a revisão no nível do código-fonte é especialmente desafiadora para um depurador. Por exemplo, você pode entrar involuntariamente em uma função embutida (se a próxima instrução fizer parte de uma função embutida), ou você pode entrar e sair da mesma função embutida várias vezes (porque os blocos de código para a função embutida foram divididos e movidos pelo compilador). Para preservar a experiência de revisão familiar, o depurador do Windows mantém uma pequena pilha de chamadas conceitual para cada endereço de instrução de código e cria uma máquina de estado interna para executar operações de entrada, passo a passo e saída. Isso proporciona uma aproximação razoavelmente precisa da experiência de revisão para funções não embutidas.
Informação Adicional
Observação Você pode usar o comando .inline 0 para desativar a depuração de funções inline. O comando .inline 1 permite a depuração de funções inline. Técnicas de depuração padrão