| /* |
| * 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> |
| */ |
| |
| /* |
| * Code to track centrally which process has what /dev files open |
| */ |
| #include <iostream> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include <sys/stat.h> |
| #include <vector> |
| #include <algorithm> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <string.h> |
| |
| using namespace std; |
| |
| #include "devlist.h" |
| #include "lib.h" |
| #include "report.h" |
| |
| #include "process/process.h" |
| #include "devices/device.h" |
| /* |
| |
| * collect list of processes that have devices open |
| (alternate between before and after lists) |
| |
| * charge a "surcharge" to a device (sub)string |
| - count how many openers |
| - add proprotion to each process that has it open |
| |
| * list of devices + power they use for processing |
| |
| */ |
| |
| static vector<struct devuser *> one; |
| static vector<struct devuser *> two; |
| |
| static int phase; |
| /* |
| * 0 - one = before, two = after |
| * 1 - one = after, two = before |
| */ |
| |
| |
| |
| void collect_open_devices(void) |
| { |
| struct dirent *entry; |
| DIR *dir; |
| char filename[4096]; |
| char link[4096]; |
| unsigned int i; |
| vector<struct devuser *> *target; |
| |
| if (phase == 1) |
| target = &one; |
| else |
| target = &two; |
| |
| for (i = 0; i < target->size(); i++) { |
| free((*target)[i]); |
| } |
| target->resize(0); |
| |
| |
| dir = opendir("/proc/"); |
| if (!dir) |
| return; |
| while (1) { |
| struct dirent *entry2; |
| DIR *dir2; |
| entry = readdir(dir); |
| |
| if (!entry) |
| break; |
| if (entry->d_name[0] == '.') |
| continue; |
| if (strcmp(entry->d_name, "self") == 0) |
| continue; |
| |
| sprintf(filename, "/proc/%s/fd/", entry->d_name); |
| |
| dir2 = opendir(filename); |
| if (!dir2) |
| continue; |
| while (1) { |
| int ret; |
| struct devuser * dev; |
| entry2 = readdir(dir2); |
| if (!entry2) |
| break; |
| if (entry2->d_name[0] == '.') |
| continue; |
| sprintf(filename, "/proc/%s/fd/%s", entry->d_name, entry2->d_name); |
| memset(link, 0, 4096); |
| ret = readlink(filename, link, 4095); |
| if (ret < 0) |
| continue; |
| |
| if (strcmp(link, "/dev/null") == 0) |
| continue; |
| if (strcmp(link, "/dev/.udev/queue.bin") == 0) |
| continue; |
| if (strcmp(link, "/dev/initctl") == 0) |
| continue; |
| if (strcmp(link, "/dev/ptmx") == 0) |
| continue; |
| if (strstr(link, "/dev/pts/")) |
| continue; |
| if (strstr(link, "/dev/shm/")) |
| continue; |
| if (strstr(link, "/dev/urandom")) |
| continue; |
| if (strstr(link, "/dev/tty")) |
| continue; |
| |
| if (strncmp(link, "/dev", 4)==0) { |
| dev = (struct devuser *)malloc(sizeof(struct devuser)); |
| if (!dev) |
| continue; |
| dev->pid = strtoull(entry->d_name, NULL, 10); |
| strncpy(dev->device, link, 251); |
| dev->device[251] = '\0'; |
| strncpy(dev->comm, read_sysfs_string("/proc/%s/comm", entry->d_name).c_str(), 31); |
| dev->comm[31] = '\0'; |
| target->push_back(dev); |
| |
| } |
| } |
| closedir(dir2); |
| } |
| closedir(dir); |
| |
| if (phase) |
| phase = 0; |
| else |
| phase = 1; |
| } |
| |
| |
| /* returns 0 if no process is identified as having the device open and a value > 0 otherwise */ |
| int charge_device_to_openers(const char *devstring, double power, class device *_dev) |
| { |
| unsigned int i; |
| int openers = 0; |
| class process *proc; |
| /* 1. count the number of openers */ |
| |
| for (i = 0; i < one.size(); i++) { |
| if (strstr(one[i]->device, devstring)) |
| openers++; |
| } |
| for (i = 0; i < two.size(); i++) { |
| if (strstr(two[i]->device, devstring)) |
| openers++; |
| } |
| |
| |
| /* 2. divide power by this number */ |
| |
| if (!openers) |
| return 0; |
| power = power / openers; |
| |
| |
| /* 3. for each process that has it open, add the charge */ |
| |
| for (i = 0; i < one.size(); i++) |
| if (strstr(one[i]->device, devstring)) { |
| proc = find_create_process(one[i]->comm, one[i]->pid); |
| if (proc) { |
| proc->power_charge += power; |
| if (strlen(_dev->guilty) < 2000 && strstr(_dev->guilty, one[i]->comm) == NULL) { |
| strcat(_dev->guilty, one[i]->comm); |
| strcat(_dev->guilty, " "); |
| } |
| } |
| } |
| |
| for (i = 0; i < two.size(); i++) |
| if (strstr(two[i]->device, devstring)) { |
| proc = find_create_process(two[i]->comm, two[i]->pid); |
| if (proc) { |
| proc->power_charge += power; |
| if (strlen(_dev->guilty) < 2000 && strstr(_dev->guilty, two[i]->comm) == NULL) { |
| strcat(_dev->guilty, two[i]->comm); |
| strcat(_dev->guilty, " "); |
| } |
| } |
| } |
| |
| |
| |
| return openers; |
| } |
| |
| static vector<struct devpower *> devpower; |
| |
| void clear_devpower(void) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < devpower.size(); i++) { |
| devpower[i]->power = 0.0; |
| devpower[i]->dev->guilty[0] = 0; |
| } |
| } |
| |
| void register_devpower(const char *devstring, double power, class device *_dev) |
| { |
| unsigned int i; |
| struct devpower *dev = NULL; |
| |
| for (i = 0; i < devpower.size(); i++) |
| if (strcmp(devstring, devpower[i]->device) == 0) { |
| dev = devpower[i]; |
| } |
| |
| if (!dev) { |
| dev = (struct devpower *)malloc(sizeof (struct devpower)); |
| strcpy(dev->device, devstring); |
| dev->power = 0.0; |
| devpower.push_back(dev); |
| } |
| dev->dev = _dev; |
| dev->power = power; |
| } |
| |
| void run_devpower_list(void) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < devpower.size(); i++) { |
| int ret; |
| ret = charge_device_to_openers(devpower[i]->device, devpower[i]->power, devpower[i]->dev); |
| if (ret) |
| devpower[i]->dev->hide = true; |
| else |
| devpower[i]->dev->hide = false; |
| |
| } |
| |
| } |
| |
| static bool devlist_sort(struct devuser * i, struct devuser * j) |
| { |
| if (i->pid != j->pid) |
| return i->pid < j->pid; |
| |
| return (strcmp(i->device, j->device)< 0); |
| } |
| |
| static const char *dev_class(int line) |
| { |
| if (line & 1) { |
| return "device_odd"; |
| } |
| return "device_even"; |
| } |
| |
| void report_show_open_devices(void) |
| { |
| vector<struct devuser *> *target; |
| unsigned int i; |
| char prev[128], proc[128]; |
| |
| if ((!reportout.csv_report)&&(!reportout.http_report)) |
| return; |
| |
| prev[0] = 0; |
| |
| if (phase == 1) |
| target = &one; |
| else |
| target = &two; |
| |
| if (target->size() == 0) |
| return; |
| |
| sort(target->begin(), target->end(), devlist_sort); |
| |
| if (reporttype) { |
| fprintf(reportout.http_report,"<h2>Process device activity</h2>\n <table width=\"100%%\">\n"); |
| fprintf(reportout.http_report,"<tr><th class=\"device\" width=\"40%%\">Process</th><th class=\"device\">Device</th></tr>\n"); |
| }else { |
| fprintf(reportout.csv_report,"**Process Device Activity**, \n\n"); |
| fprintf(reportout.csv_report,"Process, Device, \n"); |
| } |
| |
| for (i = 0; i < target->size(); i++) { |
| proc[0] = 0; |
| |
| if (strcmp(prev, (*target)[i]->comm) != 0) |
| sprintf(proc, "%s", (*target)[i]->comm); |
| |
| if (reporttype) |
| fprintf(reportout.http_report, |
| "<tr class=\"%s\"><td>%s</td><td>%s</td></tr>\n", |
| dev_class(i), proc, (*target)[i]->device); |
| else |
| fprintf(reportout.csv_report, |
| "%s, %s, \n", |
| proc, (*target)[i]->device); |
| |
| sprintf(prev, "%s", (*target)[i]->comm); |
| } |
| if (reporttype) |
| fprintf(reportout.http_report,"</table></div>\n"); |
| else |
| fprintf(reportout.csv_report,"\n"); |
| } |