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 relevantes do blog se você é iniciante na depuração de C/C++ e no Memory Inspector:

Introdução

O Memory Inspector 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 objeto entre a memória do WebAssembly foi 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 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 o vídeo abaixo para conferir o Memory Inspector em ação. À medida que você revela a matriz x no Inspetor de memória, a memória destacada aparece no Memory Viewer 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. Também é possível 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 usa o Memory Inspector para depurar o problema. Acompanhe esta demonstração. Primeiro, eles inspecionam a matriz no Memory Inspector e verificam que a matriz numbers contém apenas os números inteiros 1, 2, 3 e 4, como esperado.

Captura de tela do inspetor de memória aberto com uma matriz int32 inspecionada. Todos os elementos da matriz sã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 contou errado o deslocamento do ponteiro na linha 8. O valor 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 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, vamos analisar o ecossistema de ferramentas que permitem a depuração de C/C++. Especificamente, você vai aprender como o DevTools, o V8, a extensão C/C++ DWARF e o Emscripten tornam possível a depuração de C/C++ no Chrome.

Para aproveitar ao máximo a 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 WebAssembly com o compilador Emscripten mais recente, conforme instruído nesta postagem do blog.

Mas por quê? O V8, mecanismo do JavaScript e do WebAssembly do Chrome, não sabe como executar C ou C++. Graças ao Emscripten, um compilador C/C++ para WebAssembly, é possível 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 variáveis C/C++ e muito mais. Dessa forma, o DevTools pode mostrar suas 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, ele consulta a extensão no tipo de dados e 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ê olhar para lastNumber no exemplo anterior, vai notar que inspecionamos lastNumber: int *, mas o chip no Memory Inspector 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 mostrará para que ele aponta.

Como manter destaques em etapas do depurador

Quando você revela um objeto no Inspetor de memória e avança com o depurador, o Inspetor mantém o destaque se ele achar que ele ainda é relevante. Inicialmente, não tínhamos esse recurso no nosso plano, mas percebemos rapidamente que isso comprometia 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, ela compara os locais e tipos dos objetos. Se forem iguais, o destaque persiste. No vídeo acima, há um loop for que grava na matriz x. Essas operações não mudam o tipo ou a posição da matriz, portanto, ela permanece destacada.

Você pode se perguntar 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 estar em qualquer lugar na memória do WebAssembly e provavelmente terá pouca relação com o local de memória anterior, remover o destaque é mais claro do que pular para um novo local de memória. Para destacar o ponteiro novamente, clique no ícone de memória dele no painel Escopo.

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: