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 discutir como melhoramos o Inspector para depuração em C/C++ e os desafios técnicos encontrados no 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++, é possível 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 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 fica claro de imediato 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

Realce de objetos 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. Quando você revela a matriz x no Memory Inspector, a memória destacada aparece no Memory Viewer com um novo chip logo acima. 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 focar novamente, clique em qualquer um dos bytes do objeto ou no chip.

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? Você vai precisar:

  • 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 do WebAssembly: 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 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, em vez de imprimir 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 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 incorretamente o deslocamento do ponteiro na linha 8. Ele 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 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 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 esteja executando 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, ele consulta a extensão no tipo de dados e local de lastNumber. Assim que a extensão responder com essas informações, o Memory Inspector poderá mostrar a fatia de memória relevante 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 para a variável associada ao destaque anterior. Em seguida, ela compara os locais e tipos dos objetos. Se forem correspondentes, o destaque vai ser mantido. No vídeo acima, há um loop for gravando 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 o atribuir novamente a um objeto diferente, as posições antigas e novas dos objetos destacados serão diferentes, e o destaque vai 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 as melhorias no Memory Inspector para depuração em 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: