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 とメタデータ、ネイティブ 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 開発者の感覚では、主にこのあたりの情報を期待します。
ソースファイルとコンパイル後コードの対応
ソース行番号
メソッドや関数のシンボル
ローカル変数名
スコープ情報
ソースファイルのパスやチェックサム
Source Link の情報
場合によっては埋め込まれたソース
特に大事なのは、ソース上の位置と実行時の位置の対応です。
C# で書いた 1 行が、IL や JIT 後のネイティブコードでは複数の命令になることがあります。 逆に、最適化によって複数のソース行がまとめられたり、消えたり、順序が変わったように見えたりすることもあります。
デバッガーは PDB の情報を使って、「いま表示すべきソース行」を判断します。
6. PDBに入っていないもの
PDB については、入っているものより、入っていないものを理解した方が誤解が減ります。
PDB は、通常、こういうものではありません。
- アプリケーション本体ではない
- 実行に必須のランタイムファイルではない
- ソースコード一式の完全なバックアップではない
- Git リポジトリの代わりではない
- すべてのビルド設定や環境情報を完全に復元するものではない
- それだけで不具合原因を自動的に説明してくれるものではない
ただし、注意点があります。
PDB には、ソースファイルのパス、型名、関数名、ローカル変数名、場合によっては Source Link 情報や埋め込みソースが含まれ得ます。
そのため、PDB は「ソースコードそのものではないから、何も気にせず公開してよい」とは言えません。
内部プロジェクト名、ユーザー名を含むパス、社内ディレクトリ構成、未公開の型名、業務ロジックを推測できる名前などが含まれる可能性があります。
7. よくある誤解1: PDBがあると本番が遅くなる
PDB が横に置いてあるだけで、通常のアプリケーション処理が遅くなるわけではありません。
PDB は、デバッガーや診断ツールがシンボル情報を必要としたときに使うものです。 アプリケーションの通常処理が、毎回 PDB を読みながら動くわけではありません。
もちろん、例外スタックトレースでファイル名や行番号を解決する、デバッガーをアタッチする、プロファイラーや診断ツールがシンボルを読む、といった場面では利用されます。
しかし、「PDB を置くと常時遅くなるから、絶対に本番に置いてはいけない」という理解は雑です。
実務上は、こう考えるのが安全です。
実行性能の理由だけで PDB を消す必要は薄い
公開範囲・情報漏えい・成果物サイズ・運用ポリシーの理由で配置方針を決める
配置しない場合でも、同じビルドの PDB は必ず保管する
8. よくある誤解2: ReleaseビルドならPDBはいらない
Release ビルドでも PDB は有用です。むしろ、本番障害の調査で必要になるのは Release ビルドの PDB です。
本番で動いているのが Release ビルドなら、Debug ビルドの PDB を持っていても意味がありません。 デバッガーが必要とするのは、その本番バイナリを作ったときに生成された PDB です。
ここで重要なのは、この区別です。
| 項目 | 意味 |
|---|---|
| Debug / Release | 最適化、条件付きコンパイル、出力設定などのビルド構成 |
| PDB の有無 | デバッグ情報を生成・保存するか |
| デバッグしやすさ | 最適化の有無、PDB の内容、ソースの一致、JIT 挙動などで決まる |
Release ビルドは最適化されていることが多いため、Debug ビルドよりステップ実行は分かりにくくなります。 ローカル変数が最適化で消えたり、ソース行の順序通りに止まらなかったりすることがあります。
それでも、PDB があればこうした情報は得やすくなります。
- 例外が起きたソース行
- スタック上のメソッド名
- ダンプ解析時の対応位置
- プロファイリング結果の関数名
- ログとソースの突き合わせ
Release だから PDB が不要、ではありません。 Release だからこそ、そのビルドに対応する PDB を残しておく必要があります。
9. よくある誤解3: PDBがあればどんなバイナリでもデバッグできる
PDB は、どの .dll や .exe にでも使い回せるわけではありません。
デバッガーは、対象バイナリと PDB が一致しているかを確認します。 一致していない PDB を無理に使うと、ソース行、関数、変数の対応がずれてしまうからです。
たとえば、こういう状況では、PDB があっても使えない、または役に立たないことがあります。
手元で再ビルドした PDB を本番の DLL に当てようとしている
同じバージョン番号だが、実際には別コミットからビルドされている
hotfix 後の DLL に、hotfix 前の PDB を読み込ませようとしている
最適化設定や条件付きコンパイルが違う
PDB は「同じソースならだいたい合う」ではなく、「同じビルド成果物に対応している必要がある」と考えるべきです。
そのため、CI/CD ではこの単位で保存するのが基本です。
コミットID
ビルド番号
成果物のバージョン
.dll / .exe
.pdb
ソース参照情報
この組み合わせを崩さないことが重要です。
10. よくある誤解4: PDBがあればソースコードがなくても完全に読める
PDB があっても、ソースコードが必ず入っているわけではありません。
PDB は主に、ソースとバイナリを対応づける情報を持ちます。 ソースファイルのパスやチェックサム、Source Link の情報は持てますが、通常の PDB が常にソース本文を丸ごと持つとは限りません。
そのため、デバッガーで外部ライブラリへステップインしたい場合は、このうちどれかが必要になります。
手元に同じソースファイルがある
Source Link によって正しいコミットのソースを取得できる
PDB にソースが埋め込まれている
デコンパイルしたソースで代用する
Visual Studio には、.NET アセンブリをデコンパイルして表示する機能もあります。 ただし、デコンパイル結果は元のソースそのものではありません。 コメント、空白、ローカル変数名、元の書き方、プリプロセッサ条件などは失われたり変わったりします。
調査で使うには便利ですが、元ソースの代替として過信しない方がよいです。
11. PDBとスタックトレース
.NET の例外スタックトレースでは、PDB があるかどうかで見え方が変わります。
PDB がない場合でも、メソッド名や型名が表示されることはあります。 .NET アセンブリにはメタデータがあるためです。
しかし、ファイル名や行番号まで出したい場合は、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 ランタイムのシンボルを読み込めていない
自社アプリの PDB はあるが、サードパーティライブラリのシンボルがない
シンボルパスが設定されておらず、デバッガーが PDB を見つけられない
ダンプ解析は、問題が起きてから準備しても間に合わないことがあります。 ビルド時点で PDB を保存し、調査時に取り出せるようにしておくことが重要です。
13. Windows PDBとPortable PDB
PDB には複数の形式があります。
実務で押さえるべき代表は、この 2 つです。
| 種類 | 主な文脈 | 特徴 |
|---|---|---|
| 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 です。
そのため、通常は Portable PDB を出すためだけに DebugType を明示する必要はありません。
明示的に書く意味があるのは、プロジェクトや組織として「この形式に固定する」と示したい場合、古いプロジェクトとの違いを見えやすくしたい場合、または 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ランタイムなどのシンボル取得に使う
NuGet.org Symbol Server
公開NuGetパッケージのシンボル取得に使う
社内シンボルサーバー
自社アプリ・自社ライブラリのPDBを保存する
ローカルシンボルキャッシュ
一度取得したPDBを再利用してデバッグを速くする
社内サービスで本番障害を調べるなら、CI が PDB を社内シンボルストアへ発行する仕組みを用意すると便利です。
20. Source Linkとは何か
Source Link は、PDB とソース管理システムを結びつける仕組みです。
PDB が「この位置はこのソースファイルに対応する」と分かっても、そのソースファイルが手元にないと、デバッガーは表示できません。
Source Link があると、デバッガーは PDB に入った情報を使って、GitHub、Azure Repos、GitLab、Bitbucket などから、対応するコミットのソースファイルを取得できます。
つまり、Source Link が解決するのはこういう問題です。
NuGetで取得したライブラリの中にステップインしたい
手元にそのライブラリのソースをcloneしていない
しかし、PDBとリポジトリ情報はある
デバッガーが正しいコミットのソースを取りに行く
これは、ライブラリ利用者の体験を大きく改善します。
Source Link のポイントは、「最新の main ブランチ」ではなく、「そのバイナリを作ったコミット」を参照することです。
最新のソースではなく、ビルド時点のソースへ結びつくことに意味があります。
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パッケージのメタデータまで整えたい
NuGet パッケージでリポジトリ情報を出したい場合は、この設定を使います。
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup>
未追跡ファイルを 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 は基本的に 管理コードの Portable PDB 向けという点です。
少なくとも NuGet.org のシンボルサーバーでは Portable PDB のみがサポートされ、C++ などのネイティブプロジェクトが生成する Windows PDB は受け付けられません。
Windows PDB を配布・保管したい場合は、レガシーな .symbols.nupkg、社内シンボルサーバー、CI artifact など別の経路を検討します。
作成するには、たとえばこう書きます。
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
コマンドラインで指定することもできます。
dotnet pack -c Release -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
公開 NuGet ライブラリなら、PDB を .nupkg 本体に入れるより、.snupkg と Source Link を使う方が配布サイズとデバッグ体験のバランスを取りやすいです。
ただし、フィードやツールの対応状況には注意します。
社内 NuGet フィードが .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 は、開発者のローカル PC にだけ残っていても意味がありません。 本番障害が起きたときに必要なのは、実際にリリースされたビルドに対応する PDB です。
そのため、CI/CD ではこういう形で保管します。
ビルド番号: 2026.06.10.1234
コミット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 だけを置き続ける運用は、いつか壊れます。
ビルド単位、バージョン単位、コミット単位で取り出せるようにします。
27. PDBの配置パターン
PDB の扱いには、いくつかの実務パターンがあります。
パターン1: DLLと同じフォルダーにPDBを置く
最も単純です。
publish/
MyApp.dll
MyApp.pdb
メリットは、設定が簡単なことです。 デバッガーやランタイムが見つけやすく、例外ログの行番号も出しやすくなります。
デメリットは、配布物にデバッグ情報が含まれることです。 外部配布には向かない場合があります。
パターン2: PDBは配置せず、CI成果物に保管する
本番サーバーには PDB を置かず、CI の artifact に残します。
release-artifacts/
app.zip
symbols.zip
本番障害時は、ダンプやログを取得し、対応するビルド番号の PDB を取り出して解析します。
情報公開範囲を抑えやすい一方、調査時に取り出す手順が必要です。
パターン3: 社内シンボルサーバーへ発行する
大規模チームでは、これが扱いやすいです。
CI がビルド時に PDB をシンボルストアへ発行します。 開発者や調査者の Visual Studio / WinDbg は、そのシンボルサーバーを参照します。
メリットは、複数バージョンの PDB を安全に扱いやすいことです。 デメリットは、初期構築とアクセス制御が必要なことです。
パターン4: NuGetの.snupkgで配布する
公開ライブラリでは有力です。
MyLibrary.1.2.3.nupkg
MyLibrary.1.2.3.snupkg
利用者は通常のパッケージだけを復元し、デバッグ時に必要なシンボルだけが取得されます。
Source Link と組み合わせると、外部ライブラリでもソースへステップインしやすくなります。
パターン5: 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が残っていないため、行番号の意味を検証できない
コンテナイメージは残っているが、対応するソースコミットが分からない
そのため、ログには行番号だけでなく、ビルド情報も出すとよいです。
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 には単一ファイル発行があります。
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
このときも、デバッグ情報の扱いを確認する必要があります。
単一ファイルにしたからといって、障害調査が不要になるわけではありません。 むしろ、配布形態が特殊になるほど、対応するシンボルとソースをどう保存するかが大切です。
方針として決めるのは、このあたりです。
PDBを別ファイルとして配布するのか
DebugType=embedded にするのか
シンボルは社内にだけ保存するのか
クラッシュダンプをどう解析するのか
単一ファイル発行、トリミング、AOT などを使う場合は、通常の IL アセンブリとは調査体験が変わることがあります。 リリース前に「クラッシュしたらどう読むか」を一度試しておくのが安全です。
35. PDBとトリミング / AOT
現行 .NET では、トリミングや 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 artifact を整備します。
37. OSSライブラリではどうするか
OSS の .NET ライブラリなら、基本的にはこの構成をおすすめします。
ReleaseでもPDBを生成する
Portable PDBを使う
Source Linkを有効にする
NuGetに.snupkgを公開する
Repositoryメタデータを設定する
決定的ビルドを意識する
例です。
<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 フィードを使っているなら、このあたりを検討します。
社内フィードが.snupkgに対応しているか
対応していないならPDBを.nupkgに含めるか
社内シンボルサーバーを用意するか
Gitリポジトリへの認証をどうするか
退職者や異動後もソースにアクセスできるか
内部向けだからといって、ローカル PC にだけ PDB がある状態は避けます。
CI で生成し、チームが取り出せる場所に保存します。
39. オンプレ製品ではどうするか
顧客環境へ配布するオンプレ製品では、PDB の扱いが難しくなります。
PDB を同梱すれば、顧客先で取ったダンプやログを読みやすくなります。 しかし、内部構造が見えやすくなります。
よくある選択肢はこのあたりです。
PDBは同梱しないが、ベンダー側で完全版を保管する
顧客サポート用にpublic symbolsだけ提供する
障害時にダンプを回収し、ベンダー側でPDBを使って解析する
重大顧客向けに限定的なシンボルパッケージを提供する
大事なのは、リリース後に PDB を失わないことです。
オンプレ製品では、数年前のバージョンの障害を調べることもあります。 そのとき、対応する PDB がないと、調査能力が大きく落ちます。
40. PDBを消してしまったらどうなるか
PDB を消してしまっても、アプリケーションは動きます。 しかし、後から困ります。
同じコミットから再ビルドすれば再現できる、と考えるかもしれません。 しかし、完全再現は意外と難しいです。
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を公開する
不要なソース埋め込みは避ける
公開されるメタデータを確認する
小さな内部ツール
<PropertyGroup>
<DebugType>embedded</DebugType>
</PropertyGroup>
方針です。
PDBの置き忘れを避ける
配布物のサイズ増加を許容する
内部利用に限定する
外部配布製品
完全版PDBを社内で保管する
必要ならpublic symbolsを別途作る
顧客配布物に含めるPDBの内容を確認する
サポート時のダンプ回収と解析手順を決める
42. PDBを見るときのチェックリスト
最後に、PDB の扱いで迷ったときのチェックリストを置いておきます。
このPDBはどのDLL / EXEに対応しているか
そのDLL / EXEはどのコミットからビルドされたか
Debugビルド用ではなく、本番Release用のPDBか
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、コミット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
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
.NETでGC待ちとメモリリークを見分ける ── 増えるメモリを観測・比較・証明する実務手順
.NETアプリケーションで、メモリが増えている理由がガベージコレクション待ちなのか、本当にメモリリークしているのかを、dotnet-counters、dotnet-gcdump、dotnet-dumpを使って切り分ける手順を整理します。
Windowsの偽装トークンを正しく扱う ── スレッド単位の権限借用と安全な戻し方
Windowsの偽装トークンについて、アクセストークン、プライマリトークン、スレッドトークン、偽装レベル、RevertToSelf、.NETのWindowsIdentity.RunImpersonatedまで、実務で安全に扱うための考え方を整理します。
TCPでSendした単位ごとにReceiveできるという誤解 ── バイトストリームとして扱うための受信設計
TCP通信で、SendやWriteした単位ごとに受信できると思い込むと、分割・結合・文字化け・プロトコル破損が起きます。TCPをバイトストリームとして扱い、アプリケーション側でフレーミングする考え方と、.NET/C#での実装例を整理します。
Roslynとは何か ── C#コードをコンパイラの視点で読む・直す・生成する
Roslyn(.NET Compiler Platform)の概要、Syntax Tree、SemanticModel、Workspace、Analyzer、Source Generator、実務での使いどころと注意点を整理します。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
不具合調査・原因解析
再現しにくい障害、長期稼働後の不具合、通信停止などの調査を支援します。
著者プロフィール
記事の著者プロフィールページです。
小村 豪
合同会社小村ソフト 代表
Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。
公開リンク