/* * Copyright 2010, Intel Corporation * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc, * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * or just google for it. * * Authors: * Arjan van de Ven */ #include "process.h" #include "interrupt.h" #include "timer.h" #include "work.h" #include "processdevice.h" #include "../lib.h" #include "../report/report.h" #include "../report/report-data-html.h" #include "../report/report-maker.h" #include "../devlist.h" #include #include #include #include #include #include #include "../perf/perf_bundle.h" #include "../perf/perf_event.h" #include "../parameters/parameters.h" #include "../display.h" #include "../measurement/measurement.h" static class perf_bundle * perf_events; vector all_power; vector< vector > cpu_stack; vector cpu_level; vector cpu_credit; vector cpu_blame; #define LEVEL_HARDIRQ 1 #define LEVEL_SOFTIRQ 2 #define LEVEL_TIMER 3 #define LEVEL_WAKEUP 4 #define LEVEL_PROCESS 5 #define LEVEL_WORK 6 static uint64_t first_stamp, last_stamp; double measurement_time; static void push_consumer(unsigned int cpu, class power_consumer *consumer) { if (cpu_stack.size() <= cpu) cpu_stack.resize(cpu + 1); cpu_stack[cpu].push_back(consumer); } static void pop_consumer(unsigned int cpu) { if (cpu_stack.size() <= cpu) cpu_stack.resize(cpu + 1); if (cpu_stack[cpu].size()) cpu_stack[cpu].resize(cpu_stack[cpu].size()-1); } static int consumer_depth(unsigned int cpu) { if (cpu_stack.size() <= cpu) cpu_stack.resize(cpu + 1); return cpu_stack[cpu].size(); } static class power_consumer *current_consumer(unsigned int cpu) { if (cpu_stack.size() <= cpu) cpu_stack.resize(cpu + 1); if (cpu_stack[cpu].size()) return cpu_stack[cpu][cpu_stack[cpu].size()-1]; return NULL; } static void clear_consumers(void) { unsigned int i; for (i = 0; i < cpu_stack.size(); i++) cpu_stack[i].resize(0); } static void consumer_child_time(unsigned int cpu, uint64_t time) { unsigned int i; if (cpu_stack.size() <= cpu) cpu_stack.resize(cpu + 1); for (i = 0; i < cpu_stack[cpu].size(); i++) cpu_stack[cpu][i]->child_runtime += time; } static void set_wakeup_pending(unsigned int cpu) { if (cpu_credit.size() <= cpu) cpu_credit.resize(cpu + 1); cpu_credit[cpu] = 1; } static void clear_wakeup_pending(unsigned int cpu) { if (cpu_credit.size() <= cpu) cpu_credit.resize(cpu + 1); cpu_credit[cpu] = 0; } static int get_wakeup_pending(unsigned int cpu) { if (cpu_credit.size() <= cpu) cpu_credit.resize(cpu + 1); return cpu_credit[cpu]; } static void change_blame(unsigned int cpu, class power_consumer *consumer, int level) { if (cpu_level[cpu] >= level) return; cpu_blame[cpu] = consumer; cpu_level[cpu] = level; } static void consume_blame(unsigned int cpu) { if (!get_wakeup_pending(cpu)) return; if (cpu_level.size() <= cpu) return; if (cpu_blame.size() <= cpu) return; if (!cpu_blame[cpu]) return; cpu_blame[cpu]->wake_ups++; cpu_blame[cpu] = NULL; cpu_level[cpu] = 0; clear_wakeup_pending(cpu); } class perf_process_bundle: public perf_bundle { virtual void handle_trace_point(void *trace, int cpu, uint64_t time); }; static bool comm_is_xorg(char *comm) { return strcmp(comm, "Xorg") == 0 || strcmp(comm, "X") == 0; } /* some processes shouldn't be blamed for the wakeup if they wake a process up... for now this is a hardcoded list */ int dont_blame_me(char *comm) { if (comm_is_xorg(comm)) return 1; if (strcmp(comm, "dbus-daemon") == 0) return 1; return 0; } static char * get_pevent_field_str(void *trace, struct event_format *event, struct format_field *field) { unsigned long long offset, len; if (field->flags & FIELD_IS_DYNAMIC) { offset = field->offset; len = field->size; offset = pevent_read_number(event->pevent, (char *)trace + offset, len); offset &= 0xffff; return (char *)trace + offset; } /** no __data_loc field type*/ return (char *)trace + field->offset; } void perf_process_bundle::handle_trace_point(void *trace, int cpu, uint64_t time) { struct event_format *event; struct pevent_record rec; /* holder */ struct format_field *field; unsigned long long val; int type; int ret; rec.data = trace; type = pevent_data_type(perf_event::pevent, &rec); event = pevent_find_event(perf_event::pevent, type); if (!event) return; if (time < first_stamp) first_stamp = time; if (time > last_stamp) { last_stamp = time; measurement_time = (0.0001 + last_stamp - first_stamp) / 1000000000 ; } if (strcmp(event->name, "sched_switch") == 0) { class process *old_proc = NULL; class process *new_proc = NULL; const char *next_comm; int next_pid; int prev_pid; field = pevent_find_any_field(event, "next_comm"); if (!field || !(field->flags & FIELD_IS_STRING)) return; /* ?? */ next_comm = get_pevent_field_str(trace, event, field); ret = pevent_get_field_val(NULL, event, "next_pid", &rec, &val, 0); if (ret < 0) return; next_pid = (int)val; ret = pevent_get_field_val(NULL, event, "prev_pid", &rec, &val, 0); if (ret < 0) return; prev_pid = (int)val; /* find new process pointer */ new_proc = find_create_process(next_comm, next_pid); /* find the old process pointer */ while (consumer_depth(cpu) > 1) { pop_consumer(cpu); } if (consumer_depth(cpu) == 1) old_proc = (class process *)current_consumer(cpu); if (old_proc && strcmp(old_proc->name(), "process")) old_proc = NULL; /* retire the old process */ if (old_proc) { old_proc->deschedule_thread(time, prev_pid); old_proc->waker = NULL; } if (consumer_depth(cpu)) pop_consumer(cpu); push_consumer(cpu, new_proc); /* start new process */ new_proc->schedule_thread(time, next_pid); if (strncmp(next_comm,"migration/", 10) && strncmp(next_comm,"kworker/", 8) && strncmp(next_comm, "kondemand/",10)) { if (next_pid) { /* If someone woke us up.. blame him instead */ if (new_proc->waker) { change_blame(cpu, new_proc->waker, LEVEL_PROCESS); } else { change_blame(cpu, new_proc, LEVEL_PROCESS); } } consume_blame(cpu); } new_proc->waker = NULL; } else if (strcmp(event->name, "sched_wakeup") == 0) { class power_consumer *from = NULL; class process *dest_proc = NULL; class process *from_proc = NULL; const char *comm; int flags; int pid; ret = pevent_get_common_field_val(NULL, event, "common_flags", &rec, &val, 0); if (ret < 0) return; flags = (int)val; if ( (flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) { class timer *timer; timer = (class timer *) current_consumer(cpu); if (timer && strcmp(timer->name(), "timer")==0) { if (strcmp(timer->handler, "delayed_work_timer_fn") && strcmp(timer->handler, "hrtimer_wakeup") && strcmp(timer->handler, "it_real_fn")) from = timer; } /* woken from interrupt */ /* TODO: find the current irq handler and set "from" to that */ } else { from = current_consumer(cpu); } field = pevent_find_any_field(event, "comm"); if (!field || !(field->flags & FIELD_IS_STRING)) return; comm = get_pevent_field_str(trace, event, field); ret = pevent_get_field_val(NULL, event, "pid", &rec, &val, 0); if (ret < 0) return; pid = (int)val; dest_proc = find_create_process(comm, pid); if (from && strcmp(from->name(), "process")!=0){ /* not a process doing the wakeup */ from = NULL; from_proc = NULL; } else { from_proc = (class process *) from; } if (from_proc && (dest_proc->running == 0) && (dest_proc->waker == NULL) && (pid != 0) && !dont_blame_me(from_proc->comm)) dest_proc->waker = from; if (from) dest_proc->last_waker = from; /* Account processes that wake up X specially */ if (from && dest_proc && comm_is_xorg(dest_proc->comm)) from->xwakes ++ ; } else if (strcmp(event->name, "irq_handler_entry") == 0) { class interrupt *irq = NULL; const char *handler; int nr; field = pevent_find_any_field(event, "name"); if (!field || !(field->flags & FIELD_IS_STRING)) return; /* ?? */ handler = get_pevent_field_str(trace, event, field); ret = pevent_get_field_val(NULL, event, "irq", &rec, &val, 0); if (ret < 0) return; nr = (int)val; irq = find_create_interrupt(handler, nr, cpu); push_consumer(cpu, irq); irq->start_interrupt(time); if (strstr(irq->handler, "timer") ==NULL) change_blame(cpu, irq, LEVEL_HARDIRQ); } else if (strcmp(event->name, "irq_handler_exit") == 0) { class interrupt *irq = NULL; uint64_t t; /* find interrupt (top of stack) */ irq = (class interrupt *)current_consumer(cpu); if (!irq || strcmp(irq->name(), "interrupt")) return; pop_consumer(cpu); /* retire interrupt */ t = irq->end_interrupt(time); consumer_child_time(cpu, t); } else if (strcmp(event->name, "softirq_entry") == 0) { class interrupt *irq = NULL; const char *handler = NULL; int vec; ret = pevent_get_field_val(NULL, event, "vec", &rec, &val, 0); if (ret < 0) { fprintf(stderr, "softirq_entry event returned no vector number?\n"); return; } vec = (int)val; if (vec <= 9) handler = softirqs[vec]; if (!handler) return; irq = find_create_interrupt(handler, vec, cpu); push_consumer(cpu, irq); irq->start_interrupt(time); change_blame(cpu, irq, LEVEL_SOFTIRQ); } else if (strcmp(event->name, "softirq_exit") == 0) { class interrupt *irq = NULL; uint64_t t; irq = (class interrupt *) current_consumer(cpu); if (!irq || strcmp(irq->name(), "interrupt")) return; pop_consumer(cpu); /* pop irq */ t = irq->end_interrupt(time); consumer_child_time(cpu, t); } else if (strcmp(event->name, "timer_expire_entry") == 0) { class timer *timer = NULL; uint64_t function; uint64_t tmr; ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); if (ret < 0) { fprintf(stderr, "timer_expire_entry event returned no function value?\n"); return; } function = (uint64_t)val; timer = find_create_timer(function); if (timer->is_deferred()) return; ret = pevent_get_field_val(NULL, event, "timer", &rec, &val, 0); if (ret < 0) { fprintf(stderr, "softirq_entry event returned no timer ?\n"); return; } tmr = (uint64_t)val; push_consumer(cpu, timer); timer->fire(time, tmr); if (strcmp(timer->handler, "delayed_work_timer_fn")) change_blame(cpu, timer, LEVEL_TIMER); } else if (strcmp(event->name, "timer_expire_exit") == 0) { class timer *timer = NULL; uint64_t tmr; uint64_t t; ret = pevent_get_field_val(NULL, event, "timer", &rec, &val, 0); if (ret < 0) return; tmr = (uint64_t)val; timer = (class timer *) current_consumer(cpu); if (!timer || strcmp(timer->name(), "timer")) { return; } pop_consumer(cpu); t = timer->done(time, tmr); if (t == ~0ULL) { timer->fire(first_stamp, tmr); t = timer->done(time, tmr); } consumer_child_time(cpu, t); } else if (strcmp(event->name, "hrtimer_expire_entry") == 0) { class timer *timer = NULL; uint64_t function; uint64_t tmr; ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); if (ret < 0) return; function = (uint64_t)val; timer = find_create_timer(function); ret = pevent_get_field_val(NULL, event, "hrtimer", &rec, &val, 0); if (ret < 0) return; tmr = (uint64_t)val; push_consumer(cpu, timer); timer->fire(time, tmr); if (strcmp(timer->handler, "delayed_work_timer_fn")) change_blame(cpu, timer, LEVEL_TIMER); } else if (strcmp(event->name, "hrtimer_expire_exit") == 0) { class timer *timer = NULL; uint64_t tmr; uint64_t t; timer = (class timer *) current_consumer(cpu); if (!timer || strcmp(timer->name(), "timer")) { return; } ret = pevent_get_field_val(NULL, event, "hrtimer", &rec, &val, 0); if (ret < 0) return; tmr = (uint64_t)val; pop_consumer(cpu); t = timer->done(time, tmr); if (t == ~0ULL) { timer->fire(first_stamp, tmr); t = timer->done(time, tmr); } consumer_child_time(cpu, t); } else if (strcmp(event->name, "workqueue_execute_start") == 0) { class work *work = NULL; uint64_t function; uint64_t wk; ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); if (ret < 0) return; function = (uint64_t)val; ret = pevent_get_field_val(NULL, event, "work", &rec, &val, 0); if (ret < 0) return; wk = (uint64_t)val; work = find_create_work(function); push_consumer(cpu, work); work->fire(time, wk); if (strcmp(work->handler, "do_dbs_timer") != 0 && strcmp(work->handler, "vmstat_update") != 0) change_blame(cpu, work, LEVEL_WORK); } else if (strcmp(event->name, "workqueue_execute_end") == 0) { class work *work = NULL; uint64_t t; uint64_t wk; ret = pevent_get_field_val(NULL, event, "work", &rec, &val, 0); if (ret < 0) return; wk = (uint64_t)val; work = (class work *) current_consumer(cpu); if (!work || strcmp(work->name(), "work")) { return; } pop_consumer(cpu); t = work->done(time, wk); if (t == ~0ULL) { work->fire(first_stamp, wk); t = work->done(time, wk); } consumer_child_time(cpu, t); } else if (strcmp(event->name, "cpu_idle") == 0) { pevent_get_field_val(NULL, event, "state", &rec, &val, 0); if (val == (unsigned int)-1) consume_blame(cpu); else set_wakeup_pending(cpu); } else if (strcmp(event->name, "power_start") == 0) { set_wakeup_pending(cpu); } else if (strcmp(event->name, "power_end") == 0) { consume_blame(cpu); } else if (strcmp(event->name, "i915_gem_ring_dispatch") == 0 || strcmp(event->name, "i915_gem_request_submit") == 0) { /* any kernel contains only one of the these tracepoints, * the latter one got replaced by the former one */ class power_consumer *consumer = NULL; int flags; ret = pevent_get_common_field_val(NULL, event, "common_flags", &rec, &val, 0); if (ret < 0) return; flags = (int)val; consumer = current_consumer(cpu); /* currently we don't count graphic requests submitted from irq contect */ if ( (flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) { consumer = NULL; } /* if we are X, and someone just woke us, account the GPU op to the guy waking us */ if (consumer && strcmp(consumer->name(), "process")==0) { class process *proc = NULL; proc = (class process *) consumer; if (comm_is_xorg(proc->comm) && proc->last_waker) { consumer = proc->last_waker; } } if (consumer) { consumer->gpu_ops++; } } else if (strcmp(event->name, "writeback_inode_dirty") == 0) { static uint64_t prev_time; class power_consumer *consumer = NULL; int dev; consumer = current_consumer(cpu); ret = pevent_get_field_val(NULL, event, "dev", &rec, &val, 0); if (ret < 0) return; dev = (int)val; if (consumer && strcmp(consumer->name(), "process")==0 && dev > 0) { consumer->disk_hits++; /* if the previous inode dirty was > 1 second ago, it becomes a hard hit */ if ((time - prev_time) > 1000000000) consumer->hard_disk_hits++; prev_time = time; } } } void start_process_measurement(void) { if (!perf_events) { perf_events = new perf_process_bundle(); perf_events->add_event("sched:sched_switch"); perf_events->add_event("sched:sched_wakeup"); perf_events->add_event("irq:irq_handler_entry"); perf_events->add_event("irq:irq_handler_exit"); perf_events->add_event("irq:softirq_entry"); perf_events->add_event("irq:softirq_exit"); perf_events->add_event("timer:timer_expire_entry"); perf_events->add_event("timer:timer_expire_exit"); perf_events->add_event("timer:hrtimer_expire_entry"); perf_events->add_event("timer:hrtimer_expire_exit"); if (!perf_events->add_event("power:cpu_idle")){ perf_events->add_event("power:power_start"); perf_events->add_event("power:power_end"); } perf_events->add_event("workqueue:workqueue_execute_start"); perf_events->add_event("workqueue:workqueue_execute_end"); perf_events->add_event("i915:i915_gem_ring_dispatch"); perf_events->add_event("i915:i915_gem_request_submit"); perf_events->add_event("writeback:writeback_inode_dirty"); } first_stamp = ~0ULL; last_stamp = 0; perf_events->start(); } void end_process_measurement(void) { if (!perf_events) return; perf_events->stop(); } static bool power_cpu_sort(class power_consumer * i, class power_consumer * j) { double iW, jW; iW = i->Witts(); jW = j->Witts(); if (equals(iW, jW)) { double iR, jR; iR = i->accumulated_runtime - i->child_runtime; jR = j->accumulated_runtime - j->child_runtime; if (equals(iR, jR)) return i->wake_ups > j->wake_ups; return (iR > jR); } return (iW > jW); } double total_wakeups(void) { double total = 0; unsigned int i; for (i = 0; i < all_power.size() ; i++) total += all_power[i]->wake_ups; total = total / measurement_time; return total; } double total_gpu_ops(void) { double total = 0; unsigned int i; for (i = 0; i < all_power.size() ; i++) total += all_power[i]->gpu_ops; total = total / measurement_time; return total; } double total_disk_hits(void) { double total = 0; unsigned int i; for (i = 0; i < all_power.size() ; i++) total += all_power[i]->disk_hits; total = total / measurement_time; return total; } double total_hard_disk_hits(void) { double total = 0; unsigned int i; for (i = 0; i < all_power.size() ; i++) total += all_power[i]->hard_disk_hits; total = total / measurement_time; return total; } double total_xwakes(void) { double total = 0; unsigned int i; for (i = 0; i < all_power.size() ; i++) total += all_power[i]->xwakes; total = total / measurement_time; return total; } void process_update_display(void) { unsigned int i; WINDOW *win; double pw; double joules; int tl; int tlt; int tlr; int show_power; int need_linebreak = 0; sort(all_power.begin(), all_power.end(), power_cpu_sort); show_power = global_power_valid(); win = get_ncurses_win("Overview"); if (!win) return; wclear(win); wmove(win, 1,0); #if 0 double sum; calculate_params(); sum = 0.0; sum += get_parameter_value("base power"); for (i = 0; i < all_power.size(); i++) { sum += all_power[i]->Witts(); } wprintw(win, _("Estimated power: %5.1f Measured power: %5.1f Sum: %5.1f\n\n"), all_parameters.guessed_power, global_power(), sum); #endif pw = global_power(); joules = global_joules(); tl = global_time_left() / 60; tlt = (tl /60); tlr = tl % 60; if (pw > 0.0001) { char buf[32]; wprintw(win, _("The battery reports a discharge rate of %sW\n"), fmt_prefix(pw, buf)); wprintw(win, _("The power consumed was %sJ\n"), fmt_prefix(joules, buf)); need_linebreak = 1; } if (tl > 0 && pw > 0.0001) { wprintw(win, _("The estimated remaining time is %i hours, %i minutes\n"), tlt, tlr); need_linebreak = 1; } if (need_linebreak) wprintw(win, "\n"); wprintw(win, "%s: %3.1f %s, %3.1f %s, %3.1f %s %3.1f%% %s\n\n",_("Summary"), total_wakeups(), _("wakeups/second"), total_gpu_ops(), _("GPU ops/seconds"), total_disk_hits(), _("VFS ops/sec and"), total_cpu_time()*100, _("CPU use")); if (show_power) wprintw(win, "%s %s %s %s %s\n", _("Power est."), _("Usage"), _("Events/s"), _("Category"), _("Description")); else wprintw(win, " %s %s %s %s\n", _("Usage"), _("Events/s"), _("Category"), _("Description")); for (i = 0; i < all_power.size(); i++) { char power[16]; char name[20]; char usage[20]; char events[20]; char descr[128]; format_watts(all_power[i]->Witts(), power, 10); if (!show_power) strcpy(power, " "); snprintf(name, sizeof(name), "%s", all_power[i]->type()); align_string(name, 14, 20); if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 && all_power[i]->Witts() == 0) break; usage[0] = 0; if (all_power[i]->usage_units()) { if (all_power[i]->usage() < 1000) snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units()); else snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units()); } align_string(usage, 14, 20); snprintf(events, sizeof(events), "%5.1f", all_power[i]->events()); if (!all_power[i]->show_events()) events[0] = 0; else if (all_power[i]->events() <= 0.3) snprintf(events, sizeof(events), "%5.2f", all_power[i]->events()); align_string(events, 12, 20); wprintw(win, "%s %s %s %s %s\n", power, usage, events, name, pretty_print(all_power[i]->description(), descr, 128)); } } void report_process_update_display(void) { unsigned int i; unsigned int total; int show_power, cols, rows, idx; /* div attr css_class and css_id */ tag_attr div_attr; init_div(&div_attr, "clear_block", "software"); /* Set Table attributes, rows, and cols */ cols=7; sort(all_power.begin(), all_power.end(), power_cpu_sort); show_power = global_power_valid(); if (show_power) cols=8; idx=cols; total = all_power.size(); if (total > 100) total = 100; rows=total+1; table_attributes std_table_css; init_nowarp_table_attr(&std_table_css, rows, cols); /* Set Title attributes */ tag_attr title_attr; init_title_attr(&title_attr); /* Set array of data in row Major order */ string *software_data = new string[cols * rows]; software_data[0]=__("Usage"); software_data[1]=__("Wakeups/s"); software_data[2]=__("GPU ops/s"); software_data[3]=__("Disk IO/s"); software_data[4]=__("GFX Wakeups/s"); software_data[5]=__("Category"); software_data[6]=__("Description"); if (show_power) software_data[7]=__("PW Estimate"); for (i = 0; i < total; i++) { char power[16]; char name[20]; char usage[20]; char wakes[20]; char gpus[20]; char disks[20]; char xwakes[20]; char descr[128]; format_watts(all_power[i]->Witts(), power, 10); if (!show_power) strcpy(power, " "); snprintf(name, sizeof(name), "%s", all_power[i]->type()); if (strcmp(name, "Device") == 0) continue; if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 && all_power[i]->Witts() == 0) break; usage[0] = 0; if (all_power[i]->usage_units()) { if (all_power[i]->usage() < 1000) snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units()); else snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units()); } snprintf(wakes, sizeof(wakes), "%5.1f", all_power[i]->wake_ups / measurement_time); if (all_power[i]->wake_ups / measurement_time <= 0.3) snprintf(wakes, sizeof(wakes), "%5.2f", all_power[i]->wake_ups / measurement_time); snprintf(gpus, sizeof(gpus), "%5.1f", all_power[i]->gpu_ops / measurement_time); snprintf(disks, sizeof(disks), "%5.1f (%5.1f)", all_power[i]->hard_disk_hits / measurement_time, all_power[i]->disk_hits / measurement_time); snprintf(xwakes, sizeof(xwakes), "%5.1f", all_power[i]->xwakes / measurement_time); if (!all_power[i]->show_events()) { wakes[0] = 0; gpus[0] = 0; disks[0] = 0; } if (all_power[i]->gpu_ops == 0) gpus[0] = 0; if (all_power[i]->wake_ups == 0) wakes[0] = 0; if (all_power[i]->disk_hits == 0) disks[0] = 0; if (all_power[i]->xwakes == 0) xwakes[0] = 0; software_data[idx]=string(usage); idx+=1; software_data[idx]=string(wakes); idx+=1; software_data[idx]=string(gpus); idx+=1; software_data[idx]=string(disks); idx+=1; software_data[idx]=string(xwakes); idx+=1; software_data[idx]=string(name); idx+=1; software_data[idx]=string(pretty_print(all_power[i]->description(), descr, 128)); idx+=1; if (show_power) { software_data[idx]=string(power); idx+=1; } } /* Report Output */ report.add_div(&div_attr); report.add_title(&title_attr, __("Overview of Software Power Consumers")); report.add_table(software_data, &std_table_css); report.end_div(); delete [] software_data; } void report_summary(void) { unsigned int i; unsigned int total; int show_power; int rows, cols, idx; sort(all_power.begin(), all_power.end(), power_cpu_sort); show_power = global_power_valid(); /* div attr css_class and css_id */ tag_attr div_attr; init_div(&div_attr, "clear_block", "summary"); /* Set table attributes, rows, and cols */ cols=4; if (show_power) cols=5; idx=cols; total = all_power.size(); if (total > 10) total = 10; rows=total+1; table_attributes std_table_css; init_std_table_attr(&std_table_css, rows, cols); /* Set title attributes */ tag_attr title_attr; init_title_attr(&title_attr); /* Set array for summary */ int summary_size =12; string *summary = new string [summary_size]; summary[0]=__("Target:"); summary[1]=__("1 units/s"); summary[2]=__("System: "); summary[3]= double_to_string(total_wakeups()); summary[3].append(__(" wakeup/s")); summary[4]=__("CPU: "); summary[5]= double_to_string(total_cpu_time()*100); summary[5].append(__("\% usage")); summary[6]=__("GPU:"); summary[7]=double_to_string(total_gpu_ops()); summary[7].append(__(" ops/s")); summary[8]=__("GFX:"); summary[9]=double_to_string(total_xwakes()); summary[9].append(__(" wakeups/s")); summary[10]=__("VFS:"); summary[11]= double_to_string(total_disk_hits()); summary[11].append(__(" ops/s")); /* Set array of data in row Major order */ string *summary_data = new string[cols * (rows + 1)]; summary_data[0]=__("Usage"); summary_data[1]=__("Events/s"); summary_data[2]=__("Category"); summary_data[3]=__("Description"); if (show_power) summary_data[4]=__("PW Estimate"); for (i = 0; i < all_power.size(); i++) { char power[16]; char name[20]; char usage[20]; char events[20]; char descr[128]; format_watts(all_power[i]->Witts(), power, 10); if (!show_power) strcpy(power, " "); snprintf(name, sizeof(name), "%s", all_power[i]->type()); if (i > total) break; if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 && all_power[i]->Witts() == 0) break; usage[0] = 0; if (all_power[i]->usage_units()) { if (all_power[i]->usage() < 1000) snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage_summary(), all_power[i]->usage_units_summary()); else snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage_summary(), all_power[i]->usage_units_summary()); } snprintf(events, sizeof(events), "%5.1f", all_power[i]->events()); if (!all_power[i]->show_events()) events[0] = 0; else if (all_power[i]->events() <= 0.3) snprintf(events, sizeof(events), "%5.2f", all_power[i]->events()); summary_data[idx]=string(usage); idx+=1; summary_data[idx]=string(events); idx+=1; summary_data[idx]=string(name); idx+=1; summary_data[idx]=string(pretty_print(all_power[i]->description(), descr, 128)); idx+=1; if (show_power){ summary_data[idx]=power; idx+=1; } } /* Report Summary for all */ report.add_summary_list(summary, summary_size); report.add_div(&div_attr); report.add_title(&title_attr, __("Top 10 Power Consumers")); report.add_table(summary_data, &std_table_css); report.end_div(); delete [] summary; delete [] summary_data; } void process_process_data(void) { if (!perf_events) return; clear_processes(); clear_interrupts(); all_power.erase(all_power.begin(), all_power.end()); clear_consumers(); cpu_credit.resize(0, 0); cpu_credit.resize(get_max_cpu()+1, 0); cpu_level.resize(0, 0); cpu_level.resize(get_max_cpu()+1, 0); cpu_blame.resize(0, NULL); cpu_blame.resize(get_max_cpu()+1, NULL); /* process data */ perf_events->process(); perf_events->clear(); run_devpower_list(); merge_processes(); all_processes_to_all_power(); all_interrupts_to_all_power(); all_timers_to_all_power(); all_work_to_all_power(); all_devices_to_all_power(); sort(all_power.begin(), all_power.end(), power_cpu_sort); } double total_cpu_time(void) { unsigned int i; double total = 0.0; for (i = 0; i < all_power.size() ; i++) { if (all_power[i]->child_runtime > all_power[i]->accumulated_runtime) all_power[i]->child_runtime = 0; total += all_power[i]->accumulated_runtime - all_power[i]->child_runtime; } total = (total / (0.0001 + last_stamp - first_stamp)); return total; } void end_process_data(void) { report_utilization("cpu-consumption", total_cpu_time()); report_utilization("cpu-wakeups", total_wakeups()); report_utilization("gpu-operations", total_gpu_ops()); report_utilization("disk-operations", total_disk_hits()); report_utilization("disk-operations-hard", total_hard_disk_hits()); report_utilization("xwakes", total_xwakes()); all_power.erase(all_power.begin(), all_power.end()); clear_processes(); clear_proc_devices(); clear_interrupts(); clear_timers(); clear_work(); clear_consumers(); perf_events->clear(); } void clear_process_data(void) { if (perf_events) perf_events->release(); delete perf_events; }