blob: 7f3af69d4d4bcbe75e03949885a5b4c6b0ea0d33 [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 <iostream>
#include <fstream>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
#include "cpu.h"
#include "cpudevice.h"
#include "../parameters/parameters.h"
#include "../perf/perf_bundle.h"
#include "../lib.h"
#include "../display.h"
#include "../report/report.h"
#include "../report/report-maker.h"
static class abstract_cpu system_level;
vector<class abstract_cpu *> all_cpus;
static class perf_bundle * perf_events;
class perf_power_bundle: public perf_bundle
{
virtual void handle_trace_point(void *trace, int cpu, uint64_t time);
};
static class abstract_cpu * new_package(int package, int cpu, char * vendor, int family, int model)
{
class abstract_cpu *ret = NULL;
class cpudevice *cpudev;
char packagename[128];
if (strcmp(vendor, "GenuineIntel") == 0) {
if (family == 6)
switch (model) {
case 0x1A: /* Core i7, Xeon 5500 series */
case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case 0x2E: /* Nehalem-EX Xeon */
case 0x2F: /* Westmere-EX Xeon */
case 0x25: /* Westmere */
case 0x27: /* Medfield Atom*/
case 0x2C: /* Westmere */
ret = new class nhm_package;
break;
case 0x2A: /* SNB */
case 0x2D: /* SNB Xeon */
case 0x3A: /* IVB */
case 0x3C:
case 0x3D: /* IVB Xeon */
has_c2c7_res = 1;
ret = new class nhm_package;
break;
}
}
if (!ret)
ret = new class cpu_package;
ret->set_number(package, cpu);
ret->set_type("Package");
ret->childcount = 0;
sprintf(packagename, _("cpu package %i"), cpu);
cpudev = new class cpudevice(_("cpu package"), packagename, ret);
all_devices.push_back(cpudev);
return ret;
}
static class abstract_cpu * new_core(int core, int cpu, char * vendor, int family, int model)
{
class abstract_cpu *ret = NULL;
if (strcmp(vendor, "GenuineIntel") == 0) {
if (family == 6)
switch (model) {
case 0x1A: /* Core i7, Xeon 5500 series */
case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case 0x2E: /* Nehalem-EX Xeon */
case 0x2F: /* Westmere-EX Xeon */
case 0x25: /* Westmere */
case 0x2C: /* Westmere */
case 0x2A: /* SNB */
case 0x2D: /* SNB Xeon */
case 0x3A: /* IVB */
case 0x3C:
case 0x3D: /* IVB Xeon */
ret = new class nhm_core;
}
}
if (!ret)
ret = new class cpu_core;
ret->set_number(core, cpu);
ret->childcount = 0;
ret->set_type("Core");
return ret;
}
static class abstract_cpu * new_i965_gpu(void)
{
class abstract_cpu *ret = NULL;
ret = new class i965_core;
ret->childcount = 0;
ret->set_type("GPU");
return ret;
}
static class abstract_cpu * new_cpu(int number, char * vendor, int family, int model)
{
class abstract_cpu * ret = NULL;
if (strcmp(vendor, "GenuineIntel") == 0) {
if (family == 6)
switch (model) {
case 0x1A: /* Core i7, Xeon 5500 series */
case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case 0x2E: /* Nehalem-EX Xeon */
case 0x2F: /* Westmere-EX Xeon */
case 0x25: /* Westmere */
case 0x2C: /* Westmere */
case 0x2A: /* SNB */
case 0x2D: /* SNB Xeon */
case 0x3A: /* IVB */
case 0x3C:
case 0x3D: /* IVB Xeon */
ret = new class nhm_cpu;
}
}
if (!ret)
ret = new class cpu_linux;
ret->set_number(number, number);
ret->set_type("CPU");
ret->childcount = 0;
return ret;
}
static void handle_one_cpu(unsigned int number, char *vendor, int family, int model)
{
char filename[1024];
ifstream file;
unsigned int package_number = 0;
unsigned int core_number = 0;
class abstract_cpu *package, *core, *cpu;
sprintf(filename, "/sys/devices/system/cpu/cpu%i/topology/core_id", number);
file.open(filename, ios::in);
if (file) {
file >> core_number;
file.close();
}
sprintf(filename, "/sys/devices/system/cpu/cpu%i/topology/physical_package_id", number);
file.open(filename, ios::in);
if (file) {
file >> package_number;
if (package_number == (unsigned int) -1)
package_number = 0;
file.close();
}
if (system_level.children.size() <= package_number)
system_level.children.resize(package_number + 1, NULL);
if (!system_level.children[package_number]) {
system_level.children[package_number] = new_package(package_number, number, vendor, family, model);
system_level.childcount++;
}
package = system_level.children[package_number];
package->parent = &system_level;
if (package->children.size() <= core_number)
package->children.resize(core_number + 1, NULL);
if (!package->children[core_number]) {
package->children[core_number] = new_core(core_number, number, vendor, family, model);
package->childcount++;
}
core = package->children[core_number];
core->parent = package;
if (core->children.size() <= number)
core->children.resize(number + 1, NULL);
if (!core->children[number]) {
core->children[number] = new_cpu(number, vendor, family, model);
core->childcount++;
}
cpu = core->children[number];
cpu->parent = core;
if (number >= all_cpus.size())
all_cpus.resize(number + 1, NULL);
all_cpus[number] = cpu;
}
static void handle_i965_gpu(void)
{
unsigned int core_number = 0;
class abstract_cpu *package;
package = system_level.children[0];
core_number = package->children.size();
if (package->children.size() <= core_number)
package->children.resize(core_number + 1, NULL);
if (!package->children[core_number]) {
package->children[core_number] = new_i965_gpu();
package->childcount++;
}
}
void enumerate_cpus(void)
{
ifstream file;
char line[1024];
int number = -1;
char vendor[128];
int family = 0;
int model = 0;
file.open("/proc/cpuinfo", ios::in);
if (!file)
return;
/* Not all /proc/cpuinfo include "vendor_id\t". */
vendor[0] = '\0';
while (file) {
file.getline(line, sizeof(line));
if (strncmp(line, "vendor_id\t",10) == 0) {
char *c;
c = strchr(line, ':');
if (c) {
c++;
if (*c == ' ')
c++;
strncpy(vendor,c, 127);
}
}
if (strncmp(line, "processor\t",10) == 0) {
char *c;
c = strchr(line, ':');
if (c) {
c++;
number = strtoull(c, NULL, 10);
}
}
if (strncmp(line, "cpu family\t",11) == 0) {
char *c;
c = strchr(line, ':');
if (c) {
c++;
family = strtoull(c, NULL, 10);
}
}
if (strncmp(line, "model\t",6) == 0) {
char *c;
c = strchr(line, ':');
if (c) {
c++;
model = strtoull(c, NULL, 10);
}
}
if (strncasecmp(line, "bogomips\t", 9) == 0) {
if (number == -1) {
/* Not all /proc/cpuinfo include "processor\t". */
number = 0;
}
if (number >= 0) {
handle_one_cpu(number, vendor, family, model);
set_max_cpu(number);
number = -2;
}
}
}
file.close();
if (access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK) == 0)
handle_i965_gpu();
perf_events = new perf_power_bundle();
if (!perf_events->add_event("power:cpu_idle")){
perf_events->add_event("power:power_start");
perf_events->add_event("power:power_end");
}
if (!perf_events->add_event("power:cpu_frequency"))
perf_events->add_event("power:power_frequency");
}
void start_cpu_measurement(void)
{
perf_events->start();
system_level.measurement_start();
}
void end_cpu_measurement(void)
{
system_level.measurement_end();
perf_events->stop();
}
static void expand_string(char *string, unsigned int newlen)
{
while (strlen(string) < newlen)
strcat(string, " ");
}
static int has_state_level(class abstract_cpu *acpu, int state, int line)
{
switch (state) {
case PSTATE:
return acpu->has_pstate_level(line);
break;
case CSTATE:
return acpu->has_cstate_level(line);
break;
}
return 0;
}
static const char * fill_state_name(class abstract_cpu *acpu, int state, int line, char *buf)
{
switch (state) {
case PSTATE:
return acpu->fill_pstate_name(line, buf);
break;
case CSTATE:
return acpu->fill_cstate_name(line, buf);
break;
}
return "-EINVAL";
}
static const char * fill_state_line(class abstract_cpu *acpu, int state, int line,
char *buf, const char *sep = "")
{
switch (state) {
case PSTATE:
return acpu->fill_pstate_line(line, buf);
break;
case CSTATE:
return acpu->fill_cstate_line(line, buf, sep);
break;
}
return "-EINVAL";
}
void report_display_cpu_cstates(void)
{
char buffer[512], buffer2[512];
unsigned int package, core, cpu;
int line;
class abstract_cpu *_package, * _core, * _cpu;
report.begin_section(SECTION_CPUIDLE);
report.add_header("Processor Idle state report");
for (package = 0; package < system_level.children.size(); package++) {
bool first_core = true;
_package = system_level.children[package];
if (!_package)
continue;
report.begin_table(TABLE_WIDE);
for (core = 0; core < _package->children.size(); core++) {
_core = _package->children[core];
if (!_core)
continue;
for (line = LEVEL_HEADER; line < 10; line++) {
bool first_cpu = true;
if (!_package->has_cstate_level(line))
continue;
report.begin_row();
buffer[0] = 0;
buffer2[0] = 0;
if (line == LEVEL_HEADER) {
if (first_core) {
report.begin_cell(CELL_FIRST_PACKAGE_HEADER);
report.addf(__("Package %i"), _package->get_number());
} else {
report.begin_cell(CELL_EMPTY_PACKAGE_HEADER);
report.add_empty_cell();
}
} else if (first_core) {
report.begin_cell(CELL_STATE_NAME);
report.add(_package->fill_cstate_name(line, buffer));
report.begin_cell(CELL_PACKAGE_STATE_VALUE);
report.add(_package->fill_cstate_line(line, buffer2));
} else {
report.begin_cell(CELL_EMPTY_PACKAGE_STATE);
report.add_empty_cell();
}
report.begin_cell(CELL_SEPARATOR);
report.add_empty_cell();
if (!_core->can_collapse()) {
buffer[0] = 0;
buffer2[0] = 0;
if (line == LEVEL_HEADER) {
report.begin_cell(CELL_CORE_HEADER);
/* Here we need to check for which core type we
* are using. Do not use the core type for the
* report.addf as it breaks an important macro use
* for translation decision making for the reports.
*/
const char* core_type = _core->get_type();
if (core_type != NULL) {
if (strcmp(core_type, "Core") == 0 ) {
report.addf(__("Core %i"), _core->get_number());
} else {
report.addf(__("GPU %i"), _core->get_number());
}
}
} else {
report.begin_cell(CELL_STATE_NAME);
report.add(_core->fill_cstate_name(line, buffer));
report.begin_cell(CELL_CORE_STATE_VALUE);
report.add(_core->fill_cstate_line(line, buffer2));
}
}
report.begin_cell(CELL_SEPARATOR);
report.add_empty_cell();
for (cpu = 0; cpu < _core->children.size(); cpu++) {
_cpu = _core->children[cpu];
if (!_cpu)
continue;
report.set_cpu_number(cpu);
if (line == LEVEL_HEADER) {
report.begin_cell(CELL_CPU_CSTATE_HEADER);
report.addf(__("CPU %i"), _cpu->get_number());
continue;
}
if (first_cpu) {
report.begin_cell(CELL_STATE_NAME);
report.add(_cpu->fill_cstate_name(line, buffer));
first_cpu = false;
}
buffer[0] = 0;
report.begin_cell(CELL_CPU_STATE_VALUE);
report.add(_cpu->fill_cstate_percentage(line, buffer));
report.begin_cell(CELL_CPU_STATE_VALUE);
if (line != LEVEL_C0)
report.add(_cpu->fill_cstate_time(line, buffer));
else
report.add_empty_cell();
}
}
first_core = false;
}
}
}
void report_display_cpu_pstates(void)
{
char buffer[512], buffer2[512];
unsigned int package, core, cpu;
int line;
class abstract_cpu *_package, * _core, * _cpu;
unsigned int i, pstates_num;
for (i = 0, pstates_num = 0; i < all_cpus.size(); i++)
if (all_cpus[i] && all_cpus[i]->pstates.size() > pstates_num)
pstates_num = all_cpus[i]->pstates.size();
report.begin_section(SECTION_CPUFREQ);
report.add_header("Processor Frequency Report");
for (package = 0; package < system_level.children.size(); package++) {
bool first_core = true;
_package = system_level.children[package];
if (!_package)
continue;
report.begin_table(TABLE_WIDE);
for (core = 0; core < _package->children.size(); core++) {
_core = _package->children[core];
if (!_core)
continue;
if (!_core->has_pstates())
continue;
for (line = LEVEL_HEADER; line < (int)pstates_num; line++) {
bool first_cpu = true;
if (!_package->has_pstate_level(line))
continue;
report.begin_row();
buffer[0] = 0;
buffer2[0] = 0;
if (first_core) {
if (line == LEVEL_HEADER) {
report.begin_cell(CELL_FIRST_PACKAGE_HEADER);
report.addf(__("Package %i"), _package->get_number());
} else {
report.begin_cell(CELL_STATE_NAME);
report.add(_package->fill_pstate_name(line, buffer));
report.begin_cell(CELL_PACKAGE_STATE_VALUE);
report.add(_package->fill_pstate_line(line, buffer2));
}
} else {
report.begin_cell(CELL_EMPTY_PACKAGE_STATE);
report.add_empty_cell();
}
report.begin_cell(CELL_SEPARATOR);
report.add_empty_cell();
if (!_core->can_collapse()) {
buffer[0] = 0;
buffer2[0] = 0;
if (line == LEVEL_HEADER) {
report.begin_cell(CELL_CORE_HEADER);
report.addf(__("Core %i"), _core->get_number());
} else {
report.begin_cell(CELL_STATE_NAME);
report.add(_core->fill_pstate_name(line, buffer));
report.begin_cell(CELL_PACKAGE_STATE_VALUE);
report.add(_core->fill_pstate_line(line, buffer2));
}
}
report.begin_cell(CELL_SEPARATOR);
report.add_empty_cell();
for (cpu = 0; cpu < _core->children.size(); cpu++) {
buffer[0] = 0;
_cpu = _core->children[cpu];
if (!_cpu)
continue;
report.set_cpu_number(cpu);
if (line == LEVEL_HEADER) {
report.begin_cell(CELL_CPU_PSTATE_HEADER);
report.addf(__("CPU %i"), _cpu->get_number());
continue;
}
if (first_cpu) {
report.begin_cell(CELL_STATE_NAME);
report.add(_cpu->fill_pstate_name(line, buffer));
first_cpu = false;
}
buffer[0] = 0;
report.begin_cell(CELL_CPU_STATE_VALUE);
report.add(_cpu->fill_pstate_line(line, buffer));
}
}
first_core = false;
}
}
}
void impl_w_display_cpu_states(int state)
{
WINDOW *win;
char buffer[128];
char linebuf[1024];
unsigned int package, core, cpu;
int line;
class abstract_cpu *_package, * _core, * _cpu;
int ctr = 0;
if (state == PSTATE)
win = get_ncurses_win("Frequency stats");
else
win = get_ncurses_win("Idle stats");
if (!win)
return;
wclear(win);
wmove(win, 2,0);
for (package = 0; package < system_level.children.size(); package++) {
int first_pkg = 0;
_package = system_level.children[package];
if (!_package)
continue;
for (core = 0; core < _package->children.size(); core++) {
_core = _package->children[core];
if (!_core)
continue;
if (!_core->has_pstates() && state == PSTATE)
continue;
for (line = LEVEL_HEADER; line < 10; line++) {
int first = 1;
ctr = 0;
linebuf[0] = 0;
if (!has_state_level(_package, state, line))
continue;
buffer[0] = 0;
if (first_pkg == 0) {
strcat(linebuf, fill_state_name(_package, state, line, buffer));
expand_string(linebuf, ctr + 10);
strcat(linebuf, fill_state_line(_package, state, line, buffer));
}
ctr += 20;
expand_string(linebuf, ctr);
strcat(linebuf, "| ");
ctr += strlen("| ");
if (!_core->can_collapse()) {
buffer[0] = 0;
strcat(linebuf, fill_state_name(_core, state, line, buffer));
expand_string(linebuf, ctr + 10);
strcat(linebuf, fill_state_line(_core, state, line, buffer));
ctr += 20;
expand_string(linebuf, ctr);
strcat(linebuf, "| ");
ctr += strlen("| ");
}
for (cpu = 0; cpu < _core->children.size(); cpu++) {
_cpu = _core->children[cpu];
if (!_cpu)
continue;
if (first == 1) {
strcat(linebuf, fill_state_name(_cpu, state, line, buffer));
expand_string(linebuf, ctr + 10);
first = 0;
ctr += 12;
}
buffer[0] = 0;
strcat(linebuf, fill_state_line(_cpu, state, line, buffer));
ctr += 10;
expand_string(linebuf, ctr);
}
strcat(linebuf, "\n");
wprintw(win, "%s", linebuf);
}
wprintw(win, "\n");
first_pkg++;
}
}
}
void w_display_cpu_pstates(void)
{
impl_w_display_cpu_states(PSTATE);
}
void w_display_cpu_cstates(void)
{
impl_w_display_cpu_states(CSTATE);
}
struct power_entry {
#ifndef __i386__
int dummy;
#endif
int64_t type;
int64_t value;
} __attribute__((packed));
void perf_power_bundle::handle_trace_point(void *trace, int cpunr, uint64_t time)
{
struct event_format *event;
struct pevent_record rec; /* holder */
class abstract_cpu *cpu;
int type;
rec.data = trace;
type = pevent_data_type(perf_event::pevent, &rec);
event = pevent_find_event(perf_event::pevent, type);
if (!event)
return;
if (cpunr >= (int)all_cpus.size()) {
cout << "INVALID cpu nr in handle_trace_point\n";
return;
}
cpu = all_cpus[cpunr];
#if 0
unsigned int i;
printf("Time is %llu \n", time);
for (i = 0; i < system_level.children.size(); i++)
if (system_level.children[i])
system_level.children[i]->validate();
#endif
unsigned long long val;
int ret;
if (strcmp(event->name, "cpu_idle")==0) {
ret = pevent_get_field_val(NULL, event, "state", &rec, &val, 0);
if (ret < 0) {
fprintf(stderr, _("cpu_idle event returned no state?\n"));
exit(-1);
}
if (val == 4294967295)
cpu->go_unidle(time);
else
cpu->go_idle(time);
}
if (strcmp(event->name, "power_frequency") == 0
|| strcmp(event->name, "cpu_frequency") == 0){
ret = pevent_get_field_val(NULL, event, "state", &rec, &val, 0);
if (ret < 0) {
fprintf(stderr, _("power or cpu_frequecny event returned no state?\n"));
exit(-1);
}
cpu->change_freq(time, val);
}
if (strcmp(event->name, "power_start")==0)
cpu->go_idle(time);
if (strcmp(event->name, "power_end")==0)
cpu->go_unidle(time);
#if 0
unsigned int i;
for (i = 0; i < system_level.children.size(); i++)
if (system_level.children[i])
system_level.children[i]->validate();
#endif
}
void process_cpu_data(void)
{
unsigned int i;
system_level.reset_pstate_data();
perf_events->process();
for (i = 0; i < system_level.children.size(); i++)
if (system_level.children[i])
system_level.children[i]->validate();
}
void end_cpu_data(void)
{
system_level.reset_pstate_data();
perf_events->clear();
}
void clear_cpu_data(void)
{
if (perf_events)
perf_events->release();
delete perf_events;
}
void clear_all_cpus(void)
{
unsigned int i;
for (i = 0; i < all_cpus.size(); i++) {
delete all_cpus[i];
}
all_cpus.clear();
}