同事報案,某個排程執行有錯,錯誤訊息如下。

失敗的應用程式名稱: AppCrash.exe,版本: 1.0.0.0,時間戳記: 0x84a8515b
失敗的模組名稱: KERNELBASE.dll,版本: 10.0.18362.535,時間戳記: 0x5bd9df62
例外狀況代碼: 0xe0434352
錯誤位移: 0x001135d2
失敗的處理程序識別碼: 0x3b7a0
失敗的應用程式開始時間: 0x01d6280af63277ea
失敗的應用程式路徑: X:\Lab\ImpTest\AppCrash\bin\Debug\AppCrash.exe
失敗的模組路徑: C:\WINDOWS\System32\KERNELBASE.dll
報告識別碼: 345b4ed6-d4e8-4a76-8992-57ee7b57f3ad
失敗的套件完整名稱: 
失敗的套件相關應用程式識別碼: 

爬文 KERNELBAE.DLL 0xe0434352 有點類似「發生了無法處理的系統錯誤」,原因五花八門,單憑此一資訊很難斷定問題來源(光看錯誤代碼內含 e04 就知道它有多難纏了,呵)。詢問進一步細節,得到的回覆是整個錯誤訊息只有這些。我請同事改用其他機器做測試,程式一樣出錯但傳回的是 System.TypeInitializationException 錯誤訊息,這個我就不陌生了,TypeInitializationException 是類別的靜態建構式或靜態屬性、欄位計算初始值時出錯造成。由此一訊息,同事很快找出問題,原本的排程也就正常了。

但我的疑問是:為何在第一台機器會得到 KERNELBASE.dll 錯誤訊息而非 System.TypeInitializationException,平白延誤了故障排除時間?

我推測第一回報錯誤是來自 Windows 排程出錯留下的 Windows 錯誤事件,錯誤事件有多個,協助檢測人員是挑選了其中一筆回報,未注意到其他事件有更明確的訊息。要求對照測試時改為手動測試,這才取得明確資訊。

我寫了以下程式重現問題:

using System;

namespace AppCrash
{
    class Program
    {
        static void Main(string[] args)
        {
            Foo.Test();
        }
    }

    class Foo
    {
        static Foo()
        {
            System.IO.File.ReadAllText($"X:\\NotThere.txt");
        }

        public static void Test()
        {
            Console.WriteLine("Hello World!");
        }
    }
}

在靜態建構式故意去開啟不存在的錯誤引發 TypeInitializationException。

將程式排入 Windows 排程,果然重現了兩則 Windows 錯誤事件。

先有一筆 .NET Runtime 錯誤:

再有一筆 Application KERNELBASE.dll 錯誤:

因 Windows 事件排序晚發生的排在上面,故一般人直覺會抓最上面的 KERNELBASE.dll 錯誤。另外我再做了測試,就算不是 TypeInitializationException,如下例改在 Test() 引發錯誤也會有兩筆錯誤:

    class Foo
    {
        static Foo() { }

        public static void Test()
        {
            System.IO.File.ReadAllText($"X:\\NotThere.txt");
            Console.WriteLine("Hello World!");
        }
    }

快速總結:使用事件檢視器檢查 Windows 排程 .NET 程式錯誤時,請忽略 KERNELBASE.dll 錯誤,聚焦 .NET Runtime 錯誤。

When scheduled .NET program crash, please ignore KERNALBASE.DLL error and focus on .NET Runtime error.


Comments

Be the first to post a comment

Post a comment