wsl.exe changes console mode? (Garbles CTest output with multiple commands in flight) #11954

Closed
opened 2026-01-31 03:02:14 +00:00 by claunia · 7 comments
Owner

Originally created by @MathiasMagnus on GitHub (Jan 4, 2021).

When running multiple commands in parallel inside WSL using CTest, the CTest output itself is garbled. The unix sleep command doesn't even have console output, so that cannot be the issue. If the name of the issue is nonsense, please feel free to change it.

Note how the new lines align with the end of the previous line. This is Windows ctest.exe invoking commands inside WSL using wsl.exe.

Environment

Microsoft Windows [Version 10.0.19042.685]
Windows Terminal version (if applicable): 1.4.3243.0
cmake version 3.19.0-rc2

Steps to reproduce

PS C:\Users\mate\Desktop\SleepWSL\build> Get-Content ..\CMakeLists.txt
cmake_minimum_required(VERSION 3.0)

project(SleepWSL
  LANGUAGES NONE
)

set(BUILD_TESTING ON CACHE STRING "Cache var for enabling testing")
include(CTest)

foreach(NUMBER RANGE 3)
  add_test(
    NAME WSL-${NUMBER}
    COMMAND wsl.exe -- sleep 1s
  )
  add_test(
    NAME Native-${NUMBER}
    COMMAND timeout 1
  )
endforeach()
PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\cmake.exe -G Ninja ..
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/mate/Desktop/SleepWSL/build

Expected behavior

PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\ctest.exe -R Native --parallel 4
Test project C:/Users/mate/Desktop/SleepWSL/build
    Start 2: Native-0
    Start 4: Native-1
    Start 6: Native-2
    Start 8: Native-3
1/4 Test #2: Native-0 .........................   Passed    0.99 sec
2/4 Test #4: Native-1 .........................   Passed    0.99 sec
3/4 Test #6: Native-2 .........................   Passed    0.99 sec
4/4 Test #8: Native-3 .........................   Passed    0.99 sec

Actual behavior

PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\ctest.exe -R WSL --parallel 4
Test project C:/Users/mate/Desktop/SleepWSL/build
    Start 7: WSL-3
    Start 1: WSL-0
    Start 5: WSL-2
    Start 3: WSL-1
1/4 Test #7: WSL-3 ............................   Passed    1.03 sec
                                                                    2/4 Test #1: WSL-0 ............................   Passed    1.03 sec
                     3/4 Test #5: WSL-2 ............................   Passed    1.03 sec
                                                                                         4/4 Test #3: WSL-1 ............................   Passed    1.03 sec

                                          100% tests passed, 0 tests failed out of 4

                                                                                    Total Test time (real) =   1.06 sec
Originally created by @MathiasMagnus on GitHub (Jan 4, 2021). When running multiple commands in parallel inside WSL using CTest, the CTest output itself is garbled. The unix `sleep` command doesn't even have console output, so that cannot be the issue. If the name of the issue is nonsense, please feel free to change it. Note how the new lines align with the end of the previous line. This is Windows `ctest.exe` invoking commands inside WSL using `wsl.exe`. # Environment ```none Microsoft Windows [Version 10.0.19042.685] Windows Terminal version (if applicable): 1.4.3243.0 cmake version 3.19.0-rc2 ``` # Steps to reproduce ```none PS C:\Users\mate\Desktop\SleepWSL\build> Get-Content ..\CMakeLists.txt cmake_minimum_required(VERSION 3.0) project(SleepWSL LANGUAGES NONE ) set(BUILD_TESTING ON CACHE STRING "Cache var for enabling testing") include(CTest) foreach(NUMBER RANGE 3) add_test( NAME WSL-${NUMBER} COMMAND wsl.exe -- sleep 1s ) add_test( NAME Native-${NUMBER} COMMAND timeout 1 ) endforeach() PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\cmake.exe -G Ninja .. -- Configuring done -- Generating done -- Build files have been written to: C:/Users/mate/Desktop/SleepWSL/build ``` # Expected behavior ```none PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\ctest.exe -R Native --parallel 4 Test project C:/Users/mate/Desktop/SleepWSL/build Start 2: Native-0 Start 4: Native-1 Start 6: Native-2 Start 8: Native-3 1/4 Test #2: Native-0 ......................... Passed 0.99 sec 2/4 Test #4: Native-1 ......................... Passed 0.99 sec 3/4 Test #6: Native-2 ......................... Passed 0.99 sec 4/4 Test #8: Native-3 ......................... Passed 0.99 sec ``` # Actual behavior ```none PS C:\Users\mate\Desktop\SleepWSL\build> C:\Kellekek\Kitware\CMake\3.19.0-rc2\bin\ctest.exe -R WSL --parallel 4 Test project C:/Users/mate/Desktop/SleepWSL/build Start 7: WSL-3 Start 1: WSL-0 Start 5: WSL-2 Start 3: WSL-1 1/4 Test #7: WSL-3 ............................ Passed 1.03 sec 2/4 Test #1: WSL-0 ............................ Passed 1.03 sec 3/4 Test #5: WSL-2 ............................ Passed 1.03 sec 4/4 Test #3: WSL-1 ............................ Passed 1.03 sec 100% tests passed, 0 tests failed out of 4 Total Test time (real) = 1.06 sec ```
claunia added the Issue-QuestionNeeds-Tag-FixResolution-Answered labels 2026-01-31 03:02:15 +00:00
Author
Owner

@zadjii-msft commented on GitHub (Jan 4, 2021):

This is by design - wsl.exe changes the console modes to be more friendly to output from linux-style applications. Looks to me like CTest is just emitting \n to end it's lines, while wsl is disabling "newline auto-return", which means commandline apps would need to emit \r\n to move to the start of the line AND move down a line.

@zadjii-msft commented on GitHub (Jan 4, 2021): This is by design - `wsl.exe` changes the console modes to be more friendly to output from linux-style applications. Looks to me like CTest is just emitting `\n` to end it's lines, while `wsl` is disabling "newline auto-return", which means commandline apps would need to emit `\r\n` to move to the start of the line AND move down a line.
Author
Owner

@MathiasMagnus commented on GitHub (Jan 4, 2021):

I have a patch which solves the issue. Opened a discussion about it on the Kitware repo. Hope it gets resolved.

@MathiasMagnus commented on GitHub (Jan 4, 2021): I have a patch which solves the issue. Opened a discussion about it on the [Kitware repo](https://gitlab.kitware.com/cmake/cmake/-/issues/21656). Hope it gets resolved.
Author
Owner

@DHowett commented on GitHub (Jan 4, 2021):

Until #4954 lands (which would be groundbreaking, heh) the advice on Windows for all console processes that spawn other processes and have specific console mode requirements is to save/restore the mode across a subprocess launch.

@DHowett commented on GitHub (Jan 4, 2021): Until #4954 lands (which would be groundbreaking, heh) the advice on Windows for _all console processes that spawn other processes_ and have specific console mode requirements is to save/restore the mode across a subprocess launch.
Author
Owner

@MathiasMagnus commented on GitHub (Jan 11, 2021):

@DHowett I tried looking into how that could be achieved in the specific case of CTest, but it does become tricky fairly fast, as CTest spawns multiple processes asynchronously (ctest.exe --parallel <n>) to get things done fast, like proper build systems do, and as a result the console mode may change just about any time. To make this bullet proof, one would have to couple parts of the software that were deliberetly decoupled, and implement shared mutex for all logging components and the test scheduler to share, because console output and test launch cannot happen concurrently. It's not enough to save and restore console state after a test has launched, one also has to make sure no logging occurs in the meantime.

I'm just noting this here, that it may get hairy fast if every app (launching sub-processes) have to take on this burden. May influence prioritization of #4954.

@MathiasMagnus commented on GitHub (Jan 11, 2021): @DHowett I tried looking into how that could be achieved in the specific case of CTest, but it does become tricky fairly fast, as CTest spawns multiple processes asynchronously (`ctest.exe --parallel <n>`) to get things done fast, like proper build systems do, and as a result the console mode may change just about _any time_. To make this bullet proof, one would have to couple parts of the software that were deliberetly decoupled, and implement shared mutex for all logging components and the test scheduler to share, because console output and test launch cannot happen concurrently. It's not enough to save and restore console state after a test has launched, one also has to make sure no logging occurs in the meantime. I'm just noting this here, that it may get hairy fast if every app (launching sub-processes) have to take on this burden. May influence prioritization of #4954.
Author
Owner

@MathiasMagnus commented on GitHub (Jan 11, 2021):

@DHowett @zadjii-msft Is this the idiom I should be using on STD_OUTPUT_HANDLE/STD_ERROR_HANDLE to save console state and be restoring them?

@MathiasMagnus commented on GitHub (Jan 11, 2021): @DHowett @zadjii-msft Is [this](https://docs.microsoft.com/en-us/windows/console/reading-input-buffer-events) the idiom I should be using on `STD_OUTPUT_HANDLE`/`STD_ERROR_HANDLE` to save console state and be restoring them?
Author
Owner

@MathiasMagnus commented on GitHub (Feb 25, 2021):

@DHowett @zadjii-msft I tried patching CMake with the following code snippet (in various places) but my output is still scrambled. No matter if I save the console state right around (wsl.exe) process launch or I save it right in the CTest constructor, if I restore it using the linked idiom, it still scrambles the output. I put a lock_guard around both launching a process and logging, so that they don't mingle, but it doesn't work. I'm starting the quesiton whether this is the correct way to handle console state.

@MathiasMagnus commented on GitHub (Feb 25, 2021): @DHowett @zadjii-msft I tried patching CMake with the [following](https://github.com/Kitware/CMake/commit/eeec9b0dc1cbeefff470055638170f8f1a16aca3#diff-bed0750dae7f1cc40e919d9b6b7b6489ef116333cd73eac74a65cf8515beb348R607-R651) code snippet (in various places) but my output is still scrambled. No matter if I save the console state right around (wsl.exe) process launch or I save it right in the CTest constructor, if I restore it using the linked idiom, it still scrambles the output. I put a lock_guard around both launching a process and logging, so that they don't mingle, but it doesn't work. I'm starting the quesiton whether this is the correct way to handle console state.
Author
Owner

@zadjii-msft commented on GitHub (Feb 26, 2021):

Out of curiosity, what does save_console_mode(STD_OUTPUT_HANDLE) return before and after the call to ForkProcess there? Presumably, it's different values.

I might be forgetting my *nix from college, but presumably isn't the call to fork going to start the other process, then return immediately? So you'd be returning control to the test runner, then restoring the console modes before WSL even gets a chance to change them. Then the WSL process is going to start up, attached to the same console, and change the modes. Granted, I've got no experience with the internals of the CMake codebase here, so ForkProcess might wait for the child to complete. In that case, I don't know what's going on here.

@zadjii-msft commented on GitHub (Feb 26, 2021): Out of curiosity, what does `save_console_mode(STD_OUTPUT_HANDLE)` return before _and after_ the call to `ForkProcess` there? Presumably, it's different values. I might be forgetting my \*nix from college, but presumably isn't the call to `fork` going to start the other process, then return immediately? So you'd be returning control to the test runner, then restoring the console modes before WSL even gets a chance to change them. Then the WSL process is going to start up, attached to the same console, and change the modes. Granted, I've got no experience with the internals of the CMake codebase here, so `ForkProcess` might wait for the child to complete. In that case, I don't know what's going on here.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#11954