Je travaille avec une application intégrée multithreadée. Chaque thread est alloué tailles de pile en fonction de sa fonctionnalité. Récemment, nous avons constaté que l'un des threads a corrompu la pile en définissant une gamme de variables locales qui était plus que la taille de la pile. Le système d'exploitation est Uitron. P>
Ma solution, J'ai enregistré une minuterie pendant 10 ms et cette minuterie vérifiera la corruption de pile. P>
Méthode de vérification de la corruption de pile, 1. Initialisez la mémoire de la pile avec un modèle unique (j'utilise 0x5a5A5A5A) 2. Vérifiez à partir de l'heure si le sommet de la mémoire de pile est toujours 0x5A5A5A5A P>
Y a-t-il un meilleur moyen de vérifier ce type de corruption p>
J'ai oublié d'ajouter, ajoutant maintenant: OS: Itron, Processeur: Arm9. Compilateur: N'EST PAS GCC (SPÉCIFIQUE ARM9 fourni par le fournisseur de processeur) ... et il n'y a pas de support intégré pour la vérification de la pile ... P>
8 Réponses :
Idéally Valgrind prendrait en charge votre plate-forme / système d'exploitation. Il est choquant pour moi que vous n'obtenez pas une région de mémoire VM distincte pour chaque pile de fil. S'il y a un moyen de construire votre application afin qu'il puisse également exécuter Linux, vous pouvez probablement reproduire le bogue et l'attraper avec Valgrind. P>
La plate-forme que je travaille ne supporte pas Valgrind par défaut, dois-je le porter. Mais ce système intégré a une mémoire limitée ...
Cela semble être une situation raisonnablement commune dans une situation intégrée, je ne serais pas choqué.
Ce n'est en fait pas surprenant du tout - de nombreux processeurs ARM9 n'ont pas de mmus par défaut, sauvent $$ et que vous n'en avez pas vraiment besoin, car vous n'avez pas de swapfile pour le soutenir
@Paul, il est assez courant qu'un système ait une MMU mais aucun fichier de swap.
Quel compilateur utilisez-vous? Je suppose qu'un système d'exploitation spécifique. Si vous utilisez GCC, vous pourrez peut-être utiliser le Stacking Protecteur . Cela pourrait être une solution pour que votre système de production empêche le problème et vous permettrait également de le détecter dans le développement. P>
Pour vérifier efficacement la corruption de pile, vous devez vérifier votre espace de pile disponible, mettre des gardes des deux côtés des arguments de pile avant l'appel, puis vérifiez les gardes du retour de l'appel. Ce type de changement nécessite généralement de modifier le code que le compilateur génère. P>
Je me demande s'il est possible d'écrire un piratage laid de préprocesseur, en utilisant des appels de fonctions nus et suffisamment d'assemblage pour suivre la convention d'appel de plate-forme plus les gardes et les chèques ...
@Eugene je suis à peu près sûr que ce que l'OP demande :-)
Notez que si vous vous sentez particulièrement insidieux, vous pouvez généralement obtenir GCC de générer un assemblage intermédiaire, modifier un peu et que votre assembleur exclusif / fermé mâche à ce sujet. Je l'ai déjà fait, puisque la génération d'ASM de GCC est des lieues d'avance sur ce que j'utilise dans des cas spécifiques.
As Lee mentionne, votre meilleur pari pourrait être de porter une clôture électrique à votre compilateur propriétaire Arm9. À défaut, le format ABI et Stack Bras est bien documenté, vous pouvez donc écrire une fonction check_stack qui vérifie que les adresses de retour indiquent les fonctions, etc. P>
Cependant, il est difficile de rédiger vraiment certains de ces chèques, sauf si vous êtes le compilateur, donc si vous n'êtes pas particulièrement attaché à ce compilateur, GCC fait em> bras de support et prend également en charge la pile gardes. p>
Arm9 a JTAG / ETM Déboguer un soutien sur matrice; Vous devriez être capable de configurer un point de cours d'accès aux données couvrant E.G. 64 octets près du sommet de vos piles, qui déclencheraient ensuite une adresse de données, que vous pourriez attraper dans votre programme ou à l'extérieur. P>
(le matériel que je travaille avec uniquement prend en charge 2 points de surveillance de lecture / écriture, pas sûr si c'est une limitation du truc sur puce ou du kit de débogage tiers environnant.) P>
Ce document , qui est extrêmement faible -Level Description de la manière d'interfacer avec la fonctionnalité JTAG, vous suggère de lire votre processeur Manuel de référence technique - et je peux garantir une quantité décente d'informations de niveau supérieur au chapitre 9 (" Support de débogage ") pour le Arm946E-S R1P1 TRM . P>
Avant de creuser dans la compréhension de tout ce genre de choses (sauf si vous ne le faites que pour le plaisir / l'éducation), vérifiez que le matériel et le logiciel que vous utilisez ne gèrent pas déjà les points d'arrêt / points de garde pour vous. Le concept de "WatchPoint" était un peu difficile à trouver dans le logiciel de débogage que nous utilisons - c'était un onglet «matériel» intitulé dans la boîte de dialogue Ajouter un point d'arrêt. P>
Une autre alternative: votre compilateur peut prendre en charge une option de ligne de commande pour ajouter des appels de fonction aux points d'entrée et de sortie des fonctions (une sorte de «Void» (const char * appelingfuncunc) et "VOID EXITEFUNCUNUNCUNCUNUNCUNCONNUM ) "), pour le profilage des coûts de fonction, un traçage de pile plus précis, ou similaire. Vous pouvez ensuite écrire ces fonctions pour vérifier votre valeur Canarie de pile. p>
(comme à l'écart, dans notre cas, nous ignorons réellement le nom de la fonction qui est transmis (j'aimerais que je puisse obtenir le lien de lien pour les dépasser) et utilisez simplement la valeur du registre de liaison du processeur (LR) pour enregistrer l'endroit où nous venons de . Nous utilisons cela pour obtenir des traces d'appel précises ainsi que des informations de profilage; vérifier les canaris de la pile à ce stade serait également trivial!) P>
Le problème est, bien entendu, appelant ces fonctions change le registre et les profils de pile pour les fonctions un peu ... PAS beaucoup, dans nos expériences, mais un peu. Les conséquences sur la performance sont pires et, partout où il y a une implication de performance, il y a eu des chances de changement de comportement dans le programme, ce qui peut signifier que vous par exemple. Évitez de déclencher une affaire de récursion profonde que vous pourriez avoir avant ... p>
Mise à jour très tardive: ces jours-ci, si vous avez un pipeline basé sur Clang + LLVM, vous pourrez peut-être utiliser Assainisseur d'adresses (ASAN) pour en attraper certaines d'entre elles. Soyez à la recherche de fonctionnalités similaires dans votre compilateur! (Ça vaut la peine de savoir sur Ubsan et les autres désintinérations. ) p>
N'hésitez pas à suggérer des ajouts / corrections - je n'ai jamais mis en place un "programme de moniteur de débogage" car les TRMS décrivent ici. Je suis un peu de lumière sur la connaissance dans ce domaine et la terminologie n'est pas encore solidement ancrée.
Lorsque vous travaillez récemment sur une plate-forme intégrée, j'ai semblé haut et bas pour les moyens de le faire (c'était sur un ARM7). p>
La solution suggérée était ce que vous avez déjà proposé: initialisez la pile avec un motif connu et assurez-vous que le motif existe après son retour d'une fonction. Je pensais la même chose "Il doit y avoir une meilleure façon" et "n'a pas de personne automatisée cela". La réponse aux deux questions était "non" et je devais creuser tout comme vous l'avez fait pour essayer de trouver où la corruption se produisait. p>
J'ai aussi "roulé mes propres" vecteurs d'exception pour le data_Abort, etc. Il existe de super exemples sur le "net de la manière de corriger la pile d'appels. C'est quelque chose que vous pouvez faire avec un débogueur JTAG, casser lorsque tous ces vecteurs d'avortement se produisent, puis enquêtent sur la pile. Cela peut être utile si vous n'avez que 1 ou 2 points d'arrêt (ce qui semble être la norme pour le débogage du bras JTAG). p>
+1, merci pour le conseil Data_Abort, je n'ai utilisé aucun gestionnaire d'exception pour ma pile et à cause de laquelle je devais sonder ~~
J'ai fait exactement comme vous l'avez suggéré sur DSPIC à l'aide de CMX-Tiny +, cependant, dans le chèque de pile, je maintiens également une «marque de cache-marée» pour chaque pile. Plutôt que de vérifier la valeur en haut de la pile, j'ai itérus du haut pour trouver la première valeur non signature, et si cela est plus élevé que précédemment, je le stocke dans une variable statique. Cela se fait dans une tâche de priorité la plus basse de sorte qu'elle soit effectuée chaque fois que rien d'autre ne soit programmé (remplaçant essentiellement la boucle de ralenti; dans votre RTO, vous pourrez peut-être accrocher la boucle inactive et le faire là-bas). Cela signifie qu'il est typiquement vérifié plus souvent que votre vérification périodique de 10 ms; En ce moment, tout le planificateur pourrait être foutu. P>
Ma méthodologie est ensuite de surdimensionnaliser les piles, d'exercer le code, puis de vérifier les marques haute marée pour déterminer la marge de chaque tâche (et la pile ISR - n'oubliez pas cela!) et ajustez les piles en conséquence si Je dois récupérer l'espace "gaspillé" des piles surdimensionnées (je ne me dérange pas si l'espace n'est pas autrement nécessaire). P>
L'avantage de cette approche est que vous n'attendez pas que la pile soit cassée pour détecter un problème potentiel; Vous le surveillez lorsque vous développez et car les modifications sont vérifiées. Ceci est utile car si la corruption frappe une adresse TCB ou une adresse de retour, votre planificateur peut être si cassé le chèque ne frappe jamais après un débordement. P>
Certaines rupestres ont cette fonctionnalité intégrée (embos, VXWorks que je connaisse). Le système d'exploitation que l'utilisation du matériel MMU peut mieux se faire taire en plaçant la pile dans un espace mémoire protégé afin qu'un débordement provoque une avertissement de données. C'est le «meilleur moyen» que vous recherchez peut-être; Arm9 a un MMU, mais le système d'exploitation qui le soutient bien a bien tendance à être plus chers. QNX Neutrino Peut-être? P>
Si vous ne voulez pas faire la vérification de la marée haute manuelle, il suffit de surdire les piles en disant 1k, puis dans la tâche de la pile-chèque, la condition lorsque la marge tombe en dessous de 1k. De cette façon, vous êtes plus susceptible de piéger la condition d'erreur pendant que le planificateur est toujours viable. Pas du poisson preuve, mais si vous commencez à allouer des objets assez gros, le coup sur la pile en une fois, les cloches d'alarme devraient sonner dans votre tête dans tous les cas - c'est le fluage de pile lent plus courant causé par une neige jamais plus profonde nidification et similaires que ce sera aide avec. p>
clifford. p>
+1 Pour mentionner la tâche ISR, comme je l'ai complètement oublié. Et aussi merci pour l'idée de donner un espace de pile supplémentaire pour le débogage.
Avez-vous la source du noyau? La dernière fois que j'ai écrit un noyau, j'ai ajouté (en option) la vérification du noyau lui-même. P>
Chaque fois qu'un coup de contexte allait se produire, le noyau vérifierait 2 piles: p>
(1) La tâche étant échangée forte> -> si la tâche a soufflé sa pile alors qu'elle était en cours d'exécution, savons actuellement. P>
(2) théoriquement les piles de toutes les tâches pourraient être vérifiées, mais les commentaires ci-dessus fournissent la raison pour laquelle j'ai vérifié ces 2 piles (configurable). P>
En plus de cela, le code d'application peut surveiller les tâches (incl. la pile d'interruptions si vous en avez une) dans la boucle inactive, la tique ISR, etc ... p>
Checkout Ces questions similaires: Handling Stack débordent dans des systèmes embarqués et Comment puis-je visualiser la mémoire Utilisation SRAM d'un programme AVR . P>
Personnellement, j'utiliserais l'unité de gestion de la mémoire de votre processeur qu'elle en a une. Il peut effectuer une vérification de la mémoire pour vous avec un logiciel minimal. P>
Configurez une zone de mémoire dans la MMU qui sera utilisée pour la pile. Il devrait être bordé par deux zones de mémoire où la MMU n'autorise pas l'accès. Lorsque votre application est en cours d'exécution, vous recevrez une exception / une interruption dès que vous déborde de la pile. P>
Parce que vous obtenez une exception pour le moment où l'erreur se produit, vous savez exactement où dans votre application, la pile a été mauvaise. Vous pouvez regarder la pile d'appels pour voir exactement comment vous êtes arrivé là où vous êtes. Cela rend beaucoup plus facile de trouver votre problème que d'essayer de déterminer ce qui ne va pas en détectant votre problème longtemps après l'arrivée. P>
Un MMU peut également détecter les accès à zéro pointeur si vous interdisez l'accès à la mémoire à la partie inférieure de votre RAM. P>
Si vous avez la source des RTO, vous pouvez construire la protection MMU de la pile et de la tasse. P>
Si cela est purement à des fins de débogage, il doit y avoir un meilleur moyen que de rouler votre propre solution. Si cela est à des fins de production, vous feriez mieux de fixer la cause au lieu d'essayer de détecter le symptôme.
Vous n'aurez-vous pas de faux positifs une fois que vous appelez une fonction, revenez-en? (C'est-à-dire une fois que la fonction se ferme, elle rend la pile plus petite mais ne remplace pas le 0x5A5A5A5A5A5A). Ou parlez-vous d'attraper complètement la pile complètement
Stackoverflow.com/questions/1185997/... < / a>