-
Notifications
You must be signed in to change notification settings - Fork 5.5k
[NativeAOT] Print OOM message before Abort() on Linux #125311
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
Open
Copilot
wants to merge
27
commits into
main
Choose a base branch
from
copilot/fix-out-of-memory-reporting
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+172
−11
Open
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
faf77aa
Initial plan
Copilot bc41dce
Fix NativeAOT OOM message not printed on Linux before Abort()
Copilot 3ef2a6d
Add OomHandling smoke test for NativeAOT OOM message reporting
Copilot ac7e07e
Merge branch 'main' into copilot/fix-out-of-memory-reporting
eduardo-vp 99a6529
Update minimalFailFast condition and test
57092c7
Add test timeout
cf2ca95
Revert changes to minimalFailFast
2eafa92
Fix test
cc43588
Show consistent error messages
15e81d4
Apply suggestion from @jkotas
jkotas f373a39
Code review feedback
aa3ce5e
Move test to src/tests/baseservices/exceptions
821b61d
Move test to src/tests/baseservices/exceptions, make it work correctl…
fb34a0d
Nit
c12f292
Adjust timeout and disable on Mono
88e7eae
Nit
5984be7
Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtim…
eduardo-vp 027d91f
Make OOM minimal message consistent with CoreCLR
a2b7dec
Update comment
643046c
Merge branch 'main' into copilot/fix-out-of-memory-reporting
eduardo-vp 1e2888e
Use Process.RunAndCaptureText
e3b0bd4
Potential fix for pull request finding
eduardo-vp ae0981e
Potential fix for pull request finding
eduardo-vp 9559af2
Accept both minimal and standard OOM message
258f4ed
Update test
d370881
Potential fix for pull request finding
eduardo-vp a4111bb
Stop using lists and chain of objects
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 hidden or 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
113 changes: 113 additions & 0 deletions
113
src/tests/baseservices/exceptions/OutOfMemoryException/OutOfMemoryException.cs
This file contains hidden or 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,113 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
|
eduardo-vp marked this conversation as resolved.
|
||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| // This test verifies that an out-of-memory condition produces a diagnostic | ||
| // message on stderr before the process terminates. | ||
| // | ||
| // The test spawns itself as a subprocess with a small GC heap limit set via | ||
| // DOTNET_GCHeapHardLimit so that the subprocess reliably runs out of memory. | ||
| // The outer process then validates that the subprocess wrote the expected | ||
| // OOM message to its standard error stream. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
|
|
||
| class OutOfMemoryExceptionTest | ||
| { | ||
| const int Pass = 100; | ||
| const int Fail = -1; | ||
| const int TimeoutMilliseconds = 60 * 1000; | ||
|
|
||
| const string AllocateSmallArg = "--allocate-small"; | ||
| const string AllocateLargeArg = "--allocate-large"; | ||
| // The standard unhandled-exception path ("Unhandled exception. System.OutOfMemoryException...") | ||
| // contains this token. The minimal OOM fail-fast path may only print a short "Out of memory." message. | ||
| // The test validates that some OOM diagnostic is printed rather than just "Aborted" with no context. | ||
| const string ExpectedOomToken = "OutOfMemoryException"; | ||
| const string ExpectedMinimalOomToken = "Out of memory."; | ||
|
|
||
| static int Main(string[] args) | ||
| { | ||
| if (args.Length > 0 && args[0] == AllocateSmallArg) | ||
| { | ||
| // Pre-allocate a flat array for storage. | ||
| object[] storage = new object[8192]; | ||
| int idx = 0; | ||
| // We expect ~2048 iterations in the first loop and ~64 iterations in the second. | ||
| try { while (idx < storage.Length) storage[idx++] = new byte[16 * 1024]; } catch (OutOfMemoryException) { } | ||
| try { while (idx < storage.Length) storage[idx++] = new byte[256]; } catch (OutOfMemoryException) { } | ||
| // < 280 bytes free. | ||
| // Use the smallest possible allocation to exhaust the last scraps. | ||
| while (idx < storage.Length) storage[idx++] = new object(); | ||
| } | ||
|
|
||
| if (args.Length > 0 && args[0] == AllocateLargeArg) | ||
| { | ||
| // Subprocess mode: allocate 128 KB chunks until OOM is triggered. | ||
| // This leaves some free memory when OOM fires, exercising the code | ||
| // path where GetRuntimeException may allocate a new OutOfMemoryException. | ||
| var list = new List<byte[]>(); | ||
| while (true) list.Add(new byte[128 * 1024]); | ||
| } | ||
|
|
||
| // Controller mode: launch subprocesses with a GC heap limit and verify their output. | ||
| int result = RunSubprocess(AllocateSmallArg, "small allocations"); | ||
| if (result != Pass) | ||
| return result; | ||
|
|
||
| return RunSubprocess(AllocateLargeArg, "large allocations"); | ||
| } | ||
|
|
||
| static int RunSubprocess(string allocateArg, string description) | ||
| { | ||
| Console.WriteLine($"Testing OOM with {description}..."); | ||
|
|
||
| string fileName = Environment.ProcessPath; | ||
|
eduardo-vp marked this conversation as resolved.
eduardo-vp marked this conversation as resolved.
|
||
| string[] arguments = TestLibrary.Utilities.IsNativeAot | ||
| ? [allocateArg] | ||
| : [typeof(OutOfMemoryExceptionTest).Assembly.Location, allocateArg]; | ||
|
|
||
| var psi = new ProcessStartInfo(fileName, arguments) | ||
| { | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| }; | ||
| // 32 MB GC heap limit (0x2000000): small enough to exhaust quickly but large enough for startup. | ||
| psi.Environment["DOTNET_GCHeapHardLimit"] = "0x2000000"; | ||
| psi.Environment["DOTNET_DbgEnableMiniDump"] = "0"; | ||
|
|
||
| ProcessTextOutput output; | ||
| try | ||
| { | ||
| output = Process.RunAndCaptureText(psi, TimeSpan.FromMilliseconds(TimeoutMilliseconds)); | ||
| } | ||
| catch (TimeoutException) | ||
| { | ||
| Console.WriteLine($"Subprocess timed out after {TimeoutMilliseconds / 1000} seconds."); | ||
| return Fail; | ||
| } | ||
|
|
||
| Console.WriteLine($"Subprocess exit code: {output.ExitStatus.ExitCode}"); | ||
| Console.WriteLine($"Subprocess stderr: {output.StandardError}"); | ||
|
|
||
| if (output.ExitStatus.ExitCode == 0 || output.ExitStatus.ExitCode == Pass) | ||
| { | ||
| Console.WriteLine("Expected a non-success exit code from the OOM subprocess."); | ||
| return Fail; | ||
| } | ||
|
|
||
| string stderr = output.StandardError; | ||
|
|
||
| // Even in the small allocations case, the runtime might still have enough memory to construct | ||
| // an OutOfMemoryException and print the full diagnostic. | ||
| // Either token is acceptable, but at least one should be present to confirm that OOM was the reason for termination. | ||
| if (!(stderr.Contains(ExpectedOomToken) || stderr.Contains(ExpectedMinimalOomToken))) | ||
| { | ||
| Console.WriteLine($"Expected OOM diagnostic token not found in subprocess stderr."); | ||
| return Fail; | ||
| } | ||
|
|
||
| return Pass; | ||
| } | ||
| } | ||
16 changes: 16 additions & 0 deletions
16
src/tests/baseservices/exceptions/OutOfMemoryException/OutOfMemoryException.csproj
This file contains hidden or 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,16 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
eduardo-vp marked this conversation as resolved.
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <CLRTestPriority>0</CLRTestPriority> | ||
| <!-- This test spawns a subprocess; not supported on mobile, browser, or WASI platforms --> | ||
| <CLRTestTargetUnsupported Condition="'$(TargetsAppleMobile)' == 'true' or '$(TargetsAndroid)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'">true</CLRTestTargetUnsupported> | ||
| <RequiresProcessIsolation>true</RequiresProcessIsolation> | ||
| <ReferenceXUnitWrapperGenerator>false</ReferenceXUnitWrapperGenerator> | ||
| <!-- Mono doesn't enforce DOTNET_GCHeapHardLimit as a GC heap limit --> | ||
| <DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'mono'">true</DisableProjectBuild> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Compile Include="OutOfMemoryException.cs" /> | ||
| <ProjectReference Include="$(TestLibraryProjectPath)" /> | ||
| </ItemGroup> | ||
| </Project> | ||
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.
Uh oh!
There was an error while loading. Please reload this page.