[client/idd] input: add transport diagnostics

This commit is contained in:
Geoffrey McRae
2026-08-09 01:55:45 +10:00
parent 5b90384123
commit 487920ada3
9 changed files with 634 additions and 34 deletions

View File

@@ -44,6 +44,20 @@ struct HIDQueuedReport
size_t size;
};
struct HIDStatistics
{
uint64_t direct;
uint64_t queued;
uint64_t relativeCoalesced;
uint64_t absoluteCoalesced;
uint64_t staleAbsoluteCompacted;
uint64_t keyboardDuplicates;
uint64_t consumerDuplicates;
uint64_t overflows;
uint64_t resetDiscarded;
size_t queueHighWater;
};
struct HIDDeviceContext
{
WDFQUEUE reportQueue;
@@ -63,6 +77,7 @@ struct HIDDeviceContext
bool absoluteValid;
uint16_t absoluteX;
uint16_t absoluteY;
HIDStatistics statistics;
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
CInputPipeClient * inputPipe;
};
@@ -199,6 +214,7 @@ static bool CompactStaleMotion(
if (superseded)
{
RemoveQueuedReport(context, i);
++context->statistics.staleAbsoluteCompacted;
return true;
}
}
@@ -236,6 +252,7 @@ static NTSTATUS QueueReport(
{
previous->x = static_cast<int16_t>(x);
previous->y = static_cast<int16_t>(y);
++context->statistics.relativeCoalesced;
return STATUS_SUCCESS;
}
}
@@ -250,19 +267,26 @@ static NTSTATUS QueueReport(
{
CopyMemory(tail.data, data, size);
tail.size = size;
++context->statistics.absoluteCoalesced;
return STATUS_SUCCESS;
}
}
else if (reportId == HID_REPORT_ID_KEYBOARD &&
tail.size == size && memcmp(tail.data, data, size) == 0)
{
++context->statistics.keyboardDuplicates;
return STATUS_SUCCESS;
}
}
}
if (context->reportCount == REPORT_QUEUE_LENGTH)
{
if (!CompactStaleMotion(context, reportId))
{
++context->statistics.overflows;
return STATUS_BUFFER_OVERFLOW;
}
}
const size_t index =
@@ -272,6 +296,9 @@ static NTSTATUS QueueReport(
report->size = size;
report->pureMotion = pureMotion;
++context->reportCount;
++context->statistics.queued;
if (context->reportCount > context->statistics.queueHighWater)
context->statistics.queueHighWater = context->reportCount;
return STATUS_SUCCESS;
}
@@ -454,7 +481,10 @@ NTSTATUS CHIDDevice::SubmitReport(
if (reportId == HID_REPORT_ID_CONSUMER &&
context->consumerUsage ==
static_cast<const HIDConsumerReport *>(report)->usage)
{
++context->statistics.consumerDuplicates;
return STATUS_SUCCESS;
}
bool pureMotion = false;
switch (reportId)
@@ -493,7 +523,11 @@ NTSTATUS CHIDDevice::SubmitReport(
status = QueueReport(context, report, size, pureMotion);
}
else if (NT_SUCCESS(status))
{
status = CopyToRequest(request, report, size);
if (NT_SUCCESS(status))
++context->statistics.direct;
}
if (NT_SUCCESS(status))
{
@@ -553,6 +587,7 @@ NTSTATUS CHIDDevice::ResetReports()
absoluteValid = context->absoluteValid;
absoluteX = context->absoluteX;
absoluteY = context->absoluteY;
context->statistics.resetDiscarded += context->reportCount;
context->reportHead = 0;
context->reportCount = 0;
context->mouseMode = 0;
@@ -601,6 +636,44 @@ NTSTATUS CHIDDevice::ResetReports()
return status;
}
void CHIDDevice::LogStatistics()
{
HIDStatistics statistics = {};
{
CSRWSharedLock deviceLock(&s_deviceLock);
HIDDeviceContext * context = s_device;
if (!context)
return;
CSRWExclusiveLock reportLock(&context->reportLock);
statistics = context->statistics;
context->statistics = {};
context->statistics.queueHighWater = context->reportCount;
}
if (!(statistics.direct || statistics.queued ||
statistics.relativeCoalesced || statistics.absoluteCoalesced ||
statistics.staleAbsoluteCompacted ||
statistics.keyboardDuplicates || statistics.consumerDuplicates ||
statistics.overflows || statistics.resetDiscarded))
return;
DEBUG_TRACE("HID reports: %llu direct, %llu queued, peak %zu; "
"%llu relative and %llu absolute coalesced, %llu stale absolute "
"compacted, %llu keyboard and %llu consumer duplicates, "
"%llu overflows, %llu discarded on reset",
static_cast<unsigned long long>(statistics.direct),
static_cast<unsigned long long>(statistics.queued),
statistics.queueHighWater,
static_cast<unsigned long long>(statistics.relativeCoalesced),
static_cast<unsigned long long>(statistics.absoluteCoalesced),
static_cast<unsigned long long>(statistics.staleAbsoluteCompacted),
static_cast<unsigned long long>(statistics.keyboardDuplicates),
static_cast<unsigned long long>(statistics.consumerDuplicates),
static_cast<unsigned long long>(statistics.overflows),
static_cast<unsigned long long>(statistics.resetDiscarded));
}
VOID HIDEvtIoDeviceControl(
_In_ WDFQUEUE queue,
_In_ WDFREQUEST request,

View File

@@ -31,4 +31,5 @@ public:
_In_reads_bytes_(size) const void * report,
_In_ size_t size);
static NTSTATUS ResetReports();
static void LogStatistics();
};

View File

@@ -37,7 +37,13 @@ static constexpr uint16_t HID_CONSUMER_USAGE_VOLUME_DOWN = 0xea;
bool CInputPipeClient::Start()
{
m_lastSequence = 0;
m_endpoint.Stop();
m_lastSequence = 0;
m_statReceived = 0;
m_statMalformed = 0;
m_statSequenceResets = 0;
m_statSubmitFailed = 0;
m_lastStatistics = GetTickCount64();
m_endpoint.SetHandler(this);
return m_endpoint.Start(
LG_INPUT_PIPE_NAME,
@@ -51,7 +57,10 @@ void CInputPipeClient::Stop()
m_endpoint.Stop();
m_lastSequence = 0;
if (wasRunning)
{
LogStatistics(true);
CHIDDevice::ResetReports();
}
}
void CInputPipeClient::OnPipeConnected()
@@ -63,6 +72,7 @@ void CInputPipeClient::OnPipeConnected()
void CInputPipeClient::OnPipeDisconnected()
{
m_lastSequence = 0;
LogStatistics(true);
CHIDDevice::ResetReports();
DEBUG_INFO("Disconnected from the LGIdd input transport; reconnecting");
}
@@ -72,7 +82,11 @@ bool CInputPipeClient::OnPipeMessage(
size_t size)
{
if (size != sizeof(LGInputPipeMessage))
{
++m_statMalformed;
DEBUG_WARN("Received a malformed LGInput pipe message");
return false;
}
const LGInputPipeMessage & message =
*static_cast<const LGInputPipeMessage *>(frame);
@@ -80,16 +94,22 @@ bool CInputPipeClient::OnPipeMessage(
message.version != LG_INPUT_PIPE_VERSION ||
!message.payloadSize ||
message.payloadSize > sizeof(message.payload))
{
++m_statMalformed;
DEBUG_WARN("Received a malformed LGInput pipe message");
return false;
}
if (!message.sequence ||
(m_lastSequence && message.sequence != m_lastSequence + 1))
{
++m_statSequenceResets;
DEBUG_WARN("LGInput pipe report sequence changed unexpectedly");
CHIDDevice::ResetReports();
return false;
}
bool handled = false;
const uint64_t submitFailures = m_statSubmitFailed;
switch (message.type)
{
case LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE:
@@ -107,13 +127,24 @@ bool CInputPipeClient::OnPipeMessage(
break;
default:
++m_statMalformed;
DEBUG_WARN("Received an unknown LGInput pipe message");
return false;
}
if (!handled)
{
if (m_statSubmitFailed == submitFailures)
{
++m_statMalformed;
DEBUG_WARN("Received a malformed LGInput pipe payload");
}
return false;
}
m_lastSequence = message.sequence;
++m_statReceived;
LogStatistics(false);
return true;
}
@@ -265,19 +296,53 @@ bool CInputPipeClient::SubmitReport(
const NTSTATUS status = CHIDDevice::SubmitReport(report, size);
if (status == STATUS_INVALID_PARAMETER)
{
++m_statSubmitFailed;
return false;
}
if (status == STATUS_BUFFER_OVERFLOW)
{
++m_statSubmitFailed;
DEBUG_WARN("LGInput HID report queue overflowed; resetting input state");
CHIDDevice::ResetReports();
return false;
}
if (!NT_SUCCESS(status))
{
++m_statSubmitFailed;
if (status != STATUS_DEVICE_NOT_READY)
DEBUG_WARN_HR(status, "Failed to submit an LGInput HID report");
return false;
}
return true;
}
void CInputPipeClient::LogStatistics(bool force)
{
const ULONGLONG now = GetTickCount64();
if (!force && now - m_lastStatistics < STATISTICS_INTERVAL_MS)
return;
const uint64_t received = m_statReceived;
const uint64_t malformed = m_statMalformed;
const uint64_t sequenceResets = m_statSequenceResets;
const uint64_t submitFailed = m_statSubmitFailed;
m_statReceived = 0;
m_statMalformed = 0;
m_statSequenceResets = 0;
m_statSubmitFailed = 0;
m_lastStatistics = now;
if (received || malformed || sequenceResets || submitFailed)
{
DEBUG_TRACE("LGInput pipe receive: %llu reports, %llu malformed, "
"%llu sequence resets, %llu HID submit failures",
static_cast<unsigned long long>(received),
static_cast<unsigned long long>(malformed),
static_cast<unsigned long long>(sequenceResets),
static_cast<unsigned long long>(submitFailed));
}
CHIDDevice::LogStatistics();
}

View File

@@ -34,6 +34,8 @@ public:
void Stop();
private:
static constexpr ULONGLONG STATISTICS_INTERVAL_MS = 5000;
void OnPipeConnected() override;
void OnPipeDisconnected() override;
bool OnPipeMessage(const void * message, size_t size) override;
@@ -41,7 +43,13 @@ private:
bool HandleMouseAbsolute(const void * payload, size_t size);
bool HandleKeyboard(const void * payload, size_t size);
bool SubmitReport(const void * report, size_t size);
void LogStatistics(bool force);
CPipeEndpoint m_endpoint;
uint64_t m_lastSequence = 0;
uint64_t m_lastSequence = 0;
uint64_t m_statReceived = 0;
uint64_t m_statMalformed = 0;
uint64_t m_statSequenceResets = 0;
uint64_t m_statSubmitFailed = 0;
ULONGLONG m_lastStatistics = 0;
};