Comment déterminer l'utilisation maximale de la pile dans un système intégré avec gcc?

j'écris le code de démarrage d'un système intégré -- le code qui charge le pointeur de pile initial avant de passer à la fonction main () -- et je dois lui dire combien d'octets de pile mon application utilisera (ou une estimation plus grande et conservatrice).

on m'a dit que le compilateur gcc a maintenant une option-fstack-usage et-fcallgraph-info qui peut être utilisée d'une façon ou d'une autre pour calculer statiquement l'exacte" utilisation maximale de la pile " pour moi. ( "Au moment de la compilation de la pile d'analyse des besoins avec GCC" par Botcazou, Comar, et Hainque ).

Nigel Jones dit que la récursion est une très mauvaise idée dans les systèmes embarqués ("Computing your stack size" 2009), donc j'ai pris soin de ne pas faire de fonctions mutuellement récursives dans ce code.

de plus, je m'assure qu'aucun de mes gestionnaires d'interruptions ne réinitialise jamais les interruptions jusqu'à leur dernier retour de l'instruction d'interruption, donc je n'ai pas besoin de occupez-vous des nouveaux maîtres de l'interruption.

sans les manipulateurs d'interruption de rentrée ou de récursion, il devrait être possible de 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 s'applique pas). Ma compréhension est I (ou de préférence, un peu de code sur mon PC qui est exécuté automatiquement chaque fois que je reconstruis l'exécutable) d'abord trouver la profondeur maximale de pile pour chaque gestionnaire d'interruption quand il n'est pas interrompu par une interruption de priorité supérieure, et la profondeur maximale de la pile de la fonction main() lorsqu'elle n'est pas interrompue. Puis je les additionne tous pour trouver la profondeur maximale totale (dans le pire des cas) de la pile. Cela se produit (dans mon système embarqué) lorsque la tâche principale () background est à sa profondeur maximale lorsqu'elle est interrompue par l'interruption de priorité la plus basse, et que cette interruption est à sa profondeur maximale lorsqu'elle est interrompue par l'interruption de priorité la plus basse, et ainsi de suite.

J'utilise YAGARTO avec gcc 4.6.0 pour compiler le code du Cortex ARM LM3S1968-M3.

alors comment utiliser l'option-fstack-usage et-fcallgraph-info avec gcc pour calculer la profondeur maximale de la pile? Ou y a-t-il une meilleure approche pour déterminer l'utilisation maximale des cheminées?

(voir ) Comment déterminer l'utilisation maximale de la pile dans un système intégré? pour presque la même question destinée au compilateur Keil .)

31
demandé sur Community 2011-06-17 18:51:09

5 réponses

GCC docs:

-fstack-l'utilisation de la

rend l'information d'utilisation de sortie de pile de compilateur pour le programme, sur une base de par fonction. Le nom du fichier pour le dump est fait en ajoutant .su à l'auxname. auxname est généré à partir du nom du fichier de sortie, si explicitement indiqué et qu'il n'est pas un 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 qualificatifs: statique, dynamique, borné.

le qualificatif statique signifie que la fonction manipule la pile de façon statique: un nombre fixe d'octets est alloué pour la trame à l'entrée de la fonction et libéré à la sortie de la fonction; Aucun ajustement de la pile n'est effectué dans la fonction. Le deuxième champ est ce nombre fixe d'octets.

le qualificatif dynamique signifie que la fonction manipule dynamiquement la pile: 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 pousser/pop arguments autour des appels de fonction. Si le qualificatif délimitée est également présent, le montant de ces ajustements est délimitée au moment de la compilation et le deuxième champ est la limite supérieure de la quantité totale de pile utilisé par la fonction. Si elle n'est pas présente, le le montant de ces rajustements n'est pas limité 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érence à-fcallgraph-info

vous pourriez potentiellement créer l'information dont vous avez besoin à partir de-fstack-usage et-fdump-tree-optimized

pour chaque feuille in-fdump-arbre-optimisé, obtenir ses parents et la somme de leur nombre de taille de pile (en gardant à l'esprit que ce nombre se trouve pour toute fonction avec "dynamic" mais pas "bounded") à partir de-fstack-usage, trouvez le maximum de ces valeurs et cela devrait être votre utilisation maximale de la pile.

17
répondu τεκ 2011-06-17 19:44:49

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

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

Si vous combinez cette information avec un graphe d'appel produit par cflow ou un outil similaire, vous pouvez obtenir le type de pile analyse de profondeur que vous recherchez (un script pourrait probablement être écrit assez facilement pour le faire). Demandez au script de lire les informations d'utilisation de la pile et de charger une table des noms de fonction avec la pile utilisée par la fonction. Ensuite, le script parcourt le graphe cflow (qui peut être un arbre de texte facile à analyser), en additionnant l'utilisation de la pile associée à chaque ligne pour chaque branche dans le graphe d'appel.

Donc il semble que cela peut être fait avec GCC, mais vous pourriez avoir à bricoler ensemble le bon ensemble d'outils.

9
répondu Michael Burr 2011-06-17 21:44:46

assez tard, mais pour quiconque regarde ceci, les réponses données impliquant la combinaison des sorties de fstack-utilisation et les outils de graphique d'appel comme cflow peuvent finir par être sauvagement incorrects pour n'importe quelle allocation dynamique, même bornée, parce qu'il n'y a aucune information sur quand cette allocation dynamique de pile se produit. Il n'est donc pas possible de savoir vers quelles fonctions vous devriez appliquer la valeur. Par exemple, si la sortie (simplifiée) fstack-usage est:

main        1024     dynamic,bounded
functionA    512     static
functionB     16     static

et un arbre d'appel très simple est:

main
    functionA
    functionB

l'approche naïve de combiner ces deux options peut conduire à choisir la fonction main -> comme chemin d'utilisation maximale de la pile, à 1536 octets. Mais, si la plus grande allocation dynamique de la pile dans main () est de pousser un grand argument comme un enregistrement à functionB() directement sur la pile dans un bloc conditionnel qui appelle functionB (j'ai déjà dit que c'était inventé), alors vraiment main - > functionB est le chemin d'utilisation maximale de la pile, à 1040 octets. En fonction de la conception du logiciel existant, et aussi pour d'autres cibles plus restreintes qui passent tout sur la pile, les erreurs cumulatives peuvent rapidement vous conduire à regarder des chemins entièrement erronés réclamant des tailles de pile maximales considérablement surévaluées.

en outre, selon votre classification de" rentrant " en parlant d'interruptions, il est possible de manquer certaines allocations de pile entièrement. Par exemple, de nombreux processeurs Coldfire de niveau 7 interrompent sensible aux bords et donc ignore le masque de désactivation d'interruption, donc si un sémaphore est utilisé pour laisser l'instruction en avance, vous ne pouvez pas le considérer comme rentrant, mais l'allocation initiale de la pile se produira encore avant que le sémaphore ne soit vérifié.

En bref, vous devez être extrêmement prudent sur l'utilisation de cette approche.

6
répondu Adam Palaniuk 2016-09-29 04:35:41

Je ne suis pas familier avec les options -fstack-usage et -fcallgraph-info . Cependant, il est toujours possible de calculer l'utilisation réelle de la pile par:

  1. attribuer suffisamment d'espace de pile (pour cette expérience), et l'initialiser à quelque chose facilement identifiable. J'aime 0xee .
  2. exécute l'application et teste tous ses chemins internes (par toutes les combinaisons d'entrée et de paramètres). Laissez-le fonctionner plus que "assez longtemps".
  3. examiner la zone de la cheminée et voir quelle quantité de la cheminée a été utilisée.
  4. font que la taille de la pile, plus 10% ou 20% pour tolérer les mises à jour du logiciel et des conditions rares.
3
répondu wallyk 2011-06-17 18:38:05

j'ai fini par écrire un script python pour implémenter la réponse de τεκ . Il est trop de code à poster ici, mais peut être trouvé sur github

3
répondu PeterM 2017-05-23 12:03:01