2 * Library for getting power usage statistics
4 * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd.
6 * Contact: Igor Zhbanov <i.zhbanov@samsung.com>
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
37 #include <powertop-dapi.h>
38 #include <powertop-wrapper.h>
41 #define LINE_SIZE 4096
43 static FILE *log = stderr;
44 static volatile bool stopping = false, running = false;
45 static int check_interval = 3;
47 static void (*t_callback)(void *) = NULL;
48 static void *t_arg = NULL;
50 /* ************************************************************************ */
53 logprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
56 logprintf(const char *fmt, ...)
70 strftime(timebuf, sizeof(timebuf), "[%Y-%m-%d %H:%M:%S] ", tmp);
72 vfprintf(log, fmt, ap);
78 /* ************************************************************************ */
81 loginit(const char *logname)
83 if (!strcmp(logname, "STDERR")) {
88 log = fopen(logname, "a");
90 perror("Can't open the log file");
97 /* ************************************************************************ */
102 if (log && log != stderr)
106 /* ************************************************************************ */
109 get_double(char **str, double *d)
113 if (!(comma = strchr(*str, ',')))
122 /* ************************************************************************ */
125 get_pid_t(char **str, pid_t *p)
129 if (!(comma = strchr(*str, ',')))
133 *p = (pid_t)atol(*str);
138 /* ************************************************************************ */
141 get_percent(char **str, double *d)
145 if (!(comma = strchr(*str, ',')))
149 if (!strchr(*str, '%')) {
159 /* ************************************************************************ */
162 get_packets(char **str, double *usage)
166 if (!(comma = strchr(*str, ',')))
170 if (sscanf(*str, "%lf pkts/s", usage) != 1) {
179 /* ************************************************************************ */
182 get_usage(char **str, double *usage)
184 char *comma, suffix[10];
186 if (!(comma = strchr(*str, ',')))
190 if (sscanf(*str, "%lf %9s", usage, suffix) != 2) {
195 if (!strcmp(suffix, "ms/s"))
197 else if (!strcmp(suffix, "us/s"))
208 /* ************************************************************************ */
211 get_disks(char **str, double *harddisk, double *disk)
215 if (!(comma = strchr(*str, ',')))
221 sscanf(*str, "%lf (%lf)", harddisk, disk);
226 /* ************************************************************************ */
229 get_string(char **str, char **s)
233 if (!(comma = strchr(*str, ',')))
245 /* ************************************************************************ */
248 get_qstring(char **str, char **s)
252 comma = strchr(*str, ',');
255 if (comma[-1] == '"')
270 /* ************************************************************************ */
272 /* Will modify the line */
274 update_software_power_consumers(char *line, work_ctx *ctx)
276 double usage, wakeups, gpu, harddisk, disk, gfx;
277 char *category, *description, *newdescr, buf[LINE_SIZE], *str;
279 snprintf(buf, sizeof(buf), "%s", line);
282 if (!get_usage(&str, &usage))
283 return; /* Skip invalid or header line */
285 if (!get_double(&str, &wakeups))
288 if (!get_double(&str, &gpu))
291 if (!get_disks(&str, &harddisk, &disk))
294 if (!get_double(&str, &gfx))
297 if (!get_string(&str, &category))
300 if (!get_string(&str, &description))
303 newdescr = strdup(description);
304 sw_power_consumer &consumer = ctx->swpc[newdescr];
305 consumer.usage += usage;
306 consumer.wakeups += wakeups;
308 consumer.harddisk += harddisk;
309 consumer.disk += disk;
311 if (!consumer.category)
312 consumer.category = strdup(category);
314 if (!consumer.description)
315 consumer.description = newdescr;
320 /* ************************************************************************ */
322 /* Will modify the line */
324 update_device_power_report(const char *line, work_ctx *ctx)
327 char *device, *newdevice, buf[LINE_SIZE], *str;
328 bool network = false;
330 snprintf(buf, sizeof(buf), "%s", line);
333 if (!get_percent(&str, &usage)) {
334 if (!get_packets(&str, &usage))
335 return; /* Skip invalid or header line */
340 if (!get_qstring(&str, &device))
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;
353 /* ************************************************************************ */
356 update_mali_gpu_power_consumers(const char *line, work_ctx *ctx)
360 char *name, *description, buf[LINE_SIZE], *str;
362 snprintf(buf, sizeof(buf), "%s", line);
365 if (!get_double(&str, &usage))
366 return; /* Skip invalid or header line */
368 if (!get_pid_t(&str, &pid))
371 return; /* Skip header */
373 if (!get_qstring(&str, &name))
376 if (!get_qstring(&str, &description))
379 mali_power_consumer &consumer = ctx->malipc[pid];
381 consumer.usage += usage;
384 consumer.name = strdup(name);
386 if (!consumer.description)
387 consumer.description = strdup(description);
390 /* ************************************************************************ */
393 process_report(const char *report, work_ctx *ctx)
395 char line[LINE_SIZE];
396 const char *rep = report, *eol;
397 bool in_section = false;
398 sections section = S_UNKNOWN;
403 rep = strchr(rep, '\n') + 1; /* Skip the first line. */
404 while ((eol = strchr(rep, '\n')) != NULL) {
408 if (len > LINE_SIZE - 1)
411 strncpy(line, rep, len);
418 else if (line[0] == '\n')
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);
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 "
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;
448 free((void *)report);
451 /* ************************************************************************ */
454 collect_reports(const char *cmdline, work_ctx *ctx)
456 char line[LINE_SIZE], buf[256], *report = NULL;
459 f = popen(cmdline, "r");
461 strerror_r(errno, buf, sizeof(buf));
462 logprintf("Can't open pipe to powertop: %s",
467 while (fgets(line, sizeof(line), f)) {
471 if (!strcmp(line, "***PowerTOP Report***, \n")) {
472 /* Try to process incomplete report */
473 process_report(report, ctx);
474 report = strndup(line, len+1);
476 logprintf("No memory!");
480 } else if (!strcmp(line, "***End of report***\n")) {
481 process_report(report, ctx);
487 report = (char *)realloc(report,
488 strlen(report) + len + 1);
490 logprintf("No memory!");
496 strcat(report, line);
497 } /* else skip unknown line */
501 process_report(report, ctx); /* Try to process incomplete report */
505 /* ************************************************************************ */
508 clear_context(work_ctx *ctx)
510 sw_power_consumer_map::iterator sit;
511 hw_power_consumer_map::iterator hit;
512 mali_power_consumer_map::iterator mit;
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);
525 for (hit = ctx->hwpc.begin(); hit != ctx->hwpc.end(); hit++) {
526 hw_power_consumer &hwpc = hit->second;
533 for (mit = ctx->malipc.begin(); mit != ctx->malipc.end(); mit++) {
534 mali_power_consumer &malipc = mit->second;
538 if (malipc.description)
539 free(malipc.description);
546 /* ************************************************************************ */
548 static char fbuf[65536];
551 report_write_header(FILE *out)
556 in = fopen(REPORTHEADER, "r");
558 logprintf("Can't open header file '%s'.", REPORTHEADER);
562 while ((len = fread(fbuf, 1, sizeof(fbuf), in)) > 0)
563 fwrite(fbuf, 1, len, out);
568 /* ************************************************************************ */
571 report_write_footer(FILE *f)
578 /* ************************************************************************ */
581 report_write_swpc(work_ctx *ctx, FILE *f) /* Software Power Consumers */
583 int n = ctx->iterations, i;
584 sw_power_consumer_map::iterator it;
587 "<div id=\"software\"><h2>Overview of Software Power"
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;
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);
614 /* ************************************************************************ */
617 report_write_hwpc(work_ctx *ctx, FILE *f) /* Device Power Consumers */
619 int n = ctx->iterations, i;
620 hw_power_consumer_map::iterator it;
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();
629 hw_power_consumer &hwpc = it->second;
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);
640 fprintf(f, "</table>\n");
643 /* ************************************************************************ */
646 report_write_malipc(work_ctx *ctx, FILE *f) /* MALI GPU Power Consumers */
648 int n = ctx->iterations, i;
649 mali_power_consumer_map::iterator it;
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·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();
661 mali_power_consumer &malipc = it->second;
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);
671 fprintf(f, "</table>\n");
674 /* ************************************************************************ */
677 write_report(work_ctx *ctx)
681 f = fopen(ctx->report_file, "w+");
683 logprintf("Can't open report file '%s'.",
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);
698 /* ************************************************************************ */
704 char *oldlocale, cmdline[8192];
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);
711 oldlocale = setlocale(LC_NUMERIC, NULL);
712 oldlocale = strdup(oldlocale);
713 setlocale(LC_NUMERIC, "C"); /* For dots in %f in printf() */
715 ctx.report_file = (char *)d;
716 unlink(ctx.report_file);
719 if (collect_reports(cmdline, &ctx) == -1) {
720 /* logprintf("Powertop error");*/
728 if (ctx.iterations) /* Generate total report */
733 setlocale(LC_NUMERIC, oldlocale);
739 /* logprintf("Thread finished.");*/
743 /* ************************************************************************ */
746 powertop_set_check_interval(unsigned int interval)
748 check_interval = interval;
751 /* ************************************************************************ */
754 powertop_start_check(const char *output_path)
756 pthread_attr_t thr_attr;
758 if (loginit(LOGFILE) == -1)
762 logprintf("The thread is already started.");
767 /* logprintf("start_power_consumption_check()");*/
771 pthread_attr_init(&thr_attr);
772 pthread_create(&thr, &thr_attr, &thread_main, (void *)output_path);
773 pthread_attr_destroy(&thr_attr);
777 /* ************************************************************************ */
780 powertop_stop_check(void)
782 /* logprintf("stop_power_consumption_check()");*/
784 logprintf("The thread is not started of unexpectedly died.");
787 pthread_join(thr, NULL);
791 /* ************************************************************************ */
794 powertop_async_stop_check(void (*callback)(void *), void *arg)
796 /* logprintf("async_stop_power_consumption_check()");*/
798 logprintf("The thread is not started of unexpectedly died.");
801 t_callback = callback;