Premièrement, je m'excuse de la longueur de cette question.
Je suis l'auteur d' IronScheme . Récemment, j'ai travaillé dur pour émettre des informations de débogage correctes, afin de pouvoir utiliser le débogueur .NET «natif».
Bien que cela ait été en partie réussi, je rencontre des problèmes de démarrage.
Le premier problème est lié au pas.
Comme Scheme est un langage d'expression, tout a tendance à être entouré de parenthèses, contrairement aux principaux langages .NET qui semblent être basés sur des instructions (ou des lignes).
Le code d'origine (Scheme) ressemble à:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
J'ai volontairement disposé chaque expression sur une nouvelle ligne.
Le code émis se transforme en C # (via ILSpy) ressemble à ceci:
public static object ::baz(object x)
{
if (x == null)
{
return x;
}
if (x is Cons)
{
return Builtins.Car(x);
}
return #.ironscheme.exceptions::assertion-violation+(
RuntimeHelpers.False, "nooo", Builtins.List(x));
}
Comme vous pouvez le voir, assez simple.
Remarque: Si le code était transformé en une expression conditionnelle (? :) en C #, le tout ne serait qu'une étape de débogage, gardez cela à l'esprit.
Voici la sortie IL avec les numéros de source et de ligne:
.method public static object '::baz'(object x) cil managed
{
// Code size 56 (0x38)
.maxstack 6
.line 15,15 : 1,2 ''
//000014:
//000015: (define (baz x)
IL_0000: nop
.line 17,17 : 6,15 ''
//000016: (cond
//000017: [(null? x)
IL_0001: ldarg.0
IL_0002: brtrue IL_0009
.line 18,18 : 7,8 ''
//000018: x]
IL_0007: ldarg.0
IL_0008: ret
.line 19,19 : 6,15 ''
//000019: [(pair? x)
.line 19,19 : 6,15 ''
IL_0009: ldarg.0
IL_000a: isinst [IronScheme]IronScheme.Runtime.Cons
IL_000f: ldnull
IL_0010: cgt.un
IL_0012: brfalse IL_0020
IL_0017: ldarg.0
.line 20,20 : 7,14 ''
//000020: (car x)]
IL_0018: tail.
IL_001a: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_001f: ret
IL_0020: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_0025: ldstr "nooo"
IL_002a: ldarg.0
IL_002b: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
//000021: [else
//000022: (assertion-violation #f "nooo" x)]))
IL_0030: tail.
IL_0032: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_0037: ret
} // end of method 'eval-core(033)'::'::baz'
Remarque: pour empêcher le débogueur de simplement mettre en évidence la méthode entière, je fais le point d'entrée de la méthode sur une seule colonne de large.
Comme vous pouvez le voir, chaque expression correspond correctement à une ligne.
Maintenant, le problème avec le pas à pas (testé sur VS2010, mais même problème / similaire sur VS2008):
Ceux-ci ne sont IgnoreSymbolStoreSequencePoints
pas appliqués.
- Appelez baz avec un argument nul, cela fonctionne correctement. (nul? x) suivi de x.
- Appelez baz avec Cons arg, cela fonctionne correctement. (nul? x) puis (paire? x) puis (voiture x).
- Appelez baz avec un autre argument, cela échoue. (nul? x) puis (paire? x) puis (voiture x) puis (assertion-violation ...).
Lors de l'application IgnoreSymbolStoreSequencePoints
(comme recommandé):
- Appelez baz avec un argument nul, cela fonctionne correctement. (nul? x) suivi de x.
- Appelez baz avec Contre arg, ça échoue. (nul? x) puis (paire? x).
- Appelez baz avec un autre argument, cela échoue. (nul? x) puis (paire? x) puis (voiture x) puis (assertion-violation ...).
Je trouve également dans ce mode que certaines lignes (non représentées ici) ne sont pas correctement mises en évidence, elles sont décalées de 1.
Voici quelques idées quelles pourraient en être les causes:
- Tailcalls confond le débogueur
- Le chevauchement d'emplacements (non affichés ici) confond le débogueur (il le fait très bien lors de la définition d'un point d'arrêt)
- ????
Le deuxième problème, mais également sérieux, est que le débogueur ne parvient pas à casser / atteindre les points d'arrêt dans certains cas.
Le seul endroit où je peux faire en sorte que le débogueur se brise correctement (et de manière cohérente) est au point d'entrée de la méthode.
La situation s'améliore un peu lorsqu'elle IgnoreSymbolStoreSequencePoints
n'est pas appliquée.
Conclusion
Il se peut que le débogueur VS soit tout simplement bogué :(
Références:
Mise à jour 1:
Mdbg ne fonctionne pas pour les assemblys 64 bits. Donc c'est fini. Je n'ai plus de machines 32 bits pour le tester. Mise à jour: je suis sûr que ce n'est pas un gros problème, est-ce que quelqu'un a une solution? Edit: Oui, idiot, lancez simplement mdbg sous l'invite de commande x64 :)
Mise à jour 2:
J'ai créé une application C # et essayé de disséquer les informations de ligne.
Mes découvertes:
- Après toute
brXXX
instruction, vous devez avoir un point de séquence (s'il n'est pas valide aka '#line hidden', émettez unnop
). - Avant toute
brXXX
instruction, émettez un '#line hidden' et unnop
.
L'application de cela ne résout cependant pas les problèmes (seul?).
Mais en ajoutant ce qui suit, donne le résultat souhaité :)
- Ensuite
ret
, émettez un '#line hidden' et unnop
.
Ceci utilise le mode où IgnoreSymbolStoreSequencePoints
n'est pas appliqué. Lorsqu'elle est appliquée, certaines étapes sont toujours ignorées :(
Voici la sortie IL lorsque ci-dessus a été appliquée:
.method public static object '::baz'(object x) cil managed
{
// Code size 63 (0x3f)
.maxstack 6
.line 15,15 : 1,2 ''
IL_0000: nop
.line 17,17 : 6,15 ''
IL_0001: ldarg.0
.line 16707566,16707566 : 0,0 ''
IL_0002: nop
IL_0003: brtrue IL_000c
.line 16707566,16707566 : 0,0 ''
IL_0008: nop
.line 18,18 : 7,8 ''
IL_0009: ldarg.0
IL_000a: ret
.line 16707566,16707566 : 0,0 ''
IL_000b: nop
.line 19,19 : 6,15 ''
.line 19,19 : 6,15 ''
IL_000c: ldarg.0
IL_000d: isinst [IronScheme]IronScheme.Runtime.Cons
IL_0012: ldnull
IL_0013: cgt.un
.line 16707566,16707566 : 0,0 ''
IL_0015: nop
IL_0016: brfalse IL_0026
.line 16707566,16707566 : 0,0 ''
IL_001b: nop
IL_001c: ldarg.0
.line 20,20 : 7,14 ''
IL_001d: tail.
IL_001f: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_0024: ret
.line 16707566,16707566 : 0,0 ''
IL_0025: nop
IL_0026: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_002b: ldstr "nooo"
IL_0030: ldarg.0
IL_0031: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
IL_0036: tail.
IL_0038: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_003d: ret
.line 16707566,16707566 : 0,0 ''
IL_003e: nop
} // end of method 'eval-core(033)'::'::baz'
Mise à jour 3:
Problème avec le 'semi-fix' ci-dessus. Peverify signale des erreurs sur toutes les méthodes en raison de l' nop
après ret
. Je ne comprends pas vraiment le problème. Comment une nop
vérification peut-elle interrompre après un ret
. C'est comme du code mort (sauf que ce n'est même PAS du code) ... Eh bien, l'expérimentation continue.
Mise à jour 4:
De retour à la maison maintenant, supprimé le code «invérifiable», fonctionnant sur VS2008 et les choses sont bien pires. Peut-être qu'exécuter du code non vérifiable pour un bon débogage pourrait être la réponse. En mode «release», toutes les sorties seraient toujours vérifiables.
Mise à jour 5:
J'ai maintenant décidé que mon idée ci-dessus est la seule option viable pour le moment. Bien que le code généré ne soit pas vérifiable, je n'en ai pas encore trouvé VerificationException
. Je ne sais pas quel sera l'impact sur l'utilisateur final de ce scénario.
En prime, mon deuxième problème a également été résolu. :)
Voici un petit screencast de ce que j'ai fini avec. Il atteint les points d'arrêt, fait un pas correct (entrée / sortie / dépassement), etc. Dans l'ensemble, l'effet désiré.
Cependant, je n'accepte toujours pas cela comme moyen de le faire. Cela me semble trop piraté. Avoir une confirmation sur le vrai problème serait bien.
Mise à jour 6:
Je viens d'avoir le changement pour tester le code sur VS2010, il semble y avoir des problèmes:
Le premier appel ne se déroule désormais pas correctement. (assertion-violation ...) est touché. D'autres cas fonctionnent bien.Certains anciens codes émettaient des positions inutiles. Suppression du code, fonctionne comme prévu. :)- Plus sérieusement, les points d'arrêt échouent à la deuxième invocation du programme (en utilisant la compilation en mémoire, le vidage de l'assembly dans un fichier semble rendre les points d'arrêt heureux à nouveau).
Ces deux cas fonctionnent correctement sous VS2008. La principale différence est que sous VS2010, l'application entière est compilée pour .NET 4 et sous VS2008, compile vers .NET 2. Les deux exécutant 64 bits.
Mise à jour 7:
Comme mentionné, j'ai mdbg fonctionnant sous 64 bits. Malheureusement, il a également le problème de point d'arrêt où il ne parvient pas à se briser si je réexécute le programme (cela implique qu'il est recompilé, donc pas en utilisant le même assembly, mais toujours en utilisant la même source).
Mise à jour 8:
J'ai déposé un bogue sur le site MS Connect concernant le problème du point d'arrêt.
Mise à jour: fixe
Mise à jour 9:
Après une longue réflexion, le seul moyen de rendre le débogueur heureux semble être de faire SSA, de sorte que chaque étape peut être isolée et séquentielle. Je n'ai pas encore prouvé cette notion. Mais cela semble logique. De toute évidence, le nettoyage des temps de SSA interrompra le débogage, mais cela est facile à basculer, et les laisser n'a pas beaucoup de frais généraux.
nop
s, le pas à pas échoue (je le vérifierai à nouveau pour être sûr). C'est un sacrifice que je devrais faire. Ce n'est pas comme si VS pouvait même fonctionner sans droits d'administrateur :) Btw en utilisant Reflection.Emit via le DLR (un premier branchement très piraté).