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

No Chrome 92, lançamos o Inspetor de memória, uma ferramenta para inspecionar buffers de memória linear. Neste artigo, vamos discutir como melhoramos o Inspector para depuração C/C++ e os desafios técnicos encontrados.

Estas são algumas postagens relevantes do blog para iniciantes na depuração de C/C++ e no 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 do C/C++, você pode inspecionar objetos de memória C/C++ na memória WebAssembly.

Reconhecer os bytes do seu objeto na memória do WebAssembly era um problema. 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 fica 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

Objeto em destaque 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 você a diferenciá-los da memória ao redor.

Captura de tela do inspetor de memória atualizado com uma matriz destacada de forma vibrante

Assista o vídeo abaixo para conferir o Inspetor de memória em ação. Ao revelar a matriz x no Inspetor de memória, a memória destacada aparece no visualizador de memória com um novo chip 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, clique novamente nos bytes do objeto ou no ícone.

O suporte ao destaque de objetos não se limita a matrizes. Também é possível inspecionar structs, objetos e ponteiros. Essas mudanças facilitam a navegação pela memória dos seus apps C/C++.

Quer tentar? Você precisará:

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

Exemplo de depuração

Nesta seção, vamos analisar um bug de brinquedo para ilustrar como usar o Memory Inspector para depuração C/C++. No exemplo de código abaixo, um programador cria uma matriz de números inteiros e decide usar a aritmética do ponteiro para selecionar o último elemento. Infelizmente, o programador cometeu um erro no cálculo do ponteiro e, em vez de mostrar o último elemento, o programa mostra 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 consulta o 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, ele revela a variável lastNumber do painel Scope e percebe que o ponteiro aponta para um número inteiro fora da matriz. Equipado com esse conhecimento, o programador percebe que errou na contagem do deslocamento do ponteiro na linha 8. Deveria ser 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 está logo após o último byte da matriz destacada anteriormente.

Embora seja um exemplo de brinquedo, ele ilustra como o destaque de objetos 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 app C/C++.

Como o DevTools define o que destacar

Nesta seção, vamos conhecer o ecossistema de ferramentas que permite a depuração C/C++. Especificamente, você aprenderá como o DevTools, o V8, a extensão DWARF do C/C++ e o Emscripten possibilitam a depuração C/C++ no Chrome.

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

  • A extensão DWARF do C/C++ 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.

Mas por quê? O V8 , o mecanismo JavaScript e WebAssembly do Chrome, não sabe executar C ou C++. Graças ao Emscripten, um compilador de 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 incorpora 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 suas variáveis C++ mesmo que o V8 esteja executando o WebAssembly. Caso tenha curiosidade, confira esta postagem do blog para um exemplo de dados de depuração DWARF.

O que acontece quando você revela o lastNumber? Assim que você clicar no ícone de memória, o DevTools verificará 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 essas informações, o Memory Inspector poderá mostrar a fração de memória relevante e saber o tipo, além de mostrar o tamanho do objeto.

Se você analisar lastNumber no exemplo anterior, vai notar que inspecionamos lastNumber: int *, mas o ícone no Inspetor de memória diz *lastNumber: int. O que está acontecendo? 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 vai mostrar para que ele aponta.

Destaques persistentes nas etapas do depurador

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

Quando o depurador atinge um novo ponto de interrupção, o Inspetor de memória consulta novamente o V8 e a extensão para a variável associada ao destaque anterior. Em seguida, ele compara os locais e tipos dos objetos. Se eles forem correspondentes, o destaque vai persistir. No vídeo acima, há um loop for gravado na matriz x. Essas operações não alteram o tipo ou a posição da matriz, por isso ela permanece destacada.

Você pode se perguntar como isso afeta os ponteiros. Se você tiver um ponteiro destacado e reatribuí-lo a um objeto diferente, as posições antiga e nova dos objetos destacados serão diferentes, e o destaque desaparecerá. Como o objeto recém-pontilhado pode residir em qualquer lugar da memória do 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. É possível destacar o ponteiro novamente clicando no ícone de memória no painel Scope.

Conclusão

Este artigo descreveu nossas melhorias no Inspetor de memória para depuração 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: