/* * @(#)Ksamp.cpp * * Date 01/19/2011 * * @author * * Benjamin Bertka * bbertka@mss.icics.ubc.ca * * This program is a CUI utility that displays the averages * for entries in /proc files. User inputs a duration and * interval amount, then averages are displayed for * the average amount of change between intervals. * Values averaged: user-system-idle mode, disk i/o, * numbeer of processes & context switching */ #include #include #include #include #include #include #include #include #include #include #include using namespace std; //Function prototypes (comments above individual methods) void run(vector &s, vector &val, vector &avg, int i); void firstRun(vector &s, vector &val, vector &avg); void display(vector &s); void help(); string cpuInfo(); string osRelease(); string upTime(); string bootTime(); int cpuStat(string type); int diskStat(string type); int memory(string type); long long averageDifference(vector diff); /* * This is used to store differences between * intervals for each cpu statistic. It is a global * since I was having trouble passing the sub array. * All the arrays have 13 values in them by default * since I have 13 attributes to measure. Note: * not all values are measured. */ vector< vector > difference(13); /** This method computes an average of the current changes of state for values in the "differences" vector */ long long averageDifference(vector diff){ long long sum = 0; for(int i = 0; i < diff.size(); ++i){ sum += diff[i]; //cout << sum << " " << diff[i] << ", "; } //cout << endl; return sum/diff.size(); } /** * Main entry point to program. User inputs zero or two args. * No args prints out the default value from the proc files. * Args must be integer types or error message with help will print. * A vector string of messages to print along with a vector _int64 * (long long) to keep running averages of certain values. * This main method calls run(), display(), sleep() until the * user parameters have been satisfied. * In between printouts, the shell is cleared for easier viewing. */ int main(int argc, char** argv){ vector svec(13, " "); //string representation for values vector val(13, 0); //change in value vector avg(13, 0); // average value if(argc == 1) { firstRun(svec, val, avg); display(svec); }else if(argc == 3){ string arg1 = argv[1]; string arg2 = argv[2]; if(arg1.find_first_of("0123456789") != string::npos && arg2.find_first_of("0123456789") != string::npos){ system("clear"); int a = atoi(arg1.c_str()); int b = atoi(arg2.c_str()); if( a <= b && a !=0 && b !=0){ int interval = ceil(double(b)/a); firstRun(svec, val, avg); sleep(a); for(int i = 0; i < interval; ++i){ run(svec, val, avg, (i+1) ); //get new values display(svec); //display data sleep(a); //sleep if(i < interval -1){ system("clear"); //clear if not the last one } } }else{ cout << "Error, bad values. "; help(); //calls the help output } }else{ cout << "Error, invalid types. "; help(); } }else{ cout << "Error, invalid args. "; help(); } return(0); } /** * Displays the proper use of Ksamp * PRE: None * POST: Message printed to std out */ void help(){ cout << endl << "Usage: $ksamp [intervals (int)] [duration (seconds)]" << endl; } /** * Displays the latest values * @param &s is the vector string with most recent values * PRE: @param is valid * POST: Message printed to std out */ void display(vector &s){ cout << "*-----------------------------------------------------------"< &s, vector &val, vector &avg){ stringstream out1, out2, out3, out4, out5, out6, out7, out8, out9; val[3] = cpuStat("user"); //compute the intial state change of value val[4] = cpuStat("system"); val[5] = cpuStat("idle"); val[6] = diskStat("read"); val[7] = diskStat("write"); val[8] = cpuStat("ctxt"); val[10] = cpuStat("processes"); val[11] = memory("total"); val[12] = memory("free"); s[0] = "* Procesor: \t\t" + cpuInfo(); //procesor type s[1] = "* Kernal: \t\t" + osRelease(); //kernal version s[2] = "* Uptime (sec): \t" + upTime(); //uptime out1 << (double(val[3])); //doubles are used for precision s[3] = "* User mode (USER_HZ):\t" + out1.str(); //time in user mode out2 << (double(val[4])); s[4] = "* Sys mode (USER_HZ):\t" + out2.str(); //time in system mode out3 << (double(val[5])); s[5] = "* Idle mode (USER_HZ):\t" + out3.str(); //time in idle mode out4 << ((val[6])); s[6] = "* Disk reads: \t\t" + out4.str(); //disk reads out5 << ((val[7])); s[7] = "* Disk writes: \t\t" + out5.str(); //disk writes out6 << ((val[8])); s[8] = "* Context switches: \t" + out6.str(); //context switches s[9] = "* Last boot at: \t" + bootTime(); //time of last boot; out7 << ((val[10])); s[10] = "* Num Processes: \t" + out7.str(); //num processes out8 << ((val[11])); s[11] = "* System memory (kb): \t" + out8.str(); //total memory out9 << ((val[12])); s[12] = "* Free memory (kb): \t" + out9.str(); //free memory } /** * This method computes the change of state for the * value vector, cpmputes a running average, and updates the string vector. * @param &s is a valid sring vector and @param &val is a valid long vector * @param interval is valid integer * PRE: interval is greater than zero * POST: The strings are updated for display and the values accumulated. */ void run(vector &s, vector &val, vector &avg, int interval){ /*sacrificing lots of stringstream objects to have less conversion code, would have liked to condense this stuff to look cleaner */ stringstream out1, out2, out3, out4, out5, out6, out7, out8, out9; //val[] index values 0,1,2,9 dont need to be averaged set long long int value = 0; value = cpuStat("user") - val[3]; //compute the default change of value val[3] = cpuStat("user"); //reset the default value difference[3].push_back(value); //add the difference to the array avg[3] = averageDifference(difference[3]); //take avg of all diff value = cpuStat("system") - val[4]; val[4] = cpuStat("system"); difference[4].push_back(value); avg[4] = averageDifference(difference[4]); value = cpuStat("idle") - val[5]; val[5] = cpuStat("idle"); difference[5].push_back(value); avg[5] = averageDifference(difference[5]); value = diskStat("read") - val[6]; val[6] = diskStat("read"); difference[6].push_back(value); avg[6] = averageDifference(difference[6]); value = diskStat("write") - val[7]; val[7] = diskStat("write"); difference[7].push_back(value); avg[7] = averageDifference(difference[7]); value = cpuStat("ctxt") - val[8]; val[8] = cpuStat("ctxt"); difference[8].push_back(value); avg[8] = averageDifference(difference[8]); value = cpuStat("processes") - val[10]; val[10] = cpuStat("processes"); difference[10].push_back(value); avg[10] = averageDifference(difference[10]); val[11] = memory("total"); val[12] = memory("free"); s[0] = "* Procesor: \t\t" + cpuInfo(); //procesor type s[1] = "* Kernal: \t\t" + osRelease(); //kernal version s[2] = "* Uptime (sec): \t" + upTime(); //uptime out1 << (double(avg[3])); //doubles are used for precision s[3] = "* User mode (USER_HZ):\t" + out1.str(); //time in user mode out2 << (double(avg[4])); s[4] = "* Sys mode (USER_HZ):\t" + out2.str(); //time in system mode out3 << (double(avg[5])); s[5] = "* Idle mode (USER_HZ):\t" + out3.str(); //time in idle mode out4 << (avg[6]); s[6] = "* Disk reads: \t\t" + out4.str(); //disk reads out5 << (avg[7]); s[7] = "* Disk writes: \t\t" + out5.str(); //disk writes out6 << (avg[8]); s[8] = "* Context switches: \t" + out6.str(); //context switches s[9] = "* Last boot at: \t" + bootTime(); //time of last boot; out7 << ((avg[10])); s[10] = "* Num Processes: \t" + out7.str(); //num processes out8 << ((val[11])); s[11] = "* System memory (kb): \t" + out8.str(); //total memory out9 << ((val[12])); s[12] = "* Free memory (kb): \t" + out9.str(); //free memory } /** * Searches /proc/cpuinfo for the 'model name' * Parsing the file, it creates a substring two spaces up from the * first ':', to one less character from the last 'U' in CPU. * PRE: none * POST: returns processor name as string */ string cpuInfo(){ ifstream ifs("/proc/cpuinfo"); string line; string make, speed; while(getline(ifs, line)){ if(line.find("model name") != string::npos ){ make = line.substr(line.find_first_of(":") + 2, line.find_last_of("U") - line.find_first_of(":") - 1 ); speed = line.substr(line.find_first_of("@")); return make + " " + speed; } } } /** * Searches /proc/sys/kernel/osrelease for kernal version * The line itself is the information we need. * PRE: none * POST: returns kernal version as string */ string osRelease(){ ifstream ifs("/proc/sys/kernel/osrelease"); string line; getline(ifs, line); return line; } /** * Searches /proc/uptime for computer uptime * PRE: none * POST: returns uptime as string */ string upTime() { ifstream ifs("/proc/uptime"); string line; getline(ifs, line); return line.substr(line.find_first_of("1234567890"), line.find_first_of(".")); } /** * Searches /proc/stat for boot time * PRE: none * POST: returns boot time as string */ string bootTime(){ ifstream ifs("/proc/stat"); string line; while(getline(ifs, line)){ if(line.find("btime") != string::npos){ line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 2); int value = (atoi(line.c_str())); time_t booted = (unsigned int)(value); string st = ctime(&booted); st.erase(st.length() - 1, st.length()); return st; } } return " "; } /** * Searches /proc/stat for various data * @param type * Input string designates which piece of data to search for. Depending on the * type either cpu, ctxt, processes is found and appropruate data returned. * Returns integers because they are averaged elsewhere in this program. * Strings are chomped according to file format. * PRE: /proc/* files are formatted correctly, and type is valid type. * POST: returns values as int */ int cpuStat(string type){ if (type.find("user") != string::npos || type.find("system") != string::npos || type.find("idle") != string::npos || type.find("ctxt") != string::npos || type.find("processes") != string::npos ) { ifstream ifs("/proc/stat"); string line, tmp; while(getline(ifs, line)){ int value = 1; //looking for user mode time, we return units of 1/100, if(line.find("cpu ")!= string::npos && type.find("user") != string::npos){ line = line.substr(line.find_first_of(" ") + 2, line.length() - line.find_first_of(" ") + 2); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; }//looking for system mode time, we return units of 1/100, else if(line.find("cpu ") != string::npos && type.find("system") != string::npos){ line = line.substr(line.find_first_of(" ") + 2, line.length() - line.find_first_of(" ") + 2); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; }//looking for idle time mode /* Here get get idletime, however according to documentation it is returned as seconds of USER_HZ*/ else if(line.find("cpu ") != string::npos && type.find("idle") != string::npos){ line = line.substr(line.find_first_of(" ") + 2, line.length() - line.find_first_of(" ") + 2); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; }//looking for context switches, no conversions to do else if(line.find("ctxt") != string::npos && type.find("ctxt") != string::npos){ line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 2); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; }//looking for processes, no conversions to do else if(line.find("processes") != string::npos && type.find("processes") != string::npos){ line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 2); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; } line.clear(); //make room for the new data } } return 0; } /** * Searches /proc/diskstats for disk statistics data * @param type * Depending on the type either read or write processes are found. * Returns integers because they are averaged elsewhere in this program. * Strings are chomped according to file format. * PRE: /proc/* files are formatted correctly, and type is valid type. * POST: returns values as int */ int diskStat(string type){ if(type.find("read") != string::npos || type.find("write") != string::npos ) { ifstream ifs("/proc/diskstats"); string line, tmp; while(getline(ifs, line)){ int value = 0; if(line.find("sda")!= string::npos && type.find("read") != string::npos){ line = line.substr(line.find_first_of("sda"), line.length() - line.find_first_of("sda")); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; } else if(line.find("sda") != string::npos && type.find("write") != string::npos){ line = line.substr(line.find_first_of("sda"), line.length() - line.find_first_of("sda")); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(line.find_first_of(" ") + 1, line.length() - line.find_first_of(" ") + 1); line = line.substr(0, line.find_first_of(" ")); value += atoi(line.c_str()); return value; } line.clear(); } } return 0; } /** * Searches /proc/meminfo for memory statistics data * @param type * Depending on the type either total or allocated memory is found. * Returns integers because they are averaged elsewhere in this program. * Strings are chomped according to file format. * PRE: /proc/* files are formatted correctly, and type is valid type. * POST: returns values as int */ int memory(string type){ if(type.find("total") != string::npos || type.find("free") != string::npos){ ifstream ifs("/proc/meminfo"); string line; int value = 0; while(getline(ifs, line)){ if(line.find("MemTotal")!= string::npos && type.find("total") != string::npos){ line = line.substr(line.find_first_of("1234567890"), line.find_last_of("1234567890") - line.find_first_of("1234567890") + 1); line = line.substr(0, line.find_first_of(" ")); value = atoi(line.c_str()); return value; //returns the total memory } else if(line.find("MemFree") != string::npos && type.find("free") != string::npos){ line = line.substr(line.find_first_of("1234567890"), line.find_last_of("1234567890") - line.find_first_of("1234567890") + 1); line = line.substr(0, line.find_first_of(" ")); value = atoi(line.c_str()); return value; //returns the free memory } line.clear(); } } return 0; }