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*https://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 :-)