"呼叫堆疊(Call Stack)"意即程式執行時從觸發的方法到最後回傳,把整個呼叫的一連串方法紀錄在一個Stack裡。呼叫堆疊可以協助開發人員Debug,快速找到問題發生位置並解決。在軟體測試中,呼叫堆疊通常我們會把它轉成Call Graph,以視覺化表示。像限量這次在寫Log模組時就需要記錄是哪裡觸發寫Log,這樣出問題才會比較好找。所以這篇限量就要來展示一下如何使用C#取得呼叫堆疊資訊。
.NET提供了一個StackTrace的類別會記錄呼叫堆疊, 利用這個類別就可以簡單的實作相關的方法來取得呼叫堆疊資訊。要如何利用StackTrace取得呼叫堆疊呢?最簡單的方式就是只要new一個StackTrace的實體,然後把它ToString回傳就OK了,當然還有其他進階的應用,這就要來了解一下StackTrace的結構。
StackTrace內部其實是StackFrame的集合容器,而StackFrame則是每個呼叫記錄的結構化物件,內容包含了MethodBase物件, 來源檔案行列數位置, 來源檔案名稱等,可以說是整個StackTrace的精隨。我們可以透過StackTrace的GetFrames取得所有StackFrame或GetFrame取得特定的StackFrame,只要取得StackFrame就可以利用它的一些方法如:GetMethod, GetFileName, GetFileLineNumber來取得來源資訊。
StackTrace的建構子有許多種參數:
在限量實作了一個簡單的Helper讓開發人員更方便使用StackTrace。Helper裡有兩個方法。WhoCallMe可以取得呼叫目標方法的位置;CallStack可以取得目標方法之前的呼叫堆疊。程式碼如下:
MyHelper.cs
接下來就來用一個範例來看看StackTrace如何運作,範例程式碼如下:
Program.cs
執行結果:
圖中上方紅框為呼叫WhoCallMe的結果,可以看到每個方法呼叫WhoCallMe後會取得是誰呼叫它的路徑;下方紅框為呼叫CallStack的結果,在範例程式中,我們在FunctionB2呼叫CallStack,此時會以FunctionB2為基準,抓取在這之前的呼叫堆疊。
加碼放送
限量在開發Web專案時,有應用到StackTrace,因為限量把fNeedFileInfo設為true,在開發時Log會包含行號與檔案資訊。但是當限量利用Visual Studio Publish功能把網站發佈出去後,寫出來的Log竟然沒有行號與檔案資訊。後來限量查了一下,發現因為開發模式中,我們的DLL有對應.pdb檔案,這才可以抓到來源檔案的行號與其他資訊。發佈後的網站DLL沒有包含.pdb檔案,所以如果要有檔案資訊的話就要把.pdb檔案複製到DLL的資料夾中才可以。
參考來源:
MSDN - StackTrace 類別
.NET提供了一個StackTrace的類別會記錄呼叫堆疊, 利用這個類別就可以簡單的實作相關的方法來取得呼叫堆疊資訊。要如何利用StackTrace取得呼叫堆疊呢?最簡單的方式就是只要new一個StackTrace的實體,然後把它ToString回傳就OK了,當然還有其他進階的應用,這就要來了解一下StackTrace的結構。
StackTrace內部其實是StackFrame的集合容器,而StackFrame則是每個呼叫記錄的結構化物件,內容包含了MethodBase物件, 來源檔案行列數位置, 來源檔案名稱等,可以說是整個StackTrace的精隨。我們可以透過StackTrace的GetFrames取得所有StackFrame或GetFrame取得特定的StackFrame,只要取得StackFrame就可以利用它的一些方法如:GetMethod, GetFileName, GetFileLineNumber來取得來源資訊。
StackTrace的建構子有許多種參數:
- bool fNeedFileInfo:是否要擷取程式來源檔案資訊。
- StackFrame frame:使用提供的StackFrame進行初始化。
- Exception e:使用提供的Exception進行初始化。
- int skipFrames:要略過的StackFrame數量。
在限量實作了一個簡單的Helper讓開發人員更方便使用StackTrace。Helper裡有兩個方法。WhoCallMe可以取得呼叫目標方法的位置;CallStack可以取得目標方法之前的呼叫堆疊。程式碼如下:
MyHelper.cs
public class MyHelper { public static string WhoCallMe() { // 2:省略目前位置與呼叫目前Function的位置 // true:顯示檔案資訊 var stackTrace = new StackTrace(2, true); var target = stackTrace.GetFrame(0); return new StackTrace(target).ToString(); } public static string CallStack() { // 1:省略目前位置 // true:顯示檔案資訊 var stackTrace = new StackTrace(1, true); return stackTrace.ToString(); } }
接下來就來用一個範例來看看StackTrace如何運作,範例程式碼如下:
Program.cs
class Program { static void Main(string[] args) { (new ClassA()).FunctionA01(); Console.Read(); } } class ClassA { public void FunctionA01() { Console.WriteLine( string.Format( "Method: {0}, WhoCallMe: {1}", MethodBase.GetCurrentMethod().Name, MyHelper.WhoCallMe() ) ); FunctionA02(); } public void FunctionA02() { Console.WriteLine( string.Format( "Method: {0}, WhoCallMe: {1}", MethodBase.GetCurrentMethod().Name, MyHelper.WhoCallMe() ) ); ClassB.FunctionB01(); } } class ClassB { public static void FunctionB01() { Console.WriteLine( string.Format( "Method: {0}, WhoCallMe: {1}", MethodBase.GetCurrentMethod().Name, MyHelper.WhoCallMe() ) ); FunctionB02(); } public static void FunctionB02() { Console.WriteLine( string.Format( "Method: {0}, WhoCallMe: {1}", MethodBase.GetCurrentMethod().Name, MyHelper.WhoCallMe() ) ); Console.WriteLine(MyHelper.CallStack()); } }
執行結果:
圖中上方紅框為呼叫WhoCallMe的結果,可以看到每個方法呼叫WhoCallMe後會取得是誰呼叫它的路徑;下方紅框為呼叫CallStack的結果,在範例程式中,我們在FunctionB2呼叫CallStack,此時會以FunctionB2為基準,抓取在這之前的呼叫堆疊。
加碼放送
限量在開發Web專案時,有應用到StackTrace,因為限量把fNeedFileInfo設為true,在開發時Log會包含行號與檔案資訊。但是當限量利用Visual Studio Publish功能把網站發佈出去後,寫出來的Log竟然沒有行號與檔案資訊。後來限量查了一下,發現因為開發模式中,我們的DLL有對應.pdb檔案,這才可以抓到來源檔案的行號與其他資訊。發佈後的網站DLL沒有包含.pdb檔案,所以如果要有檔案資訊的話就要把.pdb檔案複製到DLL的資料夾中才可以。
參考來源:
MSDN - StackTrace 類別
留言
張貼留言