Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
22 changes: 19 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,15 @@ void WSLCVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket)

transaction.SendResultMessage<std::int32_t>(0);
relay::InterruptableRelay(reinterpret_cast<HANDLE>(channel.Socket()), file.get(), nullptr);

file.reset();

// 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
Loading