summaryrefslogtreecommitdiff
path: root/src/dkp-wakeups.c
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2009-01-30 13:03:31 +0000
committerRichard Hughes <richard@hughsie.com>2009-01-30 13:03:31 +0000
commita239eff3bf0bfe2587ece54f2aff3f5624bb0fde (patch)
treea196766a3f014465fb94ebc199c442f42f358fb9 /src/dkp-wakeups.c
parent609133f43feed72ca6e8d5a3688076bae77aa865 (diff)
trivial: implement org.freedesktop.DeviceKit.Power.Wakeups
Diffstat (limited to 'src/dkp-wakeups.c')
-rw-r--r--src/dkp-wakeups.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/src/dkp-wakeups.c b/src/dkp-wakeups.c
new file mode 100644
index 0000000..7bf9ea8
--- /dev/null
+++ b/src/dkp-wakeups.c
@@ -0,0 +1,646 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "egg-debug.h"
+#include "egg-string.h"
+
+#include "dkp-wakeups.h"
+#include "dkp-daemon.h"
+#include "dkp-marshal.h"
+#include "dkp-wakeups-glue.h"
+#include "dkp-wakeups-obj.h"
+
+static void dkp_wakeups_class_init (DkpWakeupsClass *klass);
+static void dkp_wakeups_init (DkpWakeups *wakeups);
+static void dkp_wakeups_finalize (GObject *object);
+
+#define DKP_WAKEUPS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_TYPE_WAKEUPS, DkpWakeupsPrivate))
+
+#define DKP_WAKEUPS_REQUESTS_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_BOOLEAN, \
+ G_TYPE_UINT, \
+ G_TYPE_UINT, \
+ G_TYPE_STRING, \
+ G_TYPE_STRING, \
+ G_TYPE_INVALID))
+
+#define DKP_WAKEUPS_POLL_INTERVAL_KERNEL 2 /* seconds */
+#define DKP_WAKEUPS_POLL_INTERVAL_USERSPACE 2 /* seconds */
+#define DKP_WAKEUPS_SOURCE_KERNEL "/proc/interrupts"
+#define DKP_WAKEUPS_SOURCE_USERSPACE "/proc/timer_stats"
+
+struct DkpWakeupsPrivate
+{
+ GPtrArray *data;
+ DBusGConnection *connection;
+};
+
+enum {
+ TOTAL_CHANGED,
+ DATA_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (DkpWakeups, dkp_wakeups, G_TYPE_OBJECT)
+
+/**
+ * dkp_wakeups_get_cmdline:
+ **/
+static gchar *
+dkp_wakeups_get_cmdline (guint pid)
+{
+ gboolean ret;
+ gchar *filename = NULL;
+ gchar *cmdline = NULL;
+ GError *error = NULL;
+
+ /* get command line from proc */
+ filename = g_strdup_printf ("/proc/%i/cmdline", pid);
+ ret = g_file_get_contents (filename, &cmdline, NULL, &error);
+ if (!ret) {
+ egg_debug ("failed to get cmdline: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+out:
+ g_free (filename);
+ return cmdline;
+}
+
+/**
+ * dkp_wakeups_data_obj_compare:
+ **/
+static gint
+dkp_wakeups_data_obj_compare (const DkpWakeupsObj **obj1, const DkpWakeupsObj **obj2)
+{
+ if ((*obj1)->value > (*obj2)->value)
+ return -1;
+ if ((*obj1)->value < (*obj2)->value)
+ return 1;
+ return -0;
+}
+
+/**
+ * dkp_wakeups_data_print:
+ **/
+static void
+dkp_wakeups_data_print (DkpWakeups *wakeups)
+{
+ guint i;
+ DkpWakeupsObj *obj;
+
+ /* sort data */
+ g_ptr_array_sort (wakeups->priv->data, (GCompareFunc) dkp_wakeups_data_obj_compare);
+
+ for (i=0; i<wakeups->priv->data->len; i++) {
+ obj = g_ptr_array_index (wakeups->priv->data, i);
+ if (obj->value > 0)
+ dkp_wakeups_obj_print (obj);
+ }
+}
+
+/**
+ * dkp_wakeups_data_get_or_create:
+ **/
+static DkpWakeupsObj *
+dkp_wakeups_data_get_or_create (DkpWakeups *wakeups, guint id)
+{
+ guint i;
+ DkpWakeupsObj *obj;
+
+ for (i=0; i<wakeups->priv->data->len; i++) {
+ obj = g_ptr_array_index (wakeups->priv->data, i);
+ if (obj->id == id)
+ goto out;
+ }
+ obj = dkp_wakeups_obj_new ();
+ obj->id = id;
+ g_ptr_array_add (wakeups->priv->data, obj);
+out:
+ return obj;
+}
+
+/**
+ * dkp_wakeups_data_get_total:
+ **/
+static guint
+dkp_wakeups_data_get_total (DkpWakeups *wakeups)
+{
+ guint i;
+ guint total = 0;
+ DkpWakeupsObj *obj;
+
+ for (i=0; i<wakeups->priv->data->len; i++) {
+ obj = g_ptr_array_index (wakeups->priv->data, i);
+ total += obj->value;
+ }
+ return total;
+}
+
+/**
+ * dkp_wakeups_get_total:
+ *
+ * Gets the current latency
+ **/
+gboolean
+dkp_wakeups_get_total (DkpWakeups *wakeups, guint *value, GError **error)
+{
+ guint total;
+
+ /* no data */
+ total = dkp_wakeups_data_get_total (wakeups);
+ if (total == 0) {
+ *error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "no interrupt data");
+ return FALSE;
+ }
+
+ /* return total */
+ *value = total;
+ return TRUE;
+}
+
+/**
+ * dkp_wakeups_get_data:
+ **/
+gboolean
+dkp_wakeups_get_data (DkpWakeups *wakeups, GPtrArray **data, GError **error)
+{
+ guint i;
+ GPtrArray *array;
+ DkpWakeupsObj *obj;
+
+ /* sort data */
+ g_ptr_array_sort (wakeups->priv->data, (GCompareFunc) dkp_wakeups_data_obj_compare);
+
+ *data = g_ptr_array_new ();
+ array = wakeups->priv->data;
+ for (i=0; i<array->len; i++) {
+ GValue elem = {0};
+
+ obj = g_ptr_array_index (array, i);
+ if (obj->value == 0)
+ continue;
+ g_value_init (&elem, DKP_WAKEUPS_REQUESTS_STRUCT_TYPE);
+ g_value_take_boxed (&elem, dbus_g_type_specialized_construct (DKP_WAKEUPS_REQUESTS_STRUCT_TYPE));
+ dbus_g_type_struct_set (&elem,
+ 0, FALSE,
+ 1, obj->value,
+ 2, obj->id,
+ 3, obj->cmdline,
+ 4, obj->details,
+ G_MAXUINT);
+ g_ptr_array_add (*data, g_value_get_boxed (&elem));
+ }
+
+// dbus_g_method_return (context, data);
+// g_ptr_array_foreach (*data, (GFunc) g_value_array_free, NULL);
+// g_ptr_array_free (*data, TRUE);
+
+ return TRUE;
+}
+
+/**
+ * dkp_is_in:
+ **/
+static gboolean
+dkp_is_in (gchar needle, const gchar *delimiters)
+{
+ guint i;
+ for (i=0; delimiters[i] != '\0'; i++) {
+ if (delimiters[i] == needle)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * dkp_strsplit_complete_set:
+ **/
+static GPtrArray *
+dkp_strsplit_complete_set (const gchar *string, const gchar *delimiters, guint max_tokens)
+{
+ guint i;
+ gboolean ret;
+ const gchar *start = NULL;
+ gchar temp_data[100];
+ guint len;
+ guint tokens = 0;
+ GPtrArray *array;
+
+ /* find sections not delimited by space */
+ array = g_ptr_array_new ();
+ for (i=0; string[i] != '\0'; i++) {
+ ret = dkp_is_in (string[i], delimiters);
+ if (ret) {
+ /* no character data yet */
+ if (start == NULL)
+ continue;
+ if (tokens == max_tokens - 1) {
+ g_ptr_array_add (array, g_strdup (start));
+ break;
+ }
+
+ /* find length of string */
+ len = &string[i] - start;
+ if (len > 100)
+ len = 100;
+ strncpy (temp_data, start, len);
+ temp_data[len] = '\0';
+
+ /* add to array */
+ g_ptr_array_add (array, g_strdup (temp_data));
+ tokens++;
+ start = NULL;
+ continue;
+ }
+ /* we've got character data */
+ if (start == NULL)
+ start = &string[i];
+ }
+ return array;
+}
+
+/**
+ * dkp_wakeups_poll_kernel_cb:
+ **/
+static gboolean
+dkp_wakeups_poll_kernel_cb (DkpWakeups *wakeups)
+{
+ guint i;
+ guint j;
+ gboolean ret;
+ gboolean special_ipi;
+ gchar *data = NULL;
+ gchar **lines = NULL;
+ GError *error = NULL;
+ guint cpus = 0;
+ const gchar *found;
+ guint irq;
+ guint interrupts;
+ guint total;
+ GPtrArray *sections;
+ DkpWakeupsObj *obj;
+
+ egg_warning ("pOLL kernel");
+
+ /* set all kernel data objs to zero */
+ for (i=0; i<wakeups->priv->data->len; i++) {
+ obj = g_ptr_array_index (wakeups->priv->data, i);
+ if (!obj->is_userspace)
+ obj->value = 0;
+ }
+
+ /* get the data */
+ ret = g_file_get_contents (DKP_WAKEUPS_SOURCE_KERNEL, &data, NULL, &error);
+ if (!ret) {
+ egg_warning ("failed to get data: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* split */
+ lines = g_strsplit (data, "\n", 0);
+
+ /* find out how many processors we have */
+ sections = dkp_strsplit_complete_set (lines[0], " ", 0);
+ cpus = sections->len;
+ g_ptr_array_foreach (sections, (GFunc) g_free, NULL);
+ g_ptr_array_free (sections, TRUE);
+
+ /* get the data from " 9: 29730 365 IO-APIC-fasteoi acpi" */
+ for (i=1; lines[i] != NULL; i++) {
+
+ /* get sections and check correct length */
+ sections = dkp_strsplit_complete_set (lines[i], " :", 2 + cpus);
+ if (sections->len != 2 + cpus)
+ goto skip;
+
+ /* get irq */
+ special_ipi = TRUE;
+ found = g_ptr_array_index (sections, 0);
+ if (strcmp (found, "NMI") == 0)
+ irq = 0xff0;
+ else if (strcmp (found, "LOC") == 0)
+ irq = 0xff1;
+ else if (strcmp (found, "RES") == 0)
+ irq = 0xff2;
+ else if (strcmp (found, "CAL") == 0)
+ irq = 0xff3;
+ else if (strcmp (found, "TLB") == 0)
+ irq = 0xff4;
+ else if (strcmp (found, "TRM") == 0)
+ irq = 0xff5;
+ else if (strcmp (found, "SPU") == 0)
+ irq = 0xff6;
+ else if (strcmp (found, "ERR") == 0)
+ irq = 0xff7;
+ else if (strcmp (found, "MIS") == 0)
+ irq = 0xff8;
+ else {
+ irq = atoi (found);
+ special_ipi = FALSE;
+ }
+
+ /* get the number of interrupts over all processors */
+ interrupts = 0;
+ for (j=1; j<cpus; j++) {
+ found = g_ptr_array_index (sections, j);
+ interrupts += atoi (found);
+ }
+ if (interrupts == 0)
+ goto skip;
+
+ /* get the detail string */
+ found = g_ptr_array_index (sections, cpus+1);
+
+ /* save in database */
+ obj = dkp_wakeups_data_get_or_create (wakeups, irq);
+ if (obj->details == NULL) {
+ if (strcmp (found, "i8042") == 0)
+ obj->details = g_strdup_printf ("<interrupt> : %s", "PS/2 keyboard/mouse/touchpad");
+ else if (special_ipi)
+ obj->details = g_strdup_printf ("<kernel IPI> : %s", found);
+ else
+ obj->details = g_strdup_printf ("<interrupt> : %s", found);
+ obj->cmdline = g_strdup ("<kernel>");
+ obj->is_userspace = FALSE;
+ }
+ /* we report this in minutes, not seconds */
+ if (obj->old > 0)
+ obj->value = (interrupts - obj->old) * 60 / DKP_WAKEUPS_POLL_INTERVAL_KERNEL;
+ obj->old = interrupts;
+skip:
+ g_ptr_array_foreach (sections, (GFunc) g_free, NULL);
+ g_ptr_array_free (sections, TRUE);
+ }
+
+ dkp_wakeups_data_print (wakeups);
+
+ /* tell GUI we've changed */
+ total = dkp_wakeups_data_get_total (wakeups);
+ g_signal_emit (wakeups, signals [TOTAL_CHANGED], 0, total);
+out:
+ g_free (data);
+ g_strfreev (lines);
+ return TRUE;
+}
+
+/**
+ * dkp_wakeups_poll_userspace_cb:
+ **/
+static gboolean
+dkp_wakeups_poll_userspace_cb (DkpWakeups *wakeups)
+{
+ guint i;
+ guint total;
+ gboolean ret;
+ GError *error = NULL;
+ gchar *data = NULL;
+ gchar **lines = NULL;
+ const gchar *string;
+ DkpWakeupsObj *obj;
+ GPtrArray *sections;
+ guint pid;
+ guint interrupts;
+ gfloat interval = 5.0f;
+
+ /* set all userspace data objs to zero */
+ for (i=0; i<wakeups->priv->data->len; i++) {
+ obj = g_ptr_array_index (wakeups->priv->data, i);
+ if (obj->is_userspace)
+ obj->value = 0;
+ }
+
+ /* get the data */
+ ret = g_file_get_contents (DKP_WAKEUPS_SOURCE_USERSPACE, &data, NULL, &error);
+ if (!ret) {
+ egg_warning ("failed to get data: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* split */
+ lines = g_strsplit (data, "\n", 0);
+
+ /* get the data from " 9: 29730 365 IO-APIC-fasteoi acpi" */
+ for (i=0; lines[i] != NULL; i++) {
+
+ if (strstr (lines[i], "Timer Stats Version:") != NULL)
+ continue;
+ if (strstr (lines[i], "events/sec") != NULL)
+ continue;
+
+ /* get sections */
+ sections = dkp_strsplit_complete_set (lines[i], " :", 4);
+
+ /* get timeout */
+ if (strstr (lines[i], "Sample period:") != NULL) {
+ string = g_ptr_array_index (sections, 2);
+ interval = atof (string);
+ egg_debug ("interval=%f", interval);
+ goto skip;
+ }
+
+ /* check correct length */
+ if (sections->len != 4)
+ goto skip;
+
+ /* if deferred */
+ string = g_ptr_array_index (sections, 0);
+ if (strstr (string, "D") != NULL)
+ goto skip;
+ interrupts = atoi (string);
+ if (interrupts == 0)
+ goto skip;
+
+ /* get pid */
+ string = g_ptr_array_index (sections, 1);
+ pid = atoi (string);
+
+ /* ignore scheduled */
+ string = g_ptr_array_index (sections, 3);
+ if (g_str_has_prefix (string, "tick_nohz_"))
+ goto skip;
+ if (g_str_has_prefix (string, "tick_setup_sched_timer"))
+ goto skip;
+
+ /* get details */
+
+ /* save in database */
+ obj = dkp_wakeups_data_get_or_create (wakeups, pid);
+ if (obj->details == NULL) {
+ /* get process name (truncated) */
+ string = g_ptr_array_index (sections, 2);
+ if (strcmp (string, "insmod") == 0)
+ obj->cmdline = g_strdup ("<kernel module>");
+ else if (strcmp (string, "modprobe") == 0)
+ obj->cmdline = g_strdup ("<kernel module>");
+ else if (strcmp (string, "swapper") == 0)
+ obj->cmdline = g_strdup ("<kernel core>");
+ else {
+ /* try to get a better command line */
+ obj->cmdline = dkp_wakeups_get_cmdline (pid);
+ if (obj->cmdline == NULL)
+ obj->cmdline = g_strdup (string);
+ }
+ string = g_ptr_array_index (sections, 3);
+ obj->details = g_strdup (string);
+ obj->is_userspace = TRUE;
+ }
+ /* we report this in minutes, not seconds */
+ obj->value = (gfloat) interrupts * 60.0f / interval;
+skip:
+ g_ptr_array_foreach (sections, (GFunc) g_free, NULL);
+ g_ptr_array_free (sections, TRUE);
+
+ }
+
+ /* tell GUI we've changed */
+ total = dkp_wakeups_data_get_total (wakeups);
+ g_signal_emit (wakeups, signals [TOTAL_CHANGED], 0, total);
+
+ dkp_wakeups_data_print (wakeups);
+
+out:
+ g_free (data);
+ g_strfreev (lines);
+ return TRUE;
+}
+
+/**
+ * dkp_wakeups_timerstats_enable:
+ **/
+static gboolean
+dkp_wakeups_timerstats_enable (DkpWakeups *wakeups, gboolean enabled)
+{
+ FILE *file;
+ file = fopen (DKP_WAKEUPS_SOURCE_USERSPACE, "w");
+ if (file == NULL)
+ return FALSE;
+ if (enabled)
+ fprintf (file, "1\n");
+ else
+ fprintf (file, "0\n");
+ fclose (file);
+ return TRUE;
+}
+
+/**
+ * dkp_wakeups_class_init:
+ **/
+static void
+dkp_wakeups_class_init (DkpWakeupsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = dkp_wakeups_finalize;
+
+ signals [TOTAL_CHANGED] =
+ g_signal_new ("total-changed",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DkpWakeupsClass, total_changed),
+ NULL, NULL, g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [DATA_CHANGED] =
+ g_signal_new ("data-changed",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DkpWakeupsClass, data_changed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* introspection */
+ dbus_g_object_type_install_info (DKP_TYPE_WAKEUPS, &dbus_glib_dkp_wakeups_object_info);
+
+ g_type_class_add_private (klass, sizeof (DkpWakeupsPrivate));
+}
+
+/**
+ * dkp_wakeups_init:
+ **/
+static void
+dkp_wakeups_init (DkpWakeups *wakeups)
+{
+ GError *error = NULL;
+
+ wakeups->priv = DKP_WAKEUPS_GET_PRIVATE (wakeups);
+ wakeups->priv->data = g_ptr_array_new ();
+
+ wakeups->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (error != NULL) {
+ egg_warning ("Cannot connect to bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* register on the bus */
+ dbus_g_connection_register_g_object (wakeups->priv->connection, "/org/freedesktop/DeviceKit/Power/Wakeups", G_OBJECT (wakeups));
+
+ /* setup polls */
+ g_timeout_add_seconds (DKP_WAKEUPS_POLL_INTERVAL_KERNEL, (GSourceFunc) dkp_wakeups_poll_kernel_cb, wakeups);
+ g_timeout_add_seconds (DKP_WAKEUPS_POLL_INTERVAL_USERSPACE, (GSourceFunc) dkp_wakeups_poll_userspace_cb, wakeups);
+
+ /* setup timerstats */
+ dkp_wakeups_timerstats_enable (wakeups, TRUE);
+}
+
+/**
+ * dkp_wakeups_finalize:
+ **/
+static void
+dkp_wakeups_finalize (GObject *object)
+{
+ DkpWakeups *wakeups;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (DKP_IS_WAKEUPS (object));
+
+ wakeups = DKP_WAKEUPS (object);
+ wakeups->priv = DKP_WAKEUPS_GET_PRIVATE (wakeups);
+
+ /* stop timerstats */
+ dkp_wakeups_timerstats_enable (wakeups, FALSE);
+
+ g_ptr_array_foreach (wakeups->priv->data, (GFunc) dkp_wakeups_obj_free, NULL);
+ g_ptr_array_free (wakeups->priv->data, TRUE);
+
+ G_OBJECT_CLASS (dkp_wakeups_parent_class)->finalize (object);
+}
+
+/**
+ * dkp_wakeups_new:
+ **/
+DkpWakeups *
+dkp_wakeups_new (void)
+{
+ DkpWakeups *wakeups;
+ wakeups = g_object_new (DKP_TYPE_WAKEUPS, NULL);
+ return DKP_WAKEUPS (wakeups);
+}
+