mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[client/idd] input: add transport diagnostics
This commit is contained in:
@@ -289,19 +289,31 @@ bool CLGMPInputTransport::IsOwner(
|
||||
bool CLGMPInputTransport::Claim(
|
||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||
{
|
||||
if (message.sequence != 1 || !m_sink)
|
||||
if (message.sequence != 1)
|
||||
{
|
||||
++m_statistics.sequenceErrors;
|
||||
return false;
|
||||
}
|
||||
if (!m_sink)
|
||||
{
|
||||
++m_statistics.deliveryFailures;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t sinkState = m_sink->GetState();
|
||||
if (!(sinkState & 1) || sinkState != m_sinkState ||
|
||||
!m_sink->Reset() || m_sink->GetState() != sinkState)
|
||||
{
|
||||
++m_statistics.deliveryFailures;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ownerClientID = sourceClientID;
|
||||
m_ownerGeneration = message.generation;
|
||||
m_ownerSequence = message.sequence;
|
||||
RenewLease();
|
||||
m_statusDirty = true;
|
||||
++m_statistics.claims;
|
||||
DEBUG_INFO("Input owner %u generation %u acquired",
|
||||
m_ownerClientID, m_ownerGeneration);
|
||||
return true;
|
||||
@@ -328,6 +340,7 @@ void CLGMPInputTransport::ReleaseOwner(
|
||||
m_ownerSequence = 0;
|
||||
m_ownerDeadline = 0;
|
||||
m_statusDirty = true;
|
||||
++m_statistics.releases;
|
||||
DEBUG_INFO("Input owner %u generation %u released (%s)",
|
||||
clientID, generation, reason);
|
||||
}
|
||||
@@ -420,6 +433,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
if (!message.generation || !message.sequence || message.reserved ||
|
||||
!ValidatePayload(message))
|
||||
{
|
||||
++m_statistics.malformedMessage;
|
||||
if (owner)
|
||||
ReleaseOwner(true, "invalid input message");
|
||||
return false;
|
||||
@@ -430,10 +444,14 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
if (m_ownerClientID)
|
||||
{
|
||||
if (!owner)
|
||||
{
|
||||
++m_statistics.nonOwner;
|
||||
return true;
|
||||
}
|
||||
if (message.sequence == 1 && m_ownerSequence == 1)
|
||||
return true;
|
||||
|
||||
++m_statistics.sequenceErrors;
|
||||
ReleaseOwner(true, "sequence discontinuity");
|
||||
return false;
|
||||
}
|
||||
@@ -441,18 +459,23 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
}
|
||||
|
||||
if (!owner)
|
||||
{
|
||||
++m_statistics.nonOwner;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t expectedSequence = m_ownerSequence + 1;
|
||||
if (!expectedSequence)
|
||||
expectedSequence = 1;
|
||||
if (message.sequence != expectedSequence)
|
||||
{
|
||||
++m_statistics.sequenceErrors;
|
||||
ReleaseOwner(true, "sequence discontinuity");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool accepted = false;
|
||||
bool accepted = false;
|
||||
bool inputReport = false;
|
||||
switch (message.type)
|
||||
{
|
||||
case KVMFR_INPUT_MESSAGE_RELEASE:
|
||||
@@ -468,6 +491,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_MOUSE_RELATIVE:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendMouseRelative(
|
||||
message.payload.mouseRelative.deltaX,
|
||||
message.payload.mouseRelative.deltaY,
|
||||
@@ -476,6 +500,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_MOUSE_ABSOLUTE:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendMouseAbsolute(
|
||||
message.payload.mouseAbsolute.x,
|
||||
message.payload.mouseAbsolute.y,
|
||||
@@ -484,6 +509,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_KEYBOARD:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendKeyboard(
|
||||
message.payload.keyboard.modifiers,
|
||||
message.payload.keyboard.keys);
|
||||
@@ -495,6 +521,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
|
||||
if (!accepted)
|
||||
{
|
||||
++m_statistics.deliveryFailures;
|
||||
ReleaseOwner(true, "input delivery failed");
|
||||
return false;
|
||||
}
|
||||
@@ -509,13 +536,16 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
|
||||
m_ownerSequence = message.sequence;
|
||||
RenewLease();
|
||||
if (inputReport)
|
||||
++m_statistics.reports;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::DrainMessages()
|
||||
{
|
||||
bool received = false;
|
||||
for (unsigned count = 0; count < 256; ++count)
|
||||
unsigned count = 0;
|
||||
for (; count < 256; ++count)
|
||||
{
|
||||
uint8_t data[LGMP_MSGS_SIZE] = {};
|
||||
size_t size = 0;
|
||||
@@ -532,9 +562,11 @@ bool CLGMPInputTransport::DrainMessages()
|
||||
}
|
||||
|
||||
received = true;
|
||||
++m_statistics.messages;
|
||||
if (size != sizeof(KVMFRInputMessage))
|
||||
{
|
||||
DEBUG_WARN("Ignoring invalid KVMFR input message size");
|
||||
++m_statistics.malformedSize;
|
||||
if (sourceClientID == m_ownerClientID)
|
||||
ReleaseOwner(true, "invalid input message");
|
||||
}
|
||||
@@ -547,9 +579,50 @@ bool CLGMPInputTransport::DrainMessages()
|
||||
|
||||
lgmpHostAckData(m_queue);
|
||||
}
|
||||
if (count > m_statistics.maxDrain)
|
||||
m_statistics.maxDrain = count;
|
||||
if (count == 256)
|
||||
++m_statistics.drainLimit;
|
||||
return received;
|
||||
}
|
||||
|
||||
void CLGMPInputTransport::LogStatistics(ULONGLONG now)
|
||||
{
|
||||
if (!m_statistics.lastLog)
|
||||
{
|
||||
m_statistics.lastLog = now;
|
||||
return;
|
||||
}
|
||||
if (now - m_statistics.lastLog < LOG_INTERVAL_MS)
|
||||
return;
|
||||
|
||||
const Statistics statistics = m_statistics;
|
||||
m_statistics = {};
|
||||
m_statistics.lastLog = now;
|
||||
|
||||
if (!statistics.messages && !statistics.claims &&
|
||||
!statistics.releases && !statistics.deliveryFailures)
|
||||
return;
|
||||
|
||||
const double elapsed =
|
||||
static_cast<double>(now - statistics.lastLog) / 1000.0;
|
||||
DEBUG_TRACE("LGMP input host: %.1f msg/s, %llu reports, "
|
||||
"drain max %u, %llu limit; %llu bad size, %llu malformed, "
|
||||
"%llu sequence, %llu non-owner, %llu delivery failures; "
|
||||
"%llu claims, %llu releases",
|
||||
statistics.messages / elapsed,
|
||||
static_cast<unsigned long long>(statistics.reports),
|
||||
statistics.maxDrain,
|
||||
static_cast<unsigned long long>(statistics.drainLimit),
|
||||
static_cast<unsigned long long>(statistics.malformedSize),
|
||||
static_cast<unsigned long long>(statistics.malformedMessage),
|
||||
static_cast<unsigned long long>(statistics.sequenceErrors),
|
||||
static_cast<unsigned long long>(statistics.nonOwner),
|
||||
static_cast<unsigned long long>(statistics.deliveryFailures),
|
||||
static_cast<unsigned long long>(statistics.claims),
|
||||
static_cast<unsigned long long>(statistics.releases));
|
||||
}
|
||||
|
||||
DWORD CALLBACK CLGMPInputTransport::ThreadProc(void * context)
|
||||
{
|
||||
static_cast<CLGMPInputTransport *>(context)->Thread();
|
||||
@@ -567,15 +640,20 @@ void CLGMPInputTransport::Thread()
|
||||
GetLastError());
|
||||
|
||||
ULONGLONG activeUntil = 0;
|
||||
m_statistics = {};
|
||||
m_statistics.lastLog = GetTickCount64();
|
||||
const HANDLE waitHandles[] = { m_stopEvent, m_pollTimer };
|
||||
for (;;)
|
||||
{
|
||||
CheckOwner();
|
||||
if (DrainMessages())
|
||||
activeUntil = GetTickCount64() + ACTIVE_POLL_MS;
|
||||
const bool received = DrainMessages();
|
||||
PublishStatus();
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
if (received)
|
||||
activeUntil = now + ACTIVE_POLL_MS;
|
||||
LogStatistics(now);
|
||||
|
||||
const bool active = GetTickCount64() < activeUntil;
|
||||
const bool active = now < activeUntil;
|
||||
if (!ArmPollTimer(m_pollTimer, active))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to arm LGMP input timer");
|
||||
|
||||
@@ -40,6 +40,23 @@ class CLGMPInputTransport final : public IInputTransport
|
||||
private:
|
||||
static constexpr ULONGLONG OWNER_LEASE_MS = 500;
|
||||
static constexpr ULONGLONG ACTIVE_POLL_MS = 50;
|
||||
static constexpr ULONGLONG LOG_INTERVAL_MS = 5000;
|
||||
|
||||
struct Statistics
|
||||
{
|
||||
ULONGLONG lastLog;
|
||||
uint64_t messages;
|
||||
uint64_t reports;
|
||||
uint64_t drainLimit;
|
||||
uint64_t malformedSize;
|
||||
uint64_t malformedMessage;
|
||||
uint64_t sequenceErrors;
|
||||
uint64_t nonOwner;
|
||||
uint64_t deliveryFailures;
|
||||
uint64_t claims;
|
||||
uint64_t releases;
|
||||
unsigned maxDrain;
|
||||
};
|
||||
|
||||
CLGMPHost& m_host;
|
||||
|
||||
@@ -52,19 +69,21 @@ private:
|
||||
HANDLE m_pollTimer = nullptr;
|
||||
HANDLE m_thread = nullptr;
|
||||
|
||||
uint32_t m_ownerClientID = 0;
|
||||
uint32_t m_ownerGeneration = 0;
|
||||
uint32_t m_ownerSequence = 0;
|
||||
ULONGLONG m_ownerDeadline = 0;
|
||||
uint64_t m_sinkState = 0;
|
||||
uint32_t m_endpointGeneration = 0;
|
||||
uint32_t m_statusSerial = 0;
|
||||
bool m_statusDirty = false;
|
||||
uint32_t m_ownerClientID = 0;
|
||||
uint32_t m_ownerGeneration = 0;
|
||||
uint32_t m_ownerSequence = 0;
|
||||
ULONGLONG m_ownerDeadline = 0;
|
||||
uint64_t m_sinkState = 0;
|
||||
uint32_t m_endpointGeneration = 0;
|
||||
uint32_t m_statusSerial = 0;
|
||||
bool m_statusDirty = false;
|
||||
Statistics m_statistics = {};
|
||||
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
void UpdateSinkState(uint64_t state);
|
||||
void PublishStatus();
|
||||
void LogStatistics(ULONGLONG now);
|
||||
bool DrainMessages();
|
||||
bool ProcessMessage(uint32_t sourceClientID,
|
||||
const KVMFRInputMessage& message);
|
||||
|
||||
Reference in New Issue
Block a user