added to git
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
# MyMonitoringTool
|
||||
|
||||
## 1. Metadata
|
||||
Author: Jeremy Janella
|
||||
Date: 2025/03/30
|
||||
|
||||
## 2. Introduction
|
||||
This is a copy of A1 modified to utilize background processes.
|
||||
|
||||
What this program does:
|
||||
- Graphs memory usage
|
||||
- Graphs cpu usage
|
||||
- Displays basic system information
|
||||
|
||||
## 3. Approach of the problem
|
||||
I completely modularized as much as I could for the utils that this function relies on, as well as the print related and such functions. I might have gotten carried away making modules but I don't think I will be penalized for more than average. They are run in background processes, communicating back their results with pipes and synchronized with signals. I also added SIGTSTP and SIGINT handlers to give this complete terminal behaviour control.
|
||||
|
||||
## 4. Implementation
|
||||
### 4.i
|
||||
I made a module for main and everything coupled to it, one for cpu utils, one for memory utils, one for printing utils, and one for data reading utils. THe print utils doesn't actually print, it formats text blocks to be printed later. This makes printing race condition possibilities disappear. The main program parses all CLAs, then creates pipes and child processes for cpu and mem data gathering. It does the same for core information, but ends after one iteration as the data is constant. The iterations for the number of samples given wait tdelay, read from cpu/mem pipes, and print the text blocks read. Then it triggers SIGCONT to cpu and mem children. The cpu and mem children do the same iterations, collecting data and writing to the pipe, the raising themselves a SIGSTOP. This signalling ensures the children collect data at the right time. Below is some technical explanations of how components work, copied from the A1 documentation. I set an X_OPEN_SOURCE 500 definition as this allows usage of usleep without warnings.
|
||||
|
||||
### Graphs
|
||||
There are 10 rows to the graph. The top row is labeled as 100% and contains cpu usages in the range (90%, 100%]. Generally it can be said row i (from 1 to 10 inclusive) corresponds to usage amount ((i-1)\*10%, i\*10%]. The exception is row 1 which contains values in [0%, 10%]. The graphs are graphed the same way with the same function. There is no reason to show them differently, the same goes for showing percentages on the y axis or values. I still made sure to show the max value for memory above the graph.
|
||||
|
||||
### Memory usage
|
||||
Memory info is all taken from /proc/meminfo, namely total system memory and memory available. The difference of these gives the amount of memory actively used by the computer (opposed to dirty memory that is no longer needed). Descrepencies is memory usage can be caused by measuring in gigabytes/gibibytes/gigabits etc.
|
||||
|
||||
### CPU usage
|
||||
CPU utilization is found by calculating the difference in the sum of cpu-jiffies (0.01s) in /proc/stat measured before and after the sample interval given. Then this amount of time over the time the CPU is capable of spending (computed using number of processors times tdelay) gives the cpu utilization on a scale 0 to 1. Notably if less than a jiffy is used as the interval (or around that range) cpu usage becomes less useful as it is likely to say 0% which isn't that accurate of whats really happening - the integer amount of jiffies hasnt incremented yet. Then these get graphed the same as memory usage.
|
||||
|
||||
### System information
|
||||
CPU max frequency is found in /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq. As this is running on desktops, each core is assumed to have the same max frequency. While this isnt necessarily true, the oversight has been allowed to prevent the scope of this project from creeping too high.
|
||||
Number of cores is found in /proc/cpuinfo looking at the line number that says siblings. The alternative was counting processors individually in that file but siblings does the same job.
|
||||
|
||||
Displaying the cores was just printing boxes for the number of processors. The graph looks slightly weird if processors % 2 = 1, at the same time having an odd number of processors is slightly weird.
|
||||
|
||||
## 4.ii/iii
|
||||
### Module myMonitoringTool:
|
||||
This is the wrapper moduled triggering all the others. It contains all signal logic, child logic, CLA and printing (not formatting).
|
||||
|
||||
- bool isint(char *str): This is a simple helper that returns true iff str is an integer.
|
||||
- void sigint_handler(int sig): This handles SIGINT sent to the parent process, and exits if 2 SIGINTs are given. I used a static int taught in B07 to handle multiple times running this function, even though we are allowed global variables I thought it was cool. I also used fgets here which allows any character or none to be sent, as scanf and getchar didnt completely handle these cases. THis means the user can enter whatever they want as long as its not ctrl-c, press enter, and the desired behaviour will occur.
|
||||
- void int main(int argc, char **argv): This parses the arguments according to the given requiorements for argument parsing. From there it creates 3 children, one quickly generates the core information and saves it into a text block for the first iteration. The other two report back with text blocks for the memory and cpu graphing.
|
||||
|
||||
### Module printUtil:
|
||||
This formats data into printable text blocks to be printed by the main thread.
|
||||
|
||||
- void draw_graph(char buf[PANE_BUF], int *datapoints, int i): This draws i datapoints onto a graph and write it into buf. Detailed explanation of the graph in the Graph section above. It will only draw the most recent X_SIZE (40) datapoints.
|
||||
- void draw_cores(int cpu_cores, int cpu_max_freq, int pipe): This writes the cpu max frequency and core section display to a given pipe.
|
||||
- void draw_cpu_title(char buf[PANE_BUF], float cpu_util_percent): This draws the cpu graph header using cpu_util_percent to the given buffer.
|
||||
- void draw_mem_title(char buf[PANE_BUF], float mem_used, float mem_total): This draws the cpu graph header using cpu_util_percent to the given buffer.
|
||||
- void draw_reset(int samples, int tdelay): This resets the screen and gives basic info about what the programs samples are set as.
|
||||
- double min(double a, double b): A simple function to take the min of two numbers.
|
||||
|
||||
### Module cpuUtil
|
||||
This module is responsible for gathering cpu info required by the main module.
|
||||
|
||||
- void get_cpu_usage(int cpu_cores, char buf[PANE_BUF], int *cpu_utils, int i, int tdelay): Wrapper function for CPU usage. Uses information about the CPUs and calculates usage with a slight augmentation (dividingnusage by number of cores to scale 0-100%) of the given function, writing the formatted result and graph with i values to a buffer buf. (This is piped to the parent after)
|
||||
|
||||
|
||||
### Module memUtil
|
||||
This module is responsible for gathering memory info required by the main module.
|
||||
|
||||
|
||||
- void get_mem_usage(int mem_total, char buf[PANE_BUF], int *mem_usages, int i): Wrapper function for memory usage. Uses information about the total memory and calculates usage by subtracting the amount available, writing the formatted result and graph with i values to a buffer buf. (This is piped to the parent after)
|
||||
|
||||
### Module readUtil
|
||||
Used for doing misc data read operations.
|
||||
|
||||
- int extract_int(char *str): Helper function to extract whatever numbers are in a string to an integer.
|
||||
- int get_file_line_value(char *filename, int linenum): Give a file and a line number, extracts the number stored there.
|
||||
- long read_cpu_util(): Gathers information on cpu usage from /stat/proc and parses it to find cpu usage since boot with the values given in the handout.
|
||||
|
||||
|
||||
|
||||
## 5. Pseudocode:
|
||||
|
||||
Set behaviour to ignore any Ctrl+Z
|
||||
parse CLA for samples, tdelay, and what to draw
|
||||
store constants (cpu cores, total memory)
|
||||
initialize pipes and fork children for cpu/memory/cores
|
||||
if cores process:
|
||||
format frequency and cores display into a pipe
|
||||
exit
|
||||
if main process:
|
||||
set behaviour to exit with two consecutive sigints
|
||||
else:
|
||||
set behaviour to ignore SIGINTs
|
||||
initialize data arrays for memory and cpu graphs
|
||||
for sample 0..samples:
|
||||
if mem process and showing memory:
|
||||
memory data array += memory usage
|
||||
draw graph of memory usage
|
||||
write memory usage to memory pipe
|
||||
wait
|
||||
if cpu process and showing cpu:
|
||||
cpu data array += cpu usage
|
||||
draw graph of cpu usage
|
||||
write cpu usage to cpu pipe
|
||||
wait
|
||||
if main process:
|
||||
if showing memory:
|
||||
print text in memory pipe
|
||||
resume mem process
|
||||
if showing cpu:
|
||||
print text in cpu pipe
|
||||
resume cpu process
|
||||
if showing cores:
|
||||
if first iteration:
|
||||
core_info = text in core pipe
|
||||
print(core_info)
|
||||
|
||||
|
||||
## 6. Compilation
|
||||
make/make all: Creates the executable myMonitoringTool
|
||||
make clean: Cleans the directory of leftover compilations files generated.
|
||||
make help: Gives helpful explanations on the make file
|
||||
|
||||
|
||||
## Program Usage
|
||||
Note tdelay is measured in microseconds. Default values are to show all info, with graphs updated every half second for 20 samples.
|
||||
./myMonitoringTool [samples [tdelay]] [--memory] [--cpu] [--cores] [--samples=N] [--tdelay=T]
|
||||
|
||||
The expected iterval is above or equal to 0.01s
|
||||
The expected amount of samples is a rational integer. The upper limit will likely be the systems array size limit.
|
||||
|
||||
|
||||
## 7. Expected Results
|
||||
The results will look almost identical to my A1. Each 'frame' will show the options requested in order memory, cpu, cores, or all 3 if none are requested. The tdelay is the period of the samples, and samples is the number of samples to take. Pressing Ctrl+C twice in a row will also cause the program to exit, while Ctrl+Z does nothing. An example output with no arguments after a single sample is the following:
|
||||
|
||||
- 20 samples | 500000μs (0.50s) intervals
|
||||
|
||||
- Memory Used: 6.21/31.97 GB
|
||||
100%|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|#
|
||||
0%|________________________________________
|
||||
|
||||
- CPU Used: 9.83%
|
||||
100%|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
0%|#_______________________________________
|
||||
|
||||
- Number of Cores: 24
|
||||
Max frequency: 5.73 GHz
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
+——+ +——+ +——+ +——+
|
||||
| | | | | | | |
|
||||
+——+ +——+ +——+ +——+
|
||||
|
||||
### Examples:
|
||||
Graph the memory usage every half second for 20 samples
|
||||
./myMonitoringTool 20 500000 --memory
|
||||
./myMonitoringTool --memory --samples=20 --tdelay=500000
|
||||
|
||||
Graph the cpu usage every half second for 200 samples, then show system information
|
||||
./myMonitoringTool 200 500000 --cpu --cores
|
||||
./myMonitoringTool --cpu --cores --samples=20 --tdelay=500000
|
||||
|
||||
## 8. Test Cases
|
||||
I tested for one-off errors in number of samples printed in the graph, capping values to 100% and prventing them from going over the graph, different combinations of Ctrl+C/Z and resuming, and various CLA arguments. All goes well and all cases are handled. If invalid CLA are provided the program will warn to STDOUT (not STDERR like others as this is the users err so they have to look at it) and exit.
|
||||
|
||||
|
||||
## 9. Disclaimers
|
||||
The way odd numbers of cores is printed isn't the prettiest, but having an odd number of cores isnt the prettiest either.
|
||||
|
||||
|
||||
## 10. Sources
|
||||
My old A1 is much of the non-A3 code.
|
||||
For usleep: https://stackoverflow.com/questions/10053788/implicit-declaration-of-function-usleep
|
||||
static int source is B07, it works in B09 as well wow.
|
||||
Other sources are the class notes
|
||||
Reference in New Issue
Block a user