web-dev-qa-db-fra.com

Comment déterminer l'utilisation maximale de la pile dans un système embarqué avec GCC?

J'écris le code de démarrage pour un système intégré - le code qui charge le pointeur de pile initial avant de sauter sur la fonction principale () - et j'ai besoin de le dire combien d'octets d'empilement à ma application utilisera (ou plus , estimation conservatrice).

On m'a dit que le compilateur de la GCC a maintenant une option d'utilisation de Fstetack et -fCallGraph-Info Option pouvant être utilisée de manière significative pour calculer statiquement la "utilisation maximale de la pile maximale". ( "Analyse des exigences de pile de compilée avec GCC" par Botcazou, Comar et Hainque).

Nigel Jones dit que la récursivité est une très mauvaise idée dans les systèmes embarqués ("calculant la taille de votre pile" 2009), donc j'ai veillé à ne pas faire de fonctions mutuellement récursives dans ce code.

En outre, je m'assure que aucun de mes manutentionnaires d'interruption n'a jamais réactivé les interruptions avant leur instruction finale de retour d'interruption, donc je n'ai pas à vous soucier de vous inquiéter des gestionnaires d'interruption de ré-entrant.

Sans réursion ou ré-entrant interrompt les gestionnaires, il devrait déterminer statiquement l'utilisation maximale de la pile. (Et donc la plupart des réponses à Comment déterminer l'utilisation maximale de la pile? Ne pas appliquer). Ma compréhension est i (ou de préférence, un peu de code sur mon PC qui s'exécute automatiquement à chaque fois que je reconstruit l'exécutable) trouvez d'abord la profondeur de pile maximale pour chaque gestionnaire d'interruption lorsqu'il n'est pas interrompu par une interruption de priorité supérieure et le maximum La profondeur de la pile de la fonction principale () lorsqu'elle n'est pas interrompue. Ensuite, je les ajoute tout pour trouver la profondeur totale de pile maximale (pire des cas). Qui se produit (dans mon système intégré) lorsque la tâche principale () est à sa profondeur maximale lorsqu'elle est interrompue par l'interruption de priorité la plus basse et que l'interruption est à sa profondeur maximale lorsqu'elle est interrompue par la priorité suivante interrompre, et ainsi de suite.

J'utilise Yagarto avec GCC 4.6.0 pour compiler le code de la LM3S1968 ARM cortex-m3.

Comment puis-je utiliser l'option -fstack-usage et l'option -fCallGraph-info avec GCC pour calculer la profondeur maximale de la pile? Ou existe-t-il une meilleure approche pour déterminer l'utilisation maximale de la pile?

(Voir Comment déterminer l'utilisation maximale de la pile dans un système embarqué? Pour presque la même question ciblée au compilateur de Keil.)

41
David Cary

GCC Docs:

-fstack-usage

Rend les informations d'utilisation de la pile de sortie du compilateur pour le programme, sur une base de fonction. Le nom de fichier pour la décharge est fabriqué en ajoutant .su à l'AuxName. AuxName est généré à partir du nom du fichier de sortie, si spécifié explicitement et qu'il n'est pas exécutable, sinon c'est le nom de base du fichier source. Une entrée est composée de trois champs:

  • Le nom de la fonction.
  • Un certain nombre d'octets.
  • Un ou plusieurs qualificateurs: statique, dynamique, bornée.

Le qualificatif statique signifie que la fonction manipule la pile de manière statique: un nombre fixe d'octets est attribué à la saisie sur la saisie de la fonction et publié sur la sortie de la fonction; Aucun ajustement de la pile n'est autrement effectué dans la fonction. Le deuxième champ est ce nombre fixe d'octets.

La dynamique qualificatif signifie que la fonction manipule la pile de manière dynamique: en plus de l'allocation statique décrite ci-dessus, des ajustements de la pile sont effectués dans le corps de la fonction, par exemple pour appuyer sur les appels de fonctions. Si le qualificatif borné est également présent, la quantité de ces ajustements est délimitée au moment de la compilation et le deuxième champ est une limite supérieure de la quantité totale de pile utilisée par la fonction. S'il n'est pas présent, la quantité de ces ajustements n'est pas limitée au moment de la compilation et le deuxième champ ne représente que la partie délimitée.

Je ne trouve aucune références à -fcallgraph-info

Vous pouvez potentiellement créer les informations dont vous avez besoin de -fstack-usage et -FDump-Tree-Optimisée

Pour chaque feuille dans -fdump-Tree-Optimisée, obtenez ses parents et résumez leur numéro de taille de la pile (gardant à l'esprit que ce nombre réside pour n'importe quelle fonction avec "dynamique" mais non "bornée") de -fstack-usage, trouvez le maximum de ces valeurs et cela devrait être votre utilisation maximale de la pile.

21
τεκ

Juste au cas où personne ne propose une meilleure réponse, je posterai ce que j'avais dans le commentaire de votre autre question, même si je n'ai aucune expérience en utilisant ces options et outils:

GCC 4.6 ajoute le -fstack-usage Option qui donne les statistiques d'utilisation de la pile sur une base de fonction par fonction.

Si vous combinez ces informations avec un graphe d'appels produites par CFLOW ou un outil similaire, vous pouvez obtenir le type d'analyse de profondeur de pile que vous recherchez (un script pourrait probablement être écrit assez facilement pour le faire) . Demandez au script lire les informations sur la pile-usage et chargez une carte des noms de fonction avec la pile utilisée par la fonction. Demandez ensuite au script Walk the cflow graphique (qui peut être un arborescence de texte facile à analyser), ajoutant l'utilisation de la pile associée à chaque ligne pour chaque branche du graphique d'appel.

Donc, on dirait que cela peut être fait avec GCC, mais vous devrez peut-être paguer ensemble le bon ensemble d'outils.

12
Michael Burr

Assez tard, mais pour que quiconque en regardant cela, les réponses données concernant la combinaison de la combinaison des sorties de Fstack-usage et des outils de graphes d'appel tels que CFLOW peuvent finissent par être extrêmement incorrectes pour toute allocation dynamique, même bornée, car il n'y a pas d'informations sur cette pile dynamique l'allocation se produit. Il n'est donc pas possible de savoir quelles fonctions vous devriez appliquer la valeur vers. À titre d'exemple artificiel, si (simplifié) la sortie de la fstack-usage est:

main        1024     dynamic,bounded
functionA    512     static
functionB     16     static

et un appel très simple est:

main
    functionA
    functionB

L'approche naïve de combiner ces éléments peut entraîner une choisi pour la main-d'œuvre de la pile maximale, à 1536 octets. Mais, si la plus grande allocation de pile dynamique sur la principale () consiste à appuyer sur une grande argument comme un enregistrement sur la fonction de fonctionnement () directement sur la pile dans un bloc conditionnel qui appelle la fonctionb (j'ai déjà dit cela a été conçu), alors vraiment principal -> La fonction est la voie de l'utilisation maximale de la pile, à 1040 octets. Selon la conception logicielle existante, et aussi pour d'autres cibles plus restreintes qui transmettent tout sur la pile, des erreurs cumulatives peuvent rapidement vous conduire à regarder des chemins entièrement erronés réclamant de manière significative des tailles de pile maximales.

De plus, en fonction de votre classification de "réentrant" lorsque vous parlez d'interruptions, il est possible de manquer de manière entièrement des allocations de pile. Par exemple, de nombreux interruptions de niveau 7 de niveau 7 de processeurs Coldfire sont sensibles au bord et ignorent donc le masque d'interruption désactivé, de sorte que si un sémaphore est utilisé pour laisser l'instruction tôt, vous ne le considérerez peut-être pas, mais l'allocation de pile initiale sera toujours auparavant. Le sémaphore est vérifié.

En bref, vous devez faire très attention à l'utilisation de cette approche.

6
Adam Palaniuk

J'ai fini par écrire A python script pour mettre en œuvre τεκ's Réponse . C'est trop de code pour publier ici, mais peut être trouvé sur Github

6
PeterM

Je ne connais pas le -fstack-usage et -fcallgraph-info options. Cependant, il est toujours possible de déterminer l'utilisation réelle des piles par:

  1. Allouer un espace de pile adéquat (pour cette expérience) et l'initialiser à quelque chose de facilement identifiable. J'apprécie 0xee.
  2. Exécutez l'application et testez tous ses chemins internes (par toutes les combinaisons d'entrée et de paramètres). Laissez-le courir pendant plus que "assez longtemps".
  3. Examinez la zone de pile et voyez la quantité de pile utilisée.
  4. Faites que la taille de la pile, plus 10% ou 20% pour tolérer des mises à jour logicielles et des conditions rares.
3
wallyk