-
Notifications
You must be signed in to change notification settings - Fork 1.6k
diagnostics: improve logging of hcs helper utilities on debug builds #13971
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
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR enhances diagnostic capabilities for HCS/HNS operations by adding debug-only tracing and improving error messages. The changes include adding WSL_LOG_DEBUG calls to key functions, optimizing static data caching with std::call_once, and improving error message formatting.
Key changes:
- Added WSL_LOG_DEBUG tracing to 11 HCS/HNS API wrapper functions for better diagnostics
- Refactored
GetProcessorFeatures()andGetSchemaVersion()to use static caching withstd::call_oncefor thread-safe one-time initialization - Improved error messages by standardizing format specifiers to
%lsand splitting long error messages into separate log calls
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@benhillis I've opened a new pull request, #13973, to work on those changes. Once the pull request is ready, I'll request review from you. |
…#13973) * Initial plan * Fix code review issues: assign g_schemaVersion and correct ExecutionContext Co-authored-by: benhillis <[email protected]> * Complete code review fixes Co-authored-by: benhillis <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: benhillis <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 2 changed files in this pull request and generated 4 comments.
| ExecutionContext context(Context::HCS); | ||
| static std::vector<std::string> g_processorFeatures; | ||
| static std::once_flag flag; | ||
| std::call_once(flag, [&]() { |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lambda in std::call_once captures by reference ([&]) but doesn't use any variables from the enclosing scope. This should use an empty capture list ([]) or [=] to avoid potential issues. The lambda only modifies the static variable g_processorFeatures which is safe to access without capturing.
| std::call_once(flag, [&]() { | |
| std::call_once(flag, []() { |
| if (version.Major >= majorVersion) | ||
| static std::pair<uint32_t, uint32_t> g_schemaVersion{}; | ||
| static std::once_flag flag; | ||
| std::call_once(flag, [&]() { |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lambda in std::call_once captures by reference ([&]) but doesn't use any variables from the enclosing scope. This should use an empty capture list ([]) or [=] to avoid potential issues. The lambda only modifies the static variable g_schemaVersion which is safe to access without capturing.
| std::call_once(flag, [&]() { | |
| std::call_once(flag, []() { |
| static std::vector<std::string> g_processorFeatures; | ||
| static std::once_flag flag; | ||
| std::call_once(flag, [&]() { | ||
| ExecutionContext context(Context::HCS); | ||
|
|
||
| wil::unique_cotaskmem_string result; | ||
| THROW_IF_FAILED(::HcsGetServiceProperties(c_processorCapabilitiesQuery, &result)); | ||
| wil::unique_cotaskmem_string result; | ||
| THROW_IF_FAILED(::HcsGetServiceProperties(c_processorCapabilitiesQuery, &result)); | ||
|
|
||
| const auto properties = wsl::shared::FromJson<ServicePropertiesResponse<PropertyResponse<ProcessorCapabilitiesInfo>>>(result.get()); | ||
| const auto properties = | ||
| wsl::shared::FromJson<ServicePropertiesResponse<PropertyResponse<ProcessorCapabilitiesInfo>>>(result.get()); | ||
|
|
||
| const auto& response = properties.PropertyResponses.at(c_processorCapabilities); | ||
| if (response.Error) | ||
| { | ||
| THROW_HR_MSG(static_cast<HRESULT>(response.Error->Error), "%hs", response.Error->ErrorMessage.c_str()); | ||
| } | ||
| const auto& response = properties.PropertyResponses.at(c_processorCapabilities); | ||
| if (response.Error) | ||
| { | ||
| THROW_HR_MSG(static_cast<HRESULT>(response.Error->Error), "%hs", response.Error->ErrorMessage.c_str()); | ||
| } | ||
|
|
||
| g_processorFeatures = std::move(response.Response.ProcessorFeatures); | ||
| }); | ||
|
|
||
| return response.Response.ProcessorFeatures; | ||
| return g_processorFeatures; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function returns a copy of g_processorFeatures by value, but if an exception is thrown during the std::call_once initialization, the static vector will remain in an uninitialized state. Subsequent calls will return the empty vector instead of retrying the initialization. Consider storing an optional or using a different pattern to handle initialization failures.
| static std::pair<uint32_t, uint32_t> g_schemaVersion{}; | ||
| static std::once_flag flag; | ||
| std::call_once(flag, [&]() { | ||
| ExecutionContext context(Context::HCS); | ||
|
|
||
| PropertyQuery query; | ||
| query.PropertyTypes.emplace_back(PropertyType::Basic); | ||
| wil::unique_cotaskmem_string result; | ||
| THROW_IF_FAILED(::HcsGetServiceProperties(wsl::shared::ToJsonW(query).c_str(), &result)); | ||
|
|
||
| const auto properties = wsl::shared::FromJson<ServiceProperties<BasicInformation>>(result.get()); | ||
| THROW_HR_IF_MSG(E_UNEXPECTED, properties.Properties.empty(), "%ls", result.get()); | ||
|
|
||
| uint32_t majorVersion = 0; | ||
| uint32_t minorVersion = 0; | ||
| for (const auto& version : properties.Properties[0].SupportedSchemaVersions) | ||
| { | ||
| if ((version.Major > majorVersion) || (version.Minor > minorVersion)) | ||
| if (version.Major >= majorVersion) | ||
| { | ||
| majorVersion = version.Major; | ||
| minorVersion = version.Minor; | ||
| if ((version.Major > majorVersion) || (version.Minor > minorVersion)) | ||
| { | ||
| majorVersion = version.Major; | ||
| minorVersion = version.Minor; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return {majorVersion, minorVersion}; | ||
| g_schemaVersion = {majorVersion, minorVersion}; | ||
| }); | ||
|
|
||
| return g_schemaVersion; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function returns g_schemaVersion by value, but if an exception is thrown during the std::call_once initialization, the static pair will remain in its default-initialized state (0, 0). Subsequent calls will return (0, 0) instead of retrying the initialization. Consider storing an optional or using a different pattern to handle initialization failures.
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@benhillis I've opened a new pull request, #13977, to work on those changes. Once the pull request is ready, I'll request review from you. |
| THROW_HR_MSG(static_cast<HRESULT>(response.Error->Error), "%hs", response.Error->ErrorMessage.c_str()); | ||
| } | ||
|
|
||
| g_processorFeatures = std::move(response.Response.ProcessorFeatures); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this doesn't actually move the structure since response is a const reference
| std::vector<std::string> wsl::windows::common::hcs::GetProcessorFeatures() | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now that we're keeping a static copy, we could return a const reference to avoid copying the vector every call
|
|
||
| wsl::shared::hns::HNSEndpoint wsl::windows::common::hcs::GetEndpointProperties(HCN_ENDPOINT Endpoint) | ||
| { | ||
| WSL_LOG_DEBUG("HcsGetEndpointProperties"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'm not sure how much we want to log this since it doesn't carry any specific information, and the below logline will display the full failure context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Same for below calls)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is on debug builds it will give a better idea of the sequence of calls, not sure what fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im playing with this a bit more, will adjust.
This change adds additional debug-only tracing to help diagnose HCS / HNS issues.