In Chrome 92 abbiamo introdotto il Controllo memoria, uno strumento per esaminare i buffer di memoria lineari. In questo articolo, illustreremo come abbiamo migliorato il debug di Inspector per C/C++ e le difficoltà tecniche incontrate durante il processo.
Di seguito sono riportati alcuni post del blog pertinenti se non hai mai utilizzato il debug C/C++ e l'Controllo memoria:
- Ti interessa il debug della memoria profonda? Vedi Introduzione a Controllo memoria.
- Vuoi un'introduzione alla suite completa di strumenti di debug C/C++? Consulta Debug di WASM con strumenti moderni e Debug di WebAssembly più veloce.
Introduzione
Il Controllo memoria offre opzioni di debug più efficaci per i buffer di memoria lineari. Nel caso di C/C++, puoi ispezionare gli oggetti di memoria C/C++ nella memoria di WebAssembly.
Riconoscere i byte dell'oggetto nella memoria WebAssembly circostante era un punto dolente. Devi conoscere le dimensioni dell'oggetto e contare i byte dall'inizio dell'oggetto. Nello screenshot seguente viene selezionato il primo byte di un array int32
a 10 elementi, ma non è immediatamente chiaro quali altri byte appartengono all'array. Non sarebbe bello se si potesse riconoscere all'istante tutti i byte che appartengono all'oggetto?
Evidenziazione degli oggetti in Controllo memoria
A partire da Chrome 107, il Controllo memoria evidenzia tutti i byte di un oggetto di memoria C/C++. Questo ti aiuta a distinguerli dalla memoria circostante.
Guarda il video qui sotto per vedere come funziona il Controllo memoria. Quando riveli l'array x
in Controllo memoria, nel visualizzatore memoria viene mostrata la memoria evidenziata insieme a un nuovo chip al di sopra. Questo chip ti ricorda il nome e il tipo di ricordo evidenziato. Fai clic sul chip per passare alla memoria dell'oggetto. Se passi il mouse sopra il chip, viene visualizzata un'icona a forma di croce. Fai clic sul chip per rimuovere l'evidenziazione.
Quando selezioni un byte all'esterno dell'oggetto ispezionato, l'evidenziazione viene sfocata per evitare di distrarti. Per rimettere a fuoco l'oggetto, fai di nuovo clic su uno qualsiasi dei byte dell'oggetto o sul chip.
Il supporto dell'evidenziazione degli oggetti non è limitato agli array. Puoi anche ispezionare struct, oggetti e puntatori. Grazie a questi cambiamenti, esplorare la memoria delle tue app C/C++ sarà più facile che mai.
Vuoi provare? Dovrai:
- Avere Chrome 107 o versioni successive.
- Installa l'estensione DWARF C/C++.
- Attiva il debug di DWARF in DevTools > Impostazioni > Esperimenti > WebAssemble Debugging: attiva il supporto DWARF.
- Apri questa pagina demo.
- Segui le istruzioni riportate nella pagina.
Esempio di debug
In questa sezione, diamo un'occhiata a un bug giocattolo per spiegare come puoi usare lo strumento Controllo memoria per il debug C/C++. Nel codice di esempio riportato di seguito, un programmatore crea un array di numeri interi e decide di utilizzare un'aritmetica del puntatore per selezionare l'ultimo elemento. Sfortunatamente, il programmatore ha commesso un errore nel calcolo del puntatore e ora, invece di stampare l'ultimo elemento, il programma stampa valori senza senso.
#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;
}
Il programmatore utilizza il Controllo memoria per eseguire il debug del problema. Puoi seguire questa demo. Innanzitutto, esaminano l'array nel Controllo memoria e vedono che l'array numbers
contiene solo i numeri interi 1
, 2
, 3
e 4
, come previsto.
Quindi, rivelano la variabile lastNumber
dal riquadro Ambito e noteranno che il puntatore punta a un numero intero esterno all'array. Grazie a queste conoscenze, il programmatore si rende conto di aver sbagliato il calcolo dell'offset del puntatore alla linea 8. Avrebbe dovuto essere ptr + arraySize - 1
.
Anche se questo è un esempio di giocattolo, illustra come l'evidenziazione degli oggetti trasmette in modo efficace le dimensioni e la posizione degli oggetti di memoria, il che può aiutarti a capire meglio cosa sta succedendo nella memoria della tua app C/C++.
In che modo DevTools individua gli elementi da evidenziare
In questa sezione, esamineremo l'ecosistema di strumenti che consentono il debug C/C++. Nello specifico, scoprirai in che modo DevTools, V8, l'estensione DWARF C/C++ ed Emscripten rendono possibile il debug di C/C++ in Chrome.
Per sfruttare tutta la potenza del debug C/C++ in DevTools, ti servono due cose:
- L'estensione DWARF C/C++ installata in Chrome
- File di origine C/C++ compilati in WebAssembly con il compilatore Emscripten più recente, come indicato in questo post del blog
Ma perché? V8 , il motore JavaScript e WebAssembly di Chrome, non sa come eseguire C o C++. Grazie a Emscripten, un compilatore da C/C++ a WebAssembly, puoi compilare app create in C o C++ come WebAssembly ed eseguirle nel browser.
Durante la compilazione, emscripten incorporerà i dati di debug DWARF nel tuo file binario. A livello generale, questi dati aiutano l'estensione a capire quali variabili WebAssembly corrispondono alle tue variabili C/C++ e altro ancora. In questo modo, DevTools può mostrarti le variabili C++ nonostante V8 esegua effettivamente WebAssembly. Se ti interessa, leggi questo post del blog per un esempio dei dati di debug di DWARF.
Cosa succede effettivamente quando sveli lastNumber
? Non appena fai clic sull'icona della memoria, DevTools controlla quale variabile vuoi esaminare. Quindi esegue una query all'estensione sul tipo di dati e sulla località di lastNumber
. Non appena l'estensione risponde con queste informazioni, Controllo memoria può mostrare la porzione di memoria pertinente e, conoscendo il tipo, può anche mostrare le dimensioni dell'oggetto.
Se osservi il grafico lastNumber
nell'esempio precedente, potresti notare che abbiamo ispezionato lastNumber: int *
, ma il chip in Controllo memoria indica *lastNumber: int
, che cosa restituisce? La finestra di ispezione utilizza il deriferimento del puntatore in stile C++ per indicare il tipo di oggetto che ti viene mostrato. Se ispezioni un puntatore, la finestra di controllo ti mostrerà a cosa punta.
Evidenziazioni persistenti nei passaggi del debugger
Quando riveli un oggetto nello strumento Controllo memoria e nel passaggio tramite il debugger, l'ispezione mantiene l'evidenziazione se ritiene che sia ancora applicabile. Inizialmente non avevamo questa funzionalità nella nostra roadmap, ma ci siamo subito resi conto che questa funzionalità comprometteva la tua esperienza di debug. Immagina di dover controllare nuovamente l'array dopo ogni passaggio, come nel seguente video.
Quando il debugger raggiunge un nuovo punto di interruzione, il Controllo memoria esegue nuovamente una query su V8 e sull'estensione per la variabile associata all'evidenziazione precedente. Quindi, confronta le posizioni e i tipi di oggetti. Se corrispondono, l'evidenziazione persiste. Nel video riportato sopra, c'è una scrittura for-loop sull'array x
. Queste operazioni non cambiano il tipo o la posizione dell'array, quindi rimane evidenziato.
Potresti chiederti come questo influisce sui puntatori. Se hai un puntatore evidenziato e lo riassegna a un oggetto diverso, la vecchia e la nuova posizione degli oggetti evidenziati differiscono e l'evidenziazione scompare. Poiché l'oggetto appena puntato può trovarsi ovunque nella memoria di WebAssembly e probabilmente avrà poca relazione con la posizione della memoria precedente, rimuovere l'evidenziazione è più chiaro che passare a una nuova posizione di memoria. Puoi evidenziare di nuovo il puntatore facendo clic sulla relativa icona della memoria nel riquadro Ambito.
Conclusione
In questo articolo sono stati descritti i miglioramenti che abbiamo apportato al Controllo memoria per il debug C/C++. Ci auguriamo che le nuove funzionalità semplificheranno il debug della memoria delle tue app C/C++. Se hai suggerimenti per migliorarla ulteriormente, segnalando un bug.
Passaggi successivi
Per saperne di più, consulta: