Adresse d'origine : http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
Publié : mardi 10 octobre 2006 à 16h10
Auteur : Tess
Un jour, j'ai reçu un e-mail à propos de mon blog posant les questions suivantes, brièvement décrites comme suit :
Je voulais créer un plan de site rapidement, j'ai donc remplacé la méthode BuildSiteMap() et, à l'intérieur, j'ai écrit une boucle qui ajoutait de faux nœuds de plan de site.
remplacement public SiteMapNode BuildSiteMap(){
pour (int i = 0; i < 5; i++)
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
renvoie maRacine ;
}
Lors de l'exécution du programme, un débordement de pile se produit et le serveur plante. J'ai utilisé le débogueur pour parcourir et j'ai trouvé quelque chose de vraiment étrange :
1) int i = 0
2) je < 5
3) maRacine...
4) entier je = 0
5) je < 5
etc.
La valeur de i ne semble jamais augmenter à moins que j'appelle le SiteMapNode (accéder à une propriété, appeler une méthode), ce qui semble que la boucle est correcte.
Qu’est-ce qui rend cette boucle indéterminée ? À première vue, il pourrait s'agir d'un bug du compilateur ou du CLR.
(Quand j'ai eu ce problème, je ne connaissais vraiment pas la navigation sur les sites dans ASP.NET 2.0, mais j'ai trouvé ces articles... http://weblogs. asp. net/scottgu/archive/2005/11/20/431019.aspx et http://aspnet.4guysfromrolla.com/articles/111605-1.aspx , les descriptions sont vraiment bonnes.)
premières réflexions
La chose la plus importante à propos de ce problème est qu'il redémarre toujours, ce qui signifie qu'il peut être débogué sur place. Mais n’allons pas aussi loin, regardons en arrière et voyons ce que nous avons maintenant…
1. Débordement de pile
2. Un cycle qui recommence encore et encore
J'ai discuté du débordement de pile dans un article de blog précédent, et je vais le répéter... Le débordement de pile se produit lorsque trop de pointeurs de fonction, de pointeurs de variable et de paramètres sont alloués, de sorte que la quantité de mémoire allouée sur la pile ne l'est pas. assez. La cause de loin la plus courante des débordements de pile est la récursivité sans fin. En d'autres termes, la fonction A appelle la fonction B, la fonction B appelle la fonction A...
La pile d'appels ressemble donc un peu à ceci....
...
fonctionB()
fonctionA()
fonctionB()
fonctionA()
D'accord, c'est bien beau, mais cela explique simplement Stack Overflow. Alors que se passe-t-il avec ce cycle fou ?
OK...imaginez qu'il existe une telle fonction (il y a un point d'arrêt à -->)
void MyRecursiveFunction(){
pour(int i=0; i<5; i++){
--> MaFonctionRécursive();
}
}
Lorsque vous vous arrêtez pour la première fois au point d'arrêt, la valeur de i devrait être 0 et la pile d'appels ressemblera à ceci...
MaFonctionRécursive()
...
Appelez maintenant la fonction MyRecrusive. Chaque fois que la fonction elle-même est appelée, i=0 réapparaîtra (même si nous ne sommes pas vraiment dans la même boucle). Si vous appelez la fonction MyRecrusive plusieurs fois et la remplacez par le code réel exécuté, elle exécutera un code similaire au suivant :
pour(int i=0; i<5; i++){
pour(int i2=0; i2<5; i2++){
pour(int i3=0; i3<5; i3++){
pour(int i4=0; i4<5; i4++){
pour(int i5=0; i5<5; i5++){
pour(int i6=0; i6<5; i6++){
pour(int i7=0; i7<5; i7++){
...
}
}
}
}
}
}
}
... en le regardant dans Visual Studio, il semble que la même boucle soit toujours exécutée et ne change pas la valeur de la variable i. Pour l'instant, vous n'aurez pas une compréhension approfondie de cela tant que vous n'aurez pas réellement vu l'appel de la pile.
Si nous regardons la pile d'appels, la pile d'appels ressemble maintenant à ceci...
MyRecursiveFunction()
MaFonctionRécursive()
MaFonctionRécursive()
MaFonctionRécursive()
MaFonctionRécursive()
MaFonctionRécursive()
MaFonctionRécursive()
...
donc la conclusion de la réflexion initiale est que nous voulons sans aucun doute envisager une certaine récursion... mais où ? Le code de l'exemple
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString())
)
; new SiteMapNode() et myRoot.ChildNodes.Add(), ce n'est plus si mystérieux si on le regarde avec un réflecteur.
Problèmes de débogage
Enfin :) Moins de mots, plus d'action éolienne...
Puisqu'il est restitué facilement, je vais le restituer sur ma machine en attachant simplement windbg (Fichier / Attacher au processus) à w3wp.exe et en appuyant sur g pour démarrer. Le problème réapparaît alors et le programme s'arrête en me disant qu'il s'agit d'un débordement de pile (que nous connaissons déjà).
(7e4.ddc) : Débordement de pile - code c00000fd (première chance)
Les exceptions de première chance sont signalées avant toute gestion des exceptions.
Cette exception peut être attendue et gérée.
eax=0fa4235c ebx=02beca74 ecx=02beca74 edx=02becb54 esi=02becb54 edi=02beca74
eip=686b5cb4 esp=02163000 ebp=02163004 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246
System_Web_ni+0xf5cb4:686b5cb4 56 push esi
Vérifions la pile et utilisons la commande !clrstack pour voir comment elle a été interrompue, mais nous ne pouvons que voir....
0:016> !clrstack
ID de fil de discussion du système d’exploitation : 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... Cela ne nous aide pas vraiment beaucoup. Parfois, lorsque nous rencontrons un débordement de pile, certains problèmes de ce type surviennent à l'aide de la commande !clrstack. Par conséquent, nous devons également utiliser la commande !dumpstack pour afficher la pile brute.
0:016> !dumpstack
ID de fil de discussion du système d’exploitation : 0xddc (16)
Image actuelle : (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
ChildEBP RetAddr Appelant,Callee
02163004 686b1fc4 (MethodDesc 0x68aeff30 +0x18 System.Web.SiteMapNode.get_ChildNodes())
0216300c 0f765641 (MethodDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
0216303c 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
02163074 686b1fc4 (MethodDesc 0x68aeff30 +0x18 System.Web.SiteMapNode.get_ChildNodes())
0216307c 0f765641 (MethodDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
021630ac 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
021630e4 686b1fc4 (MethodDesc 0x68aeff30 +0x18 System.Web.SiteMapNode.get_ChildNodes())
021630ec 0f765641 (MethodDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
0216311c 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
02163154 686b1fc4 (MethodDesc 0x68aeff30 +0x18 System.Web.SiteMapNode.get_ChildNodes())
0216315c 0f765641 (MethodDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
...
D'accord, il semble que le problème vienne de la propriété ChildNodes. Lorsque cette propriété est utilisée, la fonction GetChildNodes est appelée, ce qui appelle à nouveau la fonction BuildSiteMap, qui à son tour appelle la propriété ChildNodes, et ainsi de suite, provoquant un débordement de pile.
en conclusion
Dans la documentation sur BuildSitemap vous pouvez trouver le paragraphe suivant :
La méthode BuildSiteMap est appelée par l'implémentation par défaut des méthodes FindSiteMapNode, GetChildNodes et GetParentNode. Si vous remplacez la méthode BuildSiteMap dans une classe dérivée, assurez-vous qu'elle charge les données du plan de site une seule fois et les renvoie lors des appels suivants.
Afin d'éviter la récursion et le débordement de pile, il est préférable d'éviter d'appeler cette méthode. Comme dans l'exemple BuildSiteMap, nous pouvons utiliser la méthode AddNode pour ajouter des nœuds enfants.
Ceci est archivé dans l’article Fournisseurs de plans de site, qui mérite également d’être lu.
BuildSiteMap ne doit généralement pas appeler de méthodes ou de propriétés fournies par d'autres plans de site, car de nombreuses méthodes et propriétés implémenteront les appels BuildSiteMap par défaut. Par exemple, le RootNode dans BuildSiteMap provoque une récursion, ce qui entraîne sa fin avec un débordement de pile.
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html