Brief Issue Summary
After 1.24.42 landed our workspace became unusable in VS Code.
On window open the status bar sits on "Configuring..." for several minutes with nothing further appearing in the CMake/Build output channel, and the extension host is unresponsive the whole time (other extensions stop answering, and opening a view shows "Activating Extensions..."). Launching a target looks the same. ninja reports no work to do, the log prints
[driver] Build completed: 00:00:00.748
[build] Build finished with exit code 0
and then nothing happens for minutes before the executable starts. This is every configure and every launch, not a one-off.
The project itself is fine. The same configure takes 2.1 s from the command line, a clean 662-step build takes 28.8 s, and the target is up in 0.26 s.
What it is doing
I profiled the extension host while it was stuck. All of the time is in CTest source resolution:
36.9% existsSync
35.4% readFileUtf8
16.7% readFileSync
8.3% parseDoctestCases dist/main.js:7834
0.6% tryResolveTestSourceFromExecutableSources dist/main.js:7760
tryResolveTestSourceFromExecutableSources walks every source file of the test's target once per test, and tryResolveDoctestLine does existsSync + readFileSync + a regex parse for each of those files, with nothing cached between tests:
for (const source of targetInfo.sources) {
const candidateFile = path.resolve(targetInfo.sourceDir, source);
const candidateLine = this.tryResolveDoctestLine(candidateFile, test.name);
if (candidateLine !== undefined) { ... }
}
We use GoogleTest through gtest_discover_tests(), which registers 4093 CTest tests, and the test target has 250 sources. Those sources contain no doctest TEST_CASEs, so parseDoctestCases never matches, the loop never exits early, and every test ends up scanning all 250 files. That is up to 1,023,250 existsSync + readFileSync + parse calls, synchronously on the extension host, per configure.
Two things make it avoidable:
The expensive path runs before the cheap one. resolveTestSourceLocation calls tryResolveTestSourceFromExecutableSources first and only checks the DEF_SOURCE_LINE test property afterwards. gtest_discover_tests already writes DEF_SOURCE_LINE for every test it registers, so the correct file and line were available without touching the disk at all.
There is also no way to opt out. cmake.ctest.testExplorerIntegrationEnabled: false gates the post-build refreshTests call (#4909, #4914), but the post-configure call is unconditional:
const result = await drv.configure(trigger, []);
if (result.exitCode === 0) { ... }
await this.cTestController.refreshTests(drv); // not gated
cmake.configureOnOpen: false does not help either, since it still runs off the cached configuration. I measured both and neither changed the profile.
Numbers
Same workspace, same settings, only the extension version changed. CPU time measured 240 s after opening the folder, with the machine otherwise idle:
| Version |
Extension host process |
All VS Code processes |
| 1.24.42 |
220.0 s, still climbing (~92% of one core, indefinitely) |
242.8 s |
| 1.22.28 |
5.0 s, flat after 40 s |
14.2 s |
We have pinned 1.22.28 as a workaround, which is also why the diagnostics block below reports 1.22.28 rather than 1.24.42.
Repro
- A CMake project with a GoogleTest target registered through
gtest_discover_tests(), where the test count multiplied by the number of sources in the test target is large. Ours is 4093 x 250, but it should be visible well below that.
- Build once so CTest can enumerate the tests.
- Open the folder with CMake Tools 1.24.42 and let it configure.
- The extension host pegs a core for minutes after the configure has already finished.
Suggested fix
Check DEF_SOURCE_LINE before falling back to scanning target sources, and memoize the parse per file, since the same 250 files are read and parsed 4093 times in a row. Honoring cmake.ctest.testExplorerIntegrationEnabled on the post-configure refreshTests call would at least give people a way out in the meantime.
CMake Tools Diagnostics
{
"os": "win32",
"vscodeVersion": "1.137.0",
"cmtVersion": "1.22.28",
"configurations": [
{
"folder": "c:\Users\mewio\Documents\GitHub\Shalltear",
"cmakeVersion": "4.2.0",
"configured": true,
"generator": "Ninja",
"usesPresets": true,
"compilers": {
"C": "C:/Users/mewio/Documents/GitHub/Shalltear/External/Prebuilt/llvm-mingw/bin/x86_64-w64-mingw32-clang.exe",
"CXX": "C:/Users/mewio/Documents/GitHub/Shalltear/External/Prebuilt/llvm-mingw/bin/x86_64-w64-mingw32-clang++.exe"
}
}
],
"cpptoolsIntegration": {
"isReady": false,
"hasCodeModel": false,
"activeBuildType": "",
"buildTypesSeen": [],
"requests": [],
"responses": [],
"partialMatches": [],
"targetCount": 0,
"executablesCount": 0,
"librariesCount": 0,
"targets": []
},
"settings": [
{
"communicationMode": "automatic",
"useCMakePresets": "auto",
"configureOnOpen": true
}
]
}
Debug Log
cmake.loggingLevel: trace, cmake.showTimestampsInOutput: true. Configure finishes at 13:38:24.481 and the last line of any kind is 13:38:25.092; the extension host then stays pegged at ~93% of one core for minutes with nothing further logged.
2026-09-13T13:38:23.441Z [info] [proc] Executing command: "C:\Program Files\CMake\bin\cmake.EXE" -DCMAKE_BUILD_TYPE=Debug -DSHALLTEAR_BUILD_SHARED=ON -DCMAKE_C_COMPILER=... -DCMAKE_CXX_COMPILER=... -DCMAKE_MAKE_PROGRAM=... -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S C:/Users/mewio/Documents/GitHub/Shalltear -B C:/Users/mewio/Documents/GitHub/Shalltear/build -G Ninja
2026-09-13T13:38:24.478Z [trace] [cache] Read line in cache with name=GIT_EXECUTABLE-ADVANCED, typename=INTERNAL, valuestr=1
2026-09-13T13:38:24.481Z [trace] [cache] Parsed 161 cache entries
2026-09-13T13:38:24.481Z [trace] [cache] Parsed 161 entries from C:/Users/mewio/Documents/GitHub/Shalltear/build/CMakeCache.txt
2026-09-13T13:38:25.092Z [trace] [rollbar] Invoking function [$] with Rollbar wrapping [$Resolving process on "close" event]
<nothing further; UI frozen from here>
Additional Information
The profile was taken by launching VS Code with --inspect-extensions and running a CPU profile against the extension host over the DevTools protocol while the freeze was in progress, on an otherwise idle machine. The A/B was run in a separate --user-data-dir and --extensions-dir so that nothing but the extension version differed between the two runs.
Environment: VS Code 1.137.0, Windows 11 build 26200, CMake 4.2.0, Ninja, clang 23.1.1 (llvm-mingw), 28 logical cores. Test target is 250 sources and 4093 CTest tests registered by gtest_discover_tests.
Brief Issue Summary
After 1.24.42 landed our workspace became unusable in VS Code.
On window open the status bar sits on "Configuring..." for several minutes with nothing further appearing in the CMake/Build output channel, and the extension host is unresponsive the whole time (other extensions stop answering, and opening a view shows "Activating Extensions..."). Launching a target looks the same. ninja reports no work to do, the log prints
and then nothing happens for minutes before the executable starts. This is every configure and every launch, not a one-off.
The project itself is fine. The same configure takes 2.1 s from the command line, a clean 662-step build takes 28.8 s, and the target is up in 0.26 s.
What it is doing
I profiled the extension host while it was stuck. All of the time is in CTest source resolution:
tryResolveTestSourceFromExecutableSourceswalks every source file of the test's target once per test, andtryResolveDoctestLinedoesexistsSync+readFileSync+ a regex parse for each of those files, with nothing cached between tests:We use GoogleTest through
gtest_discover_tests(), which registers 4093 CTest tests, and the test target has 250 sources. Those sources contain no doctestTEST_CASEs, soparseDoctestCasesnever matches, the loop never exits early, and every test ends up scanning all 250 files. That is up to 1,023,250existsSync+readFileSync+ parse calls, synchronously on the extension host, per configure.Two things make it avoidable:
The expensive path runs before the cheap one.
resolveTestSourceLocationcallstryResolveTestSourceFromExecutableSourcesfirst and only checks theDEF_SOURCE_LINEtest property afterwards.gtest_discover_testsalready writesDEF_SOURCE_LINEfor every test it registers, so the correct file and line were available without touching the disk at all.There is also no way to opt out.
cmake.ctest.testExplorerIntegrationEnabled: falsegates the post-buildrefreshTestscall (#4909, #4914), but the post-configure call is unconditional:cmake.configureOnOpen: falsedoes not help either, since it still runs off the cached configuration. I measured both and neither changed the profile.Numbers
Same workspace, same settings, only the extension version changed. CPU time measured 240 s after opening the folder, with the machine otherwise idle:
We have pinned 1.22.28 as a workaround, which is also why the diagnostics block below reports 1.22.28 rather than 1.24.42.
Repro
gtest_discover_tests(), where the test count multiplied by the number of sources in the test target is large. Ours is 4093 x 250, but it should be visible well below that.Suggested fix
Check
DEF_SOURCE_LINEbefore falling back to scanning target sources, and memoize the parse per file, since the same 250 files are read and parsed 4093 times in a row. Honoringcmake.ctest.testExplorerIntegrationEnabledon the post-configurerefreshTestscall would at least give people a way out in the meantime.CMake Tools Diagnostics
{ "os": "win32", "vscodeVersion": "1.137.0", "cmtVersion": "1.22.28", "configurations": [ { "folder": "c:\Users\mewio\Documents\GitHub\Shalltear", "cmakeVersion": "4.2.0", "configured": true, "generator": "Ninja", "usesPresets": true, "compilers": { "C": "C:/Users/mewio/Documents/GitHub/Shalltear/External/Prebuilt/llvm-mingw/bin/x86_64-w64-mingw32-clang.exe", "CXX": "C:/Users/mewio/Documents/GitHub/Shalltear/External/Prebuilt/llvm-mingw/bin/x86_64-w64-mingw32-clang++.exe" } } ], "cpptoolsIntegration": { "isReady": false, "hasCodeModel": false, "activeBuildType": "", "buildTypesSeen": [], "requests": [], "responses": [], "partialMatches": [], "targetCount": 0, "executablesCount": 0, "librariesCount": 0, "targets": [] }, "settings": [ { "communicationMode": "automatic", "useCMakePresets": "auto", "configureOnOpen": true } ] }Debug Log
Additional Information
The profile was taken by launching VS Code with
--inspect-extensionsand running a CPU profile against the extension host over the DevTools protocol while the freeze was in progress, on an otherwise idle machine. The A/B was run in a separate--user-data-dirand--extensions-dirso that nothing but the extension version differed between the two runs.Environment: VS Code 1.137.0, Windows 11 build 26200, CMake 4.2.0, Ninja, clang 23.1.1 (llvm-mingw), 28 logical cores. Test target is 250 sources and 4093 CTest tests registered by
gtest_discover_tests.