디버거에서 .NET 언어 단계를 올바르게 만들기


135

먼저이 질문의 길이에 대해 사과드립니다.

나는 IronScheme 의 저자입니다 . 최근에는 '기본'.NET 디버거를 사용할 수 있도록 적절한 디버그 정보를 생성하기 위해 열심히 노력하고 있습니다.

이것이 부분적으로 성공적 이었지만, 나는 이빨 문제가 발생했습니다.

첫 번째 문제는 스테핑과 관련이 있습니다.

Scheme은 표현 언어이기 때문에 문장 또는 라인 기반의 주요 .NET 언어와 달리 모든 것이 괄호로 묶이는 경향이 있습니다.

원래 코드 (스키마)는 다음과 같습니다.

(define (baz x)
  (cond
    [(null? x) 
      x]
    [(pair? x) 
      (car x)]
    [else
      (assertion-violation #f "nooo" x)]))

나는 의도적으로 개행에 각 표현을 배치했습니다.

방출 된 코드는 ILSpy를 통해 C #으로 변환됩니다.

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));
}

보다시피, 매우 간단합니다.

참고 : C #에서 코드가 조건식 (? :)으로 변환 된 경우 전체 단계는 하나의 디버그 단계 일 뿐이므로 명심하십시오.

다음은 소스 및 줄 번호가있는 IL 출력입니다.

  .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'

참고 : 디버거가 단순히 전체 메서드를 강조 표시하지 못하게하기 위해 메서드 입력 지점을 1 열 너비로 만듭니다.

보시다시피 각 표현식은 줄에 올바르게 매핑됩니다.

이제 스테핑 문제 (VS2010에서 테스트되었지만 VS2008에서 동일 / 유사한 문제) :

이들은 IgnoreSymbolStoreSequencePoints적용되지 않습니다.

  1. null 인수로 baz를 호출하면 올바르게 작동합니다. (null? x) 다음에 x가옵니다.
  2. Cons arg를 사용하여 baz를 호출하면 올바르게 작동합니다. (null? x), (pair? x), (car x).
  3. 다른 인수와 함께 baz를 호출하면 실패합니다. (null? x), (pair? x), (car x), (어설 션 위반 ...)

신청할 때 IgnoreSymbolStoreSequencePoints(권장) :

  1. null 인수로 baz를 호출하면 올바르게 작동합니다. (null? x) 다음에 x가옵니다.
  2. Cons arg로 baz를 호출하면 실패합니다. (null? x) 그리고 (pair? x).
  3. 다른 인수와 함께 baz를 호출하면 실패합니다. (null? x), (pair? x), (car x), (어설 션 위반 ...)

또한이 모드에서 (여기에 표시되지 않은) 일부 줄이 잘못 강조 표시되고 1만큼 줄어 듭니다.

다음은 원인이 될 수있는 몇 가지 아이디어입니다.

  • Tailcalls는 디버거를 혼동합니다
  • 겹치는 위치 (여기에 표시되지 않음)는 디버거를 혼동합니다 (브레이크 포인트를 설정할 때 매우 잘 수행됨)
  • ????

두 번째이지만 심각한 문제는 디버거가 경우에 따라 중단 점을 중단 / 히트하지 못하는 것입니다.

디버거를 올바르게 (및 구성 적으로) 깨뜨리는 유일한 방법은 메소드 진입 점입니다.

IgnoreSymbolStoreSequencePoints적용하지 않으면 상황이 조금 나아 집니다.

결론

VS 디버거가 평범한 버그 일 수 있습니다.

참고 문헌 :

  1. CLR / .NET 언어를 디버깅 가능하게 만들기

업데이트 1 :

Mdbg는 64 비트 어셈블리에서 작동하지 않습니다. 그래서 밖으로입니다. 더 이상 테스트 할 32 비트 시스템이 없습니다. 업데이트 : 이것이 큰 문제는 아니라고 확신합니다. 누구든지 수정 했습니까? 편집 : 네, 어리석은, x64 명령 프롬프트에서 mdbg를 시작하십시오 :)

업데이트 2 :

C # 앱을 만들고 회선 정보를 해부하려고했습니다.

나의 발견 :

  • brXXX지시가 끝나면 시퀀스 포인트가 필요합니다 (유효하지 않은 경우 '#line hidden'이라고도 함 nop).
  • brXXX지시 하기 전에 '#line hidden'과 a를 내 보냅니다 nop.

이것을 적용해도 문제가 해결되지는 않습니다 (단독?).

그러나 다음을 추가하면 원하는 결과가 나옵니다. :)

  • 다음 ret에 '#line hidden'과 a을 내 보냅니다 nop.

IgnoreSymbolStoreSequencePoints적용되지 않은 모드를 사용하고 있습니다 . 적용 할 때 일부 단계는 여전히 건너 뜁니다. (

위의 적용했을 때의 IL 출력은 다음과 같습니다.

  .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'

업데이트 3 :

위의 '반 고정'에 문제가 있습니다. Peverify는 nop이후 로 인한 모든 방법의 오류를보고합니다 ret. 나는 문제를 정말로 이해하지 못한다. 방법을 수 nop후 휴식 검증 ret. 그것은 코드가 아닌 것을 제외하고는 죽은 코드와 같습니다 ... 아, 실험은 계속됩니다.

업데이트 4 :

집으로 돌아와서 VS2008에서 실행되는 '확인할 수없는'코드를 제거했는데 상황이 훨씬 나빠졌습니다. 적절한 디버깅을 위해 검증 할 수없는 코드를 실행하는 것이 답이 될 수 있습니다. '릴리스'모드에서 모든 출력은 여전히 ​​검증 가능합니다.

업데이트 5 :

나는 이제 내 생각이 유일하게 실행 가능한 옵션이라고 결정했다. 생성 된 코드를 확인할 수는 없지만 아직의 코드를 찾지 못했습니다 VerificationException. 이 시나리오에서 최종 사용자에게 어떤 영향을 줄지 모르겠습니다.

보너스로 두 번째 문제도 해결되었습니다. :)

여기에 내가 끝낸 것에 대한 작은 스크린 캐스트 가 있습니다. 브레이크 포인트에 도달하고 적절한 스테핑 (입 / 출력 / 오버) 등을 수행합니다.

그러나 나는 여전히 이것을하는 방법으로 이것을 받아들이지 않고 있습니다. 그것은 나에게 지나치게 해킹을 느낀다. 실제 문제에 대한 확인이 있으면 좋을 것입니다.

업데이트 6 :

VS2010에서 코드를 테스트하기 위해 변경 했으므로 몇 가지 문제가있는 것 같습니다.

  1. 첫 번째 통화는 이제 올바르게 진행되지 않습니다. (어설 션 위반 ...)이 발생했습니다. 다른 경우에는 잘 작동합니다. 일부 오래된 코드는 불필요한 위치를 방출했습니다. 코드를 제거하고 예상대로 작동합니다. :)
  2. 더 심각하게는 프로그램의 두 번째 호출에서 중단 점이 실패합니다 (메모리 내 컴파일 사용, 파일로 어셈블리 덤프는 중단 점이 다시 만족스러운 것으로 보입니다).

이 두 경우 모두 VS2008에서 올바르게 작동합니다. 주요 차이점은 VS2010에서 전체 응용 프로그램이 .NET 4 용으로 컴파일되고 VS2008에서 .NET 2로 컴파일된다는 것입니다. 둘 다 64 비트를 실행합니다.

업데이트 7 :

언급했듯이 mdbg가 64 비트에서 실행되었습니다. 불행히도 프로그램을 다시 실행하면 중단되지 않는 중단 점 문제가 있습니다 (이것은 다시 컴파일되므로 동일한 어셈블리를 사용하지 않고 여전히 동일한 소스를 사용함을 의미합니다).

업데이트 8 :

나는 한 버그를 제출 중단 점 문제에 관한 MS Connect 사이트에서.

업데이트 : 수정

업데이트 9 :

오랫동안 생각한 후에 디버거를 행복하게 만드는 유일한 방법은 SSA를 수행하는 것처럼 보이므로 모든 단계를 격리하고 순차적으로 수행 할 수 있습니다. 나는이 개념을 아직 증명하지 못했다. 그러나 논리적 인 것 같습니다. 분명히 SSA에서 temp를 정리하면 디버깅이 중단되지만 전환하기 쉽고 오버 헤드가 많지 않습니다.


어떤 아이디어라도 환영합니다. 시도해보고 결과를 질문에 추가하겠습니다. 첫 번째 아이디어는 mdbg를 시도하고 C #으로 동일한 코드를 작성하고 비교하십시오.
leppie

6
질문이 뭐야?
George Stocker

9
그의 질문은 "왜 내 언어가 올바르게 스테핑되지 않습니까?"
McKay

1
Peverify를 통과하지 않으면 매핑 된 드라이브 또는 많은 플러그인 호스팅 설정과 같은 부분 신뢰 상황에서 실행할 수 없습니다. 당신의 IL, 반사를 어떻게 생성합니까? 어느 경우이든 항상 .line 16707566,16707566 : 0,0 ''을 넣을 수 있습니다. 귀하가 귀환 후 nop을 넣는 것에 대해 걱정할 필요가 없습니다.
Hippiehunter

@Hippiehunter : 감사합니다. 불행히도 nops가 없으면 스테핑이 실패합니다 (확실히 다시 확인합니다). 내가 만들어야 할 희생이다. VS는 관리자 권한 없이도 실행할 수 없습니다. : Reflection을 사용하는 Btw. DLR (매우 해킹 된 초기 분기)을 통해 방출하십시오.
leppie

답변:


26

Visual Studio Debugger 팀의 엔지니어입니다.

내가 틀렸다면 수정하십시오.하지만 남은 유일한 문제는 PDB에서 .NET 4 동적 컴파일 기호 형식으로 전환 할 때 일부 중단 점이 누락되었다는 것입니다.

문제를 정확하게 진단하려면 재현이 필요할 수 있지만 다음은 도움이 될만한 참고 사항입니다.

  1. 비 관리자로 실행 가능한 VS (2008+)
  2. 두 번째로 항상 심볼이로드됩니까? 예외를 통해 침입하거나 System.Diagnostic.Debugger.Break ()를 호출하여 테스트 할 수 있습니다.
  3. 심볼이로드되었다고 가정하면 보내실 수있는 재현이 있습니까?
  4. 가능한 차이점은 동적 컴파일 코드의 기호 형식이 .NET 2 (PDB 스트림)와 .NET 4 (IL DB라고 부르는 것입니다)와 100 % 다르다는 것입니다.
  5. 바로 '잡음'소리. 아래의 암시 적 시퀀스 포인트 생성 규칙을 참조하십시오.
  6. 실제로 다른 라인에서 물건을 방출 할 필요는 없습니다. 기본적으로 VS는 컴파일러 작성기로서 '기호 문'의 의미를 정의하는 '기호 문'을 단계적으로 수행합니다. 따라서 각 표현식이 심볼 파일에서 별도의 것이기를 원한다면 제대로 작동합니다.

JIT는 다음 규칙에 따라 암시 적 시퀀스 포인트를 만듭니다. 1. IL nop 명령어 2. IL 스택 빈 포인트 3. 호출 명령어 바로 다음의 IL 명령어

문제를 해결하기 위해 repro가 필요한 경우 연결 버그를 신고하고 해당 매체를 통해 파일을 안전하게 업로드 할 수 있습니다.

최신 정보:

이 문제가 발생하는 다른 사용자에게 http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27543 에서 Dev11 개발자 미리보기를 시도 하고 의견 을 보내 주시기 바랍니다 . (4.5 대상)

업데이트 2 :

Leppie는 http://www.microsoft.com/visualstudio/11/en-us/downloads(http://www.microsoft.com/visualstudio/11/en-us/downloads) 에있는 Dev11 베타 버전 에서 해당 버그를 수정했습니다 . com / VisualStudio / feedback / details / 684089 / .

감사,

누가


고마워 필요한 경우 재현을 시도합니다.
leppie

확인에 관해서는 그렇습니다. 그러나 여전히 검증 가능성을 잃지 않고 스테핑 문제를 해결하는 것을 선호합니다. 그러나 말했듯이, 만약 내가 가능하다면 런타임이 결코 발생하지 않을 것이라는 것을 증명할 수 있다면 기꺼이 희생해야한다 VerificationException.
leppie

포인트 6에 관해서는 간단히이 질문에 대해 '라인 기반'으로 만들었습니다. 디버거는 실제로 작동하려는 의도와 같이 올바르게 입력 / 출력 / 초과 표현식을 단계적으로 수행합니다. VS의 디버거는 가장 외부 표현식을 중단 점으로 만들고 싶어합니다.
leppie

중단 점 문제를 격리했다고 생각합니다. CLR2에서 실행하는 동안 새 코드 임에도 불구하고 코드를 다시 실행할 때 중단 점이 다시 평가되는 것 같습니다. CLR4에서 중단 점은 원래 코드에만 '고착'됩니다. CLR4 동작 @ screencast.com/t/eiSilNzL5Nr 의 작은 스크린 캐스트를 만들었습니다 . 유형 이름은 호출간에 변경됩니다.
leppie

connect에 버그를 제출했습니다. 재현은 매우 쉽습니다 :) connect.microsoft.com/VisualStudio/feedback/details/684089
leppie

3

나는 SharpDevelop 디버거 팀의 엔지니어입니다 :-)

문제를 해결 했습니까?

SharpDevelop에서 디버깅을 시도 했습니까? .NET에 버그가 있으면 해결 방법을 구현해야하는지 궁금합니다. 이 문제를 알지 못합니다.

ILSpy에서 디버깅을 시도 했습니까? 특히 디버그 기호가 없습니다. C # 코드를 디버깅하지만 IL 명령어가 제대로 디버깅 가능한지 알려줍니다. (ILSpy 디버거는 베타 버전입니다)

원래 IL 코드에 대한 빠른 참고 사항 :

  • .line 19,19 : 6,15 ''두 번 발생합니까?
  • .line 20,20 : 7,14 ''는 암시 적 시퀀스 지점에서 시작되지 않습니다 (스택이 비어 있지 않음). 내가 걱정
  • .line 20,20 : 7,14 ''에는 "car x"(양호)와 "#f nooo x"(나쁜?)에 대한 코드가 포함됩니다.
  • ret 후 nop에 관한. Stloc, ldloc, ret은 어떻습니까? C # 은이 트릭을 사용하여 ret을 독특한 시퀀스 포인트로 만듭니다.

데이비드


+1 첫 번째 포인트 인 피드백 덕분에 코드 생성기의 불행한 부작용이 있지만 무해한 것으로 보입니다. 두 번째 요점, 이것은 정확히 내가 필요한 것입니다. 그러나 나는 당신의 요점을 볼 것입니다. 마지막 포인트의 의미를 잘 모르겠습니다.
leppie

세 번째 요점은 .line 22,22 : 7,40 ''이 IL_0020 앞에 있어야한다는 것입니다. 또는 IL_0020 이전에 무언가가 있어야합니다. 그렇지 않으면 코드는 여전히 .line 20,20 : 7,14 ''로 계산됩니다. 네 번째 포인트는 "ret, nop"이며 "sloc, .line, ldloc, ret"으로 대체 될 수 있습니다. 나는 전에 패턴을 보았을 것입니다. 아마 중복되었을 수도 있지만 이유가있을 수도 있습니다.
dsrbecky

꼬리 호출을 풀기 때문에 ret 전에 stloc, ldloc을 수행 할 수 없습니다. 아 내가 당신을 얻을, 당신은 원래 출력을 참조했다?
leppie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.