2009-04-20 2 views
6

マネージドコードのWindowsサービスアプリケーションがあります。このサービスは、管理対象のStackOverFlowExceptionのためにプロダクションで時折クラッシュします。私はこれを知っています。なぜなら、私は衝突モードでadplusを実行し、SoSを使用して死後の事故を分析したからです。私はwindbgデバッガを添付して、 "未処理の例外にする"ように設定しました。スタックオーバーフローのライブデバッグ

私の問題は、管理されたスタックを見ることができず、スレッドに切り替えることができないということです。彼らは、デバッガが壊れたときにはすべて解体されています。

私はWindbgのエキスパートではありません。ライブシステムにVisual Studioをインストールしたり、リモートデバッグとデバッグを行ったりするのに手間がかかりません。どのようにスタックトレースを取得できますか?問題のスレッド?

ここは私がやっていることです。

00000000072c9058を無効スレッドが

...

XXXX 11 27C 000000001b2175f0 B220:!00000000072cad80 0000000019bdd3f0 0 Ukn System.StackOverflowException(0000000000c010d0)

...

そして、この時点で、スレッドがかなり死んでいることを示すXXXX IDが表示されます。

答えて

8

あなたは、スタックオーバーフローをヒットしたら、あなたは問題をデバッグするための運のうちのかなりのだ - あなたはどのに頼ることができないので、あなたのスタック空間を吹いは、非決定論的な状態でプログラムを離れますその時点でその情報が表示されます。取得しようとしているスタックトレースが破損している可能性があり、誤った方向にあなたを容易に指すことがあります。つまり、StackOverflowExceptionが発生すると、それは遅すぎます。

また、によると、.Net 2.0以降ではStackOverflowExceptionを捕まえられない可能性があります。そのため、try/catchを使用してコードを囲む他の提案はおそらく動作しません。スタックのオーバーフローの副作用を考えると、これは完璧な意味合いを持っています(私はあなたがそれをキャッチすることをネットに驚かせました)。

唯一の本当の選択肢は、コードを分析したり、スタックのオーバーフローを引き起こす可能性のあるものを探したり、何らかのマーカーを入れたりすることです。の前に発生する。たとえば、明らかに再帰的メソッドが最初に始まるので、デプスカウンタを与えて例外を投げます。あなたが実際に有効なスタックトレースを得ることができるように、あなたが定義した "不合理な"値を得たら。

+1

それは面白いです。私は変わったことに気付かなかった。私がこれらのうちの1つを持っていた最後の時は、プロパティ/メンバゲッターをミスタイプし、無限の再帰呼び出しを得たときでした(そして、それを捕まえてデバッグすることができました)。 +1は実際に最新のドキュメントを読むためのものです。 –

0

EventLog(またはファイルなど)に書き込み、このデバッグを一度だけ実行するコードをtry-catchでラップするオプションはありますか?

try { ... } catch(SOE) { EventLog.Write(...); throw; } 

あなたはデバッグすることはできませんが、あなたは、スタックトレースを取得します。

0

高いレベルでtry/catchブロックを使用し、例外によって提供されるスタックトレースを出力または記録することが1つのオプションです。すべての例外には、どこからスローされたかを示すStackTraceプロパティがあります。これでインタラクティブなデバッグはできませんが、開始する場所が必要です。

+0

... :) –

+0

ちょっと、私はあなたの答えを読んで、私はあなたのポイントを見ます:P。ああ、おそらく明白な価値があることは明白なことですが、例外であると明示されていない場合は、投げられたスタックを持っています。 –

0

.NET 4.0以降では、Visual Studio(およびICorDebug apiに依存するデバッガ)がミニダンプをデバッグできるようになりました。つまり、別のコンピュータのVSデバッガにクラッシュダンプをロードし、クラッシュ時にデバッガを接続した場合と同様の管理スタックを参照することができます。詳細については、PDC talkまたはRick Byers' blogを参照してください。残念ながら、これは手元の問題であなたを助けませんが、おそらく次回この問題にぶつかるでしょう。

0

ADPLUSクラッシュモードデバッグログをご覧ください。 管理されたStackOverflowExceptionがスローされる前に、アクセス違反または真のネイティブのスタックオーバーフロー例外が発生していないかどうかを確認してください。

私の推測では、スレッドが終了する前にコールドキャッチしているスレッドのスタックには例外があります。

ます。また

おかげで、 をwww.iis.netからDebugDiagを使用して、クラッシュルールを設定し、アクセス違反(SXEのAV)およびスタックオーバーフローネイティブの例外(SXE SOV)の完全なダンプファイルを作成することができ

Aaron

0

私はこのようなことのためにRecursionCheckerクラスを持っています。私はここで、以下のコードに著作権を放棄します。

ターゲットオブジェクトのチェックがあまりにも頻繁に行われると、それは文句を言います。それはすべてではありません。例えば、ループは偽陽性を引き起こす可能性があります。危険なコードの後に​​別の呼び出しをして、それがターゲットオブジェクトの反復呼び出しを減らすことができることをチェッカーに伝えることを避けることができます。それでも防弾ではありません。

それを使用するには、私はちょうど呼んここ

public void DangerousMethod() { 
    RecursionChecker.Check(someTargetObjectThatWillBeTheSameIfWeReturnHereViaRecursion); 
    // recursion-risky code here. 
} 

はRecursionCheckerクラスです:

/// <summary>If you use this class frequently from multiple threads, expect a lot of blocking. In that case, 
/// might want to make this a non-static class and have an instance per thread.</summary> 
public static class RecursionChecker 
{ 
    #if DEBUG 
    private static HashSet<ReentrancyInfo> ReentrancyNotes = new HashSet<ReentrancyInfo>(); 
    private static object LockObject { get; set; } = new object(); 
    private static void CleanUp(HashSet<ReentrancyInfo> notes) { 
    List<ReentrancyInfo> deadOrStale = notes.Where(info => info.IsDeadOrStale()).ToList(); 
    foreach (ReentrancyInfo killMe in deadOrStale) { 
     notes.Remove(killMe); 
    } 
    } 
    #endif 
    public static void Check(object target, int maxOK = 10, int staleMilliseconds = 1000) 
    { 
    #if DEBUG 
    lock (LockObject) { 
     HashSet<ReentrancyInfo> notes = RecursionChecker.ReentrancyNotes; 
     foreach (ReentrancyInfo note in notes) { 
     if (note.HandlePotentiallyRentrantCall(target, maxOK)) { 
      break; 
     } 
     } 
     ReentrancyInfo newNote = new ReentrancyInfo(target, staleMilliseconds); 
     newNote.HandlePotentiallyRentrantCall(target, maxOK); 
     RecursionChecker.CleanUp(notes); 
     notes.Add(newNote); 
    } 
    #endif 
    } 
} 

ヘルパークラス以下:私はちょうどデジャヴのこの奇妙な感じがした

internal class ReentrancyInfo 
{ 
    public WeakReference<object> ReentrantObject { get; set;} 
    public object GetReentrantObject() { 
    return this.ReentrantObject?.TryGetTarget(); 
    } 
    public DateTime LastCall { get; set;} 
    public int StaleMilliseconds { get; set;} 
    public int ReentrancyCount { get; set;} 
    public bool IsDeadOrStale() { 
    bool r = false; 
    if (this.LastCall.MillisecondsBeforeNow() > this.StaleMilliseconds) { 
     r = true; 
    } else if (this.GetReentrantObject() == null) { 
     r = true; 
    } 
    return r; 
    } 
    public ReentrancyInfo(object reentrantObject, int staleMilliseconds = 1000) 
    { 
    this.ReentrantObject = new WeakReference<object>(reentrantObject); 
    this.StaleMilliseconds = staleMilliseconds; 
    this.LastCall = DateTime.Now; 
    } 
    public bool HandlePotentiallyRentrantCall(object target, int maxOK) { 
    bool r = false; 
    object myTarget = this.GetReentrantObject(); 
    if (target.DoesEqual(myTarget)) { 
     DateTime last = this.LastCall; 
     int ms = last.MillisecondsBeforeNow(); 
     if (ms > this.StaleMilliseconds) { 
     this.ReentrancyCount = 1; 
     } 
     else { 
     if (this.ReentrancyCount == maxOK) { 
      throw new Exception("Probable infinite recursion"); 
     } 
     this.ReentrancyCount++; 
     } 
    } 
    this.LastCall = DateTime.Now; 
    return r; 
    } 
} 

public static class DateTimeAdditions 
{ 
    public static int MillisecondsBeforeNow(this DateTime time) { 
    DateTime now = DateTime.Now; 
    TimeSpan elapsed = now.Subtract(time); 
    int r; 
    double totalMS = elapsed.TotalMilliseconds; 
    if (totalMS > int.MaxValue) { 
     r = int.MaxValue; 
    } else { 
     r = (int)totalMS; 
    } 
    return r; 
    } 
} 

public static class WeakReferenceAdditions { 
    /// <summary> returns null if target is not available. </summary> 
    public static TTarget TryGetTarget<TTarget> (this WeakReference<TTarget> reference) where TTarget: class 
    { 
    TTarget r = null; 
    if (reference != null) { 
     reference.TryGetTarget(out r); 
    } 
    return r; 
    } 
}