C# 实时调试堆栈溢出

C# 实时调试堆栈溢出,c#,crash,windbg,stack-overflow,sos,C#,Crash,Windbg,Stack Overflow,Sos,我有一个托管代码Windows服务应用程序,由于托管StackOverFlowException,该应用程序在生产过程中偶尔会崩溃。我知道这一点,因为我在崩溃模式下运行了adplus,并使用SoS分析了崩溃转储。我甚至附加了windbg调试器,并将其设置为“GoUnhandledException” 我的问题是,我看不到任何托管堆栈或切换到任何线程。当调试器崩溃时,它们都被拆除了 我不是Windbg专家,除了在实时系统上安装Visual Studio或使用该工具进行远程调试和调试之外,有人对如何

我有一个托管代码Windows服务应用程序,由于托管StackOverFlowException,该应用程序在生产过程中偶尔会崩溃。我知道这一点,因为我在崩溃模式下运行了adplus,并使用SoS分析了崩溃转储。我甚至附加了windbg调试器,并将其设置为“GoUnhandledException”

我的问题是,我看不到任何托管堆栈或切换到任何线程。当调试器崩溃时,它们都被拆除了

我不是Windbg专家,除了在实时系统上安装Visual Studio或使用该工具进行远程调试和调试之外,有人对如何从有问题的线程中获取堆栈跟踪有什么建议吗

这是我正在做的

!!线程

XXXX 11 27c 00000000 1B2175F0 b220禁用000000000 72C9058:000000000 72CAD80 00000000 19BDD3F0 0 Ukn系统。堆栈溢出异常(0000000000 C010D0)


此时,您会看到XXXX ID,指示线程已完全死机。

是否可以使用
try catch
将代码包装到
事件日志(或文件或其他文件)中,然后一次性运行此调试

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

您将无法调试,但会得到堆栈跟踪。

您可以选择在高级使用try/catch块,然后打印或记录异常提供的堆栈跟踪。每个异常都有一个
StackTrace
属性,可以告诉您它是从哪里抛出的。这不会让你进行任何交互式调试,但它应该给你一个开始的地方。

一旦你遇到堆栈溢出,你调试这个问题的运气就差多了——清理堆栈空间会让你的程序处于不确定的状态,因此,此时您不能依赖其中的任何信息—您尝试获取的任何堆栈跟踪都可能被破坏,并且很容易将您指向错误的方向。也就是说,一旦StackOverflowException发生,就太晚了

此外,根据我们的说法,从.NET2.0开始,您无法捕获StackOverflowException,因此其他建议用try/catch来包围您的代码可能不起作用。考虑到堆栈溢出的副作用,这是非常有意义的(我很惊讶.Net允许您捕获它)


您唯一真正的选择是参与繁琐的代码分析,寻找可能导致堆栈溢出的任何东西,并放入某种标记,以便在它们发生之前了解它们发生的位置。例如,显然任何递归方法都是首先开始的,所以给它们一个深度计数器,如果它们达到您定义的某个“不合理”值,则抛出您自己的异常,这样您就可以实际获得有效的堆栈跟踪。

从.NET 4.0 Visual Studio开始(以及任何依赖于
ICorDebug
api的调试器)获得调试小型转储的能力。这意味着您将能够将崩溃转储加载到另一台计算机上的VS调试器中,并查看托管堆栈,类似于在崩溃时附加了调试器。有关详细信息,请参阅或。不幸的是,这对您解决当前的问题没有帮助,但下一步可能会有帮助当您遇到此问题时。

请查看您的ADPLUS崩溃模式调试日志。 在抛出托管StackOverflowException之前,查看是否存在任何访问冲突或真正的本机堆栈溢出异常

我的猜测是,线程堆栈上存在一个异常,在线程退出之前,您会冷捕获该异常

您还可以从www.iis.net使用DebugDiag,然后设置崩溃规则,并为访问冲突(sxe av)和堆栈溢出本机异常(sxe sov)创建完整转储文件

谢谢,
Aaron

我有一个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
  }
}
///如果您经常从多个线程使用该类,则会出现大量阻塞。在这种情况下,
///可能希望使其成为非静态类,并且每个线程都有一个实例。
公共静态类递归检查器
{
#如果调试
私有静态HashSet ReentrancyNotes=新HashSet();
私有静态对象LockObject{get;set;}=new object();
专用静态无效清除(哈希集注释){
List deadOrStale=notes.Where(info=>info.IsDeadOrStale()).ToList();
foreach(在deadOrStale中重新输入killMe信息){
注释:删除(killMe);
}
}
#恩迪夫
公共静态无效检查(对象目标,int maxOK=10,int=1000)
{
#如果调试
锁定(锁定对象){
HashSet notes=RecursionChecker.ReentrancyNotes;
foreach(在notes中重新输入信息备注){
if(注意:HandlePotentiallyEntrantCall(目标,maxOK)){
打破
}
}
ReentrancyInfo newNote=新的ReentrancyInfo(目标,毫秒);
newNote.handlePotentiallyEntrantCall(目标,maxOK);
RecursionChecker.CleanUp(注释);
注释。添加(新注释);
}
#恩迪夫
}
}
助手类如下:

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;
  }
}
内部类ReentrancyInfo
{
公益引用可重入对象{get;set;}
公共对象GetReentrantObject(){
返回此.ReentrantObject?.TryGetTarget();
}
公共日期时间上次调用{get;set;}
公共整数{get;set;}
公共整数可重入计数{get;set;}
公共广播电台IsDeadOrStale(){
布尔r=假;
if(this.LastCall.millissecondsBeforeNow()>this.StaleMilliconds)