blob: c81b0f60fb88c169ab2c221c744a267e9bb82051 [file] [log] [blame]
/*
* 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 <arjan@linux.intel.com>
*/
#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-maker.h"
#include "../devlist.h"
#include <vector>
#include <algorithm>
#include <stack>
#include <stdio.h>
#include <string.h>
#include <ncurses.h>
#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 <class power_consumer *> all_power;
vector< vector<class power_consumer *> > cpu_stack;
vector<int> cpu_level;
vector<int> cpu_credit;
vector<class power_consumer *> 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 void dbg_printf_pevent_info(struct event_format *event, struct pevent_record *rec)
{
static struct trace_seq s;
event->pevent->print_raw = 1;
trace_seq_init(&s);
pevent_event_info(&s, event, rec);
trace_seq_putc(&s, '\n');
trace_seq_terminate(&s);
fprintf(stderr, "%.*s", s.len, s.buffer);
trace_seq_destroy(&s);
}
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) {
ret = pevent_get_field_val(NULL, event, "state", &rec, &val, 0);
if (val == 4294967295)
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;
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_joules_consumed(), sum);
#endif
pw = global_joules_consumed();
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));
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, " ");
sprintf(name, "%s", all_power[i]->type());
while (mbstowcs(NULL,name,20) < 14) strcat(name, " ");
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)
sprintf(usage, "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units());
else
sprintf(usage, "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units());
}
while (mbstowcs(NULL,usage,20) < 14) strcat(usage, " ");
sprintf(events, "%5.1f", all_power[i]->events());
if (!all_power[i]->show_events())
events[0] = 0;
else if (all_power[i]->events() <= 0.3)
sprintf(events, "%5.2f", all_power[i]->events());
while (strlen(events) < 12) strcat(events, " ");
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;
sort(all_power.begin(), all_power.end(), power_cpu_sort);
show_power = global_power_valid();
report.begin_section(SECTION_SOFTWARE);
report.add_header(__("Overview of Software Power Consumers"));
report.begin_table(TABLE_WIDE);
report.begin_row();
if (show_power) {
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("Power est."));
}
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("Usage"));
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("Wakeups/s"));
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("GPU ops/s"));
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("Disk IO/s"));
report.begin_cell(CELL_SOFTWARE_HEADER);
report.add(__("GFX Wakeups/s"));
report.begin_cell(CELL_SOFTWARE_PROCESS);
report.add(__("Category"));
report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
report.add(__("Description"));
total = all_power.size();
if (total > 100)
total = 100;
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, " ");
sprintf(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)
sprintf(usage, "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units());
else
sprintf(usage, "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units());
}
sprintf(wakes, "%5.1f", all_power[i]->wake_ups / measurement_time);
if (all_power[i]->wake_ups / measurement_time <= 0.3)
sprintf(wakes, "%5.2f", all_power[i]->wake_ups / measurement_time);
sprintf(gpus, "%5.1f", all_power[i]->gpu_ops / measurement_time);
sprintf(disks, "%5.1f (%5.1f)", all_power[i]->hard_disk_hits / measurement_time,
all_power[i]->disk_hits / measurement_time);
sprintf(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;
report.begin_row(ROW_SOFTWARE);
if (show_power) {
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(power);
}
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(usage);
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(wakes);
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(gpus);
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(disks);
report.begin_cell(CELL_SOFTWARE_POWER);
report.add(xwakes);
report.begin_cell();
report.add(name);
report.begin_cell();
report.add(pretty_print(all_power[i]->description(), descr, 128));
}
}
void report_summary(void)
{
unsigned int i;
unsigned int total;
int show_power;
sort(all_power.begin(), all_power.end(), power_cpu_sort);
show_power = global_power_valid();
report.begin_section(SECTION_SUMMARY);
report.add_header(__("Power Consumption Summary"));
report.begin_paragraph();
report.addf("%.1f %s, %.1f %s, %.1f %s, %.1f %s %.1f%% %s",
total_wakeups(), __("wakeups/second"),
total_gpu_ops(), __("GPU ops/second"),
total_disk_hits(), __("VFS ops/sec"),
total_xwakes(), __("GFX wakes/sec and"),
total_cpu_time() * 100, __("CPU use"));
report.begin_table(TABLE_WIDE);
report.begin_row();
if (show_power) {
report.begin_cell(CELL_SUMMARY_HEADER);
report.add(__("Power est."));
}
report.begin_cell(CELL_SUMMARY_HEADER);
report.add(__("Usage"));
report.begin_cell(CELL_SUMMARY_HEADER);
report.add(__("Events/s"));
report.begin_cell(CELL_SUMMARY_CATEGORY);
report.add(__("Category"));
report.begin_cell(CELL_SUMMARY_DESCRIPTION);
report.add(__("Description"));
total = all_power.size();
if (total > 10)
total = 10;
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, " ");
sprintf(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)
sprintf(usage, "%5.1f%s", all_power[i]->usage_summary(),
all_power[i]->usage_units_summary());
else
sprintf(usage, "%5i%s", (int)all_power[i]->usage_summary(),
all_power[i]->usage_units_summary());
}
sprintf(events, "%5.1f", all_power[i]->events());
if (!all_power[i]->show_events())
events[0] = 0;
else if (all_power[i]->events() <= 0.3)
sprintf(events, "%5.2f", all_power[i]->events());
report.begin_row(ROW_SUMMARY);
if (show_power) {
report.begin_cell(CELL_SUMMARY_ITEM);
report.add(power);
}
report.begin_cell(CELL_SUMMARY_ITEM);
report.add(usage);
report.begin_cell(CELL_SUMMARY_ITEM);
report.add(events);
report.begin_cell();
report.add(name);
report.begin_cell();
report.add(pretty_print(all_power[i]->description(), descr, 128));
}
}
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;
}