2011-03-21 21 views
64

Estoy tratando de generar un gráfico de llamadas con el que averiguar todas las posibles rutas de ejecución que están golpeando una función particular (para que no tenga que descubrir todas las rutas manualmente, como hay muchos caminos que conducen a esta función). Por ejemplo:Generar gráfico de llamadas para el código de C++

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: ... 

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

¿Alguien sabe cómo lograr esto?

Respuesta

95
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 

reduce un poco la imagen brillante (hay un "nodo externo", porque main tiene vinculación externa y puede ser llamado desde fuera que la unidad de traducción también):

Callgraph

Es posible que desee posprocesar esto con c++filt, de modo que pueda obtener los nombres no marcados de th Funciones y clases involucradas. Al igual que 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  

Rendimientos esta belleza (¡oh, el tamaño sin optimizaciones encendido era demasiado grande!)

Beauty

Esa función sin nombre mística, Node0x884c4e0, es un marcador de posición asumida a ser llamado por cualquier función cuya definición no se conozca.

+17

¿Ha hecho esto en un proyecto de archivo de varias? se ve muy bien como una herramienta – dirvine

+0

¿Hay alguna manera de hacerlo para que no se llamen las funciones que no son locales al archivo/archivos como todas las funciones estándar que se llaman entre sí? – soandos

+2

+1 Por alguna razón tuve que pasar la opción -n a C++ filt para que los nombres se deshaga. Pensé que lo mencionaría aquí en caso de que alguien más tenga el mismo problema. – Aky

3

El cálculo estático de un gráfico preciso de llamadas de C++ es difícil, porque necesita un analizador de lenguaje preciso, búsqueda correcta de nombres y un buen analizador de puntos que respete la semántica del lenguaje correctamente. Doxygen no tiene ninguno de estos, no sé por qué las personas dicen que les gusta para C++; es fácil construir un ejemplo de C++ de 10 líneas que Doxygen analiza erróneamente).

Es mejor que ejecute un timing profiler which collects a call graph dynamically (esto describe el nuestro) y simplemente ejercite una gran cantidad de casos. Tales perfiladores le mostrarán el gráfico de llamada real ejercido.

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

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

+0

Desafortunadamente no conozco todos los casos de uso que pueden desencadenar esa función :(. De hecho, mi objetivo final es averiguar la lista exacta de casos de uso que utilizan esa función para fines de depuración.Puedo averiguar las personas que llaman directamente con la herramienta de indexación de código, pero necesito descubrir todas las rutas de ejecución para un análisis posterior. – shiouming

3

Para que el comando clang++ para encontrar los archivos de cabecera estándar como mpi.h dos opciones adicionales deben utilizarse -### -fsyntax-only, es decir, el comando completo debería verse como:

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

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

11

Puede lograr eso mediante el uso de doxygen (con la opción de utilizar el punto para la generación de gráficos).

enter image description here

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

enter image description here

doxygen/punto son probablemente más fácil de sonido metálico/opt para instalar y ejecutar. No logré instalarlo yo mismo y es por eso que traté de encontrar una solución alternativa.

+1

¿Podría agregar un ejemplo de cómo ejecutar doxygen para obtener la ventana que incluyó? –

+0

@nimble_ninja: ¿No es suficiente la captura de pantalla del diálogo de configuración de doxywizard? – jpo38

+0

No sabía que era de doxywizard. ¡Gracias! –

3

Puede utilizar CppDepend, puede genera muchos tipos de gráficos

  • gráfico de dependencias
  • gráfico de llamadas
  • clase de herencia Gráfico
  • acoplamiento Gráfico
  • Ruta Gráfico
  • Todas las trayectorias Gráfico
  • Gráfico de ciclo

enter image description here

Cuestiones relacionadas