Skip to content

Conversation

@MichalStrehovsky
Copy link
Member

Fixes #121093 (because I needed more precise tracking of what gets stack trace data)
Fixes #68714

Submitting as draft for now:

  • Doesn't implement reading this data at runtime yet
  • This should be opt-in, not on by default if we have stack traces (we've had an issue tracking this for a long time and there's some but not huge demand)
  • Embeds line number data into executable (avoids having to carry PDB/DWO/DSYM, and writing parsers for those)
  • The data is emitted into a separate hashtable that hashes two different things based on the source of the stack trace data (we carry method stack trace data in reflection metadata and in dedicated stack trace data; for methods that are not targets of reflection). This requires using two mechanisms to do lookups. As to why we use these mechanisms for lookups instead of code addresses: we cannot do hashtables based on code addresses, this would require complex object writer coordination.

Cc @dotnet/ilc-contrib

@dotnet-policy-service
Copy link
Contributor

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

Copilot finished reviewing on behalf of MichalStrehovsky December 5, 2025 14:44
Copy link
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 support for embedding line number information into native AOT applications, allowing stack traces to include file names and line numbers without requiring separate debug symbol files (PDB/DWO/DSYM). The implementation uses hashtables to map methods to their line number data, with two different lookup mechanisms: one for methods with dedicated stack trace metadata and another for methods with reflection metadata.

Key Changes

  • Introduces StackTraceLineNumbersNode and StackTraceDocumentsNode to embed line number and document information into the executable
  • Adds HasLineNumbers flag to MethodStackTraceVisibilityFlags and corresponding StackTraceRecordFlags to control line number emission
  • Extends VersionResilientHashCode with CombineThreeValuesIntoHash method for hashing three values

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs Adds regression test for issue #121093 validating stack trace contains method names for both reflected and non-reflected generic methods
src/libraries/Common/src/Internal/VersionResilientHashCode.cs Adds CombineThreeValuesIntoHash method for creating stable hash codes from three values
src/coreclr/tools/aot/ILCompiler/Program.cs Updates stack trace emission policy to enable line numbers when stack trace data is emitted
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj Adds new node files to project
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs Updates ComputeMetadata signature to include reflection-based stack trace mappings
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs Adds HasLineNumbers flag support and constructor parameter to control line number inclusion
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs Refactors stack trace mapping to support both regular and reflection-based line number generation, adds flag-based filtering
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs Updates to use flag enum instead of boolean
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceLineNumbersNode.cs New node that generates hashtable mapping methods to compressed line number data
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceDocumentsNode.cs New node that stores document paths with offset-based indexing
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs Updates ComputeMetadata signature to include reflection-based stack trace mappings
src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs Adds blob IDs for line numbers and documents
src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs Adds ordering entries for new node types

// is a document number. Otherwise we use NativeOffsetDelta == 0 as a marker that the next
// byte is a document number and not a native offset delta.
if (currentDocument != null)
bw.Write7BitEncodedInt((byte)0);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The cast to (byte)0 is incorrect. Write7BitEncodedInt expects an int parameter, not a byte. This should be bw.Write7BitEncodedInt(0); instead.

Suggested change
bw.Write7BitEncodedInt((byte)0);
bw.Write7BitEncodedInt(0);

Copilot uses AI. Check for mistakes.
Comment on lines +1036 to +1038
public IEnumerable<ReflectionStackTraceMapping> GetReflectionStackTraceMappings(NodeFactory factor)
{
EnsureMetadataGenerated(factor);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Parameter name inconsistency: the parameter is named factor but should be factory to match the pattern used in other similar methods (e.g., GetStackTraceMapping on line 1030).

Suggested change
public IEnumerable<ReflectionStackTraceMapping> GetReflectionStackTraceMappings(NodeFactory factor)
{
EnsureMetadataGenerated(factor);
public IEnumerable<ReflectionStackTraceMapping> GetReflectionStackTraceMappings(NodeFactory factory)
{
EnsureMetadataGenerated(factory);

Copilot uses AI. Check for mistakes.

public static uint CombineThreeValuesIntoHash(uint value1, uint value2, uint value3)
{
// This matches the behavior of System.HashCode.Combine(value1, value2) as of the time of authoring
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The comment on line 209 is incorrect and appears to be copied from CombineTwoValuesIntoHash. It should say "This matches the behavior of System.HashCode.Combine(value1, value2, value3)" to accurately reflect that it combines three values, not two.

Suggested change
// This matches the behavior of System.HashCode.Combine(value1, value2) as of the time of authoring
// This matches the behavior of System.HashCode.Combine(value1, value2, value3) as of the time of authoring

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +69
foreach (StackTraceMapping mapping in factory.MetadataManager.GetStackTraceMapping(factory))
{
var entrypointSymbol = factory.MethodEntrypoint(mapping.Method);
if (entrypointSymbol is not INodeWithDebugInfo debugInfo)
continue;

BlobVertex blob = CreateLineNumbersBlob(_documents, debugInfo);
if (blob == null)
continue;

Vertex methodPointer = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(entrypointSymbol));
var hashtableEntry = nativeWriter.GetTuple(methodPointer, blob);

uint hashcode = VersionResilientHashCode.CombineThreeValuesIntoHash((uint)mapping.OwningTypeHandle, (uint)mapping.MethodNameHandle, (uint)mapping.MethodSignatureHandle);
hashtable.Append(hashcode, nativeSection.Place(hashtableEntry));
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Missing flag check: The loop should verify that (mapping.Flags & StackTraceRecordFlags.HasLineNumbers) != 0 before attempting to generate line numbers. Currently, it will attempt to generate line numbers for all methods in the stack trace mapping, regardless of whether the HasLineNumbers flag is set. This could result in generating line numbers for methods where line number emission was not intended according to the policy.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant