Fix universal symbol conversion for overlapping mappings#2252
Conversation
Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
| } | ||
|
|
||
| // Get or create the loaded module. | ||
| TraceLoadedModule loadedModule = FindModuleAndIndexContainingAddress(data.StartAddress, data.TimeStampQPC, out index); |
There was a problem hiding this comment.
GetOrCreateModule will only return a TraceModuleFile whose ImageBase matches the incoming mapping, so if the mapping happens to have the same name, as does the mappings for jitted code on Linux, then GetOrCreateModule will just create a new module.
FindModuleAndIndexContainingAddress will return a TraceLoadedModule that contains data.StartAddress, but doesn't necessarily start with it. If it doesn't start with it, then we need to create a new TraceLooadedModule and insert it - this is needed to handle the overlapping mapping case.
There was a problem hiding this comment.
Fixed in 6caab6b. Now checking if loadedModule.ImageBase != data.StartAddress and creating a new TraceLoadedModule when they don't match, ensuring proper handling of overlapping mappings where the module containing the address doesn't start at the mapping's start address.
src/TraceEvent/TraceLog.cs
Outdated
|
|
||
| InsertAndSetOverlap(index + 1, loadedModule); | ||
| } | ||
| else |
There was a problem hiding this comment.
This else clause is valid if the ImageBase of loadedModule matches the StartAddress of the new mapping. But, in this case, we still should update loadTimeQPC to match the new mapping timestamp. We also need to determine if overlaps needs to be set because we're not re-inserting it. If we can't do this here, we will need to just create a new TraceLoadedModule and insert it so that the underlying code can properly set overlaps if necessary.
There was a problem hiding this comment.
Fixed in 6caab6b. The else clause now properly updates loadTimeQPC to match the new mapping timestamp with loadedModule.loadTimeQPC = data.TimeStampQPC. The InsertAndSetOverlap call in the creation path will handle the overlaps flag correctly.
src/TraceEvent/TraceLog.cs
Outdated
| @@ -7125,6 +7131,14 @@ internal TraceModuleFile UniversalMapping(ProcessMappingTraceData data, ProcessM | |||
|
|
|||
There was a problem hiding this comment.
On line 7130, we set the timestamp to the start of the trace because mappings are enumerated at the start of the trace. This doesn't always happen - we should set the timestamp properly.
There was a problem hiding this comment.
Fixed in 6caab6b. Changed from process.Log.sessionStartTimeQPC to data.TimeStampQPC to use the proper timestamp from the mapping data.
Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
|
Copilot Post-Mortem:
|
|
@noahfalk, this change is a bit more involved than just the repro case that you hit, but FWIW, I did confirm that your trace works, and that there are no symbols that don't have a corresponding loaded module. |
|
Is there anything blocking this PR from getting merged? |
|
This one just needs a review from someone in the CODEOWNERS file. I have a request out on that, and I will see if I can get traction on it tomorrow. |
src/TraceEvent/TraceLog.cs
Outdated
| // We should always get a loadedModule here because if we have a symbol, then we should have a module that contains it. | ||
| // Assert so that we can detect bugs here during development. | ||
| Debug.Assert(false, "loadedModule is missing for symbol"); | ||
| } |
There was a problem hiding this comment.
Why not Debug.Assert(loadedModule != null, "loadedModule is missing for symbol") before the if statement?
There was a problem hiding this comment.
You're right. I can't remember why I went with this, but I've updated to use your suggestion.
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
This PR fixes a critical issue where overlapping memory mappings in universal traces (nettrace files) cause a NullReferenceException during symbol processing.
Problem
When loading nettrace files with overlapping mappings, PerfView would crash with a NullReferenceException. The issue occurred when:
TraceModuleFilewith a smaller image sizeTraceModuleFilewithout updating its sizeExample scenario from the issue:
Solution
1. Enhanced
UniversalMappingmethod:ModuleFile.imageSizewhen new mappings have larger ranges2. Added defensive null checks in
AddUniversalDynamicSymbol:loadedModuleis null before using itChanges
TraceLoadedModules.UniversalMapping(): Now properly expands ModuleFile size for overlapping mappingsTraceLoadedModules.UniversalMapping(): Adds a new LoadedModuleFile when the necessaryTraceCodeAddresses.AddUniversalDynamicSymbol(): Added null checks to handle missing modules gracefullyTraceCodeAddresses.AddUniversalDynamicSymbol(): Adds an assert to ensure that all dynamic symbols have a corresponding LoadedModule.Coverage
This fix handles all overlapping mapping scenarios:
✅ Complete replacement of existing mapping
✅ Mapping that overlaps and extends beyond existing mapping
✅ Mapping contained within existing mapping
✅ Partial overlaps with shared start addresses
The changes are minimal and surgical, maintaining backward compatibility while resolving the crash in user_events scenarios.
Fixes #2250.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.