Что такое PDB (Program Database) — отладочная информация, символы и Source Link

· Обновлено: · · .NET, CSharp, VisualStudio, PDB, Debugging, Symbols, SourceLink, Diagnostics, Эксплуатация, Использование существующих активов

История изменений (1 обновлений, последнее 30 Aug 2026)

Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.

Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619899)

Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.

Го Комура (2026). Что такое PDB (Program Database) — отладочная информация, символы и Source Link. KomuraSoft LLC. https://comcomponent.com/ru/blog/2026/06/10/000-pdb-program-database/

DOI (зарегистрированный архив)
10.5281/zenodo.21619899
DOI (последняя зарегистрированная версия)
10.5281/zenodo.21619900

1. Что стоит зафиксировать сразу

Когда собирают приложение на .NET или C++, рядом с .dll или .exe нередко появляется файл .pdb.

Например, такой вывод:

MyApp.exe
MyApp.dll
MyApp.pdb

Если разрабатывать, так и не разобравшись, что это за .pdb, рано или поздно всплывают такие вопросы:

  • Можно ли класть .pdb в продакшен?
  • Перестанет ли приложение работать без .pdb?
  • Нормально ли, что .pdb появляется и у Release-сборки?
  • Лежит ли в .pdb исходный код целиком?
  • Гарантирует ли наличие .pdb, что точку останова всегда можно поставить?
  • Почему .pdb нужен при разборе дампов и сбоев?
  • Как обращаться с .pdb в пакетах NuGet?
  • Чем отличаются Source Link, сервер символов и .snupkg?

В повседневной разработке PDB редко оказывается в центре внимания. Но при разборе сбоев, анализе дампов, поставке библиотек, читаемости эксплуатационных логов и удобстве отладки он очень важен.

Сразу вывод.

PDB — это файл отладочной информации, который связывает исполняемый файл или сборку с исходным кодом. Это не сам код приложения, а данные, по которым отладчик и диагностические инструменты понимают: какая инструкция какой строке исходника соответствует, что за локальная переменная перед ними и какой исходный файл нужно открыть.

В статье PDB — не «довесок для отладки», а артефакт сборки, с которым в работе нужно обращаться осознанно.

Куда смотреть в зависимости от задачи

Глав всего 43, читать подряд не обязательно. Если уже понятно, что нужно, достаточно следующих глав.

Что нужно узнать Какие главы читать
Что такое PDB и что в нём лежит гл. 2 → гл. 5 → гл. 6
Только решить, класть ли в продакшен гл. 24 (оси решения) → гл. 25 (конфиденциальность) → гл. 27 (схемы размещения)
Только политика поставки через NuGet гл. 20 (Source Link) → гл. 22 (embedded) → гл. 23 (.snupkg) → гл. 41 (примеры настроек)
Как настроить сборку гл. 13 (типы PDB) → гл. 14 (DebugType) → гл. 41 (рекомендуемые настройки)
Отладчик не загружает символы гл. 18 (порядок поиска) → гл. 29 (как локализовать) → гл. 30 (Just My Code)
Разобрать дамп продакшен-сбоя гл. 11 (трассировка стека) → гл. 12 (анализ дампа) → гл. 26 (хранение в CI/CD)
Сначала снять типичные заблуждения гл. 7–10
Нужны только настройки следующий подраздел и гл. 41

Сразу вывод — практические настройки

Не дожидаясь главы 41, вынесу выводы по трём частым схемам. Причины и исключения — в соответствующих главах.

Для чего Что ставить Коротко
Внутреннее приложение <DebugType>portable</DebugType>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
PDB делать и в Release, и обязательно хранить как артефакт CI
Публичная библиотека NuGet плюс к предыдущему
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
включить Source Link, PDB отдавать через .snupkg
Небольшой внутренний инструмент <DebugType>embedded</DebugType> чтобы PDB не забыли положить рядом; рост размера принимаем

Для продукта, который уходит наружу, обстоятельства расходятся — читайте главу 41. И во всех схемах одно общее правило.

Кладёте PDB в продакшен или нет — PDB этой сборки нужно сохранить в любом случае.

Позже заново получить тот же PDB трудно даже пересборкой из того же коммита (гл. 40).

На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 20, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle

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++ — бинарник, близкий к машинному коду.

Из одного исполняемого файла такие вещи понять уже трудно или вовсе нельзя:

Какой строке какого .cs соответствует этот машинный код / IL
Какой позиции какой функции соответствует этот адрес
Как называлась эта локальная переменная
В какую именно позицию инструкции на самом деле нужно поставить эту точку останова
Какому исходнику соответствует этот кадр стека

PDB как раз закрывает этот разрыв.

3. Нужен ли PDB для запуска

Обычно PDB для запуска приложения не нужен. Если есть .dll или .exe, приложение стартует. Отсутствие .pdb само по себе не мешает обычной работе.

Но без PDB усложняется вот что:

Что хочется сделать Что ломается без PDB
Точно идти по шагам в Visual Studio Строка исходника и позиция выполнения не сопоставляются
Поставить точку останова Неизвестно, какой инструкции она соответствует; точка останова может остаться unbound
Показать имя файла и номер строки в трассировке стека исключения Номера строк нет или он неполный
Разобрать файл дампа Стеки, переменные и типы читать трудно
Зайти внутрь внешней библиотеки Исходник библиотеки привязать не к чему
Читать нативный краш Видны только адреса, без имён функций и позиций

Иначе говоря, 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# в IL или в нативном коде после JIT часто становится несколькими инструкциями. И наоборот, из-за оптимизации несколько строк исходника могут слиться, исчезнуть или выглядеть переставленными.

Отладчик по PDB решает, «какую строку исходника показать сейчас».

6. Чего в PDB нет

Про PDB меньше путаницы, если сначала понять, чего в нём нет, а не только что есть.

Обычно PDB — это не:

  • само приложение
  • обязательный для запуска файл runtime
  • полная резервная копия всего набора исходников
  • замена Git-репозитория
  • средство, которое целиком восстанавливает все настройки сборки и окружения
  • средство, которое само объяснит причину бага

Но есть оговорка.

В PDB могут оказаться пути к исходным файлам, имена типов, имена функций, имена локальных переменных, а иногда — сведения Source Link или встроенный исходный код.

Поэтому нельзя сказать: «это не сам исходник, значит публиковать можно, ни о чём не думая».

Там могут быть внутренние имена проектов, пути с именем пользователя, структура внутренних каталогов, ещё не опубликованные имена типов и имена, по которым угадывается бизнес-логика.

7. Частое заблуждение 1: PDB замедляет продакшен

То, что PDB просто лежит рядом, само по себе не замедляет обычную работу приложения.

PDB нужен, когда отладчику или диагностическому инструменту понадобились символы. Обычная обработка приложения не читает PDB на каждом шаге.

Разумеется, он используется, когда по трассировке стека исключения разрешают имя файла и номер строки, когда подключают отладчик, когда профилировщик или диагностический инструмент читает символы.

Но вывод «PDB всегда всё замедляет, поэтому в продакшен его класть нельзя» — слишком грубый.

На практике безопаснее так:

Удалять PDB только ради скорости выполнения почти не нужно
Где его размещать, решают по аудитории, риску утечки, размеру поставки и политике эксплуатации
Даже если рядом с приложением его нет, PDB той же сборки хранят обязательно

8. Частое заблуждение 2: для Release-сборки PDB не нужен

PDB полезен и в Release. Более того, при сбое в продакшене как раз и нужен PDB от Release-сборки.

Если в продакшене крутится Release, PDB от Debug бесполезен. Отладчику нужен тот PDB, который получили в момент сборки именно этого продакшен-бинарника.

Здесь важно не смешивать три разные вещи:

Что Что это значит
Debug / Release Конфигурация сборки: оптимизация, условная компиляция, настройки вывода и т. п.
Есть PDB или нет Генерируют и сохраняют ли отладочную информацию
Насколько удобно отлаживать Зависит от оптимизации, содержимого PDB, совпадения исходников, поведения JIT и т. п.

Release обычно оптимизирован, поэтому по шагам идти менее наглядно, чем в Debug. Локальные переменные могут пропасть из-за оптимизации, остановка — не в порядке строк исходника.

И всё же с PDB такие сведения получить гораздо проще:

  • строка исходника, где произошло исключение
  • имена методов в стеке
  • соответствующая позиция при разборе дампа
  • имена функций в результатах профилирования
  • сопоставление логов с исходником

Не «раз Release — PDB не нужен». Как раз потому, что это Release, PDB этой сборки нужно сохранить.

9. Частое заблуждение 3: с PDB можно отладить любой бинарник

PDB нельзя подставлять к любому .dll или .exe.

Отладчик проверяет, совпадают ли целевой бинарник и PDB. Если насильно взять чужой PDB, соответствие строк исходника, функций и переменных съедет.

Например, в таких ситуациях PDB может оказаться бесполезным или неприменимым, даже если файл у вас есть:

Локально пересобранный PDB пытаются натянуть на продакшен-DLL
Номер версии тот же, но сборка на самом деле из другого коммита
На DLL после hotfix пытаются загрузить PDB, собранный до hotfix
Отличаются настройки оптимизации или условной компиляции

Не «раз исходник тот же, скорее всего подойдёт», а «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 держится высокой
  • похоже на deadlock
  • память всё растёт
  • ответ не возвращается
  • падение на границе с нативной библиотекой

Тогда снимают дамп и разбирают его.

Но одного дампа мало. В дампе — состояние процесса на тот момент. Чтобы превратить попавшие туда стеки и модули в понятные человеку имена и строки исходника, нужен соответствующий PDB.

В .NET для разбора используют dotnet-dump, Visual Studio, WinDbg, SOS и похожие инструменты. Если в деле нативный код, важна настройка символов в WinDbg.

Типичные промахи при разборе дампа:

Продакшен-DLL сохранилась, PDB нет
PDB есть, но это другой файл, пересобранный локально
Символы Windows / runtime .NET не загружаются
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», стоит уточнить, о каком контексте речь:

О Portable PDB в .NET
О Windows PDB в Visual C++
О старом проекте на .NET Framework
О PDB для поставки через NuGet
О символах, которые читает WinDbg

14. DebugType в .NET

В проектах на C# способ вывода отладочной информации задаёт DebugType.

Основные значения такие:

DebugType Смысл
portable Генерирует Portable PDB отдельным файлом
embedded Встраивает отладочную информацию, эквивалентную Portable PDB, в .dll / .exe
full Генерирует PDB в формате по умолчанию для текущей платформы
pdbonly Начиная с C# 6.0 фактически не отличается от full
none PDB не генерирует

В текущих проектах в стиле .NET SDK значение DebugType для C# по умолчанию равно portable и в Debug, и в Release. Поэтому обычно нет нужды явно указывать 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. PDB в C++ — это немного другой контекст

Контекст PDB в C++ немного отличается от .NET.

В Visual C++ PDB появляется из опций вроде /Zi или /ZI. Кроме того, есть PDB, которым пользуется компилятор, и PDB, который линкер собирает для итогового .exe / .dll.

В C++ PDB критически важен, чтобы читать адреса нативного кода, функции, типы, локальные переменные, инлайнинг и позиции после оптимизации.

При разборе нативных сбоев без PDB картина обычно такая:

Адрес исключения известен
Имя модуля известно
Но имя функции и строка исходника неизвестны

В приложениях, где смешаны C++ и C#, при P/Invoke, C++/CLI и в .NET-приложениях, которые вызывают нативные DLL, нужны не только PDB со стороны .NET, но и PDB со стороны нативного кода.

17. public symbols и private symbols

В мире символов Windows различают public symbols и private symbols.

Упрощённо разница такая:

Вид Какая информация внутри
private symbols Почти полные сведения: локальные переменные, типы, параметры, внутренние детали
public symbols Сведения, урезанные для публикации: имена функций, адреса и т. п.

В символах, которые отдают наружу, private symbols иногда снимают и оставляют только public symbols.

Так балансируют возможность отладки и то, сколько внутренней структуры видно снаружи.

Например: для разбора краша своего продукта хочется хотя бы минимальные имена функций, но не хочется отдавать имена внутренних локальных переменных и сведения о типах. Тогда берут stripped PDB.

В нативной разработке под Windows PDB без private symbols иногда делают инструментами вроде PDBCopy.

При этом для внутреннего разбора сбоев нужно хранить полный PDB. Если остался только PDB, урезанный для внешней поставки, при глубоком разборе будет тесно.

18. Откуда отладчик берёт PDB

Visual Studio и WinDbg ищут PDB в нескольких местах.

Основные из них такие:

Папка вывода проекта
Та же папка, что и .dll / .exe
Исходный путь PDB, записанный внутри .dll / .exe
Папки, указанные в настройках символов Visual Studio
Локальный кэш символов
Внутренний сервер символов
Microsoft Symbol Server
NuGet.org Symbol Server
Серверы символов вроде Azure Artifacts

Если PDB есть, но не загружается, проверяют по порядку:

Совпадает ли PDB с целевым бинарником
Попадает ли PDB на путь поиска отладчика
Доступен ли сервер символов
Не осталась ли в локальном кэше устаревшая копия
Не отключена ли загрузка символов для целевого модуля
Не считается ли модуль внешним кодом из-за Just My Code

В Visual Studio во время отладки в окне Modules видно, загрузились ли символы каждого модуля.

Debug
  Windows
    Modules

Там смотрят Symbol Status нужной DLL.

Часто встречаются такие четыре строки:

Symbols loaded.
Cannot find or open the PDB file.
PDB does not match image.
Skipped loading symbols.

Если PDB «не грузится», короткий путь — сначала открыть окно Modules.

19. Что такое сервер символов

С главы 19 по главу 23 речь о том, куда класть PDB и как довести его до отладчика. Действующих лиц много, поэтому сначала общая схема.

Как PDB доходит до отладчикаСхема показывает, что сборка одновременно даёт dll/exe, pdb и привязку к коммиту исходников; pdb можно положить рядом, сохранить в артефактах CI, опубликовать на сервер символов, раздать как snupkg или встроить в dll; Source Link не доставляет pdb, а по уже полученному pdb забирает исходники.положить в ту же папкухранить в артефактах CIопубликовать на сервер символовраздавать через NuGetвстроить в dllСборкаdll / exe и pdb появляются вместеdll / exeто, что распространяют и запускаютpdbотладочная информациярепозиторий исходниковкоммит этой сборкиКак доставить PDBpdb рядом с dllгл. 27, вариант 1артефакт CIгл. 27, вариант 2сервер символовгл. 19snupkgгл. 23embedded PDBгл. 22Отладчик / диагностические инструментыSource Linkгл. 20 и 21трассировка стека с номерами строкпошаговая отладкаанализ дампа

Левая половина схемы — как PDB хранят и поставляют, правая — как отладчик этим пользуется. Среди всех путей роль Source Link другая. Это не способ доставить PDB, а сведения, по которым из уже полученного PDB идти за исходным кодом. Бинарник, PDB и исходники ведут отдельно, и как раз связать эти три вещи заново — тема глав 19–23.

Теперь — с сервера символов.

Сервер символов — механизм, благодаря которому отладчик может получить файлы символов вроде PDB в тот момент, когда они нужны.

Просто сложить 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

Появляются десятки файлов с одним и тем же именем MyApp.pdb.

Сервер символов раскладывает PDB не просто по имени файла, а по сведениям о соответствии бинарнику. Поэтому отладчику проще найти «PDB, который подходит именно к этой DLL».

Пример конфигурации, которая встречается на практике:

Microsoft Symbol Server
  Нужен, чтобы получать символы Windows, runtime .NET и т. п.

NuGet.org Symbol Server
  Нужен, чтобы получать символы публичных пакетов NuGet

Внутренний сервер символов
  Хранит PDB своих приложений и библиотек

Локальный кэш символов
  Повторно использует уже полученные PDB и ускоряет отладку

Если вы разбираете продакшен-сбои внутренних сервисов, удобно, чтобы CI публиковал PDB во внутреннее хранилище символов.

Source Link связывает PDB с системой управления исходным кодом.

Даже если PDB «знает», что эта позиция соответствует такому-то исходному файлу, отладчик не покажет его, пока файла нет под рукой.

Со Source Link отладчик по сведениям внутри PDB может забрать исходные файлы нужного коммита с GitHub, Azure Repos, GitLab, Bitbucket и т. п.

Иначе говоря, Source Link закрывает такую задачу:

Хочется зайти внутрь библиотеки, полученной через NuGet
Исходник этой библиотеки локально не клонирован
Но есть PDB и сведения о репозитории
Отладчик сам идёт за исходником нужного коммита

Для тех, кто потребляет библиотеку, это сильно улучшает отладку.

Суть Source Link в том, что он ссылается не на «последнюю ветку main», а на «коммит, из которого собрали этот бинарник».

Важно именно то, что связь идёт не с самым свежим исходником, а с исходником на момент сборки.

В SDK .NET 8 и новее работа с Source Link стала проще.

Для часто используемых провайдеров — GitHub, Azure Repos, GitLab, Bitbucket — механизм Source Link теперь входит в сам .NET SDK.

Поэтому прежнее правило «обязательно явно добавлять Microsoft.SourceLink.GitHub и подобные пакеты» постепенно устаревает.

Тем не менее проверять всё же приходится:

Сборка идёт на SDK версии ниже .NET 8
Проект старый, не в стиле SDK
Стоит собственный on-prem 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
  • небольшой внутренний инструмент, для которого отладочную информацию хочется держать вместе с бинарником
  • хочется меньше возиться с поставкой PDB для пакета NuGet

Но есть и минусы:

  • растёт размер сборки
  • отладочная информация всегда есть в поставляемом артефакте
  • контроль над тем, что видно снаружи, становится грубее
  • у крупных библиотек это бьёт по 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; Windows PDB, которые генерируют нативные проекты вроде C++, туда не берут. Если нужно поставлять или хранить Windows PDB, смотрят другие пути: устаревший .symbols.nupkg, внутренний сервер символов или артефакты CI.

Чтобы собрать такой пакет, пишут, например, так:

<PropertyGroup>
  <IncludeSymbols>true</IncludeSymbols>
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

То же можно указать в командной строке.

dotnet pack -c Release -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg

Для публичной библиотеки NuGet .snupkg вместе с Source Link обычно лучше балансирует размер дистрибутива и удобство отладки, чем PDB внутри основного .nupkg.

Но стоит смотреть, что умеют фиды и инструменты. Если внутренний фид NuGet .snupkg не поддерживает, нужен другой способ.

24. Стоит ли класть PDB в продакшен

«Класть ли PDB в продакшен» — не вопрос с простым «да» или «нет».

Осей решения четыре:

Насколько легче разбирать сбои
Риск раскрыть лишнее
Размер поставки
Правила эксплуатации

Для внутренней системы вполне разумно держать .dll и .pdb в одной папке. Тогда в логах исключений чаще видны номера строк, и дамп разбирать проще.

Для приложения, которое уходит наружу, решение включать PDB как есть стоит принимать осторожно. Могут стать видны внутренняя структура, имена локальных переменных, пути к исходникам. Если нужно, оставляют только public symbols, отдают через сервер символов или хранят лишь для поддержки.

Для веб-сервиса, помимо вопроса, класть ли PDB на сервер, думают и о трассировке стека в логах. Трассировку стека во внешнем ответе отдавать не следует. Во внутренних логах её можно оставить, но нужно определить права доступа и срок хранения.

Практическая рекомендация такая:

PDB генерируют всегда
PDB хранят как артефакт сборки
Класть ли в продакшен, решают по тому, насколько система видна снаружи
Если отдаёте наружу, проверяют, какие сведения окажутся видны
Кладут туда, откуда PDB можно достать для разбора дампа

25. PDB — это конфиденциальные данные?

PDB не обязательно требует того же обращения, что сам исходный код или закрытые ключи. Но относиться к нему как к безобидному файлу тоже опасно.

Вот что PDB потенциально может раскрыть:

  • локальные пути разработчиков
  • структуру внутренних папок
  • имена проектов
  • имена классов и методов
  • имена локальных переменных
  • имена внутренних API
  • бизнес-термины
  • URL репозитория Source Link
  • встроенный исходный код
  • настройки Source Server

В частности, в старом механизме Source Server и в части функций нативного PDB отладчик может выполнять команды, чтобы забрать исходник. Недоверенные PDB и серверы символов без проверки лучше не подключать.

Осторожность нужна и когда недоверенный PDB скармливают инструментам и библиотекам, которые его разбирают. Систему, которая автоматически обрабатывает PDB, полученные снаружи, проектируют так, чтобы входные данные не считались доверенными.

Итого безопаснее обращаться с PDB так:

Полные внутренние PDB защищают как внутренние артефакты
Содержимое и аудиторию PDB, которые уходят наружу, проверяют
Если Source Link указывает на private repository, управляют аутентификацией и правами
Недоверенные серверы символов в отладчик не регистрируют

26. Как хранить PDB в CI/CD

PDB, который остался только на локальном компьютере разработчика, бесполезен. При продакшен-сбое нужен 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-файл

Важно не хранение PDB само по себе, а то, чтобы не потерять, какому именно бинарнику этот PDB соответствует.

Схема, при которой в файловом хранилище постоянно перезаписывается один и тот же MyApp.pdb, рано или поздно ломается. Нужно уметь достать PDB по конкретной сборке, версии или коммиту.

27. Практические схемы размещения PDB

На практике с PDB обращаются несколькими способами.

Вариант 1: положить PDB в ту же папку, что и DLL

Самый простой.

publish/
  MyApp.dll
  MyApp.pdb

Плюс — почти ничего не нужно настраивать. Отладчику и runtime его легко найти, номера строк в логах исключений появляются охотнее.

Минус — в поставляемом артефакте оказывается отладочная информация. Для внешней поставки это иногда не подходит.

Вариант 2: на сервер не класть, хранить в артефактах CI

На продакшен-сервер PDB не кладут, оставляют в артефактах CI.

release-artifacts/
  app.zip
  symbols.zip

При продакшен-сбое снимают дампы и логи, достают PDB нужного номера сборки и разбирают.

Так проще ограничить, что видно снаружи, но во время разбора нужна процедура, как PDB достать.

Вариант 3: публиковать на внутренний сервер символов

Для крупных команд это самый удобный вариант.

CI во время сборки публикует PDB в хранилище символов. Visual Studio / WinDbg у разработчиков и тех, кто разбирает инциденты, ходят на этот сервер символов.

Плюс — с PDB многих версий можно работать спокойно. Минус — нужна начальная настройка и контроль доступа.

Вариант 4: раздавать через .snupkg в NuGet

Сильный вариант для публичных библиотек.

MyLibrary.1.2.3.nupkg
MyLibrary.1.2.3.snupkg

Потребитель восстанавливает только обычный пакет, а символы, нужные при отладке, подтягиваются отдельно.

В паре с Source Link заходить в исходник проще даже у внешних библиотек.

Вариант 5: embedded PDB

Удобно, чтобы PDB не забыли приложить.

<PropertyGroup>
  <DebugType>embedded</DebugType>
</PropertyGroup>

Но стоит следить за размером сборки и тем, сколько внутренней информации уходит вместе с бинарником.

28. Что проверить в существующем проекте

Если в существующем .NET-проекте пересматриваете обращение с PDB, начните с этих проверок:

Генерируется ли PDB в Release-сборке
Где хранятся сгенерированные 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>

Впрочем, одни и те же настройки во все проекты подряд класть не стоит. Для внутреннего приложения, внешней библиотеки, on-prem продукта, SaaS и OSS оптимальное решение разное.

29. Если PDB не загружается

Когда PDB не загружается, не стоит сразу пересобирать наугад — лучше идти по шагам.

1. Проверить целевой модуль

В окне Modules Visual Studio найдите нужный 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 или повреждённый кэш. Стоит очистить кэш символов, указать другой кэш или внимательно прочитать подробный лог загрузки.

Где эти настройки в Visual Studio

Ниже — где искать настройки, которые уже встречались в статье. Иерархия окна параметров зависит от версии Visual Studio, поэтому даны и старый, и новый путь.

| Что нужно сделать | Полный путь меню | |—|—| | Посмотреть модули и состояние символов | Debug > Windows > Modules (только во время отладки. Ctrl + Alt + U) | | Вручную загрузить символы этого модуля | в окне Modules правый щелчок по модулю > Load Symbols | | Понять, почему не загрузилось (куда смотрели) | в окне Modules правый щелчок > Symbol Load Information | | Изменить места поиска символов | Tools (или Debug) ` > Options > Debugging > Symbols<br>в новом экране Tools > Options > All Settings > Debugging > General > Symbols > Search Locations | | Включить серверы символов Microsoft / NuGet.org | на том же экране флажки Microsoft Symbol Servers / NuGet.org Symbol Server | | Задать локальный кэш символов | на том же экране Cache symbols in this directory | | Из окна Modules перейти к настройкам символов | в окне Modules правый щелчок > Symbol Settings | | Включить / выключить Just My Code | Tools (или Debug) > Options > Debugging > General > Enable Just My Code<br>в новом экране под All Settings > Debugging > General | | Включить Source Link | на том же Debugging > General пункт Enable Source Link support` |

Про каталог кэша в официальной документации есть два предупреждения.

  • Не указывать защищённые папки вроде C:\Windows. Нужна папка, куда можно и читать, и писать
  • Если задана переменная окружения _NT_SYMBOL_PATH, она перекрывает настройку Cache symbols in this directory

Ещё один момент: Enable Just My Code — глобальная настройка всего Visual Studio. Это не настройка проекта. Если переключили её под одну задачу и забыли вернуть, в другой задаче это всплывёт как «почему-то нельзя step into».

DebugType же лучше писать прямо в <PropertyGroup> csproj, а не через GUI. В свойствах проекта соответствующий пункт есть, но и имя, и место зависят от того, SDK-style ли проект, и от версии Visual Studio. В csproj запись остаётся и в ревью, и в diff (гл. 14).

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», стоит проверить и настройки отладчика. Где именно они лежат, собрано в главе 29, в подразделе «Где эти настройки в Visual Studio». И Enable Just My Code, и Enable Source Link support находятся в Tools (или Debug) ` > Options > Debugging > General`.

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 нужно зафиксировать явно.

Например — включать ли PDB в образ Docker.

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Если PDB есть в publish/, они попадут и в образ как есть.

У этого есть плюсы:

  • внутри контейнера проще получить номера строк в трассировке стека
  • при снятии дампа соответствующие файлы проще найти в той же файловой системе
  • во время разбора сопоставление проще

Но есть и опасения:

  • растёт размер образа
  • отладочная информация оказывается в продакшен-образе
  • если образ уходит наружу, снаружи видно больше

Для внутреннего SaaS включить PDB в образ — вполне допустимый выбор. Для on-prem продукта, который отдают внешним заказчикам, PDB иногда лучше хранить отдельно.

В любом случае, даже если PDB в образ не кладут, соответствующий PDB как артефакт хранить обязательно.

34. Публикация в один файл и PDB

В .NET есть публикация в единый файл.

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, но и генерируемые нативные символы, а также отладочная информация конкретной платформы.

Например, на стороне нативного кода нужно учитывать DWARF в Linux, dSYM в macOS, PDB в Windows.

Даже для .NET-приложений в таких конфигурациях проектирование символов усложняется:

Native AOT
Self-contained publish
PublishSingleFile
ReadyToRun
Вызов нативной DLL через P/Invoke
Наличие C++/CLI

Для обычных веб-приложений и библиотек классов на первое время достаточно понимать Portable PDB и Source Link. Но чем сложнее формат поставки, тем важнее заранее заложить в проектирование сборки вопрос «как разбирать этот краш».

36. Нюансы для проектов на .NET Framework

В старых проектах на .NET Framework настройки и значения по умолчанию иногда отличаются от проектов в стиле SDK.

Например, такие различия:

Старый формат csproj
Используется packages.config
Значение DebugType по умолчанию отличается от текущего .NET
Используется Windows PDB
Для настройки Source Link нужны дополнительные пакеты или настройки MSBuild
Версия MSBuild в CI старая

В .NET Framework подход к PDB тот же:

Для запуска не обязателен
Важен для отладки и разбора сбоев
Нужен PDB, совпадающий с целевым бинарником
PDB Release-сборки стоит хранить

Но если просто скопировать объяснения для текущего .NET, в старом проекте всё может работать не так, как ожидается.

В уже существующем проекте сначала смотрят фактический результат сборки.

msbuild MyApp.csproj /p:Configuration=Release
Get-ChildItem bin\Release -Filter *.pdb -Recurse

После этого уже приводят в порядок настройки сборки и артефакты CI.

37. Что делать в OSS-библиотеке

Для OSS-библиотеки на .NET в целом стоит такая конфигурация:

Генерировать PDB даже для Release
Использовать Portable PDB
Включить Source Link
Публиковать .snupkg в NuGet
Настроить метаданные 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-репозиторию
Останется ли доступ к исходнику после увольнения или перевода сотрудника

Не стоит допускать ситуацию, когда PDB есть только на чьём-то локальном компьютере, даже если библиотека внутренняя.

Их генерируют в CI и кладут туда, откуда команда сможет их достать.

39. Что делать в on-prem продукте

Для on-prem продукта, который ставят в среду заказчика, обращение с PDB усложняется.

Если приложить PDB, дампы и логи, снятые у заказчика, читать проще. Но при этом внутренняя структура становится заметнее.

Часто рассматривают такие варианты:

PDB не прикладывают, но полную версию хранят на стороне вендора
Для поддержки заказчика отдают только public symbols
При сбое дамп забирают и разбирают на стороне вендора с PDB
Для важных заказчиков дают ограниченный пакет символов

Важно не потерять PDB после релиза.

С on-prem продуктами иногда приходится разбирать сбой в версии, выпущенной несколько лет назад. Если к этому моменту соответствующего PDB нет, возможности разбора резко падают.

40. Если PDB уже удалили

Если PDB удалить, приложение продолжит работать. Но позже вы упрётесь в трудности.

Может казаться, что пересборка из того же коммита всё восстановит. Но полное воспроизведение оказывается на удивление трудным:

Другая версия SDK
Другой результат разрешения NuGet
Другое время сборки или другие переменные окружения
Другой сгенерированный код
Другая условная компиляция
Есть настройки, которые применяются только в CI
Другие зависимые нативные инструменты

Детерминированная сборка повышает воспроизводимость, но даже тогда безопаснее «сохранять как артефакт сборки с самого начала».

PDB близок к страховке. Ценность проявляется только тогда, когда он действительно нужен. А если его нет именно в тот момент, когда понадобился, это уже не поправить.

41. Практические настройки

Единой настройки на все проекты нет. Но для типичного .NET-приложения можно взять за основу следующее.

Внутренние приложения

<PropertyGroup>
  <DebugType>portable</DebugType>
  <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

Политика такая:

Генерировать PDB даже для Release
Класть ли в продакшен, решают по политике эксплуатации
Обязательно сохранять в артефактах CI
Подготовить процедуру разбора дампов

Публичные библиотеки 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.

Какому DLL / EXE соответствует этот PDB
Из какого коммита собрана эта DLL / EXE
Это PDB продакшен-Release, а не Debug
Хранится ли PDB как артефакт CI
Есть ли сервер символов или процедура, как PDB достать
Включён ли Source Link
Уместны ли права доступа к источнику исходников
Нет ли в PDB сведений, которые нежелательно раскрывать
Для внешней поставки определена ли политика public/private symbols
Проверено ли, что PDB загружается при разборе дампа

Особенно важны эти три пункта:

PDB — сведения для разбора, а не исполняемый файл
PDB должен совпадать с целевым бинарником
PDB нужно сохранить до того, как случится продакшен-сбой

43. Итог

PDB — это не «непонятный файл» рядом с .dll или .exe, а отладочная информация, которая связывает собранный бинарник с исходным кодом, который читает разработчик.

Для обычного запуска приложения он не обязателен. Но он важен для отладки, разбора исключений, анализа дампов, профилирования и захода внутрь внешних библиотек.

PDB полезен и в Release. Более того, при продакшен-сбое нужен именно PDB, соответствующий Release-сборке.

Класть ли PDB в продакшен, решают по тому, сколько внутренней информации окажется видно, и по политике эксплуатации. Но генерировать PDB и хранить его как артефакт сборки нужно почти в любом проекте.

На практике за основу стоит взять такую политику:

Генерировать PDB даже для Release
Хранить PDB как артефакт CI/CD
Связывать бинарник, PDB, ID коммита и номер сборки
Давать дойти до исходника через Source Link
Для библиотек NuGet рассматривать .snupkg
При внешней поставке проверять, какие сведения о символах окажутся видны

Пока всё спокойно, PDB незаметен. Но когда что-то ломается, именно он становится той зацепкой, которая возвращает разбирающего к исходному коду.

Вместо «PDB можно удалить, и всё будет работать» лучше думать так:

PDB — это карта, которая понадобится, когда в будущем придётся разбирать сбой.

Источники

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Как безопасно менять унаследованное бизнес-приложение без тестов — характеризационные тесты и рефакторинг на практике

На примерах C# разбираем, как безопасно менять бизнес-приложение без тестов: как зафиксировать текущее поведение характеризационным тесто...

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Что такое файл PDB?
PDB — это сокращение от Program Database, файл отладочной информации, который также называют файлом символов. Он связывает собранные .dll и .exe с исходным кодом, который читает разработчик. Внутри — имена функций и методов, имена локальных переменных, имена исходных файлов и номера строк, соответствие между позицией в исходнике и скомпилированными инструкциями, а также сведения, по которым Source Link забирает исходники. Отладчик и диагностические инструменты смотрят в PDB, чтобы понять, какой строке исходного кода соответствует данная инструкция.
Будет ли приложение работать без PDB? Можно ли его удалить?
Обычно PDB для запуска не нужен: если есть .dll или .exe, приложение стартует. Но без PDB сложнее ставить точки останова и идти по шагам, показывать имя файла и номер строки в трассировке стека исключения, разбирать дампы и заходить внутрь внешних библиотек. PDB — не файл, без которого программа не запускается, а файл, без которого её трудно разбирать. Класть его в продакшен или нет — отдельный вопрос; как артефакт сборки его нужно сохранять всегда. Если PDB удалили, полное воспроизведение даже пересборкой из того же коммита оказывается на удивление трудным, и при разборе сбоя это уже не поправить.
Нужен ли PDB для Release-сборки?
Да, нужен. Более того, при сбое в продакшене как раз и требуется PDB от Release-сборки. Если в продакшене крутится Release, PDB от Debug бесполезен: отладчику нужен тот PDB, который получили в момент сборки именно этого продакшен-бинарника. PDB должен совпадать с целевым бинарником, поэтому в CI/CD базово хранят вместе ID коммита, номер сборки, .dll/.exe и .pdb. В современных проектах в стиле .NET SDK Portable PDB по умолчанию генерируется и для Debug, и для Release.
Можно ли класть файлы PDB в продакшен?
Это не простое «да» или «нет». Смотрят на четыре оси: насколько легче разбирать сбои, какой риск раскрыть лишнее, какой размер поставки и какие правила эксплуатации. Для внутренней системы положить .pdb в ту же папку, что и .dll, вполне разумно: в логах исключений чаще появляются номера строк. При внешней поставке стоит быть осторожнее: из PDB могут быть видны локальные пути, внутренняя структура папок, имена типов и локальных переменных, URL репозитория из Source Link. Если нужно, оставляют только public symbols или отдают символы через сервер символов. Даже если PDB рядом с приложением не кладут, хранить PDB той же сборки обязательно.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог