mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
291
grd-rdp-session-metrics.c
Normal file
291
grd-rdp-session-metrics.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Pascal Nowack
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "grd-rdp-session-metrics.h"
|
||||
|
||||
#include "grd-rdp-surface.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean pending_frame_reception;
|
||||
gboolean pending_frame_transmission;
|
||||
int64_t first_frame_reception;
|
||||
int64_t first_frame_transmission;
|
||||
uint32_t skipped_frames;
|
||||
} SurfaceMetrics;
|
||||
|
||||
struct _GrdRdpSessionMetrics
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GrdRdpPhase phase;
|
||||
|
||||
int64_t rd_session_init_us;
|
||||
int64_t rd_session_post_connect_us;
|
||||
int64_t rd_session_ready_us;
|
||||
int64_t rd_session_started_us;
|
||||
|
||||
gboolean pending_layout_change;
|
||||
int64_t layout_change_notification;
|
||||
|
||||
GMutex metrics_mutex;
|
||||
GHashTable *surface_metrics_table;
|
||||
uint32_t n_pending_surface_metrics;
|
||||
|
||||
gboolean pending_output;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GrdRdpSessionMetrics, grd_rdp_session_metrics, G_TYPE_OBJECT)
|
||||
|
||||
void
|
||||
grd_rdp_session_metrics_notify_phase_completion (GrdRdpSessionMetrics *session_metrics,
|
||||
GrdRdpPhase phase)
|
||||
{
|
||||
g_assert (session_metrics->pending_output);
|
||||
|
||||
g_assert (phase > session_metrics->phase);
|
||||
session_metrics->phase = phase;
|
||||
|
||||
switch (phase)
|
||||
{
|
||||
case GRD_RDP_PHASE_SESSION_INITIALIZATION:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
case GRD_RDP_PHASE_POST_CONNECT:
|
||||
session_metrics->rd_session_post_connect_us = g_get_monotonic_time ();
|
||||
break;
|
||||
case GRD_RDP_PHASE_SESSION_READY:
|
||||
session_metrics->rd_session_ready_us = g_get_monotonic_time ();
|
||||
break;
|
||||
case GRD_RDP_PHASE_SESSION_STARTED:
|
||||
session_metrics->rd_session_started_us = g_get_monotonic_time ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_session_metrics_notify_frame_reception (GrdRdpSessionMetrics *session_metrics,
|
||||
GrdRdpSurface *rdp_surface)
|
||||
{
|
||||
SurfaceMetrics *surface_metrics = NULL;
|
||||
g_autoptr (GMutexLocker) locker = NULL;
|
||||
|
||||
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
||||
|
||||
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
||||
if (session_metrics->pending_layout_change)
|
||||
return;
|
||||
|
||||
if (!session_metrics->pending_output)
|
||||
return;
|
||||
|
||||
if (!g_hash_table_lookup_extended (session_metrics->surface_metrics_table,
|
||||
rdp_surface,
|
||||
NULL, (gpointer *) &surface_metrics))
|
||||
g_assert_not_reached ();
|
||||
|
||||
if (surface_metrics->pending_frame_reception)
|
||||
{
|
||||
g_assert (surface_metrics->pending_frame_transmission);
|
||||
|
||||
surface_metrics->first_frame_reception = g_get_monotonic_time ();
|
||||
surface_metrics->pending_frame_reception = FALSE;
|
||||
}
|
||||
else if (surface_metrics->pending_frame_transmission)
|
||||
{
|
||||
++surface_metrics->skipped_frames;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_session_metrics (GrdRdpSessionMetrics *session_metrics)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = NULL;
|
||||
SurfaceMetrics *surface_metrics = NULL;
|
||||
GHashTableIter iter;
|
||||
|
||||
g_debug ("[RDP] Remote Desktop session metrics: _post_connect: %lims, "
|
||||
"_ready: %lims, _started: %lims, whole startup: %lims",
|
||||
(session_metrics->rd_session_post_connect_us -
|
||||
session_metrics->rd_session_init_us) / 1000,
|
||||
(session_metrics->rd_session_ready_us -
|
||||
session_metrics->rd_session_post_connect_us) / 1000,
|
||||
(session_metrics->rd_session_started_us -
|
||||
session_metrics->rd_session_ready_us) / 1000,
|
||||
(session_metrics->rd_session_started_us -
|
||||
session_metrics->rd_session_init_us) / 1000);
|
||||
|
||||
g_hash_table_iter_init (&iter, session_metrics->surface_metrics_table);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &rdp_surface,
|
||||
(gpointer *) &surface_metrics))
|
||||
{
|
||||
GrdRdpSurfaceMapping *surface_mapping =
|
||||
grd_rdp_surface_get_mapping (rdp_surface);
|
||||
GrdRdpSurfaceMappingType mapping_type = surface_mapping->mapping_type;
|
||||
|
||||
g_assert (!surface_metrics->pending_frame_reception);
|
||||
g_assert (!surface_metrics->pending_frame_transmission);
|
||||
|
||||
g_assert (mapping_type == GRD_RDP_SURFACE_MAPPING_TYPE_MAP_TO_OUTPUT);
|
||||
|
||||
g_debug ("[RDP] Surface %ux%u (%i, %i) Metrics: First frame: "
|
||||
"Reception: %lims, Transmission: %lims, Skipped frames: %u",
|
||||
grd_rdp_surface_get_width (rdp_surface),
|
||||
grd_rdp_surface_get_height (rdp_surface),
|
||||
surface_mapping->output_origin_x,
|
||||
surface_mapping->output_origin_y,
|
||||
(surface_metrics->first_frame_reception -
|
||||
session_metrics->layout_change_notification) / 1000,
|
||||
(surface_metrics->first_frame_transmission -
|
||||
surface_metrics->first_frame_reception) / 1000,
|
||||
surface_metrics->skipped_frames);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_session_metrics_notify_frame_transmission (GrdRdpSessionMetrics *session_metrics,
|
||||
GrdRdpSurface *rdp_surface)
|
||||
{
|
||||
SurfaceMetrics *surface_metrics = NULL;
|
||||
g_autoptr (GMutexLocker) locker = NULL;
|
||||
|
||||
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
||||
|
||||
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
||||
g_assert (!session_metrics->pending_layout_change);
|
||||
|
||||
if (!session_metrics->pending_output)
|
||||
return;
|
||||
|
||||
if (!g_hash_table_lookup_extended (session_metrics->surface_metrics_table,
|
||||
rdp_surface,
|
||||
NULL, (gpointer *) &surface_metrics))
|
||||
g_assert_not_reached ();
|
||||
|
||||
g_assert (!surface_metrics->pending_frame_reception);
|
||||
|
||||
if (surface_metrics->pending_frame_transmission)
|
||||
{
|
||||
surface_metrics->first_frame_transmission = g_get_monotonic_time ();
|
||||
surface_metrics->pending_frame_transmission = FALSE;
|
||||
|
||||
g_assert (session_metrics->n_pending_surface_metrics > 0);
|
||||
--session_metrics->n_pending_surface_metrics;
|
||||
}
|
||||
|
||||
if (session_metrics->n_pending_surface_metrics == 0)
|
||||
{
|
||||
print_session_metrics (session_metrics);
|
||||
session_metrics->pending_output = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_session_metrics_notify_layout_change (GrdRdpSessionMetrics *session_metrics)
|
||||
{
|
||||
g_autoptr (GMutexLocker) locker = NULL;
|
||||
|
||||
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
||||
|
||||
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
||||
session_metrics->layout_change_notification = g_get_monotonic_time ();
|
||||
session_metrics->pending_layout_change = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_session_metrics_prepare_surface_metrics (GrdRdpSessionMetrics *session_metrics,
|
||||
GList *surfaces)
|
||||
{
|
||||
g_autoptr (GMutexLocker) locker = NULL;
|
||||
GList *l;
|
||||
|
||||
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
||||
|
||||
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
||||
g_hash_table_remove_all (session_metrics->surface_metrics_table);
|
||||
session_metrics->n_pending_surface_metrics = 0;
|
||||
|
||||
for (l = surfaces; l; l = l->next)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = l->data;
|
||||
SurfaceMetrics *surface_metrics;
|
||||
|
||||
surface_metrics = g_new0 (SurfaceMetrics, 1);
|
||||
surface_metrics->pending_frame_reception = TRUE;
|
||||
surface_metrics->pending_frame_transmission = TRUE;
|
||||
|
||||
++session_metrics->n_pending_surface_metrics;
|
||||
g_hash_table_insert (session_metrics->surface_metrics_table,
|
||||
rdp_surface, surface_metrics);
|
||||
}
|
||||
|
||||
session_metrics->pending_layout_change = FALSE;
|
||||
}
|
||||
|
||||
GrdRdpSessionMetrics *
|
||||
grd_rdp_session_metrics_new (void)
|
||||
{
|
||||
return g_object_new (GRD_TYPE_RDP_SESSION_METRICS, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_session_metrics_dispose (GObject *object)
|
||||
{
|
||||
GrdRdpSessionMetrics *session_metrics = GRD_RDP_SESSION_METRICS (object);
|
||||
|
||||
g_clear_pointer (&session_metrics->surface_metrics_table, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_session_metrics_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_session_metrics_finalize (GObject *object)
|
||||
{
|
||||
GrdRdpSessionMetrics *session_metrics = GRD_RDP_SESSION_METRICS (object);
|
||||
|
||||
g_mutex_clear (&session_metrics->metrics_mutex);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_session_metrics_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_session_metrics_init (GrdRdpSessionMetrics *session_metrics)
|
||||
{
|
||||
session_metrics->pending_layout_change = TRUE;
|
||||
session_metrics->pending_output = TRUE;
|
||||
|
||||
session_metrics->phase = GRD_RDP_PHASE_SESSION_INITIALIZATION;
|
||||
session_metrics->rd_session_init_us = g_get_monotonic_time ();
|
||||
|
||||
session_metrics->surface_metrics_table = g_hash_table_new_full (NULL, NULL,
|
||||
NULL, g_free);
|
||||
|
||||
g_mutex_init (&session_metrics->metrics_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_session_metrics_class_init (GrdRdpSessionMetricsClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = grd_rdp_session_metrics_dispose;
|
||||
object_class->finalize = grd_rdp_session_metrics_finalize;
|
||||
}
|
||||
Reference in New Issue
Block a user