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

No Chrome 92, apresentamos o Memory Inspector, 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++, é possível 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 a partir do início dele. 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 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 Memory Inspector 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 Memory Inspector 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 mostra 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 que está inspecionando, o destaque é desfocado para evitar distrações. Para mudar o foco novamente, clique novamente em qualquer um dos bytes do objeto ou no ícone.

O suporte para destaque de objetos não é limitado a matrizes. Também é possível inspecionar structs, objetos e ponteiros. Com essas mudanças, ficou mais fácil analisar a memória dos seus apps C/C++.

Quer testar? Será necessário:

  • Ter o Chrome 107 ou mais recente.
  • Instale a extensão C/C++ DWARF.
  • Ative a depuração DWARF em Ferramentas do desenvolvedor > Configurações. Configurações > Experimentos > Depuração de WebAssembly: Ativar 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 analisar um bug de brinquedo para ilustrar como usar o Memory Inspector na 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. Confira 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, eles revelam a variável lastNumber no painel Escopo e notam 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 seja um exemplo simples, ele ilustra como o destaque de objetos transmite de forma eficaz o tamanho e a posição dos objetos de memória, o que pode ajudar a entender melhor o que está acontecendo na memória do app 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 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 WebAssembly com o compilador Emscripten mais recente, conforme instruído nesta postagem do blog.

Mas por quê? O V8 , mecanismo JavaScript e WebAssembly do Chrome, não sabe 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 vai incorporar dados de depuração DWARF ao seu 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 você tiver curiosidade, confira esta postagem do blog para conferir um exemplo de dados de depuração 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 essas informações, o Memory Inspector poderá mostrar a parte relevante da memória e, sabendo o tipo, 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 desreferenciação de ponteiro no estilo C++ para indicar o tipo do objeto mostrado. Se você inspecionar um ponteiro, o inspetor vai mostrar para onde ele aponta.

Persistência de destaques nas 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, então 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: