Estender o Memory Inspector para depuração C/C++

No Chrome 92, introduzimos o Inspetor de memória, uma ferramenta para inspecionar buffers de memória linear. Neste artigo, vamos falar sobre como melhoramos o Inspector para depuração de C/C++ e os desafios técnicos encontrados ao longo do caminho.

Estas são algumas postagens de blog relevantes se você não conhece a depuração de C/C++ e o Memory Inspector (link em inglês):

Introdução

O Inspetor de memória oferece opções de depuração mais eficientes para buffers de memória linear. No caso de C/C++, você pode inspecionar objetos de memória C/C++ na memória do WebAssembly.

Reconhecer os bytes do seu objeto entre a memória do WebAssembly ao redor era um ponto problemático. Você precisa saber o tamanho do objeto e contar os bytes desde o início. Na captura de tela abaixo, o primeiro byte de uma matriz int32 de 10 elementos está selecionado, mas não está imediatamente claro quais outros bytes pertencem à matriz. Não seria ótimo se você pudesse reconhecer instantaneamente todos os bytes que pertencem ao objeto?

Captura de tela do inspetor de memória original com um único byte destacado

Destaque de objeto no Inspetor de memória

A partir do Chrome 107, o Inspetor de memória destaca todos os bytes de um objeto de memória C/C++. Isso ajuda a diferenciá-los da memória ao redor.

Captura de tela do inspetor de memória atualizado com uma matriz em destaque

Assista ao vídeo abaixo para ver o Memory Inspector em ação. À medida que você revela a matriz x no Inspetor de memória, a memória destacada é exibida no Visualizador de memória, junto com um novo ícone logo acima dela. Esse ícone lembra o nome e o tipo da memória destacada. Clique no ícone para acessar a memória do objeto. Se você passar o cursor sobre o ícone, um ícone de cruz vai aparecer. Clique nele para remover o destaque.

Quando você seleciona um byte fora do objeto inspecionado, o destaque desfoca para evitar distrair você. Para mudar o foco novamente, clique novamente em qualquer um dos bytes do objeto ou no ícone.

O suporte ao destaque de objetos não se limita a matrizes. Você também pode inspecionar structs, objetos e ponteiros. Essas mudanças facilitam ainda mais a exploração da memória dos seus apps C/C++.

Quer testar? Será necessário:

  • Ter o Chrome 107 ou mais recente.
  • Instale a extensão DWARF do C/C++.
  • Ative a depuração DWARF no DevTools > Configurações. Configurações > Experimentos > Depuração WebAssemble: ativar o suporte a DWARF.
  • Abra esta página de demonstração.
  • Siga as instruções na página.

Exemplo de depuração

Nesta seção, vamos dar uma olhada em um bug para ilustrar como você pode usar o Memory Inspector para depuração de C/C++. No exemplo de código abaixo, um programador cria uma matriz de números inteiros e decide usar a aritmética de ponteiro para selecionar o último elemento. Infelizmente, o programador cometeu um erro no cálculo do ponteiro e agora, em vez de exibir o último elemento, o programa exibe valores sem sentido.

#include <iostream>

int main()
{
    int numbers[] = {1, 2, 3, 4};
    int *ptr = numbers;
    int arraySize = sizeof(numbers)/sizeof(int);
    int* lastNumber = ptr + arraySize;  // Can you notice the bug here?
    std::cout <<../ *lastNumber <<../ '\n';
    return 0;
}

O programador recorre ao Inspetor de memória para depurar o problema. Acompanhe esta demonstração. Primeiro, eles inspecionam a matriz no Inspetor de memória e percebem que a matriz numbers contém apenas os números inteiros 1, 2, 3 e 4, conforme esperado.

Captura de tela do inspetor de memória aberto com uma matriz int32 inspecionada. Todos os elementos da matriz estão destacados.

Em seguida, ela revela a variável lastNumber do painel Scope e percebe que o ponteiro aponta para um número inteiro fora da matriz. Com esse conhecimento, o programador percebe que houve um erro na contagem do deslocamento do ponteiro na linha 8. Deve ter sido ptr + arraySize - 1.

Captura de tela do inspetor de memória aberto mostrando a memória destacada apontada por um ponteiro chamado &quot;lastNumber&quot;. A memória destacada fica logo após o último byte da matriz destacada anteriormente.

Embora esse seja um exemplo de brinquedo, ele ilustra como o destaque de objeto transmite efetivamente o tamanho e a posição dos objetos de memória, o que pode ajudar você a entender melhor o que está acontecendo na memória do seu aplicativo C/C++.

Como o DevTools descobre o que destacar

Nesta seção, analisaremos o ecossistema de ferramentas que permitem a depuração de C/C++. Especificamente, você aprenderá como DevTools, V8, a extensão DWARF C/C++ e o Emscripten viabilizam a depuração C/C++ no Chrome.

Para aproveitar toda a capacidade da depuração de C/C++ no DevTools, você precisa de duas coisas:

  • A extensão C/C++ DWARF instalada no Chrome
  • Arquivos de origem C/C++ compilados para o WebAssembly com o compilador Emscripten mais recente, conforme instruído nesta postagem do blog (link em inglês).

Mas por quê? O V8 , o mecanismo JavaScript e WebAssembly do Chrome, não sabe como executar C ou C++. Graças ao Emscripten, um compilador C/C++ para WebAssembly, você pode compilar apps criados em C ou C++ como WebAssembly e executá-los no navegador.

Durante a compilação, o emscripten incorporará dados de depuração do DWARF ao binário. De modo geral, esses dados ajudam a extensão a descobrir quais variáveis do WebAssembly correspondem às suas variáveis C/C++ e muito mais. Dessa forma, o DevTools pode mostrar as variáveis C++ mesmo que o V8 realmente execute o WebAssembly. Se quiser saber mais, confira esta postagem do blog (em inglês) para ver um exemplo de dados de depuração do DWARF.

O que acontece quando você revela o lastNumber? Assim que você clica no ícone de memória, o DevTools verifica qual variável você quer inspecionar. Em seguida, ela consulta a extensão sobre o tipo de dados e o local de lastNumber. Assim que a extensão responder com essa informação, o Memory Inspector poderá exibir a fração relevante de memória e saber o tipo dela, também poderá mostrar o tamanho do objeto.

Se você analisar o lastNumber no exemplo anterior, vai notar que inspecionamos o lastNumber: int *, mas o que aparece no chip no Memory Inspector (link em inglês)?*lastNumber: int O inspetor usa a desreferenciamento de ponteiro no estilo C++ para indicar o tipo de objeto mostrado a você. Se você inspecionar um ponteiro, o inspetor mostrará para que ele aponta.

Como manter destaques em etapas do depurador

Quando você revela um objeto no Memory Inspector e usa o depurador, o Inspector mantém o destaque se achar que ainda é aplicável. Inicialmente, não tínhamos esse recurso em nosso roteiro, mas logo percebemos que isso compromete sua experiência de depuração. Imagine ter que inspecionar a matriz novamente após cada etapa, como no vídeo abaixo.

Quando o depurador atinge um novo ponto de interrupção, o Memory Inspector consulta novamente o V8 e a extensão da variável associada ao destaque anterior. Em seguida, ele compara os locais e tipos dos objetos. Se forem iguais, o destaque persiste. No vídeo acima, há uma repetição "for" na matriz x. Essas operações não mudam o tipo ou a posição da matriz, portanto, ela permanece destacada.

Você deve estar se perguntando como isso afeta os ponteiros. Se você tiver um ponteiro destacado e reatribuí-lo a outro objeto, as posições antiga e nova dos objetos destacados serão diferentes, e o destaque desaparecerá. Como o objeto recém-apontado pode residir em qualquer lugar na memória WebAssembly e provavelmente terá pouca relação com o local da memória anterior, remover o destaque é mais claro do que pular para um novo local da memória. Você pode destacar o ponteiro novamente clicando no ícone de memória dele no painel Scope.

Conclusão

Este artigo descreve nossas melhorias no Inspetor de memória para depuração de C/C++. Esperamos que os novos recursos simplifiquem a depuração da memória dos seus apps C/C++. Se você tiver sugestões para melhorar ainda mais, registre um bug.

O que vem em seguida?

Para saber mais, veja: