reactos/sdk/lib/drivers/wdf/shared/support/um/fxtelemetryum.cpp

427 lines
13 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxTelemetryUm.cpp
Abstract:
This module implements a telemetry methods.
Author:
Environment:
User mode only
Revision History:
Notes:
--*/
#include "fxsupportpch.hpp"
#include "DriverFrameworks-UserMode-UmEvents.h"
#include "FxldrUm.h"
#include <winmeta.h>
#include <TraceLoggingProvider.h>
#include <telemetry\MicrosoftTelemetry.h>
#include <rpc.h>
#include <rpcndr.h>
extern "C" {
#if defined(EVENT_TRACING)
#include "FxTelemetryUm.tmh"
#endif
}
/* 8ad60765-a021-4494-8594-9346970cf50f */
TRACELOGGING_DEFINE_PROVIDER(g_TelemetryProvider,
UMDF_FX_TRACE_LOGGING_PROVIDER_NAME,
(0x8ad60765, 0xa021, 0x4494, 0x85, 0x94, 0x93, 0x46, 0x97, 0x0c, 0xf5, 0x0f),
TraceLoggingOptionMicrosoftTelemetry());
VOID
AllocAndInitializeTelemetryContext(
_In_ PFX_TELEMETRY_CONTEXT* TelemetryContext
)
{
PFX_TELEMETRY_CONTEXT context = NULL;
RPC_STATUS status;
context = (PFX_TELEMETRY_CONTEXT)MxMemory::MxAllocatePoolWithTag(NonPagedPool,
sizeof(FX_TELEMETRY_CONTEXT),
FX_TAG);
if (NULL == context) {
goto exit;
}
status = UuidCreate(&(context->DriverSessionGUID));
if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) {
MxMemory::MxFreePool(context);
context = NULL;
goto exit;
}
context->DoOnceFlagsBitmap = 0;
exit:
*TelemetryContext = context;
}
VOID
RegisterTelemetryProvider(
VOID
)
{
EventRegisterMicrosoft_Windows_DriverFrameworks_UserMode();
TraceLoggingRegister(g_TelemetryProvider);
}
VOID
UnregisterTelemetryProvider(
VOID
)
{
EventUnregisterMicrosoft_Windows_DriverFrameworks_UserMode();
TraceLoggingUnregister(g_TelemetryProvider);
}
VOID
LogDeviceStartTelemetryEvent(
_In_ PFX_DRIVER_GLOBALS Globals,
_In_opt_ FxDevice* Fdo
)
{
// If provider is not enabled we're done.
if (FALSE == FX_TELEMETRY_ENABLED(g_TelemetryProvider, Globals)) {
return;
}
//
// If we already fired an event during PnP start we are done. This avoids
// repeatedly firing events during PnP rebalance.
//
if (InterlockedBitTestAndSet(
&Globals->TelemetryContext->DoOnceFlagsBitmap,
DeviceStartEventBit) == 1) {
return;
}
//
// log the DriverInfo stream.
//
LogDriverInfoStream(Globals, Fdo);
}
VOID
LogDriverInfoStream(
_In_ PFX_DRIVER_GLOBALS DriverGlobals,
_In_ FxDevice* Fdo
)
{
LONG error = ERROR_SUCCESS;
PWCHAR str = NULL;
UFxTelemetryDriverInfo driverInfo = {0};
LPCWSTR hardwareIds = NULL;
LPCWSTR setupClass = NULL;
LPCWSTR busEnum = NULL;
LPCWSTR manufacturer = NULL;
UMDF_DRIVER_REGSITRY_INFO devRegInfo = {0};
PCWSTR groupId = NULL;
IWudfDeviceStack* devStack = NULL;
if (Fdo == NULL) {
//
// Telemetry events are logged during DriverEntry as well to capture non-pnp
// and class extension (which are non-pnp drivers) driver info. Although
// current UMDF datapoint doesn't have a separate flag for non-pnp driver,
// we still want to log the driver name and its properies if available.
//
devStack = DriverGlobals->Driver->GetDriverObject()->WudfDevStack;
if (devStack != NULL) {
devStack->GetPdoProperties(&hardwareIds,
&setupClass,
&busEnum,
&manufacturer);
}
}
else {
devStack = Fdo->GetDeviceStack();
devStack->GetPdoProperties(&hardwareIds,
&setupClass,
&busEnum,
&manufacturer);
Fdo->RetrieveDeviceInfoRegistrySettings(&groupId, &devRegInfo);
}
//
// Log Driver info
//
if (Fdo != NULL) {
GetDriverInfo(Fdo, &devRegInfo, &driverInfo);
}
UMDF_CENSUS_EVT_WRITE_DEVICE_START(g_TelemetryProvider,
DriverGlobals,
driverInfo,
setupClass,
busEnum,
hardwareIds,
manufacturer);
if (groupId != NULL) {
delete [] groupId;
groupId = NULL;
}
}
VOID
GetDriverInfo(
_In_ FxDevice* Fdo,
_In_ PUMDF_DRIVER_REGSITRY_INFO RegInfo,
_Out_ UFxTelemetryDriverInfo* DriverInfo
)
{
FxPkgPnp* pnpPkg;
USHORT devInfo = 0;
WDF_DEVICE_IO_TYPE readWritePreference;
WDF_DEVICE_IO_TYPE ioControlPreference;
UMDF_DRIVER_REGSITRY_INFO devRegInfo = {0};
PWSTR groupId = NULL;
pnpPkg = (FxPkgPnp*)Fdo->GetFdoPkg();
devInfo = Fdo->GetDeviceTelemetryInfoFlags();
Fdo->GetDeviceStackIoType(&readWritePreference, &ioControlPreference);
DriverInfo->bitmap.IsFilter = Fdo->IsFilter();
DriverInfo->bitmap.IsPowerPolicyOwner = pnpPkg->IsPowerPolicyOwner();
DriverInfo->bitmap.IsS0IdleWakeFromS0Enabled = pnpPkg->IsS0IdleWakeFromS0Enabled();
DriverInfo->bitmap.IsS0IdleUsbSSEnabled = pnpPkg->IsS0IdleUsbSSEnabled();
DriverInfo->bitmap.IsS0IdleSystemManaged = pnpPkg->IsS0IdleSystemManaged();
DriverInfo->bitmap.IsSxWakeEnabled = pnpPkg->IsSxWakeEnabled();
DriverInfo->bitmap.IsUsingLevelTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedLevelTriggeredInterrupt);
DriverInfo->bitmap.IsUsingEdgeTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedEdgeTriggeredInterrupt);
DriverInfo->bitmap.IsUsingMsiXOrSingleMsi22Interrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsiXOrSingleMsi22Interrupt);
DriverInfo->bitmap.IsUsingMsi22MultiMessageInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsi22MultiMessageInterrupt);
DriverInfo->bitmap.IsUsingMultipleInterrupt = pnpPkg->HasMultipleInterrupts();
DriverInfo->bitmap.IsDirectHardwareAccessAllowed = Fdo->IsDirectHardwareAccessAllowed();
DriverInfo->bitmap.IsUsingUserModemappingAccessMode = Fdo->AreRegistersMappedToUsermode();
DriverInfo->bitmap.IsKernelModeClientAllowed = RegInfo->IsKernelModeClientAllowed;
DriverInfo->bitmap.IsNullFileObjectAllowed = RegInfo->IsNullFileObjectAllowed;
DriverInfo->bitmap.IsPoolingDisabled = RegInfo->IsHostProcessSharingDisabled;
DriverInfo->bitmap.IsMethodNeitherActionCopy = RegInfo->IsMethodNeitherActionCopy;
DriverInfo->bitmap.IsUsingDirectIoForReadWrite = (readWritePreference == WdfDeviceIoDirect);
DriverInfo->bitmap.IsUsingDirectIoForIoctl = (ioControlPreference == WdfDeviceIoDirect);
DriverInfo->bitmap.IsUsingDriverWppRecorder = Fdo->GetDriver()->IsDriverObjectFlagSet(DriverObjectUmFlagsLoggingEnabled);
return;
}
_Must_inspect_result_
NTSTATUS
GetImageName(
_In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
_Out_ PUNICODE_STRING ImageName
)
/*++
Routine Description:
Retrieve the ImageName value from the named Service registry key.
Caller is responsible for freeing the buffer allocated in ImageName::Buffer.
Arguments:
DriverGlobals - pointer to FX_DRIVER_GLOBALS
ImageeName - Pointer to a UNICODE_STRING which will receive the image name
upon a return value of NT_SUCCESS()
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
FxAutoRegKey hKey;
DECLARE_CONST_UNICODE_STRING(valueName, L"ImagePath");
UNICODE_STRING imagePath = {0};
UNICODE_STRING imageName = {0};
PKEY_VALUE_PARTIAL_INFORMATION value = NULL;
USHORT size;
ULONG length, type;
PVOID dataBuffer;
type = REG_SZ;
length = 0;
ASSERT(ImageName != NULL);
RtlZeroMemory(ImageName, sizeof(UNICODE_STRING));
//
// Open driver's Service base key
//
status = FxRegKey::_OpenKey(NULL,
FxDriverGlobals->Driver->GetRegistryPathUnicodeString(),
&hKey.m_Key,
KEY_READ);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"Unable to open driver's service key, %!STATUS!", status);
return status;
}
//
// Find out how big a buffer we need to allocate if the value is present
//
status = FxRegKey::_QueryValue(FxDriverGlobals,
hKey.m_Key,
&valueName,
length,
NULL,
&length,
&type);
//
// We expect the list to be bigger then a standard partial, so if it is
// not, just bail now.
//
if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) {
return STATUS_UNSUCCESSFUL;
}
//
// Pool can be paged b/c we are running at PASSIVE_LEVEL and we are going
// to free it at the end of this function.
//
dataBuffer = FxPoolAllocate(FxDriverGlobals, PagedPool, length);
if (dataBuffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"Failed to allocate memory for image path string, %!STATUS!",
status);
return status;
}
//
// Requery now that we have a big enough buffer
//
status = FxRegKey::_QueryValue(FxDriverGlobals,
hKey.m_Key,
&valueName,
length,
dataBuffer,
&length,
&type);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"Failed to get Image name from service key, %!STATUS!",
status);
goto cleanUp;
}
//
// Verify that the data from the registry is a valid string.
//
if (type != REG_SZ && type != REG_EXPAND_SZ) {
return STATUS_OBJECT_TYPE_MISMATCH;
}
if (length == 0 || length > USHORT_MAX) {
return STATUS_INVALID_PARAMETER;
}
//
// string must be NULL-terminated
//
PWCHAR str = (PWCHAR) dataBuffer;
if (str[(length/sizeof(WCHAR)) - 1] != UNICODE_NULL) {
status = STATUS_INVALID_PARAMETER;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"ERROR: string not terminated with NULL, %!status!\n",
status);
goto cleanUp;
}
RtlInitUnicodeString(&imagePath, (PCWSTR) dataBuffer);
//
// Now read the "ImagePath" and extract just the driver filename as a new
// unicode string.
//
GetNameFromPath(&imagePath, &imageName);
if (imageName.Length == 0x0) {
status = STATUS_INVALID_PARAMETER;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"ERROR: GetNameFromPath could not find a name, %!status!\n",
status);
goto cleanUp;
}
//
// Check for interger overflow for length before we allocate memory
// size = path->Length + sizeof(UNICODE_NULL);
// len is used below to compute the string size including the NULL, so
// compute len to include the terminating NULL.
//
status = RtlUShortAdd(imageName.Length, sizeof(UNICODE_NULL), &size);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"ERROR: size computation failed, %!status!\n", status);
goto cleanUp;
}
//
// allocate a buffer to hold Unicode string + null char.
//
ImageName->Buffer = (PWCH) FxPoolAllocate(FxDriverGlobals, PagedPool, size);
if (ImageName->Buffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"ERROR: ExAllocatePoolWithTag failed, %!status!\n", status);
goto cleanUp;
}
RtlZeroMemory(ImageName->Buffer, size);
ImageName->Length = 0x0;
ImageName->MaximumLength = size;
HRESULT hr = StringCbCopy(ImageName->Buffer, size, imageName.Buffer);
if (FAILED(hr)) {
status = STATUS_UNSUCCESSFUL;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"ERROR: failed to copy string buffer, HRESULT 0x%x, %!status!\n",
hr, status);
goto cleanUp;
}
//
// The copy cannot fail since we setup the buffer to hold enough space for
// the contents of the ImagePath value.
//
ASSERT(NT_SUCCESS(status));
cleanUp:
if (!NT_SUCCESS(status)) {
if (ImageName->Buffer != NULL) {
FxPoolFree(ImageName->Buffer);
RtlInitUnicodeString(ImageName, NULL);
}
}
if (dataBuffer != NULL) {
FxPoolFree(dataBuffer);
}
return status;
}