Reading through the documentation for the auxiliary C libraries listed in the assignment handout, I determined which functions I can use to gather all the required information.
- To get the total memory usage (in kB), I used the
ru_maxrss
field from therusage
struct defined insys/resource.h
. Getting this information required using thegetrusage()
function. - To get the amount of memory used (physical and virtual), I used the
sysinfo
struct defined insys/sysinfo.h
. Getting this information required using thesysinfo()
function. The following fields from thesysinfo
struct were useful for the purposes of this program:totalram
: the total usable main memory sizefreeram
: the available memory spacetotalswap
: the total swap space sizefreeswap
: the size of the swap space still availablemem_unit
: the memory unit size in bytes
- To get the number of CPU cores, I called the
sysconf(_SC_NPROCESSORS_ONLN)
function fromunistd.h
. - To get the total CPU usage, I read through the
/proc/stat
file and performed the calculations outlined in Step 2. - To get the user usage information, I used the
utmp
struct defined inutmp.h
. Getting this information required using thegetutent()
function. The following fields in the struct were useful for this program:ut_user
: the user log-in nameut_line
: the device namehost
: the host name
- To get the system usage information, I used the
utsname
struct defined in<sys/utsname.h>
. Getting this information required using theuname()
function. The following fields in the struct were useful for this program:sysname
: the system namenodename
: the machine nameversion
: the version of the operating systemrelease
: the release of the operating systemmachine
: the machine's architecture
The next step involved understanding how to calculate the CPU and memory usage.
CPU Usage:
Calculating CPU usage requires two time points.
CPU Usage = (1 - (idle_2 - idle_1)/(totalTime_2 - totalTime_1)) * 100%
Note: the subscript 1 denotes the value at the first time point and the subscript 2 denotes the value at the second time point.
The total time can be calculated be summing all the values in the first line of the /proc/stat
file and the idle time is the fourth number on the first line of the /proc/stat
file.
Memory Usage:
The physical memory consists of RAM and the virtual memory consists of the RAM and the swap space. The values are reported based on this distinction, as follows:
- (total physical memory) = (total ram)
- (total virtual memory) = (total ram) + (total swap space)
- (physical memory used) = (total ram) - (free ram)
- (swap space used) = (total swap space) - (free swap space)
- (virtual memory used) = (physical memory used) + (swap space used)
I decided to store the data collected at each time point using linked lists - one linked list for memory usage and another for CPU usage. Each timepoint is represented as a node in the linked list and each node contains a string containing all the relevant information for that time point. I had also created a struct called UsageInfoLL
which stores pointers to the head and tail of each linked list.
I iterated through argv
to get access to the command line arguments entered by the user. Since some flags have a =
sign in the middle, I used strtok()
from string.h
to split each argument at =
. This way, we can read the flag name and the value inputted (if applicable separately). If the the string after the =
cannot be converted to an integer, an error message will appear and the program will terminate. To decide which flags have been inputted by the user, I used strcmp()
from string.h
and used boolean variables to store whether or not each flag has been inputted. Depending on which combination of flags have been inputted, the program prints the relevant information (through a series of if
/else
statements.
To ensure that the output is refreshed at every time point, before taking each sample, I saved the position of the cursor using the \x1b7
escape code. After printing out the relevant information using the functions described below, I used the \x1b8
escape code to allow for the cursor to return to previously saved position. In the next iteration, the previous output is overwritten. In this way, the output refreshes at every time point.
Function | Description |
---|---|
void printReport(int samples, int tdelay, bool systemFlagPresent, bool userFlagPresent, bool graphicsFlagPresent) |
Prints final output. samples is the number times statistics will be collected and tdelay is the frequency of statistic collection. systemFlagPresent , userFlagPresent and graphicsFlagPresent hold true iff the system flag, user flag and graphics flag are inputted by the user, respectively. |
bool parseArguments(int argc, char **argv, int *samples, int *tdelay, bool *systemFlagPresent, bool *userFlagPresent, bool *graphicsFlagPresent) |
Parses through command line arguments to determine which flags have been entered. Returns true iff arguments are entered in correct format. argc is the number of command line arguments entered and argv is an array of strings that holds the command line arguments entered. samples points to the number times statistics will be collected and tdelay points to the frequency of statistic collection. systemFlagPresent , userFlagPresent and graphicsFlagPresent each point to a boolean variables that holds true iff the system flag, user flag and graphics flag are inputted by the user, respectively. |
void generateMemoryUsage(int samples, int tdelay, UsageInfoLL *usageInfo, int i) |
Displays memory usage without graphics. samples is the number times statistics will be collected and tdelay is the frequency of statistic collection. usageInfo is a struct that holds pointers to the linked lists holding memory/CPU usage information. i indicates the number times statistics will have been collected by the end of the current cycle. |
void generateCPUUsage(int samples, int tdelay, UsageInfoLL *usageInfo, int i) |
Displays CPU usage without graphics. samples is the number times statistics will be collected and tdelay is the frequency of statistic collection. usageInfo is a struct that holds pointers to the linked lists holding memory/CPU usage information. i indicates the number times statistics will have been collected by the end of the current cycle. |
void generateMemoryUsageGraphics(int samples, int tdelay, UsageInfoLL *usageInfo, int i) |
Displays memory usage with graphics. samples is the number times statistics will be collected and tdelay is the frequency of statistic collection. usageInfo is a struct that holds pointers to the linked lists holding memory/CPU usage information. i indicates the number times statistics will have been collected by the end of the current cycle. |
void generateCPUUsageGraphics(int samples, int tdelay, UsageInfoLL *usageInfo, int i) |
Displays CPU usage with graphics. samples is the number times statistics will be collected and tdelay is the frequency of statistic collection. usageInfo is a struct that holds pointers to the linked lists holding memory/CPU usage information. i indicates the number times statistics will have been collected by the end of the current cycle. |
void generateUserUsage() |
Display users usage. |
float calculateCPUUsage(int *lastTotal, int *lastIdle) |
Returns CPU usage as a percentage. lastTotal and lastIdle point to the total uptime and total idle time at the previous time point, respectively. |
bool isInteger(char *s) |
Returns true iff the string s is an integer. |
void deleteList(Node *head) |
Deletes a linked list whose head node is pointed to by head . |
-
Navigate to the directory (i.e.,
cd
) in whichmySystemStats.c
is saved on your machine. -
In the terminal, enter
gcc -Wall -Werror mySystemStats.c -o mySystemStats
to compile the program. -
To execute the program, enter
./mySystemStats
into the terminal. You may also use the following flags when executing:Flag Description --system
to print system usage report --user
to print users usage report --graphics
,-g
to include graphical output in system usage report --samples=N
to indicate that the system statistics will be collected N
times, whereN
is a positive integer (if this flag is not used, the default value is 10).--tdelay=T
to indicate that the system statistics will be collected every T
seconds, whereT
is a positive integer (if this flag is not used, the default value is 1 second).- For example,
./mySystemStats --system -g --samples=5 --tdelay=2
will print the system usage report with graphics and will collect statistics every 2 seconds for a total of 5 time points. - The number of samples and frequency can also be inputted as positional arguments (i.e., as two adjacent numbers separated by a space, where the first number is the number of samples and the second number is the frequency).
- These positional arguments can be located anywhere along the input as long as the two numbers are adjacent (e.g.,
./mySystemStats 5 --system 2
is invalid). - In this implementation, having just one of the numbers assumes that the entered number represents the number of samples (e.g.,
./mySystemStats 5
is equivalent to./mySystemStats --samples=5
). - For example,
./mySystemStats --user 5 2
and./mySystemStats 5 2 --system
will both print the system usage report and will collect statistics every 2 seconds for a total of 5 time points.
- These positional arguments can be located anywhere along the input as long as the two numbers are adjacent (e.g.,
./mySystemStats
is equivalent to./mySystemStats --system --user --samples=10 --tdelay=1
.
- For example,
-
If
Invalid argument entered!
is printed on the screen after executing, refer back to flags outline in Step 3 and repeat the above steps.
This section applies if the user inputs the graphics flag (i.e., --graphics
or -g
).
Memory Usage Graphics:
The graphics are a display of deltaMemoryUsage
, the relative change in memory usage between the previous and current samples. If deltaMemoryUsage
is negative, there is a :
symbol for every change of 0.01 with a @
at the end. If deltaMemoryUsage
is positive, there is a #
symbol for every change of 0.01 with a *
at the end. If deltaMemoryUsage
is 0.00, a o
is printed and if deltaMemoryUsage
is -0.00, a @
is printed. Beside this graphic is an expression of the form deltaMemoryUsage (totalMemoryUsed)
, where totalMemoryUsed
is expressed in GB.
Sample Graphics:
### Memory ### (Phys.Used/Tot -- Virtual Used/Tot)
5.50 GB / 16.50 GB -- 5.50 GB / 17.53 GB |o 0.00 (5.50)
5.50 GB / 16.50 GB -- 5.50 GB / 17.53 GB |o 0.00 (5.50)
5.50 GB / 16.50 GB -- 5.50 GB / 17.53 GB |@ -0.00 (5.50)
5.32 GB / 16.50 GB -- 5.32 GB / 17.53 GB |::::@ -0.03 (5.32)
5.47 GB / 16.50 GB -- 5.47 GB / 17.53 GB |###* 0.03 (5.47)
5.51 GB / 16.50 GB -- 5.51 GB / 17.53 GB |#* 0.01 (5.51)
5.51 GB / 16.50 GB -- 5.51 GB / 17.53 GB |o 0.00 (5.51)
5.51 GB / 16.50 GB -- 5.51 GB / 17.53 GB |@ -0.00 (5.51)
5.51 GB / 16.50 GB -- 5.51 GB / 17.53 GB |o 0.00 (5.51)
CPU Usage Graphics:
If the CPU usage is 0%, the *
symbol is printed. If the CPU usage is positive, a |
is printed for every percent of usage (rounded up to the nearest percent). The CPU usage is printed beside the graphic.
Sample Graphics:
Number of cores: 4
total cpu use = 20.91%
* 0.00%
|||||||||||||||||||| 19.44%
|||||||||||||||||||||||||| 25.38%
|||||||||||||||||||||||||||||||||||||||| 39.70%
|||||||||||||||||||||||||||||||||||||||| 39.90%
|||||||||||||||||||||||||||||||||||| 35.34%
||||||||||||||||||||||||||||||||||| 34.41%
||||||||||||||||||| 18.16%
|||||||||||||||||||||| 22.00%
||||||||||||||||||||| 20.91%
Potential Technical Difficulties: If there are too many users connected to the machine, the formatting for the graphics may not be as desired. If this occurs, kindly connect to another machine that has less users.