Apresentação do Memory Inspector

Kim-Anh Tran
Kim-Anh Tran

Este artigo apresenta o Memory Inspector lançado no Chrome 91. Ele permite inspecionar ArrayBuffer, TypedArray, DataView e Wasm Memory.

Introdução

Já quis entender os dados no ArrayBuffer? Antes do Memory Inspector, as DevTools permitiam apenas insights limitados sobre ArrayBuffers. A inspeção da visualização Escopo durante uma sessão de depuração era limitada a uma lista de valores únicos no buffer de matriz, o que dificultava a compreensão dos dados como um todo. Por exemplo, a visualização Escopo mostra o buffer como intervalos expansíveis de matrizes no exemplo abaixo:

Visualização de escopo no DevTools

A navegação para um determinado intervalo dentro do buffer era um ponto problemático, exigindo que o usuário rolasse a tela para baixo até chegar a esse índice. Mas, mesmo que a navegação até uma posição seja fácil, essa forma de inspecionar valores é complicada: é difícil dizer o que esses números significam. E se eles não forem interpretados como bytes únicos, mas, por exemplo, como números inteiros de 32 bits?

Como inspecionar valores usando o Memory Inspector

Inspetor de memória

Com o Chrome 91, apresentamos o Memory Inspector, uma ferramenta para inspecionar buffers de matriz. Você já deve ter visto ferramentas de inspeção de memória para visualizar dados binários, que mostram o conteúdo binário em uma grade com os endereços e oferecem maneiras diferentes de interpretar os valores subjacentes. É isso que o Memory Inspector oferece. Com o Memory Inspector, agora é possível visualizar o conteúdo, navegar por ele e selecionar tipos para interpretar os valores disponíveis. Ele mostra os valores ASCII ao lado dos bytes e permite que o usuário selecione diferentes endianidades. Confira o Memory Inspector em ação abaixo:

Quer fazer um teste? Para saber como abrir o Memory Inspector e conferir seu buffer de matriz (ou TypedArray, DataView ou Wasm Memory) e mais informações sobre como usá-lo, acesse nossa documentação sobre o Memory Inspector. Teste estes exemplos (para JS, Wasm e C++).

Como projetar o Inspetor de memória

Nesta parte, vamos analisar como o Memory Inspector foi projetado usando Web Components e mostraremos uma das metas de design que tínhamos e como a implementamos. Se você quiser saber mais, confira nosso documento de design sobre o Memory Inspector.

Você pode ter visto nossa postagem no blog sobre Como migrar para os Web Components, em que Jack publicou nosso guia interno sobre como criar componentes de interface usando Web Components. A migração para os Web Components coincidiu com nosso trabalho no Memory Inspector. Por isso, decidimos testar o novo sistema. Confira abaixo um diagrama que mostra os componentes que criamos para criar o Memory Inspector (chamado internamente de Linear Memory Inspector):

Componentes da Web

O componente LinearMemoryInspector é o pai que combina os subcomponentes que criam todos os elementos no Memory Inspector. Basicamente, ele usa um Uint8Array e um address, e, em cada mudança, ele propaga os dados para os filhos, o que aciona uma nova renderização. O LinearMemoryInspector renderiza três subcomponentes:

  1. LinearMemoryViewer (mostrando os valores),
  2. LinearMemoryNavigator (permitindo a navegação) e
  3. LinearMemoryValueInterpreter (mostrando diferentes interpretações de tipo dos dados subjacentes).

O último é um componente pai, que renderiza o ValueInterpreterDisplay (mostrando os valores) e o ValueInterpreterSettings (selecionando quais tipos serão mostrados na tela).

Cada um dos componentes é projetado para representar apenas um pequeno componente da interface, de modo que os componentes possam ser reutilizados, se necessário. Sempre que novos dados são definidos em um componente, uma nova renderização é acionada, mostrando a mudança refletida nos dados definidos no componente. Confira abaixo um exemplo de fluxo de trabalho com nossos componentes, em que o usuário está alterando o endereço na barra de endereço, o que aciona uma atualização ao definir os novos dados, neste caso, o endereço a ser visualizado:

Diagrama de componentes

O LinearMemoryInspector se adiciona como um listener no LinearMemoryNavigator. A função addressChanged precisa ser acionada em um evento address-changed. Assim que o usuário edita a entrada de endereço, o evento mencionado é enviado, de modo que a função addressChanged seja chamada. Agora, essa função salva o endereço internamente e atualiza os subcomponentes usando um setter data(address, ..). Os subcomponentes salvam o endereço internamente e renderizam as visualizações novamente, mostrando o conteúdo nesse endereço específico.

Objetivo de design: tornar o desempenho e o consumo de memória independentes do tamanho do buffer

Um aspecto que tínhamos em mente durante o design do Inspetor de memória era que o desempenho dele deveria ser independente do tamanho do buffer.

Como você viu na parte anterior, o componente LinearMemoryInspector usa um UInt8Array para renderizar os valores. Ao mesmo tempo, queríamos garantir que o Memory Inspector não precisasse manter todos os dados, já que ele mostra apenas uma parte deles. Por exemplo, a memória Wasm pode ter até 4 GB, e não queremos armazenar esse volume no Memory Inspector.

Para garantir que a velocidade e o consumo de memória do Memory Inspector sejam independentes do buffer real que mostramos, permitimos que o componente LinearMemoryInspector mantenha apenas um subintervalo do buffer original.

Para que isso funcione, o LinearMemoryInspector precisa primeiro receber mais dois argumentos: um memoryOffset e um outerMemoryLength. O memoryOffset indica o deslocamento, em que o Uint8Array transmitido começa, e é necessário para renderizar os endereços de dados corretos. O outerMemoryLength é o comprimento do buffer original e é necessário para entender qual intervalo podemos mostrar:

buffer

Com essas informações, podemos garantir que ainda podemos renderizar a mesma visualização de antes (o conteúdo em torno do address), sem ter todos os dados no lugar. O que fazer se um endereço diferente for solicitado, que se enquadra em um intervalo diferente? Nesse caso, o LinearMemoryInspector aciona um RequestMemoryEvent, que atualiza o intervalo atual que é mantido. Confira um exemplo abaixo:

Diagrama de fluxo do gatilho de evento

Neste exemplo, o usuário navega pela página de memória (o Memory Inspector está usando a paginação para mostrar blocos de dados), o que aciona um PageNavigationEvent, que aciona um RequestMemoryEvent. Esse evento inicia a busca do novo intervalo, que é propagado para o componente LinearMemoryInspector ao definir os dados. Como resultado, mostramos os dados recém-buscados.

Ah, e você sabia? É possível até inspecionar a memória no código Wasm e C/C++.

O Memory Inspector não está disponível apenas para ArrayBuffers em JavaScript, mas também pode ser usado para inspecionar a memória Wasm e a memória apontada por referências/ponteiros C/C++ (usando nossa extensão DWARF. Consulte Como depurar o WebAssembly com ferramentas modernas. Uma pequena perspectiva do Memory Inspector em ação para depuração nativa de C++ na Web:

Inspecionar a memória em C++

Conclusão

Este artigo apresentou o Memory Inspector e mostrou um pouco do design dele. Esperamos que o Memory Inspector ajude você a entender o que está acontecendo no ArrayBuffer :-). Se você tiver sugestões para melhorar, entre em contato e registre um bug.

Fazer o download dos canais de visualização

Use o Chrome Canary, Dev ou Beta como navegador de desenvolvimento padrão. Esses canais de visualização dão acesso aos recursos mais recentes do DevTools, permitem testar APIs de plataforma da Web de última geração e ajudam a encontrar problemas no seu site antes dos usuários.

Entre em contato com a equipe do Chrome DevTools

Use as opções a seguir para discutir os novos recursos, atualizações ou qualquer outra coisa relacionada ao DevTools.