diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 2309e8173d..40ad2d5fa9 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -4408,10 +4408,9 @@ HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly) } BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID; + ULONG id = 0; if (!IsKernelDebugger()) { - ULONG id = 0; - if (bSwitchedOutFiber) { table.WriteColumn(0, "<<<< "); @@ -4456,8 +4455,19 @@ HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly) // Apartment state #ifndef FEATURE_PAL DWORD_PTR OleTlsDataAddr; - if (IsWindowsTarget() && !bSwitchedOutFiber - && SafeReadMemory(TO_TADDR(Thread.teb + offsetof(TEB, ReservedForOle)), + ULONG64 teb = 0; + if (IsWindowsTarget() && !bSwitchedOutFiber && id != 0) + { + ULONG curId; + if (SUCCEEDED(g_ExtSystem->GetCurrentThreadId(&curId)) && + SUCCEEDED(g_ExtSystem->SetCurrentThreadId(id))) + { + g_ExtSystem->GetCurrentThreadTeb(&teb); + g_ExtSystem->SetCurrentThreadId(curId); + } + } + if (teb != 0 + && SafeReadMemory(TO_TADDR(teb + offsetof(TEB, ReservedForOle)), &OleTlsDataAddr, sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) { diff --git a/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.cs b/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.cs new file mode 100644 index 0000000000..7e1045aceb --- /dev/null +++ b/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Threading; + +internal sealed class ThreadApartment +{ + private static readonly ManualResetEventSlim s_staReady = new(); + private static readonly ManualResetEventSlim s_mtaReady = new(); + + private static void Main() + { + // Create an STA thread using SetApartmentState before Start. + // The runtime will call CoInitializeEx(COINIT_APARTMENTTHREADED) when the thread starts. + Thread staThread = new Thread(() => + { + s_staReady.Set(); + Thread.Sleep(Timeout.Infinite); + }); + staThread.SetApartmentState(ApartmentState.STA); + staThread.IsBackground = true; + staThread.Start(); + + // Create an MTA thread using SetApartmentState before Start. + Thread mtaThread = new Thread(() => + { + s_mtaReady.Set(); + Thread.Sleep(Timeout.Infinite); + }); + mtaThread.SetApartmentState(ApartmentState.MTA); + mtaThread.IsBackground = true; + mtaThread.Start(); + + s_staReady.Wait(); + s_mtaReady.Wait(); + + Debugger.Break(); + + throw new Exception("ThreadApartment test complete"); + } +} diff --git a/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.csproj b/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.csproj new file mode 100644 index 0000000000..5438f6aa45 --- /dev/null +++ b/src/tests/SOS.UnitTests/Debuggees/ThreadApartment/ThreadApartment.csproj @@ -0,0 +1,9 @@ + + + Exe + $(BuildProjectFramework) + $(SupportedSubProcessTargetFrameworks) + + $(NoWarn);CA1416 + + diff --git a/src/tests/SOS.UnitTests/SOS.cs b/src/tests/SOS.UnitTests/SOS.cs index 59de9f14ec..88350a0b00 100644 --- a/src/tests/SOS.UnitTests/SOS.cs +++ b/src/tests/SOS.UnitTests/SOS.cs @@ -463,6 +463,17 @@ public async Task SimpleThrow(TestConfiguration config) await SOSTestHelpers.RunTest(config, debuggeeName: "SimpleThrow", scriptName: "SimpleThrow.script", Output, testTriage: true); } + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ThreadApartment(TestConfiguration config) + { + if (OS.Kind != OSKind.Windows) + { + throw new SkipTestException("Apartment state is a Windows COM concept"); + } + + await SOSTestHelpers.RunTest(config, debuggeeName: "ThreadApartment", scriptName: "ThreadApartment.script", Output); + } + [SkippableTheory, MemberData(nameof(Configurations))] public async Task AsyncMain(TestConfiguration config) { diff --git a/src/tests/SOS.UnitTests/Scripts/ThreadApartment.script b/src/tests/SOS.UnitTests/Scripts/ThreadApartment.script new file mode 100644 index 0000000000..9fdf82186d --- /dev/null +++ b/src/tests/SOS.UnitTests/Scripts/ThreadApartment.script @@ -0,0 +1,18 @@ +# +# Tests that clrthreads properly displays COM apartment state (STA/MTA). +# This test is Windows-only since apartment state is a Windows COM concept. +# + +CONTINUE + +LOADSOS + +# Verify that clrthreads shows the apartment state column with STA and MTA values. +# The ThreadApartment debuggee creates one STA and one MTA thread before breaking. +IFDEF:WINDOWS +SOSCOMMAND:clrthreads +VERIFY:\s*ThreadCount:\s+\s+ +VERIFY:\s+ID\s+OSID\s+ThreadOBJ\s+State.*Apt.* +VERIFY:.*\bSTA\b.* +VERIFY:.*\bMTA\b.* +ENDIF:WINDOWS