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
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-07-23 14:42:07 +0000
+++ CMakeLists.txt 2013-08-12 14:34:58 +0000
@@ -46,6 +46,7 @@
46 src/powerd-client.c46 src/powerd-client.c
47 src/powerd-object.c47 src/powerd-object.c
48 src/powerd-sensors.cpp48 src/powerd-sensors.cpp
49 src/stats.c
49 src/util.c50 src/util.c
50 src/${GDBUS_NAME}.c51 src/${GDBUS_NAME}.c
51)52)
5253
=== modified file 'cli/powerd-cli.c'
--- cli/powerd-cli.c 2013-08-02 12:48:24 +0000
+++ cli/powerd-cli.c 2013-08-12 14:34:58 +0000
@@ -22,6 +22,7 @@
22#include <stdio.h>22#include <stdio.h>
23#include <string.h>23#include <string.h>
24#include <unistd.h>24#include <unistd.h>
25#include <inttypes.h>
25#include <sys/types.h>26#include <sys/types.h>
26#include <glib-object.h>27#include <glib-object.h>
27#include <gio/gio.h>28#include <gio/gio.h>
@@ -57,6 +58,28 @@
57 guint32 flags;58 guint32 flags;
58};59};
5960
61struct SysRequestStats {
62 const char *owner;
63 const char *name;
64 unsigned active_count;
65 guint64 active_time;
66 guint64 max_active_time;
67 guint64 active_since;
68};
69
70struct DispRequestStats {
71 const char *owner;
72 const char *name;
73 unsigned active_count;
74 guint64 active_time;
75 guint64 max_active_time;
76 guint64 active_since;
77 guint64 disp_on_time;
78 guint64 disp_on_since;
79 guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS];
80 guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS];
81};
82
60static GMainLoop *main_loop = NULL;83static GMainLoop *main_loop = NULL;
61static powerd_cookie_t main_cookie;84static powerd_cookie_t main_cookie;
62static gboolean checkForDbusName(const char *dbusname, int count,85static gboolean checkForDbusName(const char *dbusname, int count,
@@ -307,6 +330,171 @@
307 }330 }
308}331}
309332
333static GArray *
334getSysRequestStats(void)
335{
336 GVariant *ret, *item;
337 GVariantIter *iter;
338 GArray *retarray;
339 GError *error;
340
341 retarray = g_array_new(FALSE, FALSE, sizeof(struct SysRequestStats));
342
343 error = NULL;
344 ret = g_dbus_proxy_call_sync(powerd_proxy,
345 "getSysRequestStats",
346 NULL,
347 G_DBUS_CALL_FLAGS_NONE,
348 -1,
349 NULL,
350 &error);
351 if (ret == NULL) {
352 cli_warn("getSysRequestStats failed: %s", error->message);
353 g_error_free(error);
354 } else {
355 g_variant_get(ret, "(a(ssuttt))", &iter);
356 while ((item = g_variant_iter_next_value (iter))) {
357 struct SysRequestStats stats;
358 g_variant_get_child(item, 0, "s", &stats.owner);
359 g_variant_get_child(item, 1, "s", &stats.name);
360 g_variant_get_child(item, 2, "u", &stats.active_count);
361 g_variant_get_child(item, 3, "t", &stats.active_time);
362 g_variant_get_child(item, 4, "t", &stats.max_active_time);
363 g_variant_get_child(item, 5, "t", &stats.active_since);
364 g_array_append_val(retarray, stats);
365 g_variant_unref(item);
366 }
367 g_variant_unref(ret);
368 }
369 return retarray;
370}
371
372static void
373printSysRequestStats(GArray *stats)
374{
375 int i;
376 struct SysRequestStats *stat;
377 printf("System Request Statistics:\n");
378 if (stats->len == 0) {
379 printf(" None\n");
380 } else {
381 printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n",
382 "", "", "Active", "", "Max Active", "");
383 printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s\n",
384 "Owner", "Name", "Count", "Active Time", "Time", "Active Since");
385 for (i = 0; i < stats->len; i++) {
386 stat = &g_array_index(stats, struct SysRequestStats, i);
387 printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f\n",
388 stat->owner, stat->name, stat->active_count,
389 (double)stat->active_time / 1000000.0f,
390 (double)stat->max_active_time / 1000000.0f,
391 (double)stat->active_since / 1000000.0f);
392 }
393 }
394}
395
396static GArray *
397getDispRequestStats(void)
398{
399 GVariant *ret, *item;
400 GVariantIter *iter;
401 GArray *retarray;
402 GError *error;
403
404 retarray = g_array_new(FALSE, FALSE, sizeof(struct DispRequestStats));
405
406 error = NULL;
407 ret = g_dbus_proxy_call_sync(powerd_proxy,
408 "getDispRequestStats",
409 NULL,
410 G_DBUS_CALL_FLAGS_NONE,
411 -1,
412 NULL,
413 &error);
414 if (ret == NULL) {
415 cli_warn("getDispRequestStats failed: %s", error->message);
416 g_error_free(error);
417 } else {
418 g_variant_get(ret, "(a(ssutttttatat))", &iter);
419 while ((item = g_variant_iter_next_value (iter))) {
420 struct DispRequestStats stats;
421 GVariantIter *array_iter;
422 guint64 val;
423 int i;
424
425 g_variant_get_child(item, 0, "s", &stats.owner);
426 g_variant_get_child(item, 1, "s", &stats.name);
427 g_variant_get_child(item, 2, "u", &stats.active_count);
428 g_variant_get_child(item, 3, "t", &stats.active_time);
429 g_variant_get_child(item, 4, "t", &stats.max_active_time);
430 g_variant_get_child(item, 5, "t", &stats.active_since);
431 g_variant_get_child(item, 6, "t", &stats.disp_on_time);
432 g_variant_get_child(item, 7, "t", &stats.disp_on_since);
433
434 g_variant_get_child(item, 8, "at", &array_iter);
435 i = 0;
436 while (g_variant_iter_loop(array_iter, "t", &val)) {
437 if (i >= POWERD_NUM_DISPLAY_FLAGS)
438 break;
439 stats.flag_on_time[i++] = val;
440 }
441 g_variant_iter_free(array_iter);
442
443 g_variant_get_child(item, 9, "at", &array_iter);
444 i = 0;
445 while (g_variant_iter_loop(array_iter, "t", &val)) {
446 if (i >= POWERD_NUM_DISPLAY_FLAGS)
447 break;
448 stats.flag_on_since[i++] = val;
449 }
450 g_variant_iter_free(array_iter);
451
452 g_array_append_val(retarray, stats);
453 g_variant_unref(item);
454 }
455 g_variant_unref(ret);
456 }
457 return retarray;
458}
459
460static void
461printDispRequestStats(GArray *stats)
462{
463 int i, j;
464 struct DispRequestStats *stat;
465 printf("Display Request Statistics:\n");
466 if (stats->len == 0) {
467 printf(" None\n");
468 } else {
469 printf(" %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s",
470 "", "", "Active", "", "Max Active", "", "Display On",
471 "Display On");
472 for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++)
473 printf(" Flag %-2d Flag %-2d ", i, i);
474 printf("\n %-16.16s %-20.20s %-8.8s %-16.16s %-16.16s %-16.16s %-16.16s %-16.16s",
475 "Owner", "Name", "Count", "Active Time", "Time", "Active Since",
476 "Time", "Since");
477 for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++)
478 printf(" On Time On Since ");
479 printf("\n");
480 for (i = 0; i < stats->len; i++) {
481 stat = &g_array_index(stats, struct DispRequestStats, i);
482 printf(" %-16.16s %-20.20s %-8u %-16.6f %-16.6f %-16.6f %-16.6f %-16.6f",
483 stat->owner, stat->name, stat->active_count,
484 (double)stat->active_time / 1000000.0f,
485 (double)stat->max_active_time / 1000000.0f,
486 (double)stat->active_since / 1000000.0f,
487 (double)stat->disp_on_time / 1000000.0f,
488 (double)stat->disp_on_since / 1000000.0f);
489 for (j = 0; j < POWERD_NUM_DISPLAY_FLAGS; j++) {
490 printf(" %-16.6f", (double)stat->flag_on_time[j] / 1000000.0f);
491 printf(" %-16.6f", (double)stat->flag_on_since[j] / 1000000.0f);
492 }
493 printf("\n");
494 }
495 }
496}
497
310gboolean498gboolean
311clearSysState(powerd_cookie_t cookie)499clearSysState(powerd_cookie_t cookie)
312{500{
@@ -601,7 +789,7 @@
601 do_test(updateDisplayState(pdr, cookie) == TRUE);789 do_test(updateDisplayState(pdr, cookie) == TRUE);
602790
603 pdr.state = -1;791 pdr.state = -1;
604 do_test(updateDisplayState(pdr, cookie) == TRUE);792 do_test(updateDisplayState(pdr, cookie) == FALSE);
605793
606 do_test(clearDisplayState(cookie) == TRUE);794 do_test(clearDisplayState(cookie) == TRUE);
607795
@@ -730,6 +918,7 @@
730 printf("list - list outstanding requests\n");918 printf("list - list outstanding requests\n");
731 printf("listen - listen for signals from powerd. This runs a\n"\919 printf("listen - listen for signals from powerd. This runs a\n"\
732 "\tgmainloop and must be manually killed.\n");920 "\tgmainloop and must be manually killed.\n");
921 printf("stats - print request statistics\n");
733 printf("test - runs tests\n");922 printf("test - runs tests\n");
734}923}
735924
@@ -756,6 +945,7 @@
756 }945 }
757946
758 if ((strcmp(argv[1],"list")) &&947 if ((strcmp(argv[1],"list")) &&
948 (strcmp(argv[1],"stats")) &&
759 (strcmp(argv[1],"active")) &&949 (strcmp(argv[1],"active")) &&
760 (strcmp(argv[1],"active-nc")) &&950 (strcmp(argv[1],"active-nc")) &&
761 (strcmp(argv[1],"test")) &&951 (strcmp(argv[1],"test")) &&
@@ -807,6 +997,15 @@
807 printDisplayRequests(requests);997 printDisplayRequests(requests);
808 g_array_free(requests, TRUE);998 g_array_free(requests, TRUE);
809 }999 }
1000 else if (!strcmp(argv[1], "stats")) {
1001 requests = getSysRequestStats();
1002 printSysRequestStats(requests);
1003 g_array_free(requests, TRUE);
1004
1005 requests = getDispRequestStats();
1006 printDispRequestStats(requests);
1007 g_array_free(requests, TRUE);
1008 }
810 else if (!strcmp(argv[1],"active-nc")) {1009 else if (!strcmp(argv[1],"active-nc")) {
811 requestSysState("active-nc", POWERD_SYS_STATE_ACTIVE,&cookie);1010 requestSysState("active-nc", POWERD_SYS_STATE_ACTIVE,&cookie);
812 printf("Power State requested, cookie is %s\n", cookie);1011 printf("Power State requested, cookie is %s\n", cookie);
8131012
=== modified file 'data/com.canonical.powerd.xml'
--- data/com.canonical.powerd.xml 2013-07-29 16:05:34 +0000
+++ data/com.canonical.powerd.xml 2013-08-12 14:34:58 +0000
@@ -52,6 +52,14 @@
52 <arg type="a(ssiu)" name="requestList" direction="out" />52 <arg type="a(ssiu)" name="requestList" direction="out" />
53 </method>53 </method>
5454
55 <method name="getSysRequestStats">
56 <arg type="a(ssuttt)" name="requestStats" direction="out" />
57 </method>
58
59 <method name="getDispRequestStats">
60 <arg type="a(ssutttttatat)" name="requestStats" direction="out" />
61 </method>
62
55 <!-- Signals -->63 <!-- Signals -->
56 <signal name="SysPowerStateChange">64 <signal name="SysPowerStateChange">
57 <arg type="i" name="sysState" direction="out" />65 <arg type="i" name="sysState" direction="out" />
5866
=== modified file 'src/display-request.c'
--- src/display-request.c 2013-07-29 16:05:34 +0000
+++ src/display-request.c 2013-08-12 14:34:58 +0000
@@ -116,6 +116,8 @@
116 powerd_dbus_name_watch_add(request->owner);116 powerd_dbus_name_watch_add(request->owner);
117 __add_request(&request->req);117 __add_request(&request->req);
118 update_internal_state();118 update_internal_state();
119 powerd_account_add_display_req(request->owner, request->name,
120 &request->req);
119}121}
120122
121static gboolean update_request(struct powerd_display_request *new_req)123static gboolean update_request(struct powerd_display_request *new_req)
@@ -135,6 +137,7 @@
135 ireq->req.flags = new_req->flags;137 ireq->req.flags = new_req->flags;
136138
137 update_internal_state();139 update_internal_state();
140 powerd_account_update_display_req(ireq->owner, ireq->name, &ireq->req);
138 return TRUE;141 return TRUE;
139}142}
140143
@@ -154,11 +157,12 @@
154 ireq = g_hash_table_lookup(display_request_hash, cookie);157 ireq = g_hash_table_lookup(display_request_hash, cookie);
155 if (ireq) {158 if (ireq) {
156 req = ireq->req;159 req = ireq->req;
157 /* We need to remove it from our watch hash before we remove it160 /* We need to remove it from our watch hash and do accounting
158 * from the state hash or the ireq->owner memory will be freed161 * before we remove it from the state hash or the ireq->owner
159 * before we try to use it.162 * memory will be freed before we try to use it.
160 */163 */
161 powerd_dbus_name_watch_remove(ireq->owner);164 powerd_dbus_name_watch_remove(ireq->owner);
165 powerd_account_clear_display_req(ireq->owner, ireq->name);
162 found = g_hash_table_remove(display_request_hash, cookie);166 found = g_hash_table_remove(display_request_hash, cookie);
163 if (!found)167 if (!found)
164 powerd_warn("Display request found on lookup but not on remove");168 powerd_warn("Display request found on lookup but not on remove");
@@ -172,25 +176,11 @@
172 return found;176 return found;
173}177}
174178
175static int add_request_worker(gpointer data)179static gboolean request_valid(struct powerd_display_request *request)
176{180{
177 struct display_request_internal *req = data;
178 add_request(req);
179 return 0;
180}
181
182/*
183 * @request need not refer to persistent memory, as the data from
184 * the struct will be copied into internally-managed storage.
185 */
186static int __powerd_add_display_request(struct powerd_display_request *request,
187 const char *name, const char *owner)
188{
189 struct display_request_internal *hash_req;
190
191 if ((unsigned)request->state >= POWERD_NUM_DISPLAY_STATES) {181 if ((unsigned)request->state >= POWERD_NUM_DISPLAY_STATES) {
192 powerd_warn("Invalid display state requested: %d", request->state);182 powerd_warn("Invalid display state requested: %d", request->state);
193 return -EINVAL;183 return FALSE;
194 }184 }
195185
196 /*186 /*
@@ -199,8 +189,30 @@
199 */189 */
200 if (request->flags & (-1U << POWERD_NUM_DISPLAY_FLAGS)) {190 if (request->flags & (-1U << POWERD_NUM_DISPLAY_FLAGS)) {
201 powerd_warn("Invalid display flags requested: 0x%08x", request->flags);191 powerd_warn("Invalid display flags requested: 0x%08x", request->flags);
192 return FALSE;
193 }
194
195 return TRUE;
196}
197
198static int add_request_worker(gpointer data)
199{
200 struct display_request_internal *req = data;
201 add_request(req);
202 return 0;
203}
204
205/*
206 * @request need not refer to persistent memory, as the data from
207 * the struct will be copied into internally-managed storage.
208 */
209static int __powerd_add_display_request(struct powerd_display_request *request,
210 const char *name, const char *owner)
211{
212 struct display_request_internal *hash_req;
213
214 if (!request_valid(request))
202 return -EINVAL;215 return -EINVAL;
203 }
204216
205 uuid_generate(request->cookie);217 uuid_generate(request->cookie);
206 hash_req = g_new(struct display_request_internal, 1);218 hash_req = g_new(struct display_request_internal, 1);
@@ -235,6 +247,9 @@
235{247{
236 gboolean found;248 gboolean found;
237249
250 if (!request_valid(request))
251 return -EINVAL;
252
238 found = powerd_run_mainloop_sync(update_request_worker, request);253 found = powerd_run_mainloop_sync(update_request_worker, request);
239 return found ? 0 : -EINVAL;254 return found ? 0 : -EINVAL;
240}255}
@@ -434,6 +449,7 @@
434 while (g_hash_table_iter_next(&iter, &key, &value)) {449 while (g_hash_table_iter_next(&iter, &key, &value)) {
435 struct display_request_internal *ireq = value;450 struct display_request_internal *ireq = value;
436 if (!strcmp(owner, ireq->owner)) {451 if (!strcmp(owner, ireq->owner)) {
452 powerd_account_clear_display_req(ireq->owner, ireq->name);
437 __remove_request(&ireq->req);453 __remove_request(&ireq->req);
438 powerd_dbus_name_watch_remove(ireq->owner);454 powerd_dbus_name_watch_remove(ireq->owner);
439 g_hash_table_iter_remove(&iter);455 g_hash_table_iter_remove(&iter);
440456
=== modified file 'src/power-request.c'
--- src/power-request.c 2013-08-05 08:47:30 +0000
+++ src/power-request.c 2013-08-12 14:34:58 +0000
@@ -210,6 +210,7 @@
210 g_hash_table_insert(state_request_hash, sr->cookie, sr);210 g_hash_table_insert(state_request_hash, sr->cookie, sr);
211 state_request_count[sr->state - 1] += 1;211 state_request_count[sr->state - 1] += 1;
212 powerd_dbus_name_watch_add(sr->owner);212 powerd_dbus_name_watch_add(sr->owner);
213 powerd_account_request_sys_state(sr->owner, sr->name);
213 return 0;214 return 0;
214}215}
215216
@@ -278,6 +279,7 @@
278{279{
279 powerd_dbus_name_watch_remove(req->owner);280 powerd_dbus_name_watch_remove(req->owner);
280 state_request_count[req->state - 1] -= 1;281 state_request_count[req->state - 1] -= 1;
282 powerd_account_clear_sys_state(req->owner, req->name);
281}283}
282284
283/* Must only be called from main loop */285/* Must only be called from main loop */
284286
=== modified file 'src/powerd-internal.h'
--- src/powerd-internal.h 2013-07-29 16:05:34 +0000
+++ src/powerd-internal.h 2013-08-12 14:34:58 +0000
@@ -161,6 +161,22 @@
161int powerd_client_init(void);161int powerd_client_init(void);
162void powerd_client_deinit(void);162void powerd_client_deinit(void);
163163
164/* Statistics functions */
165void powerd_account_request_sys_state(const char *dbus_name, const char *name);
166void powerd_account_clear_sys_state(const char *dbus_name, const char *name);
167void powerd_account_add_display_req(const char *dbus_name, const char *name,
168 const struct powerd_display_request *req);
169void powerd_account_update_display_req(const char *dbus_name,
170 const char *name,
171 const struct powerd_display_request *req);
172void powerd_account_clear_display_req(const char *dbus_name, const char *name);
173int powerd_stats_init(void);
174void powerd_stats_deinit(void);
175gboolean handle_get_sys_request_stats(PowerdSource *obj,
176 GDBusMethodInvocation *invocation);
177gboolean handle_get_disp_request_stats(PowerdSource *obj,
178 GDBusMethodInvocation *invocation);
179
164/* Utility functions */180/* Utility functions */
165int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data);181int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data);
166guint powerd_uuid_hash(gconstpointer key);182guint powerd_uuid_hash(gconstpointer key);
167183
=== modified file 'src/powerd-object.c'
--- src/powerd-object.c 2013-07-24 14:39:08 +0000
+++ src/powerd-object.c 2013-08-12 14:34:58 +0000
@@ -177,6 +177,10 @@
177 G_CALLBACK(handle_ack_state_change), powerd_source);177 G_CALLBACK(handle_ack_state_change), powerd_source);
178 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-display-requests",178 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-display-requests",
179 G_CALLBACK(handle_list_display_requests), powerd_source);179 G_CALLBACK(handle_list_display_requests), powerd_source);
180 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-sys-request-stats",
181 G_CALLBACK(handle_get_sys_request_stats), powerd_source);
182 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-disp-request-stats",
183 G_CALLBACK(handle_get_disp_request_stats), powerd_source);
180184
181 powerd_dbus_init_complete();185 powerd_dbus_init_complete();
182}186}
183187
=== modified file 'src/powerd.cpp'
--- src/powerd.cpp 2013-07-29 16:05:34 +0000
+++ src/powerd.cpp 2013-08-12 14:34:58 +0000
@@ -472,6 +472,7 @@
472 powerd_debug("Auto-dim Timeout is %d seconds\n", dim_timeout);472 powerd_debug("Auto-dim Timeout is %d seconds\n", dim_timeout);
473473
474 libsuspend_init(0);474 libsuspend_init(0);
475 powerd_stats_init();
475 powerd_client_init();476 powerd_client_init();
476 power_request_init();477 power_request_init();
477 display_request_init();478 display_request_init();
@@ -512,5 +513,6 @@
512 display_request_deinit();513 display_request_deinit();
513 power_request_deinit();514 power_request_deinit();
514 powerd_client_deinit();515 powerd_client_deinit();
516 powerd_stats_deinit();
515 return g_exit_code;517 return g_exit_code;
516}518}
517519
=== added file 'src/stats.c'
--- src/stats.c 1970-01-01 00:00:00 +0000
+++ src/stats.c 2013-08-12 14:34:58 +0000
@@ -0,0 +1,508 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This file is part of powerd.
5 *
6 * powerd is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * powerd is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdlib.h>
20#include <errno.h>
21#include <string.h>
22#include <time.h>
23
24#include <glib.h>
25#include <gio/gio.h>
26
27#include "powerd-internal.h"
28#include "powerd-dbus.h"
29#include "log.h"
30
31#define USECS_PER_SEC 1000000
32#define NSECS_PER_USEC 1000
33
34struct client_stats {
35 const char *dbus_name;
36 GSList *sys_stats;
37 GSList *disp_stats;
38};
39
40struct sys_request_stats {
41 const char *name;
42 unsigned active_count;
43 guint64 active_time;
44 guint64 max_active_time;
45 guint64 active_since;
46};
47
48struct disp_request_stats {
49 const char *name;
50 unsigned active_count;
51 guint64 active_time;
52 guint64 max_active_time;
53 guint64 active_since;
54 guint64 disp_on_time;
55 guint64 disp_on_since;
56 guint64 flag_on_time[POWERD_NUM_DISPLAY_FLAGS];
57 guint64 flag_on_since[POWERD_NUM_DISPLAY_FLAGS];
58 struct powerd_display_request last_req;
59};
60
61GHashTable *stats_hash;
62
63static guint64 get_usecs(void)
64{
65 struct timespec ts;
66
67 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
68 powerd_error("Could not get monotonic time: %s", strerror(errno));
69 return 0;
70 }
71 return (guint64)ts.tv_sec * USECS_PER_SEC +
72 (ts.tv_nsec + NSECS_PER_USEC / 2) / NSECS_PER_USEC;
73}
74
75static struct client_stats *alloc_client(const char *dbus_name)
76{
77 struct client_stats *client = calloc(1, sizeof(*client));
78 if (!client) {
79 powerd_error("Error allocating client for statistics");
80 return NULL;
81 }
82
83 client->dbus_name = strdup(dbus_name);
84 if (!client->dbus_name) {
85 powerd_error("Error duplicating client dbus name for statistics");
86 free(client);
87 return NULL;
88 }
89
90 return client;
91}
92
93static struct sys_request_stats *alloc_sys_stats(const char *name)
94{
95 struct sys_request_stats *stats = calloc(1, sizeof(*stats));
96 if (!stats) {
97 powerd_error("Error allocating memory for system request statistics");
98 return NULL;
99 }
100
101 stats->name = strdup(name);
102 if (!stats->name) {
103 powerd_error("Error duplicating request name for statistics");
104 free(stats);
105 return NULL;
106 }
107
108 return stats;
109}
110
111static struct disp_request_stats *alloc_disp_stats(const char *name)
112{
113 struct disp_request_stats *stats = calloc(1, sizeof(*stats));
114 if (!stats) {
115 powerd_error("Error allocating memory for display request statistics");
116 return NULL;
117 }
118
119 stats->name = strdup(name);
120 if (!stats->name) {
121 powerd_error("Error duplicating request name for statistics");
122 free(stats);
123 return NULL;
124 }
125
126 return stats;
127}
128
129static gint sys_stats_comp(gconstpointer a, gconstpointer b)
130{
131 const struct sys_request_stats *stats = a;
132 const char *name = b;
133 return strcmp(stats->name, name);
134}
135
136static gint disp_stats_comp(gconstpointer a, gconstpointer b)
137{
138 const struct disp_request_stats *stats = a;
139 const char *name = b;
140 return strcmp(stats->name, name);
141}
142
143static struct client_stats *lookup_client(const char *dbus_name,
144 gboolean alloc)
145{
146 struct client_stats *client;
147
148 if (!stats_hash)
149 return NULL;
150
151 client = g_hash_table_lookup(stats_hash, dbus_name);
152 if (!client && alloc) {
153 client = alloc_client(dbus_name);
154 if (client)
155 g_hash_table_insert(stats_hash, (gpointer)client->dbus_name,
156 client);
157 }
158 return client;
159}
160
161void powerd_account_request_sys_state(const char *dbus_name, const char *name)
162{
163 struct client_stats *client;
164 struct sys_request_stats *stats;
165 GSList *item;
166
167 client = lookup_client(dbus_name, TRUE);
168 if (!client)
169 return;
170
171 item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
172 if (item) {
173 stats = item->data;
174 } else {
175 stats = alloc_sys_stats(name);
176 if (!stats)
177 return;
178 client->sys_stats = g_slist_prepend(client->sys_stats, stats);
179 }
180
181 stats->active_count++;
182 stats->active_since = get_usecs();
183}
184
185void powerd_account_clear_sys_state(const char *dbus_name, const char *name)
186{
187 struct client_stats *client;
188 struct sys_request_stats *stats;
189 GSList *item;
190 guint64 duration;
191
192 if (!stats_hash)
193 return;
194
195 client = lookup_client(dbus_name, FALSE);
196 if (!client)
197 return;
198 item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
199 if (!item)
200 return;
201 stats = item->data;
202
203 duration = get_usecs() - stats->active_since;
204 stats->active_since = 0;
205 stats->active_time += duration;
206 if (duration > stats->max_active_time)
207 stats->max_active_time = duration;
208}
209
210static void aggregate_disp_stats(struct disp_request_stats *stats,
211 const struct powerd_display_request *new)
212{
213 guint64 us;
214 int i;
215
216 us = get_usecs();
217 if (new->state != stats->last_req.state) {
218 if (new->state == POWERD_DISPLAY_STATE_ON) {
219 stats->disp_on_since = us;
220 } else {
221 stats->disp_on_time += us - stats->disp_on_since;
222 stats->disp_on_since = 0;
223 }
224 }
225 for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
226 guint32 mask = 1 << i;
227 if ((new->flags & mask) != (stats->last_req.flags & mask)) {
228 if (new->flags & mask) {
229 stats->flag_on_since[i] = us;
230 } else {
231 stats->flag_on_time[i] += us - stats->flag_on_since[i];
232 stats->flag_on_since[i] = 0;
233 }
234 }
235 }
236}
237
238void powerd_account_add_display_req(const char *dbus_name, const char *name,
239 const struct powerd_display_request *req)
240{
241 struct client_stats *client;
242 struct disp_request_stats *stats;
243 GSList *item;
244 guint64 us;
245 int i;
246
247 if (!stats_hash)
248 return;
249
250 client = lookup_client(dbus_name, TRUE);
251 if (!client)
252 return;
253
254 item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
255 if (item) {
256 stats = item->data;
257 } else {
258 stats = alloc_disp_stats(name);
259 if (!stats)
260 return;
261 client->disp_stats = g_slist_prepend(client->disp_stats, stats);
262 }
263
264 us = get_usecs();
265 stats->active_count++;
266 stats->active_since = us;
267 if (req->state == POWERD_DISPLAY_STATE_ON)
268 stats->disp_on_since = us;
269 for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
270 if (req->flags & (1 << i))
271 stats->flag_on_since[i] = us;
272 }
273 stats->last_req = *req;
274}
275
276void powerd_account_update_display_req(const char *dbus_name,
277 const char *name,
278 const struct powerd_display_request *req)
279{
280 struct client_stats *client;
281 struct disp_request_stats *stats;
282 GSList *item;
283
284 client = lookup_client(dbus_name, FALSE);
285 if (!client)
286 return;
287
288 item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
289 if (!item)
290 return;
291 stats = item->data;
292
293 aggregate_disp_stats(stats, req);
294 stats->last_req = *req;
295}
296
297void powerd_account_clear_display_req(const char *dbus_name, const char *name)
298{
299 struct client_stats *client;
300 struct disp_request_stats *stats;
301 GSList *item;
302 struct powerd_display_request req;
303 guint64 duration;
304
305 client = lookup_client(dbus_name, FALSE);
306 if (!client)
307 return;
308
309 item = g_slist_find_custom(client->disp_stats, name, disp_stats_comp);
310 if (!item)
311 return;
312 stats = item->data;
313
314 memset(&req, 0, sizeof(req));
315 aggregate_disp_stats(stats, &req);
316
317 duration = get_usecs() - stats->active_since;
318 stats->active_time += duration;
319 stats->active_since = 0;
320 if (duration > stats->max_active_time)
321 stats->max_active_time = duration;
322
323 stats->last_req = req;
324}
325
326static void sys_stats_list_destroy(gpointer data)
327{
328 struct sys_request_stats *stats = data;
329 free((void *)stats->name);
330 free(stats);
331}
332
333static void disp_stats_list_destroy(gpointer data)
334{
335 struct disp_request_stats *stats = data;
336 free((void *)stats->name);
337 free(stats);
338}
339
340static void stats_hash_destroy(gpointer data)
341{
342 struct client_stats *client = data;
343 g_slist_free_full(client->sys_stats, sys_stats_list_destroy);
344 g_slist_free_full(client->disp_stats, disp_stats_list_destroy);
345 free((void *)client->dbus_name);
346 free(client);
347}
348
349int powerd_stats_init(void)
350{
351 stats_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
352 stats_hash_destroy);
353 if (!stats_hash) {
354 powerd_warn("Unable to allocate stats hash table");
355 return -ENOMEM;
356 }
357 return 0;
358}
359
360void powerd_stats_deinit(void)
361{
362 if (stats_hash)
363 g_hash_table_destroy(stats_hash);
364}
365
366/* Dbus interface support */
367
368static int build_sys_request_list(GVariantBuilder *builder,
369 struct client_stats *client)
370{
371 const char *dbus_name = client->dbus_name;
372 GSList *cur;
373 guint64 us;
374 int count = 0;
375
376 us = get_usecs();
377 for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) {
378 struct sys_request_stats *stats = cur->data;
379 guint64 active_time, max_active_time;
380
381 /*
382 * Aggregate currently held requests into active_time, and
383 * consider whether this is greater than the current
384 * max_active_time.
385 */
386 active_time = stats->active_time;
387 max_active_time = stats->max_active_time;
388 if (stats->active_since != 0) {
389 guint64 duration = us - stats->active_since;
390 active_time += duration;
391 if (duration > max_active_time)
392 max_active_time = duration;
393 }
394 g_variant_builder_add(builder, "(ssuttt)", dbus_name, stats->name,
395 stats->active_count, active_time,
396 max_active_time, stats->active_since);
397 count++;
398 }
399 return count;
400}
401
402gboolean handle_get_sys_request_stats(PowerdSource *obj,
403 GDBusMethodInvocation *invocation)
404{
405 GVariantBuilder *builder;
406 GVariant *list, *tuple;
407 GHashTableIter iter;
408 gpointer key, value;
409 int count = 0;
410
411 builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssuttt)"));
412 if (stats_hash) {
413 g_hash_table_iter_init(&iter, stats_hash);
414 while (g_hash_table_iter_next(&iter, &key, &value))
415 count += build_sys_request_list(builder, value);
416 }
417
418 if (count == 0) {
419 g_variant_builder_clear(builder);
420 list = g_variant_new_array(G_VARIANT_TYPE("a(ssuttt)"), NULL, 0);
421 } else {
422 list = g_variant_builder_end(builder);
423 }
424 tuple = g_variant_new_tuple(&list, 1);
425 g_dbus_method_invocation_return_value(invocation, tuple);
426 g_variant_builder_unref(builder);
427
428 return TRUE;
429}
430
431static int build_disp_request_list(GVariantBuilder *builder,
432 struct client_stats *client)
433{
434 const char *dbus_name = client->dbus_name;
435 GSList *cur;
436 guint64 us;
437 int count = 0;
438
439 us = get_usecs();
440 for (cur = client->disp_stats; cur; cur = g_slist_next(cur)) {
441 struct disp_request_stats *stats = cur->data;
442 guint64 active_time, max_active_time;
443 guint64 disp_on_time;
444 GVariantBuilder *fot_builder, *fos_builder;
445 int i;
446
447 active_time = stats->active_time;
448 max_active_time = stats->max_active_time;
449 if (stats->active_since != 0) {
450 guint64 duration = us - stats->active_since;
451 active_time += duration;
452 if (duration > max_active_time)
453 max_active_time = duration;
454 }
455
456 disp_on_time = stats->disp_on_time;
457 if (stats->disp_on_since != 0)
458 disp_on_time += us - stats->disp_on_since;
459
460 fot_builder = g_variant_builder_new(G_VARIANT_TYPE("at"));
461 fos_builder = g_variant_builder_new(G_VARIANT_TYPE("at"));
462 for (i = 0; i < POWERD_NUM_DISPLAY_FLAGS; i++) {
463 guint64 flag_on_time = stats->flag_on_time[i];
464 if (stats->flag_on_since[i] != 0)
465 flag_on_time += us - stats->flag_on_since[i];
466 g_variant_builder_add(fot_builder, "t", flag_on_time);
467 g_variant_builder_add(fos_builder, "t", stats->flag_on_since[i]);
468 }
469 g_variant_builder_add(builder, "(ssutttttatat)", dbus_name,
470 stats->name, stats->active_count, active_time,
471 max_active_time, stats->active_since,
472 disp_on_time, stats->disp_on_since,
473 fot_builder, fos_builder);
474 g_variant_builder_unref(fot_builder);
475 g_variant_builder_unref(fos_builder);
476 count++;
477 }
478 return count;
479}
480
481gboolean handle_get_disp_request_stats(PowerdSource *obj,
482 GDBusMethodInvocation *invocation)
483{
484 GVariantBuilder *builder;
485 GVariant *list, *tuple;
486 GHashTableIter iter;
487 gpointer key, value;
488 int count = 0;
489
490 builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssutttttatat)"));
491 if (stats_hash) {
492 g_hash_table_iter_init(&iter, stats_hash);
493 while (g_hash_table_iter_next(&iter, &key, &value))
494 count += build_disp_request_list(builder, value);
495 }
496
497 if (count == 0) {
498 g_variant_builder_clear(builder);
499 list = g_variant_new_array(G_VARIANT_TYPE("a(ssutttttatat)"), NULL, 0);
500 } else {
501 list = g_variant_builder_end(builder);
502 }
503 tuple = g_variant_new_tuple(&list, 1);
504 g_dbus_method_invocation_return_value(invocation, tuple);
505 g_variant_builder_unref(builder);
506
507 return TRUE;
508}

Subscribers

People subscribed via source and target branches