2010-01-25 4 views
4

Nous utilisons C pour construire un système sur un noyau ARM (c'est-à-dire un système embarqué). La question est: comment pouvons-nous éviter le problème de la rentrée de manière formelle afin que nous soyons sûrs que tous les bugs de rentrée sont supprimés. Ce n'est peut-être pas un souhait concret mais sûrement important pour tout système, je suppose. Juste pour la discussion, je suppose que dessiner un diagramme UML ou avoir une machine à états complète serait un bon début (mais comment le générer APRÈS que tout le système ait été développé?). Des suggestions sur la façon d'utiliser le diagramme de machine d'état/UML pour faire l'analyse?Une manière systématique d'éviter les problèmes de "rentrée"? (système embarqué)

Répondre

4

Je ne suis pas vraiment sûr du problème que vous voulez résoudre, mais permettez-moi de faire une supposition éclairée.

Le premier point consiste à identifier les fonctions qui pourraient poser problème. Une ré-entrée se produit soit par des appels récursifs, qui peuvent passer par plusieurs appels imbriqués et même être masqués par des rappels/injection de dépendance, ou par des fonctions qui sont utilisées dans plusieurs threads.

Vous pouvez dessiner un graphique d'appel dirigé. Dites que la fonction A appelle B et C, la fonction B appelle D, E et F, la fonction C n'appelle rien, et ainsi de suite. Dessinez ceci pour chaque thread lors du multithreading. S'il y a des cycles dans le graphique, alors toutes les fonctions faisant ce cycle doivent être sécurisées. Vous pouvez ignorer les sous-branches dans ce cas. Les fonctions utilisées dans plusieurs threads doivent également être sécurisées, mais en incluant toutes les sous-branches, car vous ne savez pas exactement où se trouve chaque thread. Les choses deviendront complexes et compliquées quand les verrous sont utilisés, alors laissez-nous ignorer cela pour le moment.

Cette étape peut sûrement être automatisée par des outils d'analyse de code.

Maintenant que les fonctions sont identifiées,

  • Les fonctions qui ne dépendent que de leurs données de fonction locale sont généralement sans danger. C'est l'une des belles propriétés de la programmation fonctionnelle.
  • Les fonctions qui dépendent de données externes (pas dans la portée de la fonction) doivent être examinées de près, il est particulièrement important de savoir quand et où les données externes sont modifiées.
  • Les fonctions qui modifient les données externes doivent déclencher un drapeau rouge et activer une sirène d'alarme forte, en particulier en multithreading.
+1

Quand je fais ce genre d'examen de code, le premier endroit où je regarde est mes globals. Les fonctions qui accèdent/manipulent directement les variables globales sont généralement les fonctions ayant le plus de problèmes de ré-entrée. Comme mentionné précédemment, les fonctions qui modifient uniquement les arguments de fonction ou les données internes sont généralement OK. N'oubliez pas de vérifier que toutes les fonctions de bibliothèque que vous appelez (y compris les fonctions de bibliothèque standard) sont compatibles avec les threads! – bta

+0

J'ai compris. Merci Secure & bta pour le conseil. –

+0

"masqué par des rappels/injection de dépendance" ou des gestionnaires de signal, juste pour l'exhaustivité. –

4

Quick Fix, si vous pensez que quelque chose:

int some_func(int x, int y) 
{ 
    static volatile int do_not_enter_twice = 0; 
    assert(!(do_not_enter_twice++)); 

    /* some_func continued */ 

    do_not_enter_twice--; 
    return whatever; 
} 

réponse plus longue:
Utilisez un autre outil pour faire un call graph et continuer manuellement à partir de là.

+0

Oh! Je vois. Nous avons déjà créé un graphique d'appel mais nous n'avons pas réussi à l'utiliser pour la résolution de problèmes. Et oui, nous avons fait la vérification de la rentrée, mais je ne sais pas quelle fonction devrait passer par cette vérification (maintenant je sais). En passant, y a-t-il un outil d'aide pour analyser le graphe d'appel puisque le nôtre est énorme ... –

+0

Voir la réponse DMS pour calculer d'énormes graphiques d'appels. –

0

Je ferais 2 choses pour vérifier votre code. Révision approfondie du code de groupe (dans le but de trouver des erreurs de réentrance seulement, pas de style ou d'autres erreurs). Deuxièmement, une attaque pratique sur les problèmes.

Par exemple:

int myfunction(int x, int y) { 
    REENTRANCE_CHECK; 
    ... body of function 
} 

Maintenant vous pouvez #Define REENTRANCE_CHECK être soit vide (pour la production) ou un code qui vérifie la fonction est de nouveau entré jamais. Exécutez vos tests (si vous n'avez pas de tests, puis exécutez-le sur votre appareil avec le débogueur joint) avec ces contrôles activés, et voir si quelque chose tombe. De même, vous pouvez ajouter une logique de débogage pour détecter les mises à jour incorrectes de l'état global.Écrire un code qui utilise des verrous (qui affirment si elles sont acquises lorsque déjà tenu

Quelque chose comme ceci:.

int my_global; 
DEFINE_GLOBAL_LOCK(my_global); 

void my_dangerous_function() { 
    ... 
    LOCK_GLOBAL(my_global); 
    .. some critical section of code that uses my_global. 
    UNLOCK_GLOBAL(my_global); 
    ... 
} 

Encore une fois, DECLARE_GLOBAL_LOCK, LOCK_GLOBAL et UNLOCK_GLOBAL peuvent être soit #defined être réel code de verrouillage (ce qui bien sûr vous devrez écrire) pour les tests, et pour la production, ils peuvent être # définis à rien

Cette approche ne fonctionne que si vous trouvez et envelopper tous les accès à votre état global, mais c'est facile avec une recherche

2

Un outil qui peut calculer énormes graphiques d'appel est le DMS Software Reengineering Toolkit et son frontal C. Le frontal C est utilisé pour analyser le code C; DMS a construit des machines pour calculer l'analyse de contrôle et de flux de données, analyser les points et analyser les faits call-direct et call-indirect-thru-pointer

DMS a été utilisé pour construire des graphes d'appel pour les systèmes de code source C de 35 millions lignes de code C (= 250 000 fonctions), puis d'extraire des informations à partir de ce graphique d'appel. Un problème clé lors de la construction de grands graphiques comme celui-ci est de calculer des points aussi précisément que possible (il y a des limites théoriques strictes à cela) afin que les appels de fonction indirects soient ciblés sur un nombre minimal de cibles faussement positives.

Dans votre cas, l'information à extraire est, comme d'autres auteurs l'indiquent, "y at-il un cycle?" dans ce graphique d'appel.

À cette échelle, vous ne voulez pas le faire à la main, et vous devez le refaire chaque fois que vous êtes prêt pour une version de production. Donc mécaniser le contrôle aurait beaucoup de sens.

Questions connexes