Welcome to MSDN Blogs Sign in | Join | Help
Debogage Silverlight avec WinDbg et SOS - C'est possible !

Tout comme les applications développées en .NET, qu'elles soient clientes (Windows Forms, WPF) ou serveurs (ASP.NET, Service Windows, WCF), les applications Silverlight ont aussi le droit d’être déboguées !

Comment analyser un crash ou une fuite mémoire de nos applications riche Internet ? "Tout simplement" (façon de parler) de la même manière que pour toutes les autres... avec WinDbg.

Cela veut donc dire que ce que nous avons vu sur le débogage .NET avec WinDbg et SOS est valable : C’est un luxe de pouvoir s’appuyer sur ses acquis !

Si vous n’êtes pas familier avec WinDbg et SOS, je vous invite à lire mes précédents articles :

 

Allez zou… Démonstration !

 

1. Exemple d’application

Prenons un exemple le plus simple possible. Le but est de pouvoir être capable de visualiser nos appels de fonctions et nos objets. Pour ce faire, j’ai créé un nouvelle application Silverlight et modifié le code associé à la page par défaut ("MainPage.xaml.cs") :

  • J’utilise une liste d’objets d’un type particulier "Elt" > Ce qui me permettra de les différencier dans WinDbg
  • Cette liste est rempli au démarrage et je marque un temps d’arrêt avec un message destiné à l’utilisateur > Ce qui me laissera le temps de lancer WinDbg et m’attacher au processus

Voici le code (mémorisez l’espace de nom)

namespace ExempleSL
{
    public partial class MainPage : UserControl
    {
        private List<Elt> listeElements = new List<Elt>();

        public MainPage()
        {
            for (int i = 0; i < 10; i++)
            {
                listeElements.Add(new Elt() { Name = "elt" + DateTime.Now.Millisecond, Donnees = i });
            }
            MessageBox.Show("Continuer ?");
            InitializeComponent();
        }

        public class Elt
        {
            public string Name { get; set; }
            public int Donnees { get; set; }
        }
    }
}

C’est parti : lancement de l’application avec CTRL+F5 dans Visual Studio.

ExempleSL

Le message apparait : nous pouvons lancer le débogueur pour regarder ce que nous avons en mémoire.

 

2. S’attacher au processus avec WinDbg

Les applications Silverlight s’exécutent dans le navigateur. Il ne faut donc pas chercher à prendre un dump ou s’attacher à ExempleSL.exe mais plutôt à iexplore.exe.

Lançons WinDbg en tant qu’administrateur et attachons-nous au processus avec "FILE / Attach to process…" ou F6. Prenons iexplore.exe.

 WinDbg

Ensuite, comme nous le faisons à l’accoutumé, 3 étapes :

  • Renseignement du serveur de symboles

.sympath SRV*c:\symbols*http://msdl.microsoft.com/download/symbols

  • Chargement des symboles

.reload

  • Chargement de l’extension SOS pour Silverlight

.load c:\Program Files (x86)\Microsoft Silverlight\3.0.40818.0\sos

Notez bien que nous avons une version sos.dll par version du Framework dans C:\windows\Microsoft.NET\Framework\vX et un une version sos.dll par version de Silverlight.

 

3. Deboguer

La liste des commandes disponibles de SOS est donnée par

!help

Windbg !help

 

Pour savoir quel code est en cours d’exécution dans notre application, nous lançons une commande qui s’exécutera sur tous les threads du processus. Cette commande est "!ClrStack". Elle nous permet d’obtenir la pile d’appel d’un thread. 

~*e !ClrStack

Beaucoup de threads ne sont pas en cours d’exécution de code Silverlight :

image

Mais le thread 5, l’est :Windbg !ClrStack

ESP       EIP    
02abd4f4 77cd9a94 [NDirectMethodFrameStandalone: 02abd4f4] MS.Internal.XcpImports.MessageBox_ShowCoreNative(IntPtr, System.String, System.String, UInt32, Int32 ByRef)
02abd510 03fd84f2 MS.Internal.XcpImports.MessageBox_ShowCore(System.String, System.String, UInt32)
02abd52c 03fd8449 System.Windows.MessageBox.ShowCore(System.String, System.String, System.Windows.MessageBoxButton)
02abd55c 03fd835c System.Windows.MessageBox.Show(System.String)
02abd56c 03f40376 ExempleSL.MainPage..ctor()
02abd5c8 03f401fe ExempleSL.App.Application_Startup(System.Object, System.Windows.StartupEventArgs)
02abd5e0 03fa69c9 System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32, System.Delegate, System.Object, System.Object)
02abd6a0 03fa3b04 MS.Internal.JoltHelper.FireEvent(IntPtr, IntPtr, Int32, System.String)
02abd884 694917b0 [GCFrame: 02abd884]
02abd940 694917b0 [ContextTransitionFrame: 02abd940]
02abda38 694917b0 [UMThkCallFrame: 02abda38]

Nous voyons donc noir sur blanc, le "cheminement" d’exécution.

  • D’abord nous avons le démarrage de l’application avec "ExempleSL.App.Application_Startup"
  • Puis le constructeur de la clase MainPage ("ExempleSL.MainPage..ctor")
  • Et enfin, l’affichage de notre message : "System.Windows.MessageBox.Show"

 

Est-il possible de visualiser nos objets en mémoire ?

Oui, la méthode "!DumpHeap" nous donne tous les objets .NET en mémoire. Le paramètre "-type" nous permet de filtrer sur les objets dont le nom de la classe contient "ExempleSL".

!DumpHeap -stat -type ExempleSL

WinDbg !DumpHeap

total 13 objects
Statistics:
      MT    Count    TotalSize Class Name
039d3dcc        1           24 System.Collections.Generic.List`1[[ExempleSL.MainPage+Elt, ExempleSL]]
039d3868        1           44 ExempleSL.App
039d3c24        1           84 ExempleSL.MainPage
039d3d6c       10          160 ExempleSL.MainPage+Elt

Nous avons bien

  • Notre liste
  • 1 objet pour notre application
  • 1 objet pour notre page
  • Les 10 éléments de la liste

cqfd :-)

Comment savoir si une dll est 32bits ou 64bits ?

Point important pour vos développements natifs ou pour l’utilisation de composants COM sur plateformes x64 :

Un processus 64bits peut charger seulement des dlls 64bits. De la même manière, un processus 32bits peut charger seulement des dlls 32bits.

Ceci peut paraitre trivial comme affirmation, mais cela a son importance car un processus 64bits ne pourra donc pas charger de dll 32bits. L’inverse n’est pas possible non plus.

Voici la référence : http://msdn.microsoft.com/en-us/library/aa384231(VS.85).aspx

 

Ok, d’accord… Et pour la vérification, comment puis-je savoir si une dll est compilée en 32 ou 64 ?

Simplement avec l’outil DumpBin livré avec Visual Studio. Vous pouvez le lancer directement à partir de la ligne de commande Visual Studio 2008.

VisualStudioCommandPrompt

 

Par exemple :

dumpbin "C:\Windows\System32\inetsrv\asp.dll" /HEADERS

dumpbin

 

dumpbin "C:\Windows\System32\inetsrv\asp.dll" /HEADERS | findstr "(x64)"

dumpbinfindstrx64

 

dumpbin "C:\Windows\SysWOW64\inetsrv\asp.dll" /HEADERS | findstr "(x86)"

dumpbinfindstrx86

Ca peut servir :-)

Bye,

Sebastien.

Propagation de clés par Office2007 de HKLM vers HKCU

Office 2007 apporte une fonctionnalité intéressante de propagation de clés de registre. Le principe est de pouvoir utiliser HKEY_LOCAL_MACHINE pour des clés qui se retrouvent normalement que dans HKEY_CURRENT_USER.

Cette fonctionnalité peut vous être utile, par exemple, dans le cadre d’un déploiement d’un Add-In Office 2007 qui ne peut se faire par définition que pour l’utilisateur courant (c’est à dire dans HKCU). Vous pouvez aussi en avoir besoin dans le cadre d’un paramétrage Office ne s’effectuant que dans la ruche HKCU comme l’affichage du ruban développeur Office.

Voici comment cela fonctionne.

  • A chaque lancement d’applications Office, les clés contenus dans "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings" sont vérifiées
  • Disons, par exemple, que l’on crée "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation" contenant notre logique de propagation. Nous pouvons ensuite créer des sous-clés "Create" qui copieront toutes les clés contenues de cette sous-clé vers HKCU. De la même façon, des sous-clé "Delete" effaceront toutes les clés correspondantes dans l’arborescence HKCU

Pour être concret, prenons l’exemple suivant :

Windows Registry Editor Version 5.00

; Installation
; Utilisation de la fonctionnalité Office 2007 de propagation des clés HKLM vers HKCU
; Exemple pour afficher le ruban developpeur Office

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation]
"Count"=dword:00000001
; "MaPropagation" est le code donnée à mon exemple
; "Count" est notre compteur qui sera propagé dans
; "HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\User Settings\MaPropagation"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation\Create\Software\Microsoft\Office\12.0\Common\General]
"DeveloperTools"=dword:00000001
; Nous créons le dword "DeveloperTools" avec la valeur 1 dans
; "HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\General"

Détaillons :

Nous mettons dans la base de registre le dword "Count" = 1 dans HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation. "MaPropagation" est le nom que j’ai choisi pour  cet exemple.image

 

Lorsque qu’Office va se lancer, il effectue un parcours de toutes les clés contenues dans "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings". Pour chaque clé, il va vérifier si la même clé existe dans "HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\User Settings". Dans notre exemple,

  • Si la clé "MaPropagation" n’existe pas. La clé est créée avec le compteur et la propagation est faite
  • Si la clé "MaPropagation" existe, le compteur est vérifié et la propagation est faite que si la valeur du compteur est différent de celle contenu dans "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation".
image

Cette vérification (de la valeur du compteur) permet de ne pas refaire indéfiniment la propagation à chaque lancement d’Office ! Justement, je pense que vous aurez compris que pour refaire un propagation qui modifiera ou supprimera des clés, nous devons modifier le compteur de "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation"

 

Au final, en quoi consiste cette fameuse propagation ? Très simplement :

  • Les clés contenues dans "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation\Create" sont ajoutées/modifiées de "HKEY_CURRENT_USER"
  • Les clés contenues dans "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\User Settings\MaPropagation\Delete" sont supprimées de "HKEY_CURRENT_USER"

Dans notre exemple, nous créons le dword "DeveloperTools" dans "HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\General".

Sans titre

image

 

Pour résumé, avant tout lancement d’Office, seules les clés HKLM sont présentes. Lorsque un utilisateur lance Office, le mécanisme de propagation opère. C’est donc comme ceci que l’on peut déployer un Add-In VSTO 3 pour tous les utilisateurs Office 2007 (même ceux qui n’ont pas encore de profil dans la base de registre)

A titre d’exemple, voici les .REG qui permettent d’afficher/masquer le ruban développeur Office 2007
SB-Installation-AjoutDeveloperToolsDansOffice2007PourTousLesUtilisateurs.reg 

Bye,

Sebastien.

Debogage .NET avec WinDbg et SOS - Travaux Pratiques

Ci-dessous, dans mes premiers articles sur le débogage WinDbg avec SOS, j’aborde le chargement des symboles/SOS, l’examen des piles d’appels et la visualisation des objets :

J’espère que ces billets vous sont utiles !

 

Afin que ce soit plus interactif, je mets à votre disposition le dump que j’ai pris et utilisé pour illustrer mes explications.

Vous le trouverez ici (66Mo)
W3WPActif.zip

 

Comme exercices, pourquoi pas essayer de trouver par vous-même

  1. Quels sont les threads qui exécutent des pages Web au moment de la prise du dump ? Combien sont-ils ?
  2. Quelles sont les pages Web exécutées (les urls) ?
  3. Et pour le plaisir : une dll est utilisée par le site Web. Elle s’appelle "BlogEngine.Core.dll". Quelle est la version de cette dll et à quelle date a-t-elle été compilée ?

Bon débogage :-)

Sebastien.

>>> To be continued…

Ajouter Bing dans votre site Web !

Envie d’avoir un vrai moteur de recherche pour votre blog ou site Internet sans avoir en acheter un, le coder et le maintenir ou même l’héberger… Alors lisez bien ce qui suit !

De plus, pour une fois, je ne pense pas que l’on puisse faire plus simple ! Si peut-être un bouton "copy to clipboard" dans l’étape 3 de l’assistant :-) Vous verrez : il faut faire un CTRL+A et CTRL+C :-)

Naviguez sur http://www.bing.com/siteowner/

BingBox

Suivez l’assistant

- en donnant l’url de votre site -
BingBox-Etape1 

- en choisissant la taille de zone de recherche, les dimensions de la fenetre de résultat ainsi que sa couleur -
BingBox-Etape2 

- en sélectionnant le HTML généré et le copier dans le presse-papier (c’est ici que l’on aurait pu rajouter un bouton :-) -
BingBox-Etape3

Coller ce HTML dans votre site

Le mieux est d’avoir un page maitre, comme cela : une seule insertion… C’est tout ! Ca fonctionne même immédiatement puisque c’est le moteur de recherche qui travaille pour nous ! Je l’ai adopté illico :-)

C’est tellement facile et tellement efficace ! Vraiment IMPRESSIONANT !!!

Qu’en pensez-vous ?

A bientôt,

Sebastien.

Debogage .NET avec WinDbg et SOS - Objects

Dans le précédent billet, nous parlions des threads. Continuons… et intéressons-nous aux objets en mémoire.

Objets sur la pile d’appels

!dso

(pour dump stack objets)

!dso affiche tous les objets .NET contenus dans la pile d’appel du thread courant. Nous pouvons ensuite regarder ceux qui nous intéressent. Voici à quoi cela ressemble :

dso 

Deux informations sont importantes ici :

  • La deuxième colonne donne l’adresse mémoire de l’objet - sur 64bits s’il vous plait:-)
  • La troisième colonne indique le "type" de l’objet ; c’est à dire sa classe

Voici un extrait du résultat de !dso :

RSP/REG          Object           Name
...
00000000039ae340 000000013f4655d0 System.String
00000000039ae350 000000013f5df210 System.Web.HttpRequest
00000000039ae358 000000013f5e5920 System.Web.UI.ControlCollection
00000000039ae360 000000013f5e56d0 System.Web.UI.HtmlControls.HtmlForm
00000000039ae370 000000013f4656b0 System.String
00000000039ae388 000000013f5e56d0 System.Web.UI.HtmlControls.HtmlForm
00000000039ae3b8 000000013f5e0de8 ASP.search_aspx
...

 

Pour obtenir plus d’information sur un objet, il suffit de faire double clic puis clic droit sur son adresse mémoire. Cela a le même effet que de faire une sélection de l’adresse puis un CTRL+C. A ce moment là, l’adresse est copiée dans le presse papier. Nous pourrons la coller par la suite avec un clic droit dans la zone de saisie ou un CTRL+V.

WindbgZoneSaisie

Prenons, par exemple, l’objet HttpRequest stocké sur la pile d’appel :

00000000039ae350 000000013f5df210 System.Web.HttpRequest

 

Analyse d’un objet

!do AdresseMemoire

(pour dump objet)

!do affiche l’objet ainsi que toute sa structure interne. Nous avons donc directement les valeurs des propriétés qui composent l’objet ou bien leur adresse mémoire. Nous pouvons donc renouveler la même opération sur les adresses mémoires des propriétés/objets qui nous intéressent.

Suivez le guide…

!do 000000013f5df210

pour l’objet de type System.Web.HttpRequestdo

Vous avez repéré l'adresse de la propriété _url ?

!do 000000013f5e9658

pour l’objet _url de type System.Uri contenu dans l’objet HttpRequest précédentdo

Un dernier petit effort pour avoir l’url demandée par cette requête HTTP, voyons voir "m_String" :

!do 000000013f5e95d0

pour l’objet m_String de type System.String contenu dans l’objet Uri précédent dostring

Enfin, nous y voila

String: http://localhost:80/BlogEngine/search.aspx?q=BlogEngine

Tout simplement fantastique ! :-) Et ce n’est que le début. "!DumpHeap -stat" vous connaissez ?

Bye, Sebastien.

>>> Suite : Debogage .NET avec WinDbg et SOS - Travaux Pratiques

Debogage .NET avec WinDbg et SOS - Threads

Le débogage .NET diffère du débogage natif dans la mesure ou les objets et les piles d’appels qui nous intéressent ne sont pas visibles/exploitables sans utiliser une extension WinDbg nommée SOS.

Chargement de l’extension

Pour charger SOS dans WinDbg, la syntaxe est la suivante :

.loadby sos mscorwks

Vous pouvez vérifier la liste des extensions chargées avec

.chain

Pour information, le déchargement se fait avec

.unload sos

 

Démarrage

!EEVersion

Permet d'obtenir lu numéro de version exact de la CLR chargée dans le processus.

EEVersion

 

La liste des commandes disponibles de l’extension s’obtient par

!help

!help

Vous trouverez l’aide complète en ligne sur MSDN : SOS Debugging Extension (SOS.dll) - http://msdn.microsoft.com/en-us/library/bb190764.aspx

 

Afin de visualiser un semblant d’activité, j’utilise TinyGet (un peu brusquement :-)) avec le lancement des deux commandes suivantes (2x50 utilisateurs simultanés effectuant 100 requêtes, les unes à la suite des autres)

tinyget.exe -srv:localhost -uri:/BlogEngine/ -status:200 -threads:50 -loop:100

et

tinyget.exe -srv:localhost -uri:/BlogEngine/search.aspx?q=BlogEngine -status:200 -threads:50 -loop:100

Pendant l’exécution, après avoir lancé WinDbg en tant qu’administrateur, je m’attache au processus W3WP.EXE avec le menu FILE/Attach to process…

AttahToProcess

Ensuite comme d’habitude :

Chargement des symboles .sympath SRV*c:\symbols*http://msdl.microsoft.com/download/symbols/
Demande de chargement .reload
Chargement de SOS

.loadby sos mscorwks

 

--

 

Voyons voir ce que nous avons et ce que nous pouvons obtenir avec WinDbg et SOS. Les questions que l’on peut se poser sont par exemple :

  • Combien de threads exécutent des pages ASPX ?
  • Quelles sont les pages exécutées ?
  • Où en est leur traitement ?

 

Piles d’appels ou "call stacks"

Vous remarquerez le nombre en bas à droite de la fenêtre de Windbg : il s’agit du numéro du thread sur lequel nous somme positionné

ThreadID

Pour visualiser la pile d’appel de ce thread, la commande est

kp

kp

Loin d’être intuitif n’est pas ? En fait, c’est normal puisque nous visualisons les informations natives de la pile d’appels. Pour obtenir la pile d’appels managée, nous utilisons

!ClrStack

ClrStack

Pour information, le changement de thread se fait avec

~XXs

Où XX est le numéro du thread.

Comme raccourci, pour exécuter une commande sur tous les threads, nous pouvons utiliser

~*kp

~*e !ClrStack

"e" permet d’exécuter une commande (contenu dans un extension - c'est le cas pour SOS)

 

Donc, si l’on reprend la pile d’appels du thread 20,
capture ci-dessus
System.Web.UI.HtmlTextWriter.WriteUTF8ResourceString(IntPtr, Int32, Int32, Boolean)
ASP.search_aspx.__RenderContent1(System.Web.UI.HtmlTextWriter, System.Web.UI.Control)
System.Web.UI.Control.RenderChildrenInternal(System.Web.UI.HtmlTextWriter, ... ASP.themes_standard_site_master.__Render__control5(System.Web.UI.HtmlTextWriter, ...
System.Web.UI.Control.RenderChildrenInternal(System.Web.UI.HtmlTextWriter, ...
System.Web.UI.HtmlControls.HtmlForm.RenderChildren(System.Web.UI.HtmlTextWriter)
System.Web.UI.HtmlControls.HtmlForm.Render(System.Web.UI.HtmlTextWriter)
System.Web.UI.HtmlControls.HtmlForm.RenderControl(System.Web.UI.HtmlTextWriter)
ASP.themes_standard_site_master.__Render__control1(System.Web.UI.HtmlTextWriter, ...
System.Web.UI.Control.RenderChildrenInternal(System.Web.UI.HtmlTextWriter, ...
System.Web.UI.Control.RenderChildrenInternal(System.Web.UI.HtmlTextWriter, ...
System.Web.UI.Page.Render(System.Web.UI.HtmlTextWriter)
BlogEngine.Core.Web.Controls.BlogBasePage.Render(System.Web.UI.HtmlTextWriter)
System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
System.Web.UI.Page.ProcessRequest()
System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
ASP.search_aspx.ProcessRequest(System.Web.HttpContext)
System.Web.HttpApplication+CallHandlerExecutionStep...
System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest ...
System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

 

Nous avons le nom de la page éxécutée : "search.aspx”. Elle est donnée par

ASP.search_aspx.ProcessRequest(System.Web.HttpContext)

Comme j’ai le code source sous la main, c’est plus facile ensuite de confirmer :-) : Vérification

La page maitre est "site.master" correspondant au thème "Standard" :

ASP.themes_standard_site_master.__Render__control1(System.Web.UI.HtmlTextWriter, System.Web.UI.Control)

Vérification :-) : Vérification

Si l’on décortique la pile d’appels, nous obtenons un peu plus en détails. Nous avons l’enchainement des toutes les fonctions appelées. A noter que la lecture se fait de bas en haut (du plus ancien appel au plus récent).

La pile d’appels nous apprend que l’exécution de la page "search.aspx" a demandé le chargement de la classe "BlogEngine.Core.Web.Controls.BlogBasePage" et l'exécution de sa méthode "Render" : Nous devons être dans le cas d’un classe de base héritée.

ASP.search_aspx.ProcessRequest(System.Web.HttpContext)

puis

BlogEngine.Core.Web.Controls.BlogBasePage.Render(System.Web.UI.HtmlTextWriter)

Nous avons ensuite, la demande de chargement de la page maitre et des contrôles contenus :

ASP.themes_standard_site_master.__Render__control1(System.Web.UI.HtmlTextWriter, ...

Enfin, au moment du débogage, nous étions toujours en dans la phase de génération la page Web :

System.Web.UI.HtmlTextWriter.WriteUTF8ResourceString(IntPtr, ...

C’est pas mal pour un début non ? La suite au prochain épisode.

A bientôt, Sebastien.

>>> Suite : Debogage .NET avec WinDbg et SOS - Objects

deployment retail="true" pour ASP.NET

Ce n’est pas une information de première fraicheur :-) mais compte tenu qu’elle n’est pas très documentée, je la relaie dans cette article.

Depuis ASP.NET 2.0, nous avons l’élément <deployment> dans <system.web> qui permet de définir à un seul endroit (Machine.config) si les applications ASP.NET présentes sur le serveurs sont en production :

<configuration>

    <system.web>

          <deployment retail="true"/>

    </system.web>

</configuration>

Ce paramétrage a deux effets :

  • Rétablir les performances optimales d’ASP.NET (Compilation, cache des ressources AXD, gestion mémoire, timeouts des pages) qui étaient réduites par le mode debug
  • Et purement et simplement désactiver le debug=true, la trace ASP.NET et les messages d’erreurs ASP.NET complets

Ceci quelque soient les paramétrages mis dans les Web.config.

 

Je vous conseille d’appliquer ce paramétrage systématiquement dans la production.

Et justement, par curiosité, avez-vous testé de rechercher si vous aviez du debug="true" sur vos serveurs ?

findstr /S " debug=" *.*

 

findstrA

 

A bientôt,

Sebastien,

-

deployment Element - http://msdn.microsoft.com/en-us/library/ms228298.aspx

When retail is set to true, ASP.NET disables certain configuration settings such as trace output, custom errors, and debug capabilities.

Don’t run production ASP.NET Applications with debug=”true” enabled - http://weblogs.asp.net/scottgu/archive/2006/04/11/Don_1920_t-run-production-ASP.NET-Applications-with-debug_3D001D20_true_1D20_-enabled.aspx

Premiers pas avec WinDbg

Pour faciliter la prise en main de cet outil légendaire et pour que vous puissiez tester vous même, je suis parti avec le Starter Kit "BlogEngine” (http://www.asp.net/community/projects/) ; Vous pourrez ainsi prendre les dumps, les ouvrir et utiliser WinDbg en suivant les étapes ci-dessous.

J’ai donc copié les sources du Starter Kit dans le répertoire "C:\inetpub\wwwroot" :BlogEngineDansWWWROOT 

Ensuite, dans la console IIS, j’ai créé une application pour ce répertoire en choisissant le pool d’application "Classic .NET AppPool" :ConvertToApplication

ApplicationIIS

BlogEngineDansLeNavigateur

C’est bon, l’application tourne, nous pouvons passer aux choses sérieuses…

 

Nous retrouvons le processus w3wp.exe avec le gestionnaire de taches, "process explorer" ou la commande suivante :

tasklist /FI "IMAGENAME eq w3wp.exe"

TaskList

La prise de dumps peut se faire facilement avec adplus. Voici toutes les informations pour ce faire - Billet précédent : Plan d‘action pour la capture des informations lors d’un problème de production IIS

Pour nos tests aujourd’hui, je fais un simple clic droit sur processus dans le gestionnaire de taches puis "Create Dump File"

GestionnaireDeTachesCreteDumpFile

image image

Ouvrons le dump par un double-clic ou en lançant WinDbg puis un glisser/déplacer du fichier .DMP dans l’interface de WinDbg… Un mot : Magnifique !!

WInDbg

 

Informations disponibles

 InformationsDansWinDbg

Nous avons entre les mains une photographie de la mémoire utilisée par le processus. Au premier coup d’oeil, nous avons les informations suivantes

  • La version exacte de Windows : Windows 7, build 7100 en 64bits
  • Le jour et l’heure de la prise de dump : jeudi 18 juin à 14h10
  • Le système d’exploitation tourne depuis 2 jours et 23 heures (System Uptime)
  • Le processus est démarré depuis 13min et 9 secondes

Symboles

L’une des premières actions à faire est de paramétrer le serveur de symboles. En deux mots : Les symboles sont les fichiers .PDB donnant la correspondance entre les adresses mémoires et les noms des fonctions d’une dll ou d’un exécutable. WinDbg peut les utiliser pour nous afficher les piles d’appels avec les noms des fonctions plutôt que des adresses mémoire brutes : Appréciable est un adjectif faible pour décrire cet avantage :-)

Dans la zone de texte, en bas de l’interface, il vous suffit d’entrer

.sympath SRV*c:\symbols*http://msdl.microsoft.com/download/symbols

pour spécifier le serveur public de symboles Microsoft : http://msdl.microsoft.com/symbols/. Dans l’exemple ci-dessus, je donne le crépertoire "c:\symbols" comme emplacement pour une copie locale des ces fichiers de symboles. WinDbg va donc pouvoir télécharger et stocker ces fichiers pour une utilisation déconnectée.

.reload

      Permet de charger les symboles et de les rapatrier en local s’il ne sont pas présents.

SymbolesCaptureDEcran

Modules

Sans commencer maintenant un débogage poussé, nous pouvons facilement connaitre quelle sont toutes les dlls chargées par le processus et obtenir des informations très précises sur chacune d’elles comme la date de build, le numéro de version, le chemin de la dll, etc…

L’affichage de toutes les dlls se fait par

lm

Modules

Nous obtenons les informations sur une dll avec

lmvm <<NomModule>>

 LMVM

 

Ce n’est que le début. Nous continuons dans le prochain billet.

Sebastien.

>>> Suite : Debogage .NET avec WinDbg et SOS - Threads

"Deboguer ce dump" dans le menu contextuel de l’explorateur Windows

Voici un petit raccourci bien pratique lorsque vous avez à ouvrir des dumps avec WinDbg :

Windows Registry Editor Version 5.00

; Ajout de la fonctionnalité d’ouverture en double clic d’un dump par WinDbg dans le menu contextuel de l'explorateur Windows
; Cette fonctionnalité est ajouté pour les fichiers .DMP

[HKEY_CLASSES_ROOT\.dmp]
@="SB-DebugDumpFile"

[HKEY_CLASSES_ROOT\SB-DebugDumpFile]

[HKEY_CLASSES_ROOT\SB-DebugDumpFile\DefaultIcon]
@="c:\\debuggers\\windbg.exe"

[HKEY_CLASSES_ROOT\SB-DebugDumpFile\Shell]

[HKEY_CLASSES_ROOT\SB-DebugDumpFile\Shell\SB-DebugThisDump]
@="-> Deboguer ce dump"

[HKEY_CLASSES_ROOT\SB-DebugDumpFile\Shell\SB-DebugThisDump\Command]
@="\"C:\\debuggers\\windbg\" -z \"%1\""

A noter, que j’utilise le chemin "c:\debuggers\windbg.exe" ; N’oubliez pas de le changer si vous avez les outils de débogage dans un autre répertoire.

Maintenant, un double clic sur un dump suffit pour l’ouvrir dans WinDbg !

A bientôt,

Sebastien.

Plan d‘action pour la capture des informations lors d’un problème de production IIS

Je fais suite à mon précédent billet sur l’introduction sur le débogage en production afin de vous donner plus de précisions sur la collecte d’informations lors de l’apparition d’un problème en production.

 

Dans tous les cas se présentant à vous, je vous conseille de récupérer des serveurs Web les logs IIS, les logs HTTPErr et vos logs applicatifs. Vous trouverez les explications dans le billet Données à récolter pour un travail de surveillance ou d'investigation sur un serveur Web. Ces logs nous permettent de vérifier la charge utilisateur, les temps de réponses, les erreurs HTTP : ce sont des informations précieuses à relier avec le comportement observé de l’application. Aussi, si vous avez la possibilité de prendre un log Perfmon, cela constitue un apport d’informations supplémentaires.

 

D’un point de vue système, je résumerais les problématiques de production en deux comportements distincts :

  • Hang =  Etat de blocage "deadlock" (avec ou sans 100% CPU) ou d’attente "hang" du processus
  • Crash = Arrêt "crash" du processus

La prise de dumps peut se faire par le script Adplus. Voici comment faire : Procédures de prises de dumps pour un serveur Web

D’une manière générale, vous pouvez utiliser Adplus pour les prises de dumps/logs pour tous les processus et pas pour IIS seulement. Les paramètres les plus utiles sont :

-p –> Spécifie le PID du processus
-pn –> Donne le nom du processus (plutôt que son PID)
-o –> Renseigne le répertoire de sortie pour la création du dumps

Ainsi, pour une prise de dumps de plusieurs processus et la création des dumps dans un répertoire particulier, la commande ressemble à

cscript.exe adplus.vbs -pn w3wp.exe -pn inetinfo.exe -crash -o c:\temp -quiet

Deux autres paramètres sont utiles :

-NoDumpOnFirst –> Permet de ne pas surcharger le débogueur (et donc le serveur) qui prend un mini dump pour chaque exception de première chance. Logue toutes les exceptions levées
-NoDumpOnSecond –> Aucune prise de dump mais seulement le remplissage d’un log permettant de visualiser toutes les exceptions levées

Pour vous donner plus de précisions, en deux mots, il existe deux types d'exceptions : première chance et seconde chance.

  • Une exception de première chance peut être interceptée par votre application par un try/catch ou un gestionnaire d’exceptions global
  • Si cette exception n’est pas gérée, elle est à nouveau levée mais comme une exception de seconde chance. Dans ce cas, seul un débogueur peut l’intercepter et si aucun débogueur n’est attaché, l’application se ferme (autrement dit : le processus crashe).

Par exemple, pour une prise d’un dump lors du crash de l’application "WpfApplication1.exe", vous pouvez utiliser "cscript.exe adplus.vbs -crash -pn WpfApplication1.exe -o c:\temp -quiet".

Adplus.vbs

Cette commande a pour effet de brancher le débogueur "cdb.exe" au processus WpfApplication1.exe. Le debogueur logue toutes les exceptions dans un fichier pendant l’exécution du processus et si une exception de seconde chance est levée (provoquant l’arrêt du processus), un dump est pris.

cdb.exe

Le répertoire de sortie contient les fichiers suivants :

  • Process_List.txt - Liste des processus s’éxécutant sur la machine au moment du lancement de la commande
  • un ou plusieurs fichiers du type PID-2852__WPFAPPLICATION1.EXE__Date_05-13-2009__Time_15-46-0303.log - Log des exceptions levées par l’application
  • un ou plusieurs fichiers du type PID-2852__WPFAPPLICATION1.EXE__2nd_chance_NET_CLR__full_0a70_2009-05-13_15-46-10-421_0b24.dmp - Dump du processus

RepertoireSortie

A noter que le répertoire et les fichiers contiennent la date et l’heure exacte de la prise de dumps. Dans l’exemple précédent, il s’agit du 2009-05-13 pour le 13 mai et 15-46-10 pour 15h46m.

 

Nous avons donc maintenant toutes les données nécessaires (en particulier les dumps) pour déboguer à tête reposée. Ouvrons le dump avec Windbg :-)

Sebastien.

>>> Suite : Premiers pas avec WinDbg

Déboguer une application en production... Mais pour quoi faire ?

Joli titre non ? En fait, je souhaite vous faire part de mon expérience sur le débogage .NET en production. "Débogage .NET en production" par opposition au débogage pas à pas dans Visual Studio.

En effet, il n’est pas toujours possible de "dégainer" son Visual Studio dans les environnements comme la recette ou la production. Et même, dans certains cas ce n’est pas adapté car la problématique à déboguer nécessite une certaine charge utilisateurs.

 

Quels sont les différents environnements ?

Chaque environnement a ses caractéristiques propres ; Pour simplifier, nous pouvons dire que nous avons les types suivants :

  • Développement
    • Pas de charge utilisateurs
    • Problèmes unitaires reproductibles
    • Serveurs et réseau différents de la production
    • Débogage invasif ou pas à pas autorisé
  • Test/Recette
    • Pas forcement identique à la production
    • Débogage invasif autorisé mais pas utilisable dans tous les cas (exemple : tests de charge)
    • Outils de débogage possiblement non autorisés
  • Production
    • Environnement à forte charge / disponibilité
    • Outils de débogage souvent non autorisés

 

Quelles problématiques pouvons-nous rencontrer ?

Malgré tous les efforts mis en œuvre dans la phase de développement et de tests, il est possible d’observer des comportements anormaux de l’application mise en production. Voici les plus fréquents :

  • Apparition de messages d’erreurs aux utilisateurs alors qu’aucune exception n’est loguée (Exceptions non gérées)
  • Un état de blocage ou d’attente qui empêche le traitement des requêtes (deadlock ou hang du processus d’exécution W3WP.EXE)
    • Attente sur des ressources externes
    • Locks
    • Deadlocks
  • 100% CPU du processus d’exécution W3WP.EXE
    • Boucle infini
    • Serveur surchargé
    • Nombre élevé d’exceptions
    • Utilisation du processeur par le Garbage Collector
  • Un arrêt du processus d’exécution inexpliqué (crash de W3WP.EXE)
    • Exceptions non gérées
    • Exceptions internes à la CLR
    • Exceptions de composants COM
    • Recyclages

 

Comment procéder au débogage ?

Ces problèmes, n’ont pas forcement été identifiés au préalable lors du développement et de la phase de tests et le plus souvent ne sont pas reproductibles sur une autre plateforme que la production. Comment faire pour investiguer et trouver l’origine du problème ?

Le plan d’action le plus souvent utilisé est en 3 étapes :

  • Capture d’informations
  • Rétablissement de la production
  • Analyse ultérieure

>>> Suite : Plan d‘action pour la capture des informations lors d’un problème de production IIS

Gestion des versions des assemblies du GAC par ASP.NET

Le GAC nous permet de partager des assemblies entre plusieurs applications ASP.NET. La gestion des versions des ces assemblies en est aussi facilitée à condition de bien comprendre la différence entre Assembly version et File version.

Allez hop... Petite explication !

 

Lors de la mise à jour d’une assembly nous avons le choix entre changer l’Assembly version et la File version

ProprieteProjet Versions

 

Pour illustrer les deux possibilités, prenons l’exemple de plusieurs sites Web utilisant une même assembly dans le GAC (Assembly V1.0.0.0 / File V1.0.0.0). Les Web.config des applications ASP.NET utilisent :

Assemblies

 

Possibilité 1 : Modification mineure = Correctif

Nous mettons à jour cette assembly dans la GAC en gardant la même Assembly version mais en changeant la File version (Assembly V1.0.0.0 / File V1.1.0.0)

  • Dans la GAC, nous avons maintenant seulement la nouvelle assembly (Assembly V1.0.0.0 / File V1.1.0.0). Cette nouvelle assembly remplace la précédente car nous avons la même Assembly version. En effet, dans la GAC, deux assemblies du même nom ne peuvent pas avoir la même Assembly version
  • Les applications en cours d’exécution utilisent toujours la version précédente (Assembly V1.0.0.0 / File V1.0.0.0) car pour elles rien ne leur indique un changement (le web.config n’a pas changé)
  • Si l’appDomain redémarre : c'est-à-dire si l’application Web redémarre suite à une modification du Web.config par exemple, la nouvelle version n’est toujours pas chargée car l’ancienne est en mémoire dans le processus W3WP.EXE qui n’a pas redémarré
  • Si le processus W3WP.EXE redémarre suite à un recyclage, un crash, alors la CLR va chercher dans le GAC la dll et prend la nouvelle version

 

Possibilité 2 : Modification majeure = Nouvelle version

Nous mettons à jour cette assembly dans la GAC en changeant le numéro de l’Assembly version (Assembly V2.0.0.0)

  • Dans la GAC, nous voyons maintenant les deux assemblies

GAC

  • Les applications en cours d’exécution utiliseront toujours l’Assembly version 1.0.0.0 tant que le web.config n’est pas modifié pour mentionner le changement de version :

AssemblyVersion

  • Comme modifions le Web.config, l’appDomain redémarre (sans redémarrage du processus W3WP.EXE) et prend en charge la nouvelle version

 

Conclusion en images :-)

Changement File version
Redémarrage W3WP.EXE Prise en compte de la nouvelle version
Modification Web.config
Aucun changement

Changement Assembly version
Redémarrage W3WP.EXE Aucun changement
Modification Web.config Prise en compte de la nouvelle version

 

J’espère que ces éclaircissements vous aideront dans vos déploiements en production. A bientôt,

Sebastien.

Utilisez TinyGet pour effectuer rapidement une charge simple sur un serveur Web

L’outil TinyGet n’a pas vocation remplacer un outil de test de charge blogs.mais il s’avère extrêmement pratique dans certains cas : Je vous conseille de le garder dans vos outils favoris. Il vous permettra de simuler une charge basique sur une application Web en quelques lignes de commandes.

Les avantages sont les suivants :

  • Support du multithread pour effectuer des requêtes simultanées
  • Support des itérations
  • Paramétrage possible pour le type authentification, la version HTTP, les headers, le contenu de la requête, l’utilisation d’un certificat client
  • Attente d’un contenu particulier pour la réponse, le code de retour

Exemple de trois threads simultanés effectuant dix requêtes chacun

tinyget.exe -srv:sbovo02 -uri:/default.aspx -status:200 -threads:3 -loop:10

-threads donne le nombre de threads lancés simultanément.

-loop indique le nombre d’itérations

-status représente le code de retour attendu. Si TinyGet n’obtient pas ce code, un message est affiché dans la console

ConsoleIISRequests

Exemple d’une requête attendant un contenu particulier

tinyget.exe -srv:sbovo02 -uri:/contenu.aspx -testContainString:"Texte"

-testContainString permet de mentionner quelle chaine (contenu dans le body de la réponse) nous attendons

 TinyGetTexte

Exemple d’une requête avec l’affichage de la trace TinyGet ou les headers de la réponse

tinyget.exe -srv:sbovo02 -uri:/stylesheet.css -trace

TinyGetTrace

tinyget.exe -srv:sbovo02 -uri:/stylesheet.css -headers

TinyGetHeaders

Pour télécharger l’outil et l’aide associée : IIS 6.0 Resource Kit Tools - http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1352

Je pense que vous avez bien vu l’utilité de cet outil soit pour simuler une charge soit pour vérifier les réponses d’une application Web.

Bonne utilisation !

Sebastien.

Comment lister les mises à jour (Hotfixes) Windows ?

Bonjour,

Dans le même esprit que mon précédent post, voici comment vous pouvez obtenir la liste des mises à jour ou hotfixes Windows en ligne de commande :

systeminfo

systeminfo

ResultatSysteminfo

 

Et ainsi, nous pouvons facilement rechercher si une mise à jour particulière a bien été installé :

systeminfo | findstr KB961367

systeminfo_findstr

 

A noter, que "SystemInfo" donne une multitude d’informations supplémentaires qui peuvent vous être utile :

  • OS Name
  • OS Version
  • Processor(s)
  • Total Physical Memory
  • Available Physical Memory
  • Domain
  • Logon Server
  • etc…

 

A bientôt,

Sebastien.

More Posts Next page »
Page view tracker