Skip to content

[cDAC] Handle GettingReJITParameters state - #135471

Merged
rcj1 merged 3 commits into
dotnet:mainfrom
pcshrosbree:cdac-rejit-getting-parameters-state
Oct 16, 2026
Merged

rcj1 merged 3 commits into
dotnet:mainfrom
pcshrosbree:cdac-rejit-getting-parameters-state

Conversation

@pcshrosbree

@pcshrosbree pcshrosbree commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

While ReJIT parameter configuration is in progress, an IL code version's state is RejitFlags::kStateGettingReJITParameters (1); one example is while the profiler's GetReJITParameters callback runs. The ReJIT contract knew only kStateRequested (0) and kStateActive (2), and threw InvalidOperationException for state 1. So every SOS or DBI query that reads the state of such a version failed when a dump or live attach caught a method mid-ReJIT.

The legacy DAC handles the state. Its two flag switches report kUnknown for any state other than Requested and Active; in a Debug DAC they _ASSERTE first. Its comparisons treat the state as "not Requested, not Active".

Changes:

  • Add RejitState.Unknown. The contract maps every state value it does not recognize to it, state 1 included, instead of throwing. New runtime states report Unknown until there is a reason to expose them.
  • The two SOS flag conversions report Unknown as kUnknown: GetReJITInformation, and CopyNativeCodeVersionToReJitData behind GetMethodDescData.
  • The other callers already compare against Requested or Active. For an Unknown version they now behave as the legacy DAC does, instead of failing:
    • GetPendingReJITID returns S_FALSE;
    • GetRejitIds skips the version;
    • GetProfilerModifiedILInformation and GetMethodsWithProfilerModifiedIL treat the version as ReJIT-modified;
    • GetActiveRejitILCodeVersionNode reports no active node.
  • The no-op Debug.Assert(true, ...) on the unhandled-state path of GetReJITInformation becomes Debug.Fail.
  • Update ReJIT.md, including the rule that unrecognized values are Unknown.

Tests:

  • Contract level: GetRejitState returns Unknown, without throwing, for state 1 and for the undefined value 3.
  • SOS level: GetReJITInformation succeeds and reports kUnknown for a state-1 version.
  • UnitTests 3300/3300 on linux-x64. With the Unknown arm of GetRejitState put back to the original throw, all 12 rows of these two tests fail and nothing else does. CdacUsageGraph docs --check and Usage.Tests pass. The first revision also ran on osx-arm64 and windows-x64.

Noticed, not changed here: on S_FALSE the legacy GetPendingReJITID sets *pRejitId to -1, and the cDAC leaves it untouched.

Note

This PR was prepared with AI assistance (Anthropic Claude, reviewed with OpenAI Codex). The tests and validation runs were executed locally.

While ReJIT parameter configuration is in progress, an IL code
version's state is RejitFlags::kStateGettingReJITParameters (1). The
ReJIT contract knew only kStateRequested (0) and kStateActive (2) and
threw InvalidOperationException for it, so every SOS and DBI query
that read the state of such a version failed. The legacy DAC handles
it: GetReJITInformation reports DacpReJitData2::kUnknown and
GetPendingReJITID returns S_FALSE.

- Add RejitState.GettingReJITParameters and map the native value.
- The two SOS flag conversions report it as kUnknown, as the legacy
  DAC does. The other callers compare against Requested or Active and
  now behave as the legacy DAC does for this state instead of failing.
- Replace the no-op Debug.Assert(true, ...) on the unknown-state path
  with Debug.Fail.
- Mark the value in codeversion.h as one the contract depends on.
- Update ReJIT.md.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@rcj1

rcj1 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

I'd suggest returning Unknown instead of throwing and leave it at that, as I don't believe this intermediate state that should only occur in a narrow window here

else if (fWaitForParameters)
needs to be exposed or elaborated on. @max-charlamb thoughts?

@pcshrosbree

Copy link
Copy Markdown
Contributor Author

Thanks, Unknown works for me. With it, the SOS callers report kUnknown and the checks against Requested/Active behave as they do in this PR, so what SOS and DBI report is unchanged.

One question on scope before I change it: should only state 1 map to Unknown, or every value the contract does not recognise? I'd lean towards mapping only 1 and keeping the throw for 3 to 15, so that a new runtime state still fails loudly instead of quietly reading as Unknown.

A small note on the window: the state is set at L971 on every ReJIT, not only when threads race. It holds for the whole GetReJITParameters callback, until L1033 or L1060, and L1064 is the second thread waiting on it. So it is short when the profiler's callback is short, but a hang dump taken while a callback is stuck will show it. That argues for not throwing, which your suggestion already covers, rather than for naming the state.

@rcj1

rcj1 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Thanks, Unknown works for me. With it, the SOS callers report kUnknown and the checks against Requested/Active behave as they do in this PR, so what SOS and DBI report is unchanged.

One question on scope before I change it: should only state 1 map to Unknown, or every value the contract does not recognise? I'd lean towards mapping only 1 and keeping the throw for 3 to 15, so that a new runtime state still fails loudly instead of quietly reading as Unknown.

A small note on the window: the state is set at L971 on every ReJIT, not only when threads race. It holds for the whole GetReJITParameters callback, until L1033 or L1060, and L1064 is the second thread waiting on it. So it is short when the profiler's callback is short, but a hang dump taken while a callback is stuck will show it. That argues for not throwing, which your suggestion already covers, rather than for naming the state.

I would suggest mapping any unknown state to Unknown. New runtime states can report Unknown until it is valuable to expose them.

Per review, replace RejitState.GettingReJITParameters with
RejitState.Unknown, and map every state value the contract does not
recognize to it instead of throwing. State 1
(kStateGettingReJITParameters) is one such value. New runtime states
report Unknown until there is a reason to expose them.

- The SOS flag conversions report Unknown as kUnknown, as the legacy
  DAC does for any state other than Requested and Active.
- Drop kStateGettingReJITParameters from the contract's RejitFlags and
  its contract-dependency comment from codeversion.h.
- ReJIT.md states the rule.
- Tests: state-nibble values other than Requested and Active, with and
  without flag bits, now expect Unknown. The SOS theories add the
  unrecognized values 3 and 15.
@pcshrosbree

Copy link
Copy Markdown
Contributor Author

Thanks, done in f66c547. RejitState.GettingReJITParameters is gone, and GetRejitState now returns RejitState.Unknown for every state value the contract does not recognise, state 1 included, instead of throwing. The two SOS flag conversions report Unknown as kUnknown, which is what the legacy DAC reports for any state other than Requested and Active. ReJIT.md states the rule, and the contract-dependency comment on kStateGettingReJITParameters in codeversion.h is reverted, since the contract no longer depends on that value. The tests now expect Unknown for every value of the state nibble other than Requested and Active, and the SOS tests add the values 3 and 15.

Comment thread src/native/managed/cdac/tests/UnitTests/ReJITTests.cs Outdated
Remove the comments on RejitState.Unknown and on its cases in
SOSDacImpl. Reduce the new tests to one contract theory and one SOS
check that an unrecognized state reports Unknown without throwing.
CopyNativeCodeVersionToReJitData is private again, since no test calls
it directly.
@rcj1

rcj1 commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

/ba-g unrelated stress fails

@rcj1
rcj1 merged commit 2ec49fe into dotnet:main Oct 16, 2026
84 of 86 checks passed
@pcshrosbree

Copy link
Copy Markdown
Contributor Author

@rcj1: thanks for the review and for merging it.

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

Labels

area-Diagnostics-cdac community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants