tizen 2.3.1 release
[kernel/api/system-resource.git] / src / powertop-wrapper / powertop-wrapper.cpp
1 /*
2  * Library for getting power usage statistics
3  *
4  * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd.
5  *
6  * Contact: Igor Zhbanov <i.zhbanov@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #define _BSD_SOURCE
23
24 #include <time.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <locale.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <pthread.h>
33
34 #include <map>
35 #include <string>
36
37 #include <powertop-dapi.h>
38 #include <powertop-wrapper.h>
39
40
41 #define LINE_SIZE 4096
42
43 static FILE *log = stderr;
44 static volatile bool stopping = false, running = false;
45 static int check_interval = 3;
46 static pthread_t thr;
47 static void (*t_callback)(void *) = NULL;
48 static void *t_arg = NULL;
49
50 /* ************************************************************************ */
51
52 static void
53 logprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
54
55 static void
56 logprintf(const char *fmt, ...)
57 {
58         va_list ap;
59         char timebuf[128];
60         time_t t;
61         struct tm *tmp;
62
63         if (!log)
64                 return;
65
66         t = time(NULL);
67         tmp = localtime(&t);
68
69         va_start(ap, fmt);
70         strftime(timebuf, sizeof(timebuf), "[%Y-%m-%d %H:%M:%S] ", tmp);
71         fputs(timebuf, log);
72         vfprintf(log, fmt, ap);
73         fputc('\n', log);
74         fflush(log);
75         va_end(ap);
76 }
77
78 /* ************************************************************************ */
79
80 static int
81 loginit(const char *logname)
82 {
83         if (!strcmp(logname, "STDERR")) {
84                 log = stderr;
85                 return 0;
86         }
87
88         log = fopen(logname, "a");
89         if (!log) {
90                 perror("Can't open the log file");
91                 return -1;
92         }
93
94         return 0;
95 }
96
97 /* ************************************************************************ */
98
99 static void
100 logclose(void)
101 {
102         if (log && log != stderr)
103                 fclose(log);
104 }
105
106 /* ************************************************************************ */
107
108 static bool
109 get_double(char **str, double *d)
110 {
111         char *comma;
112
113         if (!(comma = strchr(*str, ',')))
114                 return false;
115
116         *comma = '\0';
117         *d = atof(*str);
118         *str = comma + 1;
119         return true;
120 }
121
122 /* ************************************************************************ */
123
124 static bool
125 get_pid_t(char **str, pid_t *p)
126 {
127         char *comma;
128
129         if (!(comma = strchr(*str, ',')))
130                 return false;
131
132         *comma = '\0';
133         *p = (pid_t)atol(*str);
134         *str = comma + 1;
135         return true;
136 }
137
138 /* ************************************************************************ */
139
140 static bool
141 get_percent(char **str, double *d)
142 {
143         char *comma;
144
145         if (!(comma = strchr(*str, ',')))
146                 return false;
147
148         *comma = '\0';
149         if (!strchr(*str, '%')) {
150                 *comma = ',';
151                 return false;
152         }
153
154         *d = atof(*str);
155         *str = comma + 1;
156         return true;
157 }
158
159 /* ************************************************************************ */
160
161 static bool
162 get_packets(char **str, double *usage)
163 {
164         char *comma;
165
166         if (!(comma = strchr(*str, ',')))
167                 return false;
168
169         *comma = '\0';
170         if (sscanf(*str, "%lf pkts/s", usage) != 1) {
171                 *comma = ',';
172                 return false;
173         }
174
175         *str = comma + 1;
176         return true;
177 }
178
179 /* ************************************************************************ */
180
181 static bool
182 get_usage(char **str, double *usage)
183 {
184         char *comma, suffix[10];
185
186         if (!(comma = strchr(*str, ',')))
187                 return false;
188
189         *comma = '\0';
190         if (sscanf(*str, "%lf %9s", usage, suffix) != 2) {
191                 *comma = ',';
192                 return false;
193         }
194
195         if (!strcmp(suffix, "ms/s"))
196                 *usage /= 1000.;
197         else if (!strcmp(suffix, "us/s"))
198                 *usage /= 1000000.;
199         else {
200                 *comma = ',';
201                 return false;
202         }
203
204         *str = comma + 1;
205         return true;
206 }
207
208 /* ************************************************************************ */
209
210 static bool
211 get_disks(char **str, double *harddisk, double *disk)
212 {
213         char *comma;
214
215         if (!(comma = strchr(*str, ',')))
216                 return false;
217
218         *comma = '\0';
219         *harddisk = 0;
220         *disk = 0;
221         sscanf(*str, "%lf (%lf)", harddisk, disk);
222         *str = comma + 1;
223         return true;
224 }
225
226 /* ************************************************************************ */
227
228 static bool
229 get_string(char **str, char **s)
230 {
231         char *comma;
232
233         if (!(comma = strchr(*str, ',')))
234                 return false;
235
236         *comma = '\0';
237         *s = *str;
238         while (**s == ' ')
239                 (*s)++;
240
241         *str = comma + 1;
242         return true;
243 }
244
245 /* ************************************************************************ */
246
247 static bool
248 get_qstring(char **str, char **s)
249 {
250         char *comma;
251
252         comma = strchr(*str, ',');
253         if (comma) {
254                 *comma = '\0';
255                 if (comma[-1] == '"')
256                         comma[-1] = '\0';
257         }
258
259         *s = *str;
260         while (**s == ' ')
261                 (*s)++;
262
263         if (**s == '"')
264                 (*s)++;
265
266         *str = comma + 1;
267         return true;
268 }
269
270 /* ************************************************************************ */
271
272 /* Will modify the line */
273 static void
274 update_software_power_consumers(char *line, work_ctx *ctx)
275 {
276         double usage, wakeups, gpu, harddisk, disk, gfx;
277         char *category, *description, *newdescr, buf[LINE_SIZE], *str;
278
279         snprintf(buf, sizeof(buf), "%s", line);
280         str = buf;
281
282         if (!get_usage(&str, &usage))
283                 return; /* Skip invalid or header line */
284
285         if (!get_double(&str, &wakeups))
286                 return;
287
288         if (!get_double(&str, &gpu))
289                 return;
290
291         if (!get_disks(&str, &harddisk, &disk))
292                 return;
293
294         if (!get_double(&str, &gfx))
295                 return;
296
297         if (!get_string(&str, &category))
298                 return;
299
300         if (!get_string(&str, &description))
301                 return;
302
303         newdescr = strdup(description);
304         sw_power_consumer &consumer = ctx->swpc[newdescr];
305         consumer.usage    += usage;
306         consumer.wakeups  += wakeups;
307         consumer.gpu      += gpu;
308         consumer.harddisk += harddisk;
309         consumer.disk     += disk;
310         consumer.gfx      += gfx;
311         if (!consumer.category)
312                 consumer.category = strdup(category);
313
314         if (!consumer.description)
315                 consumer.description = newdescr;
316         else
317                 free(newdescr);
318 }
319
320 /* ************************************************************************ */
321
322 /* Will modify the line */
323 static void
324 update_device_power_report(const char *line, work_ctx *ctx)
325 {
326         double usage;
327         char *device, *newdevice, buf[LINE_SIZE], *str;
328         bool network = false;
329
330         snprintf(buf, sizeof(buf), "%s", line);
331         str = buf;
332
333         if (!get_percent(&str, &usage)) {
334                 if (!get_packets(&str, &usage))
335                         return; /* Skip invalid or header line */
336                 else
337                         network = true;
338         }
339
340         if (!get_qstring(&str, &device))
341                 return;
342
343         newdevice = strdup(device);
344         hw_power_consumer &consumer = ctx->hwpc[newdevice];
345         consumer.network = network;
346         consumer.usage += usage;
347         if (!consumer.device)
348                 consumer.device = newdevice;
349         else
350                 free(newdevice);
351 }
352
353 /* ************************************************************************ */
354
355 static void
356 update_mali_gpu_power_consumers(const char *line, work_ctx *ctx)
357 {
358         double usage;
359         pid_t pid;
360         char *name, *description, buf[LINE_SIZE], *str;
361
362         snprintf(buf, sizeof(buf), "%s", line);
363         str = buf;
364
365         if (!get_double(&str, &usage))
366                 return; /* Skip invalid or header line */
367
368         if (!get_pid_t(&str, &pid))
369                 return;
370         if (!pid)
371                 return; /* Skip header */
372
373         if (!get_qstring(&str, &name))
374                 return;
375
376         if (!get_qstring(&str, &description))
377                 return;
378
379         mali_power_consumer &consumer = ctx->malipc[pid];
380         consumer.pid = pid;
381         consumer.usage += usage;
382
383         if (!consumer.name)
384                 consumer.name = strdup(name);
385
386         if (!consumer.description)
387                 consumer.description = strdup(description);
388 }
389
390 /* ************************************************************************ */
391
392 static void
393 process_report(const char *report, work_ctx *ctx)
394 {
395         char line[LINE_SIZE];
396         const char *rep = report, *eol;
397         bool in_section = false;
398         sections section = S_UNKNOWN;
399
400         if (!report)
401                 return;
402
403         rep = strchr(rep, '\n') + 1; /* Skip the first line. */
404         while ((eol = strchr(rep, '\n')) != NULL) {
405                 ssize_t len;
406
407                 len = eol + 1 - rep;
408                 if (len > LINE_SIZE - 1)
409                         len = LINE_SIZE - 1;
410
411                 strncpy(line, rep, len);
412                 line[len] = '\0';
413                 rep += len;
414
415                 if (in_section) {
416                         if (line[0] == '*')
417                                 in_section = false;
418                         else if (line[0] == '\n')
419                                 continue;
420                         else if (section == S_SOFTWARE_POWER_CONSUMERS)
421                                 update_software_power_consumers(line, ctx);
422                         else if (section == S_DEVICE_POWER_REPORT)
423                                 update_device_power_report(line, ctx);
424                         else if (section == S_MALI_GPU_POWER_CONSUMERS)
425                                 update_mali_gpu_power_consumers(line, ctx);
426                 }
427
428                 if (!in_section) {
429                         if (line[0] != '*')
430                                 continue; /* Wrong line */
431                         else if (!strcmp(line, "**Overview of Software "
432                                           "Power Consumers**, \n"))
433                                 section = S_SOFTWARE_POWER_CONSUMERS;
434                         else if (!strcmp(line, "**Device Power "
435                                                  "Report**,\n"))
436                                 section = S_DEVICE_POWER_REPORT;
437                         else if (!strcmp(line, "** Overview of MALI GPU "
438                                                  "power consumers **\n"))
439                                 section = S_MALI_GPU_POWER_CONSUMERS;
440                         else
441                                 section = S_UNKNOWN;
442
443                         in_section = true;
444                 }
445
446         }
447
448         free((void *)report);
449 }
450
451 /* ************************************************************************ */
452
453 static int
454 collect_reports(const char *cmdline, work_ctx *ctx)
455 {
456         char line[LINE_SIZE], buf[256], *report = NULL;
457         FILE *f;
458
459         f = popen(cmdline, "r");
460         if (!f) {
461                 strerror_r(errno, buf, sizeof(buf));
462                 logprintf("Can't open pipe to powertop: %s",
463                           buf);
464                 return -1;
465         }
466
467         while (fgets(line, sizeof(line), f)) {
468                 size_t len;
469
470                 len = strlen(line);
471                 if (!strcmp(line, "***PowerTOP Report***, \n")) {
472                         /* Try to process incomplete report */
473                         process_report(report, ctx);
474                         report = strndup(line, len+1);
475                         if (!report) {
476                                 logprintf("No memory!");
477                                 pclose(f);
478                                 return -1;
479                         }
480                 } else if (!strcmp(line, "***End of report***\n")) {
481                         process_report(report, ctx);
482                         report = NULL;
483                 } else if (report) {
484                         char *oldreport;
485
486                         oldreport = report;
487                         report = (char *)realloc(report,
488                                                  strlen(report) + len + 1);
489                         if (!report) {
490                                 logprintf("No memory!");
491                                 free(oldreport);
492                                 pclose(f);
493                                 return -1;
494                         }
495
496                         strcat(report, line);
497                 } /* else skip unknown line */
498         }
499
500         pclose(f);
501         process_report(report, ctx); /* Try to process incomplete report */
502         return 0;
503 }
504
505 /* ************************************************************************ */
506
507 static void
508 clear_context(work_ctx *ctx)
509 {
510         sw_power_consumer_map::iterator sit;
511         hw_power_consumer_map::iterator hit;
512         mali_power_consumer_map::iterator mit;
513
514         for (sit = ctx->swpc.begin(); sit != ctx->swpc.end(); sit++) {
515                 sw_power_consumer &swpc = sit->second;
516                 if (swpc.description)
517                         free(swpc.description);
518
519                 if (swpc.category)
520                         free(swpc.category);
521         }
522
523         ctx->swpc.clear();
524
525         for (hit = ctx->hwpc.begin(); hit != ctx->hwpc.end(); hit++) {
526                 hw_power_consumer &hwpc = hit->second;
527                 if (hwpc.device)
528                         free(hwpc.device);
529         }
530
531         ctx->hwpc.clear();
532
533         for (mit = ctx->malipc.begin(); mit != ctx->malipc.end(); mit++) {
534                 mali_power_consumer &malipc = mit->second;
535                 if (malipc.name)
536                         free(malipc.name);
537
538                 if (malipc.description)
539                         free(malipc.description);
540         }
541
542         ctx->malipc.clear();
543         ctx->iterations = 0;
544 }
545
546 /* ************************************************************************ */
547
548 static char fbuf[65536];
549
550 static void
551 report_write_header(FILE *out)
552 {
553         FILE *in;
554         size_t len;
555
556         in = fopen(REPORTHEADER, "r");
557         if (!in) {
558                 logprintf("Can't open header file '%s'.", REPORTHEADER);
559                 return;
560         }
561
562         while ((len = fread(fbuf, 1, sizeof(fbuf), in)) > 0)
563                 fwrite(fbuf, 1, len, out);
564
565         fclose(in);
566 }
567
568 /* ************************************************************************ */
569
570 static void
571 report_write_footer(FILE *f)
572 {
573         fprintf(f,
574                 "</body>\n"
575                 "</html>\n");
576 }
577
578 /* ************************************************************************ */
579
580 static void
581 report_write_swpc(work_ctx *ctx, FILE *f) /* Software Power Consumers */
582 {
583         int n = ctx->iterations, i;
584         sw_power_consumer_map::iterator it;
585
586         fprintf(f,
587                 "<div id=\"software\"><h2>Overview of Software Power"
588                         " Consumers</h2>\n"
589                 "<table width=\"100%%\">"
590                 "<tr><th width=\"10%%\">Usage</th>"
591                         "<th width=\"10%%\">Wakeups/s</th>"
592                         "<th width=\"10%%\">GFX Wakeups/s</th>"
593                         "<th width=\"10%%\" class=\"process\">Category</th>"
594                         "<th class=\"process\">Description</th></tr>\n");
595         for (i = 0, it = ctx->swpc.begin(); it != ctx->swpc.end(); it++, i++) {
596                 sw_power_consumer &swpc = it->second;
597
598                 fprintf(f,
599                         "<tr class=\"process_%s\">"
600                                 "<td class=\"process_power\">%g</td>"
601                                 "<td class=\"process_power\">%g</td>"
602                                 "<td class=\"process_power\">%g</td>"
603                                 "<td>%s</td><td>%s</td></tr>\n",
604                         (i & 1 ? "even" : "odd"),
605                         swpc.usage / n, swpc.wakeups / n, swpc.gfx / n,
606                         swpc.category, swpc.description);
607         }
608
609         fprintf(f,
610                 "</table>\n"
611                 "</div>\n");
612 }
613
614 /* ************************************************************************ */
615
616 static void
617 report_write_hwpc(work_ctx *ctx, FILE *f) /* Device Power Consumers */
618 {
619         int n = ctx->iterations, i;
620         hw_power_consumer_map::iterator it;
621
622         fprintf(f,
623                 "<h2>Device Power Report</h2>\n"
624                 "<table width=\"100%%\">\n"
625                 "<tr><th width=\"10%%\">Usage</th>"
626                         "<th class=\"device\">Device name</th></tr>\n");
627         for (i = 0, it = ctx->hwpc.begin(); it != ctx->hwpc.end();
628              it++, i++) {
629                 hw_power_consumer &hwpc = it->second;
630
631
632                 fprintf(f,
633                         "<tr class=\"device_%s\">"
634                                 "<td class=\"device_util\">%g%s</td>"
635                                 "<td>%s</td></tr>\n",
636                         (i & 1 ? "even" : "odd"), hwpc.usage / n,
637                         (hwpc.network ? " pkts/s" : "%"), hwpc.device);
638         }
639
640         fprintf(f, "</table>\n");
641 }
642
643 /* ************************************************************************ */
644
645 static void
646 report_write_malipc(work_ctx *ctx, FILE *f) /* MALI GPU Power Consumers */
647 {
648         int n = ctx->iterations, i;
649         mali_power_consumer_map::iterator it;
650
651         fprintf(f,
652                 "<h2>Overview of MALI GPU power consumers</h2>\n"
653                 "<p><table width='100%%'>\n"
654                 "<tr><th width='10%%' class='process'>Power est. "
655                         "(PseudoWatts&middot;s)</th>"
656                         "<th class='process'>PID</th>"
657                         "<th class='process'>Name</th>"
658                         "<th class='process'>Description</th></tr>\n");
659         for (i = 0, it = ctx->malipc.begin(); it != ctx->malipc.end();
660              it++, i++) {
661                 mali_power_consumer &malipc = it->second;
662
663                 fprintf(f,
664                         "<tr class='process_%s'>"
665                         "<td class='process_power'>%g PWs</td><td>%ld</td>"
666                         "<td>%s</td><td>%s</td></tr>\n",
667                         (i & 1 ? "even" : "odd"), malipc.usage / n,
668                         (long)malipc.pid, malipc.name, malipc.description);
669         }
670
671         fprintf(f, "</table>\n");
672 }
673
674 /* ************************************************************************ */
675
676 static void
677 write_report(work_ctx *ctx)
678 {
679         FILE *f;
680
681         f = fopen(ctx->report_file, "w+");
682         if (!f) {
683                 logprintf("Can't open report file '%s'.",
684                           ctx->report_file);
685                 return;
686         }
687
688         report_write_header(f);
689         report_write_swpc(ctx, f);
690         fprintf(f, "<div id=\"device\">\n");
691         report_write_hwpc(ctx, f);
692         report_write_malipc(ctx, f);
693         fprintf(f,"</div>\n");
694         report_write_footer(f);
695         fclose(f);
696 }
697
698 /* ************************************************************************ */
699
700 static void *
701 thread_main(void *d)
702 {
703         work_ctx ctx;
704         char *oldlocale, cmdline[8192];
705
706 /*      logprintf("Thread started.");*/
707         snprintf(cmdline, sizeof(cmdline),
708                 "LC_NUMERIC=en %s -D -i 1 --time=%d --csv 2>/dev/null",
709                 POWERTOP, check_interval);
710
711         oldlocale = setlocale(LC_NUMERIC, NULL);
712         oldlocale = strdup(oldlocale);
713         setlocale(LC_NUMERIC, "C"); /* For dots in %f in printf() */
714
715         ctx.report_file = (char *)d;
716         unlink(ctx.report_file);
717         ctx.iterations = 0;
718         while (running) {
719                 if (collect_reports(cmdline, &ctx) == -1) {
720 /*                      logprintf("Powertop error");*/
721                         running = false;
722                         break;
723                 }
724
725                 ctx.iterations++;
726         }
727
728         if (ctx.iterations) /* Generate total report */
729                 write_report(&ctx);
730
731         clear_context(&ctx);
732
733         setlocale(LC_NUMERIC, oldlocale);
734         free(oldlocale);
735
736         if (t_callback)
737                 t_callback(t_arg);
738
739 /*      logprintf("Thread finished.");*/
740         return NULL;
741 }
742
743 /* ************************************************************************ */
744
745 void
746 powertop_set_check_interval(unsigned int interval)
747 {
748         check_interval = interval;
749 }
750
751 /* ************************************************************************ */
752
753 bool
754 powertop_start_check(const char *output_path)
755 {
756         pthread_attr_t thr_attr;
757
758         if (loginit(LOGFILE) == -1)
759                 return false;
760
761         if (running) {
762                 logprintf("The thread is already started.");
763                 logclose();
764                 return false;
765         }
766
767 /*      logprintf("start_power_consumption_check()");*/
768
769         t_callback = NULL;
770         running = true;
771         pthread_attr_init(&thr_attr);
772         pthread_create(&thr, &thr_attr, &thread_main, (void *)output_path);
773         pthread_attr_destroy(&thr_attr);
774         return true;
775 }
776
777 /* ************************************************************************ */
778
779 void
780 powertop_stop_check(void)
781 {
782 /*      logprintf("stop_power_consumption_check()");*/
783         if (!running)
784                 logprintf("The thread is not started of unexpectedly died.");
785
786         running = false;
787         pthread_join(thr, NULL);
788         logclose();
789 }
790
791 /* ************************************************************************ */
792
793 void
794 powertop_async_stop_check(void (*callback)(void *), void *arg)
795 {
796 /*      logprintf("async_stop_power_consumption_check()");*/
797         if (!running)
798                 logprintf("The thread is not started of unexpectedly died.");
799
800         t_arg = arg;
801         t_callback = callback;
802         running = false;
803         pthread_detach(thr);
804         logclose();
805 }