Skip to content

Commit d565cc0

Browse files
noahfalkCopilot
andcommitted
Add RecommendedReaderVersion advisory mechanism to cDAC RuntimeInfo contract
Adds a new CDAC_RECOMMENDED_READER_VERSION constant and RecommendedReaderVersion global to datadescriptor.inc. The runtime team can increment this value when making changes where updating diagnostic tools is recommended. This is purely advisory and does not necessarily mean a breaking change occurred. The RuntimeInfo contract exposes two new properties: - CurrentReaderVersion: a compile-time constant representing the reader version this contract was authored against (currently 1) - RecommendedReaderVersion: reads the runtime global of the same name Diagnostic tools can compare these to determine whether to recommend an update to the user. When the runtime ships a higher RecommendedReaderVersion than a tool's CurrentReaderVersion, the tool knows newer contracts are available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f6777ec commit d565cc0

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed

docs/design/datacontracts/RuntimeInfo.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ RuntimeInfoArchitecture GetTargetArchitecture();
3030

3131
// Gets the targets operating system. If this information is not available returns Unknown.
3232
RuntimeInfoOperatingSystem GetTargetOperatingSystem();
33+
34+
// Returns the runtime's RecommendedReaderVersion global. Returns 0 if the global is absent.
35+
uint RecommendedReaderVersion { get; }
36+
37+
// An embedded version constant indicating how much runtime functionality this reader knows how to parse.
38+
uint CurrentReaderVersion { get; }
3339
```
3440

3541
## Version 1
@@ -39,5 +45,15 @@ Global variables used:
3945
| --- | --- | --- |
4046
| Architecture | string | Target architecture |
4147
| OperatingSystem | string | Target operating system |
48+
| RecommendedReaderVersion | uint32 | Incremented when an update to the latest contracts is recommended |
49+
50+
The contract implementation returns the architecture and operating system global values parsed as the
51+
respective enum case-insensitively. If these globals are not available, the contract returns Unknown.
52+
53+
### Reader versioning scheme
4254

43-
The contract implementation simply returns the contract descriptor global values parsed as the respective enum case-insensitively. If these globals are not available, the contract returns Unknown.
55+
When the .NET runtime team wants to signal that an update is recommended we update both the
56+
`CurrentReaderVersion` constant in the cDAC implementation and the `RecommendedReaderVersion`
57+
global value in the runtime. This causes older tools on older cDAC versions to observe
58+
RecommendedReaderVersion > CurrentReaderVersion. The tool can notify the user that an update
59+
is recommended.

src/coreclr/vm/datadescriptor/datadescriptor.inc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@
66
// This file is compiled using the target architecture. Preprocessor defines for the target
77
// platform will be available. It is ok to use `#ifdef`.
88

9+
// Increment this when making a change where we'd like to recommend users update their diagnostic tools.
10+
// Tools can use this to display an advisory notice to users encouraging them to update. Changing
11+
// this value on its own is not considered a breaking change. See the RuntimeInfo.md data contract for details.
12+
// When incrementing this value, also implement the new functionality in the cDAC reader and update the
13+
// CurrentReaderVersion property on the IRuntimeInfo contract in
14+
// src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs
15+
// to match.
16+
#ifndef CDAC_RECOMMENDED_READER_VERSION
17+
#define CDAC_RECOMMENDED_READER_VERSION 1
18+
#endif
19+
20+
// When changes will break existing diagnostic tools (changes that are incompatible with the existing data contracts)
21+
// you should also update the contract versions at the bottom of this file for any contract that is being broken.
22+
923

1024
CDAC_BASELINE("empty")
1125
CDAC_TYPES_BEGIN()
@@ -1082,6 +1096,7 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2)
10821096
CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1)
10831097
#endif //TARGET_64BIT
10841098
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
1099+
CDAC_GLOBAL(RecommendedReaderVersion, uint32, CDAC_RECOMMENDED_READER_VERSION)
10851100
CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A)
10861101
CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET)
10871102
CDAC_GLOBAL(HashMapValueMask, uint64, VALUE_MASK)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public interface IRuntimeInfo : IContract
3434
static string IContract.Name { get; } = nameof(RuntimeInfo);
3535
RuntimeInfoArchitecture GetTargetArchitecture() => throw new NotImplementedException();
3636
RuntimeInfoOperatingSystem GetTargetOperatingSystem() => throw new NotImplementedException();
37+
uint CurrentReaderVersion { get => throw new NotImplementedException(); }
38+
uint RecommendedReaderVersion { get => throw new NotImplementedException(); }
3739
}
3840

3941
public readonly struct RuntimeInfo : IRuntimeInfo

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static class Globals
1919

2020
public const string ObjectToMethodTableUnmask = nameof(ObjectToMethodTableUnmask);
2121
public const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
22+
public const string RecommendedReaderVersion = nameof(RecommendedReaderVersion);
2223

2324
public const string ExceptionMethodTable = nameof(ExceptionMethodTable);
2425
public const string FreeObjectMethodTable = nameof(FreeObjectMethodTable);

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,16 @@ readonly RuntimeInfoOperatingSystem IRuntimeInfo.GetTargetOperatingSystem()
3939

4040
return RuntimeInfoOperatingSystem.Unknown;
4141
}
42+
43+
readonly uint IRuntimeInfo.CurrentReaderVersion => 1;
44+
45+
readonly uint IRuntimeInfo.RecommendedReaderVersion
46+
{
47+
get
48+
{
49+
if (_target.TryReadGlobal(Constants.Globals.RecommendedReaderVersion, out uint runtimeVersion))
50+
return runtimeVersion;
51+
return 0;
52+
}
53+
}
4254
}

src/native/managed/cdac/tests/RuntimeInfoTests.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ public class RuntimeInfoTests
1313
{
1414
internal static Target CreateTarget(
1515
MockTarget.Architecture arch,
16-
(string Name, string Value)[] globalStrings)
16+
(string Name, string Value)[] globalStrings,
17+
(string Name, ulong Value)[] globals = null)
1718
{
1819
MockMemorySpace.Builder builder = new MockMemorySpace.Builder(new TargetTestHelpers(arch));
19-
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, [], [], globalStrings);
20+
TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, [], globals, globalStrings);
2021

2122
IContractFactory<IRuntimeInfo> runtimeInfoFactory = new RuntimeInfoFactory();
2223

@@ -99,4 +100,21 @@ public void GetTargetOperatingSystemTest(
99100
var actualArchitecture = runtimeInfo.GetTargetOperatingSystem();
100101
Assert.Equal(expectedOS, actualArchitecture);
101102
}
103+
104+
private static readonly MockTarget.Architecture DefaultArch = new MockTarget.Architecture { IsLittleEndian = true, Is64Bit = true };
105+
106+
[Fact]
107+
public void RecommendedReaderVersion_GlobalPresent_ReturnsValue()
108+
{
109+
var target = CreateTarget(DefaultArch, [],
110+
[(Constants.Globals.RecommendedReaderVersion, (ulong)2)]);
111+
Assert.Equal((uint)2, target.Contracts.RuntimeInfo.RecommendedReaderVersion);
112+
}
113+
114+
[Fact]
115+
public void RecommendedReaderVersion_GlobalAbsent_ReturnsZero()
116+
{
117+
var target = CreateTarget(DefaultArch, []);
118+
Assert.Equal((uint)0, target.Contracts.RuntimeInfo.RecommendedReaderVersion);
119+
}
102120
}

0 commit comments

Comments
 (0)