Production debugging : 100% CPU dans une application ASP.NET

Le but de cet article est de donner deux méthodes (la simple, l’avancée) permettant de trouver la cause d’un 100% CPU qui ne se produit que sur un environnement de production sur lequel nous n’avons pas toute la panoplie d’outils nous permettant de trouver rapidement la cause.

(Les ecrans qui suivent sont sur Windows 2008 R2)

Diagnostiques de base

Avant de passer dans les diagnostiques avancés, nous allons commencer par essayer d’isoler une URL qui génère ce probleme.

A l’aide de l’outil d’administration de IIS, au niveau serveur, on peut afficher la liste des processus généré par IIS.

Pour chaque process, il est possible de voir la liste des Requetes.

Et de trouver ainsi celle qui pose probleme dans un cas particulier.

A l’aide de ces requetes on a un déjà une bonne idée d’ou peut se situer le probleme.

Diagnostiques avancés

Connaitre la liste des requetes ne résoud pas pour autant le probleme dans tous les cas (cas ou cela ne depend pas de la requete mais d’un evenement exterieur…). Et il faudrait au minimum savoir quel bout de code génère ce 100% CPU. S’il n’est pas facile de le connaitre de prime abord, il est possible à l’aide de la stack trace des thread en cours d’execution de savoir approximativement ou se trouve le probleme.

Le principe qui va etre appliqué pour ce diagnostique avancé, sera de prendre des images du processus en cours et de regarder dans quel partie du code se trouve le programme. A l’aide de plusieurs de ces images prises a des moments différents, on va pouvoir grossierement déterminer ou se trouve le bout de code en cause.

Prérequis

Il faut commencer par télécharger les debugging tools pour windows et les installer sur le serveur ou le problème se produit : (Voir plus bas une méthode par le gestionnaire de tache ne nécessitant pas ce prérequis sur le serveur)

http://msdn.microsoft.com/en-us/windows/hardware/gg463009.aspx

Une fois installé dans le dossier de destination vous devez avoir l’outils adplus.exe :

Récupération du Process ID

Repérer le PID du processus ASP.NET qui pose problème à l’aide du gestionnaire de taches (Menu View > Select columns) :

Enregistrement d’images mémoires (Dumps)

Nous allons utiliser adplus.exe pour générer un Dump complet du processus que nous pourrons analyser à l’aide de l’outil WinDbg.

Prenons comme exemple ou le processus w3wp.exe utilisant la majorité du CPU est le PID : 3376, et nous crérons un dossier c:\dumps qui contiendra l’ensemble des dumps du processus.

adplus.exe -hang -p 3376 -quiet -o c:\dumps

Patienter environ 10s après que le programme vous ait rendu la main.

Il vous faudra reproduire cette actions plusieurs fois lorsque le probleme se produit. Attention le process est mis en pause (freezer) pendant les quelques secondes du Dump mémoire et ne répondra donc plus aux requetes utilisateurs pendant cette durée.

Dump sans ADPlus

Si vous n’avez pas le temps d’installer ADPlus, vous pouvez tout de même effectué des dumps directement depuis le gestionnaire des taches (TaskManager). Pour cela, dans l’onglet process, faites un clic droit sur le process en question, puis Create Dump File.

Analyse d’une image mémoire (dump)

En plus de AdPlus, les debugging tools incluent l’outils WinDbg que vous trouverez dans votre menu démarrer.

Lancez cet outil. (attention à executer celui correspondant à votre architecture à debugger : x64 ou x86)

Ouvrez le Dump en utilisant : File > Open Crash Dump et en allant chercher le dump qui a été généré dans le dossier que vous avez fournit à adplus lors de l’étape précédente.

Pour qu’il soit capable de transformer des adresses dans des DLLs en Nom de méthode, vous allez devoir charger les symbols. Le plus simple est de laisser WinDbg le faire tout seul en lui indiquant le serveur de Microsoft adapté.

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

Adapter WinDbg aux diagnostiques de code .NET :

dans la fenêtre command :

.loadby sos clr

Maintenant il ne reste plus qu’a lister la pile des appels (call stack) de l’ensemble des threads pour récupérer l’emplacement d’execution à l’instant de ce dump mémoire.

Pour cela la commande ci-dessous a exécuter dans la fenêtre de commande va lister l’ensemble des piles d’appels dans les processus et threads en cours d’execution dans ce dump :

~* e !clrstack

Maintenant, il reste a partir de ces informations a en déduire le problème.

plus d’informations :

Sharepoint 2010 : WebPart Générique pour charger des Contrôles ASP.NET

Le modèle de création de WebPart Sharepoint poussé par Microsoft est de faire contenir dans une WebPart un contrôle ASP.NET. Le contenu du WebPart étant assez simple, on peut en faire un générique dont un paramètre permettra de définir le contrôle ASP.NET à charger.

L’intérêt, bien que limité, permet de créer des interfaces qui ne seront pas disponible directement à travers l’ajout de WebPart mais en connaissant le chemin du contrôle ASP.NET uniquement. Cela peut être pratique lorsqu’on a quelques contrôles ASP.NET de debug qu’on ne souhaite pas faire apparaitre dans la liste des WebPart disponibles.

Elle permettra aussi de déployer des contrôles (dépourvus de code behind) en uploadant des contrôles dans Sharepoint.

Il conviendra de vérifier que seuls les administrateurs aient le droit de modifier le chemin car dans le cas contraire cela pourrait entrainer une faille de sécurité.

[ToolboxItemAttribute(false)]
 public class LoadUserControl : Microsoft.SharePoint.WebPartPages.WebPart
 {
 [WebBrowsable(true),
 Category("WebPart Configuration"),
 WebPartStorage(Storage.Shared),
 FriendlyName("ASCX"),
 Description("Chemin du controle ASP.NET."),
 Personalizable(true)]
 public string ascx { get; set; }

 protected override void CreateChildControls()
 {
 if (!string.IsNullOrEmpty(ascx))
 {
 Control control = Page.LoadControl(ascx);
 Controls.Add(control);
 }
 }
 }

Exemple d’exploitation pour charger un contrôle :

<webParts>
 <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
 <metaData>
 <type name="MyNameSpace.LoadUserControl.LoadUserControl, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0000000000000000" />
 <importErrorMessage>$Resources:core,ImportErrorMessage;</importErrorMessage>
 </metaData>
 <data>
 <properties>
 <property name="Title" type="string">Titre publique de la WebPart</property>
 <property name="Description" type="string">My WebPart</property>
 <property name="ascx" type="string">~/_CONTROLTEMPLATES/Exemple/Exemple.ascx</property>
 </properties>
 </data>
 </webPart>
</webParts>

IIS Application WarmUP

Lorsque dans votre application ASP.NET vous souhaitez effectuer des actions a heures précises, vous ne pouviez jusqu’à présent le faire de façon fiable qu’en utilisant un Service Windows ou en utilisant le gestionnaire de taches planifiées.

La complexité de la solution “service windows” est la communication entre ce service et l’application ASP.NET. La solution que j’ai trouvé la plus fiable jusque la est l’utilisation du Remoting.

Mais, la solution la plus simple, était de mettre dans l’application ASP.NET un thread qui fournissait cette fonctionnalité. Je n’ai pas utilisé jusque là cette solution car lorsque l’application était recyclée et qu’aucun utilisateur ne se connectait au site internet l’action planifiée n’était pas déclenchée.

Récemment, je suis tombé sur l’Application WarmUP (Béta 1) de Microsoft. Ce produit a comme fonctionnalité principale de précharger l’application permettant ainsi une meilleure réactivité pour le premier utilisateur.

Il se pourrait bien que cette application me permette aussi de m’affranchir de la lourdeur de déployer un service Windows ou une tache planifiée !

Update 25/10/2010 : Après une série de tests, c’est concluant !