blob: 95c8577712fd759ff8123f6151942091b1de4bcd [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>
* Peter Anvin
*/
#include <map>
#include <string.h>
#include <iostream>
#include <utility>
#include <iostream>
#include <fstream>
#include <string>
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <limits.h>
#include "lib.h"
#ifndef HAVE_NO_PCI
extern "C" {
#include <pci/pci.h>
}
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <locale.h>
#include <limits>
#include <math.h>
#include <ncurses.h>
#include <fcntl.h>
#include <glob.h>
#ifndef __ANDROID__
#include <libintl.h>
#endif
static int kallsyms_read = 0;
int is_turbo(uint64_t freq, uint64_t max, uint64_t maxmo)
{
if (freq != max)
return 0;
if (maxmo + 1000 != max)
return 0;
return 1;
}
double percentage(double F)
{
F = F * 100.0;
// if (F > 100.0)
// F = 100.0;
if (F < 0.0)
F = 0.0;
return F;
}
char *hz_to_human(unsigned long hz, char *buffer, int digits)
{
unsigned long long Hz;
buffer[0] = 0;
Hz = hz;
/* default: just put the Number in */
sprintf(buffer,"%9lli", Hz);
if (Hz>1000) {
if (digits == 2)
sprintf(buffer, "%4lli MHz", (Hz+500)/1000);
else
sprintf(buffer, "%6lli MHz", (Hz+500)/1000);
}
if (Hz>1500000) {
if (digits == 2)
sprintf(buffer, "%4.2f GHz", (Hz+5000.0)/1000000);
else
sprintf(buffer, "%3.1f GHz", (Hz+5000.0)/1000000);
}
return buffer;
}
using namespace std;
map<unsigned long, string> kallsyms;
static void read_kallsyms(void)
{
ifstream file;
char line[1024];
kallsyms_read = 1;
file.open("/proc/kallsyms", ios::in);
while (file) {
char *c = NULL, *c2 = NULL;
unsigned long address = 0;
memset(line, 0, 1024);
file.getline(line, 1024);
c = strchr(line, ' ');
if (!c)
continue;
*c = 0;
c2 = c + 1;
if (*c2) c2++;
if (*c2) c2++;
address = strtoull(line, NULL, 16);
c = strchr(c2, '\t');
if (c)
*c = 0;
if (address != 0)
kallsyms[address] = c2;
}
file.close();
}
const char *kernel_function(uint64_t address)
{
const char *c;
if (!kallsyms_read)
read_kallsyms();
c = kallsyms[address].c_str();
if (!c)
return "";
return c;
}
static int _max_cpu;
int get_max_cpu(void)
{
return _max_cpu;
}
void set_max_cpu(int cpu)
{
if (cpu > _max_cpu)
_max_cpu = cpu;
}
void write_sysfs(const string &filename, const string &value)
{
ofstream file;
file.open(filename.c_str(), ios::out);
if (!file)
return;
#ifndef DISABLE_TRYCATCH
try
{
#endif
file << value;
file.close();
#ifndef DISABLE_TRYCATCH
} catch (std::exception &exc) {
return;
}
#endif
}
int read_sysfs(const string &filename, bool *ok)
{
ifstream file;
int i;
file.open(filename.c_str(), ios::in);
if (!file) {
if (ok)
*ok = false;
return 0;
}
#ifndef DISABLE_TRYCATCH
try
{
#endif
file >> i;
if (ok)
*ok = true;
#ifndef DISABLE_TRYCATCH
} catch (std::exception &exc) {
if (ok)
*ok = false;
i = 0;
}
#endif
file.close();
return i;
}
string read_sysfs_string(const string &filename)
{
ifstream file;
char content[4096];
char *c;
file.open(filename.c_str(), ios::in);
if (!file)
return "";
#ifndef DISABLE_TRYCATCH
try
{
#endif
file.getline(content, 4096);
file.close();
c = strchr(content, '\n');
if (c)
*c = 0;
#ifndef DISABLE_TRYCATCH
} catch (std::exception &exc) {
file.close();
return "";
}
#endif
return content;
}
string read_sysfs_string(const char *format, const char *param)
{
ifstream file;
char content[4096];
char *c;
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), format, param);
file.open(filename, ios::in);
if (!file)
return "";
#ifndef DISABLE_TRYCATCH
try
{
#endif
file.getline(content, 4096);
file.close();
c = strchr(content, '\n');
if (c)
*c = 0;
#ifndef DISABLE_TRYCATCH
} catch (std::exception &exc) {
file.close();
return "";
}
#endif
return content;
}
void align_string(char *buffer, size_t min_sz, size_t max_sz)
{
size_t sz;
char *buf = buffer;
/** mbsrtowcs() allows NULL dst and zero sz,
* comparing to mbstowcs(), which causes undefined
* behaviour under given circumstances*/
/* start with mbsrtowcs() local mbstate_t * and
* NULL dst pointer*/
sz = mbsrtowcs(NULL, (const char **)&buffer, max_sz, NULL);
if (sz == (size_t)-1) {
buffer[min_sz] = 0x00;
return;
}
while (sz < min_sz) {
strcat(buf, " ");
sz++;
}
}
void format_watts(double W, char *buffer, unsigned int len)
{
buffer[0] = 0;
char buf[32];
sprintf(buffer, _("%7sW"), fmt_prefix(W, buf));
if (W < 0.0001)
sprintf(buffer, _(" 0 mW"));
align_string(buffer, len, len);
}
#ifndef HAVE_NO_PCI
static struct pci_access *pci_access;
char *pci_id_to_name(uint16_t vendor, uint16_t device, char *buffer, int len)
{
char *ret;
buffer[0] = 0;
if (!pci_access) {
pci_access = pci_alloc();
pci_init(pci_access);
}
ret = pci_lookup_name(pci_access, buffer, len, PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, vendor, device);
return ret;
}
void end_pci_access(void)
{
if (pci_access)
pci_free_name_list(pci_access);
}
#else
char *pci_id_to_name(uint16_t vendor, uint16_t device, char *buffer, int len)
{
return NULL;
}
void end_pci_access(void)
{
}
#endif /* HAVE_NO_PCI */
int utf_ok = -1;
/* pretty print numbers while limiting the precision */
char *fmt_prefix(double n, char *buf)
{
static const char prefixes[] = "yzafpnum kMGTPEZY";
char tmpbuf[16];
int omag, npfx;
char *p, *q, pfx, *c;
int i;
if (utf_ok == -1) {
char *g;
g = getenv("LANG");
if (g && strstr(g, "UTF-8"))
utf_ok = 1;
else
utf_ok = 0;
}
p = buf;
*p = ' ';
if (n < 0.0) {
*p = '-';
n = -n;
p++;
}
snprintf(tmpbuf, sizeof tmpbuf, "%.2e", n);
c = strchr(tmpbuf, 'e');
if (!c) {
sprintf(buf, "NaN");
return buf;
}
omag = atoi(c + 1);
npfx = ((omag + 27) / 3) - (27/3);
omag = (omag + 27) % 3;
q = tmpbuf;
if (omag == 2)
omag = -1;
for (i = 0; i < 3; i++) {
while (!isdigit(*q))
q++;
*p++ = *q++;
if (i == omag)
*p++ = '.';
}
*p++ = ' ';
pfx = prefixes[npfx + 8];
if (pfx == ' ') {
/* do nothing */
} else if (pfx == 'u' && utf_ok > 0) {
strcpy(p, "µ"); /* Mu is a multibyte sequence */
while (*p)
p++;
} else {
*p++ = pfx;
}
*p = '\0';
return buf;
}
static map<string, string> pretty_prints;
static int pretty_print_init = 0;
static void init_pretty_print(void)
{
pretty_prints["[12] i8042"] = _("PS/2 Touchpad / Keyboard / Mouse");
pretty_prints["ahci"] = _("SATA controller");
pretty_prints["usb-device-8087-0020"] = _("Intel built in USB hub");
pretty_print_init = 1;
}
char *pretty_print(const char *str, char *buf, int len)
{
const char *p;
if (!pretty_print_init)
init_pretty_print();
p = pretty_prints[str].c_str();
if (strlen(p) == 0)
p = str;
snprintf(buf, len, "%s", p);
if (len)
buf[len - 1] = 0;
return buf;
}
int equals(double a, double b)
{
return fabs(a - b) <= std::numeric_limits<double>::epsilon();
}
void process_directory(const char *d_name, callback fn)
{
struct dirent *entry;
DIR *dir;
dir = opendir(d_name);
if (!dir)
return;
while (1) {
entry = readdir(dir);
if (!entry)
break;
if (entry->d_name[0] == '.')
continue;
fn(entry->d_name);
}
closedir(dir);
}
void process_glob(const char *d_glob, callback fn)
{
glob_t g;
size_t c;
switch (glob(d_glob, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) {
case GLOB_NOSPACE:
fprintf(stderr,_("glob returned GLOB_NOSPACE\n"));
globfree(&g);
return;
case GLOB_ABORTED:
fprintf(stderr,_("glob returned GLOB_ABORTED\n"));
globfree(&g);
return;
case GLOB_NOMATCH:
fprintf(stderr,_("glob returned GLOB_NOMATCH\n"));
globfree(&g);
return;
}
for (c=0; c < g.gl_pathc; c++) {
fn(g.gl_pathv[c]);
}
globfree(&g);
}
int get_user_input(char *buf, unsigned sz)
{
fflush(stdout);
echo();
/* Upon successful completion, these functions return OK. Otherwise, they return ERR. */
int ret = getnstr(buf, sz);
noecho();
fflush(stdout);
/* to distinguish between getnstr error and empty line */
return ret || strlen(buf);
}
int read_msr(int cpu, uint64_t offset, uint64_t *value)
{
#if defined(__i386__) || defined(__x86_64__)
ssize_t retval;
uint64_t msr;
int fd;
char msr_path[256];
snprintf(msr_path, sizeof(msr_path), "/dev/cpu/%d/msr", cpu);
if (access(msr_path, R_OK) != 0){
snprintf(msr_path, sizeof(msr_path), "/dev/msr%d", cpu);
if (access(msr_path, R_OK) != 0){
fprintf(stderr,
_("Model-specific registers (MSR)\
not found (try enabling CONFIG_X86_MSR).\n"));
return -1;
}
}
fd = open(msr_path, O_RDONLY);
if (fd < 0)
return -1;
retval = pread(fd, &msr, sizeof msr, offset);
close(fd);
if (retval != sizeof msr) {
return -1;
}
*value = msr;
return retval;
#else
return -1;
#endif
}
int write_msr(int cpu, uint64_t offset, uint64_t value)
{
#if defined(__i386__) || defined(__x86_64__)
ssize_t retval;
int fd;
char msr_path[256];
snprintf(msr_path, sizeof(msr_path), "/dev/cpu/%d/msr", cpu);
if (access(msr_path, R_OK) != 0){
snprintf(msr_path, sizeof(msr_path), "/dev/msr%d", cpu);
if (access(msr_path, R_OK) != 0){
fprintf(stderr,
_("Model-specific registers (MSR)\
not found (try enabling CONFIG_X86_MSR).\n"));
return -1;
}
}
fd = open(msr_path, O_WRONLY);
if (fd < 0)
return -1;
retval = pwrite(fd, &value, sizeof value, offset);
close(fd);
if (retval != sizeof value) {
return -1;
}
return retval;
#else
return -1;
#endif
}
#define UI_NOTIFY_BUFF_SZ 2048
void ui_notify_user_ncurses(const char *frmt, ...)
{
char notify[UI_NOTIFY_BUFF_SZ];
va_list list;
start_color();
init_pair(1, COLOR_BLACK, COLOR_WHITE);
attron(COLOR_PAIR(1));
va_start(list, frmt);
/* there is no ncurses *print() function which takes
* int x, int y and va_list, this is why we use temp
* buffer */
vsnprintf(notify, UI_NOTIFY_BUFF_SZ - 1, frmt, list);
va_end(list);
mvprintw(1, 0, notify);
attroff(COLOR_PAIR(1));
}
void ui_notify_user_console(const char *frmt, ...)
{
va_list list;
va_start(list, frmt);
vprintf(frmt, list);
va_end(list);
}