La strada percorsa finora
Un anno fa, Chrome ha annunciato il supporto iniziale per il debug nativo di WebAssembly in Chrome DevTools.
Abbiamo dimostrato di avere un supporto di base durante il passo e parlato delle opportunità l'utilizzo delle informazioni DWARF anziché le mappe di origine ci apriranno in futuro:
- Risoluzione dei nomi delle variabili
- Tipi di stampa avvincenti
- Valutazione delle espressioni nelle lingue di origine
- ...e molto altro ancora.
Oggi siamo felici di presentare le funzionalità promesse che sono diventate realtà e i progressi fatti dai team di Emscripten e Chrome DevTools quest'anno, in particolare per le app C e C++.
Prima di iniziare, tieni presente che si tratta ancora di una versione beta della nuova esperienza, devi utilizzare la versione più recente di tutti gli strumenti a tuo rischio e pericolo e, se riscontri problemi, segnalali a https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.
Iniziamo con lo stesso semplice esempio dell'ultima volta:
#include <stdlib.h>
void assert_less(int x, int y) {
if (x >= y) {
abort();
}
}
int main() {
assert_less(10, 20);
assert_less(30, 20);
}
Per compilarlo, utilizziamo la versione più recente di Emscripten
e passiamo un flag -g
, proprio come nel post originale, per includere le informazioni di debugging:
emcc -g temp.c -o temp.html
Ora possiamo pubblicare la pagina generata da un server HTTP localhost (ad esempio, con serve) e nella versione più recente di Chrome Canary.
Questa volta avremo bisogno anche di un'estensione helper che si integri con Chrome DevTools e aiuta a dare un senso a tutte le informazioni di debug. codificati nel file WebAssembly. Per installarla, vai a questo link: goo.gle/wasm-debugging-extension
Ti consigliamo inoltre di attivare il debug di WebAssembly in DevTools Esperimenti. Apri Chrome DevTools, fai clic sull'icona a forma di ingranaggio (⚙) nell'angolo in alto a destra del riquadro di DevTools, vai al riquadro Sperimentali e seleziona Debug WebAssembly: abilita il supporto DWARF.
Quando chiudi le Impostazioni, DevTools ti suggerirà di ricaricarsi automaticamente. per applicare le impostazioni. È tutto per l'evento una tantum configurazione.
Ora possiamo tornare al riquadro Origini e attivare Metti in pausa su eccezioni (icona ⏸), poi seleziona Metti in pausa in caso di eccezioni rilevate e ricarica la pagina. Dovresti vedere i DevTools in pausa per un'eccezione:
Per impostazione predefinita, si interrompe su un codice glue generato da Emscripten, ma sulla
a destra puoi vedere una visualizzazione Stack di chiamate che rappresenta l'analisi dello stack
l'errore e possiamo passare alla riga C originale che ha richiamato
abort
:
Nella visualizzazione Ambito puoi vedere i nomi originali
e i valori delle variabili nel codice C/C++ e non è più necessario
cosa significano nomi alterati come $localN
e come si relazionano
che hai scritto.
Questo vale non solo per i valori primitivi come i numeri interi, ma anche per come strutture, classi, array e così via.
Supporto dei tipi avanzati
Diamo un'occhiata a un esempio più complicato per spiegarli. Questo volta, disegneremo un frattale di Mandelbrot con seguente codice 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();
}
Puoi vedere che questa applicazione è ancora abbastanza piccola: è un singolo file contenente 50 righe di codice, ma questa volta sto utilizzando anche alcune API esterne, come la libreria SDL per la grafica e i numeri complessi della libreria standard C++.
Lo compilerò con lo stesso flag -g
di cui sopra per includere
informazioni di debug e chiederò a Emscripten di fornire lo standard SDL2
libreria e consentire memoria di dimensioni arbitrarie:
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
Quando visito la pagina generata nel browser, vedo la bellissima forma frattale con alcuni colori casuali:
Quando apro DevTools, di nuovo vedo il file C++ originale. Questo Tuttavia, non abbiamo un errore nel codice (whew!), quindi impostiamo qualche punto di interruzione all'inizio del codice.
Quando ricarichiamo la pagina, il debugger viene messo in pausa direttamente all'interno della Sorgente C++:
Possiamo già vedere tutte le variabili a destra, ma solo width
e height
sono inizializzati al momento, quindi non c'è molto da
possono essere ispezionati.
Impostiamo un altro punto di interruzione all'interno del ciclo principale di Mandelbrot e riprendiamo per andare un po' avanti.
A questo punto il nostro palette
è stato riempito con alcuni colori casuali,
e possiamo espandere sia l'array stesso che la singola
SDL_Color
strutture e controllarne i componenti per verificare che
sembra tutto a posto (ad esempio, il canale "alpha" è sempre impostato
fino alla massima opacità). Allo stesso modo, possiamo espandere e verificare
parti immaginarie del numero complesso memorizzate nella variabile center
.
Se vuoi accedere a una proprietà con un alto grado di nidificazione, altrimenti difficile accedi tramite la vista Ambito, puoi usare la console valutazione. Tuttavia, tieni presente che le espressioni C++ più complesse non sono ancora supportate.
Riprenderemo l'esecuzione alcune volte per capire com'è l'elemento x
interno
modificando la vista Ambito, aggiungendo
il nome della variabile nella lista di controllo, valutandola nella console o
passando il mouse sopra la variabile nel codice sorgente:
Da qui, possiamo passare o eseguire il passaggio successivo delle istruzioni C++ e osservare come stanno cambiando anche altre variabili:
Bene, tutto questo funziona perfettamente quando sono disponibili informazioni di debug, ma E se volessimo eseguire il debug di un codice che non è stato creato con le opzioni disponibili?
Debug di WebAssembly non elaborato
Ad esempio, abbiamo chiesto a Emscripten di fornire una libreria SDL predefinita
anziché compilarlo direttamente dalla fonte, almeno
attualmente il debugger non può trovare origini associate.
Entriamo di nuovo in SDL_RenderDrawColor
:
Torniamo all'esperienza di debug di WebAssembly non elaborata.
Ora sembra un po' spaventoso e non è qualcosa che la maggior parte degli sviluppatori web ma a volte potresti voler eseguire il debug libreria creata senza informazioni di debug, indipendentemente dal fatto che si tratti di Una libreria di terze parti che non ha alcun controllo o perché ha incontrato uno di quei bug che si verificano solo in produzione.
Per venire incontro in questi casi, abbiamo apportato alcuni miglioramenti alle di debug.
Prima di tutto, se hai utilizzato prima il debug WebAssembly non elaborato, potresti
l'intero processo di smontaggio viene ora mostrato in un unico file
a indovinare a quale funzione corrisponde una voce Sources wasm-53834e3e/
wasm-53834e3e-7
.
Schema di generazione del nuovo nome
Abbiamo migliorato i nomi anche nella visualizzazione disassemblata. In precedenza vedevi solo indici numerici o, in caso di funzioni, nessun nome.
Ora generiamo i nomi in modo simile ad altri strumenti di smontaggio, utilizzando i suggerimenti della sezione dei nomi di WebAssembly, i percorsi di importazione/esportazione e, infine, se non funzionano, li generiamo in base al tipo e all'indice dell'elemento, ad esempio $func123
. Puoi
vediamo come, nello screenshot qui sopra, questo è già utile per ottenere
analisi dello stack e operazioni di smontaggio più leggibili.
Quando non sono disponibili informazioni sui tipi, potrebbe essere difficile da esaminare a qualsiasi valore oltre alle primitive: ad esempio, i puntatori verranno visualizzati come numeri interi regolari, senza modo di sapere cosa si trova dietro di essi la memoria.
Ispezione della memoria
In precedenza, potevi espandere solo l'oggetto di memoria WebAssembly, rappresentato da env.memory
nella visualizzazione Ambito per cercare,
singoli byte. Questa soluzione ha funzionato in alcune situazioni basilari, ma non
particolarmente conveniente da espandere e non permetteva di reinterpretare i dati
in formati diversi dai valori in byte. Abbiamo aggiunto una nuova funzionalità per aiutarti
anche con questo: un controllo
della memoria lineare.
Se fai clic con il tasto destro del mouse su env.memory
, ora dovresti vedere una nuova
chiamata Ispeziona memoria:
Dopo aver fatto clic, viene visualizzato un Controllo memoria, in che puoi esaminare nella memoria WebAssembly nelle visualizzazioni esadecimale e ASCII. raggiungere indirizzi specifici e interpretare i dati formati diversi:
Scenari avanzati e avvertenze
Profilazione del codice WebAssembly
Quando apri DevTools, il codice WebAssembly viene "ordinato a livelli" a un
non ottimizzata per attivare il debug. Questa versione è molto più lenta,
quindi non puoi fare affidamento su console.time
, performance.now
e altri metodi per misurare la velocità del codice, mentre DevTools
aperta, in quanto i numeri che ottieni non rappresentano il rendimento reale
del tutto.
Devi invece utilizzare il riquadro Rendimento di DevTools che esegue il codice alla massima velocità e fornisce una un'analisi dettagliata del tempo trascorso nelle varie funzioni:
In alternativa, puoi eseguire l'applicazione con DevTools chiuso e al termine dell'operazione, aprili per ispezionare la console.
Miglioreremo gli scenari di profilazione in futuro, ma per il momento è un caveat da tenere presente. Se vuoi scoprire di più su WebAssembly consulta la nostra documentazione sulla pipeline di compilazione WebAssembly.
Creazione e debug su macchine diverse (inclusi Docker / host)
Quando crei un Docker, una macchina virtuale o un server di build remoto, probabilmente imbatterai in situazioni in cui i percorsi dei file di origine utilizzati durante la creazione non corrispondono ai percorsi sul tuo file system in cui i Chrome DevTools sono in esecuzione. In questo caso, i file verranno visualizzati Origini ma non viene caricato.
Per risolvere il problema, abbiamo implementato una funzionalità di mappatura dei percorsi le opzioni dell'estensione C/C++. Puoi utilizzarlo per rimappare percorsi arbitrari e aiutare DevTools a individuare le origini.
Ad esempio, se il progetto sulla macchina host si trova in un percorso
C:\src\my_project
, ma è stata creata all'interno di un container Docker in cui
il percorso era rappresentato come /mnt/c/src/my_project
, puoi rimappare
durante il debug specificando i percorsi come prefissi:
Il primo prefisso corrispondente è "vince". Se hai familiarità con altri strumenti C++
debugger, questa opzione è simile al comando set substitute-path
in GDB o un'impostazione target.source-map
in LLDB.
Debug di build ottimizzate
Come con qualsiasi altra lingua, il debug funziona al meglio se le ottimizzazioni vengono disattivata. Le ottimizzazioni possono incorporare le funzioni una nell'altra, riordinare o rimuovere del tutto parti del codice e tutto questo ha un con la possibilità di confondere il debugger e, di conseguenza, l'utente.
Se preferisci un'esperienza di debug più limitata e vuoi comunque
eseguire il debug di una build ottimizzata, la maggior parte delle ottimizzazioni funzionerà
previsto, tranne che per l'incorporamento della funzione. Prevediamo di risolvere i problemi rimanenti in futuro, ma per il momento utilizza -fno-inline
per disattivarlo durante la compilazione con eventuali ottimizzazioni a livello di -O
, ad esempio:
emcc -g temp.c -o temp.html \ -O3 -fno-inline
Separazione delle informazioni di debug
Le informazioni di debug conservano molti dettagli sul codice, tipi, variabili, funzioni, ambiti e località, qualunque cosa possa utile al debugger. Di conseguenza, spesso può essere maggiore del il codice stesso.
Per velocizzare il caricamento e la compilazione del modulo WebAssembly, potresti
vuoi suddividere queste informazioni di debug in un'istanza WebAssembly separata
. Per farlo in Emscripten, passa un flag -gseparate-dwarf=…
con il nome del file che preferisci:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
In questo caso, l'applicazione principale memorizzerà solo il nome file
temp.debug.wasm
e l'estensione helper sarà in grado di individuare e
caricalo all'apertura di DevTools.
Se combinata con le ottimizzazioni descritte in precedenza, questa funzione può anche per produrre build di produzione quasi ottimizzate del tuo ed eseguirne il debug con un file lato locale. In questo caso, dovremo inoltre sostituire l'URL memorizzato per aiutare l'estensione per trovare il file laterale, ad esempio:
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]
Continua...
Wow, erano molte nuove funzionalità!
Con tutte queste nuove integrazioni, Chrome DevTools diventa un'app, potente, debugger non solo per JavaScript, ma anche per le app C e C++, rendendo più facile che mai prendere le app, integrate in una tecnologie e di portarle su un Web condiviso e multipiattaforma.
Tuttavia, il nostro viaggio non è ancora finito. Ecco alcune delle cose che lavorando da qui in poi:
- Eliminazione dei punti critici nell'esperienza di debug.
- Aggiunta del supporto per i formatter dei tipi personalizzati.
- Stiamo lavorando per migliorare le Profilazione per le app WebAssembly.
- È stato aggiunto il supporto della copertura del codice per facilitare la ricerca codice inutilizzato.
- Miglioramento del supporto delle espressioni nella valutazione della console.
- Aggiunta del supporto per altre lingue.
- …e altro ancora.
Nel frattempo, aiutaci provando l'attuale versione beta sul tuo codice e segnalando eventuali problemi a https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.
Scaricare i canali in anteprima
Prendi in considerazione l'utilizzo di Chrome Canary, Dev o Beta come browser di sviluppo predefinito. Questi canali di anteprima ti consentono di accedere alle funzionalità più recenti di DevTools, testare le API delle piattaforme web all'avanguardia e individuare i problemi sul tuo sito prima che lo facciano gli utenti.
Contattare il team di Chrome DevTools
Utilizza le seguenti opzioni per discutere delle nuove funzionalità e modifiche nel post o di qualsiasi altra informazione relativa a DevTools.
- Inviaci un suggerimento o un feedback tramite crbug.com.
- Segnala un problema di DevTools utilizzando Altre opzioni > Guida > Segnala un problema di DevTools in DevTools.
- Invia un tweet all'indirizzo @ChromeDevTools.
- Lascia commenti sulle novità nei video di YouTube di DevTools o nei video di YouTube dei suggerimenti di DevTools.