[gimp] app: add CPU group to GimpDashboard



commit 0823c255fa11f2ba7dcbf97242b157590f30fc49
Author: Ell <ell_se yahoo com>
Date:   Fri Jan 19 09:03:44 2018 -0500

    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.

 app/widgets/gimpdashboard.c |  405 +++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 386 insertions(+), 19 deletions(-)
---
diff --git a/app/widgets/gimpdashboard.c b/app/widgets/gimpdashboard.c
index 32479af..3014ac1 100644
--- a/app/widgets/gimpdashboard.c
+++ b/app/widgets/gimpdashboard.c
@@ -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[] =
@@ -443,7 +516,37 @@ static const GroupInfo groups[] =
 
                           {}
                         }
+  },
+
+#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];


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]