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:
- Risolvere i 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 l'ultimo Emscripten
e passare un flag -g
, proprio come nel post originale, per includere il debug
informazioni:
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 di supporto che si integri con Chrome DevTools e aiuta a dare un senso a tutte le informazioni di debug. codificati nel file WebAssembly. Installala seguendo questo link: goo.gle/wasm-debugging-extension
Inoltre, devi attivare il debug di WebAssembly in Esperimenti di DevTools. Apri Chrome DevTools, fai clic sull'icona a forma di ingranaggio (⚙) nella Nell'angolo in alto a destra del riquadro DevTools, vai al riquadro Esperimenti e seleziona WebAssembly Debugging: 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, attivare Metti in pausa su eccezioni (icona ⏸), quindi selezionare Metti in pausa su eccezioni rilevate e reloading 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
:
Ora, se guardi nella visualizzazione Ambito, puoi vedere i nomi e i valori originali delle variabili nel codice C/C++ e non devi più capire cosa significano nomi alterati come $localN
e come si riferiscono al codice sorgente che hai scritto.
Questo vale non solo per i valori primitivi come gli interi, ma anche per i tipi composti come strutture, classi, array e così via.
Supporto dei tipi avanzati
Vediamo un esempio più complicato per mostrarli. 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ò anche a Emscripten di fornire la libreria SDL2 e di consentire una 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, riesco di nuovo a vedere il file C++ originale. Questa volta, però, non c'è un errore nel codice (meno male!), quindi impostiamo un breakpoint all'inizio del codice.
Quando ricarichiamo di nuovo la pagina, il debugger si mette in pausa proprio all'interno del codice sorgente C++:
Possiamo già vedere tutte le nostre variabili a destra, ma al momento solo width
e height
sono inizializzate, quindi non c'è molto da controllare.
Impostiamo un altro punto di interruzione all'interno del nostro loop Mandelbrot principale e riprendiamo l'esecuzione 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à). Analogamente, possiamo espandere e controllare le parti reale e immaginaria del numero complesso memorizzato 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.
Riprendiamo l'esecuzione alcune volte e vediamo come cambia anche x
interno: controllando di nuovo la visualizzazione Ambito, aggiungendo il nome della variabile alla lista di controllo, valutandola nella console o passando il mouse sopra la variabile nel codice sorgente:
Da qui, possiamo eseguire il passaggio all'istruzione C++ o ignorarla e osservare anche come cambiano 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 fornirci una libreria SDL precompilata, anziché compilarla noi stessi dal codice sorgente, quindi, almeno al momento, non c'è modo per il debugger di trovare le origini associate.
Riproviamo ad accedere a SDL_RenderDrawColor
:
Torniamo all'esperienza di debug di WebAssembly non elaborata.
Sembra un po' spaventoso e non è qualcosa che la maggior parte degli sviluppatori web dovrà mai gestire, ma a volte potresti voler eseguire il debug di una libreria creata senza informazioni di debug, ad esempio perché si tratta di una libreria di terze parti su cui non hai alcun controllo o perché hai riscontrato uno di quei bug che si verificano solo in produzione.
Per aiutarti in questi casi, abbiamo apportato alcuni miglioramenti anche all'esperienza di debugging di base.
Innanzitutto, se in precedenza hai utilizzato il debug di WebAssembly non elaborato, potresti notare che l'intero disassemblage ora viene mostrato in un unico file. Non dovrai più indovinare a quale funzione potrebbe corrispondere una voce wasm-53834e3e/
wasm-53834e3e-7
Origini.
Schema di generazione del nuovo nome
Abbiamo migliorato anche i nomi nella visualizzazione di smontaggio. In precedenza, venivano visualizzati solo gli 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 sul tipo, potrebbe essere difficile ispezionare qualsiasi valore oltre a quelli primitivi. Ad esempio, i puntatori verranno visualizzati come interi regolari, senza alcun modo di sapere cosa è memorizzato al loro interno nella memoria.
Ispezione della memoria
In precedenza, per cercare singoli byte potevi espandere solo l'oggetto memoria WebAssembly, rappresentato da env.memory
nella visualizzazione Ambito. Questa soluzione ha funzionato in alcuni scenari banali, 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 visualizzare una nuova
opzione denominata Esamina memoria:
Dopo aver fatto clic, viene visualizzato un Memory Inspector, in cui puoi ispezionare la memoria WebAssembly nelle visualizzazioni esadecimali e ASCII, accedere ad indirizzi specifici e interpretare i dati in diversi formati:
Scenari avanzati e avvertenze
Profilazione del codice WebAssembly
Quando apri DevTools, il codice WebAssembly viene "ridotto" a una versione non ottimizzata per abilitare il debug. Questa versione è molto più lenta,
il che significa che non puoi fare affidamento su console.time
, performance.now
e su altri metodi per misurare la velocità del codice quando DevTools è aperto, poiché i numeri ottenuti non rappresentano affatto le prestazioni reali.
Ti consigliamo invece di utilizzare il pannello Rendimento di DevTools, che eseguirà il codice a piena velocità e ti fornirà un'analisi dettagliata del tempo trascorso nelle diverse funzioni:
In alternativa, puoi eseguire l'applicazione con DevTools chiuso e aprirlo al termine 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 esegui la compilazione in Docker, in una macchina virtuale o su un server di compilazione remoto, è probabile che si verifichino situazioni in cui i percorsi dei file di origine utilizzati durante la compilazione non corrispondono ai percorsi sul tuo file system su cui vengono eseguiti gli strumenti di sviluppo di Chrome. 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 tua macchina host si trova in un percorsoC:\src\my_project
, ma è stato creato all'interno di un contenitore Docker in cui quel percorso era rappresentato come /mnt/c/src/my_project
, puoi rimappare nuovamente il percorso durante il debug specificando questi percorsi come prefissi:
Il primo prefisso trovato "vince". Se hai dimestichezza con altri debugger C++, questa opzione è simile al comando set substitute-path
in GDB o a un'impostazione target.source-map
in LLDB.
Debug di build ottimizzate
Come per qualsiasi altra lingua, il debug funziona al meglio se le ottimizzazioni sono disabilitate. Le ottimizzazioni potrebbero inserire in linea le funzioni una nell'altra, riordinare il codice o rimuoverne parti del tutto e tutto ciò potrebbe confondere il debugger e, di conseguenza, te come utente.
Se non ti dispiace un'esperienza di debug più limitata e vuoi comunque eseguire il debugging di una build ottimizzata, la maggior parte delle ottimizzazioni funzionerà come previsto, ad eccezione dell'inserimento in linea delle funzioni. Prevediamo di risolvere i problemi rimanenti in futuro, ma per il momento utilizza -fno-inline
per disattivarlo durante la compilazione con qualsiasi ottimizzazione 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 posizioni definiti, ovvero tutto ciò che potrebbe essere utile al debugger. Di conseguenza, spesso può essere più grande del 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 file desiderato:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
In questo caso, l'applicazione principale memorizza solo un nome filetemp.debug.wasm
e l'estensione di supporto sarà in grado di trovarlo e caricarlo quando apri DevTools.
Se combinata con le ottimizzazioni descritte in precedenza, questa funzionalità può anche per produrre build di produzione quasi ottimizzate del tuo ed eseguirne il debug con un file lato locale. In questo caso, dobbiamo anche sostituire l'URL memorizzato per aiutare l'estensione a trovare il file secondario, 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, quante 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 funzionalità su cui lavoreremo da qui in poi:
- Eliminazione delle imperfezioni 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 per la copertura del codice per trovare più facilmente il 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
Valuta la possibilità di utilizzare 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 API di piattaforme web all'avanguardia e trovare i problemi sul tuo sito prima che lo facciano i tuoi utenti.
Contattare il team di Chrome DevTools
Utilizza le seguenti opzioni per discutere delle nuove funzionalità e delle modifiche nel post o di qualsiasi altro argomento relativo a DevTools.
- Inviaci un suggerimento o un feedback tramite crbug.com.
- Segnala un problema DevTools utilizzando Altre opzioni > Guida > Segnala un problema DevTools in DevTools.
- Invia un tweet all'indirizzo @ChromeDevTools.
- Lascia un commento sotto i nostri video di YouTube sulle novità di DevTools o sui video di YouTube sui suggerimenti per DevTools.