2009-11-11 41 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()を呼び出した人を理解できず、しばしばこの情報が非常に必要であるため、デバッグはより複雑になります。

私が理解しているように、解決策は2つの一般的な解決策があります。
1)スタック全体を含む静的なEnvironment.StackTraceプロパティをログに記録できます(たとえば、最もレベルの低い(メッセージキュー)、例外が発生する最も深いメソッドで終わる)。
2)最高レベルの例外をキャッチしてログに記録する必要があります。何かをするために低レベルで例外を捕捉する必要があるときは、それをさらにスローする必要があります(C#で "throw"ステートメントで)。

しかし、そのような設計の理由は疑問です。

+0

なぜオブジェクトはそれを誰が呼び出すかについて気にする必要がありますか?あなたのポイント(2)は、例外を再スローしなければならないという点で、正しいアプローチです。 –

+0

主にスタックトレース情報がデバッグの目的で例外に含まれています。既存のデザインでは、スタックの上位部分も持っているかのようにデバッグするのに役立ちません。もう一度やり直すので、誰がメソッドを呼び出したか知ることは、デバッグに非常に役立ちます。 – nightcoder

答えて

0

キャッチブロックでthrow ex;を実行すると、その時点でスタックトレースが切り捨てられることがわかりました。 throw;だけがキャッチ内のスタックを切り捨てるのではなく、スローのために「設計通りに」ある可能性があります。あなたが新しい例外を投げているので、同じことが起こっているかもしれません。

実際の例外(例:int i = 100/0;)が発生した場合はどうなりますか?スタックトレースは切り捨てられていますか?

+1

'' 'throw;' ''も同様にスタックを切り捨てます – Enerccio

-1

これは、多くの場合、コンパイラの最適化によって発生します。

あなたは、次の属性を使用してインライン化したくないメソッドを飾ることができます:

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

いいえ、これはインライン展開のためではありません。私が述べた振る舞いは、デフォルトの動作です。あなたはそれを自分で見ることができます。それはいつも私が示したようなものです。 – nightcoder

+1

なぜJITは常に予測可能な方法でメソッドをインライン化しませんか?たぶん、あなたの方法は常にインライン化されています。 –

+0

あなたの関数内でこれが常に好きであるという事実は、それがインライン展開でないことを証明しません。 –

11

は、[OK]を、今私はあなたがインライン化の事で私の混乱のために...申し訳きものを参照してください。

キャッチされた例外の 'スタック'は、現在実行中のキャッチブロックから例外がスローされた場所までのデルタです。概念的には、この動作は、Exception.StackTrackがこのtry/catchブロックのコンテキスト内で例外がどこで発生したかを伝えてくれる点で正しいです。これにより、例外スタックを「仮想」呼び出しを介して転送することができ、それでも精度を維持します。これの典型的な例の1つは.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

これは設計によるものです。 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

スポット。 –

関連する問題