[VS2010] A propos de "Just My Code" et de son Influence sur le Debugger

This article is available in english.

La fonctionnalité “Just My Code” est présente depuis un bon moment dans Visual Studio. Depuis Visual Studio 2005, en fait. Et il est facile d'en manquer quelques subtilités.

Grossièrement, cette fonctionnalité permet de ne montrer que votre code, et principalement les assemblies qui sont en mode Debug (pas optimisées) et qui ont des symboles de Debug (fichiers pdb). La plupart du temps c'est intéressant lorsque l'on Debug du code relativement simple.

Mais lorsque l'on a à traiter des problèmes plus complexes où l'on veut intercepter les exceptions qui peuvent être relancées depuis des parties du code qui ne sont pas "Just My Code", alors il devient nécessaire de la désactiver.

Si vous êtes un développeur .NET expérimenté, il y a de grandes chances que vous l'ayez désactivée parce que cela vous a ennuyé à un moment. Je l'avais fait aussi, jusqu'à récemment.


Gestion des Exceptions par le Debugger

La fonctionnalité “Just my Code” (que j'appellerais JMC pour le reste de l'article) change quelques comportements dans la manière dont le debugger gère les exceptions.

Si elle est activée, on peut alors voir deux colonnes dans le menu “Debug / Exceptions” :

  • Thrown, qui veut dire que le debugger va s'arrêter au moins profond rethrow dans la stack qui a généré l'exception
  • User-unhandled, qui veut dire que le debugger va s'arrêter si l'exception n'a pas été gérer par un gestionnaire d'exception dans du code utilisateur de la stack courante.

Si elle est activée, alors la même boite de dialogue n'affiche qu'une seule colonne :

  • Thrown, qui fait en sorte que le debugger s'arrête dès que l'exception est lancée


Vous aurez probablement remarqué une différence de taille dans la manière dont le debugger interprète l'option "Thrown".  Pour être un peu plus clair à propos de cette différence, regardons cet exemple de code :

1
2
3
4
5
6
7
8
9
10
11
12
13
    static void Main(string[] args) 
{
try
{
var t = new Class1();
t.Throw();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Exécutable Principal, en configuration Debug avec les symboles de debug activés

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public class Class1 
{
public void Throw()
{
try
{
Throw2();
}
catch (Exception e)
{
throw;
}
}
private void Throw2()
{
throw new InvalidOperationException("Test");
}
}
Assembly différente, en configuration Debug, mais sans Symboles de Debug.

Si l'on exécute ce code avec le debugger, l'option JMC activée et la colonne "Thrown" cochée pour l'exception “InvalidOperationException”, voici la stack trace affichée pour l'exception :

    NotMyCode.dll!NotMyCode.Class1.Throw() + 0x51 bytes
> MyCode.exe!MyCode.Program.Main(string[] args = {string[0]}) Line 15 + 0xb bytes


Et voici la même chose avec la fonctionnalité JMC désactivée :

    NotMyCode.dll!NotMyCode.Class1.Throw2() + 0x46 bytes
NotMyCode.dll!NotMyCode.Class1.Throw() + 0x3d bytes
> MyCode.exe!MyCode.Program.Main(string[] args = {string[0]}) Line 15 + 0xb bytes

Vous aurez remarqué l'impact de "moins profond rethrow dans la stack", et cela veut dire que si vous activez le JMC, vous n'aurez pas l'emplacement original de l'exception.

Vous vous demandez peut-être pourquoi il est intéressant d'avoir l'emplacement original de l'exception. C'est une technique de debuggage communément utilisée pour trouver des problèmes pointus, liés à des exceptions levées très loin dans du code qui n'est pas à nous, et on peut trouver une exception comme TypeInitializerException parmi celles ci. Il peut être très utile de s'arrêter à l'endroit de l'exception originale pour avoir le bon contexte, ou la stack qui a mené à cette exception.

Récemment, j'ai utilisé cette technique de "Break on all exceptions" sans JMC pour corriger un problème de chargement d'assemblies 32 Bits dans un CLR 64 Bits. On ne sait pas exactement quelle exception on recherche, et avoir le JMC qui cache des exceptions n'est pas de la plus grande aide.

Aussi, pour être complet, un debugging plus intense se fait avec WinDBG et l'extension SOS (et un bon aide mémoire sur SOS). Mais c'est un autre sujet.


L'expérience de Debugging de Step Into avec JMC

Si vous avez lu jusque là, vous vous demandez peut-être l'intérêt d'activer JMC. Après tout, vous pouvez gérer vous même votre code, et avec un peu d'expérience il est facile d'ignorer mentalement les parties de la stack qui ne sont pas les vôtres. En fait, la couleur grise des parties de code qui n'ont pas de symboles de debugging aident beaucoup.

Et bien, voici un bon exemple de l'utilisation de JMC : La fonctionnalité "Step Into" du debugger. Une fonctionnalité très simple qui permet de faire du pas à pas durant le debuggage du code.

Si vous être en cours de debugging, vous pourrez entrer dans le code qui est appelé à la lige suivante et si cela est possible, voir ce qui est dedans.

Pour expliquer cela, prenons cet exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    static void Main(string[] args) 
{
var myObject = new MyObject();

Console.WriteLine(myObject);
}

class MyObject
{
public override string ToString()
{
return "My object";
}
}

Ce programme très simple va utiliser le fait que Console.WriteLine va appeler la méthode virtuelle ToString sur l'objet qui lui est passé en paramètre.

Le but de cet exemple est de faire que "My Code" (Main) appelle un morceau de "Not My Code" (Console.WriteLine) qui lui même appelle "My Code" (MyObject.ToString). Facile.

Si l'on exécute ce code avec le JMC désactivé, et si l'on essaye de faire "Step Into" dans Console.WriteLine, on va en fait sauter l'appel. Ce n'est pas très utile si l'on veut debugger notre code, celui qui est dans le MyObject.ToString.

Un exemple très concret de manque de "Step Into" peut être trouvé lors de l'utilisation de proxies comme ceux trouvés dans Spring.NET ou Castle's DynamicProxy, puisqu'ils interceptent les appels avec du code sans symboles de Debug. Il n'est alors pas possible de faire du "Step Into" dans les méthodes des objets qui ont été "proxisés" pour faire de l'AOP, par exemple.

En revanche, si l'on active JMC, on peut faire un "Step Into" dans notre propre code, même si la vraie méthode suivante n'est pas une des nôtres et qu'elle est ignorée.

Mots de la fin

Utiliser JMC dans ce contexte est particulièrement utile et naturel, ce qui fait du sens de l'avoir activée par défaut dans VS20xx. C'est une fonctionnalité qui a été présente depuis tellement longtemps que j'en avait manqué son utilité originale. Cette option m'avait très certainement empêché de faire du debugging efficacement et je l'avais cataloguée comme fonctionnalité "Junior". J'avais eu tort...

Enfin, dans Visual Studio 2010, le JMC a été quelque peu amélioré, puisqu'il est maintenant possible d'y accéder très facilement par le panneau "Show Calls View" d'IntelliTrace.

Il est temps de passer à Visual Studio 2010, les gens ! :)

Publié lundi 5 juillet 2010 22:24 par jay
Classé sous , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour assigner des droits Full Control à un groupe défini par Blog Technique de Romelard Fabrice le 04-30-2017, 09:22

- SharePoint 20XX: Script PowerShell pour exporter en CSV toutes les listes d’une ferme pour auditer le contenu avant migration par Blog Technique de Romelard Fabrice le 03-28-2017, 17:53

- Les pièges de l’installation de Visual Studio 2017 par Blog de Jérémy Jeanson le 03-24-2017, 13:05

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22