Ciao e benvenuto.
In questa lezione vorrei parlare un po' dello stack delle chiamate, di cosa sia esattamente e di come
sia possibile leggerlo per aiutare il debug del programma.
Una traccia di stack, che è essenzialmente lo stack delle chiamate, viene generata ogni volta che l'applicazione si
blocca a causa di un errore fatale.
Il motivo per cui viene utilizzato il termine stack è che si riferisce a una struttura di dati comune nella programmazione.
Lo stack è una struttura di dati nota come LIFO, last in, first out, ed è il modo in cui i dati vengono memorizzati.
L'ultimo dato aggiunto è il primo che viene recuperato.
Si può pensare di mangiare una pila di frittelle, la prima frittella è quella in basso, quella
che si aggiunge.
L'ultimo pancake è in cima.
Quando si va a mangiare, si mangia l'ultimo che è stato aggiunto.
Una traccia di stack è quindi molto utile quando il programma si blocca, perché contiene informazioni che aiutano a eseguire
il debug.
Una traccia di stack mostra quindi un elenco delle chiamate di funzione che hanno portato all'errore.
In un programma, quando si chiama la funzione principale, questo è il punto di ingresso.
La funzione principale chiamerà altre funzioni.
Queste funzioni chiameranno altre funzioni e tutte quelle che vengono implementate o chiamate
saranno messe in pila.
Quando un programma si blocca, lo stack delle chiamate indica l'ultima funzione invocata
e la riga di codice specifica che ha causato l'errore.
Il Callstack contiene quindi molte informazioni utili.
Include il nome del file e i numeri di riga del codice che causa l'eccezione o l'errore
fatale.
La parte superiore dello stack contiene l'ultima chiamata che ha causato l'errore e potrebbero esserci chiamate annidate.
La parte inferiore dello stack contiene la prima chiamata che ha iniziato la catena di chiamate per richiamare l'errore.
Quindi, se di solito in C si trova la routine main, si invoca la main, la main invoca un'altra funzione, che
invoca un'altra funzione e così via.
E poi l'ultima riga è dettagliata.
È molto, molto utile quando un'app si blocca.
Quindi, quando un programma C si blocca, può generare un errore di segmentazione che non fornisce molte informazioni.
Significa solo che è successo qualcosa di brutto.
Anche questo genererà un file di base.
È possibile analizzare il file core, che contiene lo stack delle chiamate, per individuare l'esatta riga di codice
che ha causato il problema dell'arresto anomalo.
È quindi necessario trovare la chiamata nell'applicazione che causa l'arresto anomalo.
E lo stack delle chiamate è probabilmente il modo migliore per farlo.
E non è sempre necessario guardare il file centrale.
È anche possibile analizzare lo stack delle chiamate utilizzando GDB con un debugger.
E voglio mostrarvi come si presenta.
Il programmatore può anche scaricare la traccia dello stack in qualsiasi momento.
Quindi, se si verifica un errore o si riconosce che potrebbe verificarsi un errore fatale, si può fare il dump dello stack delle
chiamate per vedere cosa sta succedendo.
Se non si sa quando si verificherà l'errore, si può comunque eseguire il dump dello stack
delle chiamate per vedere le tracce correnti che hanno portato a una particolare chiamata.
Quando si usa GDB, lo stack delle chiamate avrà un aspetto simile a questo.
È possibile accedere allo stack delle chiamate tramite il menu quando si esegue il debugger
in GDB e ora vi mostrerò come farlo, ma questo è l'aspetto e ciò che vedrete qui è un numero, un
indirizzo e poi la funzione.
Questa particolare pila di chiamate ha molte chiamate diverse, quindi deve aver fatto molti annidamenti e
molte chiamate di funzioni diverse.
Ma in fondo allo stack, alla riga 22, c'è scritto GTK main.
Ciò significa che il.
Ciò significa che probabilmente è stata eseguita la funzione principale.
E poi, alla riga 21, sembra che sia stato eseguito.
Forse una dichiarazione dice: "Esegui il ciclo principale".
E poi sembra che sia stata chiamata una funzione chiamata G main context iterate.
E all'interno di questa funzione, ogni volta che c'era un'altra chiamata a una funzione di dispacciamento del
contesto principale di IAG.
Quindi c'era una funzione di iterazione, poi una funzione di dispacciamento e i punti interrogativi nella riga 18
significano che non ci sono informazioni di debug.
Non sa cosa sia successo lì.
Ecco perché quando si costruisce un programma, si può scegliere se costruirlo per il rilascio o se costruirlo
con le informazioni di debug.
Quando lo si costruisce con le informazioni di debug, contiene informazioni aggiuntive al momento della compilazione, in modo che durante
l'esecuzione sia possibile esaminare gli stack delle chiamate.
Quando lo rilasciate, costruite qualcosa per il rilascio.
L'eseguibile è molto più piccolo e potrebbe non contenere queste informazioni.
Potrebbe contenere più punti interrogativi.
Ma questo vi dirà anche se avete invocato librerie, programmi aggiuntivi, li chiamerà e vi dirà
lo stack di chiamate da lì.
Ma ciò che mostra, ancora una volta, è che i diversi eventi annidati per la chiamata in cima sono importanti,
perché indicano la causa dell'arresto anomalo.
Sembra quindi che in questo particolare stack di chiamate, il refresh interno abbia causato un problema.
E si può notare che dalla riga 22 alla riga zero, ci sono molte invocazioni diverse di codice diverso.
Ma l'eventuale crash del programma alla riga zero chiama stack.
È anche possibile guardare gli stack delle chiamate mentre il programma è in esecuzione per vedere le informazioni e poterle rintracciare.
Quindi, se si inserisce un punto di interruzione all'interno di una funzione, si esegue il debugger su di essa.
Il debugger si fermerà a quel punto di interruzione.
È quindi possibile visualizzare lo stack delle chiamate, che indica come si è arrivati a quella riga di codice.
Questa funzione è stata invocata prima di questa funzione.
E di nuovo, di solito inizia con la funzione principale.
Se avete familiarità con lo stack e con il concetto di memoria, potete anche passare
a frame diversi, ma questo è uno strumento molto utile per trovare principalmente i crash
in C ed è molto facile da leggere.
Se guardate solo la parte superiore e non capite molto dello stack delle chiamate, potete guardare
la parte superiore e dire: "Oh, questa è una fonte, questa è una riga di codice che ha causato l'arresto anomalo".
Ecco un bell'esempio che ho trovato sul web.
Excma.
Edu.
Questo illustra gli esempi di stack di chiamate di funzione.
Quindi, se guardiamo un esempio di codice, come vedremo, abbiamo un codice semplice.
I calcoli non hanno molta importanza.
In pratica stiamo solo chiamando alcune funzioni.
Quindi abbiamo questa funzione mogrify e questa funzione truly half e il main chiamerà una funzione
mogrify e poi chiamerà truly half e poi mogrify, quindi possiamo illustrare lo
stack delle chiamate e il loro aspetto impostando un semplice punto di interruzione in main e poi
osservando lo stack delle chiamate mentre procediamo con le chiamate.
Quindi, ancora una volta, acquisiremo maggiore familiarità con i punti di interruzione e con informazioni di questo tipo.
Ma voglio solo dare una rapida dimostrazione dello stack di chiamate, di cui daremo un esempio più
dettagliato in seguito.
Quindi ho impostato un punto di interruzione.
Posso procedere con la compilazione, avviare il debugger e raggiungere il punto di interruzione.
Ora potete vedere tutti i menu del debugger qui a destra, in basso, e vedrete lo
stack delle chiamate.
Rendiamolo un po' più grande.
Ma proprio quando si ferma alla riga 14, qui ha solo alcune istruzioni di assegnazione e vedrete che lo stack
delle chiamate mostra main.
Se ci passiamo sopra e andiamo alla riga successiva, possiamo vedere che arriva alla riga 15 prima
di chiamare Mogrify, giusto?
E quindi lo stack di chiamate rimane con Main.
Se entriamo, vedremo che lo stack delle chiamate mostra Mogrify.
Quindi siamo all'interno di questa funzione.
Possiamo superarlo.
Esegue la prima riga e poi restituisce temp.
Rimane ancora in quella funzione e poi ritorna dove fa la stampa F alla riga 16, vedete come cambia lo stack
delle chiamate.
Passiamo quindi alla riga 18, chiamiamo lo stack still in main ed entriamo nella vera metà della funzione.
Ora si vede la pila delle chiamate cambiare e mostrare che la metà della funzione in cima è sempre quella
che è stata chiamata per ultima.
Di nuovo, facciamo un passo avanti e vediamo che siamo ancora veramente a metà.
Torniamo a Main, stampiamo qualcosa alla riga 19 e passiamo alla riga 21.
Saltiamo di nuovo nella funzione modify, che vedete ancora una volta nello stack delle chiamate, saltiamo, saltiamo e alla
fine stampiamo il risultato finale nella riga 23 e usciamo dal programma.
Ma lo stack di chiamate ci mostra ancora una volta l'ordine delle funzioni che vengono invocate da una funzione all'altra.
Quindi, se si hanno funzioni annidate, può essere sempre più complicato.
Il vantaggio dello stack di chiamate è che se una riga di codice si blocca, ad esempio alla quarta riga, si avrà uno
stack di chiamate che contiene il punto in cui la riga si è bloccata.
In questo modo si può vedere, scorrendo lo stack delle chiamate, quale riga di codice è stata invocata
per ultima e si può capire quale riga di codice si è bloccata.
Ma questo strumento va usato quando il programma si blocca.
Grazie.