mscn:在 Visual Studio 調試器(C#)中指定符號 (.pdb) 和源文件
PDB全稱Program Database,程序數據庫 ( .pdb) 文件(也稱為符號文件)將項目源代碼中的標識符和語句映射到已編譯應用中的相應標識符和說明。 這些映射文件將調試器鏈接到源代碼,以進行調試。
這個文件會在我們調試的時候被使用到,這個東西可以理解為調試的時候應用程序和源文件之間的一個橋梁。正是歸功於這個文件,我們才能在debug的時候看到程序當前執行相對應的代碼和監視到一些變量。
符號文件的工作方式和包含的信息
.pdb 文件保存調試和項目狀態信息,使用這些信息可以對應用的調試配置進行增量鏈接。 在調試時,Visual Studio 調試器使用 .pdb 文件來確定兩項關鍵信息:
- 要在 Visual Studio IDE 中顯示的源文件名和行號。
- 在應用中停止的斷點位置。
- 符號文件還會包含源文件的位置,以及要從中檢索它們的服務器(可選),所以vs2022 可以同步符號文件獲取源代碼。(個人修改過了)
- 每個源文件的MD5校驗和都存儲在.pdb文件中。
- .pdb文件作用還包括Debug里的PDB是保存着調試和項目狀態信息、有斷言、堆棧檢查、記錄出錯信息等代碼。Release 里的PDB是記錄:出什么錯了+錯誤在哪行。所有的其他的數據都已經包含在了.NET Metadata中了; (個人修改過了)
調試器只會加載與在生成應用時創建的 .pdb 文件完全匹配的 .pdb 文件(即原始 .pdb 文件或副本) 。 這樣的完全重復是必需的,因為即使代碼本身未更改,應用的布局也可能會更改。
內容來源:https://docs.microsoft.com/zh-cn/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger?view=vs-2022
因為微軟並未公布PDB內部細節,只公開了一些API,所以對於這個文件一直是一個迷。
案例:pdb類型文件的作用之一:記錄具體異常的關鍵信息,如文件路徑和行號
主要作用:
1、用於本地代碼調試。在本地調試的時候就用pdb(只是被程序員忽略,默認生成放在debug文件夾的子目錄中),建立源代碼和dll之間的聯系,這樣調試器才知道哪行代碼設置斷點、斷言、變量、到底哪一行代碼異常等。
2、用於公開庫的參與本地調試。公開的庫(dll)在別程序員電腦上運行時候,要加載對應庫(dll).pdb文件。dll文件的代碼才能參與本地調試。程序員才能在庫中設置斷點、才能查到出錯代碼的行號等。
3、調試是配置增量鏈接
PDB文件什么時候產生?
PDB文件是在我們編譯工程的時候產生的,它是和對應的模塊(exe或dll)一起生成出來的。我們一般可能不會意識到PDB文件的重要性,因為如果只是我們本地進行開發,我們總是能夠進行調適。這里我要引入兩個概念:Private Build和Public Build1。Private Build指的是在開發機器上的編譯,Public Build指的是在負責編譯的機器上的編譯。
正如上面我所說Private Build一般不會有問題,因為在編譯出來的機器上進行調試所有必要的文件都在該在的地方。所有大部分不能調試的問題都發生在Public Build的情況下。
如果你的應用程序需要發布或者當作產品賣得,你就需要特別注意要保存你發布出去的那個版本的PDB文件和源文件。注意:你只有一次機會保存着發布出去的PDB文件,如果你弄丟了將無法找回(唯一性)。
c# - Visual Studio如何知道源文件是否與原始版本匹配?
每個源文件的MD5校驗和都存儲在.pdb文件中。為什么PDB這么重要--很難再次生成一樣的.pdb ?
也許你會認為如果拿一份一模一樣的源代碼重新編譯一個PDB文件,然后用來調試就行了。我也曾經這么認為過,直到有一天…......
直接的原因是因為VS生成出來的二進制文件的Header部分里面包含了它對應的PDB的GUID,PDB也包含一個GUIID,這兩個GUID實在編譯的時候添加進去的。VS調試器在載入PDB的時候會去比對這個兩個GUID,如果不一致,那么就不能使用。
當然上面那個原因只是一個表面現象,根本原因是既是兩份一模一樣的代碼編譯器編譯出來的文件可能是不一樣的。因為編譯器在編譯的時候會對代碼進行優化,而同一份代碼可能會有很多種優化的方法,它會根據當時的具體機器的環境等情況選擇一個最快的生成方法。所以它生成出來的文件有可能是不一樣的!所以如果連生成出來的文件都不一樣,那么原來的那個PDB里面的符號對應的地址也就沒有意義了。具體可以看:引用2
這里有一個非常簡單的例子來演示。假設您正在構建的組件由一個函數和一個變量組成。
生成的文件內容是否像這樣重要嗎?
0000: MyFunc ()
0020: gGlobalVariable
還是這個?
0000: gGlobalVariable
0004: MyFunc ()
在功能上,這一點都不重要,但對於調試器來說,這是非常重要的。事實上,如果出錯,可能會對調試會話造成嚴重破壞。在這個例子中,如果你使用來自構建#1的符號文件來嘗試確定全局變量的值,當運行由構建#2構建的組件時,會發生什么?調試器將查閱符號文件並返回地址0020所引用的值。不幸的是,在組件構建#2中,全局變量不在那個地址。這里有一些值組成了MyFunc()的指令流。
以上內容來源:為什么 Visual Studio 要求調試器符號文件與構建它們的二進制文件完全匹配?|微軟文檔 (microsoft.com)
.net C#如何生成.pdb文件
右建項目 屬性》生成》常規》調試符號
自 C# 6.0 起,對於所有編譯器版本而言,pdbonly 與 full 之間沒有任何區別。 請選擇 pdbonly。 若要更改 .pdb 文件的位置,請參閱 PdbFile。
以下為有效值:
DebugType
Value 含義
full 使用當前平台的默認格式向 .pdb 文件發出調試信息:
Windows:Windows pdb 文件。
Linux/macOS:可移植 PDB 文件。
pdbonly 與 full 相同。 有關詳細信息,請參閱下面的注釋。
portable 使用跨平台可移植 PDB 格式向 .pdb 文件發出調試信息。
embedded 使用 可移植 PDB 格式向 .dll/.exe 自身(未生成 .pdb 文件)發出調試信息。
如何查看二進制文件和PDB的GUID?
使用VS自帶的DUMPBIN工具可以查看二進制文件所期望的PDB的GUID。基本用法就是DUMPBIN /HEADER 文件,具體用可可參考MSDN。
查看PDB的GUID可以用下面這個工具,直接將PDB拉進去即可。http://www.codeproject.com/Articles/37456/How-To-Inspect-the-Content-of-a-Program-Database-P
PDB文件的查找策略
在運行調試》窗口》模塊》附加加載信息。就可以pab查找的過程

1. 文件被執行或者被載入的地址
2. 就是硬編碼在PE文件頭中的那個地址。大家可以看到obj\<config>才是最原始生成的地址,只是之后被拷貝到了第一個地址中去了。
2.5 如果配置了符號服務器,第二步以后應該先去符號服務器的緩存目錄下找,如果找不到再去符號服務器上去找。找到的話就會下載到緩存目錄。
3. 第三部分是我VS中設置的一些符號查詢的目錄,因為我裝過Reflector所以默認加了這幾個目錄在我的設置中。
4. Windows文件夾。
這里有一個比較有意思的現象就是,VS的查找策略都是會先找一個目錄下的symbol\exe\project.pdb,然后exe\project.pdb,最后才找project.pdb。這個順序有點出人意料。
PDB文件會影響性能么?
可能有些人會覺得PDB文件的生成會對最終的應用程序的性能產生一定的影響,所以覺得在發布版中不應該生成PDB文件。
錯!對於.NET應用程序來說,生成PDB文件不會影響編譯器的優化,所以也完全不會影響應用的性能。只會對於生成的程序集中的一個DebuggableAttribute的屬性產生影響。有興趣的人可以閱讀Do PDB Files Affect Performance?
VS2022 的符號加載設置
2)在“工具” >“選項” >“調試” >“常規”中禁用【啟用“僅我的代碼”】
3)在“工具” >“環境” >“文檔” >勾選【在解決方案的管理器中顯示雜項】點擊完成,再次運行調試的時候,編譯器就會從https://raw.githubusercontent.com/下載Dll庫的源代碼(僅限.net core)
在github.com頁面點擊raw,就是raw.githubusercontent.com界面,raw.githubusercontent.com返回存儲在github中的文件的原始內容(純文本)。例如:SpinLock.cs
加載符號
Visual Studio 提供兩種自動符號加載模式:
- 自動加載所有模塊的符號,除非排除:如標題所示,除非通過單擊"指定排除的模塊"將模塊添加到排除列表中,否則 Visual Studio 將嘗試為進程中的所有模塊加載符號。如果您希望為進程中的幾乎所有內容加載符號,或者由於內存或調試啟動性能原因,您不希望加載少數非常大的符號,則通常需要此設置。
- 僅指定模塊:默認情況下,此設置將加載磁盤上二進制文件旁邊的符號,但不會嘗試加載任何其他模塊的符號,除非您通過單擊"指定模塊"將它們添加到包含列表中。例如,如果您想使用手動加載,但始終加載名稱中帶有"Microsoft"的任何內容的符號,則可以輸入"*Microsoft*"
vs2022 默認是加載所有符號的,這大型項目中式很影響調試的。所以在大型項目中要設置成按需加載符號,如下操作:
符號未加載的一些常見原因包括:
- 符號路徑未指向正確的位置
- 符號文件來自與進程中加載的模塊版本不同的模塊版本
- Visual Studio 要求符號文件來自與模塊完全相同的內部版本。它無法加載來自不同版本的符號,即使源代碼相同
- [僅限托管]"僅我的代碼"設置阻止調試器加載符號文件
更多符號相關內容請查看
了解符號文件和 Visual Studio 的符號設置 - Azure DevOps 博客 (microsoft.com)
符號文件位置
符號緩存
自動符號加載