-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[cdac] add v2 ExecutionManager contract for NibbleMap change #109654
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
7025149
add ExecutionManager contract version
adfbcb3
add docs
d6d0d71
improve tests
3994c77
_version -> Version
f3b393d
comments
8cd0fcf
comments
a471477
comments
9a90bd6
fix typos
8218390
rename
e358fe7
comments
1841405
doc update
0f01f80
typo
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; | ||
|
||
namespace Microsoft.Diagnostics.DataContractReader.Contracts; | ||
|
||
internal sealed class ExecutionManager_1 : ExecutionManagerBase<NibbleMapLinearLookup> | ||
{ | ||
public ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionMap) : base(target, topRangeSectionMap) | ||
{ | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; | ||
|
||
namespace Microsoft.Diagnostics.DataContractReader.Contracts; | ||
|
||
internal sealed class ExecutionManager_2 : ExecutionManagerBase<NibbleMapConstantLookup> | ||
{ | ||
public ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionMap) : base(target, topRangeSectionMap) | ||
{ | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/INibbleMap.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Numerics; | ||
using System.Diagnostics; | ||
using System; | ||
|
||
namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; | ||
|
||
internal interface INibbleMap | ||
{ | ||
public static abstract INibbleMap Create(Target target); | ||
|
||
public TargetPointer FindMethodCode(Data.CodeHeapListNode heapListNode, TargetCodePointer jittedCodeAddress); | ||
} |
149 changes: 149 additions & 0 deletions
149
...ataContractReader.Contracts/Contracts/ExecutionManager/Helpers/NibbleMapConstantLookup.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Numerics; | ||
using System.Diagnostics; | ||
using System; | ||
|
||
using static Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers.NibbleMapHelpers; | ||
|
||
namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; | ||
|
||
// CoreCLR nibblemap with O(1) lookup time. | ||
// | ||
// Implementation very similar to NibbleMapLinearLookup, but with the addition of writing relative pointers | ||
// into the nibblemap whenever a code block completely covers a DWORD. This allows for O(1) lookup | ||
// with the cost of O(n) write time. | ||
// | ||
// Pointers are encoded using the top 28 bits of the DWORD normally, the bottom 4 bits of the pointer | ||
// are reduced to 2 bits due to 4 byte code offset and encoded in bits 28 .. 31 of the DWORD with values | ||
// 9-12. This is used to differentiate nibble values and pointer DWORDs. | ||
// | ||
// To read the nibblemap, we check if the DWORD is a pointer. If so, then we know the value currentPC is | ||
// part of a managed code block beginning at the mapBase + decoded pointer. If the DWORD is empty | ||
// (no pointer or previous nibbles), then we only need to read the previous DWORD. If that DWORD is empty, | ||
// then we must not be in a managed function. Otherwise the write algorithm would have written a relative | ||
// pointer in the DWORD. | ||
// | ||
// Note, a currentPC pointing to bytes outside a function have undefined lookup behavior. | ||
// In this implementation we may "extend" the lookup period of a function several hundred bytes | ||
// if there is not another function following it. | ||
|
||
internal class NibbleMapConstantLookup : INibbleMap | ||
{ | ||
private readonly Target _target; | ||
|
||
private NibbleMapConstantLookup(Target target) | ||
{ | ||
_target = target; | ||
} | ||
|
||
internal static bool IsPointer(MapUnit mapUnit) | ||
{ | ||
return (mapUnit.Value & MapUnit.NibbleMask) > 8; | ||
} | ||
|
||
internal static TargetPointer DecodePointer(TargetPointer baseAddress, MapUnit mapUnit) | ||
{ | ||
uint nibble = mapUnit.Value & MapUnit.NibbleMask; | ||
uint relativePointer = (mapUnit.Value & ~MapUnit.NibbleMask) + ((nibble - 9) << 2); | ||
return baseAddress + relativePointer; | ||
} | ||
|
||
internal static uint EncodePointer(uint relativeAddress) | ||
{ | ||
uint nibble = ((relativeAddress & MapUnit.NibbleMask) >>> 2) + 9; | ||
return (relativeAddress & ~MapUnit.NibbleMask) + nibble; | ||
} | ||
|
||
internal TargetPointer FindMethodCode(TargetPointer mapBase, TargetPointer mapStart, TargetCodePointer currentPC) | ||
{ | ||
TargetNUInt relativeAddress = new TargetNUInt(currentPC.Value - mapBase.Value); | ||
DecomposeAddress(relativeAddress, out MapKey mapIdx, out Nibble bucketByteIndex); | ||
|
||
MapUnit t = mapIdx.ReadMapUnit(_target, mapStart); | ||
|
||
// if pointer, return value | ||
if (IsPointer(t)) | ||
{ | ||
return DecodePointer(mapBase, t); | ||
} | ||
|
||
// shift the nibble we want to the least significant position | ||
t = t.FocusOnIndexedNibble(mapIdx); | ||
|
||
// if the nibble is non-zero, we have found the start of a method, | ||
// but we need to check that the start is before the current address, not after | ||
if (!t.Nibble.IsEmpty && t.Nibble.Value <= bucketByteIndex.Value) | ||
{ | ||
return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); | ||
} | ||
|
||
// search backwards through the current map unit | ||
// we processed the lsb nibble, move to the next one | ||
t = t.ShiftNextNibble; | ||
|
||
// if there's any nibble set in the current unit, find it | ||
if (!t.IsEmpty) | ||
{ | ||
mapIdx = mapIdx.Prev; | ||
while (t.Nibble.IsEmpty) | ||
{ | ||
t = t.ShiftNextNibble; | ||
mapIdx = mapIdx.Prev; | ||
} | ||
return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); | ||
} | ||
|
||
// We finished the current map unit, we want to move to the previous one. | ||
// But if we were in the first map unit, we can stop | ||
if (mapIdx.InFirstMapUnit) | ||
{ | ||
return TargetPointer.Null; | ||
} | ||
|
||
// We're now done with the current map unit. | ||
// Align the map index to the current map unit, then move back one nibble into the previous map unit | ||
mapIdx = mapIdx.AlignDownToMapUnit(); | ||
mapIdx = mapIdx.Prev; | ||
|
||
// read the map unit containing mapIdx and skip over it if it is all zeros | ||
t = mapIdx.ReadMapUnit(_target, mapStart); | ||
|
||
// if t is empty, then currentPC can not be in a function | ||
if (t.IsEmpty) | ||
{ | ||
return TargetPointer.Null; | ||
} | ||
|
||
// if t is not empty, it must contain a pointer or a nibble | ||
if (IsPointer(t)) | ||
{ | ||
return DecodePointer(mapBase, t); | ||
} | ||
|
||
// move to the correct nibble in the map unit | ||
while (!mapIdx.IsZero && t.Nibble.IsEmpty) | ||
{ | ||
t = t.ShiftNextNibble; | ||
mapIdx = mapIdx.Prev; | ||
} | ||
|
||
return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); | ||
} | ||
|
||
public static INibbleMap Create(Target target) | ||
{ | ||
return new NibbleMapConstantLookup(target); | ||
} | ||
|
||
public TargetPointer FindMethodCode(Data.CodeHeapListNode heapListNode, TargetCodePointer jittedCodeAddress) | ||
{ | ||
if (jittedCodeAddress < heapListNode.StartAddress || jittedCodeAddress > heapListNode.EndAddress) | ||
{ | ||
return TargetPointer.Null; | ||
} | ||
|
||
return FindMethodCode(heapListNode.MapBase, heapListNode.HeaderMap, jittedCodeAddress); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the move to composability here. I think it is also worth considering (perhaps as a separate change) how this could help with out unit testing. Are we now at a place where we can have a mock nibble map for testing the execution manager? For example, we could have the nibble map tests have the extensive coverage of the nibble map itself and then the execution manager tests could expect the nibble map to be correct based on that coverage and mock out
INibbleMap.FindMethodCode
to return what is needed instead of having to build out the memory.