app: add CPU group to GimpDashboard

The CPU group monitors GIMP's CPU usage, and can measure the amount
of time the CPU has been active, which can be used to get a rough
estimate of the execution time of certain operations.

Be warned: while the CPU group is available on *nix and Windows, it
has only been tested on Linux.
This commit is contained in:
Ell 2018-01-19 09:03:44 -05:00
parent b26a0a12a2
commit 0823c255fa
1 changed files with 386 additions and 19 deletions

View File

@ -26,6 +26,14 @@
#include <gio/gio.h>
#include <gtk/gtk.h>
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
#define HAVE_CPU_GROUP
#elif defined (G_OS_WIN32)
#include <windows.h>
#define HAVE_CPU_GROUP
#endif
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
@ -53,6 +61,9 @@
#define LOW_SWAP_SPACE_WARNING_ON /* swap occupied is above */ 0.90 /* of swap limit */
#define LOW_SWAP_SPACE_WARNING_OFF /* swap occupied is below */ 0.85 /* of swap limit */
#define CPU_ACTIVE_ON /* individual cpu usage is above */ 0.75
#define CPU_ACTIVE_OFF /* individual cpu usage is below */ 0.25
typedef enum
{
@ -75,6 +86,13 @@ typedef enum
VARIABLE_SWAP_BUSY,
#ifdef HAVE_CPU_GROUP
/* cpu */
VARIABLE_CPU_USAGE,
VARIABLE_CPU_ACTIVE,
VARIABLE_CPU_ACTIVE_TIME,
#endif
N_VARIABLES,
@ -86,7 +104,9 @@ typedef enum
VARIABLE_TYPE_BOOLEAN,
VARIABLE_TYPE_SIZE,
VARIABLE_TYPE_SIZE_RATIO,
VARIABLE_TYPE_INT_RATIO
VARIABLE_TYPE_INT_RATIO,
VARIABLE_TYPE_PERCENTAGE,
VARIABLE_TYPE_DURATION
} VariableType;
typedef enum
@ -95,6 +115,9 @@ typedef enum
GROUP_CACHE = FIRST_GROUP,
GROUP_SWAP,
#ifdef HAVE_CPU_GROUP
GROUP_CPU,
#endif
N_GROUPS
} Group;
@ -107,19 +130,20 @@ typedef struct _VariableData VariableData;
typedef struct _FieldData FieldData;
typedef struct _GroupData GroupData;
typedef void (* VariableSampleFunc) (GimpDashboard *dashboard,
Variable variable);
typedef void (* VariableFunc) (GimpDashboard *dashboard,
Variable variable);
struct _VariableInfo
{
const gchar *name;
const gchar *title;
const gchar *description;
VariableType type;
GimpRGB color;
VariableSampleFunc sample_func;
gpointer data;
const gchar *name;
const gchar *title;
const gchar *description;
VariableType type;
GimpRGB color;
VariableFunc sample_func;
VariableFunc reset_func;
gpointer data;
};
struct _FieldInfo
@ -127,6 +151,7 @@ struct _FieldInfo
Variable variable;
gboolean default_active;
gboolean show_in_header;
Variable meter_variable;
gint meter_value;
};
@ -149,7 +174,7 @@ struct _VariableData
union
{
gboolean boolean;
guint64 size;
guint64 size; /* in bytes */
struct
{
guint64 antecedent;
@ -160,6 +185,8 @@ struct _VariableData
gint antecedent;
gint consequent;
} int_ratio;
gdouble percentage; /* from 0 to 1 */
gdouble duration; /* in seconds */
} value;
};
@ -243,6 +270,18 @@ static void gimp_dashboard_sample_gegl_stats (GimpDashboard
Variable variable);
static void gimp_dashboard_sample_swap_limit (GimpDashboard *dashboard,
Variable variable);
#ifdef HAVE_CPU_GROUP
static void gimp_dashboard_sample_cpu_usage (GimpDashboard *dashboard,
Variable variable);
static void gimp_dashboard_sample_cpu_active (GimpDashboard *dashboard,
Variable variable);
static void gimp_dashboard_reset_cpu_active (GimpDashboard *dashboard,
Variable variable);
static void gimp_dashboard_sample_cpu_active_time (GimpDashboard *dashboard,
Variable variable);
static void gimp_dashboard_reset_cpu_active_time (GimpDashboard *dashboard,
Variable variable);
#endif /* HAVE_CPU_GROUP */
static void gimp_dashboard_sample_object (GimpDashboard *dashboard,
GObject *object,
@ -376,7 +415,41 @@ static const VariableInfo variables[] =
.color = {0.8, 0.4, 0.4, 1.0},
.sample_func = gimp_dashboard_sample_gegl_stats,
.data = "swap-busy"
},
#ifdef HAVE_CPU_GROUP
/* cpu variables */
[VARIABLE_CPU_USAGE] =
{ .name = "cpu-usage",
.title = NC_("dashboard-variable", "Usage"),
.description = N_("Total CPU usage"),
.type = VARIABLE_TYPE_PERCENTAGE,
.color = {0.8, 0.7, 0.2, 1.0},
.sample_func = gimp_dashboard_sample_cpu_usage
},
[VARIABLE_CPU_ACTIVE] =
{ .name = "cpu-active",
.title = NC_("dashboard-variable", "Active"),
.description = N_("Whether the CPU is active"),
.type = VARIABLE_TYPE_BOOLEAN,
.color = {0.9, 0.8, 0.3, 1.0},
.sample_func = gimp_dashboard_sample_cpu_active,
.reset_func = gimp_dashboard_reset_cpu_active
},
[VARIABLE_CPU_ACTIVE_TIME] =
{ .name = "cpu-active-time",
.title = NC_("dashboard-variable", "Active"),
.description = N_("Total amount of time the CPU has been active"),
.type = VARIABLE_TYPE_DURATION,
.color = {0.8, 0.7, 0.2, 0.4},
.sample_func = gimp_dashboard_sample_cpu_active_time,
.reset_func = gimp_dashboard_reset_cpu_active_time
}
#endif /* HAVE_CPU_GROUP */
};
static const GroupInfo groups[] =
@ -441,9 +514,39 @@ static const GroupInfo groups[] =
.default_active = TRUE
},
{}
}
},
#ifdef HAVE_CPU_GROUP
/* cpu group */
[GROUP_CPU] =
{ .name = "cpu",
.title = NC_("dashboard-group", "CPU"),
.description = N_("CPU usage"),
.default_expanded = FALSE,
.has_meter = TRUE,
.meter_led = VARIABLE_CPU_ACTIVE,
.fields = (const FieldInfo[])
{
{ .variable = VARIABLE_CPU_USAGE,
.default_active = TRUE,
.show_in_header = TRUE,
.meter_value = 2
},
{ VARIABLE_SEPARATOR },
{ .variable = VARIABLE_CPU_ACTIVE_TIME,
.default_active = FALSE,
.meter_variable = VARIABLE_CPU_ACTIVE,
.meter_value = 1
},
{}
}
}
#endif /* HAVE_CPU_GROUP */
};
@ -685,11 +788,25 @@ gimp_dashboard_init (GimpDashboard *dashboard)
if (field_info->meter_value)
{
const VariableInfo *variable_info = &variables[field_info->variable];
const VariableInfo *variable_info = &variables[field_info->variable];
const VariableInfo *meter_variable_info = variable_info;
if (field_info->meter_variable)
meter_variable_info = &variables[field_info->meter_variable];
gimp_meter_set_value_color (GIMP_METER (meter),
field_info->meter_value - 1,
&variable_info->color);
if (meter_variable_info->type == VARIABLE_TYPE_BOOLEAN)
{
gimp_meter_set_value_show_in_gauge (GIMP_METER (meter),
field_info->meter_value - 1,
FALSE);
gimp_meter_set_value_interpolation (GIMP_METER (meter),
field_info->meter_value - 1,
GIMP_INTERPOLATION_NONE);
}
}
}
}
@ -1084,12 +1201,12 @@ gimp_dashboard_sample (GimpDashboard *dashboard)
if (! g_cond_wait_until (&priv->cond, &priv->mutex, end_time) ||
priv->update_now)
{
gboolean varaibles_changed = FALSE;
gboolean variables_changed = FALSE;
Variable variable;
Group group;
gint field;
/* sample all varaibles */
/* sample all variables */
for (variable = FIRST_VARIABLE; variable < N_VARIABLES; variable++)
{
const VariableInfo *variable_info = &variables[variable];
@ -1098,7 +1215,7 @@ gimp_dashboard_sample (GimpDashboard *dashboard)
variable_info->sample_func (dashboard, variable);
varaibles_changed = varaibles_changed ||
variables_changed = variables_changed ||
memcmp (variable_data, &prev_variable_data,
sizeof (VariableData));
}
@ -1121,9 +1238,13 @@ gimp_dashboard_sample (GimpDashboard *dashboard)
if (field_info->meter_value)
{
if (field_info->meter_variable)
variable = field_info->meter_variable;
else
variable = field_info->variable;
sample[field_info->meter_value - 1] =
gimp_dashboard_variable_to_double (dashboard,
field_info->variable);
gimp_dashboard_variable_to_double (dashboard, variable);
}
}
@ -1132,7 +1253,7 @@ gimp_dashboard_sample (GimpDashboard *dashboard)
g_free (sample);
}
if (varaibles_changed)
if (variables_changed)
{
/* enqueue update source */
if (! priv->update_idle_id &&
@ -1369,6 +1490,189 @@ gimp_dashboard_sample_swap_limit (GimpDashboard *dashboard,
}
}
#ifdef HAVE_CPU_GROUP
#ifdef HAVE_SYS_TIMES_H
static void
gimp_dashboard_sample_cpu_usage (GimpDashboard *dashboard,
Variable variable)
{
GimpDashboardPrivate *priv = dashboard->priv;
VariableData *variable_data = &priv->variables[variable];
static clock_t prev_clock = 0;
static clock_t prev_usage;
clock_t curr_clock;
clock_t curr_usage;
struct tms tms;
curr_clock = times (&tms);
if (curr_clock == (clock_t) -1)
{
prev_clock = 0;
variable_data->available = FALSE;
return;
}
curr_usage = tms.tms_utime + tms.tms_stime;
if (prev_clock)
{
variable_data->available = TRUE;
variable_data->value.percentage = (gdouble) (curr_usage - prev_usage) /
(curr_clock - prev_clock);
variable_data->value.percentage /= g_get_num_processors ();
}
else
{
variable_data->available = FALSE;
}
prev_clock = curr_clock;
prev_usage = curr_usage;
}
#elif defined (G_OS_WIN32)
static void
gimp_dashboard_sample_cpu_usage (GimpDashboard *dashboard,
Variable variable)
{
GimpDashboardPrivate *priv = dashboard->priv;
VariableData *variable_data = &priv->variables[variable];
static guint64 prev_time = 0;
static guint64 prev_usage;
guint64 curr_time;
guint64 curr_usage;
FILETIME system_time;
FILETIME process_creation_time;
FILETIME process_exit_time;
FILETIME process_kernel_time;
FILETIME process_user_time;
if (! GetProcessTimes (GetCurrentProcess (),
&process_creation_time,
&process_exit_time,
&process_system_time,
&process_user_time))
{
prev_time = 0;
variable_data->available = FALSE;
return;
}
GetSystemTimeAsFileTime (&system_time);
curr_time = ((guint64) system_time.dwLowDateTime << 32) |
(guint64) system_time.dwHighDateTime;
curr_usage = ((guint64) process_kernel_time.dwLowDateTime << 32) |
(guint64) process_kernel_time.dwHighDateTime;
curr_usage += ((guint64) process_user_time.dwLowDateTime << 32) |
(guint64) process_user_time.dwHighDateTime;
if (prev_time)
{
variable_data->available = TRUE;
variable_data->value.percentage = (gdouble) (curr_usage - prev_usage) /
(curr_time - prev_time);
variable_data->value.percentage /= g_get_num_processors ();
}
else
{
variable_data->available = FALSE;
}
prev_time = curr_time;
prev_usage = curr_usage;
}
#endif /* G_OS_WIN32 */
static gboolean cpu_active = FALSE;
static void
gimp_dashboard_sample_cpu_active (GimpDashboard *dashboard,
Variable variable)
{
GimpDashboardPrivate *priv = dashboard->priv;
VariableData *variable_data = &priv->variables[variable];
gboolean active = FALSE;
if (priv->variables[VARIABLE_CPU_USAGE].available)
{
if (! cpu_active)
{
active =
priv->variables[VARIABLE_CPU_USAGE].value.percentage *
g_get_num_processors () > CPU_ACTIVE_ON;
}
else
{
active =
priv->variables[VARIABLE_CPU_USAGE].value.percentage *
g_get_num_processors () > CPU_ACTIVE_OFF;
}
variable_data->available = TRUE;
}
else
{
variable_data->available = FALSE;
}
cpu_active = active;
variable_data->value.boolean = active;
}
static void
gimp_dashboard_reset_cpu_active (GimpDashboard *dashboard,
Variable variable)
{
cpu_active = FALSE;
}
static gint64 cpu_active_time_prev_time = 0;
static gint64 cpu_active_time = 0;
static void
gimp_dashboard_sample_cpu_active_time (GimpDashboard *dashboard,
Variable variable)
{
GimpDashboardPrivate *priv = dashboard->priv;
VariableData *variable_data = &priv->variables[variable];
gint64 curr_time;
curr_time = g_get_monotonic_time ();
if (priv->variables[VARIABLE_CPU_ACTIVE].available)
{
gboolean active = priv->variables[VARIABLE_CPU_ACTIVE].value.boolean;
if (active && cpu_active_time_prev_time)
cpu_active_time += curr_time - cpu_active_time_prev_time;
}
cpu_active_time_prev_time = curr_time;
variable_data->available = TRUE;
variable_data->value.duration = cpu_active_time / 1000000.0;
}
static void
gimp_dashboard_reset_cpu_active_time (GimpDashboard *dashboard,
Variable variable)
{
cpu_active_time = 0;
}
#endif /* HAVE_CPU_GROUP */
static void
gimp_dashboard_sample_object (GimpDashboard *dashboard,
GObject *object,
@ -1440,6 +1744,28 @@ gimp_dashboard_sample_object (GimpDashboard *dashboard,
}
}
break;
case VARIABLE_TYPE_PERCENTAGE:
if (g_object_class_find_property (klass, variable_info->data))
{
variable_data->available = TRUE;
g_object_get (object,
variable_info->data, &variable_data->value.percentage,
NULL);
}
break;
case VARIABLE_TYPE_DURATION:
if (g_object_class_find_property (klass, variable_info->data))
{
variable_data->available = TRUE;
g_object_get (object,
variable_info->data, &variable_data->value.duration,
NULL);
}
break;
}
}
@ -1763,6 +2089,12 @@ gimp_dashboard_variable_to_boolean (GimpDashboard *dashboard,
case VARIABLE_TYPE_INT_RATIO:
return variable_data->value.int_ratio.antecedent != 0 &&
variable_data->value.int_ratio.consequent != 0;
case VARIABLE_TYPE_PERCENTAGE:
return variable_data->value.percentage != 0.0;
case VARIABLE_TYPE_DURATION:
return variable_data->value.duration != 0.0;
}
}
@ -1802,6 +2134,12 @@ gimp_dashboard_variable_to_double (GimpDashboard *dashboard,
(gdouble) variable_data->value.int_ratio.consequent;
}
break;
case VARIABLE_TYPE_PERCENTAGE:
return variable_data->value.percentage;
case VARIABLE_TYPE_DURATION:
return variable_data->value.duration;
}
}
@ -1823,6 +2161,7 @@ gimp_dashboard_field_to_string (GimpDashboard *dashboard,
/* Tranlators: "N/A" is an abbreviation for "not available" */
const gchar *str = C_("dashboard-value", "N/A");
gboolean static_str = TRUE;
gboolean show_limit = TRUE;
if (variable_data->available)
{
@ -1887,9 +2226,28 @@ gimp_dashboard_field_to_string (GimpDashboard *dashboard,
}
}
break;
case VARIABLE_TYPE_PERCENTAGE:
str = g_strdup_printf ("%d%%",
SIGNED_ROUND (100.0 * variable_data->value.percentage));
static_str = FALSE;
show_limit = FALSE;
break;
case VARIABLE_TYPE_DURATION:
str = g_strdup_printf ("%02d:%02d:%04.1f",
(gint) floor (variable_data->value.duration / 3600.0),
(gint) floor (fmod (variable_data->value.duration / 60.0, 60.0)),
floor (fmod (variable_data->value.duration, 60.0) * 10.0) / 10.0);
static_str = FALSE;
show_limit = FALSE;
break;
}
if (field_info->meter_value && group_data->limit)
if (show_limit &&
variable_data->available &&
field_info->meter_value &&
group_data->limit)
{
gdouble value;
gchar *tmp;
@ -1961,6 +2319,7 @@ void
gimp_dashboard_reset (GimpDashboard *dashboard)
{
GimpDashboardPrivate *priv;
Variable variable;
Group group;
g_return_if_fail (GIMP_IS_DASHBOARD (dashboard));
@ -1971,6 +2330,14 @@ gimp_dashboard_reset (GimpDashboard *dashboard)
gegl_reset_stats ();
for (variable = FIRST_VARIABLE; variable < N_VARIABLES; variable++)
{
const VariableInfo *variable_info = &variables[variable];
if (variable_info->reset_func)
variable_info->reset_func (dashboard, variable);
}
for (group = FIRST_GROUP; group < N_GROUPS; group++)
{
GroupData *group_data = &priv->groups[group];