PDB(程式資料庫)是什麼 ── 理解偵錯資訊、符號與 Source Link
· 小村 豪 · .NET, CSharp, VisualStudio, PDB, Debugging, Symbols, SourceLink, Diagnostics, 維運, 既有資產活用
1. 首先要掌握的重點
建置 .NET 或 C++ 應用程式時,有時會在 .dll、.exe 之外,一併產生 .pdb 檔案。
舉例來說,輸出結果會像這樣。
MyApp.exe
MyApp.dll
MyApp.pdb
如果不清楚這個 .pdb 是什麼就繼續開發,就會冒出這些疑問。
.pdb可以放在正式環境嗎- 沒有
.pdb應用程式就不能執行嗎 - 明明是 Release 建置卻還產生
.pdb,這樣正常嗎 .pdb裡面是不是整份原始碼都在- 只要有
.pdb,是不是就一定能設中斷點 - 為什麼傾印分析、故障調查時需要
.pdb - NuGet 套件該如何處理
.pdb - Source Link、符號伺服器、
.snupkg有什麼不同
PDB 在平常的實作中不會是主角。 但在故障調查、傾印分析、函式庫發布、維運日誌的易讀性、偵錯體驗上,它相當重要。
先寫下結論。
PDB 是用來把執行檔或組件與原始碼連結起來的偵錯資訊檔案。它不是讓應用程式執行的本體,而是用來告訴偵錯器與診斷工具「哪個指令對應到哪一行原始碼」「哪個區域變數是什麼」「該看哪份原始碼」的資訊。
本文不把 PDB 當成單純的「偵錯用附屬檔案」,而是從實務上該如何處理這項建置產物的角度來整理。
2. PDB 是什麼
PDB 是 Program Database 的縮寫,中文可譯為 程式資料庫。
.pdb 檔案通常也被稱為 符號檔案。
簡單來說,符號指的是程式中與名稱、位置相關的資訊。舉例來說,包括這些內容。
- 函式名稱
- 方法名稱
- 區域變數名稱
- 參數名稱
- 型別資訊
- 原始檔名
- 原始碼行號
- 原始碼位置與編譯後指令的對應關係
- 偵錯器用來設定中斷點的資訊
- Source Link 用的原始碼取得資訊
撰寫原始碼時,會有像下面這樣人類能理解的名稱。
public decimal CalculateTotalPrice(Order order)
{
var subtotal = order.Lines.Sum(x => x.Price * x.Quantity);
var tax = subtotal * 0.10m;
return subtotal + tax;
}
但建置後的 .dll、.exe 並不是原始碼本身。
.NET 會變成 IL 與 metadata,原生 C++ 則會變成接近機械碼的二進位檔。
結果就是,光靠執行檔本身,這些資訊會變得不夠清楚,或難以判斷。
這段機械碼 / IL 對應到哪個 .cs 檔的第幾行
這個位址對應到哪個函式的哪個位置
這個區域變數原本叫什麼名字
這個中斷點實際上該設在哪個指令位置
這個堆疊框架對應到哪份原始碼
PDB 就是用來填補這道落差的檔案。
3. PDB 是執行時必需的嗎
通常 PDB 對於應用程式的執行並非必要。只要有 .dll 或 .exe,應用程式就能啟動。
沒有 .pdb 並不代表一般處理流程就無法執行。
但如果沒有 PDB,下列這些事情會變得困難。
| 想做的事 | 沒有 PDB 時會遇到的困擾 |
|---|---|
| 想在 Visual Studio 中精確地逐步執行 | 無法對應原始碼行號與執行位置 |
| 想設中斷點 | 不知道對應的指令位置,中斷點可能無法解析 |
| 想在例外堆疊追蹤中顯示檔名與行號 | 行號資訊不會顯示,或顯示不完整 |
| 想分析傾印檔 | 難以讀取堆疊、變數與型別 |
| 想逐步進入外部函式庫內部 | 無法連結到函式庫的原始碼 |
| 想讀懂原生程式的當機資訊 | 只剩位址,無法得知函式名稱與位置 |
也就是說,PDB 不是「用來執行的檔案」,而是「用來調查的檔案」。
這個差異很重要。正式環境發生故障時,應用程式即使沒有 PDB 也照樣在跑,但調查者若沒有 PDB 就會很頭痛。 因此,不論要不要放進執行環境,都應該把 PDB 當作建置產物確實保存下來。
4. 有 PDB 的好處是什麼
有 PDB 的話,偵錯器與診斷工具就更容易把二進位檔還原成人類可讀的資訊。
舉例來說,沒有 PDB 的當機資訊有時會顯示成這樣。
MyApp.dll!0x00007ff9a1234567
MyApp.dll!0x00007ff9a1234abc
MyApp.dll!0x00007ff9a1234def
如果 PDB 正確載入,就能看到這樣的資訊。
MyApp.Services.OrderService.CalculateTotalPrice(Order order) Line 42
MyApp.Controllers.OrderController.Post(CreateOrderRequest request) Line 87
MyApp.Program.Main(string[] args) Line 16
這個差異很大。
前者必須從位址開始調查。 後者則能一開始就知道「是哪個方法的哪一行」。
故障調查中,能否在最初的 30 分鐘內接近原因非常重要。 光是留有 PDB,調查的起點就完全不同。
5. PDB 裡面有什麼
PDB 裡面的資訊,會依語言、編譯器、PDB 格式、建置設定而有所不同。 因此無法單純地說「PDB 裡一定會有這個」。
但以 .NET 開發者的直覺來說,主要會期待包含以下這些資訊。
原始檔與編譯後程式碼的對應關係
原始碼行號
方法或函式的符號
區域變數名稱
範圍(scope)資訊
原始檔的路徑或檢查碼
Source Link 資訊
視情況內嵌的原始碼
其中特別重要的是原始碼位置與執行時位置的對應關係。
用 C# 寫的一行,在 IL 或 JIT 後的原生程式碼中,可能會變成多個指令。 反過來,經過最佳化後,也可能出現多行原始碼被合併、消失,或順序看起來變動的情況。
偵錯器會利用 PDB 的資訊,判斷「現在該顯示哪一行原始碼」。
6. PDB 裡面沒有什麼
關於 PDB,理解它「沒有」什麼,比理解它「有」什麼更能減少誤解。
PDB 通常不是這些東西。
- 不是應用程式本體
- 不是執行時必需的 runtime 檔案
- 不是整份原始碼的完整備份
- 不能取代 Git 儲存庫
- 無法完整還原所有建置設定與環境資訊
- 光靠它本身,不會自動說明缺陷原因
不過,有個要注意的地方。
PDB 裡可能包含原始檔路徑、型別名稱、函式名稱、區域變數名稱,視情況甚至還有 Source Link 資訊或內嵌原始碼。
因此不能說「因為 PDB 不是原始碼本身,所以公開也不用擔心」。
裡面可能包含內部專案名稱、含有使用者名稱的路徑、公司內部目錄結構、未公開的型別名稱、能推測出業務邏輯的命名等資訊。
7. 常見誤解一:有 PDB 正式環境會變慢
光是把 PDB 放在旁邊,並不會讓一般的應用程式處理變慢。
PDB 是在偵錯器或診斷工具需要符號資訊時才會用到的東西。 應用程式的一般處理流程,並不會每次都邊讀取 PDB 邊執行。
當然,在例外堆疊追蹤中解析檔名與行號、附加偵錯器、剖析工具或診斷工具讀取符號等場合,還是會用到它。
但「放了 PDB 就會一直變慢,所以絕對不能放進正式環境」這種理解太粗略了。
實務上,這樣思考比較安全。
單純為了執行效能而刪除 PDB 的必要性不大
配置方針應依公開範圍、資訊外洩風險、產物體積、維運政策來決定
即使不放進正式環境,也一定要保存同一建置的 PDB
8. 常見誤解二:Release 建置就不需要 PDB
Release 建置的 PDB 同樣有用。正式環境故障調查時真正需要的,反而就是 Release 建置的 PDB。
如果正式環境跑的是 Release 建置,即使手上有 Debug 建置的 PDB 也沒有意義。 偵錯器需要的是產生該正式環境二進位檔時所生成的 PDB。
這裡重要的是以下這個區分。
| 項目 | 意義 |
|---|---|
| Debug / Release | 最佳化、條件式編譯、輸出設定等建置組態 |
| 是否有 PDB | 是否產生並保存偵錯資訊 |
| 偵錯的難易度 | 由是否最佳化、PDB 內容、原始碼是否一致、JIT 行為等因素決定 |
Release 建置多半經過最佳化,因此逐步執行會比 Debug 建置更難理解。 區域變數可能因最佳化而消失,或停止位置不會依原始碼行的順序出現。
即使如此,只要有 PDB,還是比較容易取得下列資訊。
- 發生例外的原始碼行
- 堆疊上的方法名稱
- 傾印分析時的對應位置
- 剖析結果中的函式名稱
- 日誌與原始碼的對照
並不是「因為是 Release,所以不需要 PDB」。 正因為是 Release,才更需要保留對應該次建置的 PDB。
9. 常見誤解三:有 PDB 就能偵錯任何二進位檔
PDB 不是可以套用在任何 .dll 或 .exe 上的東西。
偵錯器會確認目標二進位檔與 PDB 是否一致。 因為如果硬用不一致的 PDB,原始碼行、函式、變數的對應關係就會出現偏差。
舉例來說,在下列狀況中,即使有 PDB 也可能無法使用,或沒有幫助。
想把自己手邊重新建置出來的 PDB 套用到正式環境的 DLL 上
版本號相同,但實際上是從不同 commit 建置出來的
想讓 hotfix 之後的 DLL 讀取 hotfix 之前的 PDB
最佳化設定或條件式編譯不同
不該把 PDB 想成「只要原始碼一樣大致就會對得上」,而應該理解成「必須對應到同一份建置產物」。
因此,CI/CD 中基本上要以下列這個單位來保存。
commit ID
建置編號
產物版本
.dll / .exe
.pdb
原始碼參照資訊
重要的是不要打散這組組合。
10. 常見誤解四:有 PDB 就算沒有原始碼也能完全讀懂
即使有 PDB,也不代表裡面一定包含原始碼。
PDB 主要持有的是把原始碼與二進位檔對應起來的資訊。 它可以持有原始檔路徑或檢查碼、Source Link 資訊,但一般的 PDB 不一定總是把整份原始碼本文都存進去。
因此,想在偵錯器中逐步進入外部函式庫時,需要具備下列其中一項條件。
手邊有相同的原始檔
可以透過 Source Link 取得正確 commit 的原始碼
PDB 中內嵌了原始碼
以反編譯出來的原始碼代替
Visual Studio 也有把 .NET 組件反編譯後顯示的功能。 但反編譯的結果並不是原本的原始碼。 註解、空白、區域變數名稱、原本的寫法、前處理器條件等,都可能遺失或改變。
用來調查很方便,但不該過度信任它能取代原始碼。
11. PDB 與堆疊追蹤
在 .NET 的例外堆疊追蹤中,有沒有 PDB 會讓顯示方式不同。
即使沒有 PDB,也可能顯示方法名稱與型別名稱。 這是因為 .NET 組件本身就帶有 metadata。
但如果想連檔名與行號都顯示出來,PDB 就變得很重要。
舉例來說,沒有 PDB 時的堆疊追蹤常會是這樣。
System.InvalidOperationException: Order is invalid
at MyApp.Services.OrderService.Validate(Order order)
at MyApp.Controllers.OrderController.Post(CreateOrderRequest request)
如果有 PDB、能解析出行號,就會變成這樣。
System.InvalidOperationException: Order is invalid
at MyApp.Services.OrderService.Validate(Order order) in /src/MyApp/Services/OrderService.cs:line 42
at MyApp.Controllers.OrderController.Post(CreateOrderRequest request) in /src/MyApp/Controllers/OrderController.cs:line 87
在維運中出現這種差異時,調查速度會有很大的不同。
不過,光是顯示檔案路徑本身,有時就等於資訊公開。 因此也需要其他對策,例如不在回傳給外部的錯誤回應中顯示堆疊追蹤、限制日誌的保存位置等。
12. PDB 與傾印分析
PDB 最讓人感激的時刻,就是進行傾印分析的時候。
假設正式環境發生了這樣的問題。
- 行程當機了
- CPU 使用率持續居高不下
- 疑似發生死結
- 記憶體持續增加
- 沒有回應
- 在與原生函式庫的邊界處掛掉
這時就要取得傾印檔並進行分析。
但光靠傾印檔還不夠。 傾印檔中包含的,是那個時間點的行程狀態。 要把裡面記錄的堆疊與模組,轉換成人類可讀的名稱與原始碼行號,就需要對應的 PDB。
在 .NET 中會使用 dotnet-dump、Visual Studio、WinDbg、SOS 等工具進行分析。
若牽涉到原生程式碼,WinDbg 的符號設定就變得很重要。
列舉一些傾印分析時常見的失敗案例。
正式環境的 DLL 還留著,但沒有 PDB
有 PDB,但其實是自己重新建置出來的另一個版本
Windows / .NET runtime 的符號沒有載入
自家應用程式的 PDB 有,但第三方函式庫的符號沒有
沒有設定符號路徑,導致偵錯器找不到 PDB
傾印分析常常等問題發生後才準備就來不及了。 重要的是在建置階段就保存好 PDB,讓調查時能夠取出使用。
13. Windows PDB 與 Portable PDB
PDB 有多種格式。
實務上該掌握的代表格式有以下這兩種。
| 種類 | 主要使用情境 | 特徵 |
|---|---|---|
| Windows PDB | Visual C++、傳統 Windows 偵錯 | Windows 原生開發中常用的格式 |
| Portable PDB | .NET / .NET Core 以後 | 可跨平台使用的 .NET 專用格式 |
.NET Core 以後,Portable PDB 相當重要。 Portable PDB 是不只 Windows,連 Linux、macOS 都能使用的格式。
在 .NET Framework 時代的專案,或較舊的 Visual Studio 設定中,還是有機會看到 Windows PDB。 在目前 .NET 的 SDK 風格專案中,通常會以 Portable PDB 為前提來思考。
這裡要注意的是,兩者的副檔名都是 .pdb。
光看副檔名無法分辨格式的差異。 聽到「PDB」時,要先確認是在哪個情境下討論的。
是在說 .NET 的 Portable PDB 嗎
是在說 Visual C++ 的 Windows PDB 嗎
是在說 .NET Framework 舊專案的情況嗎
是在說 NuGet 發布用的 PDB 嗎
是在說 WinDbg 使用的符號嗎
14. .NET 的 DebugType
在 C# 專案中,可以透過 DebugType 指定偵錯資訊的輸出方式。
代表性的值如下。
| DebugType | 意義 |
|---|---|
portable |
以獨立檔案產生 Portable PDB |
embedded |
把相當於 Portable PDB 的偵錯資訊內嵌進 .dll / .exe |
full |
以目前平台的預設格式產生 PDB |
pdbonly |
在 C# 6.0 以後,實質上與 full 沒有差異 |
none |
不產生 PDB |
在目前的 .NET SDK 風格專案中,C# 的 DebugType 不論在 Debug 或 Release 下,預設都是 portable。
因此,通常不需要特地明確指定 DebugType 只是為了輸出 Portable PDB。
明確寫出來有意義的情況,是想在專案或組織層面表明「固定使用這種格式」、想讓與舊專案的差異更清楚可見,或是想選擇 embedded / none 等和預設值不同的方針時。
在 NuGet 函式庫或對外發布時,要考慮該選擇 portable、embedded、.snupkg 中的哪一種。
舉例來說,若要明確輸出 Portable PDB,可以這樣寫。
<PropertyGroup>
<DebugType>portable</DebugType>
</PropertyGroup>
若不把 PDB 存成獨立檔案,而是內嵌進組件,可以這樣寫。
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
若在 Release 建置中無論如何都不想輸出 PDB,可以這樣寫。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
</PropertyGroup>
不過,這點應該謹慎決定。 把 Release PDB 設成不輸出,可能會讓自己在正式環境故障調查時陷入困境。
15. 只設定 DebugSymbols=false 就足夠嗎
想停止產生 PDB 時,常會看到把 DebugSymbols 設為 false 的做法。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
但如果目的是確實不產生 PDB,把 DebugType 設為 none 會更明確。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
</PropertyGroup>
這種設定確實可能被用作函式庫或應用程式的發布政策。 但再次強調,不產生 PDB 會降低故障調查能力。
實務上常見的分法是這樣。
PDB 一定要當作建置產物產生出來
是否要放進正式伺服器另外判斷
即使不放進伺服器,也要保存在 CI 產物或符號伺服器中
16. C++ 的 PDB 與 .NET 稍有不同
C++ 的 PDB 和 .NET 的 PDB,脈絡稍有不同。
在 Visual C++ 中,會透過像 /Zi、/ZI 這類選項產生 PDB。
此外,編譯器使用的 PDB 與連結器為最終 .exe / .dll 建立的 PDB 之間也有關聯。
在 C++ 中,要讀取原生程式碼的位址、函式、型別、區域變數、內聯展開、最佳化後的位置等資訊,PDB 非常重要。
此外,在原生程式的故障調查中,若沒有 PDB,常會變成這樣的狀態。
知道例外發生的位址
知道模組名稱
但不知道函式名稱與原始碼行號
在混合了 C++ 與 C# 的應用程式、P/Invoke、C++/CLI,或呼叫原生 DLL 的 .NET 應用程式中,不只需要 .NET 端的 PDB,也需要原生端的 PDB。
17. Public symbol 與 private symbol
在 Windows 的符號世界中,有 public symbols 與 private symbols 的區分。
簡單來說,差異如下。
| 種類 | 包含的資訊大致印象 |
|---|---|
| private symbols | 包含區域變數、型別、參數、詳細內部資訊等,偏向完整的資訊 |
| public symbols | 為公開用途精簡過的函式名稱、位址等資訊 |
對外發布的符號,有時會去掉 private symbols,只留下 public symbols。
這是為了在可偵錯性與資訊公開範圍之間取得平衡。
舉例來說,為了分析自家產品的當機資訊,想至少保留函式名稱。 但不想連內部的區域變數名稱、型別資訊都公開出去。 這種情況下,就會使用經過精簡(stripped)的 PDB。
在針對 Windows 的原生開發中,有時會用 PDBCopy 等工具製作出移除 private symbol 的 PDB。
另一方面,公司內部用於故障調查時,需要保存完整版的 PDB。 如果只留下對外精簡過的 PDB,深入調查時就會遇到困難。
18. 偵錯器會從哪裡尋找 PDB
Visual Studio 與 WinDbg 會從幾個位置尋找 PDB。
具代表性的位置大致如下。
專案的輸出資料夾
與 .dll / .exe 相同的資料夾
記錄在 .dll / .exe 中的 PDB 原始路徑
在 Visual Studio 符號設定中指定的資料夾
本機符號快取
公司內部符號伺服器
Microsoft Symbol Server
NuGet.org Symbol Server
Azure Artifacts 等符號伺服器
如果明明有 PDB 卻沒有被載入,就依序檢查以下幾點。
PDB 是否與目標二進位檔一致
PDB 是否在偵錯器的搜尋路徑中
是否能存取符號伺服器
本機快取中是否留有舊版本
目標模組的符號載入是否被停用
是否因 Just My Code 設定被視為外部程式碼
在 Visual Studio 中,偵錯期間查看 Modules 視窗,就能確認各模組的符號載入狀態。
Debug
Windows
Modules
這裡要看目標 DLL 的 Symbol Status。
常見的顯示大致有這 4 種。
Symbols loaded.
Cannot find or open the PDB file.
PDB does not match image.
Skipped loading symbols.
PDB 相關問題卡住時,先看 Modules 視窗是最快的捷徑。
19. 符號伺服器是什麼
符號伺服器是一種機制,讓偵錯器在需要時能夠取得 PDB 等符號檔案。
單純把 PDB 放進共用資料夾,某種程度上也能運作。 但版本一多,馬上就會出問題。
MyApp v1.0.0 的 PDB
MyApp v1.0.1 的 PDB
MyApp v1.0.1 hotfix 的 PDB
MyApp v1.1.0-beta 的 PDB
只針對客戶 A 設定不同的 PDB
相同檔名的 MyApp.pdb 會出現好幾十個。
符號伺服器不是單純以檔名管理 PDB,而是依據與二進位檔的一致性資訊來整理。 因此偵錯器就更容易找到「符合這個 DLL 的 PDB」。
以下是實務上可能的架構範例。
Microsoft Symbol Server
用於取得 Windows、.NET runtime 等的符號
NuGet.org Symbol Server
用於取得公開 NuGet 套件的符號
公司內部符號伺服器
保存自家應用程式、自家函式庫的 PDB
本機符號快取
重複利用已取得的 PDB,加快偵錯速度
若要在公司內部服務調查正式環境故障,建立一套讓 CI 自動把 PDB 發布到公司內部符號儲存區的機制會很方便。
20. Source Link 是什麼
Source Link 是把 PDB 與原始碼版控系統連結起來的機制。
就算 PDB 知道「這個位置對應到這個原始檔」,只要手邊沒有那份原始檔,偵錯器就無法顯示出來。
有了 Source Link,偵錯器就能利用 PDB 中的資訊,從 GitHub、Azure Repos、GitLab、Bitbucket 等平台取得對應 commit 的原始檔。
也就是說,Source Link 解決的是這樣的問題。
想逐步進入從 NuGet 取得的函式庫內部
但手邊沒有 clone 那個函式庫的原始碼
不過有 PDB 與儲存庫資訊
偵錯器會自動去取得正確 commit 的原始碼
這大幅改善了函式庫使用者的體驗。
Source Link 的重點在於,它參照的不是「最新的 main 分支」,而是「產生該二進位檔的那個 commit」。
它的意義在於連結到建置當時的原始碼,而不是最新的原始碼。
21. .NET 8 以後的 Source Link
在 .NET 8 以後的 SDK 中,Source Link 的處理方式已經有所改善。
對 GitHub、Azure Repos、GitLab、Bitbucket 等常用的服務供應商,.NET SDK 端已經內建了 Source Link 的機制。
因此,過去那種「一定要明確加入 Microsoft.SourceLink.GitHub 之類套件」的理解,正逐漸過時。
但仍有需要確認的情況。
使用低於 .NET 8 SDK 的版本建置
專案不是 SDK 風格的舊式專案
使用自建的地端 Git 主機
使用標準未支援的 Source Link 供應商
想連 NuGet 套件的 metadata 一起整理好
想在 NuGet 套件中輸出儲存庫資訊時,可以使用這個設定。
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup>
若需要把未追蹤(untracked)的檔案內嵌進 PDB,可以考慮這個設定。
<PropertyGroup>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
但內嵌會影響資訊公開範圍。 PDB 裡要包含什麼內容,必須配合公開對象與維運方針來決定。
22. Embedded PDB 是什麼
把 DebugType 指定為 embedded 時,Portable PDB 的偵錯資訊會被內嵌進 .dll 或 .exe。
這種情況下,就不會產生獨立的 .pdb 檔案。
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
這在以下場合會很方便。
- 想以接近單一檔案的形式發布
- 想避免忘記附上 PDB
- 小型內部工具,想連偵錯資訊一起帶著
- 想減少 NuGet 套件發布 PDB 的麻煩
另一方面,也有缺點。
- 組件體積會增加
- 發布物永遠包含偵錯資訊
- 資訊公開範圍的控制會變粗略
- 對大型函式庫而言,會影響還原(restore)與發布體積
embedded PDB 雖然方便,但並不是「先全部設成 embedded 就好」這麼單純的事。
尤其是對外公開的函式庫,要考慮該用 .snupkg 符號套件、Source Link、一般 PDB 同捆,還是 embedded 比較好。
23. .snupkg 是什麼
.snupkg 是 NuGet 的符號套件格式。
一般的 NuGet 套件是 .nupkg。
符號套件則是 .snupkg。
MyLibrary.1.2.3.nupkg
MyLibrary.1.2.3.snupkg
.nupkg 包含使用者參照的函式庫本體。
.snupkg 則用來發布偵錯用的 PDB。
這裡重要的是,.snupkg 基本上是針對管理程式碼(managed code)的 Portable PDB 而設計的。
至少在 NuGet.org 的符號伺服器上,只支援 Portable PDB,C++ 等原生專案產生的 Windows PDB 並不被接受。
若想發布或保存 Windows PDB,就要考慮舊式的 .symbols.nupkg、公司內部符號伺服器、CI 產物等其他管道。
若要建立,可以像這樣寫。
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
也可以在命令列中指定。
dotnet pack -c Release -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
如果是公開的 NuGet 函式庫,比起把 PDB 放進 .nupkg 本體,使用 .snupkg 搭配 Source Link 更容易在發布體積與偵錯體驗之間取得平衡。
但要注意 feed 與工具的支援狀況。
如果公司內部的 NuGet feed 不支援 .snupkg,就需要選擇其他方法。
24. PDB 該不該放進正式環境
「PDB 該不該放進正式環境」不是單純的 yes / no 問題。
判斷的軸線有 4 個。
故障調查的方便性
資訊公開風險
發布體積
維運規則
如果是公司內部系統,把 .dll 和 .pdb 放在同一個資料夾的做法也很現實。
這樣例外日誌會更容易顯示行號,傾印分析也會更方便。
若是對外發布的應用程式,是否要直接同捆 PDB 就要謹慎判斷。 這可能會讓內部結構、區域變數名稱、原始碼路徑被看到。 必要時可以選擇只留 public symbols、改成透過符號伺服器提供,或只為支援用途而保存。
如果是 Web 服務,除了是否把 PDB 放在伺服器上,也要考慮日誌中出現的堆疊追蹤該如何處理。 不應該把堆疊追蹤回傳到對外的回應中。 即使留在內部日誌中,也應該決定好瀏覽權限與保存期限。
實務上的建議如下。
PDB 一定要產生
PDB 要當作建置產物保存
是否放進正式環境依系統的公開範圍決定
若要對外公開,先確認資訊公開範圍
放在傾印分析時能取出的位置
25. PDB 是機密資訊嗎
PDB 不一定需要和原始碼本體、私密金鑰用同樣的方式對待。 但把它當成無害的檔案來處理也很危險。
列舉一下從 PDB 中可能得知的資訊。
- 開發者的本機路徑
- 公司內部的資料夾結構
- 專案名稱
- 類別名稱、方法名稱
- 區域變數名稱
- 內部 API 名稱
- 業務用語
- Source Link 的儲存庫 URL
- 內嵌的原始碼
- Source Server 的設定
特別是舊式的 Source Server 機制,或原生 PDB 的部分功能,有時會牽涉到偵錯器為了取得原始碼而執行指令的機制。 應避免無條件使用不可信任的 PDB 或符號伺服器。
此外,把不正常的 PDB 餵給解析 PDB 的工具或函式庫時也需要注意。 若有自動處理外部收到的 PDB 的機制,設計上應該不信任該輸入。
總結來說,這樣處理 PDB 比較安全。
公司內部的完整版 PDB 要當作內部產物加以保護
對外公開的 PDB 要確認內容與公開範圍
若 Source Link 指向的是 private repository,要管理好認證與權限
不要在偵錯器中登錄不可信任的符號伺服器
26. CI/CD 中的 PDB 保存方針
如果 PDB 只留在開發者的本機電腦上,就沒有意義。 正式環境發生故障時所需要的,是對應到實際發行版本的 PDB。
因此,CI/CD 中會以這樣的形式保存。
建置編號: 2026.06.10.1234
commit ID: abcdef123456...
產物:
MyApp.dll
MyApp.pdb
MyApp.deps.json
MyApp.runtimeconfig.json
package.zip
container image digest
此外,若能連結以下這些資訊會更好。
Git commit
Git tag
發行版本號
環境名稱
建置設定
目標框架
RID
容器映像 digest
NuGet lock file
重要的不是單獨保存 PDB,而是不能失去「這個 PDB 對應到哪個二進位檔」的資訊。
一直只在檔案共用區放 MyApp.pdb 的做法,遲早會出問題。
要讓它能以建置單位、版本單位、commit 單位取出。
27. PDB 的配置模式
PDB 的處理有幾種實務上的模式。
模式一:把 PDB 放在與 DLL 相同的資料夾
最單純的做法。
publish/
MyApp.dll
MyApp.pdb
優點是設定簡單。 偵錯器與 runtime 容易找到它,例外日誌也容易顯示行號。
缺點是發布物中會包含偵錯資訊。 有些情況不適合對外發布。
模式二:不放置 PDB,而是保存在 CI 產物中
不把 PDB 放在正式伺服器上,而是留在 CI 的產物中。
release-artifacts/
app.zip
symbols.zip
正式環境發生故障時,取得傾印檔或日誌後,再取出對應建置編號的 PDB 進行分析。
這樣容易控制資訊公開範圍,但調查時需要有取出的流程。
模式三:發布到公司內部符號伺服器
對大型團隊來說,這種方式比較容易處理。
CI 在建置時把 PDB 發布到符號儲存區。 開發者或調查者的 Visual Studio / WinDbg,則參照該符號伺服器。
優點是能安全地處理多個版本的 PDB。 缺點是需要初期建置與存取控制。
模式四:以 NuGet 的 .snupkg 發布
對公開函式庫來說是有力的選項。
MyLibrary.1.2.3.nupkg
MyLibrary.1.2.3.snupkg
使用者只需還原一般套件,偵錯時才會取得所需的符號。
搭配 Source Link,即使是外部函式庫也更容易逐步進入原始碼。
模式五:改用 embedded PDB
對避免忘記附上 PDB 很方便。
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
但要注意組件體積與資訊公開範圍。
28. 在既有專案中首先該確認的事
若要重新檢視既有 .NET 專案中 PDB 的處理方式,先從這些項目確認起。
Release 建置是否有產生 PDB
產生的 PDB 保存在哪裡
正式環境發布的 DLL 與 PDB 是否有對應保存
傾印分析時能否取出 PDB
Source Link 是否啟用
若是 NuGet 函式庫,是否有輸出 .snupkg
PDB 是否含有過多不必要的資訊
在 csproj 中,確認以下這些設定。
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DebugType>portable</DebugType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
若是 NuGet 套件,這也是可以考慮的設定。
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
但並不是所有專案都套用相同設定就好。 內部應用程式、外部函式庫、地端產品、SaaS、OSS 的最佳解各不相同。
29. PDB 沒有載入時的檢查方式
PDB 沒有載入時,別急著憑感覺重新建置,先依序切分排查。
1. 確認目標模組
在 Visual Studio 的 Modules 視窗中尋找目標 DLL / EXE。
Debug > Windows > Modules
這裡該看的欄位如下。
Module
Path
Symbol Status
Symbol File
Version
Timestamp
2. 查看符號狀態
各種顯示大致可以這樣解讀。
| 顯示內容 | 意義 |
|---|---|
| Symbols loaded | 已經載入 |
| Cannot find or open the PDB file | 找不到 PDB |
| PDB does not match image | 有 PDB,但與目標二進位檔不一致 |
| Skipped loading symbols | 可能因設定而未載入 |
3. 確認手邊的 PDB 是否為同一次建置
常見的錯誤是誤用了自己手邊重新建置出來的 PDB。
即使原始碼相同,只要建置條件不同就可能不一致。 應從 CI 的產物中,取得與正式環境發布版本相同的 PDB。
4. 確認符號路徑
若是 Visual Studio,確認這裡。
Tools > Options > Debugging > Symbols
若是 WinDbg,可以這樣確認。
.sympath
.reload
!sym noisy
若要使用 Microsoft 的公開符號,順便指定本機快取會比較方便。
srv*C:\Symbols*https://msdl.microsoft.com/download/symbols
5. 懷疑快取有問題
有時抓到的是舊版 PDB 或損壞的快取。 可以嘗試清除符號快取、指定另一個快取位置、仔細查看載入日誌等方式來確認。
30. PDB 與「Just My Code」
Visual Studio 有一個叫做 Just My Code 的設定。
這是把偵錯目標限縮在「自己的程式碼」,讓外部程式碼難以逐步執行的功能。 平常開發時很方便,但在確認 PDB 或 Source Link 時,反而可能成為混亂的原因。
舉例來說,如果外部 NuGet 函式庫明明有 PDB 與 Source Link,卻無法逐步進入,就要確認以下幾點。
是否因啟用 Just My Code 而被視為外部程式碼
Enable Source Link support 是否已啟用
NuGet.org Symbol Server 是否已啟用
目標套件是否有公開 PDB / .snupkg
是否能存取原始碼取得來源
別急著斷定「PDB 有問題」,先確認偵錯器的設定。
31. PDB 與反編譯
近年的 Visual Studio 能把 .NET 組件反編譯後用於偵錯。
因此,即使外部函式庫沒有 PDB 或原始碼,也能在一定程度上追蹤內部邏輯。
但反編譯並非萬能。
原本的註解不會還原
原本的空白與結構不會還原
區域變數名稱可能改變
async / iterator / pattern matching 等看起來會和原本的寫法不同
最佳化過的程式碼難以看出對應關係
若有 PDB 與 Source Link,基本上直接使用它們比較自然。 反編譯可以視為「沒有 PDB 或原始碼時的輔助手段」。
32. 日誌設計與 PDB
PDB 也和日誌設計有關。
舉例來說,例外日誌中若能顯示檔名與行號,調查就會更容易。 但只依賴日誌是危險的。
正式環境的故障中,會發生這樣的事。
日誌中顯示的行號,和目前的 main 分支不一致
hotfix 之後用同一個版本號重新部署過
因為 PDB 沒有保留下來,無法驗證行號的意義
容器映像還留著,但不知道對應的原始碼 commit
因此,日誌中不只該有行號,最好也輸出建置資訊。
ApplicationVersion: 1.8.3
GitCommit: abcdef1234567890
BuildNumber: 20260610.12
Environment: Production
只要 PDB、原始碼、日誌、傾印檔、部署歷史能連結起來,故障調查就會輕鬆許多。
33. 容器維運中的 PDB
在容器中執行 .NET 應用程式時,需要明確處理好 PDB。
舉例來說,要決定 Docker 映像中要不要包含 PDB。
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "MyApp.dll"]
只要 publish/ 裡有 PDB,就會直接進到映像裡。
這樣有優點。
- 容器內容易顯示堆疊追蹤的行號
- 取得傾印檔時,在同一個檔案系統上容易找到
- 調查時的處理比較簡單
另一方面,也有需要留意的地方。
- 映像體積會增加
- 偵錯資訊會包含在正式環境的映像中
- 若對外發布映像,資訊公開範圍會擴大
若是公司內部專用的 SaaS,把 PDB 包進映像是很合理的選擇。 若是要交給外部客戶的地端產品,有時把 PDB 另外保存會比較好。
不論如何,即使不把 PDB 包進映像,也一定要把對應的 PDB 當作 artifact 保存下來。
34. 單一檔案發行與 PDB
.NET 有單一檔案發行(single-file publish)功能。
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
這時也需要確認偵錯資訊該如何處理。
做成單一檔案,並不代表就不需要故障調查。 反而發布形態越特殊,如何保存對應的符號與原始碼就越重要。
該決定的方針大致如下。
要把 PDB 當作獨立檔案發布嗎
要設成 DebugType=embedded 嗎
符號只保存在公司內部嗎
當機傾印該如何分析
使用單一檔案發行、trimming、AOT 等技術時,調查體驗可能會與一般的 IL 組件不同。 在發行前先試過一次「發生當機時該如何解讀」,會比較安全。
35. PDB 與 Trimming / AOT
目前的 .NET 有時會使用 trimming 或 Native AOT。
這種情況下,不只 PDB,產生出來的原生符號與各平台的偵錯資訊也有關係。
舉例來說,Linux 是 DWARF、macOS 是 dSYM、Windows 是 PDB,也需要考慮原生端的偵錯資訊。
即使是 .NET 應用程式,在這類架構下符號設計也會變得複雜。
Native AOT
Self-contained publish
PublishSingleFile
ReadyToRun
透過 P/Invoke 呼叫原生 DLL
包含 C++/CLI
一般的 Web 應用程式或類別庫,先理解 Portable PDB 與 Source Link 就足夠了。 但發布形態越進階,就越需要把「這個當機該如何分析」納入建置設計中。
36. .NET Framework 專案的注意事項
.NET Framework 的舊專案中,設定與預設值有時會和 SDK 風格專案不同。
舉例來說,有這些差異。
csproj 的格式較舊
使用 packages.config
DebugType 的預設值與目前的 .NET 不同
使用的是 Windows PDB
Source Link 的設定需要額外的套件或 MSBuild 設定
CI 的 MSBuild 版本較舊
即使是 .NET Framework,PDB 的思考方式也相同。
執行時並非必需
偵錯與故障調查上很重要
需要與目標二進位檔一致的 PDB
應該保存 Release 建置的 PDB
但如果直接照搬目前 .NET 的說明,用在舊專案上有時不會如預期般運作。
對於既有資產,先確認實際的輸出結果。
msbuild MyApp.csproj /p:Configuration=Release
Get-ChildItem bin\Release -Filter *.pdb -Recurse
在此基礎上,再整理建置設定與 CI 產物。
37. OSS 函式庫該怎麼做
若是 OSS 的 .NET 函式庫,基本上建議採用這樣的架構。
Release 也要產生 PDB
使用 Portable PDB
啟用 Source Link
在 NuGet 上公開 .snupkg
設定 Repository metadata
留意決定性建置(deterministic build)
範例如下。
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DebugType>portable</DebugType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
依 SDK 與主機供應商的不同,有時不需要額外加裝 Source Link 的套件。
舊版 SDK 或特殊主機環境下,則要加入對應的 Microsoft.SourceLink.* 套件。
在 OSS 中,使用者能不能逐步進入函式庫內部,本身就是一種品質指標。 「發生問題時能讀懂的函式庫」,光是這一點就更容易被信任。
38. 公司內部函式庫該怎麼做
即使是公司內部函式庫,Source Link 與 PDB 依然有效。
反而正是公司內部函式庫,能從業務應用程式逐步進入才更有幫助。
若使用公司內部的 NuGet feed,要考慮以下這些事項。
公司內部 feed 是否支援 .snupkg
若不支援,是否把 PDB 包進 .nupkg 裡
是否要準備公司內部符號伺服器
Git 儲存庫的認證該怎麼處理
離職或調職後是否還能存取原始碼
即使是內部使用,也要避免 PDB 只存在於本機電腦上的狀態。
應該由 CI 產生,並保存在團隊能夠取出的位置。
39. 地端產品該怎麼做
對於發布到客戶環境的地端產品,PDB 的處理會變得比較困難。
如果同捆 PDB,客戶端取得的傾印檔或日誌就會更容易讀懂。 但內部結構也會變得更容易被看到。
常見的選項大致如下。
不同捆 PDB,但由廠商端保存完整版
只為客戶支援提供 public symbols
故障時回收傾印檔,由廠商端用 PDB 進行分析
針對重要客戶提供有限度的符號套件
重要的是發行之後不要遺失 PDB。
地端產品有時要調查幾年前版本的故障。 這時如果沒有對應的 PDB,調查能力就會大幅下降。
40. 刪掉 PDB 之後會怎樣
即使刪掉 PDB,應用程式仍然能執行。 但之後會很麻煩。
也許有人會想,只要從同一個 commit 重新建置就能重現。 但完全重現出乎意料地困難。
SDK 版本不同
NuGet 的解析結果不同
建置時間或環境變數不同
產生的程式碼不同
條件式編譯不同
有些設定只在 CI 中才會套用
依賴的原生工具不同
若整備好決定性建置,重現性會提高,但即使如此,「一開始就當作產物保存下來」還是比較安全。
PDB 比較接近保險。 只有在需要的時候才會顯現出價值。 需要的時候如果沒有,就無法挽回。
41. 實務上建議的設定
沒有一種設定適用於所有專案。 但一般的 .NET 應用程式,可以先以下列內容為基準。
公司內部應用程式
<PropertyGroup>
<DebugType>portable</DebugType>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
方針如下。
Release 也要產生 PDB
是否放進正式環境依維運政策決定
一定要保存在 CI artifact 中
準備好傾印分析流程
公開 NuGet 函式庫
<PropertyGroup>
<DebugType>portable</DebugType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
方針如下。
啟用 Source Link
公開 .snupkg
避免不必要的原始碼內嵌
確認公開出去的 metadata
小型內部工具
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
方針如下。
避免忘記附上 PDB
容許發布物體積增加
限定於內部使用
對外發布產品
完整版 PDB 由公司內部保存
必要時另外製作 public symbols
確認客戶發布物中包含的 PDB 內容
決定好支援時回收傾印檔與分析的流程
42. 檢視 PDB 時的檢查清單
最後附上處理 PDB 猶豫不決時可用的檢查清單。
這份 PDB 對應到哪個 DLL / EXE
該 DLL / EXE 是從哪個 commit 建置出來的
這是本番 Release 用的 PDB,而不是 Debug 建置用的嗎
PDB 是否以 CI artifact 的形式保存
是否有符號伺服器或取得流程
Source Link 是否已啟用
原始碼取得來源的權限是否適當
PDB 中是否含有不想公開的資訊
若對外發布,是否有 public / private symbol 的方針
是否已確認傾印分析時能載入 PDB
特別重要的是這 3 點。
PDB 不是執行檔,而是用於調查的資訊
PDB 必須與目標二進位檔一致
PDB 必須在正式環境發生故障之前就保存好
43. 總結
PDB 不是出現在 .dll、.exe 旁邊那種「不太清楚是什麼的檔案」,而是用來把建置後的二進位檔與開發者能閱讀的原始碼連結起來的偵錯資訊。
對應用程式的一般執行來說並非必需。 但在偵錯、例外調查、傾印分析、剖析、逐步進入外部函式庫時都很重要。
Release 建置的 PDB 同樣有用。 正式環境故障時真正需要的,反而是對應 Release 建置的 PDB。
PDB 要不要放進正式環境,要依資訊公開範圍與維運方針來決定。 但產生 PDB、並把它當作建置產物保存下來,對絕大多數專案來說都是必要的。
實務上,可以把這個方針當作基本原則。
Release 也要產生 PDB
把 PDB 當作 CI/CD 的產物保存
把二進位檔、PDB、commit ID、建置編號連結起來
透過 Source Link 讓原始碼可以被追溯到
NuGet 函式庫要考慮使用 .snupkg
對外發布時要確認公開的符號資訊
PDB 在沒發生問題的時候並不起眼。 但當問題發生時,它是能把調查者帶回原始碼的重要線索。
別只想著「PDB 刪掉也還能動」,而該這樣思考。
PDB 是未來故障調查所需要的地圖。
參考資料
- Symbols in .NET - Microsoft Learn
- Specify symbol (.pdb) and source files in the Visual Studio debugger - Microsoft Learn
- C# Compiler Options that control code generation - Microsoft Learn
- Portable PDB Symbols - Microsoft Learn
- Source Link included in the .NET SDK - Microsoft Learn
- dotnet/sourcelink - GitHub
- Creating symbol packages (.snupkg) - Microsoft Learn
- Public and Private Symbols - Microsoft Learn
- Using PDBCopy - Microsoft Learn
- dotnet-symbol diagnostic tool - Microsoft Learn
- dotnet-dump diagnostic tool - Microsoft Learn
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
TCP 中「Send 單位=Receive 單位」的誤解 ── 把 TCP 視為位元組串流來設計接收邏輯
在 TCP 通訊中,若誤以為呼叫 Send / Write 的單位可以直接對應到接收端的單位,就會發生封包分割、合併、亂碼、協定損壞等問題。本文整理把 TCP 視為位元組串流、在應用層自行進行訊框化(framing)的思考方式,並提供 .NET / C# 的實作範例。
用 WinDbg + SOS 解讀當機傾印檔 ── 收集之後的實務分析入門
本文說明如何用 WinDbg 與 SOS 擴充功能實際讀取已收集的 Windows 當機傾印檔。內容涵蓋符號路徑設定、以 !clrstack・!pe・!dumpheap -stat・!gcroot 追蹤例外與記憶體洩漏的方法、原生當機的 !analyze -v,以及與 do...
什麼是 Roslyn?── 用編譯器的視角讀懂、修正、產生 C# 程式碼
整理 Roslyn(.NET Compiler Platform)的概觀,包括 Syntax Tree、SemanticModel、Workspace、Analyzer、Source Generator,以及在實務上的使用場合與注意事項。
正確處理 Windows 偽裝權杖 - 執行緒層級的權限借用與安全還原方式
本文整理 Windows 偽裝權杖的實務安全處理方式,涵蓋存取權杖、主要權杖、執行緒權杖、偽裝層級、RevertToSelf,以及 .NET 的 WindowsIdentity.RunImpersonated。
在 .NET 中區分 GC 延遲與記憶體洩漏 ── 觀測、比較、證明記憶體增長的實務步驟
本文整理如何用 dotnet-counters、dotnet-gcdump、dotnet-dump 來判斷 .NET 應用程式的記憶體增加,究竟是等待垂圾回收(GC)造成的,還是真正的記憶體洩漏。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
故障調查 & 長期運行故障
整理間歇性故障、通訊診斷、長期運行當機、失敗路徑測試基礎的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
故障調查 & 根本原因分析
調查難以重現的故障、長時間執行後的問題、記憶體洩漏、通訊停滯等棘手的正式環境問題。
常見問題
整理諮詢這個主題時常見的問題。
- PDB 檔案是什麼?
- PDB 是 Program Database(程式資料庫)的縮寫,也稱為符號檔案,是一種偵錯資訊檔案。它的作用是把建置後的 .dll、.exe,和開發者能閱讀的原始碼連結起來。裡面包含函式名稱、方法名稱、區域變數名稱、原始檔名與行號、原始碼位置與編譯後指令的對應關係、Source Link 用的原始碼取得資訊等,偵錯器與診斷工具會用這些資訊來判斷「這個指令對應到哪一行原始碼」。
- 沒有 PDB 檔案應用程式還能執行嗎?可以刪掉嗎?
- 通常 PDB 對應用程式的執行並非必要,只要有 .dll 或 .exe 就能啟動。但如果沒有 PDB,中斷點、逐步偵錯、例外堆疊追蹤中的檔名與行號顯示、傾印分析、對外部函式庫逐步進入都會變得困難。PDB 不是「用來執行的檔案」,而是「用來調查的檔案」,所以不論要不要放進正式環境,都應該當作建置產物確實保存下來。一旦刪除,就算用同一個 commit 重新建置,完全重現也出乎意料地困難,一旦遇到故障調查就可能無法挽回。
- Release 建置也需要 PDB 嗎?
- 需要。正式環境故障調查時真正需要的,反而是 Release 建置的 PDB。如果正式環境跑的是 Release 建置,即使手上有 Debug 建置的 PDB 也沒有意義,偵錯器需要的是產生該正式環境二進位檔時所生成的 PDB。因為 PDB 必須與目標二進位檔一致,所以在 CI/CD 中,基本做法是把 commit ID、建置編號、.dll/.exe、.pdb 一起保存。另外,目前 .NET SDK 風格專案中,Debug 與 Release 預設都會產生 Portable PDB。
- PDB 檔案可以放在正式環境中嗎?
- 這不是單純的 yes / no 問題,要從故障調查的方便性、資訊公開風險、發布體積、維運規則這 4 個面向來判斷。如果是公司內部系統,把 .dll 和 .pdb 放在同一個資料夾,例外日誌會比較容易顯示行號,算是現實的做法。若對外發布,PDB 可能透露本機路徑、公司內部的目錄結構、型別名稱與區域變數名稱、Source Link 的儲存庫 URL 等資訊,因此要謹慎判斷,必要時可以選擇只留 public symbols,或改成透過符號伺服器提供。即使不放進發布物,也一定要保存同一建置的 PDB。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。