diff options
Diffstat (limited to 'src/process')
-rw-r--r-- | src/process/do_process.cpp | 1230 | ||||
-rw-r--r-- | src/process/interrupt.cpp | 131 | ||||
-rw-r--r-- | src/process/interrupt.h | 62 | ||||
-rw-r--r-- | src/process/powerconsumer.cpp | 100 | ||||
-rw-r--r-- | src/process/powerconsumer.h | 79 | ||||
-rw-r--r-- | src/process/process.cpp | 246 | ||||
-rw-r--r-- | src/process/process.h | 96 | ||||
-rw-r--r-- | src/process/processdevice.cpp | 99 | ||||
-rw-r--r-- | src/process/processdevice.h | 55 | ||||
-rw-r--r-- | src/process/timer.cpp | 159 | ||||
-rw-r--r-- | src/process/timer.h | 64 | ||||
-rw-r--r-- | src/process/work.cpp | 130 | ||||
-rw-r--r-- | src/process/work.h | 57 |
13 files changed, 2508 insertions, 0 deletions
diff --git a/src/process/do_process.cpp b/src/process/do_process.cpp new file mode 100644 index 0000000..be8d3bd --- /dev/null +++ b/src/process/do_process.cpp @@ -0,0 +1,1230 @@ +/* + * 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-data-html.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 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 energy 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; +} + diff --git a/src/process/interrupt.cpp b/src/process/interrupt.cpp new file mode 100644 index 0000000..a6553b1 --- /dev/null +++ b/src/process/interrupt.cpp @@ -0,0 +1,131 @@ +/* + * 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 <stdint.h> +#include <string.h> +#include <stdio.h> +#include "process.h" +#include "interrupt.h" +#include "../lib.h" + +const char* softirqs[] = { + "HI_SOFTIRQ", + "timer(softirq)", + "net tx(softirq)", + "net_rx(softirq)", + "block(softirq)", + "block_iopoll(softirq)", + "tasklet(softirq)", + "sched(softirq)", + "hrtimer(softirq)", + "RCU(softirq)", + NULL +}; + + +interrupt::interrupt(const char *_handler, int _number) : power_consumer() +{ + char buf[128]; + running_since = 0; + number = _number; + pt_strcpy(handler, _handler); + raw_count = 0; + snprintf(desc, sizeof(desc), "[%i] %s", number, pretty_print(handler, buf, 128)); +} + + +vector <class interrupt *> all_interrupts; + +void interrupt::start_interrupt(uint64_t time) +{ + running_since = time; + raw_count ++; +} + +uint64_t interrupt::end_interrupt(uint64_t time) +{ + uint64_t delta; + + delta = time - running_since; + accumulated_runtime += delta; + return delta; +} + +const char * interrupt::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + return desc; +} + +double interrupt::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * interrupt::usage_units_summary(void) +{ + return "%"; +} + + +class interrupt * find_create_interrupt(const char *_handler, int nr, int cpu) +{ + char handler[64]; + unsigned int i; + class interrupt *new_irq; + + pt_strcpy(handler, _handler); + if (strcmp(handler, "timer")==0) + sprintf(handler, "timer/%i", cpu); + + + for (i = 0; i < all_interrupts.size(); i++) { + if (all_interrupts[i] && all_interrupts[i]->number == nr && strcmp(handler, all_interrupts[i]->handler) == 0) + return all_interrupts[i]; + } + + new_irq = new class interrupt(handler, nr); + all_interrupts.push_back(new_irq); + return new_irq; +} + +void all_interrupts_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_interrupts.size() ; i++) + if (all_interrupts[i]->accumulated_runtime) + all_power.push_back(all_interrupts[i]); +} + +void clear_interrupts(void) +{ + std::vector<class interrupt *>::iterator it = all_interrupts.begin(); + while (it != all_interrupts.end()) { + delete *it; + it = all_interrupts.erase(it); + } +} diff --git a/src/process/interrupt.h b/src/process/interrupt.h new file mode 100644 index 0000000..8d3a4d7 --- /dev/null +++ b/src/process/interrupt.h @@ -0,0 +1,62 @@ +/* + * 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> + */ +#ifndef _INCLUDE_GUARD_INTERRUPT_H +#define _INCLUDE_GUARD_INTERRUPT_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class interrupt : public power_consumer { + uint64_t running_since; + char desc[256]; +public: + char handler[32]; + int number; + + int raw_count; + + interrupt(const char *_handler, int _number); + + virtual void start_interrupt(uint64_t time); + virtual uint64_t end_interrupt(uint64_t time); + + virtual const char * description(void); + + virtual const char * name(void) { return "interrupt"; }; + virtual const char * type(void) { return "Interrupt"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); +}; + +extern vector <class interrupt *> all_interrupts; +extern const char* softirqs[]; + + +extern class interrupt * find_create_interrupt(const char *_handler, int nr, int cpu); +extern void all_interrupts_to_all_power(void); +extern void clear_interrupts(void); + +#endif diff --git a/src/process/powerconsumer.cpp b/src/process/powerconsumer.cpp new file mode 100644 index 0000000..2ff2132 --- /dev/null +++ b/src/process/powerconsumer.cpp @@ -0,0 +1,100 @@ +/* + * 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 "powerconsumer.h" +#include "process.h" +#include "../parameters/parameters.h" + +double power_consumer::Witts(void) +{ + double cost; + double timecost; + double wakeupcost; + double gpucost; + double disk_cost; + double hard_disk_cost; + double xwake_cost; + + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + timecost = get_parameter_value("cpu-consumption"); + wakeupcost = get_parameter_value("cpu-wakeups"); + gpucost = get_parameter_value("gpu-operations"); + disk_cost = get_parameter_value("disk-operations"); + hard_disk_cost = get_parameter_value("disk-operations-hard"); + xwake_cost = get_parameter_value("xwakes"); + + cost = 0; + + cost += wakeupcost * wake_ups / 10000.0; + cost += ( (accumulated_runtime - child_runtime) / 1000000000.0) * timecost; + cost += gpucost * gpu_ops / 100.0; + cost += hard_disk_cost * hard_disk_hits / 100.0; + cost += disk_cost * disk_hits / 100.0; + cost += xwake_cost * xwakes / 100.0; + + cost = cost / measurement_time; + + cost += power_charge; + + return cost; +} + +power_consumer::power_consumer(void) +{ + accumulated_runtime = 0; + child_runtime = 0; + disk_hits = 0; + wake_ups = 0; + gpu_ops = 0; + hard_disk_hits = 0; + xwakes = 0; + waker = NULL; + last_waker = NULL; + power_charge = 0.0; +} + +double power_consumer::usage(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time; + if (t < 0.7) + t = t * 1000; + return t; +} + +const char * power_consumer::usage_units(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time; + if (t < 0.7) { + if (utf_ok) + return " µs/s"; + else + return " us/s"; + } + return " ms/s"; +} diff --git a/src/process/powerconsumer.h b/src/process/powerconsumer.h new file mode 100644 index 0000000..0ae384a --- /dev/null +++ b/src/process/powerconsumer.h @@ -0,0 +1,79 @@ +/* + * 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> + */ +#ifndef __INCLUDE_GUARD_POWER_CONSUMER_ +#define __INCLUDE_GUARD_POWER_CONSUMER_ + +#include <stdint.h> +#include <vector> +#include <algorithm> + +using namespace std; + +extern double measurement_time; + +class power_consumer; + +class power_consumer { + +public: + uint64_t accumulated_runtime; + uint64_t child_runtime; + int disk_hits; + int wake_ups; + int gpu_ops; + int hard_disk_hits; /* those which are likely a wakeup of the disk */ + int xwakes; + + double power_charge; /* power consumed by devices opened by this process */ + class power_consumer *waker; + class power_consumer *last_waker; + + power_consumer(void); + virtual ~power_consumer() {}; + + virtual double Witts(void); + virtual const char * description(void) { return ""; }; + + virtual const char * name(void) { return "abstract"; }; + virtual const char * type(void) { return "abstract"; }; + + virtual double usage(void); + virtual const char * usage_units(void); + + virtual double usage_summary(void) { return usage();}; + virtual const char * usage_units_summary(void) { return usage_units(); }; + virtual double events(void) { return (wake_ups + gpu_ops + hard_disk_hits) / measurement_time;}; + virtual int show_events(void) { return 1; }; +}; + +extern vector <class power_consumer *> all_power; + +extern double total_wakeups(void); +extern double total_cpu_time(void); +extern double total_gpu_ops(void); + + + +#endif
\ No newline at end of file diff --git a/src/process/process.cpp b/src/process/process.cpp new file mode 100644 index 0000000..dad90ba --- /dev/null +++ b/src/process/process.cpp @@ -0,0 +1,246 @@ +/* + * 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 <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +#include <iostream> +#include <fstream> +#include <algorithm> +#include <iterator> +#include "../lib.h" + + +vector <class process *> all_processes; + +void process::account_disk_dirty(void) +{ + disk_hits++; +} + +void process::schedule_thread(uint64_t time, int thread_id) +{ + running_since = time; + running = 1; +} + + +uint64_t process::deschedule_thread(uint64_t time, int thread_id) +{ + uint64_t delta; + + if (!running_since) + return 0; + + delta = time - running_since; + + if (time < running_since) + printf("%llu time %llu since \n", (unsigned long long)time, + (unsigned long long)running_since); + + if (thread_id == 0) /* idle thread */ + delta = 0; + accumulated_runtime += delta; + running = 0; + + return delta; +} + +static void cmdline_to_string(std::string& str) +{ + std::replace(str.begin(), str.end(), '\0', ' '); +} + + +process::process(const char *_comm, int _pid, int _tid) : power_consumer() +{ + char line[4097]; + ifstream file; + ssize_t pos; + + pt_strcpy(comm, _comm); + pid = _pid; + is_idle = 0; + running = 0; + last_waker = NULL; + waker = NULL; + is_kernel = 0; + tgid = _tid; + + if (_tid == 0) { + sprintf(line, "/proc/%i/status", _pid); + file.open(line); + while (file) { + file.getline(line, 4096); + line[4096] = '\0'; + if (strstr(line, "Tgid")) { + char *c; + c = strchr(line, ':'); + if (!c) + continue; + c++; + tgid = strtoull(c, NULL, 10); + break; + } + } + file.close(); + } + + if (strncmp(_comm, "kondemand/", 10) == 0) + is_idle = 1; + + pos = snprintf(desc, sizeof(desc), "[PID %d] ", pid); + + if (pos < 0) + pos = 0; + if ((size_t)pos > sizeof(desc)) + return; + + strncpy(desc + pos, comm, sizeof(desc) - pos - 1); + desc[sizeof(desc) - 1] = '\0'; + + sprintf(line, "/proc/%i/cmdline", _pid); + file.open(line, ios::binary); + if (file) { + std::string cmdline(std::istreambuf_iterator<char>(file), (std::istreambuf_iterator<char>())); + file.close(); + if (cmdline.size() < 1) { + is_kernel = 1; + snprintf(desc + pos, sizeof(desc) - pos, "[%s]", comm); + } else { + cmdline_to_string(cmdline); + strncpy(desc + pos, cmdline.c_str(), sizeof(desc) - pos - 1); + desc[sizeof(desc) - 1] = '\0'; + } + } +} + +const char * process::description(void) +{ + + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + return desc; +} + +double process::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * process::usage_units_summary(void) +{ + return "%"; +} + +class process * find_create_process(const char *comm, int pid) +{ + unsigned int i; + class process *new_proc; + + for (i = 0; i < all_processes.size(); i++) { + if (all_processes[i]->pid == pid && strcmp(comm, all_processes[i]->comm) == 0) + return all_processes[i]; + } + + new_proc = new class process(comm, pid); + all_processes.push_back(new_proc); + return new_proc; +} + +class process * find_create_process(char *comm, int pid) +{ + return find_create_process((const char*)comm, pid); +} + +static void merge_process(class process *one, class process *two) +{ + one->accumulated_runtime += two->accumulated_runtime; + one->child_runtime += two->child_runtime; + one->wake_ups += two->wake_ups; + one->disk_hits += two->disk_hits; + one->hard_disk_hits += two->hard_disk_hits; + one->xwakes += two->xwakes; + one->gpu_ops += two->gpu_ops; + one->power_charge += two->power_charge; +} + + +void merge_processes(void) +{ + std::vector<class process*>::iterator it1, it2; + class process *one, *two; + + it1 = all_processes.begin(); + while (it1 != all_processes.end()) { + it2 = it1 + 1; + one = *it1; + while (it2 != all_processes.end()) { + two = *it2; + /* fold threads */ + if (one->pid == two->tgid && two->tgid != 0) { + merge_process(one, two); + delete *it2; + it2 = all_processes.erase(it2); + continue; + } + /* find dupes and add up */ + if (!strcmp(one->desc, two->desc)) { + merge_process(one, two); + delete *it2; + it2 = all_processes.erase(it2); + continue; + } + ++it2; + } + ++it1; + } +} + +void all_processes_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_processes.size() ; i++) + if (all_processes[i]->accumulated_runtime || + all_processes[i]->power_charge) + all_power.push_back(all_processes[i]); +} + +void clear_processes(void) +{ + std::vector <class process *>::iterator it = all_processes.begin(); + while (it != all_processes.end()) { + delete *it; + it = all_processes.erase(it); + } +} diff --git a/src/process/process.h b/src/process/process.h new file mode 100644 index 0000000..66293f7 --- /dev/null +++ b/src/process/process.h @@ -0,0 +1,96 @@ +/* + * 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> + */ +#ifndef _INCLUDE_GUARD_PROCESS_H +#define _INCLUDE_GUARD_PROCESS_H + +#include <stdint.h> + +#include "powerconsumer.h" + +#ifdef __x86_64__ +#define BIT64 1 +#endif + +/* +Need to collect + * CPU time consumed by each application + * Number of wakeups out of idle -- received and wakeups sent + * Number of disk dirties (inode for now) + */ +class process : public power_consumer { + uint64_t running_since; +public: + char desc[256]; + int tgid; + char comm[16]; + int pid; + + + int is_idle; /* count this as if the cpu was idle */ + int running; + int is_kernel; /* kernel thread */ + + process(const char *_comm, int _pid, int _tid = 0); + + virtual void schedule_thread(uint64_t time, int thread_id); + virtual uint64_t deschedule_thread(uint64_t time, int thread_id = 0); + + virtual void account_disk_dirty(void); + + virtual const char * description(void); + virtual const char * name(void) { return "process"; }; + virtual const char * type(void) { return "Process"; }; + + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + +extern vector <class process *> all_processes; + +extern double measurement_time; + + +extern void start_process_measurement(void); +extern void end_process_measurement(void); +extern void process_process_data(void); +extern void end_process_data(void); +extern void clear_process_data(void); +extern void merge_processes(void); + +extern class process * find_create_process(const char *comm, int pid); +extern class process * find_create_process(char *comm, int pid); +extern void all_processes_to_all_power(void); + +extern void clear_processes(void); +extern void process_update_display(void); +extern void report_process_update_display(void); +extern void report_summary(void); + + + +extern void clear_timers(void); + +#endif diff --git a/src/process/processdevice.cpp b/src/process/processdevice.cpp new file mode 100644 index 0000000..00f48c8 --- /dev/null +++ b/src/process/processdevice.cpp @@ -0,0 +1,99 @@ +/* + * 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 "processdevice.h" +#include "../parameters/parameters.h" +#include <stdio.h> + +vector<class device_consumer *> all_proc_devices; + + +device_consumer::device_consumer(class device *dev) : power_consumer() +{ + device = dev; + power = device->power_usage(&all_results, &all_parameters); + prio = dev->grouping_prio(); +} + + +const char * device_consumer::description(void) +{ + snprintf(str, sizeof(str), "%s", device->human_name()); + return str; +} + +double device_consumer::Witts(void) +{ + return power; +} + +static void add_device(class device *device) +{ + class device_consumer *dev; + unsigned int i; + + /* first check if we want to be shown at all */ + + if (device->show_in_list() == 0) + return; + + /* then check if a device with the same underlying object is already registered */ + for (i = 0; i < all_proc_devices.size(); i++) { + class device_consumer *cdev; + cdev = all_proc_devices[i]; + if (device->real_path[0] != 0 && strcmp(cdev->device->real_path, device->real_path) == 0) { + /* we have a device with the same underlying object */ + + /* aggregate the power */ + cdev->power += device->power_usage(&all_results, &all_parameters); + + if (cdev->prio < device->grouping_prio()) { + cdev->device = device; + cdev->prio = device->grouping_prio(); + } + + return; + } + } + + dev = new class device_consumer(device); + all_power.push_back(dev); + all_proc_devices.push_back(dev); +} + +void all_devices_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) + add_device(all_devices[i]); +} + +void clear_proc_devices(void) +{ + std::vector<class device_consumer *>::iterator it = all_proc_devices.begin(); + while (it != all_proc_devices.end()) { + delete *it; + it = all_proc_devices.erase(it); + } +} diff --git a/src/process/processdevice.h b/src/process/processdevice.h new file mode 100644 index 0000000..3392d3e --- /dev/null +++ b/src/process/processdevice.h @@ -0,0 +1,55 @@ +/* + * 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> + */ +#ifndef _INCLUDE_GUARD_DEVICE2_H +#define _INCLUDE_GUARD_DEVICE2_H + +#include <stdint.h> + +#include "powerconsumer.h" +#include "../devices/device.h" + +class device_consumer : public power_consumer { + char str[4096]; +public: + int prio; + double power; + class device *device; + device_consumer(class device *dev); + + virtual const char * description(void); + virtual const char * name(void) { return "device"; }; + virtual const char * type(void) { return "Device"; }; + virtual double Witts(void); + virtual double usage(void) { return device->utilization();}; + virtual const char * usage_units(void) {return device->util_units();}; + virtual int show_events(void) { return 0; }; +}; + +extern void all_devices_to_all_power(void); +extern vector<class device_consumer *> all_proc_devices; + +extern void clear_proc_devices(void); + +#endif diff --git a/src/process/timer.cpp b/src/process/timer.cpp new file mode 100644 index 0000000..5d9d2f8 --- /dev/null +++ b/src/process/timer.cpp @@ -0,0 +1,159 @@ +/* + * 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 <map> +#include <utility> + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "timer.h" +#include "../lib.h" +#include "process.h" + +using namespace std; + +static bool timer_is_deferred(const char *handler) +{ + FILE *file; + bool ret = false; + char line[4096]; + + file = fopen("/proc/timer_stats", "r"); + if (!file) { + return ret; + } + + while (!feof(file)) { + if (fgets(line, 4096, file) == NULL) + break; + if (strstr(line, handler)) { + ret = (strstr(line, "D,") != NULL); + if (ret == true) + break; + } + } + fclose(file); + return ret; +} + +timer::timer(unsigned long address) : power_consumer() +{ + pt_strcpy(handler, kernel_function(address)); + raw_count = 0; + deferred = timer_is_deferred(handler); +} + + +static map<unsigned long, class timer *> all_timers; +static map<unsigned long, uint64_t> running_since; + +void timer::fire(uint64_t time, uint64_t timer_struct) +{ + running_since[timer_struct] = time; +} + +uint64_t timer::done(uint64_t time, uint64_t timer_struct) +{ + int64_t delta; + + if (running_since.find(timer_struct) == running_since.end()) + return ~0ULL; + + if (running_since[timer_struct] > time) + return 0; + + delta = time - running_since[timer_struct]; + + accumulated_runtime += delta; + + raw_count++; + + return delta; +} + +double timer::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * timer::usage_units_summary(void) +{ + return "%"; +} + + + +static void add_timer(const pair<unsigned long, class timer*>& elem) +{ + all_power.push_back(elem.second); +} + +void all_timers_to_all_power(void) +{ + for_each(all_timers.begin(), all_timers.end(), add_timer); + +} + + +const char * timer::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + snprintf(desc, sizeof(desc), "%s", handler); + return desc; +} + + +class timer * find_create_timer(uint64_t func) +{ + class timer * timer; + if (all_timers.find(func) != all_timers.end()) + return all_timers[func]; + + timer = new class timer(func); + all_timers[func] = timer; + return timer; + +} + +void clear_timers(void) +{ + std::map<unsigned long, class timer *>::iterator it = all_timers.begin(); + while (it != all_timers.end()) { + delete it->second; + all_timers.erase(it); + it = all_timers.begin(); + } + running_since.clear(); +} + +bool timer::is_deferred(void) +{ + return deferred; +} diff --git a/src/process/timer.h b/src/process/timer.h new file mode 100644 index 0000000..7718c3b --- /dev/null +++ b/src/process/timer.h @@ -0,0 +1,64 @@ +/* + * 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> + */ +#ifndef _INCLUDE_GUARD_TIMER_H +#define _INCLUDE_GUARD_TIMER_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class timer : public power_consumer { + char desc[256]; +public: + char handler[32]; + int raw_count; + bool deferred; + + timer(unsigned long timer_func); + + void fire(uint64_t time, uint64_t timer_struct); + uint64_t done(uint64_t time, uint64_t timer_struct); + bool is_deferred(void); + + virtual const char * description(void); + virtual const char * name(void) { return "timer"; }; + virtual const char * type(void) { return "Timer"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + +class timer_list { +public: + uint64_t timer_address; + uint64_t timer_func; +}; + + +extern void all_timers_to_all_power(void); +extern class timer * find_create_timer(uint64_t func); +extern void clear_timers(void); + +#endif diff --git a/src/process/work.cpp b/src/process/work.cpp new file mode 100644 index 0000000..1b5c71d --- /dev/null +++ b/src/process/work.cpp @@ -0,0 +1,130 @@ +/* + * 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 <map> +#include <utility> + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "work.h" +#include "../lib.h" +#include "process.h" + +using namespace std; + + +work::work(unsigned long address) : power_consumer() +{ + pt_strcpy(handler, kernel_function(address)); + raw_count = 0; + snprintf(desc, sizeof(desc), "%s", handler); +} + + +static map<unsigned long, class work *> all_work; +static map<unsigned long, uint64_t> running_since; + +void work::fire(uint64_t time, uint64_t work_struct) +{ + running_since[work_struct] = time; +} + +uint64_t work::done(uint64_t time, uint64_t work_struct) +{ + int64_t delta; + + if (running_since.find(work_struct) == running_since.end()) + return ~0ULL; + + if (running_since[work_struct] > time) + return 0; + + delta = time - running_since[work_struct]; + + accumulated_runtime += delta; + + raw_count++; + + return delta; +} + +double work::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * work::usage_units_summary(void) +{ + return "%"; +} + + + + +static void add_work(const pair<unsigned long, class work*>& elem) +{ + all_power.push_back(elem.second); +} + +void all_work_to_all_power(void) +{ + for_each(all_work.begin(), all_work.end(), add_work); + +} + +void clear_work(void) +{ + std::map<unsigned long, class work *>::iterator it = all_work.begin(); + while (it != all_work.end()) { + delete it->second; + all_work.erase(it); + it = all_work.begin(); + } + running_since.clear(); +} + + +const char * work::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + return desc; +} + + +class work * find_create_work(uint64_t func) +{ + class work * work; + if (all_work.find(func) != all_work.end()) + return all_work[func]; + + work = new class work(func); + all_work[func] = work; + return work; +} diff --git a/src/process/work.h b/src/process/work.h new file mode 100644 index 0000000..ddd7c87 --- /dev/null +++ b/src/process/work.h @@ -0,0 +1,57 @@ +/* + * 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> + */ +#ifndef _INCLUDE_GUARD_WORK_H +#define _INCLUDE_GUARD_WORK_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class work : public power_consumer { + char desc[256]; +public: + char handler[32]; + int raw_count; + + work(unsigned long work_func); + + void fire(uint64_t time, uint64_t work_struct); + uint64_t done(uint64_t time, uint64_t work_struct); + + virtual const char * description(void); + virtual const char * name(void) { return "work"; }; + virtual const char * type(void) { return "kWork"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + + +extern void all_work_to_all_power(void); +extern class work * find_create_work(uint64_t func); + +extern void clear_work(void); + +#endif |