Metodi di analisi dinamica
Qui descrivo alcuni metodi di analisi dinamica.
I metodi dinamici eseguono effettivamente il programma per determinare il grafico delle chiamate.
L'opposto dei metodi dinamici sono metodi statici, che tentano di determinarlo solo dall'origine senza eseguire il programma.
Vantaggi dei metodi dinamici:
- cattura i puntatori a funzioni e le chiamate virtuali C ++. Questi sono presenti in gran numero in qualsiasi software non banale.
Svantaggi dei metodi dinamici:
- devi eseguire il programma, che potrebbe essere lento o richiedere una configurazione che non hai, ad esempio la compilazione incrociata
- verranno visualizzate solo le funzioni che sono state effettivamente chiamate. Ad esempio, alcune funzioni potrebbero essere chiamate o meno a seconda degli argomenti della riga di comando.
KCachegrind
https://kcachegrind.github.io/html/Home.html
Programma di prova:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
Uso:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
Ora sei lasciato all'interno di un fantastico programma GUI che contiene molti dati interessanti sulle prestazioni.
In basso a destra, seleziona la scheda "Grafico chiamate". Questo mostra un grafico di chiamata interattivo correlato alle metriche delle prestazioni in altre finestre mentre fai clic sulle funzioni.
Per esportare il grafico, fare clic con il pulsante destro del mouse e selezionare "Esporta grafico". Il PNG esportato ha questo aspetto:
Da ciò possiamo vedere che:
- il nodo radice è
_start
, che è l'effettivo punto di ingresso ELF, e contiene boilerplate di inizializzazione glibc
f0
, f1
E f2
sono chiamati come previsto l'uno dall'altro
pointed
viene mostrato anche, anche se l'abbiamo chiamato con un puntatore a funzione. Potrebbe non essere stato chiamato se avessimo passato un argomento della riga di comando.
not_called
non viene mostrato perché non è stato chiamato durante l'esecuzione, perché non abbiamo passato un argomento aggiuntivo della riga di comando.
La cosa interessante valgrind
è che non richiede alcuna speciale opzione di compilazione.
Pertanto, potresti usarlo anche se non hai il codice sorgente, solo l'eseguibile.
valgrind
riesce a farlo eseguendo il codice attraverso una "macchina virtuale" leggera. Ciò rende anche l'esecuzione estremamente lenta rispetto all'esecuzione nativa.
Come si può vedere nel grafico, si ottengono anche informazioni sulla temporizzazione su ciascuna chiamata di funzione, che possono essere utilizzate per profilare il programma, che è probabilmente il caso d'uso originale di questa configurazione, non solo per vedere i grafici delle chiamate: Come posso profilare Codice C ++ in esecuzione su Linux?
Testato su Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
aggiunge callback , etrace analizza il file ELF e implementa tutti i callback.
Purtroppo non sono riuscito a farlo funzionare: Perché `-finstrument-functions` non funziona per me?
L'output dichiarato è nel formato:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
Probabilmente il metodo più efficiente oltre al supporto di tracciamento hardware specifico, ma ha lo svantaggio di dover ricompilare il codice.