added to git
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.vscode
|
||||||
|
A3.zip
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
|
||||||
|
#include "cpuUtil.h"
|
||||||
|
|
||||||
|
void get_cpu_usage(int cpu_cores, char buf[PANE_BUF], int *cpu_utils, int i,
|
||||||
|
int tdelay) {
|
||||||
|
///_|> descry: return total cpu usage over a period
|
||||||
|
///_|> cpu_cores: number of cpu cores to consider in calculations
|
||||||
|
///_|> buf: buffer to write pane to
|
||||||
|
///_|> cpu_utils: data array
|
||||||
|
///_|> i: current datapoint in data array
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
double pre_cpu_util = read_cpu_util();
|
||||||
|
usleep(tdelay);
|
||||||
|
// Augmented the given algo
|
||||||
|
double cpu_util = (read_cpu_util() - pre_cpu_util) / (cpu_cores * tdelay);
|
||||||
|
|
||||||
|
cpu_utils[i] = min((int)ceil(1e5 * cpu_util), 10.0);
|
||||||
|
|
||||||
|
draw_cpu_title(buf, min(cpu_util * 1e6, 100.0));
|
||||||
|
draw_graph(buf, cpu_utils, i);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include "readUtil.h"
|
||||||
|
#include "printUtil.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
void get_cpu_usage(int cpu_cores, char buf[PANE_BUF], int *cpu_utils, int i,
|
||||||
|
int tdelay);
|
||||||
|
///_|> descry: return total cpu usage over a period
|
||||||
|
///_|> cpu_cores: number of cpu cores to consider in calculations
|
||||||
|
///_|> buf: buffer to write pane to
|
||||||
|
///_|> cpu_utils: data array
|
||||||
|
///_|> i: current datapoint in data array
|
||||||
|
///_|> returning: void
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-std=c99 -Werror -Wall -lm
|
||||||
|
|
||||||
|
.PHONY: all clean help
|
||||||
|
|
||||||
|
all: myMonitoringTool
|
||||||
|
|
||||||
|
myMonitoringTool: cpuUtil.o memUtil.o printUtil.o readUtil.o myMonitoringTool.o
|
||||||
|
@echo Compiling myMonitoringTool
|
||||||
|
@$(CC) $(CFLAGS) cpuUtil.o memUtil.o printUtil.o readUtil.o myMonitoringTool.o -o myMonitoringTool
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
@$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm *.o
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "make/make all: generate myMonitoringTool"
|
||||||
|
@echo "make clean: clean object debris from compilation"
|
||||||
|
@echo "make help: clean object debris from compilation"
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "memUtil.h"
|
||||||
|
|
||||||
|
void get_mem_usage(int mem_total, char buf[PANE_BUF], int *mem_usages, int i) {
|
||||||
|
///_|> descry: return total mem usage
|
||||||
|
///_|> mem_total: total memory for use in calculations
|
||||||
|
///_|> buf: buffer to write pane to
|
||||||
|
///_|> mem_usages: data array
|
||||||
|
///_|> i: current datapoint in data array
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
// Usage = total - unused
|
||||||
|
int mem_usage =
|
||||||
|
(int)min(mem_total, mem_total - get_file_line_value("/proc/meminfo", 2));
|
||||||
|
mem_usages[i] = (int)min((int)ceil(10.0 * mem_usage / mem_total), 10);
|
||||||
|
|
||||||
|
draw_mem_title(buf, (float)mem_usage / 1e6, (float)mem_total / 1e6);
|
||||||
|
draw_graph(buf, mem_usages, i);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "readUtil.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "printUtil.h"
|
||||||
|
|
||||||
|
void get_mem_usage(int mem_total, char buf[PANE_BUF], int *mem_usages, int i);
|
||||||
|
///_|> descry: return total mem usage
|
||||||
|
///_|> mem_total: total memory for use in calculations
|
||||||
|
///_|> buf: buffer to write pane to
|
||||||
|
///_|> mem_usages: data array
|
||||||
|
///_|> i: current datapoint in data array
|
||||||
|
///_|> returning: void
|
||||||
Binary file not shown.
@@ -0,0 +1,254 @@
|
|||||||
|
#include "myMonitoringTool.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define MAIN 0
|
||||||
|
#define MEM 1
|
||||||
|
#define CPU 2
|
||||||
|
#define CORE 3
|
||||||
|
|
||||||
|
bool isint(char *str) {
|
||||||
|
///_|> descry: checks if a string is an integer
|
||||||
|
///_|> str: string to check
|
||||||
|
///_|> returning: true iff is an integer
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (str[i] != '\0') {
|
||||||
|
if (!isdigit(str[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigint_handler(int sig) {
|
||||||
|
///_|> descry: Handles SIGINT, exiting if pressed twice
|
||||||
|
///_|> sig: signal number
|
||||||
|
///_|> returning: void
|
||||||
|
static int count = 0;
|
||||||
|
if (count == 0) {
|
||||||
|
printf("\nPress Ctrl+C again to quit");
|
||||||
|
count++;
|
||||||
|
char buf[LINE_BUF];
|
||||||
|
fgets(buf, sizeof buf, stdin);
|
||||||
|
count = 0;
|
||||||
|
if (signal(SIGINT, sigint_handler) != 0) {
|
||||||
|
perror("signal");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse input, read values, display requested output
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
///_|> Parse CLAs, implement children to read data, prints childrens data
|
||||||
|
///_|> argc: number of arguments including function name
|
||||||
|
///_|> argv: list of string arguments
|
||||||
|
///_|> retuning: integer success code
|
||||||
|
if (signal(SIGTSTP, SIG_IGN) != 0) {
|
||||||
|
perror("signal");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool show_mem = false;
|
||||||
|
bool show_cpu = false;
|
||||||
|
bool show_cores = false;
|
||||||
|
int samples = 20;
|
||||||
|
int tdelay = 5e5;
|
||||||
|
int nums_given = 0;
|
||||||
|
|
||||||
|
// Parse input
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
char first9[10];
|
||||||
|
strncpy(first9, argv[i], 9);
|
||||||
|
first9[9] = '\0';
|
||||||
|
char first10[11];
|
||||||
|
strncpy(first10, argv[i], 10);
|
||||||
|
first10[10] = '\0';
|
||||||
|
if (strcmp(argv[i], "--memory") == 0) {
|
||||||
|
show_mem = true;
|
||||||
|
} else if (strcmp(argv[i], "--cpu") == 0) {
|
||||||
|
show_cpu = true;
|
||||||
|
} else if (strcmp(argv[i], "--cores") == 0) {
|
||||||
|
show_cores = true;
|
||||||
|
} else if (isint(argv[i])) {
|
||||||
|
if (nums_given == 0) {
|
||||||
|
samples = atoi(argv[i]);
|
||||||
|
} else if (nums_given == 1) {
|
||||||
|
tdelay = atoi(argv[i]);
|
||||||
|
} else {
|
||||||
|
printf("Unknown flag %s\nUsage: myMonitoringTool [samples [tdelay]] "
|
||||||
|
"[--memory] [--cpu] [--cores] [--samples=N] [--tdelay=T]\n",
|
||||||
|
argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
nums_given += 1;
|
||||||
|
} else if (strcmp(first9, "--tdelay=") == 0) {
|
||||||
|
char value[LINE_BUF];
|
||||||
|
strncpy(value, argv[i] + 9, 20);
|
||||||
|
tdelay = atoi(value);
|
||||||
|
} else if (strcmp(first10, "--samples=") == 0) {
|
||||||
|
char value[LINE_BUF];
|
||||||
|
strncpy(value, argv[i] + 10, 20);
|
||||||
|
samples = atoi(value);
|
||||||
|
} else {
|
||||||
|
printf("Unknown flag %s\nUsage: myMonitoringTool [samples [tdelay]] "
|
||||||
|
"[--memory] [--cpu] [--cores] [--samples=N] [--tdelay=T]\n",
|
||||||
|
argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool show_all = !(show_mem || show_cpu || show_cores);
|
||||||
|
if (show_cores && !(show_mem || show_cpu || show_all)) {
|
||||||
|
samples = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_cores = get_file_line_value("/proc/cpuinfo", 11);
|
||||||
|
int p[2]; // Mem pipe
|
||||||
|
int q[2]; // Cpu pipe
|
||||||
|
int r[2]; // Core pipe
|
||||||
|
|
||||||
|
if (pipe(p) == -1 || pipe(q) == -1) {
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_proc = fork();
|
||||||
|
int mem_proc = -2;
|
||||||
|
if (cpu_proc != 0) {
|
||||||
|
mem_proc = fork();
|
||||||
|
}
|
||||||
|
if (cpu_proc == -1 || mem_proc == -1) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = -1;
|
||||||
|
|
||||||
|
if (cpu_proc != 0 && mem_proc > 0) {
|
||||||
|
close(p[1]);
|
||||||
|
close(q[1]);
|
||||||
|
id = MAIN;
|
||||||
|
if (signal(SIGINT, sigint_handler) != 0) {
|
||||||
|
perror("signal");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (cpu_proc == 0 && mem_proc <= 0) {
|
||||||
|
close(p[0]);
|
||||||
|
close(p[1]);
|
||||||
|
close(q[0]);
|
||||||
|
id = CPU;
|
||||||
|
if (signal(SIGINT, SIG_IGN) != 0) {
|
||||||
|
perror("signal");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(cpu_proc != 0 && mem_proc == 0);
|
||||||
|
close(p[0]);
|
||||||
|
close(q[0]);
|
||||||
|
close(q[1]);
|
||||||
|
id = MEM;
|
||||||
|
if (signal(SIGINT, SIG_IGN) != 0) {
|
||||||
|
perror("signal");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the core info
|
||||||
|
char coreinfo[PANE_BUF];
|
||||||
|
if (id == MAIN && (show_cores || show_all)) {
|
||||||
|
if (pipe(r) == -1) {
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
close(r[0]);
|
||||||
|
int cpu_max_freq = get_file_line_value(
|
||||||
|
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", 0);
|
||||||
|
draw_cores(cpu_cores, cpu_max_freq, r[1]);
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
close(r[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the frames
|
||||||
|
|
||||||
|
int mem_total = get_file_line_value("/proc/meminfo", 0);
|
||||||
|
|
||||||
|
// Initialize arrays for values that change, scaled from 0 to 10. For use
|
||||||
|
// in children
|
||||||
|
int mem_usages[samples];
|
||||||
|
int cpu_utils[samples];
|
||||||
|
|
||||||
|
// Values that will be printed, for use in parent
|
||||||
|
char meminfo[PANE_BUF];
|
||||||
|
char cpuinfo[PANE_BUF];
|
||||||
|
|
||||||
|
// Draw, compute, and display output
|
||||||
|
for (int i = 0; i < samples; i++) {
|
||||||
|
|
||||||
|
if (id == MEM && (show_mem || show_all)) {
|
||||||
|
assert(mem_proc == 0 && cpu_proc != 0);
|
||||||
|
get_mem_usage(mem_total, meminfo, mem_usages, i);
|
||||||
|
write(p[1], meminfo, PANE_BUF);
|
||||||
|
|
||||||
|
raise(SIGSTOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == CPU && (show_cpu || show_all)) {
|
||||||
|
assert(mem_proc != 0 && cpu_proc == 0);
|
||||||
|
get_cpu_usage(cpu_cores, cpuinfo, cpu_utils, i, tdelay / 2);
|
||||||
|
write(q[1], cpuinfo, PANE_BUF);
|
||||||
|
|
||||||
|
raise(SIGSTOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display cores info
|
||||||
|
if (id == MAIN) {
|
||||||
|
if (show_mem || show_cpu || show_all) {
|
||||||
|
usleep(tdelay);
|
||||||
|
}
|
||||||
|
draw_reset(samples, tdelay);
|
||||||
|
if (show_mem || show_all) {
|
||||||
|
// printf("main wait to read mem\n");
|
||||||
|
read(p[0], meminfo, PANE_BUF);
|
||||||
|
printf("%s", meminfo);
|
||||||
|
if (kill(mem_proc, SIGCONT) != 0) {
|
||||||
|
perror("kill");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_cpu || show_all) {
|
||||||
|
read(q[0], cpuinfo, PANE_BUF);
|
||||||
|
printf("%s", cpuinfo);
|
||||||
|
if (kill(cpu_proc, SIGCONT) != 0) {
|
||||||
|
perror("kill");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_cores || show_all) {
|
||||||
|
// The core info had time to generate, now it should read without
|
||||||
|
// generation time
|
||||||
|
if (i == 0) {
|
||||||
|
read(r[0], coreinfo, PANE_BUF);
|
||||||
|
}
|
||||||
|
printf("%s", coreinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
|
||||||
|
#include "printUtil.h"
|
||||||
|
#include "readUtil.h"
|
||||||
|
#include "memUtil.h"
|
||||||
|
#include "cpuUtil.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
#include "printUtil.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void draw_graph(char buf[PANE_BUF], int *datapoints, int i) {
|
||||||
|
///_|> descry: Given datapoints and the number of datapoints, graph them. Push
|
||||||
|
/// datapoints left offscreen if they cannot all be displayed.
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> datapoints: array of datapoints
|
||||||
|
///_|> i: last datapoint index
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
if (i > X_SIZE) {
|
||||||
|
start = i - X_SIZE;
|
||||||
|
}
|
||||||
|
// Draw the graph
|
||||||
|
for (int j = 10; j > 1; j--) {
|
||||||
|
if (j == 10) {
|
||||||
|
strncat(buf, "\n100%|", 8);
|
||||||
|
} else {
|
||||||
|
strncat(buf, "\n |", 8);
|
||||||
|
}
|
||||||
|
for (int k = start; k <= i; k++) {
|
||||||
|
if (j == datapoints[k]) {
|
||||||
|
strncat(buf, "#", 2);
|
||||||
|
} else {
|
||||||
|
strncat(buf, " ", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Draw the last 0 line
|
||||||
|
strncat(buf, "\n 0%|", 8);
|
||||||
|
for (int k = start; k < start + X_SIZE; k++) {
|
||||||
|
if (1 >= datapoints[k] && k <= i) {
|
||||||
|
strncat(buf, "#", 2);
|
||||||
|
} else {
|
||||||
|
strncat(buf, "_", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncat(buf, "\n", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_cores(int cpu_cores, int cpu_max_freq, int pipe) {
|
||||||
|
///_|> descry: draws the cores pane for main
|
||||||
|
///_|> cpu_cores: number of cpu cores
|
||||||
|
///_|> cpu_max_freq: max frequency of the cpu
|
||||||
|
///_|> pipe: pipe to write data to
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
char buf[PANE_BUF];
|
||||||
|
char tempbuf[LINE_BUF];
|
||||||
|
|
||||||
|
snprintf(tempbuf, 127, "\n- Number of Cores: %d\n Max frequency: %.2f GHz\n",
|
||||||
|
cpu_cores,
|
||||||
|
(float)cpu_max_freq / 1e6); // 1e6 kHz to GHz
|
||||||
|
|
||||||
|
strncat(buf, tempbuf, 127);
|
||||||
|
|
||||||
|
int drawn = 0;
|
||||||
|
while (drawn + 4 <= cpu_cores) {
|
||||||
|
strncat(
|
||||||
|
buf,
|
||||||
|
" +——+ +——+ +——+ +——+\n | | | | | | | |\n +——+ +——+ +——+ +——+\n",
|
||||||
|
100);
|
||||||
|
drawn += 4;
|
||||||
|
}
|
||||||
|
if (drawn + 2 <= cpu_cores) {
|
||||||
|
strncat(buf, " +——+ +——+\n | | | |\n +——+ +——+\n", 60);
|
||||||
|
drawn += 2;
|
||||||
|
}
|
||||||
|
if (drawn + 1 <= cpu_cores) {
|
||||||
|
strncat(buf, " +——+\n | |\n +——+\n", 30);
|
||||||
|
drawn += 1;
|
||||||
|
}
|
||||||
|
write(pipe, buf, PANE_BUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_cpu_title(char buf[PANE_BUF], float cpu_util_percent) {
|
||||||
|
///_|> descry: draws title for cpu graph
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> cpu_util_percent: the percent of cpu being utilized
|
||||||
|
///_|> returning: void
|
||||||
|
snprintf(buf, LINE_BUF, "\n- CPU Used: %.2f%%", cpu_util_percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_mem_title(char buf[PANE_BUF], float mem_used, float mem_total) {
|
||||||
|
///_|> descry: draws title for cpu graph
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> cpu_util_percent: the percent of cpu being utilized
|
||||||
|
///_|> returning: void
|
||||||
|
snprintf(buf, 32, "\n- Memory Used: %.2f/%.2f GB", mem_used, mem_total);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_reset(int samples, int tdelay) {
|
||||||
|
///_|> descry: draws title for function and resets screen
|
||||||
|
///_|> samples: number of samples to show
|
||||||
|
///_|> tdelay: period to show
|
||||||
|
///_|> returning: void
|
||||||
|
printf("\033[2J\033[H- %d samples | %dμs (%.2fs) intervals\n", samples,
|
||||||
|
tdelay, tdelay / 1e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
double min(double a, double b) {
|
||||||
|
///_|> descry: takes min of a and b
|
||||||
|
///_|> a: some number
|
||||||
|
///_|> b: some number
|
||||||
|
///_|> returning: min of a and b
|
||||||
|
if (a < b) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define X_SIZE 40
|
||||||
|
#define LINE_BUF 128
|
||||||
|
#define PANE_BUF 8194
|
||||||
|
|
||||||
|
void draw_graph(char buf[PANE_BUF], int *datapoints, int i);
|
||||||
|
///_|> descry: Given datapoints and the number of datapoints, graph them. Push
|
||||||
|
/// datapoints left offscreen if they cannot all be displayed.
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> datapoints: array of datapoints
|
||||||
|
///_|> i: last datapoint index
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
void draw_cores(int cpu_cores, int cpu_max_freq, int pipe);
|
||||||
|
///_|> descry: draws the cores pane for main
|
||||||
|
///_|> cpu_cores: number of cpu cores
|
||||||
|
///_|> cpu_max_freq: max frequency of the cpu
|
||||||
|
///_|> pipe: pipe to write data to
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
void draw_cpu_title(char buf[PANE_BUF], float cpu_util_percent);
|
||||||
|
///_|> descry: draws title for cpu graph
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> cpu_util_percent: the percent of cpu being utilized
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
void draw_mem_title(char buf[PANE_BUF], float mem_used, float mem_total);
|
||||||
|
///_|> descry: draws title for cpu graph
|
||||||
|
///_|> buf: buffer to write output to.
|
||||||
|
///_|> cpu_util_percent: the percent of cpu being utilized
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
void draw_reset(int samples, int tdelay);
|
||||||
|
///_|> descry: draws title for function and resets screen
|
||||||
|
///_|> samples: number of samples to show
|
||||||
|
///_|> tdelay: period to show
|
||||||
|
///_|> returning: void
|
||||||
|
|
||||||
|
double min(double a, double b);
|
||||||
|
///_|> descry: takes min of a and b
|
||||||
|
///_|> a: some number
|
||||||
|
///_|> b: some number
|
||||||
|
///_|> returning: min of a and b
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#include "readUtil.h"
|
||||||
|
#include "myMonitoringTool.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
// Rip an integer out of a string.
|
||||||
|
int extract_int(char *str) {
|
||||||
|
///_|> descry: takes an integer out of a string
|
||||||
|
///_|> str: string to search
|
||||||
|
///_|> returning: void
|
||||||
|
char num[LINE_BUF];
|
||||||
|
int num_idx = 0;
|
||||||
|
|
||||||
|
int str_idx = 0;
|
||||||
|
while (str[str_idx] != '\0') {
|
||||||
|
if (isdigit(str[str_idx])) {
|
||||||
|
num[num_idx] = str[str_idx];
|
||||||
|
num_idx++;
|
||||||
|
}
|
||||||
|
str_idx++;
|
||||||
|
}
|
||||||
|
num[num_idx] = '\0';
|
||||||
|
|
||||||
|
return atoi(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_file_line_value(char *filename, int linenum) {
|
||||||
|
///_|> descry: Get the integer at a specific line in a value. First line in
|
||||||
|
/// file is line num
|
||||||
|
///_|> filename: file to search
|
||||||
|
///_|> linenum: line to read
|
||||||
|
///_|> returning: integer thats found
|
||||||
|
FILE *file = fopen(filename, "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
printf("could not open %s", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
char line[LINE_BUF];
|
||||||
|
for (int i = 0; i <= linenum; i++) {
|
||||||
|
fgets(line, LINE_BUF, file);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
char label[LINE_BUF];
|
||||||
|
char sep[LINE_BUF];
|
||||||
|
int value;
|
||||||
|
char tail[LINE_BUF];
|
||||||
|
sscanf(line, "%s %s %d %s", label, sep, &value, tail);
|
||||||
|
return extract_int(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
long read_cpu_util() {
|
||||||
|
///_|> decry: Read total cpu time usage from /proc/stat
|
||||||
|
///_|> returning: cpu usage since boot
|
||||||
|
char dummy[LINE_BUF];
|
||||||
|
long user;
|
||||||
|
long nice;
|
||||||
|
long system;
|
||||||
|
long idle; // not counted
|
||||||
|
long io;
|
||||||
|
long intr;
|
||||||
|
long sintr;
|
||||||
|
long steal;
|
||||||
|
long guest;
|
||||||
|
long guest_nice;
|
||||||
|
FILE *file = fopen("/proc/stat", "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
printf("could not open /proc/stat");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fscanf(file, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", dummy, &user,
|
||||||
|
&nice, &system, &idle, &io, &intr, &sintr, &steal, &guest,
|
||||||
|
&guest_nice);
|
||||||
|
fclose(file);
|
||||||
|
// return value is in 1/100 s
|
||||||
|
return user + nice + system + io + intr + sintr;
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int get_file_line_value(char *filename, int linenum);
|
||||||
|
///_|> descry: Get the integer at a specific line in a value. First line in
|
||||||
|
/// file is line num
|
||||||
|
///_|> filename: file to search
|
||||||
|
///_|> linenum: line to read
|
||||||
|
///_|> returning: integer thats found
|
||||||
|
|
||||||
|
long read_cpu_util();
|
||||||
|
///_|> decry: Read total cpu time usage from /proc/stat
|
||||||
|
///_|> returning: cpu usage since boot
|
||||||
Reference in New Issue
Block a user