O caminho até o momento
Um ano atrás, o Chrome anunciou suporte inicial para depuração nativa do WebAssembly no Chrome DevTools.
Demonstramos suporte básico para caminhadas e conversamos sobre oportunidades de uso de informações de DWARF em vez de mapas de origem no futuro:
- Como resolver nomes de variáveis
- Tipos de estilos de formatação
- Como avaliar expressões em linguagens de origem
- E muito mais.
Hoje, temos o prazer de mostrar os recursos prometidos ganharem vida e o progresso que as equipes do Emscripten e do Chrome DevTools fizeram neste ano, principalmente para apps C e C++.
Antes de começar, lembre-se de que esta ainda é uma versão Beta da nova experiência, você precisa usar a versão mais recente de todas as ferramentas por sua conta e risco e, se tiver algum problema, informe-o em https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.
Vamos começar com o mesmo exemplo de C simples da última vez:
#include <stdlib.h>
void assert_less(int x, int y) {
if (x >= y) {
abort();
}
}
int main() {
assert_less(10, 20);
assert_less(30, 20);
}
Para compilá-lo, usamos o Emscripten mais recente
e transmitimos uma sinalização -g
, assim como na postagem original, para incluir informações
de depuração:
emcc -g temp.c -o temp.html
Agora podemos exibir a página gerada de um servidor HTTP localhost (por exemplo, com serve) e abrir a página no Chrome Canary mais recente.
Desta vez, também precisaremos de uma extensão auxiliar que se integre com o Chrome DevTools e ajude a entender todas as informações de depuração codificadas no arquivo WebAssembly. Para fazer a instalação, acesse este link: goo.gle/wasm-debugging-extension
Ative também a depuração do WebAssembly nos Experimentos do DevTools. Abra o Chrome DevTools, clique no ícone de engrenagem (⚙) no canto superior direito do painel, acesse o painel Experimentos e marque Depuração da WebAssembly: ativar suporte a DWARF.
Quando você fechar as Configurações, o DevTools vai sugerir a atualização para aplicar as configurações, então vamos fazer isso. Isso é tudo para a configuração única.
Agora podemos voltar ao painel Sources, ativar a opção Pausar em exceções (ícone ⏸), marcar Pausar em exceções capturadas e recarregar a página. O DevTools terá pausado em uma exceção:
Por padrão, ela é interrompida em um código agrupador gerado por Emscripten, mas, à
direita, é possível conferir uma visualização de Pilha de chamadas que representa o stack trace do
erro e pode navegar para a linha C original que invocou
abort
:
Na visualização Scope, você pode conferir os nomes
e valores originais das variáveis no código C/C++ e não precisa mais
descobrir o que significam nomes corrompidos, como $localN
, e como eles estão relacionados ao
código-fonte escrito.
Isso se aplica não apenas a valores primitivos, como números inteiros, mas também a tipos compostos, como estruturas, classes, matrizes etc.
Suporte a rich text
Vamos analisar um exemplo mais complicado para mostrar isso. Desta vez, renderizaremos um fractal de Mandelbrot com o seguinte código C++:
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
Você pode notar que esse aplicativo ainda é bastante pequeno. Ele é um único arquivo que contém 50 linhas de código, mas também estou usando algumas APIs externas, como a biblioteca SDL para gráficos, bem como números complexos da biblioteca C++ padrão.
Vou compilá-lo com a mesma flag -g
acima para incluir
informações de depuração e também pedir ao Emscripten que forneça a biblioteca SDL2
e permita uma memória de tamanho arbitrariamente:
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
Quando acesso a página gerada no navegador, posso ver a bela forma fractal com algumas cores aleatórias:
Quando abro o DevTools, mais uma vez, posso ver o arquivo C++ original. Desta vez, no entanto, não há um erro no código (ufa!), então vamos definir algum ponto de interrupção no início do código.
Quando atualizarmos a página novamente, o depurador será pausado dentro da fonte em C++:
Já podemos ver todas as variáveis à direita, mas apenas width
e height
são inicializados no momento, então não há muito o que
inspecionar.
Vamos definir outro ponto de interrupção dentro do loop de Mandelbrot principal e retomar a execução para avançar um pouco.
Neste ponto, a palette
foi preenchida com algumas cores aleatórias, e podemos expandir a própria matriz e as estruturas
SDL_Color
individuais e inspecionar os componentes para verificar se
tudo está certo. Por exemplo, se o canal "Alfa" está sempre definido
como opacidade total. Da mesma forma, podemos expandir e verificar as partes reais e imaginárias do número complexo armazenado na variável center
.
Se você quiser acessar uma propriedade aninhada que seria difícil de navegar pela visualização Escopo, também é possível usar a avaliação do Console. No entanto, ainda não há suporte para expressões C++ mais complexas.
Vamos retomar a execução algumas vezes e ver como o x
interno também está
mudando na visualização Scope, adicionando
o nome da variável à lista de observação, avaliando-a no console ou
passando o cursor sobre a variável no código-fonte:
A partir daqui, podemos adicionar ou ignorar instruções C++ e observar como outras variáveis também estão mudando:
Tudo isso funciona muito bem quando há informações de depuração disponíveis, mas e se quisermos depurar um código que não foi criado com as opções de depuração?
Depuração de WebAssembly brutas
Por exemplo, pedimos ao Emscripten que fornecesse uma biblioteca SDL pré-criada
para nós, em vez de compilá-la usando a origem. Então, pelo menos
no momento, não há como o depurador encontrar fontes associadas.
Faça login novamente para entrar no SDL_RenderDrawColor
:
Estamos de volta à experiência de depuração bruta do WebAssembly.
Parece um pouco assustador e não é algo com que a maioria dos desenvolvedores da Web vai precisar lidar, mas às vezes você pode querer depurar uma biblioteca criada sem informações de depuração, seja por ser uma biblioteca de terceiros que você não tem controle ou porque está enfrentando um desses bugs que ocorrem apenas na produção.
Para ajudar nesses casos, também fizemos algumas melhorias na experiência básica de depuração.
Primeiro, se você já usou a depuração bruta do WebAssembly antes,
poderá notar que todo o código desmontado agora é mostrado em um único arquivo, sem
mais adivinhar a qual função uma entrada wasm-53834e3e/
wasm-53834e3e-7
de Sources pode corresponder.
Novo esquema de geração de nomes
Também melhoramos os nomes na visualização de desmontagem. Anteriormente, você veria apenas índices numéricos ou, no caso de funções, nenhum nome.
Agora, estamos gerando nomes de maneira semelhante a outras ferramentas de desmontagem,
usando dicas da seção Nome do WebAssembly,
caminhos de importação/exportação e, por fim, se tudo falhar, gerando-os
com base no tipo e no índice do item, como $func123
. Veja
na captura de tela acima como isso já ajuda a conseguir stack traces
e desmontagens um pouco mais legíveis.
Quando não há informações de tipo disponíveis, pode ser difícil inspecionar valores além dos primitivos. Por exemplo, os ponteiros vão aparecer como números inteiros regulares, sem como saber o que está armazenado por trás deles na memória.
Inspeção de memória
Antes, só era possível expandir o objeto de memória WebAssembly, representado por env.memory
na visualização Scope, para procurar
bytes individuais. Isso funcionou em alguns cenários triviais, mas não era
muito conveniente para expandir e não permitiu reinterpretar dados
em formatos que não sejam valores de bytes. Também adicionamos um novo recurso para
ajudar com isso: o inspetor de memória linear.
Ao clicar com o botão direito do mouse em env.memory
, você verá uma nova
opção chamada Inspecionar memória:
Depois que você clicar nele, será exibido um Inspetor de memória, em que é possível inspecionar a memória do WebAssembly em visualizações hexadecimais e ASCII, navegar até endereços específicos e interpretar os dados em formatos diferentes:
Cenários e advertências avançados
Como criar o perfil do código WebAssembly
Quando você abre o DevTools, o código do WebAssembly é "em camadas" para uma
versão não otimizada para permitir a depuração. Essa versão é muito mais lenta,
o que significa que não é possível confiar em console.time
, performance.now
e outros métodos de medição da velocidade do código enquanto o DevTools está
aberto, já que os números recebidos não representam o desempenho real.
Em vez disso, use o painel Performance do DevTools, que executa o código em velocidade máxima e fornece um detalhamento detalhado do tempo gasto em diferentes funções:
Como alternativa, você pode executar seu aplicativo com o DevTools fechado e abrir o aplicativo depois de terminar para inspecionar o Console.
Vamos melhorar os cenários de criação de perfil no futuro, mas, por enquanto, é uma ressalva que você precisa conhecer. Se você quiser saber mais sobre os cenários de níveis do WebAssembly, confira nossos documentos sobre o pipeline de compilação do WebAssembly.
Criar e depurar em diferentes máquinas (incluindo Docker / host)
Ao criar em um Docker, uma máquina virtual ou em um servidor de build remoto, você provavelmente vai se deparar com situações em que os caminhos para os arquivos de origem usados durante a criação não correspondem aos caminhos no seu sistema de arquivos em que o Chrome DevTools está sendo executado. Nesse caso, os arquivos vão aparecer no painel Sources, mas não serão carregados.
Para corrigir esse problema, implementamos uma funcionalidade de mapeamento de caminho nas opções de extensão C/C++. Ele pode ser usado para remapear caminhos arbitrários e ajudar o DevTools a localizar origens.
Por exemplo, se o projeto na máquina host estiver em um caminho
C:\src\my_project
, mas tiver sido criado dentro de um contêiner do Docker em que
esse caminho foi representado como /mnt/c/src/my_project
, será possível remapeá-lo
durante a depuração especificando esses caminhos como prefixos:
O primeiro prefixo correspondente "vence". Caso você conheça outros depuradores
C++, essa opção é semelhante ao comando set substitute-path
no GDB ou a uma configuração target.source-map
no LLDB.
Como depurar builds otimizados
Como em outras linguagens, a depuração funciona melhor quando as otimizações estão desativadas. As otimizações podem incorporar funções inline em outro, reordenar o código ou remover partes dele. Tudo isso pode confundir o depurador e, consequentemente, você como usuário.
Se você não se importa em ter uma experiência de depuração mais limitada e ainda quer
depurar um build otimizado, a maioria das otimizações vai funcionar conforme
o esperado, exceto a função em linha. Planejamos resolver os problemas
restantes no futuro, mas, por enquanto, use -fno-inline
para
desativá-lo ao compilar com otimizações no nível -O
, por exemplo:
emcc -g temp.c -o temp.html \ -O3 -fno-inline
Como separar as informações de depuração
As informações de depuração preservam muitos detalhes sobre seu código, tipos definidos, variáveis, funções, escopos e locais, e tudo que possa ser útil para o depurador. Como resultado, ele geralmente pode ser maior que o código em si.
Para acelerar o carregamento e a compilação do módulo WebAssembly, divida essas informações de depuração em um arquivo WebAssembly
separado. Para fazer isso no Emscripten, transmita uma sinalização -gseparate-dwarf=…
com
o nome de arquivo desejado:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
Nesse caso, o aplicativo principal armazenará apenas um nome de arquivo
temp.debug.wasm
, e a extensão auxiliar poderá localizá-lo e
carregá-lo quando você abrir o DevTools.
Quando combinado com otimizações como as descritas acima, esse recurso pode ser usado para enviar builds de produção quase otimizados do seu aplicativo e depois depurá-los com um arquivo secundário local. Nesse caso, também precisaremos substituir o URL armazenado para ajudar a extensão a encontrar o arquivo lateral. Por exemplo:
emcc -g temp.c -o temp.html \ -O3 -fno-inline \ -gseparate-dwarf=temp.debug.wasm \ -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]
Para continuar...
Ufa, foram muitos recursos novos!
Com todas essas novas integrações, o Chrome DevTools se torna um depurador viável e avançado não apenas para JavaScript, mas também para apps C e C++, facilitando mais do que nunca o uso de apps criados com diversas tecnologias e os levar a uma Web compartilhada entre plataformas.
No entanto, nossa jornada ainda não acabou. Algumas das coisas em que trabalharemos a partir daqui em diante:
- Como limpar os pontos fora da curva na experiência de depuração
- Foi adicionado suporte a formatadores de tipos personalizados.
- Estamos trabalhando em melhorias na criação de perfil para apps WebAssembly.
- Foi adicionado suporte à cobertura de código para facilitar a localização de códigos não utilizados.
- Melhoria no suporte a expressões na avaliação do console.
- Foi adicionado suporte a mais idiomas.
- E muito mais.
Enquanto isso, ajude nossa equipe testando a versão Beta atual no seu próprio código e informando os problemas encontrados para https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.
Fazer o download dos canais de visualização
Use o Chrome Canary, Dev ou Beta como seu navegador de desenvolvimento padrão. Esses canais de pré-lançamento oferecem acesso aos recursos mais recentes do DevTools, testam as APIs modernas de plataformas da Web e encontram problemas no site antes dos usuários.
Entrar em contato com a equipe do Chrome DevTools
Use as opções a seguir para discutir os novos recursos e mudanças na publicação ou qualquer outra coisa relacionada ao DevTools.
- Envie uma sugestão ou feedback em crbug.com.
- Informe um problema do DevTools em Mais opções > Ajuda > Informar problemas no DevTools.
- Envie um tweet em @ChromeDevTools.
- Deixe comentários nos nossos vídeos do YouTube sobre a ferramenta DevTools ou nos vídeos do YouTube com dicas sobre o DevTools.