Obtenir un graphe de stream de contrôle à partir de code ANSI C

Je construis un outil pour tester des applications ansi c. Il suffit de charger le code, d’afficher le graphe du stream de contrôle, d’exécuter le test et de marquer tous les sumts touchés. J’essaie de créer CFG tout seul à partir de code syntaxique. Malheureusement, le code est nested. GCC donne la possibilité d’obtenir CFG à partir du code compilé. Je pourrais écrire un parsingur syntaxique pour sa sortie, mais j’ai besoin de numéros de ligne pour définir des points d’arrêt. Existe-t-il un moyen d’obtenir des numéros de ligne lors de la sortie du graphique de stream de contrôle avec -fdump-tree-cfg ou -fdump-tree-vcg ?

    Pour le graphe de stream de contrôle d’un programme C, vous pouvez consulter les parsingurs syntaxiques Python existants pour C:

    • PyCParser
    • pycparser
    • pyclibrary (fork de pyclibrary )
    • joern
    • Générateur et parsingur de graphes de stream de contrôle CoFlo C / C ++

    Les graphes d’appel sont une construction étroitement liée au contrôle des graphes de stream. Il existe plusieurs approches pour créer des graphiques d’appel (dépendances de fonctions) pour le code C. Cela pourrait s’avérer utile pour progresser dans la génération de graphiques de stream de contrôle. Façons de créer des graphiques de dépendance en C:

    • Utilisation de cflow :

      • cflow + pycflow2dot + dot (GPL, BSD) cflow est robuste, car il peut gérer du code qui ne peut pas être compilé, par exemple des inclus includes. Si les directives du préprocesseur sont fortement utilisées, il faudra peut-être l’option --cpp pour prétraiter le code.
      • cflow + cflow2dot + point (GPL v2, GPL v3, Licence publique Eclipse v1) (notez que cflow2dot nécessite une correction de chemin avant de fonctionner)
      • cflow + cflow2dot.bash (GPL v2 ,? )
      • cflow + cflow2vcg (GPL v2, GPL v2)
      • cflow amélioré (GPL v2) avec liste pour exclure les symboles du graphique
    • Utilisation de cscope :

      • cscope (BSD)
      • cscope + callgraphviz + dot + xdot
      • cscope + vim CCTree (Explorateur d’arbres d’appel C)
      • cscope + ccglue
      • cscope + CodeQuery pour C, C ++, Python et Java
      • cscope + producteur html python
      • cscope + calltree.sh
    • ncc (comme cflow)

    • KCachegrind (visualiseur de dépendance KDE)
    • Calltree

    Les outils suivants nécessitent malheureusement que le code soit compilable, car ils dépendent de la sortie de gcc:

    • CodeViz (GPL v2) (point faible: nécessite une source compilable, car il utilise gcc pour dumper les fichiers cdepn)
    • gcc + egypte + dot (GPL v *, Perl = licence GPL | Artistic, EPL v1) (l’ egypt utilise gcc pour produire RTL , échoue donc pour tout code source bogué, ou même si vous souhaitez simplement vous concentrer sur un fichier unique Par conséquent, il n’est pas très utile par rapport aux chaînes d’outils plus robustes basées sur cflow . Notez que l’Égypte a par défaut un bon support pour exclure les appels de bibliothèque du graphique, pour le rendre plus propre.

    En outre, les graphiques de dépendance de fichier pour C / C ++ peuvent être créés avec crowfood .

    J’ai donc fait quelques recherches supplémentaires et il n’est pas difficile d’obtenir des numéros de ligne pour les nœuds. Il suffit d’append l’option lineno à l’une de ces options pour l’obtenir. Utilisez donc -fdump-tree-cfg-lineno ou -fdump-tree-vcg-lineno . Il m’a fallu du temps pour vérifier si ces chiffres sont fiables . En cas de graphique au format VCG , l’étiquette de chaque nœud contient deux nombres . Ce sont des numéros de ligne pour la partie de début et de fin de code représentée par ce nœud.

    Méthodes d’parsing dynamic

    Dans cette réponse, je décris quelques méthodes d’parsing dynamic.

    Les méthodes dynamics exécutent réellement le programme pour déterminer le graphe d’appels.

    L’opposé des méthodes dynamics sont les méthodes statiques, qui tentent de le déterminer à partir du source seul sans exécuter le programme.

    Avantages des méthodes dynamics:

    • intercepte les pointeurs de fonction et les appels C ++ virtuels. Celles-ci sont présentes en grand nombre dans tout logiciel non sortingvial.

    Inconvénients des méthodes dynamics:

    • vous devez exécuter le programme, ce qui peut être lent ou nécessiter une configuration que vous n’avez pas, par exemple une compilation croisée
    • seules les fonctions réellement appelées apparaîtront. Par exemple, certaines fonctions peuvent être appelées ou non en fonction des arguments de la ligne de commande.

    KcacheGrind

    https://kcachegrind.github.io/html/Home.html

    Programme de test:

     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; } 

    Usage:

     sudo apt-get install -y kcachegrind valgrind # Comstack the program as usual, no special flags. gcc -ggdb3 -O0 -o main -std=c99 main.c # Generate a callgrind.out. file. valgrind --tool=callgrind ./main # Open a GUI tool to visualize callgrind data. kcachegrind callgrind.out.1234 

    Vous vous retrouvez maintenant dans un programme graphique impressionnant contenant de nombreuses données de performances intéressantes.

    En bas à droite, sélectionnez l’onglet “Graphique des appels”. Cela montre un graphe d’appels interactif qui est corrélé aux mésortingques de performance dans d’autres fenêtres lorsque vous cliquez sur les fonctions.

    Pour exporter le graphique, cliquez dessus avec le bouton droit de la souris et sélectionnez “Exporter le graphique”. Le fichier PNG exporté ressemble à ceci:

    À partir de cela, nous pouvons voir que:

    • le noeud racine est _start , qui est le point d’entrée ELF actuel et contient le passe-partout d’initialisation de glibc
    • f0 , f1 et f2 sont appelés comme prévu les uns des autres
    • pointed est également montré, même si nous l’avions appelé avec un pointeur de fonction. Il n’aurait peut-être pas été appelé si nous avions passé un argument de ligne de commande.
    • not_called n’est pas affiché car il n’a pas été appelé lors de l’exécution, car nous n’avons pas passé d’argument de ligne de commande supplémentaire.

    Le point valgrind de valgrind est qu’il ne nécessite aucune option de compilation particulière.

    Par conséquent, vous pouvez l’utiliser même si vous n’avez pas le code source, seulement l’exécutable.

    valgrind parvient en exécutant votre code via une “machine virtuelle” légère.

    Testé sur Ubuntu 18.04.

    gcc -finstrument-functions + etrace

    https://github.com/elcritch/etrace

    -finstrument-functions ajoute des rappels , etrace parsing le fichier ELF et implémente tous les rappels.

    Malheureusement, je n’ai pas réussi à le faire fonctionner: Pourquoi «fonctions -finstrument -`» ne fonctionne-t-il pas pour moi?

    La sortie revendiquée est de format:

     \-- 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 

    Probablement la méthode la plus efficace en plus du support de traçage matériel spécifique, mais elle a l’inconvénient de recomstackr le code.