2009-11-11 3 views
10

스택의 높은 부분 (Exception.StackTrace)이 잘리는 이유는 무엇입니까? 간단한 예제를 보자 :이 "디자인"처럼왜 스택이 Exception.StackTrace에서 잘리는가?

public void ExternalMethod() 
{ 
    InternalMethod(); 
} 

public void InternalMethod() 
{ 
    try 
    { 
    throw new Exception(); 
    } 
    catch(Exception ex) 
    { 
    // ex.StackTrace here doesn't contain ExternalMethod()! 
    } 
} 

것 같다. 그런 이상한 디자인의 이유는 무엇입니까? 로그 메시지에서 InternalMethod()를 호출 한 사람을 이해할 수 없으며 종종이 정보가 매우 필요하기 때문에 디버깅이 더 복잡해집니다.

해결책은 (모르는 사람들에게) 이해할 수있는 일반적인 해결책이 두 개 있습니다 :
1) 전체 스택을 포함하는 정적 Environment.StackTrace 속성을 로그 할 수 있습니다 (예 : 가장 높은 레벨 (메시지 큐)과 예외가 발생하는 가장 깊은 메소드에서 끝나는 것).
2) 우리는 최고 수준의 예외를 잡아서 기록해야합니다. 무언가를하기 위해 더 낮은 레벨에서 예외를 잡을 필요가있을 때, C#에서 "throw"문으로) 그것을 다시 일으켜 세워야합니다.

하지만 이러한 디자인의 이유에 대한 질문입니다.

+0

개체가 누가 그것을 호출했는지에 대해 신경 써야합니까? 예외를 다시 던져야한다는 점에서 (2) 올바른 방법입니다. –

+0

주로 스택 추적 정보가 디버깅 목적으로 예외와 함께 포함됩니다. 기존 설계로는 스택의 상위 부분도 가지고있는 것처럼 디버깅하는 데 도움이되지 않습니다. 다시 말하면, 누가이 메소드를 호출했는지 알면 디버깅에 매우 유용 할 수 있기 때문입니다. – nightcoder

답변

0

나는 catch 블록에서 throw ex;을 수행하면 그 시점에서 스택 추적을 자른다는 것을 알고 있습니다. throw;은 캐치에서 스택을 자르지 않으므로 던지기에 "의도적으로"있을 가능성이 있습니다. 새로운 예외를 던지고 있기 때문에 같은 일이 계속 될 수 있습니다.

실제 예외 (예 : int i = 100/0;)가 발생하면 어떻게 될까요? 스택 추적이 여전히 잘 렸는가?

+1

'''throw;'''도 스택을 잘립니다. – Enerccio

-1

이것은 종종 컴파일러 최적화로 인해 발생합니다.

당신은 다음과 같은 특성을 사용하여 인라인하지 않을 방법을 장식 할 수 있습니다

[MethodImpl(MethodImplOptions.NoInlining)] 
public void ExternalMethod() 
{ 
    InternalMethod(); 
} 
+0

아니요, 인라인 때문에 인 것은 아닙니다. 내가 말한 행동은 기본 행동입니다. 너는 너 자신을 볼 수있다. 그것은 항상 내가 보여준 것처럼 될 것입니다. – nightcoder

+1

JIT가 항상 예측 가능한 방식으로 메소드를 인라인하지 않는 이유는 무엇입니까? 어쩌면 당신의 방법은 항상 인라인됩니다. –

+0

함수에서 항상 이렇게하는 것이 인라이닝 (inlining) 때문이 아니라는 것을 증명합니다. –

11

가 좋아, 지금은 당신의 인라인 일에 내 혼란에 ... 죄송합니다 얻고 무엇을 참조하십시오.

catch 된 예외의 '스택'은 현재 실행중인 catch 블록에서 예외가 발생한 곳까지의 델타입니다. 개념적으로이 동작은 Exception.StackTrack이이 try/catch 블록의 컨텍스트 내에서 예외가 발생한 위치를 알려줍니다. 이를 통해 예외 스택을 '가상'호출을 통해 전달할 수 있으며 여전히 정확성을 유지합니다. 이 작업의 전형적인 예가 .NET Remoting 예외입니다.

따라서 catch 블록에서 전체 스택 보고서를 원할 경우 아래 예와 같이 현재 스택을 예외 스택에 추가합니다. 유일한 문제는 이것이 더 비쌀 수 있다는 것입니다.

private void InternalMethod() 
    { 
     try 
     { 
      ThrowSomething(); 
     } 
     catch (Exception ex) 
     { 
      StackTrace currentStack = new StackTrace(1, true); 
      StackTrace exceptionStack = new StackTrace(ex, true); 
      string fullStackMessage = exceptionStack.ToString() + currentStack.ToString(); 
     } 
    } 
+0

+1이 이유는, 해결 방법에 대한 내 대답을 참조하십시오 –

2

csharptest는 의도적으로 설계된 것입니다. StackTrace는 try 블록에서 멈 춥니 다. 더 나아가 예외가 발생했을 때 호출되는 프레임 워크에는 후크가 없습니다.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.CompilerServices; 
using System.Diagnostics; 

namespace ConsoleApplication15 { 

    [global::System.Serializable] 
    public class SuperException : Exception { 

     private void SaveStack() { 
      fullTrace = Environment.StackTrace; 
     } 

     public SuperException() { SaveStack(); } 
     public SuperException(string message) : base(message) { SaveStack(); } 
     public SuperException(string message, Exception inner) : base(message, inner) { SaveStack(); } 
     protected SuperException(
      System.Runtime.Serialization.SerializationInfo info, 
      System.Runtime.Serialization.StreamingContext context) 
      : base(info, context) { } 

     private string fullTrace; 
     public override string StackTrace { 
      get { 
       return fullTrace; 
      } 
     } 
    } 

    class Program { 

     public void ExternalMethod() { 
      InternalMethod(); 
     } 

     public void InternalMethod() { 
      try { 
       ThrowIt(); 
      } catch (Exception ex) { 
       Console.WriteLine(ex.StackTrace); 
      } 
     } 

     [MethodImpl(MethodImplOptions.NoInlining)] 
     public void ThrowIt() { 
      throw new SuperException(); 
     } 


     static void Main(string[] args) { 
      new Program().ExternalMethod(); 
      Console.ReadKey(); 
     } 
    } 
} 

출력 :

 
    at System.Environment.get_StackTrace() 
    at ConsoleApplication15.SuperException..ctor() in C:\Users\sam\Desktop\Source 
\ConsoleApplication15\ConsoleApplication15\Program.cs:line 17 
    at ConsoleApplication15.Program.ThrowIt() in C:\Users\sam\Desktop\Source\Cons 
oleApplication15\ConsoleApplication15\Program.cs:line 49 
    at ConsoleApplication15.Program.InternalMethod() in C:\Users\sam\Desktop\Sour 
ce\ConsoleApplication15\ConsoleApplication15\Program.cs:line 41 
    at ConsoleApplication15.Program.Main(String[] args) in C:\Users\sam\Desktop\S 
ource\ConsoleApplication15\ConsoleApplication15\Program.cs:line 55 
    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C 
ontextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 

그래서 당신이 할 수있는 최선은, 그것의 절대적인 요구 사항은 전체 스택 트레이스 (예외 생성에 전체 추적을 저장) 얻기 위해 이러한 라인을 따라 뭔가

이 동작을 기존 시스템 정의 예외에 삽입 할 수는 없지만 .Net에는 예외를 래핑하고 재실행하기위한 풍부한 인프라가 있으므로 엄청난 거래가 아니어야합니다.

+1

이것은 다른 접근 방법입니다; 그러나 예외가 던져지는 곳을 나타내지는 않습니다.하지만 생성 된 곳 (일반적으로 똑같은 것이므로 나쁘지 않습니다)을 나타냅니다. 이 경로를 계속 사용하는 경우 (원격 작업에서와 마찬가지로) 호출 컨텍스트 전체에서 부정확하며 'fullTrace'속성을 직렬화하기위한 추가 코드가 필요할 것입니다. 댓글에 –

+0

지점. –

관련 문제