* Add /attachdebugger option to automatically launch WinDbgX for test debugging When /attachdebugger is passed to test.bat, run-tests.ps1 now: - Starts te.exe with /waitfordebugger in the background - Polls for the TE.ProcessHost.exe child process via WMI - Launches WinDbgX attached directly to the test host PID - With /inproc, attaches to TE.exe itself instead This replaces the manual workflow of running /waitfordebugger, reading the PID from the output, and launching WinDbgX separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * run-tests: use /inproc with /attachdebugger, simplify exit Per review feedback from @OneBlue: - Add /inproc when /attachdebugger is set so WinDbgX attaches directly to TE.exe instead of polling for TE.ProcessHost.exe - Simplify exit to pass through TE.exe exit code directly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: update /attachdebugger to reflect /inproc behavior The script now always adds /inproc, so update the README to match: WinDbgX attaches directly to TE.exe, no ProcessHost polling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
15 KiB
Windows Subsystem for Linux (WSL)
ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
WSL is the Windows Subsystem for Linux - a compatibility layer for running Linux binary executables natively on Windows. This repository contains the core Windows components that enable WSL functionality.
Coding Conventions
Naming
- Classes/Structs:
PascalCase(e.g.,ConsoleProgressBar,DeviceHostProxy) - Functions/Methods:
PascalCase()(e.g.,GetFamilyName(),MultiByteToWide()) - Member variables:
m_camelCase(e.g.,m_isOutputConsole,m_outputHandle) - Local variables:
camelCase(e.g.,distroGuidString,asyncResponse) - Constants:
c_camelCasewithconstexpr(e.g.,constexpr size_t c_progressBarWidth = 58;) - Namespaces: lowercase with
::nesting (e.g.,wsl::windows::common::registry) - Enums:
PascalCaseValue(e.g.,LxssDistributionStateInstalled) - Windows types: Keep as-is (
LPCWSTR,HRESULT,DWORD,ULONG,GUID)
Error Handling
Use WIL (Windows Implementation Libraries) macros — never bare if (FAILED(hr)):
THROW_IF_FAILED(hr)— throw on HRESULT failureTHROW_HR_IF(hr, condition)— conditional throwTHROW_HR_IF_MSG(hr, condition, fmt, ...)— conditional throw with messageTHROW_IF_NULL_ALLOC(ptr)— throw on null allocationTHROW_LAST_ERROR_IF(condition)— throw last Win32 errorRETURN_IF_FAILED(hr)— return HRESULT on failure (no throw)RETURN_LAST_ERROR_IF_EXPECTED(condition)— expected failure pathLOG_IF_FAILED(hr)— log but don't throwCATCH_LOG()— catch and log exceptions
For user-facing errors, set a localized message before throwing:
THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageConfigInvalidBoolean(name, value));
At API boundaries (COM interfaces), return HRESULT with out params. Internal code throws exceptions.
Memory Management and RAII
Use WIL smart pointers — never raw CloseHandle() or manual cleanup:
wil::unique_handle— kernel handleswil::com_ptr<T>— COM objectswil::unique_hfile— file handleswil::unique_hkey— registry keyswil::unique_event— eventswil::unique_hlocal_string— HLOCAL stringswil::unique_cotaskmem_string— CoTaskMem strings
For non-standard resource types, use wil::unique_any<Type, Deleter, Fn>.
For cleanup scopes, use wil::scope_exit:
auto cleanup = wil::scope_exit([&] { registry::DeleteKey(LxssKey, guid.c_str()); });
// ... work ...
cleanup.release(); // dismiss on success
Synchronization
Use wil::srwlock (Slim Reader/Writer locks) with SAL annotations:
mutable wil::srwlock m_lock;
_Guarded_by_(m_lock) std::vector<Entry> m_entries;
Strings
std::wstring/std::wstring_vieware dominant throughout the codebase- Use
MultiByteToWide()/WideToMultiByte()fromstringshared.hfor conversions - Use
std::format()for formatting (the repo definesstd::formatter<std::wstring, char>for wide-to-narrow support) - The
STRING_TO_WIDE_STRING()macro handles compile-time conversion
Copy/Move Semantics
Use macros from defs.h to declare copy/move behavior:
NON_COPYABLE(MyClass);
NON_MOVABLE(MyClass);
DEFAULT_MOVABLE(MyClass);
Headers
- Use
#pragma once(no traditional#ifndefinclude guards) - In Windows C++ components, every
.cppfile must start with#include "precomp.h" - Linux-side code (
src/linux/) does not use precompiled headers - Use
.hfor C-compatible headers,.hppfor C++-only headers - Include order is enforced by
.clang-format(precomp first, then system, then project)
Copyright Headers
Use this single-line format for new files:
// Copyright (C) Microsoft Corporation. All rights reserved.
Some older files use the block format (/*++ Copyright (c) Microsoft. All rights reserved. ... --*/). Match the surrounding files in the same directory when editing.
Localization
- Use
wsl::shared::Localization::MessageXxx()static methods for user-facing strings - Use
EMIT_USER_WARNING(Localization::MessageXxx(...))for non-fatal config warnings - All new user-facing strings must have entries in
localization/strings/en-US/Resources.resw - In Resources.resw comments, use
{Locked="..."}to prevent translation of.wslconfigproperty key names
Telemetry and Logging
WSL_LOG(Name, ...)— standard trace eventWSL_LOG_DEBUG(Name, ...)— debug-only (compiled out in release viaif constexpr)WSL_LOG_TELEMETRY(Name, Tag, ...)— metrics with privacy tag and version info- Provider:
g_hTraceLoggingProvider, initialized viaWslTraceLoggingInitialize()
Platform Conditionals
Prefer constexpr checks over #ifdef where possible:
if constexpr (wsl::shared::Debug) { /* debug-only code */ }
if constexpr (wsl::shared::Arm64) { /* ARM64-specific code */ }
For compiler-specific code, use #ifdef _MSC_VER (Windows) / #ifdef __GNUC__ (Linux).
Formatting
Enforced by .clang-format:
- 130 character column limit
- 4-space indentation, no tabs
- Allman-style braces (opening brace on new line for classes, functions, structs, control statements)
- Left-aligned pointers (
int* ptr, notint *ptr) InsertBraces: true— all control statements must have braces
IDL / COM Conventions
When modifying service interfaces (src/windows/service/inc/):
- Interface attributes on separate lines:
[uuid(...), pointer_default(unique), object] - String params:
[in, unique] LPCWSTRwith[string]for marshaled strings - Handle params:
[in, system_handle(sh_file)] HANDLE - User-facing errors: pass
[in, out] LXSS_ERROR_INFO* Error - Adding methods to an existing interface is an ABI break — create a new versioned interface with a new IID
- Custom error codes:
WSL_E_xxxviaMAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + N)
Config File (.wslconfig) Conventions
When adding settings to src/shared/configfile/:
- Format is
.gitconfig-style INI:[section],key = value,#comments,\line continuation - Use the
ConfigKeytemplate class for type-safe parsing - Supported types:
bool,int(hex/octal),std::string,std::wstring,MemoryString,MacAddress, enum maps - Report invalid values with
EMIT_USER_WARNING(Localization::MessageConfigXxx(...)) - New settings require a corresponding localization string in Resources.resw
Repository Navigation
Key Directories
src/windows/— Main Windows WSL service componentssrc/linux/— Linux-side WSL componentssrc/shared/— Shared code between Windows and Linuxtest/windows/— Windows-based tests (TAEF framework)test/linux/unit_tests/— Linux unit test suitedoc/— Documentation source (MkDocs)tools/— Build and deployment scriptsdistributions/— Distribution validation and metadatalocalization/— Localized string resources
Namespace → Directory Map
| Namespace | Location |
|---|---|
wsl::shared:: |
src/shared/ |
wsl::windows::common:: |
src/windows/common/ |
wsl::windows::service:: |
src/windows/service/exe/ |
wsl::core:: |
src/windows/service/exe/ |
wsl::core::networking:: |
src/windows/common/ + src/windows/service/exe/ |
wsl::linux:: |
src/linux/init/ |
Key Files
src/shared/inc/defs.h— Shared platform definitions (NON_COPYABLE, Debug, Arm64, etc.)src/shared/inc/stringshared.h— String conversion utilitiessrc/windows/common/WslTelemetry.h— Telemetry macrossrc/windows/common/ExecutionContext.h— Error context and user-facing error macrossrc/windows/service/inc/wslservice.idl— Main service COM interface definitionssrc/windows/service/inc/wslc.idl— Container COM interface definitionssrc/windows/inc/WslPluginApi.h— Plugin API headersrc/shared/configfile/configfile.h— Config file parser.clang-format— Code formatting rules (130 col, 4-space indent, Allman braces)
Building and Deploying
Critical Platform Requirements
- Full builds ONLY work on Windows with Visual Studio and Windows SDK 26100
- DO NOT attempt to build the main WSL components on Linux — they require Windows-specific APIs, MSBuild, and Visual Studio toolchain
- Many validation and development tasks CAN be performed on Linux (documentation, formatting, Python validation scripts)
Windows Build Requirements
- CMake >= 3.25 (
winget install Kitware.CMake) - Visual Studio with these components:
- Windows SDK 26100
- MSBuild
- Universal Windows platform support for v143 build tools (X64 and ARM64)
- MSVC v143 - VS 2022 C++ ARM64 build tools (Latest + Spectre) (X64 and ARM64)
- C++ core features
- C++ ATL for latest v143 tools (X64 and ARM64)
- C++ Clang compiler for Windows
- .NET desktop development
- .NET WinUI app development tools
- Enable Developer Mode in Windows Settings OR run with Administrator privileges (required for symbolic link support)
Building WSL (Windows Only)
- Clone the repository
- Generate Visual Studio solution:
cmake . - Build:
cmake --build . -- -mOR openwsl.slnin Visual Studio
Build parameters:
cmake . -A arm64— Build for ARM64cmake . -DCMAKE_BUILD_TYPE=Release— Release buildcmake . -DBUILD_BUNDLE=TRUE— Build bundle MSIX package (requires ARM64 built first)
Deploying WSL (Windows Only)
- Install MSI:
bin\<platform>\<target>\wsl.msi - OR use script:
powershell tools\deploy\deploy-to-host.ps1 - For Hyper-V VM:
powershell tools\deploy\deploy-to-vm.ps1 -VmName <vm> -Username <user> -Password <pass>
Testing
Writing Tests (TAEF Framework)
Tests use TAEF. See .github/copilot/test.md for detailed patterns and macros.
Key points:
- Use
WSL_TEST_CLASS(Name)— not rawBEGIN_TEST_CLASS - Use
VERIFY_*macros for assertions (VERIFY_ARE_EQUAL,VERIFY_IS_TRUE, etc.) - Skip macros:
WSL1_TEST_ONLY(),WSL2_TEST_ONLY(),SKIP_TEST_ARM64() - Test infrastructure is in
test/windows/Common.h
Running Tests (Windows Only)
CRITICAL: ALWAYS build the ENTIRE project before running tests:
cmake --build . -- -m
bin\<platform>\<target>\test.bat
Why full build is required:
- Tests depend on multiple components (libwsl.dll, wsltests.dll, wslservice.exe, etc.)
- Partial builds will cause test failures
- DO NOT skip the full build step even if only one file changed
Test execution:
- Run all tests:
bin\<platform>\<target>\test.bat - Run subset:
bin\<platform>\<target>\test.bat /name:*UnitTest* - Run specific test:
bin\<platform>\<target>\test.bat /name:<class>::<test> - WSL1 tests: Add
-Version 1flag - Fast mode (after first run): Add
-fflag (requireswsl --set-default test_distro) - Requires Administrator privileges
Test debugging:
- Attach WinDbgX automatically:
/attachdebugger - Wait for debugger (manual attach):
/waitfordebugger - Break on failure:
/breakonfailure - Run in-process:
/inproc
Linux Unit Tests (Linux Only)
- Location:
test/linux/unit_tests/ - Build script:
test/linux/unit_tests/build_tests.sh - Note: Requires specific Linux build environment setup not covered in main build process
Cross-Platform Validation Tasks
Documentation (Works on Linux/Windows)
- Install tools:
pip install mkdocs-mermaid2-plugin mkdocs --break-system-packages - Build docs:
mkdocs build -f doc/mkdocs.yml - Output location:
doc/site/ - Note: May show warnings about mermaid CDN access on restricted networks
Code Formatting and Validation
- Format all source (Windows, requires
cmake .first):.\FormatSource.ps1 - Format check (Linux/cross-platform):
clang-format --dry-run --style=file <files> - Validate copyright headers:
python3 tools/devops/validate-copyright-headers.py- Note: Will report missing headers in generated/dependency files (
_deps/), which is expected
- Note: Will report missing headers in generated/dependency files (
- Validate localization:
python3 tools/devops/validate-localization.py- Note: Only works after Windows build (requires
localization/strings/en-US/Resources.resw)
- Note: Only works after Windows build (requires
Distribution Validation (Limited on Linux)
- Validate distribution info:
python3 distributions/validate.py distributions/DistributionInfo.json - Note: May fail on Linux due to network restrictions accessing distribution URLs
Pre-commit Checklist
Always run before committing:
.\FormatSource.ps1to verify formatting on changed C++ filespython3 tools/devops/validate-copyright-headers.py(ignore_deps/warnings)mkdocs build -f doc/mkdocs.ymlif documentation changed- Full Windows build if core components changed
Note: The .gitignore properly excludes build artifacts (*.sln, *.dll, *.pdb, obj/, bin/, etc.) — do not commit these files.
Frequently Used Commands
Windows Development
# Initial setup
cmake .
cmake --build . -- -m
# Deploy and test
powershell tools\deploy\deploy-to-host.ps1
wsl --version
# Run tests
bin\x64\debug\test.bat
Cross-Platform Validation
# Documentation
mkdocs build -f doc/mkdocs.yml
# Code formatting (Windows)
.\FormatSource.ps1
# Copyright header validation (reports expected issues in _deps/)
python3 tools/devops/validate-copyright-headers.py
# Distribution validation (may fail on networks without external access)
python3 distributions/validate.py distributions/DistributionInfo.json
Debugging and Logging
ETL Tracing (Windows Only)
# Collect traces
wpr -start diagnostics\wsl.wprp -filemode
# [reproduce issue]
wpr -stop logs.ETL
# Available profiles:
# - WSL (default) - General WSL tracing
# - WSL-Storage - Enhanced storage tracing
# - WSL-Networking - Comprehensive networking tracing
# - WSL-HvSocket - HvSocket-specific tracing
# Example: wpr -start diagnostics\wsl.wprp!WSL -filemode
Log Analysis Tools
- Use WPA (Windows Performance Analyzer) for ETL traces
- Key providers:
Microsoft.Windows.Lxss.Manager,Microsoft.Windows.Subsystem.Lxss
Debug Console (Linux)
Add to %USERPROFILE%\.wslconfig:
[wsl2]
debugConsole=true
Common Debugging Commands
- Debug shell:
wsl --debug-shell - Collect WSL logs:
powershell diagnostics\collect-wsl-logs.ps1 - Network logs:
powershell diagnostics\collect-wsl-logs.ps1 -LogProfile networking
Timing and Timeout Guidelines
NEVER CANCEL these operations — always wait for completion:
| Operation | Typical Duration | Minimum Timeout |
|---|---|---|
| Full Windows build | 20-45 minutes | 60+ minutes |
| Full test suite | 30-60 minutes | 90+ minutes |
| Unit test subset | 5-15 minutes | 30+ minutes |
| Documentation build | ~0.5 seconds | 5+ minutes |
| Distribution validation | 2-5 minutes | 15+ minutes |
CI/CD Integration
GitHub Actions
- distributions.yml — Validates distribution metadata (Linux)
- documentation.yml — Builds and deploys docs (Linux)
- modern-distributions.yml — Tests modern distribution support
Development Environment Setup
Windows (Full Development)
- Install Visual Studio with required components (listed above)
- Install CMake 3.25+
- Enable Developer Mode
- Clone repository
- Run
cmake .to generate solution
Linux (Documentation/Validation Only)
- Install Python 3.8+
- Install docs tools:
pip install mkdocs-mermaid2-plugin mkdocs - Clone repository
- Run validation commands as needed
Remember: This is a Windows-focused project. While some tasks can be performed on Linux, full WSL development requires Windows with Visual Studio.