Skip to content

Support embedded portable PDBs for managed symbol resolution#2434

Open
JeremyKuhne wants to merge 3 commits into
microsoft:mainfrom
JeremyKuhne:embedded-portable-pdb-support
Open

Support embedded portable PDBs for managed symbol resolution#2434
JeremyKuhne wants to merge 3 commits into
microsoft:mainfrom
JeremyKuhne:embedded-portable-pdb-support

Conversation

@JeremyKuhne
Copy link
Copy Markdown
Member

@JeremyKuhne JeremyKuhne commented Jun 3, 2026

Summary

Managed assemblies built with <DebugType>embedded</DebugType> carry their portable PDB inside the PE image rather than in a standalone .pdb file. Previously SymbolReader could not read these, so source/line lookup failed for such modules. This PR adds the ability to read embedded portable PDBs.

Changes

  • SymbolReader.OpenEmbeddedPortablePdb — reads the EmbeddedPortablePdb debug-directory entry from a module and returns a PortableSymbolModule. Results are cached under a key suffixed so they cannot collide with standalone-PDB cache entries.
  • PortableSymbolModule — gains a constructor that takes a MetadataReaderProvider (which owns its own backing memory, so the module survives disposal of the PEReader/FileStream used to open it).
  • TraceLog.OpenPdbForModuleFile — falls back to the module's embedded portable PDB when the on-disk module matches the trace and no standalone PDB exists.

Tests

Adds an EmbeddedPdbTestApp fixture (built with <DebugType>embedded</DebugType>) and covers:

  • the happy path (source/line resolution),
  • readability after the PEReader is disposed,
  • caching,
  • not-embedded and missing-file cases,
  • the end-to-end TraceLog fallback path.

All tests run cross-platform on net462 and net8.0 in Debug.

Managed assemblies built with <DebugType>embedded</DebugType> carry their
portable PDB inside the PE image rather than in a standalone .pdb file.
Previously SymbolReader could not read these, so source/line lookup failed
for such modules.

Add the ability to open an embedded portable PDB:

- SymbolReader.OpenEmbeddedPortablePdb reads the EmbeddedPortablePdb
  debug-directory entry from a module and returns a PortableSymbolModule.
  Results are cached under a key suffixed so they cannot collide with
  standalone-PDB cache entries.
- SymbolReader.OpenSymbolFileForModuleFile is a module-oriented entry point
  that prefers a standalone PDB and falls back to an embedded portable PDB.
- PortableSymbolModule gains a constructor that takes a
  MetadataReaderProvider (which owns its own backing memory).
- TraceLog.OpenPdbForModuleFile falls back to the module's embedded portable
  PDB when the on-disk module matches the trace and no standalone PDB exists.

Tests: add an EmbeddedPdbTestApp fixture (built with embedded PDBs) and
cover the happy path, caching, not-embedded/missing-file cases, the
module-oriented entry point (embedded and standalone), and the end-to-end
TraceLog fallback path. All run cross-platform on net462 and net8.0.
@JeremyKuhne JeremyKuhne requested a review from a team as a code owner June 3, 2026 00:17
@JeremyKuhne
Copy link
Copy Markdown
Member Author

Also tried the nupkg in a project where I need this support and it worked fine.

In Release the C# compiler optimizes EmbeddedTarget.Add, shifting its first sequence point from the 'int sum = a + b;' line to the 'return' line, so the source-line assertions failed in the Release CI leg. Pin Optimize=false on the fixture so its emitted sequence points are stable regardless of the build configuration.
Copy link
Copy Markdown
Member

@brianrob brianrob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate you putting this together. A couple of comments below.

Comment thread src/TraceEvent/Symbols/SymbolReader.cs Outdated
if (TraceModuleUnchanged(moduleFile, symReader.m_log))
{
pdbFileName = symReader.FindSymbolFilePathForModule(moduleFile.FilePath);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work for traces that are captured on the same machine before the binaries are removed or overwritten. Should we be looking to capture the PDBs during trace merge so that anyone who captures a trace where binaries have embedded PDBs will have them extracted and zipped up with the etl (ala NGEN PDBs from .NET Framework)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think so. I can do a follow up that explores that if you'd like. :)

Per PR review: this wrapper was called only from tests, and TraceLog.OpenPdbForModuleFile already provides the standalone-then-embedded fallback for the trace path. Removed it and its three dedicated tests, keeping OpenEmbeddedPortablePdb (the building block TraceLog uses).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants