Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Importante
A Depuração Dinâmica do C++ está atualmente em versão prévia. Essas informações estão relacionadas a um recurso de pré-lançamento que pode ser substancialmente modificado antes da versão. A Microsoft não faz garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Este recurso de visualização, disponível a partir do Visual Studio 2022 Versão 17.14 Versão Prévia 2, aplica-se somente a projetos x64.
Com a Depuração Dinâmica do C++, você pode depurar o código otimizado como se ele não estivesse otimizado. Esse recurso é útil para desenvolvedores que exigem os benefícios de desempenho do código otimizado, como desenvolvedores de jogos que precisam de altas taxas de quadros. Com a Depuração Dinâmica do C++, você pode desfrutar da experiência de depuração de código não otimizado sem sacrificar as vantagens de desempenho de builds otimizados.
A depuração de código otimizado apresenta desafios. O compilador reposiciona e reorganiza as instruções para otimizar o código. O resultado é um código mais eficiente, mas significa:
- O otimizador pode remover variáveis locais ou movê-las para locais desconhecidos para o depurador.
- O código dentro de uma função pode não estar mais alinhado com o código-fonte quando o otimizador mescla blocos de código.
- Os nomes de função para funções na pilha de chamadas podem estar errados se o otimizador mesclar duas funções.
No passado, os desenvolvedores lidavam com esses problemas e outros quando estavam no processo de depuração de código otimizado. Agora esses desafios são eliminados porque, com a Depuração Dinâmica do C++, você pode entrar no código otimizado como se ele não estivesse otimizado.
Além de gerar os binários otimizados, compilar com /dynamicdeopt
gera binários não otimizados que são usados durante a depuração. Quando você adiciona um ponto de interrupção ou acessa uma função (incluindo funções __forceinline
), o depurador carrega o binário não otimizado. Em seguida, você pode depurar o código não otimizado da função em vez do código otimizado. Você pode depurar como se estivesse depurando código não otimizado enquanto ainda obtém as vantagens de desempenho do código otimizado no restante do programa.
Experimente a Depuração Dinâmica do C++
Primeiro, vamos examinar como é o processo de depurar o código otimizado. Em seguida, você pode ver como a Depuração Dinâmica do C++ simplifica o processo.
Crie um novo projeto de aplicativo de console C++ no Visual Studio. Substitua o conteúdo do arquivo ConsoleApplication.cpp pelo seguinte código:
// Code generated by GitHub Copilot #include <iostream> #include <chrono> #include <thread> using namespace std; int step = 0; const int rows = 20; const int cols = 40; void printGrid(int grid[rows][cols]) { cout << "Step: " << step << endl; for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { cout << (grid[i][j] ? '*' : ' '); } cout << endl; } } int countNeighbors(int grid[rows][cols], int x, int y) { int count = 0; for (int i = -1; i <= 1; ++i) { for (int j = -1; j <= 1; ++j) { if (i == 0 && j == 0) { continue; } int ni = x + i; int nj = y + j; if (ni >= 0 && ni < rows && nj >= 0 && nj < cols) { count += grid[ni][nj]; } } } return count; } void updateGrid(int grid[rows][cols]) { int newGrid[rows][cols] = { 0 }; for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { int neighbors = countNeighbors(grid, i, j); if (grid[i][j] == 1) { newGrid[i][j] = (neighbors < 2 || neighbors > 3) ? 0 : 1; } else { newGrid[i][j] = (neighbors == 3) ? 1 : 0; } } } // Copy newGrid back to grid for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { grid[i][j] = newGrid[i][j]; } } } int main() { int grid[rows][cols] = { 0 }; // Initial configuration (a simple glider) grid[1][2] = 1; grid[2][3] = 1; grid[3][1] = 1; grid[3][2] = 1; grid[3][3] = 1; while (true) { printGrid(grid); updateGrid(grid); std::this_thread::sleep_for(std::chrono::milliseconds(100)); cout << "\033[H\033[J"; // Clear the screen step++; } return 0; }
Altere a lista suspensa Configurações da Solução para Liberar. Verifique se a lista suspensa de plataforma de soluções está definida como x64.
Recompile selecionando Compilar>Recompilar Solução.
Defina um ponto de interrupção na linha 55,
int neighbors = countNeighbors(grid, i, j);
emupdateGrid()
. Execute o programa.Quando você atingir o ponto de interrupção, exiba a janela Locais. No menu principal, selecione Depurar>Windows>Locais. Observe que você não pode ver o valor de
i
ouj
na janela Locais . O compilador os otimizou.Tente definir um ponto de interrupção na linha 19,
cout << (grid[i][j] ? '*' : ' ');
emprintGrid()
. Isso não é possível. Esse comportamento é esperado porque o compilador otimou o código.
Interrompa o programa e habilite a Depuração Dinâmica do C++ e tente novamente
No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Propriedades para abrir as páginas de propriedades do projeto.
Selecione Avançado>Uso de Depuração Dinâmica C++, e altere a configuração para Sim.
A página de propriedades é aberta em Propriedades da Configuração > Avançado > Usar Depuração Dinâmica do C++. A propriedade está definida como Sim.
Esta etapa adiciona o switch
/dynamicdeopt
ao compilador e ao vinculador. Nos bastidores, ele também desativa os interruptores de otimização do C++/GL
e/OPT:ICF
. Essa configuração não substitui alternadores que você adicionou manualmente à linha de comando ou outros alternadores de otimização definidos, como/O1
.Recompile selecionando Compilar>Recompilar Solução. O código
MSB8088
de diagnóstico de build é exibido, o que indica que a depuração dinâmica e a otimização de programa inteiro são incompatíveis. Esse erro significa que toda a otimização do programa (/GL
) foi desativada automaticamente durante a compilação.Você pode desativar manualmente toda a otimização do programa nas propriedades do projeto. Selecione Propriedades de Configuração>Avançado>Otimização de Programa Inteiro e altere a configuração para Desativada. Agora
MSB8088
é tratado como um aviso, mas pode ser tratado como um erro em uma versão futura do Visual Studio.Execute novamente o aplicativo.
Agora, quando você atinge o ponto de interrupção na linha 55, você vê os valores de
i
ej
na janela Locais . A janela Pilha de Chamadas mostra queupdateGrid()
está desotimizado e que o nome do arquivo élife.alt.exe
. Esse binário alternativo é usado para depurar o código otimizado.Um ponto de interrupção é mostrado na função updateGrid. A pilha de chamadas mostra que a função está desoptimizada e o nome do arquivo é life.alt.exe. A janela Locals mostra os valores de i e j e as outras variáveis locais na função.
A
updateGrid()
função é desoptimizada sob demanda porque você define um ponto de interrupção nela. Se você executar passo a passo uma função otimizada durante a depuração, ela não será desotimizada. Se você entrar em uma função, ela será desotimizada. A principal maneira de fazer com que uma função seja desotimizada é definir um ponto de interrupção nela ou intervir nela.Você também pode desoptimizar uma função na janela Pilha de Chamadas . Clique com o botão direito do mouse na função ou em um grupo selecionado de funções e selecione Deoptimize na próxima entrada. Esse recurso é útil quando você deseja exibir variáveis locais em uma função otimizada para a qual você não definiu um ponto de interrupção em outro lugar na pilha de chamadas. As funções desoptimizadas dessa forma são agrupadas na janela Pontos de Interrupção como um grupo de pontos de interrupção chamado Deoptimized Functions. Se você excluir o grupo de pontos de interrupção, as funções associadas serão revertidas para o estado otimizado.
Usar pontos de interrupção condicionais e dependentes
Tente definir um ponto de interrupção novamente na linha 19,
cout << (grid[i][j] ? '*' : ' ');
emprintGrid()
. Agora funciona. Definir um ponto de interrupção na função a desotimiza para que você possa depurá-la normalmente.Clique com o botão direito do mouse no ponto de interrupção na linha 19, selecione Condições e defina a condição como
i == 10 && j== 10
. Em seguida, selecione a caixa de seleção Somente habilitar quando o seguinte ponto de interrupção for atingido:. Selecione o ponto de interrupção na linha 55 na lista suspensa. Agora, o ponto de interrupção na linha 19 só será atingido depois que o ponto de interrupção na linha 50 for atingido primeiro e quandogrid[10][10]
estiver prestes a ser exibido no console.O ponto é que você pode definir pontos de interrupção condicionais e dependentes em uma função otimizada e usar variáveis locais e linhas de código que em um build otimizado podem estar indisponíveis para o depurador.
Um ponto de interrupção condicional é mostrado na linha 19, cout < < (grid[i][j] ? '*' : ' ');. A condição é definida como i == 10 && j== 10. A caixa de seleção de "Somente habilitar quando o ponto de interrupção seguinte for atingido" está selecionada. A lista suspensa de pontos de interrupção está definida como life.cpp linha 55.
Continue executando o aplicativo. Quando o ponto de interrupção na linha 19 for atingido, clique com o botão direito do mouse na linha 15 e selecione Definir Próxima Instrução para executar novamente o loop.
Um ponto de interrupção condicional e dependente é atingido na linha 19, cout < < (grid[i][j] ? '*' : ' ');. A janela Locals mostra os valores de i e j e as outras variáveis locais na função. A janela Pilha de Chamadas mostra que a função está desoptimizada e o nome do arquivo é life.alt.exe.
Exclua todos os pontos de interrupção para retornar funções desoptimizadas ao estado otimizado. No menu principal do Visual Studio, selecione Depurar>Excluir Todos os Pontos de Interrupção. Todas as funções retornam ao estado otimizado.
Se você adicionar pontos de interrupção por meio da opção Pilha de ChamadasDesotimizar na próxima entrada, o que não fizemos neste passo a passo, você poderá clicar com o botão direito do mouse no grupo Funções Desotimizadas e selecionar Excluir para reverter apenas as funções desse grupo ao estado otimizado.
A janela Pontos de Interrupção mostra o grupo Funções Desotimizadas. O grupo está selecionado e o menu de contexto está aberto com Excluir Grupo de Pontos de Interrupção selecionado.
Desligar Depuração Dinâmica do C++
Talvez seja necessário depurar o código otimizado sem que ele seja desotimizado, ou colocar um ponto de interrupção no código otimizado e garantir que ele permaneça otimizado quando o ponto de interrupção for acionado. Há várias maneiras de desativar a Depuração Dinâmica ou impedir que ela desotimize o código quando você atinge um ponto de interrupção:
- No menu principal do Visual Studio, selecione Ferramentas>Opções>de Depuração>Geral. Desmarque a caixa de seleção Desotimizar automaticamente as funções depuradas quando possível (.NET 8+, Depuração Dinâmica do C++). Na próxima vez que o depurador for iniciado, o código permanecerá otimizado.
- Muitos pontos de interrupção de depuração dinâmica são dois pontos de interrupção: um no binário otimizado e outro no binário não otimizado. Na janela Pontos de Interrupção, selecione Mostrar Colunas>Função. Desmarque o ponto de interrupção associado ao
alt
binário. O outro ponto de interrupção do par interrompe no código otimizado. - Quando estiver depurando, no menu principal do Visual Studio, selecione Depurar>Janelas>Desmontagem. Verifique se ele está em foco. Quando você entra em uma função por meio da janela Desmontagem , a função não será desoptimizada.
- Desabilite totalmente a depuração dinâmica não passando
/dynamicdeopt
paracl.exe
,lib.exe
elink.exe
. Se você estiver usando bibliotecas de terceiros e não puder recompilá-las, não passe/dynamicdeopt
durante alink.exe
final para desabilitar a Depuração Dinâmica para esse binário. - Para desabilitar rapidamente a Depuração Dinâmica para um único binário (por exemplo,
test.dll
), renomeie ou exclua oalt
binário (por exemplo,test.alt.dll
). - Para desabilitar a Depuração Dinâmica para um ou mais
.cpp
arquivos, não passe/dynamicdeopt
ao compilá-los. O restante do seu projeto é compilado com Depuração Dinâmica.
Habilitar depuração dinâmica do C++ no Unreal Engine
O Unreal Engine 5.6 dá suporte à Depuração Dinâmica do C++ tanto para a Ferramenta de Compilação do Unreal quanto para o Acelerador de Compilação do Unreal. Há duas maneiras de habilitá-lo:
Modifique o arquivo de projeto
Target.cs
para conterWindowsPlatform.bDynamicDebugging = true
.Use a configuração do Editor de Desenvolvimento e modifique
BuildConfiguration.xml
para incluir:<WindowsPlatform> <bDynamicDebugging>true</bDynamicDebugging> </WindowsPlatform>
Para o Unreal Engine 5.5 ou anterior, faça cherry-pick das alterações da Ferramenta de Compilação do Unreal do GitHub no seu repositório. Em seguida, habilite bDynamicDebugging
conforme indicado acima. **
Você também precisa usar o Acelerador de Compilação do Unreal do Unreal Engine versão 5.6. Use os bits mais recentes do ue5-main ou desabilite o UBA adicionando o seguinte a BuildConfiguration.xml
:
<BuildConfiguration>
<bAllowUBAExecutor>false</bAllowUBAExecutor>
<bAllowUBALocalExecutor>false</bAllowUBALocalExecutor>
</BuildConfiguration>
Para obter mais informações sobre como o Unreal Engine é construído, consulte Configuração de Build.
Resolução de problemas
Se os pontos de interrupção não forem atingidos em funções desotimizadas:
Se você sair de um quadro
[Deoptimized]
, poderá estar em código otimizado, a menos que o autor da chamada tenha sido desotimizado devido a um ponto de interrupção nele ou você tenha intervindo no autor da chamada no caminho para a função atual.Verifique se os arquivos
alt.exe
ealt.pdb
foram construídos. Paratest.exe
etest.pdb
,test.alt.exe
etest.alt.pdb
deve existir no mesmo diretório. Verifique se os comutadores de build corretos estão definidos de acordo com este artigo.Existe uma entrada
debug directory
emtest.exe
que o depurador usa para localizar o binárioalt
a ser utilizado para Depuração Desotimizada. Abra um prompt de comando do Visual Studio nativo x64 e executelink /dump /headers <your executable.exe>
para ver se existe umadeopt
entrada. Umadeopt
entrada aparece naType
coluna, conforme mostrado na última linha deste exemplo:Debug Directories Time Type Size RVA Pointer -------- ------- -------- -------- -------- 67CF0DA2 cv 30 00076330 75330 Format: RSDS, {7290497A-E223-4DF6-9D61-2D7F2C9F54A0}, 58, D:\work\shadow\test.pdb 67CF0DA2 feat 14 00076360 75360 Counts: Pre-VC++ 11.00=0, C/C++=205, /GS=205, /sdl=0, guardN=204 67CF0DA2 coffgrp 36C 00076374 75374 67CF0DA2 deopt 22 00076708 75708 Timestamp: 0x67cf0da2, size: 532480, name: test.alt.exe
Se a entrada de diretório de depuração
deopt
não estiver presente, confirme se você está passando/dynamicdeopt
paracl.exe
,lib.exe
elink.exe
.A Deoptimização Dinâmica não funcionará consistentemente se
/dynamicdeopt
não for passada paracl.exe
,lib.exe
elink.exe
para todos.cpp
,.lib
e arquivos binários. Confirme se as opções apropriadas estão definidas quando você cria seu projeto.
Para obter mais informações sobre problemas conhecidos, consulte Depuração Dinâmica do C++: Depuração Completa para Builds Otimizados.
Se as coisas não estiverem funcionando conforme o esperado, abra um tíquete na Comunidade de Desenvolvedores. Inclua o máximo de informações possível sobre o problema.
Observações gerais
O IncrediBuild 10.24 dá suporte a compilações com Depuração Dinâmica do C++.
O FastBuild v1.15 dá suporte a builds de Depuração Dinâmica C++.
As funções embutidas são deoptimizadas sob demanda. Se você definir um ponto de interrupção em uma função embutida, o depurador desotimiza a função e seu autor da chamada. O ponto de interrupção atinge onde você espera, como se seu programa tivesse sido criado sem otimizações do compilador.
Uma função permanece desoptimizada mesmo se você desabilitar os pontos de interrupção dentro dela. Você deve remover o ponto de interrupção da função para reverter para o estado otimizado.
Muitos pontos de interrupção de depuração dinâmica são dois pontos de interrupção: um no binário otimizado e outro no binário não otimizado. Por esse motivo, você verá mais de um ponto de interrupção na janela Pontos de Interrupção .
Os sinalizadores do compilador usados para a versão desoptimizada são os mesmos que os sinalizadores usados para a versão otimizada, exceto para sinalizadores de otimização e /dynamicdeopt
. Esse comportamento significa que todos os sinalizadores definidos para definir macros e assim por diante também são definidos na versão desoptimizada.
O código desoptimizado não é o mesmo que o código de depuração. O código desoptimizado é compilado com os mesmos sinalizadores de otimização que a versão otimizada, portanto, as declarações e outros códigos que dependem de configurações específicas de depuração não estão incluídos.
Integração do sistema de compilação
A Depuração Dinâmica do C++ requer que os sinalizadores do compilador e do vinculador sejam definidos de uma maneira específica. As seções a seguir descrevem como configurar uma configuração dedicada para Depuração Dinâmica que não tem comutadores conflitantes.
Se o projeto for criado com o sistema de build do Visual Studio, uma boa maneira de fazer uma configuração de Depuração Dinâmica é usar o Configuration Manager para clonar sua configuração de Versão ou Depuração e fazer alterações para acomodar a Depuração Dinâmica. As duas seções a seguir descrevem os procedimentos.
Criar uma nova configuração de versão
No menu principal do Visual Studio, selecione Criar>Gerenciador de Configurações para abrir o Configuration Manager.
Selecione a lista suspensa Configuração e, em seguida, selecione <Novo...>.
No Gerenciador de Configurações, em contextos do Projeto, a lista suspensa Configuração está aberta e
está realçado. A caixa de diálogo Nova Configuração de Solução é aberta. No campo Nome , insira um nome para a nova configuração, como
ReleaseDD
. Verifique se Copiar configurações de: está definido como Liberar. Em seguida, selecione OK para criar a nova configuração.O campo Nome está definido como ReleaseDD. A lista suspensa "Copiar configurações de" está definida como Liberar.
A nova configuração aparece na lista suspensa Configuração ativa da solução. Selecione Fechar.
Com a lista suspensa Configuração definida como ReleaseDD, clique com o botão direito do mouse no seu projeto no Gerenciador de Soluções e selecione Propriedades.
Em Propriedades> de ConfiguraçãoAvançadas, defina Usar Depuração Dinâmica do C++ como Sim.
Verifique se a Otimização de Programas Inteira está definida como Não.
A página de propriedades é aberta em Propriedades de Configuração > Avançado. Usar Depuração Dinâmica C++. A propriedade está definida como Sim. A otimização de todo o programa está configurada para Não.
Em Propriedades de Configuração>Vinculador>Otimização, verifique se Habilitar a dobragem COMDAT está definida como Não (/OPT:NOICF).
A página de propriedades é aberta em Propriedades de Configuração > Vinculador > Otimização > Habilitar Dobramento CMDAT. A propriedade está configurada para Não (/OPT:NOICF).
Esta configuração adiciona o parâmetro /dynamicdeopt
ao compilador e ao vinculador. Com as opções /GL
e /OPT:ICF
de otimização do C++ também desativadas, agora você pode compilar e executar seu projeto na nova configuração quando quiser uma compilação de versão otimizada que possa ser usada com a Depuração Dinâmica no C++.
Você pode adicionar outros parâmetros que usa com suas compilações de varejo a esta configuração para que sempre tenha exatamente os parâmetros ativados ou desativados que espera ao usar a Depuração Dinâmica. Para obter mais informações sobre opções que você não deve usar com a Depuração Dinâmica, consulte opções incompatíveis.
Para obter mais informações sobre configurações no Visual Studio, consulte Criar e editar configurações.
Criar uma nova configuração de Depuração
Se você quiser usar binários de depuração, mas quiser que eles sejam executados mais rapidamente, você poderá modificar a configuração de Depuração.
No menu principal do Visual Studio, selecione Criar>Gerenciador de Configurações para abrir o Configuration Manager.
Selecione a lista suspensa Configuração e, em seguida, selecione <Novo...>.
No Gerenciador de Configurações, na parte Contextos do projeto da janela, a lista suspensa Configuração está aberta e
está realçado. A caixa de diálogo Nova Configuração do Projeto é aberta. No campo Nome , insira um nome para a nova configuração, como DebugDD. Verifique se Copiar configurações de: está definido como Depuração. Em seguida, selecione OK para criar a nova configuração.
O campo de nome é definido como DebugDD. A lista suspensa Copiar configurações de: está definida como Depuração.
A nova configuração aparece na lista suspensa Configuração ativa da solução. Selecione Fechar.
Com a lista suspensa Configuração definida como DebugDD, clique com o botão direito do mouse no seu projeto no Gerenciador de Soluções e selecione Propriedades.
Nas Propriedades> de ConfiguraçãoC/C++>Otimização, ative as otimizações desejadas. Por exemplo, você pode definir Otimização para Maximizar Velocidade (/O2).
Na geração de >, defina verificações básicas de runtime como padrão.
Em C/C++>Geral, desabilite Suporte à Depuração Apenas Meu Código.
Defina o Formato de Informações de Depuração para Program Database (/Zi).
Você pode adicionar outros comutadores que você usa com seus builds de depuração a essa configuração para que você sempre tenha exatamente os comutadores ativados ou desativados que você espera ao usar a Depuração Dinâmica. Para obter mais informações sobre opções que você não deve usar com a Depuração Dinâmica, consulte opções incompatíveis.
Para obter mais informações sobre configurações no Visual Studio, consulte Criar e editar configurações.
Considerações sobre sistemas de build personalizados
Se você tiver um sistema de build personalizado, verifique se você:
- Passe
/dynamicdeopt
paracl.exe
,lib.exe
elink.exe
. - Não use
/ZI
, nenhum dos sinalizadores/RTC
ou/JMC
.
Para distribuidores de compilação:
- Para um projeto chamado
test
, o compilador produztest.alt.obj
,test.alt.exp
test.obj
etest.exp
. O vinculador produztest.alt.exe
,test.alt.pdb
etest.exe
test.pdb
. - Você precisa implantar o novo binário do conjunto de ferramentas
c2dd.dll
junto comc2.dll
.
Opções incompatíveis
Algumas opções de compilador e vinculador são incompatíveis com a Depuração Dinâmica do C++. Se você ativar a Depuração Dinâmica do C++ usando as configurações de projeto do Visual Studio, as opções incompatíveis serão desativadas automaticamente, a menos que você as defina especificamente na configuração de opções de linha de comando adicionais.
As seguintes opções do compilador são incompatíveis com a Depuração Dinâmica do C++:
/GH
/GL
/Gh
/RTC1
/RTCc
/RTCs
/RTCu
/ZI (/Zi is OK)
/ZW
/clr
/clr:initialAppDomain
/clr:netcore
/clr:newSyntax
/clr:noAssembly
/clr:pure
/clr:safe
/fastcap
/fsanitize=address
/fsanitize=kernel-address
As seguintes opções de vinculador são incompatíveis com a Depuração Dinâmica do C++:
/DEBUG:FASTLINK
/INCREMENTAL
/OPT:ICF You can specify /OPT:ICF but the debugging experience may be poor
Consulte também
Sinalizador do compilador /dynamicdeopt (versão prévia)
Sinalizador do vinculador /DYNAMICDEOPT (versão prévia)
Depuração Dinâmica C++: Depuração Total para Compilações Otimizadas
Depurar código otimizado