Métodos de análisis dinámico
Aquí describo algunos métodos de análisis dinámico.
Los métodos dinámicos realmente ejecutan el programa para determinar el gráfico de llamadas.
Lo contrario de los métodos dinámicos son los métodos estáticos, que intentan determinarlo solo desde la fuente sin ejecutar el programa.
Ventajas de los métodos dinámicos:
- captura punteros de función y llamadas virtuales de C ++. Estos están presentes en grandes cantidades en cualquier software no trivial.
Desventajas de los métodos dinámicos:
- tienes que ejecutar el programa, que puede ser lento o requerir una configuración que no tienes, por ejemplo, compilación cruzada
- solo se mostrarán las funciones que fueron realmente llamadas. Por ejemplo, algunas funciones podrían llamarse o no dependiendo de los argumentos de la línea de comandos.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Programa de prueba:
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
Ahora se encuentra dentro de un increíble programa GUI que contiene una gran cantidad de datos de rendimiento interesantes.
En la parte inferior derecha, seleccione la pestaña "Gráfico de llamadas". Esto muestra un gráfico de llamadas interactivo que se correlaciona con las métricas de rendimiento en otras ventanas a medida que hace clic en las funciones.
Para exportar el gráfico, haga clic derecho y seleccione "Exportar gráfico". El PNG exportado tiene este aspecto:
De eso podemos ver que:
- el nodo raíz es
_start
, que es el punto de entrada de ELF real, y contiene el texto estándar de inicialización de glibc
f0
, f1
y f2
se llaman como se esperaba el uno del otro
pointed
también se muestra, aunque lo llamamos con un puntero de función. Es posible que no se haya llamado si hubiéramos pasado un argumento de línea de comando.
not_called
no se muestra porque no se llamó en la ejecución, porque no pasamos un argumento de línea de comando adicional.
Lo bueno valgrind
es que no requiere ninguna opción de compilación especial.
Por lo tanto, puede usarlo incluso si no tiene el código fuente, solo el ejecutable.
valgrind
logra hacerlo ejecutando su código a través de una "máquina virtual" ligera. Esto también hace que la ejecución sea extremadamente lenta en comparación con la ejecución nativa.
Como se puede ver en el gráfico, también se obtiene información de tiempo sobre cada llamada de función, y esto se puede usar para perfilar el programa, que probablemente sea el caso de uso original de esta configuración, no solo para ver gráficos de llamadas: ¿Cómo puedo perfilar ¿El código C ++ se ejecuta en Linux?
Probado en Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
agrega devoluciones de llamada , etrace analiza el archivo ELF e implementa todas las devoluciones de llamada.
Sin embargo, desafortunadamente no pude hacerlo funcionar: ¿Por qué `-finstrument-functions` no funciona para mí?
La salida reclamada tiene el 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
Probablemente sea el método más eficiente además del soporte de rastreo de hardware específico, pero tiene la desventaja de que debe volver a compilar el código.