Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@
</RegistryKey>
</RegistryKey>

<!-- ICrashDumpCallback-->
<RegistryKey Root="HKCR" Key="Interface\{8C5A7B14-9D26-4FAE-AB31-7E5BC23F4801}">
<RegistryValue Value="ICrashDumpCallback" Type="string" />
<RegistryKey Key="ProxyStubClsid32">
<RegistryValue Value="{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}" Type="string" />
</RegistryKey>
</RegistryKey>

<!-- IProgressCallback-->
<RegistryKey Root="HKCR" Key="Interface\{5038842F-53DB-4F30-A6D0-A41B02C94AC1}">
<RegistryValue Value="IProgressCallback" Type="string" />
Expand Down
2 changes: 2 additions & 0 deletions src/windows/WslcSDK/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(SOURCES
IOCallback.cpp
ProgressCallback.cpp
TerminationCallback.cpp
CrashDumpCallback.cpp
wslcsdk.cpp
WslcsdkPrivate.cpp
)
Expand All @@ -10,6 +11,7 @@ set(HEADERS
IOCallback.h
ProgressCallback.h
TerminationCallback.h
CrashDumpCallback.h
wslcsdk.h
WslcsdkPrivate.h
)
Expand Down
52 changes: 52 additions & 0 deletions src/windows/WslcSDK/CrashDumpCallback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

CrashDumpCallback.cpp

Abstract:

Implementation of a type that implements ICrashDumpCallback.

--*/
#include "precomp.h"
#include "CrashDumpCallback.h"

CrashDumpCallback::CrashDumpCallback(WslcSessionCrashDumpCallback callback, PVOID context) :
m_callback(callback), m_context(context)
{
}

HRESULT STDMETHODCALLTYPE CrashDumpCallback::OnCrashDump(
_In_ LPCWSTR DumpPath, _In_opt_ LPCSTR ProcessName, _In_ ULONGLONG Pid, _In_ ULONG Signal, _In_ ULONGLONG Timestamp)
try
{
if (m_callback)
{
WslcSessionCrashDumpInfo info{};
info.dumpPath = DumpPath;
info.processName = ProcessName ? ProcessName : "";
info.pid = Pid;
info.signal = Signal;
info.timestamp = Timestamp;

m_callback(&info, m_context);
}

return S_OK;
}
CATCH_RETURN();

winrt::com_ptr<CrashDumpCallback> CrashDumpCallback::CreateIf(const WslcSessionOptionsInternal* options)
{
if (options->crashDumpCallback)
{
return winrt::make_self<CrashDumpCallback>(options->crashDumpCallback, options->crashDumpCallbackContext);
}
else
{
return nullptr;
}
}
34 changes: 34 additions & 0 deletions src/windows/WslcSDK/CrashDumpCallback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

CrashDumpCallback.h

Abstract:

Header for a type that implements ICrashDumpCallback. Bridges the COM
ICrashDumpCallback interface back to the C-style SDK callback registered
via WslcSetSessionSettingsCrashDumpCallback.

--*/
#pragma once
#include "wslc.h"
#include "wslcsdkprivate.h"
#include <winrt/base.h>

struct CrashDumpCallback : public winrt::implements<CrashDumpCallback, ICrashDumpCallback>
{
CrashDumpCallback(WslcSessionCrashDumpCallback callback, PVOID context);

// ICrashDumpCallback
HRESULT STDMETHODCALLTYPE OnCrashDump(_In_ LPCWSTR DumpPath, _In_opt_ LPCSTR ProcessName, _In_ ULONGLONG Pid, _In_ ULONG Signal, _In_ ULONGLONG Timestamp) override;

// Creates a CrashDumpCallback if the options provides a callback.
static winrt::com_ptr<CrashDumpCallback> CreateIf(const WslcSessionOptionsInternal* options);

private:
WslcSessionCrashDumpCallback m_callback = nullptr;
PVOID m_context = nullptr;
};
4 changes: 4 additions & 0 deletions src/windows/WslcSDK/WslcsdkPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ typedef struct WslcSessionOptionsInternal
WslcSessionFeatureFlags featureFlags;
WslcSessionTerminationCallback terminationCallback;
PVOID terminationCallbackContext;

WslcSessionCrashDumpCallback crashDumpCallback;
PVOID crashDumpCallbackContext;
} WslcSessionOptionsInternal;

static_assert(sizeof(WslcSessionOptionsInternal) == WSLC_SESSION_OPTIONS_SIZE, "WSLC_SESSION_OPTIONS_INTERNAL size mismatch");
Expand Down Expand Up @@ -108,6 +111,7 @@ struct WslcSessionImpl
{
wil::com_ptr<IWSLCSession> session;
wil::com_ptr<ITerminationCallback> terminationCallback;
wil::com_ptr<ICrashDumpCallback> crashDumpCallback;
};

WslcSessionImpl* GetInternalType(WslcSession handle);
Expand Down
22 changes: 22 additions & 0 deletions src/windows/WslcSDK/wslcsdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Module Name:
#include "Defaults.h"
#include "ProgressCallback.h"
#include "TerminationCallback.h"
#include "CrashDumpCallback.h"
#include "Localization.h"
#include "WslInstall.h"
#include "wslutil.h"
Expand Down Expand Up @@ -440,6 +441,12 @@ try
result->terminationCallback.attach(terminationCallback.as<ITerminationCallback>().detach());
runtimeSettings.TerminationCallback = terminationCallback.get();
}
auto crashDumpCallback = CrashDumpCallback::CreateIf(internalType);
if (crashDumpCallback)
{
result->crashDumpCallback.attach(crashDumpCallback.as<ICrashDumpCallback>().detach());
runtimeSettings.CrashDumpCallback = crashDumpCallback.get();
}
runtimeSettings.FeatureFlags = ConvertFlags(internalType->featureFlags);
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsVirtioFs);
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsDnsTunneling);
Expand Down Expand Up @@ -599,6 +606,20 @@ try
}
CATCH_RETURN();

STDAPI WslcSetSessionSettingsCrashDumpCallback(
_In_ WslcSessionSettings* sessionSettings, _In_opt_ WslcSessionCrashDumpCallback crashDumpCallback, _In_opt_ PVOID crashDumpContext)
try
{
auto internalType = CheckAndGetInternalType(sessionSettings);
RETURN_HR_IF(E_INVALIDARG, crashDumpCallback == nullptr && crashDumpContext != nullptr);

internalType->crashDumpCallback = crashDumpCallback;
internalType->crashDumpCallbackContext = crashDumpContext;

return S_OK;
}
CATCH_RETURN();

STDAPI WslcReleaseSession(_In_ WslcSession session)
try
{
Expand All @@ -608,6 +629,7 @@ try
// the termination callback ends up being invoked by session destruction.
internalType->session.reset();
internalType->terminationCallback.reset();
internalType->crashDumpCallback.reset();

return S_OK;
}
Expand Down
1 change: 1 addition & 0 deletions src/windows/WslcSDK/wslcsdk.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ WslcSetSessionSettingsCpuCount
WslcSetSessionSettingsMemory
WslcSetSessionSettingsTimeout
WslcSetSessionSettingsVhd
WslcSetSessionSettingsCrashDumpCallback

WslcTerminateSession
WslcSessionAuthenticate
Expand Down
16 changes: 15 additions & 1 deletion src/windows/WslcSDK/wslcsdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ EXTERN_C_START
#define WSLC_E_REGISTRY_BLOCKED_BY_POLICY MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 13) /* 0x8004060D */

// Session values
#define WSLC_SESSION_OPTIONS_SIZE 88
#define WSLC_SESSION_OPTIONS_SIZE 104
#define WSLC_SESSION_OPTIONS_ALIGNMENT 8

typedef struct WslcSessionSettings
Expand Down Expand Up @@ -125,6 +125,17 @@ typedef enum WslcSessionTerminationReason

typedef __callback void(CALLBACK* WslcSessionTerminationCallback)(_In_ WslcSessionTerminationReason reason, _In_opt_ PVOID context);

typedef struct WslcSessionCrashDumpInfo
{
_Field_z_ PCWSTR dumpPath;
_Field_z_ PCSTR processName;
uint64_t pid;
uint32_t signal;
uint64_t timestamp;
} WslcSessionCrashDumpInfo;

typedef __callback void(CALLBACK* WslcSessionCrashDumpCallback)(_In_ const WslcSessionCrashDumpInfo* info, _In_opt_ PVOID context);

STDAPI WslcInitSessionSettings(_In_ PCWSTR name, _In_ PCWSTR storagePath, _Out_ WslcSessionSettings* sessionSettings);

STDAPI WslcCreateSession(_In_ WslcSessionSettings* sessionSettings, _Out_ WslcSession* session, _Outptr_opt_result_z_ PWSTR* errorMessage);
Expand All @@ -142,6 +153,9 @@ STDAPI WslcSetSessionSettingsFeatureFlags(_In_ WslcSessionSettings* sessionSetti
STDAPI WslcSetSessionSettingsTerminationCallback(
_In_ WslcSessionSettings* sessionSettings, _In_opt_ WslcSessionTerminationCallback terminationCallback, _In_opt_ PVOID terminationContext);

STDAPI WslcSetSessionSettingsCrashDumpCallback(
_In_ WslcSessionSettings* sessionSettings, _In_opt_ WslcSessionCrashDumpCallback crashDumpCallback, _In_opt_ PVOID crashDumpContext);

STDAPI WslcTerminateSession(_In_ WslcSession session);
STDAPI WslcReleaseSession(_In_ WslcSession session);

Expand Down
1 change: 1 addition & 0 deletions src/windows/service/exe/WSLCSessionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ WSLCSessionInitSettings WSLCSessionManagerImpl::CreateSessionSettings(
sessionSettings.RootVhdTypeOverride = Settings->RootVhdTypeOverride;
sessionSettings.StorageFlags = Settings->StorageFlags;
sessionSettings.SwapSizeMb = Settings->MemoryMb;
sessionSettings.CrashDumpCallback = Settings->CrashDumpCallback;
return sessionSettings;
}

Expand Down
17 changes: 17 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ interface ITerminationCallback : IUnknown
HRESULT OnTermination(WSLCVirtualMachineTerminationReason Reason, LPCWSTR Details);
};

[
uuid(8C5A7B14-9D26-4FAE-AB31-7E5BC23F4801),
pointer_default(unique),
object
]
interface ICrashDumpCallback : IUnknown
{
HRESULT OnCrashDump(
[in, string] LPCWSTR DumpPath,
[in, unique, string] LPCSTR ProcessName,
[in] ULONGLONG Pid,
[in] ULONG Signal,
[in] ULONGLONG Timestamp);
};

[
uuid(5038842F-53DB-4F30-A6D0-A41B02C94AC1),
pointer_default(unique),
Expand Down Expand Up @@ -526,6 +541,7 @@ typedef struct _WSLCSessionSettings {
// Below options are used for debugging purposes only.
[unique] LPCWSTR RootVhdOverride;
[unique] LPCSTR RootVhdTypeOverride;
[unique] ICrashDumpCallback* CrashDumpCallback;
} WSLCSessionSettings;

typedef enum _WSLCLogsFlags
Expand Down Expand Up @@ -716,6 +732,7 @@ typedef struct _WSLCSessionInitSettings
WSLCNetworkingMode NetworkingMode;
WSLCFeatureFlags FeatureFlags;
[unique] LPCSTR RootVhdTypeOverride;
[unique] ICrashDumpCallback* CrashDumpCallback;
} WSLCSessionInitSettings;

[
Expand Down
20 changes: 17 additions & 3 deletions src/windows/wslcsession/WSLCVirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ WSLCVirtualMachine::WSLCVirtualMachine(_In_ IWSLCVirtualMachine* Vm, _In_ const
m_networkingMode(Settings->NetworkingMode),
m_bootTimeoutMs(Settings->BootTimeoutMs),
m_rootVhdType(Settings->RootVhdTypeOverride ? Settings->RootVhdTypeOverride : "ext4"),
m_crashDumpCallback(Settings->CrashDumpCallback),
m_sessionTerminatingEvent(SessionTerminatingEvent)
{
// N.B. The constructor should not run any operation that could throw, so the destructor runs even if the VM fails to boot.
Expand Down Expand Up @@ -1247,6 +1248,8 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)
// No impersonation needed - the session process already runs as the user.
wslutil::SetThreadDescription(L"CrashDumpCollection");

const auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);

const auto crashDumpFolder = filesystem::GetTempFolderPath(GetCurrentProcessToken()) / L"wslc-crashes";

while (!m_vmTerminatingEvent.is_signaled())
Expand All @@ -1273,10 +1276,14 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)
const auto bufferSize = responseSpan.size_bytes() - offsetof(LX_PROCESS_CRASH, Buffer);
const std::string process(message.Buffer, strnlen(message.Buffer, bufferSize));

const auto crashPid = message.Pid;
const auto crashSignal = message.Signal;
const auto crashTimestamp = message.Timestamp;

constexpr auto dumpExtension = ".dmp";
constexpr auto dumpPrefix = "wsl-crash";

auto filename = std::format("{}-{}-{}-{}-{}{}", dumpPrefix, message.Timestamp, message.Pid, process, message.Signal, dumpExtension);
auto filename = std::format("{}-{}-{}-{}-{}{}", dumpPrefix, crashTimestamp, crashPid, process, crashSignal, dumpExtension);

std::replace_if(
filename.begin(),
Expand All @@ -1289,8 +1296,8 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)
WSL_LOG(
"WSLCLinuxCrash",
TraceLoggingValue(fullPath.c_str(), "FullPath"),
TraceLoggingValue(message.Pid, "Pid"),
TraceLoggingValue(message.Signal, "Signal"),
TraceLoggingValue(crashPid, "Pid"),
TraceLoggingValue(crashSignal, "Signal"),
TraceLoggingValue(process.c_str(), "process"));

filesystem::EnsureDirectory(crashDumpFolder.c_str());
Expand All @@ -1314,6 +1321,13 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)

transaction.SendResultMessage<std::int32_t>(0);
relay::InterruptableRelay(reinterpret_cast<HANDLE>(channel.Socket()), file.get(), nullptr);
Comment thread
kvega005 marked this conversation as resolved.

// Invoke the crash dump callback (if any) now that the dump file has been fully
// written. Failures in the callback are logged but do not interrupt crash collection.
if (m_crashDumpCallback)
{
LOG_IF_FAILED(m_crashDumpCallback->OnCrashDump(fullPath.c_str(), process.c_str(), crashPid, crashSignal, crashTimestamp));
Comment thread
kvega005 marked this conversation as resolved.
}
}
CATCH_LOG()
}
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslcsession/WSLCVirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ class WSLCVirtualMachine

std::string m_rootVhdType;

// Optional callback invoked after a crash dump is successfully written.
wil::com_ptr<ICrashDumpCallback> m_crashDumpCallback;

std::thread m_processExitThread;
std::thread m_crashDumpThread;

Expand Down
Loading