2009-04-20 47 views
6

Tôi có ứng dụng Windows Service được quản lý mã thỉnh thoảng bị lỗi trong quá trình sản xuất do StackOverFlowException được quản lý. Tôi biết điều này bởi vì tôi đã chạy adplus trong chế độ tai nạn và phân tích các vụ tai nạn dump dump mortem bằng cách sử dụng SoS. Tôi thậm chí còn đính kèm trình gỡ rối windbg và đặt nó thành "ngoại lệ không được giải quyết".gỡ lỗi trực tiếp ngăn xếp ngăn xếp

Vấn đề của tôi là, tôi không thể thấy bất kỳ ngăn xếp được quản lý nào hoặc chuyển sang bất kỳ chuỗi nào. Tất cả chúng đều bị phá hủy vào thời điểm trình gỡ rối bị hỏng.

Tôi không phải là chuyên gia Windbg, và ngắn cài đặt Visual Studio trên hệ thống trực tiếp hoặc gỡ lỗi và gỡ lỗi từ xa bằng công cụ đó, có ai có bất kỳ đề xuất nào về cách tôi có thể lấy dấu vết ngăn xếp vi phạm chủ đề?

Đây là những gì tôi đang làm. !

Các luồng

...

XXXX 11 27c 000000001b2175f0 B220 tàn tật 00000000072c9058: 00000000072cad80 0000000019bdd3f0 0 Ukn System.StackOverflowException (0000000000c010d0)

...

Và tại thời điểm này, bạn thấy ID XXXX cho biết chuỗi đã chết.

Trả lời

8

Khi bạn đã nhấn tràn ngăn xếp, bạn sẽ gặp nhiều khó khăn khi gỡ lỗi vấn đề - thổi không gian ngăn xếp của bạn rời khỏi chương trình của bạn ở trạng thái không xác định, vì vậy bạn không thể dựa vào bất kỳ nào thông tin trong đó tại thời điểm đó - bất kỳ dấu vết ngăn xếp nào bạn cố gắng nhận có thể bị hỏng và có thể dễ dàng chỉ cho bạn sai hướng. Tức là, một khi StackOverflowException xảy ra, đã quá muộn.

Ngoài ra, theo the documentation bạn không thể bắt được StackOverflowException từ .Net 2.0 trở đi, vì vậy các đề xuất khác bao quanh mã của bạn bằng try/catch cho điều đó có thể sẽ không hoạt động. Điều này làm cho cảm giác hoàn hảo, cho các tác dụng phụ của một tràn ngăn xếp (Tôi ngạc nhiên. Net bao giờ cho phép bạn bắt nó).

Lựa chọn thực sự duy nhất của bạn là tham gia vào việc phân tích mã, tìm kiếm bất kỳ thứ gì có khả năng gây tràn ngăn xếp và đặt một số loại dấu để bạn có thể có ý tưởng xuất hiện trước xảy ra. Ví dụ, bất kỳ phương pháp đệ quy nào cũng là nơi đầu tiên để bắt đầu, vì vậy hãy cho chúng một bộ đếm chiều sâu và ném ngoại lệ của bạn nếu chúng có giá trị "không hợp lý" mà bạn xác định.

+1

Điều đó thật thú vị. Tôi thậm chí còn không nhận ra điều đó đã thay đổi. Lần cuối cùng tôi có một trong số đó là khi tôi gõ nhầm thuộc tính getter/thành viên và nhận được các cuộc gọi đệ quy vô tận (và tôi đã có thể bắt và gỡ lỗi nó sau đó). +1 để đọc tài liệu mới nhất. –

0

Đây có phải là tùy chọn để quấn mã của bạn bằng try-catch ghi vào EventLog (hoặc tệp hoặc bất kỳ thứ gì) và chạy một lần gỡ lỗi này không?

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

Bạn sẽ không thể gỡ lỗi, nhưng bạn sẽ nhận được theo dõi ngăn xếp.

0

Một tùy chọn bạn có là sử dụng khối try/catch ở mức cao, sau đó in hoặc ghi nhật ký ngăn xếp do ngoại lệ cung cấp. Mọi ngoại lệ đều có thuộc tính StackTrace có thể cho bạn biết vị trí được ném từ đó. Điều này sẽ không cho phép bạn thực hiện bất kỳ gỡ lỗi tương tác nào, nhưng nó sẽ cho bạn một nơi để bắt đầu.

+0

Tôi chỉ có cảm giác kỳ lạ này của deja vu ... :) –

+0

Heh, tôi vừa đọc lại câu trả lời của bạn và tôi thấy điểm của bạn: P. Ồ, có lẽ đáng để nói rõ rằng các ngoại lệ có ngăn xếp chúng được ném ra, trong trường hợp nó không rõ ràng là –

0

Đối với giá trị của nó, bắt đầu từ .NET 4.0, Visual Studio (và bất kỳ trình gỡ lỗi nào dựa trên api ICorDebug) đều có khả năng gỡ lỗi các minidumps. Điều này có nghĩa là bạn sẽ có thể tải bãi chứa sự cố vào trình gỡ rối VS trên một máy tính khác và xem các ngăn xếp được quản lý tương tự như khi bạn đã đính kèm một trình gỡ rối vào thời điểm xảy ra sự cố. Xem PDC talk hoặc Rick Byers' blog để biết thêm thông tin. Thật không may điều này sẽ không giúp bạn với vấn đề ở bàn tay, nhưng có lẽ nó sẽ tiếp theo thời gian bạn chạy vào vấn đề này.

0

Hãy xem nhật ký gỡ lỗi Chế độ sự cố ADPLUS của bạn. Xem nếu có bất kỳ vi phạm truy cập hoặc ngoại lệ tràn ngăn xếp gốc thực sự xảy ra trước khi StackOverflowException được quản lý được ném.

Đoán của tôi là có một ngoại lệ trên ngăn xếp của chuỗi mà bạn bị bắt trước khi thoát khỏi chuỗi.

Bạn cũng có thể sử dụng DebugDiag từ www.iis.net và sau đó thiết lập một quy tắc sụp đổ và tạo ra một tập tin đầy đủ bãi cho Vi phạm Access (SXE av) và Stack Overflow trường hợp ngoại lệ có nguồn gốc (SXE SOV)

Cảm ơn, Aaron

0

Tôi có một lớp RecursionChecker cho loại điều này. Tôi từ chối bản quyền trên mã bên dưới.

Nó than phiền nếu nó thấy chính nó nhấn kiểm tra đối tượng mục tiêu quá thường xuyên. Nó không phải là một tất cả-end-tất cả; các vòng lặp có thể gây ra các kết quả dương tính giả, chẳng hạn. Người ta có thể tránh điều đó bằng cách có một cuộc gọi khác sau khi mã nguy hiểm, nói với người kiểm tra rằng nó có thể giảm số tiền gọi lại của nó cho đối tượng đích. Nó vẫn sẽ không được chống đạn.

Để sử dụng nó, tôi chỉ cần gọi

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

Đây là lớp 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 
    } 
} 

lớp helper dưới đây:

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