Em primeiro lugar, peço desculpas pela extensão desta pergunta.
Eu sou o autor do IronScheme . Recentemente, tenho trabalhado duro na emissão de informações de depuração decentes, para poder usar o depurador .NET 'nativo'.
Embora isso tenha sido parcialmente bem-sucedido, estou com alguns problemas iniciais.
O primeiro problema está relacionado ao pisar.
Como o Scheme é uma linguagem de expressão, tudo tende a ser colocado entre parênteses, ao contrário das principais linguagens .NET que parecem ser baseadas em declarações (ou linhas).
O código original (Esquema) se parece com:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
De propósito, expus cada expressão em uma nova linha.
O código emitido se transforma em C # (via ILSpy) se parece com:
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));
}
Como você pode ver, bem simples.
Nota: Se o código foi transformado em uma expressão condicional (? :) em C #, a coisa toda seria apenas uma etapa de depuração, lembre-se disso.
Aqui está a saída IL com os números de origem e linha:
.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'
Nota: Para impedir que o depurador simplesmente destaque todo o método, eu faço o ponto de entrada do método ter apenas 1 coluna de largura.
Como você pode ver, cada expressão é mapeada corretamente para uma linha.
Agora, o problema com o passo (testado no VS2010, mas o mesmo problema / semelhante no VS2008):
Estes IgnoreSymbolStoreSequencePoints
não são aplicados.
- Chame baz com argumento nulo, ele funciona corretamente. (nulo? x) seguido por x.
- Chame baz com Contras, funciona corretamente. (nulo? x) então (par? x) e depois (carro x).
- Chame baz com outro argumento, ele falha. (null? x) then (pair? x) then (car x) then (violação da asserção ...).
Ao aplicar IgnoreSymbolStoreSequencePoints
(conforme recomendado):
- Chame baz com argumento nulo, ele funciona corretamente. (nulo? x) seguido por x.
- Chame baz com Contras, ele falha. (nulo? x) e depois (par? x).
- Chame baz com outro argumento, ele falha. (null? x) then (pair? x) then (car x) then (violação da asserção ...).
Também acho neste modo que algumas linhas (não mostradas aqui) estão incorretamente destacadas, e são desativadas por 1.
Aqui estão algumas idéias sobre quais poderiam ser as causas:
- Tailcalls confunde o depurador
- Locais sobrepostos (não mostrados aqui) confundem o depurador (ele funciona muito bem ao definir um ponto de interrupção)
- ????
O segundo problema, mas também sério, é o depurador que falha ao interromper / atingir pontos de interrupção em alguns casos.
O único local em que eu posso fazer com que o depurador seja quebrado corretamente (e consistentemente) é no ponto de entrada do método.
A situação melhora um pouco quando IgnoreSymbolStoreSequencePoints
não é aplicada.
Conclusão
Pode ser que o depurador do VS seja apenas um buggy :(
Referências:
Atualização 1:
Mdbg não funciona para assemblies de 64 bits. Então isso está fora. Não tenho mais máquinas de 32 bits para testá-lo. Atualização: Tenho certeza de que isso não é um grande problema, alguém tem uma correção? Edit: Sim, bobo, apenas inicie o mdbg no prompt de comando x64 :)
Atualização 2:
Eu criei um aplicativo C # e tentei dissecar as informações da linha.
Minhas descobertas:
- Após qualquer
brXXX
instrução, você precisa ter um ponto de sequência (se não for válido, também conhecido como '#line hidden', emita anop
). - Antes de qualquer
brXXX
instrução, emita um '#line hidden' e anop
.
A aplicação disso, no entanto, não corrige os problemas (sozinho?).
Mas adicionando o seguinte, dá o resultado desejado :)
- Depois
ret
, emita um '#line hidden' e anop
.
Isso está usando o modo em que IgnoreSymbolStoreSequencePoints
não é aplicado. Quando aplicadas, algumas etapas ainda são ignoradas :(
Aqui está a saída IL quando acima foi aplicada:
.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'
Atualização 3:
Problema com o 'semi-reparo' acima. Peverify relata erros em todos os métodos devido ao nop
depois ret
. Eu realmente não entendo o problema. Como pode uma nop
verificação de interrupção após a ret
. É como código morto (exceto que nem é código) ... Bem, a experimentação continua.
Atualização 4:
De volta a casa agora, removemos o código 'não verificável', rodando no VS2008 e as coisas são muito piores. Talvez a execução de código não verificável para a depuração adequada possa ser a resposta. No modo 'release', toda saída ainda seria verificável.
Atualização 5:
Decidi agora que a minha ideia acima é a única opção viável por enquanto. Embora o código gerado não seja verificável, ainda não encontrei nenhum VerificationException
. Não sei qual será o impacto no usuário final com esse cenário.
Como um bônus, minha segunda questão também foi resolvida. :)
Aqui está um pequeno screencast do que eu acabei. Ele atinge pontos de interrupção, faz o passo adequado (entrada / saída / saída), etc. Em suma, o efeito desejado.
Eu, no entanto, ainda não estou aceitando isso como a maneira de fazê-lo. Parece demais para mim. Ter uma confirmação sobre o problema real seria bom.
Atualização 6:
Só tive a alteração para testar o código no VS2010, parece haver alguns problemas:
A primeira chamada agora não é executada corretamente. (violação de asserção ...) for atingida. Outros casos funcionam bem.Algum código antigo emitia posições desnecessárias. Removido o código, funciona conforme o esperado. :)- Mais seriamente, os pontos de interrupção falham na segunda chamada do programa (usando a compilação na memória, despejar o assembly no arquivo parece tornar os pontos de interrupção felizes novamente).
Ambos os casos funcionam corretamente no VS2008. A principal diferença é que, no VS2010, o aplicativo inteiro é compilado para o .NET 4 e no VS2008, compilado para o .NET 2. Ambos executando 64 bits.
Atualização 7:
Como mencionado, eu tenho o mdbg rodando em 64 bits. Infelizmente, ele também tem o problema do ponto de interrupção onde falha ao executar novamente o programa (isso implica que ele é recompilado, portanto, não usando o mesmo assembly, mas ainda usando a mesma fonte).
Atualização 8:
Eu registrei um bug no site do MS Connect referente ao problema do ponto de interrupção.
Update: Fixed
Atualização 9:
Após uma longa reflexão, a única maneira de fazer feliz o depurador parece estar executando o SSA, para que cada passo possa ser isolado e seqüencial. Ainda estou para provar essa noção. Mas parece lógico. Obviamente, limpar os temporários do SSA interromperá a depuração, mas isso é fácil de alternar e deixá-los não tem muita sobrecarga.
nop
s, o passo falhou (vou verificar isso novamente para ter certeza). É um sacrifício, acho que teria que fazer. Não é como se o VS pudesse rodar sem direitos de administrador :) Btw usando Reflection.Emit via DLR (uma ramificação inicial muito hackeada).