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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Frey (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email:
|
Commit message
Add support for collecting and reporting statistics about requests
Description of the change
Add support for collecting and reporting statistics about requests
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
- 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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:99
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Seth Forshee (sforshee) wrote : | # |
Sample output: http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:100
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Frey (mfrey) wrote : | # |
tested. Works well.
Preview Diff
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 | +} |
PASSED: Continuous integration, rev:98 jenkins. qa.ubuntu. com/job/ powerd- ci/104/ jenkins. qa.ubuntu. com/job/ powerd- saucy-armhf- ci/58 jenkins. qa.ubuntu. com/job/ powerd- saucy-armhf- ci/58/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ powerd- ci/104/ rebuild
http://