Merge lp:~sforshee/powerd/stats into lp:powerd

Proposed by Seth Forshee
Status: Merged
Approved by: Michael Frey
Approved revision: 100
Merged at revision: 83
Proposed branch: lp:~sforshee/powerd/stats
Merge into: lp:powerd
Diff against target: 994 lines (+778/-22)
9 files modified
CMakeLists.txt (+1/-0)
cli/powerd-cli.c (+200/-1)
data/com.canonical.powerd.xml (+8/-0)
src/display-request.c (+37/-21)
src/power-request.c (+2/-0)
src/powerd-internal.h (+16/-0)
src/powerd-object.c (+4/-0)
src/powerd.cpp (+2/-0)
src/stats.c (+508/-0)
To merge this branch: bzr merge lp:~sforshee/powerd/stats
Reviewer Review Type Date Requested Status
Michael Frey (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+179711@code.launchpad.net

Commit message

Add support for collecting and reporting statistics about requests

Description of the change

Add support for collecting and reporting statistics about requests

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~sforshee/powerd/stats updated
99. By Seth Forshee

stats: Fix accounting and reporting of max_active_time

100. By Seth Forshee

powerd-cli: Reorder flag fields in display stats output

Printing out the "on time" and "on since" times for a given flag
side-by-side groups together fields which are likely to be used
together.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Seth Forshee (sforshee) wrote :
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Frey (mfrey) wrote :

tested. Works well.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-07-23 14:42:07 +0000
3+++ CMakeLists.txt 2013-08-12 14:34:58 +0000
4@@ -46,6 +46,7 @@
5 src/powerd-client.c
6 src/powerd-object.c
7 src/powerd-sensors.cpp
8+ src/stats.c
9 src/util.c
10 src/${GDBUS_NAME}.c
11 )
12
13=== modified file 'cli/powerd-cli.c'
14--- cli/powerd-cli.c 2013-08-02 12:48:24 +0000
15+++ cli/powerd-cli.c 2013-08-12 14:34:58 +0000
16@@ -22,6 +22,7 @@
17 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20+#include <inttypes.h>
21 #include <sys/types.h>
22 #include <glib-object.h>
23 #include <gio/gio.h>
24@@ -57,6 +58,28 @@
25 guint32 flags;
26 };
27
28+struct SysRequestStats {
29+ const char *owner;
30+ const char *name;
31+ unsigned active_count;
32+ guint64 active_time;
33+ guint64 max_active_time;
34+ guint64 active_since;
35+};
36+
37+struct DispRequestStats {
38+ const char *owner;
39+ const char *name;
40+ unsigned active_count;
41+ guint64 active_time;
42+ guint64 max_active_time;
43+ guint64 active_since;
44+ guint64 disp_on_time;
45+ guint64 disp_on_since;
46+ guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS];
47+ guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS];
48+};
49+
50 static GMainLoop *main_loop = NULL;
51 static powerd_cookie_t main_cookie;
52 static gboolean checkForDbusName(const char *dbusname, int count,
53@@ -307,6 +330,171 @@
54 }
55 }
56
57+static GArray *
58+getSysRequestStats(void)
59+{
60+ GVariant *ret, *item;
61+ GVariantIter *iter;
62+ GArray *retarray;
63+ GError *error;
64+
65+ retarray = g_array_new(FALSE, FALSE, sizeof(struct SysRequestStats));
66+
67+ error = NULL;
68+ ret = g_dbus_proxy_call_sync(powerd_proxy,
69+ "getSysRequestStats",
70+ NULL,
71+ G_DBUS_CALL_FLAGS_NONE,
72+ -1,
73+ NULL,
74+ &error);
75+ if (ret == NULL) {
76+ cli_warn("getSysRequestStats failed: %s", error->message);
77+ g_error_free(error);
78+ } else {
79+ g_variant_get(ret, "(a(ssuttt))", &iter);
80+ while ((item = g_variant_iter_next_value (iter))) {
81+ struct SysRequestStats stats;
82+ g_variant_get_child(item, 0, "s", &stats.owner);
83+ g_variant_get_child(item, 1, "s", &stats.name);
84+ g_variant_get_child(item, 2, "u", &stats.active_count);
85+ g_variant_get_child(item, 3, "t", &stats.active_time);
86+ g_variant_get_child(item, 4, "t", &stats.max_active_time);
87+ g_variant_get_child(item, 5, "t", &stats.active_since);
88+ g_array_append_val(retarray, stats);
89+ g_variant_unref(item);
90+ }
91+ g_variant_unref(ret);
92+ }
93+ return retarray;
94+}
95+
96+static void
97+printSysRequestStats(GArray *stats)
98+{
99+ int i;
100+ struct SysRequestStats *stat;
101+ printf("System Request Statistics:\n");
102+ if (stats->len == 0) {
103+ printf(" None\n");
104+ } else {
105+ printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n",
106+ "", "", "Active", "", "Max Active", "");
107+ printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n",
108+ "Owner", "Name", "Count", "Active Time", "Time", "Active Since");
109+ for (i = 0; i < stats->len; i++) {
110+ stat = &g_array_index(stats, struct SysRequestStats, i);
111+ printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f\n",
112+ stat->owner, stat->name, stat->active_count,
113+ (double)stat->active_time / 1000000.0f,
114+ (double)stat->max_active_time / 1000000.0f,
115+ (double)stat->active_since / 1000000.0f);
116+ }
117+ }
118+}
119+
120+static GArray *
121+getDispRequestStats(void)
122+{
123+ GVariant *ret, *item;
124+ GVariantIter *iter;
125+ GArray *retarray;
126+ GError *error;
127+
128+ retarray = g_array_new(FALSE, FALSE, sizeof(struct DispRequestStats));
129+
130+ error = NULL;
131+ ret = g_dbus_proxy_call_sync(powerd_proxy,
132+ "getDispRequestStats",
133+ NULL,
134+ G_DBUS_CALL_FLAGS_NONE,
135+ -1,
136+ NULL,
137+ &error);
138+ if (ret == NULL) {
139+ cli_warn("getDispRequestStats failed: %s", error->message);
140+ g_error_free(error);
141+ } else {
142+ g_variant_get(ret, "(a(ssutttttatat))", &iter);
143+ while ((item = g_variant_iter_next_value (iter))) {
144+ struct DispRequestStats stats;
145+ GVariantIter *array_iter;
146+ guint64 val;
147+ int i;
148+
149+ g_variant_get_child(item, 0, "s", &stats.owner);
150+ g_variant_get_child(item, 1, "s", &stats.name);
151+ g_variant_get_child(item, 2, "u", &stats.active_count);
152+ g_variant_get_child(item, 3, "t", &stats.active_time);
153+ g_variant_get_child(item, 4, "t", &stats.max_active_time);
154+ g_variant_get_child(item, 5, "t", &stats.active_since);
155+ g_variant_get_child(item, 6, "t", &stats.disp_on_time);
156+ g_variant_get_child(item, 7, "t", &stats.disp_on_since);
157+
158+ g_variant_get_child(item, 8, "at", &array_iter);
159+ i = 0;
160+ while (g_variant_iter_loop(array_iter, "t", &val)) {
161+ if (i >= POWERD_NUM_DISPLAY_FLAGS)
162+ break;
163+ stats.flag_on_time[i++] = val;
164+ }
165+ g_variant_iter_free(array_iter);
166+
167+ g_variant_get_child(item, 9, "at", &array_iter);
168+ i = 0;
169+ while (g_variant_iter_loop(array_iter, "t", &val)) {
170+ if (i >= POWERD_NUM_DISPLAY_FLAGS)
171+ break;
172+ stats.flag_on_since[i++] = val;
173+ }
174+ g_variant_iter_free(array_iter);
175+
176+ g_array_append_val(retarray, stats);
177+ g_variant_unref(item);
178+ }
179+ g_variant_unref(ret);
180+ }
181+ return retarray;
182+}
183+
184+static void
185+printDispRequestStats(GArray *stats)
186+{
187+ int i, j;
188+ struct DispRequestStats *stat;
189+ printf("Display Request Statistics:\n");
190+ if (stats->len == 0) {
191+ printf(" None\n");
192+ } else {
193+ printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s",
194+ "", "", "Active", "", "Max Active", "", "Display On",
195+ "Display On");
196+ for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++)
197+ printf(" Flag %-2d Flag %-2d ", i, i);
198+ printf("\n %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s",
199+ "Owner", "Name", "Count", "Active Time", "Time", "Active Since",
200+ "Time", "Since");
201+ for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++)
202+ printf(" On Time On Since ");
203+ printf("\n");
204+ for (i = 0; i < stats->len; i++) {
205+ stat = &g_array_index(stats, struct DispRequestStats, i);
206+ printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f %-16.6f %-16.6f",
207+ stat->owner, stat->name, stat->active_count,
208+ (double)stat->active_time / 1000000.0f,
209+ (double)stat->max_active_time / 1000000.0f,
210+ (double)stat->active_since / 1000000.0f,
211+ (double)stat->disp_on_time / 1000000.0f,
212+ (double)stat->disp_on_since / 1000000.0f);
213+ for (j = 0; j < POWERD_NUM_DISPLAY_FLAGS; j++) {
214+ printf(" %-16.6f", (double)stat->flag_on_time[j] / 1000000.0f);
215+ printf(" %-16.6f", (double)stat->flag_on_since[j] / 1000000.0f);
216+ }
217+ printf("\n");
218+ }
219+ }
220+}
221+
222 gboolean
223 clearSysState(powerd_cookie_t cookie)
224 {
225@@ -601,7 +789,7 @@
226 do_test(updateDisplayState(pdr, cookie) == TRUE);
227
228 pdr.state = -1;
229- do_test(updateDisplayState(pdr, cookie) == TRUE);
230+ do_test(updateDisplayState(pdr, cookie) == FALSE);
231
232 do_test(clearDisplayState(cookie) == TRUE);
233
234@@ -730,6 +918,7 @@
235 printf("list - list outstanding requests\n");
236 printf("listen - listen for signals from powerd. This runs a\n"\
237 "\tgmainloop and must be manually killed.\n");
238+ printf("stats - print request statistics\n");
239 printf("test - runs tests\n");
240 }
241
242@@ -756,6 +945,7 @@
243 }
244
245 if ((strcmp(argv[1],"list")) &&
246+ (strcmp(argv[1],"stats")) &&
247 (strcmp(argv[1],"active")) &&
248 (strcmp(argv[1],"active-nc")) &&
249 (strcmp(argv[1],"test")) &&
250@@ -807,6 +997,15 @@
251 printDisplayRequests(requests);
252 g_array_free(requests, TRUE);
253 }
254+ else if (!strcmp(argv[1], "stats")) {
255+ requests = getSysRequestStats();
256+ printSysRequestStats(requests);
257+ g_array_free(requests, TRUE);
258+
259+ requests = getDispRequestStats();
260+ printDispRequestStats(requests);
261+ g_array_free(requests, TRUE);
262+ }
263 else if (!strcmp(argv[1],"active-nc")) {
264 requestSysState("active-nc", POWERD_SYS_STATE_ACTIVE,&cookie);
265 printf("Power State requested, cookie is %s\n", cookie);
266
267=== modified file 'data/com.canonical.powerd.xml'
268--- data/com.canonical.powerd.xml 2013-07-29 16:05:34 +0000
269+++ data/com.canonical.powerd.xml 2013-08-12 14:34:58 +0000
270@@ -52,6 +52,14 @@
271 <arg type="a(ssiu)" name="requestList" direction="out" />
272 </method>
273
274+ <method name="getSysRequestStats">
275+ <arg type="a(ssuttt)" name="requestStats" direction="out" />
276+ </method>
277+
278+ <method name="getDispRequestStats">
279+ <arg type="a(ssutttttatat)" name="requestStats" direction="out" />
280+ </method>
281+
282 <!-- Signals -->
283 <signal name="SysPowerStateChange">
284 <arg type="i" name="sysState" direction="out" />
285
286=== modified file 'src/display-request.c'
287--- src/display-request.c 2013-07-29 16:05:34 +0000
288+++ src/display-request.c 2013-08-12 14:34:58 +0000
289@@ -116,6 +116,8 @@
290 powerd_dbus_name_watch_add(request->owner);
291 __add_request(&request->req);
292 update_internal_state();
293+ powerd_account_add_display_req(request->owner, request->name,
294+ &request->req);
295 }
296
297 static gboolean update_request(struct powerd_display_request *new_req)
298@@ -135,6 +137,7 @@
299 ireq->req.flags = new_req->flags;
300
301 update_internal_state();
302+ powerd_account_update_display_req(ireq->owner, ireq->name, &ireq->req);
303 return TRUE;
304 }
305
306@@ -154,11 +157,12 @@
307 ireq = g_hash_table_lookup(display_request_hash, cookie);
308 if (ireq) {
309 req = ireq->req;
310- /* We need to remove it from our watch hash before we remove it
311- * from the state hash or the ireq->owner memory will be freed
312- * before we try to use it.
313+ /* We need to remove it from our watch hash and do accounting
314+ * before we remove it from the state hash or the ireq->owner
315+ * memory will be freed before we try to use it.
316 */
317 powerd_dbus_name_watch_remove(ireq->owner);
318+ powerd_account_clear_display_req(ireq->owner, ireq->name);
319 found = g_hash_table_remove(display_request_hash, cookie);
320 if (!found)
321 powerd_warn("Display request found on lookup but not on remove");
322@@ -172,25 +176,11 @@
323 return found;
324 }
325
326-static int add_request_worker(gpointer data)
327-{
328- struct display_request_internal *req = data;
329- add_request(req);
330- return 0;
331-}
332-
333-/*
334- * @request need not refer to persistent memory, as the data from
335- * the struct will be copied into internally-managed storage.
336- */
337-static int __powerd_add_display_request(struct powerd_display_request *request,
338- const char *name, const char *owner)
339-{
340- struct display_request_internal *hash_req;
341-
342+static gboolean request_valid(struct powerd_display_request *request)
343+{
344 if ((unsigned)request->state >= POWERD_NUM_DISPLAY_STATES) {
345 powerd_warn("Invalid display state requested: %d", request->state);
346- return -EINVAL;
347+ return FALSE;
348 }
349
350 /*
351@@ -199,8 +189,30 @@
352 */
353 if (request->flags & (-1U << POWERD_NUM_DISPLAY_FLAGS)) {
354 powerd_warn("Invalid display flags requested: 0x%08x", request->flags);
355+ return FALSE;
356+ }
357+
358+ return TRUE;
359+}
360+
361+static int add_request_worker(gpointer data)
362+{
363+ struct display_request_internal *req = data;
364+ add_request(req);
365+ return 0;
366+}
367+
368+/*
369+ * @request need not refer to persistent memory, as the data from
370+ * the struct will be copied into internally-managed storage.
371+ */
372+static int __powerd_add_display_request(struct powerd_display_request *request,
373+ const char *name, const char *owner)
374+{
375+ struct display_request_internal *hash_req;
376+
377+ if (!request_valid(request))
378 return -EINVAL;
379- }
380
381 uuid_generate(request->cookie);
382 hash_req = g_new(struct display_request_internal, 1);
383@@ -235,6 +247,9 @@
384 {
385 gboolean found;
386
387+ if (!request_valid(request))
388+ return -EINVAL;
389+
390 found = powerd_run_mainloop_sync(update_request_worker, request);
391 return found ? 0 : -EINVAL;
392 }
393@@ -434,6 +449,7 @@
394 while (g_hash_table_iter_next(&iter, &key, &value)) {
395 struct display_request_internal *ireq = value;
396 if (!strcmp(owner, ireq->owner)) {
397+ powerd_account_clear_display_req(ireq->owner, ireq->name);
398 __remove_request(&ireq->req);
399 powerd_dbus_name_watch_remove(ireq->owner);
400 g_hash_table_iter_remove(&iter);
401
402=== modified file 'src/power-request.c'
403--- src/power-request.c 2013-08-05 08:47:30 +0000
404+++ src/power-request.c 2013-08-12 14:34:58 +0000
405@@ -210,6 +210,7 @@
406 g_hash_table_insert(state_request_hash, sr->cookie, sr);
407 state_request_count[sr->state - 1] += 1;
408 powerd_dbus_name_watch_add(sr->owner);
409+ powerd_account_request_sys_state(sr->owner, sr->name);
410 return 0;
411 }
412
413@@ -278,6 +279,7 @@
414 {
415 powerd_dbus_name_watch_remove(req->owner);
416 state_request_count[req->state - 1] -= 1;
417+ powerd_account_clear_sys_state(req->owner, req->name);
418 }
419
420 /* Must only be called from main loop */
421
422=== modified file 'src/powerd-internal.h'
423--- src/powerd-internal.h 2013-07-29 16:05:34 +0000
424+++ src/powerd-internal.h 2013-08-12 14:34:58 +0000
425@@ -161,6 +161,22 @@
426 int powerd_client_init(void);
427 void powerd_client_deinit(void);
428
429+/* Statistics functions */
430+void powerd_account_request_sys_state(const char *dbus_name, const char *name);
431+void powerd_account_clear_sys_state(const char *dbus_name, const char *name);
432+void powerd_account_add_display_req(const char *dbus_name, const char *name,
433+ const struct powerd_display_request *req);
434+void powerd_account_update_display_req(const char *dbus_name,
435+ const char *name,
436+ const struct powerd_display_request *req);
437+void powerd_account_clear_display_req(const char *dbus_name, const char *name);
438+int powerd_stats_init(void);
439+void powerd_stats_deinit(void);
440+gboolean handle_get_sys_request_stats(PowerdSource *obj,
441+ GDBusMethodInvocation *invocation);
442+gboolean handle_get_disp_request_stats(PowerdSource *obj,
443+ GDBusMethodInvocation *invocation);
444+
445 /* Utility functions */
446 int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data);
447 guint powerd_uuid_hash(gconstpointer key);
448
449=== modified file 'src/powerd-object.c'
450--- src/powerd-object.c 2013-07-24 14:39:08 +0000
451+++ src/powerd-object.c 2013-08-12 14:34:58 +0000
452@@ -177,6 +177,10 @@
453 G_CALLBACK(handle_ack_state_change), powerd_source);
454 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-display-requests",
455 G_CALLBACK(handle_list_display_requests), powerd_source);
456+ g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-sys-request-stats",
457+ G_CALLBACK(handle_get_sys_request_stats), powerd_source);
458+ g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-disp-request-stats",
459+ G_CALLBACK(handle_get_disp_request_stats), powerd_source);
460
461 powerd_dbus_init_complete();
462 }
463
464=== modified file 'src/powerd.cpp'
465--- src/powerd.cpp 2013-07-29 16:05:34 +0000
466+++ src/powerd.cpp 2013-08-12 14:34:58 +0000
467@@ -472,6 +472,7 @@
468 powerd_debug("Auto-dim Timeout is %d seconds\n", dim_timeout);
469
470 libsuspend_init(0);
471+ powerd_stats_init();
472 powerd_client_init();
473 power_request_init();
474 display_request_init();
475@@ -512,5 +513,6 @@
476 display_request_deinit();
477 power_request_deinit();
478 powerd_client_deinit();
479+ powerd_stats_deinit();
480 return g_exit_code;
481 }
482
483=== added file 'src/stats.c'
484--- src/stats.c 1970-01-01 00:00:00 +0000
485+++ src/stats.c 2013-08-12 14:34:58 +0000
486@@ -0,0 +1,508 @@
487+/*
488+ * Copyright 2013 Canonical Ltd.
489+ *
490+ * This file is part of powerd.
491+ *
492+ * powerd is free software; you can redistribute it and/or modify
493+ * it under the terms of the GNU General Public License as published by
494+ * the Free Software Foundation; version 3.
495+ *
496+ * powerd is distributed in the hope that it will be useful,
497+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
498+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
499+ * GNU General Public License for more details.
500+ *
501+ * You should have received a copy of the GNU General Public License
502+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
503+ */
504+
505+#include <stdlib.h>
506+#include <errno.h>
507+#include <string.h>
508+#include <time.h>
509+
510+#include <glib.h>
511+#include <gio/gio.h>
512+
513+#include "powerd-internal.h"
514+#include "powerd-dbus.h"
515+#include "log.h"
516+
517+#define USECS_PER_SEC 1000000
518+#define NSECS_PER_USEC 1000
519+
520+struct client_stats {
521+ const char *dbus_name;
522+ GSList *sys_stats;
523+ GSList *disp_stats;
524+};
525+
526+struct sys_request_stats {
527+ const char *name;
528+ unsigned active_count;
529+ guint64 active_time;
530+ guint64 max_active_time;
531+ guint64 active_since;
532+};
533+
534+struct disp_request_stats {
535+ const char *name;
536+ unsigned active_count;
537+ guint64 active_time;
538+ guint64 max_active_time;
539+ guint64 active_since;
540+ guint64 disp_on_time;
541+ guint64 disp_on_since;
542+ guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS];
543+ guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS];
544+ struct powerd_display_request last_req;
545+};
546+
547+GHashTable *stats_hash;
548+
549+static guint64 get_usecs(void)
550+{
551+ struct timespec ts;
552+
553+ if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
554+ powerd_error("Could not get monotonic time: %s", strerror(errno));
555+ return 0;
556+ }
557+ return (guint64)ts.tv_sec * USECS_PER_SEC +
558+ (ts.tv_nsec + NSECS_PER_USEC / 2) / NSECS_PER_USEC;
559+}
560+
561+static struct client_stats *alloc_client(const char *dbus_name)
562+{
563+ struct client_stats *client = calloc(1, sizeof(*client));
564+ if (!client) {
565+ powerd_error("Error allocating client for statistics");
566+ return NULL;
567+ }
568+
569+ client->dbus_name = strdup(dbus_name);
570+ if (!client->dbus_name) {
571+ powerd_error("Error duplicating client dbus name for statistics");
572+ free(client);
573+ return NULL;
574+ }
575+
576+ return client;
577+}
578+
579+static struct sys_request_stats *alloc_sys_stats(const char *name)
580+{
581+ struct sys_request_stats *stats = calloc(1, sizeof(*stats));
582+ if (!stats) {
583+ powerd_error("Error allocating memory for system request statistics");
584+ return NULL;
585+ }
586+
587+ stats->name = strdup(name);
588+ if (!stats->name) {
589+ powerd_error("Error duplicating request name for statistics");
590+ free(stats);
591+ return NULL;
592+ }
593+
594+ return stats;
595+}
596+
597+static struct disp_request_stats *alloc_disp_stats(const char *name)
598+{
599+ struct disp_request_stats *stats = calloc(1, sizeof(*stats));
600+ if (!stats) {
601+ powerd_error("Error allocating memory for display request statistics");
602+ return NULL;
603+ }
604+
605+ stats->name = strdup(name);
606+ if (!stats->name) {
607+ powerd_error("Error duplicating request name for statistics");
608+ free(stats);
609+ return NULL;
610+ }
611+
612+ return stats;
613+}
614+
615+static gint sys_stats_comp(gconstpointer a, gconstpointer b)
616+{
617+ const struct sys_request_stats *stats = a;
618+ const char *name = b;
619+ return strcmp(stats->name, name);
620+}
621+
622+static gint disp_stats_comp(gconstpointer a, gconstpointer b)
623+{
624+ const struct disp_request_stats *stats = a;
625+ const char *name = b;
626+ return strcmp(stats->name, name);
627+}
628+
629+static struct client_stats *lookup_client(const char *dbus_name,
630+ gboolean alloc)
631+{
632+ struct client_stats *client;
633+
634+ if (!stats_hash)
635+ return NULL;
636+
637+ client = g_hash_table_lookup(stats_hash, dbus_name);
638+ if (!client && alloc) {
639+ client = alloc_client(dbus_name);
640+ if (client)
641+ g_hash_table_insert(stats_hash, (gpointer)client->dbus_name,
642+ client);
643+ }
644+ return client;
645+}
646+
647+void powerd_account_request_sys_state(const char *dbus_name, const char *name)
648+{
649+ struct client_stats *client;
650+ struct sys_request_stats *stats;
651+ GSList *item;
652+
653+ client = lookup_client(dbus_name, TRUE);
654+ if (!client)
655+ return;
656+
657+ item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
658+ if (item) {
659+ stats = item->data;
660+ } else {
661+ stats = alloc_sys_stats(name);
662+ if (!stats)
663+ return;
664+ client->sys_stats = g_slist_prepend(client->sys_stats, stats);
665+ }
666+
667+ stats->active_count++;
668+ stats->active_since = get_usecs();
669+}
670+
671+void powerd_account_clear_sys_state(const char *dbus_name, const char *name)
672+{
673+ struct client_stats *client;
674+ struct sys_request_stats *stats;
675+ GSList *item;
676+ guint64 duration;
677+
678+ if (!stats_hash)
679+ return;
680+
681+ client = lookup_client(dbus_name, FALSE);
682+ if (!client)
683+ return;
684+ item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
685+ if (!item)
686+ return;
687+ stats = item->data;
688+
689+ duration = get_usecs() - stats->active_since;
690+ stats->active_since = 0;
691+ stats->active_time += duration;
692+ if (duration > stats->max_active_time)
693+ stats->max_active_time = duration;
694+}
695+
696+static void aggregate_disp_stats(struct disp_request_stats *stats,
697+ const struct powerd_display_request *new)
698+{
699+ guint64 us;
700+ int i;
701+
702+ us = get_usecs();
703+ if (new->state != stats->last_req.state) {
704+ if (new->state == POWERD_DISPLAY_STATE_ON) {
705+ stats->disp_on_since = us;
706+ } else {
707+ stats->disp_on_time += us - stats->disp_on_since;
708+ stats->disp_on_since = 0;
709+ }
710+ }
711+ for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
712+ guint32 mask = 1 << i;
713+ if ((new->flags & mask) != (stats->last_req.flags & mask)) {
714+ if (new->flags & mask) {
715+ stats->flag_on_since[i] = us;
716+ } else {
717+ stats->flag_on_time[i] += us - stats->flag_on_since[i];
718+ stats->flag_on_since[i] = 0;
719+ }
720+ }
721+ }
722+}
723+
724+void powerd_account_add_display_req(const char *dbus_name, const char *name,
725+ const struct powerd_display_request *req)
726+{
727+ struct client_stats *client;
728+ struct disp_request_stats *stats;
729+ GSList *item;
730+ guint64 us;
731+ int i;
732+
733+ if (!stats_hash)
734+ return;
735+
736+ client = lookup_client(dbus_name, TRUE);
737+ if (!client)
738+ return;
739+
740+ item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
741+ if (item) {
742+ stats = item->data;
743+ } else {
744+ stats = alloc_disp_stats(name);
745+ if (!stats)
746+ return;
747+ client->disp_stats = g_slist_prepend(client->disp_stats, stats);
748+ }
749+
750+ us = get_usecs();
751+ stats->active_count++;
752+ stats->active_since = us;
753+ if (req->state == POWERD_DISPLAY_STATE_ON)
754+ stats->disp_on_since = us;
755+ for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
756+ if (req->flags & (1 << i))
757+ stats->flag_on_since[i] = us;
758+ }
759+ stats->last_req = *req;
760+}
761+
762+void powerd_account_update_display_req(const char *dbus_name,
763+ const char *name,
764+ const struct powerd_display_request *req)
765+{
766+ struct client_stats *client;
767+ struct disp_request_stats *stats;
768+ GSList *item;
769+
770+ client = lookup_client(dbus_name, FALSE);
771+ if (!client)
772+ return;
773+
774+ item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
775+ if (!item)
776+ return;
777+ stats = item->data;
778+
779+ aggregate_disp_stats(stats, req);
780+ stats->last_req = *req;
781+}
782+
783+void powerd_account_clear_display_req(const char *dbus_name, const char *name)
784+{
785+ struct client_stats *client;
786+ struct disp_request_stats *stats;
787+ GSList *item;
788+ struct powerd_display_request req;
789+ guint64 duration;
790+
791+ client = lookup_client(dbus_name, FALSE);
792+ if (!client)
793+ return;
794+
795+ item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
796+ if (!item)
797+ return;
798+ stats = item->data;
799+
800+ memset(&req, 0, sizeof(req));
801+ aggregate_disp_stats(stats, &req);
802+
803+ duration = get_usecs() - stats->active_since;
804+ stats->active_time += duration;
805+ stats->active_since = 0;
806+ if (duration > stats->max_active_time)
807+ stats->max_active_time = duration;
808+
809+ stats->last_req = req;
810+}
811+
812+static void sys_stats_list_destroy(gpointer data)
813+{
814+ struct sys_request_stats *stats = data;
815+ free((void *)stats->name);
816+ free(stats);
817+}
818+
819+static void disp_stats_list_destroy(gpointer data)
820+{
821+ struct disp_request_stats *stats = data;
822+ free((void *)stats->name);
823+ free(stats);
824+}
825+
826+static void stats_hash_destroy(gpointer data)
827+{
828+ struct client_stats *client = data;
829+ g_slist_free_full(client->sys_stats, sys_stats_list_destroy);
830+ g_slist_free_full(client->disp_stats, disp_stats_list_destroy);
831+ free((void *)client->dbus_name);
832+ free(client);
833+}
834+
835+int powerd_stats_init(void)
836+{
837+ stats_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
838+ stats_hash_destroy);
839+ if (!stats_hash) {
840+ powerd_warn("Unable to allocate stats hash table");
841+ return -ENOMEM;
842+ }
843+ return 0;
844+}
845+
846+void powerd_stats_deinit(void)
847+{
848+ if (stats_hash)
849+ g_hash_table_destroy(stats_hash);
850+}
851+
852+/* Dbus interface support */
853+
854+static int build_sys_request_list(GVariantBuilder *builder,
855+ struct client_stats *client)
856+{
857+ const char *dbus_name = client->dbus_name;
858+ GSList *cur;
859+ guint64 us;
860+ int count = 0;
861+
862+ us = get_usecs();
863+ for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) {
864+ struct sys_request_stats *stats = cur->data;
865+ guint64 active_time, max_active_time;
866+
867+ /*
868+ * Aggregate currently held requests into active_time, and
869+ * consider whether this is greater than the current
870+ * max_active_time.
871+ */
872+ active_time = stats->active_time;
873+ max_active_time = stats->max_active_time;
874+ if (stats->active_since != 0) {
875+ guint64 duration = us - stats->active_since;
876+ active_time += duration;
877+ if (duration > max_active_time)
878+ max_active_time = duration;
879+ }
880+ g_variant_builder_add(builder, "(ssuttt)", dbus_name, stats->name,
881+ stats->active_count, active_time,
882+ max_active_time, stats->active_since);
883+ count++;
884+ }
885+ return count;
886+}
887+
888+gboolean handle_get_sys_request_stats(PowerdSource *obj,
889+ GDBusMethodInvocation *invocation)
890+{
891+ GVariantBuilder *builder;
892+ GVariant *list, *tuple;
893+ GHashTableIter iter;
894+ gpointer key, value;
895+ int count = 0;
896+
897+ builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssuttt)"));
898+ if (stats_hash) {
899+ g_hash_table_iter_init(&iter, stats_hash);
900+ while (g_hash_table_iter_next(&iter, &key, &value))
901+ count += build_sys_request_list(builder, value);
902+ }
903+
904+ if (count == 0) {
905+ g_variant_builder_clear(builder);
906+ list = g_variant_new_array(G_VARIANT_TYPE("a(ssuttt)"), NULL, 0);
907+ } else {
908+ list = g_variant_builder_end(builder);
909+ }
910+ tuple = g_variant_new_tuple(&list, 1);
911+ g_dbus_method_invocation_return_value(invocation, tuple);
912+ g_variant_builder_unref(builder);
913+
914+ return TRUE;
915+}
916+
917+static int build_disp_request_list(GVariantBuilder *builder,
918+ struct client_stats *client)
919+{
920+ const char *dbus_name = client->dbus_name;
921+ GSList *cur;
922+ guint64 us;
923+ int count = 0;
924+
925+ us = get_usecs();
926+ for (cur = client->disp_stats; cur; cur = g_slist_next(cur)) {
927+ struct disp_request_stats *stats = cur->data;
928+ guint64 active_time, max_active_time;
929+ guint64 disp_on_time;
930+ GVariantBuilder *fot_builder, *fos_builder;
931+ int i;
932+
933+ active_time = stats->active_time;
934+ max_active_time = stats->max_active_time;
935+ if (stats->active_since != 0) {
936+ guint64 duration = us - stats->active_since;
937+ active_time += duration;
938+ if (duration > max_active_time)
939+ max_active_time = duration;
940+ }
941+
942+ disp_on_time = stats->disp_on_time;
943+ if (stats->disp_on_since != 0)
944+ disp_on_time += us - stats->disp_on_since;
945+
946+ fot_builder = g_variant_builder_new(G_VARIANT_TYPE("at"));
947+ fos_builder = g_variant_builder_new(G_VARIANT_TYPE("at"));
948+ for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
949+ guint64 flag_on_time = stats->flag_on_time[i];
950+ if (stats->flag_on_since[i] != 0)
951+ flag_on_time += us - stats->flag_on_since[i];
952+ g_variant_builder_add(fot_builder, "t", flag_on_time);
953+ g_variant_builder_add(fos_builder, "t", stats->flag_on_since[i]);
954+ }
955+ g_variant_builder_add(builder, "(ssutttttatat)", dbus_name,
956+ stats->name, stats->active_count, active_time,
957+ max_active_time, stats->active_since,
958+ disp_on_time, stats->disp_on_since,
959+ fot_builder, fos_builder);
960+ g_variant_builder_unref(fot_builder);
961+ g_variant_builder_unref(fos_builder);
962+ count++;
963+ }
964+ return count;
965+}
966+
967+gboolean handle_get_disp_request_stats(PowerdSource *obj,
968+ GDBusMethodInvocation *invocation)
969+{
970+ GVariantBuilder *builder;
971+ GVariant *list, *tuple;
972+ GHashTableIter iter;
973+ gpointer key, value;
974+ int count = 0;
975+
976+ builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssutttttatat)"));
977+ if (stats_hash) {
978+ g_hash_table_iter_init(&iter, stats_hash);
979+ while (g_hash_table_iter_next(&iter, &key, &value))
980+ count += build_disp_request_list(builder, value);
981+ }
982+
983+ if (count == 0) {
984+ g_variant_builder_clear(builder);
985+ list = g_variant_new_array(G_VARIANT_TYPE("a(ssutttttatat)"), NULL, 0);
986+ } else {
987+ list = g_variant_builder_end(builder);
988+ }
989+ tuple = g_variant_new_tuple(&list, 1);
990+ g_dbus_method_invocation_return_value(invocation, tuple);
991+ g_variant_builder_unref(builder);
992+
993+ return TRUE;
994+}

Subscribers

People subscribed via source and target branches