Skip to content

Add source info to perfmap/jitdump#126836

Open
a74nh wants to merge 10 commits intodotnet:mainfrom
a74nh:linemapping_github
Open

Add source info to perfmap/jitdump#126836
a74nh wants to merge 10 commits intodotnet:mainfrom
a74nh:linemapping_github

Conversation

@a74nh
Copy link
Copy Markdown
Contributor

@a74nh a74nh commented Apr 13, 2026

  • Add PerfMapSymbolReader and GetInfoForMethod in System.Private.CoreLib to read PDB/embedded PDB sequence points for perfmap/jitdump.
  • Retarget the native delegate creation to System.Diagnostics.StackTrace from SOS.NETCore
  • Add JIT_CODE_DEBUG_INFO records using managed debug info, gated behind config variable

* Add PerfMapSymbolReader and GetInfoForMethod in System.Private.CoreLib
  to read PDB/embedded PDB sequence points for perfmap/jitdump.
* Retarget the native delegate creation to System.Diagnostics.StackTrace
  from SOS.NETCore
* Add JIT_CODE_DEBUG_INFO records using managed debug info, gated behind
  config variable
Copilot AI review requested due to automatic review settings April 13, 2026 14:09
@dotnet-policy-service dotnet-policy-service bot added linkable-framework Issues associated with delivering a linker friendly framework community-contribution Indicates that the PR has been added by a community member labels Apr 13, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds source/line debug-info support to perfmap/jitdump by introducing a managed PDB sequence-point reader and wiring CoreCLR’s perfmap/jitdump pipeline to emit JIT_CODE_DEBUG_INFO records (gated by a new config knob).

Changes:

  • Add PerfMapSymbolReader (System.Diagnostics.StackTrace) to read sequence points from associated/embedded Portable PDBs for a given method token.
  • Retarget native delegate creation to System.Diagnostics.StackTrace.GetInfoForMethod in System.Private.CoreLib and add re-entrancy guards.
  • Extend jitdump logging to optionally emit JIT_CODE_DEBUG_INFO records and plumb debug-info payloads from perfmap.cpp into PAL_PerfJitDump_LogMethod.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/PerfMapSymbolReader.cs New managed helper to read sequence points from associated/embedded PDBs into a native buffer.
src/libraries/System.Diagnostics.StackTrace/src/System.Diagnostics.StackTrace.csproj Include new source file in the StackTrace library build.
src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLink.Descriptors.LibraryBuild.xml Preserve PerfMapSymbolReader for trimming scenarios.
src/coreclr/vm/perfmap.h Add s_EmitDebugInfo toggle for debug-info emission.
src/coreclr/vm/perfmap.cpp Build/emit serialized jitdump debug-info payload from managed sequence points when enabled.
src/coreclr/vm/gdbjithelpers.h Update debug-info delegate signature to return int and take MethodDebugInfo*.
src/coreclr/vm/gdbjit.cpp Add re-entrancy guard and update delegate invocation for new signature.
src/coreclr/pal/src/misc/perfjitdump.cpp Add JIT_CODE_DEBUG_INFO record support and extend logging API to accept debug/unwind payloads + sizes.
src/coreclr/pal/inc/pal.h Update PAL_PerfJitDump_LogMethod signature to include debug/unwind payload sizes and const pointers.
src/coreclr/inc/clrconfigvalues.h Add PerfMapEmitDebugInfo config knob.
src/coreclr/dlls/mscoree/exports.cpp Create the native delegate against System.Private.CoreLib / System.Diagnostics.StackTrace.GetInfoForMethod.

Comment on lines +24 to +33
/// <summary>
/// Populate a native buffer with sequence points for the specified method token using the assembly's associated or embedded PDB.
/// </summary>
/// <returns>true if at least one sequence point was written; otherwise false.</returns>
internal static bool GetInfoForMethod([MarshalAs(UnmanagedType.LPUTF8Str)] string assemblyPath, int methodToken, IntPtr points, int size)
{
if (string.IsNullOrEmpty(assemblyPath) || points == IntPtr.Zero || size <= 0)
{
return false;
}
@a74nh
Copy link
Copy Markdown
Contributor Author

a74nh commented Apr 13, 2026

This PR is useful for any tool or developer that uses linux perf to analyse .NET applications. Specifically, support is being added for use by Arm Performix, with the aim of using this to help migrate users.

Example output of perf annotate showing the line numbers from an example C# that was run through perf:
image (8)


This PR was entirely written by codex. I've spent some time testing and reviewing the code. It looks sensible and works for me, however I don't claim to be familiar with some of these areas of CoreCLR.

@JulieLeeMSFT @dhartglassMSFT

@a74nh
Copy link
Copy Markdown
Contributor Author

a74nh commented Apr 13, 2026

Still need to add tests. But, taking this out of draft to get some comments to see if this is in the right direction

@a74nh a74nh marked this pull request as ready for review April 13, 2026 15:43
Copilot AI review requested due to automatic review settings April 13, 2026 15:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds managed symbol-reading support to enrich perfmap/jitdump output with source/line information by reading Portable PDB sequence points, and wires the runtime to use this new managed helper when emitting perfmap/jitdump data.

Changes:

  • Add PerfMapSymbolReader (System.Diagnostics.StackTrace) and a CoreLib StackTrace.GetInfoForMethod unmanaged callback to retrieve sequence points from associated/embedded Portable PDBs.
  • Extend perfmap/jitdump plumbing to optionally emit JIT_CODE_DEBUG_INFO records (gated by PerfMapEmitDebugInfo) and update PAL jitdump logging to accept a debug-info payload.
  • Retarget the runtime delegate creation from SOS to System.Private.CoreLib / System.Diagnostics.StackTrace for debug-info retrieval.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/PerfMapSymbolReader.cs New managed helper to read Portable PDB sequence points and fill a native buffer.
src/libraries/System.Diagnostics.StackTrace/src/System.Diagnostics.StackTrace.csproj Includes the new PerfMapSymbolReader.cs in the build.
src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLink.Descriptors.LibraryBuild.xml Preserves PerfMapSymbolReader for trimming scenarios.
src/coreclr/vm/perfmap.h Adds s_EmitDebugInfo configuration flag.
src/coreclr/vm/perfmap.cpp Emits optional jitdump debug-info payload and passes it to PAL jitdump logging.
src/coreclr/vm/gdbjithelpers.h Updates the managed debug-info delegate signature to use a pointer and int return.
src/coreclr/vm/gdbjit.cpp Adds re-entrancy guard and updates delegate invocation to new signature.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.cs Adds unmanaged callback (GetInfoForMethod) and UnsafeAccessor bridge into System.Diagnostics.StackTrace.
src/coreclr/pal/src/misc/perfjitdump.cpp Adds JIT_CODE_DEBUG_INFO record support and extends LogMethod parameters.
src/coreclr/pal/inc/pal.h Updates PAL_PerfJitDump_LogMethod signature to include debug/unwind buffers + sizes.
src/coreclr/inc/clrconfigvalues.h Adds PerfMapEmitDebugInfo config value.
src/coreclr/dlls/mscoree/exports.cpp Retargets delegate creation to System.Diagnostics.StackTrace.GetInfoForMethod and enables it for FEATURE_PERFMAP too.

@JulieLeeMSFT
Copy link
Copy Markdown
Member

@noahfalk, @brianrob, @steveisok, PTAL if it aligns with our diagnostics story. This change is related to the Performix tool supporting source annotation.

Copilot AI review requested due to automatic review settings April 14, 2026 10:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds source/line mapping support for perfmap/jitdump by introducing a managed symbol reader (Portable PDB / embedded PDB) and wiring it into CoreCLR’s perfmap and jitdump emission paths on Unix.

Changes:

  • Add PerfMapSymbolReader in System.Diagnostics.StackTrace and preserve it for trimming so CoreCLR can query Portable PDB sequence points.
  • Retarget the native debug-info delegate from SOS.NETCore to System.Private.CoreLib (System.Diagnostics.StackTrace.GetInfoForMethod) and adjust native delegate signatures/call sites.
  • Emit JIT_CODE_DEBUG_INFO records into jitdump (gated by the new PerfMapEmitDebugInfo config).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/PerfMapSymbolReader.cs New managed reader to extract sequence points from associated/embedded Portable PDBs.
src/libraries/System.Diagnostics.StackTrace/src/System.Diagnostics.StackTrace.csproj Includes the new PerfMapSymbolReader.cs in the build.
src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLink.Descriptors.LibraryBuild.xml Ensures PerfMapSymbolReader is preserved under trimming.
src/coreclr/vm/perfmap.h Adds s_EmitDebugInfo config flag.
src/coreclr/vm/perfmap.cpp Generates and logs serialized jitdump debug-info payloads when enabled.
src/coreclr/vm/gdbjithelpers.h Updates delegate signature to use pointer + int return.
src/coreclr/vm/gdbjit.cpp Adds reentrancy guard and updates delegate usage; attempts to handle missing locals.
src/coreclr/pal/src/misc/perfjitdump.cpp Extends jitdump logging to optionally write JIT_CODE_DEBUG_INFO records and adjusts API.
src/coreclr/pal/inc/pal.h Updates PAL_PerfJitDump_LogMethod signature to include buffer + size pairs.
src/coreclr/inc/clrconfigvalues.h Adds PerfMapEmitDebugInfo config value.
src/coreclr/dlls/mscoree/exports.cpp Retargets delegate creation to CoreLib StackTrace method and widens feature guard.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.cs Adds UnmanagedCallersOnly callback and UnsafeAccessor bridge to the library symbol reader.

@noahfalk
Copy link
Copy Markdown
Member

noahfalk commented Apr 14, 2026

I'm hesitant that trying to do this source resolution at runtime will necessarily give the overall scenario characteristics you are looking for but we should discuss.

  1. Do you expect all PDBs for relevant modules to be present on the disk when the application is running? Deploying PDBs is not typical for .NET apps and most other .NET profiling scenarios I can think of don't rely on this. You would need every developer who wants to use the profiler to do some manual deployment steps and/or create custom builds.
  2. How much interference from the profiling code are you willing to tolerate? Searching for PDBs, loading them into memory, and parsing them is going to execute down different non-trivial codepaths and allocate a bunch of memory.
  3. For self-contained apps I don't recall if anything is going to ensure the PDB parsing logic is always linked in. We'd need to investigate that or accept that on some apps it just wouldn't work.

The alternative that I've seen other profilers do is PDB/source resolution as a post-processing step after the trace is collected rather than at runtime. It requires at runtime capturing IP->IL offset mappings, then later using PDBs to convert IL -> source line info. Its probably a bit more complex to create initially but gives a higher fidelity result and supports acquiring symbols out-of-band such as from a symbol server. A couple possibilities on how that could be built:

  • if there is a way to smuggle IL information into a jitdump file it could be post-processed. I'm not aware that jitdump supports storing that kind of data so probably what this really means is creating a custom file format initially that turns into a valid jitdump after post-processing.
  • instead of trying to leverage the runtime's jitdump file writing capability we could use completely different mechanisms to write the initial file such as an EventPipe trace or a custom profiling plugin.

@a74nh
Copy link
Copy Markdown
Contributor Author

a74nh commented Apr 14, 2026

I'm hesitant that trying to do this source resolution at runtime will necessarily give the overall scenario characteristics you are looking for but we should discuss.

Thanks.

  1. Do you expect all PDBs for relevant modules to be present on the disk when the application is running? Deploying PDBs is not typical for .NET apps and most other .NET profiling scenarios I can think of don't rely on this. You would need every developer who wants to use the profiler to do some manual deployment steps and/or create custom builds.

Even if we did this as a post processing step instead (or in any other conceivable way) then the source code would need to be present at the point of processing.

This is all hidden behind an additional config variable (DOTNET_PerfMapEmitDebugInfo) which is off by default, so shouldn't cause any overhead for anyone who doesn't want this.

  1. How much interference from the profiling code are you willing to tolerate? Searching for PDBs, loading them into memory, and parsing them is going to execute down different non-trivial codepaths and allocate a bunch of memory.

Agreed, this would be a concern as we want this to effect performance as little as possible.

This will overhead will happen at jit compilation time, not execution time. So mostly startup would be effected.

Looking at my test app, running it under Linux perf, with DOTNET_PerfMapEnabled=2 it took ~5.3 seconds. With DOTNET_PerfMapEmitDebugInfo=1 enabled too, it jumps to ~5.8 seconds (almost 10%). Increasing the internal loops in the test app took it to 44.8 secs and 45.3 seconds (now 1%).

Do you have an suggestions as to other ways to measure the impact?

Or at what level you'd consider this to be too much?

  1. For self-contained apps I don't recall if anything is going to ensure the PDB parsing logic is always linked in. We'd need to investigate that or accept that on some apps it just wouldn't work.

Ok.

The alternative that I've seen other profilers do is PDB/source resolution as a post-processing step after the trace is collected rather than at runtime. It requires at runtime capturing IP->IL offset mappings, then later using PDBs to convert IL -> source line info. Its probably a bit more complex to create initially but gives a higher fidelity result and supports acquiring symbols out-of-band such as from a symbol server. A couple possibilities on how that could be built:

  • if there is a way to smuggle IL information into a jitdump file it could be post-processed. I'm not aware that jitdump supports storing that kind of data so probably what this really means is creating a custom file format initially that turns into a valid jitdump after post-processing.

Post processing would be a valid way of doing this

  • instead of trying to leverage the runtime's jitdump file writing capability we could use completely different mechanisms to write the initial file such as an EventPipe trace or a custom profiling plugin.

That would have the same issue that it'd be during runtime and so would effect performance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-VM-coreclr community-contribution Indicates that the PR has been added by a community member linkable-framework Issues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants