diff --git a/README.md b/README.md index cc51739d..5c61c347 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # CppND-System-Monitor -Starter code for System Monitor Project in the Object Oriented Programming Course of the [Udacity C++ Nanodegree Program](https://www.udacity.com/course/c-plus-plus-nanodegree--nd213). +System Monitor Project in the Object Oriented Programming Course of the [Udacity C++ Nanodegree Program](https://www.udacity.com/course/c-plus-plus-nanodegree--nd213). -Follow along with the classroom lesson to complete the project! +Follow along with the classroom lessons to complete the project! Important information about where to find the data on linux for the system monitor is [here](project_info.md). ![System Monitor](images/monitor.png) @@ -38,4 +38,4 @@ This project uses [Make](https://www.gnu.org/software/make/). The Makefile has f 5. Implement the `System`, `Process`, and `Processor` classes, as well as functions within the `LinuxParser` namespace. -6. Submit! \ No newline at end of file +6. Submit! diff --git a/images/htop-processor-utilization.png b/images/htop-processor-utilization.png new file mode 100644 index 00000000..2c3ea55b Binary files /dev/null and b/images/htop-processor-utilization.png differ diff --git a/images/system-monitor-kernel.png b/images/system-monitor-kernel.png new file mode 100644 index 00000000..5103865b Binary files /dev/null and b/images/system-monitor-kernel.png differ diff --git a/images/system-monitor-memory-utilization.png b/images/system-monitor-memory-utilization.png new file mode 100644 index 00000000..ba44984b Binary files /dev/null and b/images/system-monitor-memory-utilization.png differ diff --git a/images/system-monitor-os-release.png b/images/system-monitor-os-release.png new file mode 100644 index 00000000..09c627ef Binary files /dev/null and b/images/system-monitor-os-release.png differ diff --git a/images/system-monitor-process-command.png b/images/system-monitor-process-command.png new file mode 100644 index 00000000..18689091 Binary files /dev/null and b/images/system-monitor-process-command.png differ diff --git a/images/system-monitor-process-id.png b/images/system-monitor-process-id.png new file mode 100644 index 00000000..62c8492f Binary files /dev/null and b/images/system-monitor-process-id.png differ diff --git a/images/system-monitor-process-memory.png b/images/system-monitor-process-memory.png new file mode 100644 index 00000000..e97f210d Binary files /dev/null and b/images/system-monitor-process-memory.png differ diff --git a/images/system-monitor-process-uid.png b/images/system-monitor-process-uid.png new file mode 100644 index 00000000..186ec2d7 Binary files /dev/null and b/images/system-monitor-process-uid.png differ diff --git a/images/system-monitor-process-up-time.png b/images/system-monitor-process-up-time.png new file mode 100644 index 00000000..09d76305 Binary files /dev/null and b/images/system-monitor-process-up-time.png differ diff --git a/images/system-monitor-process-utilization.png b/images/system-monitor-process-utilization.png new file mode 100644 index 00000000..96b9818c Binary files /dev/null and b/images/system-monitor-process-utilization.png differ diff --git a/images/system-monitor-processes.png b/images/system-monitor-processes.png new file mode 100644 index 00000000..deff0c87 Binary files /dev/null and b/images/system-monitor-processes.png differ diff --git a/images/system-monitor-processor-utilization.png b/images/system-monitor-processor-utilization.png new file mode 100644 index 00000000..f9615333 Binary files /dev/null and b/images/system-monitor-processor-utilization.png differ diff --git a/images/system-monitor-processor.png b/images/system-monitor-processor.png new file mode 100644 index 00000000..7d3cf853 Binary files /dev/null and b/images/system-monitor-processor.png differ diff --git a/images/system-monitor-running-processes.png b/images/system-monitor-running-processes.png new file mode 100644 index 00000000..659d6d5a Binary files /dev/null and b/images/system-monitor-running-processes.png differ diff --git a/images/system-monitor-system.png b/images/system-monitor-system.png new file mode 100644 index 00000000..44854ae2 Binary files /dev/null and b/images/system-monitor-system.png differ diff --git a/images/system-monitor-total-processes.png b/images/system-monitor-total-processes.png new file mode 100644 index 00000000..67f9a0ad Binary files /dev/null and b/images/system-monitor-total-processes.png differ diff --git a/images/system-monitor-up-time.png b/images/system-monitor-up-time.png new file mode 100644 index 00000000..8471fd14 Binary files /dev/null and b/images/system-monitor-up-time.png differ diff --git a/images/system-monitor-username-1.png b/images/system-monitor-username-1.png new file mode 100644 index 00000000..86895eeb Binary files /dev/null and b/images/system-monitor-username-1.png differ diff --git a/include/format.h b/include/format.h index baace910..6bab18a4 100644 --- a/include/format.h +++ b/include/format.h @@ -4,7 +4,14 @@ #include namespace Format { -std::string ElapsedTime(long times); // TODO: See src/format.cpp -}; // namespace Format +/** + * @brief This converts a long time in seconds to a string time of format HH:MM:SS + * + * @param[in] times: Long measuring seconds + * + * @return String with format HH:MM:SS for the time + */ +std::string ElapsedTime(long times); +};// namespace Format -#endif \ No newline at end of file +#endif diff --git a/include/linux_parser.h b/include/linux_parser.h index 988ac069..18b17e69 100644 --- a/include/linux_parser.h +++ b/include/linux_parser.h @@ -1,5 +1,5 @@ -#ifndef SYSTEM_PARSER_H -#define SYSTEM_PARSER_H +#ifndef LINUX_PARSER_H +#define LINUX_PARSER_H #include #include @@ -7,7 +7,7 @@ namespace LinuxParser { // Paths -const std::string kProcDirectory{"/proc/"}; +const std::string kProcDirectory{"/proc"}; const std::string kCmdlineFilename{"/cmdline"}; const std::string kCpuinfoFilename{"/cpuinfo"}; const std::string kStatusFilename{"/status"}; @@ -19,12 +19,53 @@ const std::string kOSPath{"/etc/os-release"}; const std::string kPasswordPath{"/etc/passwd"}; // System +/** + * @brief This returns the memory utilization of the system in percentage (0-1) + * + * @return float with memory utilization of system in percentage (0-1) + */ float MemoryUtilization(); + +/** + * @brief This returns the uptime of the system in seconds + * + * @return long with uptime of system in seconds + */ long UpTime(); + +/** + * @brief This returns pids of the system in seconds + * + * @return vector of pids + */ std::vector Pids(); + +/** + * @brief Returns the total number of processes + * + * @return int with total number of processes + */ int TotalProcesses(); + +/** + * @brief Returns the number of processes running + * + * @return int with number of processes running + */ int RunningProcesses(); + +/** + * @brief This returns the operating system + * + * @return string with operating system + */ std::string OperatingSystem(); + +/** + * @brief This returns the kernel + * + * @return string with kernel + */ std::string Kernel(); // CPU @@ -40,18 +81,89 @@ enum CPUStates { kGuest_, kGuestNice_ }; -std::vector CpuUtilization(); + +/** + * @brief This returns the 10 elements from the cpu line of /proc/stat + * + * @return vec of long where each int corresponds to one of the 10 elements of the cpu utilization + */ +std::vector CpuUtilization(); + +/** + * @brief This returns the total number of Jiffies for the system (Active + Idle) + * + * @return total number of Jiffies for the system (Active + Idle) + */ long Jiffies(); + +/** + * @brief This returns the total number of active Jiffies for the system + * + * @return total number of active Jiffies for the system + */ long ActiveJiffies(); -long ActiveJiffies(int pid); + +/** + * @brief This returns the total number of idle Jiffies for the system + * + * @return total number of idle Jiffies for the system + */ long IdleJiffies(); // Processes +/** + * @brief This returns the command string that started the process + * + * @param[in] pid: process id + * + * @return the command string that started the process + */ std::string Command(int pid); + +/** + * @brief This returns the ram used by the process + * + * @param[in] pid: process id + * + * @return the ram used by the process + */ std::string Ram(int pid); + +/** + * @brief This returns the uid associated with a process + * + * @param[in] pid: process id + * + * @return uid string associated with the pid + */ std::string Uid(int pid); + +/** + * @brief This returns the user associated with a process + * + * @param[in] pid: process id + * + * @return user string associated with the pid + */ std::string User(int pid); -long int UpTime(int pid); + +/** + * @brief This returns the total number of active Jiffies for the process + * + * @param[in] pid: process id + * + * @return total number of active Jiffies for the process + */ +long ActiveJiffies(int pid); + +/** + * @brief Read and return the uptime of a process in seconds + * + * @param[in] pid: process id + * + * @return long with uptime of the process in seconds + */ +long UpTime(int pid); }; // namespace LinuxParser -#endif \ No newline at end of file +#endif diff --git a/include/ncurses_display.h b/include/ncurses_display.h index 38ff6a16..2dd8fb1b 100644 --- a/include/ncurses_display.h +++ b/include/ncurses_display.h @@ -13,4 +13,4 @@ void DisplayProcesses(std::vector& processes, WINDOW* window, int n); std::string ProgressBar(float percent); }; // namespace NCursesDisplay -#endif \ No newline at end of file +#endif diff --git a/include/process.h b/include/process.h index 6b7d1c22..2ff4bdfb 100644 --- a/include/process.h +++ b/include/process.h @@ -7,17 +7,62 @@ Basic class for Process representation It contains relevant attributes as shown below */ class Process { - public: - int Pid(); // TODO: See src/process.cpp - std::string User(); // TODO: See src/process.cpp - std::string Command(); // TODO: See src/process.cpp - float CpuUtilization(); // TODO: See src/process.cpp - std::string Ram(); // TODO: See src/process.cpp - long int UpTime(); // TODO: See src/process.cpp - bool operator<(Process const& a) const; // TODO: See src/process.cpp - - // TODO: Declare any necessary private members - private: +public: + /** + * @brief Process constructor + * + * @param[in] pid: process id + * + * @return Process object + */ + Process(int pid); + + /** + * @brief Process id getter + * + * @return Process id + */ + int Pid(); + + /** + * @brief This returns the user that started the process + * + * @return user that started the process + */ + std::string User(); + + /** + * @brief This returns the command string associated with pid_ (passthrough to LinuxParser) + * + * @return command string associated with pid_ + */ + std::string Command(); + + /** + * @brief Return this process's CPU utilization (0->1) + * + * @return process's CPU utilization (0->1) + */ + float CpuUtilization(); + + /** + * @brief This returns the ram used by the process (passthrough to LinuxParser) + * + * @return the ram used by the process + */ + std::string Ram() const; + + /** + * @brief Return the age of this process (in seconds) (passthrough to LinuxParser) + * + * @return age of this process (in seconds) + */ + long UpTime(); + + bool operator<(Process const& a) const; // Return true if the ram of a is less than process ram + +private: + int pid_; }; -#endif \ No newline at end of file +#endif diff --git a/include/processor.h b/include/processor.h index 6951a659..9c596d8e 100644 --- a/include/processor.h +++ b/include/processor.h @@ -2,11 +2,17 @@ #define PROCESSOR_H class Processor { - public: - float Utilization(); // TODO: See src/processor.cpp +public: + /** + * @brief This returns the processor utilization (0->1) + * + * @return processor utilization (0->1) + */ + float Utilization(); - // TODO: Declare any necessary private members - private: +private: + float prev_tot_jiffies_{0}; + float prev_act_jiffies_{0}; }; #endif \ No newline at end of file diff --git a/include/system.h b/include/system.h index ae445adf..fcd3dbba 100644 --- a/include/system.h +++ b/include/system.h @@ -8,20 +8,66 @@ #include "processor.h" class System { - public: - Processor& Cpu(); // TODO: See src/system.cpp - std::vector& Processes(); // TODO: See src/system.cpp - float MemoryUtilization(); // TODO: See src/system.cpp - long UpTime(); // TODO: See src/system.cpp - int TotalProcesses(); // TODO: See src/system.cpp - int RunningProcesses(); // TODO: See src/system.cpp - std::string Kernel(); // TODO: See src/system.cpp - std::string OperatingSystem(); // TODO: See src/system.cpp - - // TODO: Define any necessary private members - private: - Processor cpu_ = {}; - std::vector processes_ = {}; +public: + /** + * @brief Getter for cpu_ member + * + * @return Processor object cpu_ + */ + Processor& Cpu(); + + /** + * @brief Builds a vector of Process objects using the process ids + * + * @return Vector of Process objects (one for each process) + */ + std::vector& Processes(); + + /** + * @brief Returns memory utilization of the system in % (0-1) via linux_parser MemoryUtilization() + * + * @return float with memory utilization of system in percentage (0-1) + */ + float MemoryUtilization(); + + /** + * @brief This returns the uptime of the system in secs (a passthrough to linux_parser UpTime()) + * + * @return long with uptime of system in seconds + */ + long UpTime(); + + /** + * @brief Returns the total number of processes (a passthrough to linux_parser TotalProcesses()) + * + * @return int with total number of processes + */ + int TotalProcesses(); + + /** + * @brief Returns the number of processes running (passthrough to linux_parser RunningProcesses()) + * + * @return int with number of processes running + */ + int RunningProcesses(); + + /** + * @brief This returns the kernel (a passthrough to linux_parser Kernel()) + * + * @return string with kernel + */ + std::string Kernel(); + + /** + * @brief This returns the operating system (a passthrough to linux_parser OperatingSystem()) + * + * @return string with operating system + */ + std::string OperatingSystem(); + +private: + Processor cpu_; + std::vector processes_; }; -#endif \ No newline at end of file +#endif diff --git a/project_info.md b/project_info.md new file mode 100644 index 00000000..ad6ee741 --- /dev/null +++ b/project_info.md @@ -0,0 +1,197 @@ +# Table of Contents +1. [System Data](#system-data) +2. [Processor Data](#processor-data) +3. [Process Data](#process-data) +4. [Goal](#goal) + +# System Data + +![](images/system-monitor-system.png) + +Linux stores a lot of system data in files within the `/proc` directory. Most of the data that this project requires exists in those files. + +## Operating System + +Information about the operating system exists outside of the `/proc` directory, in the `/etc/os-release` file. + +![`cat /etc/os-release`](images/system-monitor-os-release.png) + +There are several strings from which to choose here, but the most obvious is the value specified by "PRETTY_NAME". + +## Kernel + +Information about the kernel exists `/proc/version` file. + +![`cat /proc/version`](images/system-monitor-kernel.png) + +## Memory Utilization + +Information about memory utilization exists in the `/proc/meminfo` file. + +![`cat /proc/meminfo`](images/system-monitor-memory-utilization.png) + +There are a [variety](https://www.thegeekdiary.com/understanding-proc-meminfo-file-analyzing-memory-utilization-in-linux/) of [ways](https://access.redhat.com/solutions/406773) to use this data to calculate memory utilization. + +[Hisham H. Muhammad](http://hisham.hm/about), the author of [htop](http://hisham.hm/htop/index.php), wrote a [Stack Overflow answer](https://stackoverflow.com/a/41251290) about how htop calculates memory utilization from the data in `/proc/meminfo`. + +Use the formula that makes the most sense to you! + +## Total Processes + +Information about the total number of processes on the system exists in the `/proc/stat` file. + +![`cat /proc/stat`](images/system-monitor-total-processes.png) + +## Running Processes + +Information about the number of processes on the system that are currently running exists in the `/proc/stat` file. + +![`cat /proc/stat`](images/system-monitor-running-processes.png) + +## Up Time + +Information about system up time exists in the `/proc/uptime` file. + +![`cat /proc/uptime`](images/system-monitor-up-time.png) + +> This file contains two numbers (values in seconds): the uptime of the system (including time spent in suspend) and the amount of time spent in the idle process. + +From the [_man_ page for `proc`](http://man7.org/linux/man-pages/man5/proc.5.html) + +# Processor Data + +![](images/system-monitor-processor.png) + +Linux stores processor utilization data within the `/proc/stat` file. + +![`cat /proc/stat`](images/system-monitor-processor-utilization.png) + +This data is more complex than most of the other data necessary to complete this project. + +For example, `/proc/stat` contains aggregate processor information (on the "cpu" line) and individual processor information (on the "cpu0", "cpu1", etc. lines). Indeed, [htop](https://hisham.hm/htop/) displays utilization information for each individual processor. + +![](images/htop-processor-utilization.png) + +For this project, however, you only need to display aggregate CPU information, which you can find on the "cpu" line of `/proc/stat`. + +If you would like to add individual processor information to your system monitor project, go for it! + +## Data + +`/proc/stat` contains 10 integer values for each processor. The Linux source code [documents each of these numbers](https://github.com/torvalds/linux/blob/master/Documentation/filesystems/proc.txt): + +> The very first "cpu" line aggregates the numbers in all of the other "cpuN" lines. These numbers identify the amount of time the CPU has spent performing different kinds of work. Time units are in USER_HZ (typically hundredths of a second). The meanings of the columns are as follows, from left to right: + +- user: normal processes executing in user mode +- nice: niced processes executing in user mode +- system: processes executing in kernel mode +- idle: twiddling thumbs +- iowait: In a word, iowait stands for waiting for I/O to complete. But there are several problems: + 1. Cpu will not wait for I/O to complete, iowait is the time that a task is waiting for I/O to complete. When cpu goes into idle state for outstanding task io, another task will be scheduled on this CPU. + 2. In a multi-core CPU, the task waiting for I/O to complete is not running on any CPU, so the iowait of each CPU is difficult to calculate. + 3. The value of iowait field in /proc/stat will decrease in certain conditions. So, the iowait is not reliable by reading from /proc/stat. +- irq: servicing interrupts +- softirq: servicing softirqs +- steal: involuntary wait +- guest: running a normal guest +- guest_nice: running a niced guest + +Even once you know what each of these numbers represents, it's still a challenge to determine exactly how to use these figures to calculate processor utilization. [This guide](https://github.com/Leo-G/Data-Science-Wiki) and [this StackOverflow post](https://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux) are helpful. + +## Measurement Interval + +Once you've parsed `/proc/stat` and calculated the processor utilization, you've got what you need for this project. Congratulations! + +However, when you run your system monitor, you might notice that the process utilization seems very stable. Too stable. + +That's because the processor data in `/proc/stat` is measured since boot. If the system has been up for a long time, a temporary interval of even extreme system utilization is unlikely to change the long-term average statistics very much. This means that the processor could be red-lining _right now_ but the system monitor might still show a relatively underutilized processor, if the processor has spent most of the time since boot in an idle state. + +You might want to update the system monitor to report the current utilization of the processor, rather than the long-term average utilization since boot. You would need to measure the difference in system utilization between two points in time relatively close to the present. A formula like: + +Δ active time units / Δ total time units + +Consider this a bonus challenge that is not required to pass the project. + +# Process Data + +![](images/system-monitor-processes.png) + +Linux stores data about individual processes in files within subdirectories of the `/proc` directory. Each subdirectory is named for that particular process's [identifier](https://en.wikipedia.org/wiki/Process_identifier) number. The data that this project requires exists in those files. + +## PID + +The process identifier (PID) is accessible from the `/proc` directory. Typically, all of the subdirectories of `/proc` that have integral names correspond to processes. Each integral name corresponds to a process ID. + +![`ls /proc`](images/system-monitor-process-id.png) + +Parsing directory names with C++ is tricky, so we have provided in the project starter code a pre-implemented function to capture the PIDs. + +## User + +Each process has an associated [user identifier (UID)](https://en.wikipedia.org/wiki/User_identifier), corresponding to the process owner. This means that determining the process owner requires two steps: + +1. Find the UID associated with the process +2. Find the user corresponding to that UID + +The UID for a process is stored in `/proc/[PID]/status`. + +![`cat /proc/[PID]/status`](images/system-monitor-process-uid.png) + +The [_man_ page for `proc`](http://man7.org/linux/man-pages/man5/proc.5.html) contains a "/proc/[pid]/status" section that describes this file. + +For the purposes of this project, you simply need to capture the first integer on the "Uid:" line. + +### Username + +[`/etc/passwd`](http://man7.org/linux/man-pages/man5/passwd.5.html) contains the information necessary to match the UID to a username. + +![`cat /etc/passwd`](images/system-monitor-username-1.png) + +## Processor Utilization + +Linux stores the CPU utilization of a process in the `/proc/[PID]/stat` file. + +![`cat /proc/[PID]/stat`](images/system-monitor-process-utilization.png) + +Much like the calculation of aggregate processor utilization, half the battle is extracting the relevant data from the file, and the other half of the battle is figuring out how to use those numbers to calculate processor utilization. + +The "/proc/[pid]/stat" section of the [`proc` _man_ page](http://man7.org/linux/man-pages/man5/proc.5.html) describes the meaning of the values in this file. [This StackOverflow answer](https://stackoverflow.com/a/16736599) explains how to use this data to calculate the process's utilization. + +As with the calculation of aggregate processor utilization, it is sufficient for this project to calculate the average utilization of each process since the process launched. If you would like to extend your project to calculate a more current measurement of process utilization, we encourage you to do that! + +## Memory Utilization + +Linux stores memory utilization for the process in `/proc/[pid]/status`. + +![](images/system-monitor-process-memory.png) + +In order to facilitate display, consider [converting the memory utilization into megabytes](https://www.google.com/search?q=convert+from+kb+to+mb&oq=convert+from+kb+to+mb). + +## Up Time + +Linux stores the process up time in `/proc/[pid]/stat`. + +![`cat /proc/[pid]/stat`](images/system-monitor-process-up-time.png) + +The "/proc/[pid]/stat" section of the [`proc` _man_ page](http://man7.org/linux/man-pages/man5/proc.5.html) describes each of the values in this file. + +> (22) starttime %llu +> +> The time the process started after system boot. In kernels before Linux 2.6, this value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks (divide by sysconf(_SC_CLK_TCK)). + +Note that the "starttime" value in this file is measured in "clock ticks". In order to convert from "clock ticks" to seconds, you must: + +- #include [](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html) +- divide the "clock ticks" value by `sysconf(_SC_CLK_TCK)` + +Once you have converted the time value to seconds, you can use the `Format::Time()` function from the project starter code to display the seconds in a "HH:MM:SS" format. + +## Command + +Linux stores the command used to launch the function in the `/proc/[pid]/cmdline` file. + +![`cat /proc/[pid]/cmdline`](images/system-monitor-process-command.png) + +# Goal + +It should look like [this](https://youtu.be/xw6_Mz3O54Y) when the project is complete. \ No newline at end of file diff --git a/src/format.cpp b/src/format.cpp index 8f8f854b..1b5413cd 100644 --- a/src/format.cpp +++ b/src/format.cpp @@ -1,11 +1,17 @@ #include - +#include +#include #include "format.h" -using std::string; - -// TODO: Complete this helper function -// INPUT: Long int measuring seconds -// OUTPUT: HH:MM:SS -// REMOVE: [[maybe_unused]] once you define the function -string Format::ElapsedTime(long seconds[[maybe_unused]]) { return string(); } \ No newline at end of file +std::string Format::ElapsedTime(long seconds) +{ + long hours = seconds / 3600; + seconds = seconds - hours * 3600; + long minutes = seconds / 60; + seconds = seconds - (minutes * 60); + char sec_string[3]; + char min_string[3]; + snprintf(sec_string, 3, "%02ld", seconds); + snprintf(min_string, 3, "%02ld", minutes); + return std::to_string(hours) + ":" + min_string + ":" + sec_string; +} diff --git a/src/linux_parser.cpp b/src/linux_parser.cpp index 51a946a1..273ec58c 100644 --- a/src/linux_parser.cpp +++ b/src/linux_parser.cpp @@ -3,19 +3,15 @@ #include #include #include +#include #include "linux_parser.h" -using std::stof; -using std::string; -using std::to_string; -using std::vector; - -// DONE: An example of how to read data from the filesystem -string LinuxParser::OperatingSystem() { - string line; - string key; - string value; +std::string LinuxParser::OperatingSystem() +{ + std::string line; + std::string key; + std::string value; std::ifstream filestream(kOSPath); if (filestream.is_open()) { while (std::getline(filestream, line)) { @@ -34,10 +30,10 @@ string LinuxParser::OperatingSystem() { return value; } -// DONE: An example of how to read data from the filesystem -string LinuxParser::Kernel() { - string os, kernel, version; - string line; +std::string LinuxParser::Kernel() +{ + std::string os, kernel, version; + std::string line; std::ifstream stream(kProcDirectory + kVersionFilename); if (stream.is_open()) { std::getline(stream, line); @@ -48,15 +44,16 @@ string LinuxParser::Kernel() { } // BONUS: Update this to use std::filesystem -vector LinuxParser::Pids() { - vector pids; +std::vector LinuxParser::Pids() +{ + std::vector pids; DIR* directory = opendir(kProcDirectory.c_str()); struct dirent* file; while ((file = readdir(directory)) != nullptr) { // Is this a directory? if (file->d_type == DT_DIR) { // Is every character of the name a digit? - string filename(file->d_name); + std::string filename(file->d_name); if (std::all_of(filename.begin(), filename.end(), isdigit)) { int pid = stoi(filename); pids.push_back(pid); @@ -67,50 +64,224 @@ vector LinuxParser::Pids() { return pids; } -// TODO: Read and return the system memory utilization -float LinuxParser::MemoryUtilization() { return 0.0; } +float LinuxParser::MemoryUtilization() +{ + std::string line; + float mem_total, mem_available; + std::ifstream stream(kProcDirectory + kMeminfoFilename); + if (stream.is_open()) { + std::string mem_name; + std::getline(stream, line); + std::istringstream linestream(line); + linestream >> mem_name >> mem_total; + std::getline(stream, line); + std::getline(stream, line); + std::istringstream linestream2(line); + linestream2 >> mem_name >> mem_available; + } + return (mem_total - mem_available) / mem_total; +} -// TODO: Read and return the system uptime -long LinuxParser::UpTime() { return 0; } +long LinuxParser::UpTime() +{ + long uptime; + std::string line; + std::ifstream stream(kProcDirectory + kUptimeFilename); + if (stream.is_open()) { + std::getline(stream, line); + std::istringstream linestream(line); + linestream >> uptime; + } + return uptime; +} -// TODO: Read and return the number of jiffies for the system -long LinuxParser::Jiffies() { return 0; } +long LinuxParser::Jiffies() +{ + return ActiveJiffies() + IdleJiffies(); +} + +long LinuxParser::ActiveJiffies(int pid) +{ + std::string line; + std::string line_element; + long utime; + long stime; + long cutime; + long cstime; + std::ifstream stream(kProcDirectory + '/' + std::to_string(pid) + kStatFilename); + if (stream.is_open()) { + std::getline(stream, line); + std::istringstream linestream(line); + // We don't care about the first 12 elements, so stream those away + for (int i = 0; i < 13; ++i) { + linestream >> line_element; + } + linestream >> utime; // 13th element (0 indexed) + linestream >> stime; // 14th element (0 indexed) + linestream >> cutime; // 15th element (0 indexed) + linestream >> cstime; // 16th element (0 indexed) + } + return utime + stime + cutime + cstime; +} -// TODO: Read and return the number of active jiffies for a PID -// REMOVE: [[maybe_unused]] once you define the function -long LinuxParser::ActiveJiffies(int pid[[maybe_unused]]) { return 0; } +long LinuxParser::ActiveJiffies() +{ + std::vector cpu_util_vec = CpuUtilization(); + return ( + cpu_util_vec[CPUStates::kUser_] + cpu_util_vec[CPUStates::kNice_] + + cpu_util_vec[CPUStates::kSystem_] + cpu_util_vec[CPUStates::kIRQ_] + + cpu_util_vec[CPUStates::kSoftIRQ_] + cpu_util_vec[CPUStates::kSteal_]); +} -// TODO: Read and return the number of active jiffies for the system -long LinuxParser::ActiveJiffies() { return 0; } +long LinuxParser::IdleJiffies() +{ + std::vector cpu_util_vec = CpuUtilization(); + return cpu_util_vec[CPUStates::kIdle_] + cpu_util_vec[CPUStates::kIOwait_]; +} -// TODO: Read and return the number of idle jiffies for the system -long LinuxParser::IdleJiffies() { return 0; } +std::vector LinuxParser::CpuUtilization() +{ + std::vector cpu_utilization; + long cpu_element; + std::string key_name; + std::string line; + std::ifstream stream(kProcDirectory + kStatFilename); + if (stream.is_open()) { + std::getline(stream, line); + std::istringstream linestream(line); + linestream >> key_name; + while (linestream >> cpu_element) { + cpu_utilization.push_back(cpu_element); + } + } + return cpu_utilization; +} -// TODO: Read and return CPU utilization -vector LinuxParser::CpuUtilization() { return {}; } +int LinuxParser::TotalProcesses() +{ + int total_processes; + std::string identifier; + std::string line; + std::ifstream stream(kProcDirectory + kStatFilename); + if (stream.is_open()) { + while (std::getline(stream, line)) { + std::istringstream linestream(line); + linestream >> identifier; + if (identifier == "processes") { + linestream >> total_processes; + break; + } + } + } + return total_processes; +} -// TODO: Read and return the total number of processes -int LinuxParser::TotalProcesses() { return 0; } +int LinuxParser::RunningProcesses() +{ + int running_processes; + std::string identifier; + std::string line; + std::ifstream stream(kProcDirectory + kStatFilename); + if (stream.is_open()) { + while (std::getline(stream, line)) { + std::istringstream linestream(line); + linestream >> identifier; + if (identifier == "procs_running") { + linestream >> running_processes; + break; + } + } + } + return running_processes; +} -// TODO: Read and return the number of running processes -int LinuxParser::RunningProcesses() { return 0; } +std::string LinuxParser::Command(int pid) +{ + std::string cmd_string; + std::ifstream stream(kProcDirectory + '/' + std::to_string(pid) + kCmdlineFilename); + if (stream.is_open()) { + std::getline(stream, cmd_string); + } + return cmd_string; +} -// TODO: Read and return the command associated with a process -// REMOVE: [[maybe_unused]] once you define the function -string LinuxParser::Command(int pid[[maybe_unused]]) { return string(); } +std::string LinuxParser::Ram(int pid) +{ + int ram = 0; + std::string identifier; + std::string line; + std::ifstream stream(kProcDirectory + '/' + std::to_string(pid) + kStatusFilename); + if (stream.is_open()) { + while (std::getline(stream, line)) { + std::istringstream linestream(line); + linestream >> identifier; + if (identifier == "VmSize:") { + linestream >> ram; + break; + } + } + } + return std::to_string(ram / 1000); +} -// TODO: Read and return the memory used by a process -// REMOVE: [[maybe_unused]] once you define the function -string LinuxParser::Ram(int pid[[maybe_unused]]) { return string(); } +std::string LinuxParser::Uid(int pid) +{ + std::string uid; + std::string identifier; + std::string line; + std::ifstream stream(kProcDirectory + '/' + std::to_string(pid) + kStatusFilename); + if (stream.is_open()) { + while (std::getline(stream, line)) { + std::istringstream linestream(line); + linestream >> identifier; + if (identifier == "Uid:") { + linestream >> uid; + break; + } + } + } + return uid; +} -// TODO: Read and return the user ID associated with a process -// REMOVE: [[maybe_unused]] once you define the function -string LinuxParser::Uid(int pid[[maybe_unused]]) { return string(); } +std::string LinuxParser::User(int pid) +{ + std::string p_uid = Uid(pid); + std::string user; + std::string x; + std::string user_uid; + std::string line; + std::ifstream stream(kPasswordPath); + if (stream.is_open()) { + while (std::getline(stream, line)) { + std::replace(line.begin(), line.end(), ':', ' '); + std::istringstream linestream(line); + linestream >> user >> x >> user_uid; + if (user_uid == p_uid) { + break; + } + } + } + return user; +} -// TODO: Read and return the user associated with a process -// REMOVE: [[maybe_unused]] once you define the function -string LinuxParser::User(int pid[[maybe_unused]]) { return string(); } +long LinuxParser::UpTime(int pid) +{ + std::string line; + std::string line_element; + long start_time; + std::ifstream stream(kProcDirectory + '/' + std::to_string(pid) + kStatFilename); + if (stream.is_open()) { + std::getline(stream, line); + std::istringstream linestream(line); + // We don't care about the first 20 elements, so stream those away + for (int i = 0; i < 21; ++i) { + linestream >> line_element; + } + // The 21st element (0 indexed) is the process starttime + linestream >> start_time; + } -// TODO: Read and return the uptime of a process -// REMOVE: [[maybe_unused]] once you define the function -long LinuxParser::UpTime(int pid[[maybe_unused]]) { return 0; } + // Get system uptime + long sys_uptime = UpTime(); + return sys_uptime - (start_time / sysconf(_SC_CLK_TCK)); +} diff --git a/src/main.cpp b/src/main.cpp index 6d6a1aac..4d0fb25d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,4 +4,4 @@ int main() { System system; NCursesDisplay::Display(system); -} \ No newline at end of file +} diff --git a/src/ncurses_display.cpp b/src/ncurses_display.cpp index c64a8ef9..1de0a9b9 100644 --- a/src/ncurses_display.cpp +++ b/src/ncurses_display.cpp @@ -57,7 +57,7 @@ void NCursesDisplay::DisplayProcesses(std::vector& processes, int row{0}; int const pid_column{2}; int const user_column{9}; - int const cpu_column{16}; + int const cpu_column{19}; int const ram_column{26}; int const time_column{35}; int const command_column{46}; diff --git a/src/process.cpp b/src/process.cpp index 82119905..ee36ba1e 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -5,29 +5,44 @@ #include #include "process.h" - -using std::string; -using std::to_string; -using std::vector; - -// TODO: Return this process's ID -int Process::Pid() { return 0; } - -// TODO: Return this process's CPU utilization -float Process::CpuUtilization() { return 0; } - -// TODO: Return the command that generated this process -string Process::Command() { return string(); } - -// TODO: Return this process's memory utilization -string Process::Ram() { return string(); } - -// TODO: Return the user (name) that generated this process -string Process::User() { return string(); } - -// TODO: Return the age of this process (in seconds) -long int Process::UpTime() { return 0; } - -// TODO: Overload the "less than" comparison operator for Process objects -// REMOVE: [[maybe_unused]] once you define the function -bool Process::operator<(Process const& a[[maybe_unused]]) const { return true; } \ No newline at end of file +#include "linux_parser.h" + +Process::Process(int pid) : pid_(pid) {} + +int Process::Pid() +{ + return pid_; +} + +float Process::CpuUtilization() +{ + long total_time = LinuxParser::ActiveJiffies(pid_); + long seconds = UpTime(); + return (static_cast(total_time) / sysconf(_SC_CLK_TCK)) / static_cast(seconds); +} + +std::string Process::Command() +{ + return LinuxParser::Command(pid_); +} + +std::string Process::Ram() const +{ + return LinuxParser::Ram(pid_); +} + +std::string Process::User() +{ + return LinuxParser::User(pid_); +} + +long Process::UpTime() +{ + return LinuxParser::UpTime(pid_); +} + +bool Process::operator<(Process const& a) const +{ + // Return true if the passed in process uses less ram + return std::stoi(a.Ram()) < std::stoi(this->Ram()); +} diff --git a/src/processor.cpp b/src/processor.cpp index 91662895..fe6c6595 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -1,4 +1,16 @@ #include "processor.h" +#include "linux_parser.h" -// TODO: Return the aggregate CPU utilization -float Processor::Utilization() { return 0.0; } \ No newline at end of file +float Processor::Utilization() +{ + float tot_jiffies = static_cast(LinuxParser::Jiffies()); + float act_jiffies = static_cast(LinuxParser::ActiveJiffies()); + + float total_delta = tot_jiffies - prev_tot_jiffies_; + float act_delta = act_jiffies - prev_act_jiffies_; + + prev_tot_jiffies_ = tot_jiffies; + prev_act_jiffies_ = act_jiffies; + + return act_delta / total_delta; +} diff --git a/src/system.cpp b/src/system.cpp index 98e53273..1942e6e3 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -7,35 +7,67 @@ #include "process.h" #include "processor.h" #include "system.h" +#include "linux_parser.h" using std::set; using std::size_t; using std::string; using std::vector; -/*You need to complete the mentioned TODOs in order to satisfy the rubric criteria "The student will be able to extract and display basic data about the system." -You need to properly format the uptime. Refer to the comments mentioned in format. cpp for formatting the uptime.*/ +Processor& System::Cpu() +{ + return cpu_; +} -// TODO: Return the system's CPU -Processor& System::Cpu() { return cpu_; } +vector& System::Processes() +{ + std::vector pids{LinuxParser::Pids()}; -// TODO: Return a container composed of the system's processes -vector& System::Processes() { return processes_; } + // Create a set to store and sort the pids + std::set pids_set; + // Loop through the processes already stored + for (Process process : processes_) { + pids_set.insert(process.Pid()); + } -// TODO: Return the system's kernel identifier (string) -std::string System::Kernel() { return string(); } + for (int pid : pids) { + // Only add process to the processes_ vector if the pid isn't in the set + if (pids_set.find(pid) == pids_set.end()){ + processes_.push_back(Process(pid)); + } + } -// TODO: Return the system's memory utilization -float System::MemoryUtilization() { return 0.0; } + // Sort the processes by RAM + std::sort(processes_.begin(), processes_.end(), [](Process a, Process b){return a < b;}); + return processes_; +} -// TODO: Return the operating system name -std::string System::OperatingSystem() { return string(); } +std::string System::Kernel() +{ + return LinuxParser::Kernel(); +} -// TODO: Return the number of processes actively running on the system -int System::RunningProcesses() { return 0; } +float System::MemoryUtilization() +{ + return LinuxParser::MemoryUtilization(); +} -// TODO: Return the total number of processes on the system -int System::TotalProcesses() { return 0; } +std::string System::OperatingSystem() +{ + return LinuxParser::OperatingSystem(); +} -// TODO: Return the number of seconds since the system started running -long int System::UpTime() { return 0; } +int System::RunningProcesses() +{ + return LinuxParser::RunningProcesses(); +} + +int System::TotalProcesses() +{ + return LinuxParser::TotalProcesses(); +} + +long System::UpTime() +{ + return LinuxParser::UpTime(); +}