From a239eff3bf0bfe2587ece54f2aff3f5624bb0fde Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 30 Jan 2009 13:03:31 +0000 Subject: trivial: implement org.freedesktop.DeviceKit.Power.Wakeups --- src/dkp-wakeups.c | 646 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dkp-wakeups.h | 65 ++++++ 2 files changed, 711 insertions(+) create mode 100644 src/dkp-wakeups.c create mode 100644 src/dkp-wakeups.h 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 + * + * 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 +#include +#include +#include +#include + +#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; ipriv->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; ipriv->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; ipriv->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; ilen; 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; ipriv->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; jdetails == NULL) { + if (strcmp (found, "i8042") == 0) + obj->details = g_strdup_printf (" : %s", "PS/2 keyboard/mouse/touchpad"); + else if (special_ipi) + obj->details = g_strdup_printf (" : %s", found); + else + obj->details = g_strdup_printf (" : %s", found); + obj->cmdline = g_strdup (""); + 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; ipriv->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 (""); + else if (strcmp (string, "modprobe") == 0) + obj->cmdline = g_strdup (""); + else if (strcmp (string, "swapper") == 0) + obj->cmdline = g_strdup (""); + 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); +} + diff --git a/src/dkp-wakeups.h b/src/dkp-wakeups.h new file mode 100644 index 0000000..0f3e890 --- /dev/null +++ b/src/dkp-wakeups.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Richard Hughes + * + * 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. + */ + +#ifndef __DKP_WAKEUPS_H +#define __DKP_WAKEUPS_H + +#include +#include + +G_BEGIN_DECLS + +#define DKP_TYPE_WAKEUPS (dkp_wakeups_get_type ()) +#define DKP_WAKEUPS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DKP_TYPE_WAKEUPS, DkpWakeups)) +#define DKP_WAKEUPS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), DKP_TYPE_WAKEUPS, DkpWakeupsClass)) +#define DKP_IS_WAKEUPS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DKP_TYPE_WAKEUPS)) +#define DKP_IS_WAKEUPS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DKP_TYPE_WAKEUPS)) +#define DKP_WAKEUPS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DKP_TYPE_WAKEUPS, DkpWakeupsClass)) + +typedef struct DkpWakeupsPrivate DkpWakeupsPrivate; + +typedef struct +{ + GObject parent; + DkpWakeupsPrivate *priv; +} DkpWakeups; + +typedef struct +{ + GObjectClass parent_class; + void (* total_changed) (DkpWakeups *wakeups, + guint value); + void (* data_changed) (DkpWakeups *wakeups); +} DkpWakeupsClass; + +DkpWakeups *dkp_wakeups_new (void); +GType dkp_wakeups_get_type (void); +gboolean dkp_wakeups_get_total (DkpWakeups *wakeups, + guint *value, + GError **error); +gboolean dkp_wakeups_get_data (DkpWakeups *wakeups, + GPtrArray **requests, + GError **error); + +G_END_DECLS + +#endif /* __DKP_WAKEUPS_H */ + -- cgit v1.2.3