Cómo generar un gráfico de llamada para código C ++

87

Estoy tratando de generar un gráfico de llamada con el que averiguar todas las posibles rutas de ejecución que están afectando a una función en particular (para no tener que averiguar todas las rutas manualmente, ya que hay muchas rutas que conducen a esta función ). Por ejemplo:

path 1: A -> B -> C -> D  
path 2: A -> B -> X -> Y -> D  
path 3: A -> G -> M -> N -> O -> P -> S -> D  
...  
path n: ...

Probé Codeviz y Doxygen, de alguna manera ambos resultados no muestran nada más que llamadas de la función de destino, D. En mi caso, D es una función miembro de una clase cuyo objeto será envuelto dentro de un puntero inteligente. Los clientes siempre obtendrán el objeto de puntero inteligente a través de una fábrica para invocar D.

¿Alguien sabe como lograr esto?

shiouming
fuente

Respuestas:

118
static void D() { }
static void Y() { D(); }
static void X() { Y(); }
static void C() { D(); X(); }
static void B() { C(); }
static void S() { D(); }
static void P() { S(); }
static void O() { P(); }
static void N() { O(); }
static void M() { N(); }
static void G() { M(); }
static void A() { B(); G(); }

int main() {
  A();
}

Entonces

$ clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
$ dot -Tpng -ocallgraph.png callgraph.dot

Produce una imagen brillante (hay un "nodo externo", porque maintiene un enlace externo y también se puede llamar desde fuera de esa unidad de traducción):

Callgraph

Es posible que desee posprocesar esto con c++filt, para que pueda obtener los nombres sin alterar de las funciones y clases involucradas. Como en el siguiente

#include <vector>

struct A { 
  A(int);
  void f(); // not defined, prevents inlining it!
};

int main() {
  std::vector<A> v;
  v.push_back(42);
  v[0].f();
}

$ clang++ -S -emit-llvm main1.cpp -o - |
   opt -analyze -std-link-opts -dot-callgraph
$ cat callgraph.dot | 
   c++filt | 
   sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' | 
   gawk '/external node/{id=$1} $1 != id' | 
   dot -Tpng -ocallgraph.png    

Produce esta belleza (¡oh, el tamaño sin las optimizaciones activadas era demasiado grande!)

Belleza

Esa función mística sin nombre,, Node0x884c4e0es un marcador de posición que se supone que es llamado por cualquier función cuya definición no se conoce.

Johannes Schaub - litb
fuente
22
¿Ha hecho esto en un proyecto de varios archivos? se ve muy bien como herramienta
dirvine
2
+1 Por alguna razón, tuve que pasar la opción -n a c ++ filt para que los nombres se deshagan. Pensé en mencionarlo aquí en caso de que alguien más se enfrente al mismo problema.
Aky
1
Me sale un error al intentar esto: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!¿Qué pasa con eso? clang 3.8
Arne
2
Lo encontré: tengo que eliminar la -analyzeopción por alguna razón. Otra pregunta: ¿puedo establecer el nombre del archivo de salida en algo diferente a ./callgraph.dot?
Arne
2
La segunda pregunta que tengo, ¿cómo ejecutar este comando para varios archivos en diferentes directorios?
Novato
18

Puede lograrlo usando doxygen (con la opción de usar dot para la generación de gráficos).

ingrese la descripción de la imagen aquí

Con Johannes Schaub - litb main.cpp, genera esto:

ingrese la descripción de la imagen aquí

doxygen / dot son probablemente más fáciles de instalar y ejecutar que clang / opt. ¡No logré instalarlo yo mismo y por eso traté de encontrar una solución alternativa!

jpo38
fuente
1
¿Podría agregar un ejemplo de cómo ejecutar doxygen para obtener la ventana que incluyó?
nimble_ninja
@nimble_ninja: ¿No es suficiente la captura de pantalla del diálogo de configuración de doxywizard?
jpo38
1
No sabía que era de Doxywizard. ¡Gracias!
nimble_ninja
1
¡El mejor método de todos! :)
Leslie N
No es realmente viable para un proyecto grande, se ejecutó durante 24 horas, gigabytes de documentación HTML, todavía no está terminado ... omitiendo este. Solo necesito gráficos de llamadas para algunas funciones específicas (el árbol completo hacia / desde / entre main () <=> SQL_COMMIT ()).
Gizmo
9

Calcular estáticamente un gráfico de llamadas C ++ preciso es difícil, porque necesita un analizador de idiomas preciso, una búsqueda de nombres correcta y un buen analizador de puntos que respete la semántica del lenguaje correctamente. Doxygen no tiene ninguno de estos, no sé por qué la gente dice que le gusta C ++; es fácil construir un ejemplo de C ++ de 10 líneas que Doxygen analiza erróneamente).

Es mejor que ejecute un generador de perfiles de tiempo que recopile un gráfico de llamadas de forma dinámica (esto describe el nuestro) y simplemente ejercite muchos casos. Dichos perfiladores le mostrarán el gráfico de llamadas real ejercitado.

EDITAR: De repente recordé Entender para C ++ , que afirma construir gráficos de llamadas. No sé qué usan para un analizador, o si hacen bien el análisis detallado; No tengo experiencia específica con su producto.

Estoy impresionado por la respuesta de Schaub, usando Clang; Esperaría que Clang tenga todos los elementos correctos.

Ira Baxter
fuente
Desafortunadamente, no conozco todos los casos de uso que pueden activar esa función :(. De hecho, mi objetivo final es averiguar la lista exacta de casos de uso que utilizan esa función con fines de depuración. Puedo averiguarlo las personas que llaman directos con la herramienta de códigos de indexación, pero hay que averiguar todas las rutas de ejecución para su posterior análisis.
shiouming
Entonces, ¿lo que realmente desea es la condición de ejecución bajo la cual se llama a un método? Luego, necesita un gráfico de llamadas completo y preciso y la capacidad de una herramienta para caminar a lo largo del flujo de control en varios nodos del gráfico de llamadas, recopilando expresiones condicionales, hasta que se encuentra el método deseado. No conozco ninguna herramienta disponible que haga esto (este comentario 7 años después de la pregunta); es probable que necesite un motor de análisis personalizado para hacer esto. Clang podría verse presionado a esto; nuestro kit de herramientas DMS podría usarse para esto.
Ira Baxter
5

Puede usar CppDepend , puede generar muchos tipos de gráficos

  • Gráfico de dependencia
  • Gráfico de llamadas
  • Gráfico de herencia de clases
  • Gráfico de acoplamiento
  • Gráfico de ruta
  • Gráfico de todas las rutas
  • Gráfico de ciclo

ingrese la descripción de la imagen aquí

Issam
fuente
3

Para que el clang++comando encuentre archivos de encabezado estándar mpi.h, se deben usar dos opciones adicionales -### -fsyntax-only, es decir, el comando completo debe verse como:

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
mabalenk
fuente
1

El "Analizador de C ++ Bsc" puede mostrar gráficos de llamadas - leyendo el archivo generado por la utilidad bscmake.

Resonantium
fuente
0

doxygen + graphviz podría resolver la mayoría de los problemas cuando queremos generar un gráfico de llamadas, luego entregado a la mano de obra.

Gatear.W
fuente
0

Scitools Understand es una herramienta fantástica , mejor que todo lo que conozco para la ingeniería inversa , y genera gráficos de alta calidad .

Pero tenga en cuenta que es bastante caro y que la versión de prueba tiene su gráfico de llamada de mariposa limitado a solo un nivel de llamada (en mi humilde opinión, creo que no se ayudan a sí mismos al hacerlo ...)

franckspike
fuente