aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2023-02-12 17:26:31 -0500
committerJoseph Hunkeler <jhunkeler@gmail.com>2023-02-12 17:26:31 -0500
commit2fb1839b7cb30fe4710677d9e78e84b5bc97d9f7 (patch)
tree9204ceb4c6d4143ef991ef31135c3333af6dbfe0
parentddc018b7860211b250ecd5ca80912497e09f1d36 (diff)
downloadmstat-2fb1839b7cb30fe4710677d9e78e84b5bc97d9f7.tar.gz
More stdout message modification
* Clear terminal (if possible) and pretty print all fields for each sample * Clean up usage statement(s) * mstat_find_program returns absolute path of "name" on success if "name" begins with "./" * Zero option structure(s) at runtime
-rw-r--r--README.md21
-rw-r--r--common.c15
-rw-r--r--mstat.c63
-rw-r--r--mstat_plot.c17
4 files changed, 83 insertions, 33 deletions
diff --git a/README.md b/README.md
index b6f011a..0fe45b5 100644
--- a/README.md
+++ b/README.md
@@ -6,12 +6,12 @@ Record the memory usage of a process over time.
```text
usage: mstat [OPTIONS] [-p PID] | {PROGRAM... ARGS}
--c clobber 'PID#.mstat' if it exists
--h this help message
--o DIR path to output directory (must exist)
--p PID process id to monitor
--s RATE samples per second (default: 1.00)
--v increased verbosity
+ -c clobber 'PID#.mstat' if it exists
+ -h this help message
+ -o DIR path to output directory (must exist)
+ -p PID process id to monitor
+ -s RATE samples per second (default: 1.00)
+ -v increased verbosity
```
## Monitor an existing process
@@ -47,6 +47,15 @@ Requires `gnuplot` to be installed.
- Arch Linux
- `pacman -S gnuplot`
+```text
+usage: mstat_plot [OPTIONS] {FILE}
+ -f NAME[,...] mstat field(s) to plot (default: rss,pss,swap)
+ -h this help message
+ -l list mstat fields
+ -v verbose mode
+```
+
+### Render
```shell
$ mstat_plot 12345.mstat
diff --git a/common.c b/common.c
index f9a8944..0fc55d5 100644
--- a/common.c
+++ b/common.c
@@ -591,11 +591,22 @@ int mstat_find_program(const char *name, char *where) {
path = strdup(pathtmp);
path_orig = path;
+ if (!strncmp(name, "./", 2)) {
+ strncpy(nametmp, realpath(name, NULL), PATH_MAX - 1);
+ if (access(nametmp, X_OK) < 0) {
+ return 1;
+ }
+ if (where) {
+ strcpy(where, nametmp);
+ }
+ return 0;
+ }
+
char *last_element = strrchr(name, '/');
if (last_element) {
- strncpy(nametmp, last_element + 1, sizeof(nametmp) - 1);
+ strncat(nametmp, last_element + 1, sizeof(nametmp) - 1);
} else {
- strncpy(nametmp, name, sizeof(nametmp) - 1);
+ strncat(nametmp, name, sizeof(nametmp) - 1);
}
while ((token = strsep(&path, ":")) != NULL) {
diff --git a/mstat.c b/mstat.c
index 14f0651..ab66859 100644
--- a/mstat.c
+++ b/mstat.c
@@ -36,6 +36,7 @@ struct Option {
* @param sig the trapped signal
*/
static void handle_interrupt(int sig) {
+ printf("SIGNALED %d\n", sig);
switch (sig) {
case SIGCHLD:
waitpid(option.pid, &option.status, WNOHANG|WUNTRACED);
@@ -51,7 +52,7 @@ static void handle_interrupt(int sig) {
fflush(option.file);
} else {
if (option.verbose)
- fprintf(stderr, "flush request ignored. no handle");
+ fprintf(stderr, "flush request ignored. no handle\n");
}
return;
case 0:
@@ -78,12 +79,12 @@ static void usage(char *prog) {
name = sep + 1;
}
printf("usage: %s [OPTIONS] [-p PID] | {PROGRAM... ARGS}\n"
- "-c clobber 'PID#.mstat' if it exists\n"
- "-h this help message\n"
- "-o DIR path to output directory (must exist)\n"
- "-p PID process id to monitor\n"
- "-s RATE samples per second (default: %0.2lf)\n"
- "-v increased verbosity\n"
+ " -c clobber 'PID#.mstat' if it exists\n"
+ " -h this help message\n"
+ " -o DIR path to output directory (must exist)\n"
+ " -p PID process id to monitor\n"
+ " -s RATE samples per second (default: %0.2lf)\n"
+ " -v increased verbosity\n"
"", name, option.sample_rate);
}
@@ -148,15 +149,26 @@ int smaps_rollup_usable(pid_t pid) {
return access(path, F_OK | R_OK);
}
+static void clearscr() {
+ printf("\33[H");
+ printf("\33[2J");
+}
int main(int argc, char *argv[]) {
- struct mstat_record_t record = {0};
- int positional = 0;
+ struct mstat_record_t record;
+ int positional;
+
+ // Initialize options
+ memset(&option, 0, sizeof(option));
+
+ // Initialize record
+ memset(&record, 0, sizeof(record));
// Set default options
option.sample_rate = 1;
option.verbose = 0;
option.clobber = 0;
+
// Set options based on arguments
positional = parse_options(argc, argv);
if (!option.pid && positional < 0) {
@@ -189,14 +201,14 @@ int main(int argc, char *argv[]) {
exit(1);
}
- if (p == 0) {
- // "where" is the path to the program to execute
- char where[PATH_MAX] = {0};
- if (mstat_find_program(argv[positional], where)) {
- perror(argv[positional]);
- exit(1);
- }
+ // "where" is the path to the program to execute
+ char where[PATH_MAX] = {0};
+ if (mstat_find_program(argv[positional], where)) {
+ perror(argv[positional]);
+ exit(1);
+ }
+ if (p == 0) {
// Execute the requested program (with arguments)
if (execv(where, &argv[positional]) < 0) {
perror("execv");
@@ -262,6 +274,7 @@ int main(int argc, char *argv[]) {
size_t i;
struct timespec ts_start, ts_end;
+ extern char *mstat_field_names[];
// Begin tracking time.
clock_gettime(CLOCK_MONOTONIC, &ts_start);
@@ -271,6 +284,9 @@ int main(int argc, char *argv[]) {
option.pid, option.sample_rate);
i = 0;
while (1) {
+ if (option.verbose && isatty(STDOUT_FILENO)) {
+ clearscr();
+ }
memset(&record, 0, sizeof(record));
record.pid = option.pid;
@@ -288,8 +304,19 @@ int main(int argc, char *argv[]) {
}
if (option.verbose) {
- printf("pid: %d, sample: %zu, elapsed: %lf, rss: %li\n",
- record.pid, i, record.timestamp, record.rss);
+ printf("\nPID: %d, Sample: %zu, Elapsed: %lf\n----\n",
+ record.pid, i, record.timestamp);
+ for (size_t n = 2, x = 0; mstat_field_names[n] != NULL; n++) {
+ if (x == 3) {
+ x = 0;
+ puts("");
+ }
+ union mstat_field_t field;
+ field = mstat_get_field_by_name(&record, mstat_field_names[n]);
+ printf("\t%-16s %-8lu ", mstat_field_names[n], field.u64);
+ x++;
+ }
+ puts("\n");
}
if (mstat_write(option.file, &record) < 0) {
diff --git a/mstat_plot.c b/mstat_plot.c
index 1357f48..3ccf888 100644
--- a/mstat_plot.c
+++ b/mstat_plot.c
@@ -166,19 +166,20 @@ static void usage(char *prog) {
if (sep) {
name = sep + 1;
}
- printf("usage: %s [OPTIONS] <FILE>\n"
- "-h this help message\n"
- "-l list mstat fields\n"
- "-f fields (default: rss,pss,swap)\n"
- "-v verbose mode\n"
- "\n", name);
+ printf("usage: %s [OPTIONS] {FILE}\n"
+ " -f NAME[,...] mstat field(s) to plot (default: rss,pss,swap)\n"
+ " -h this help message\n"
+ " -l list mstat fields\n"
+ " -v verbose mode\n"
+ "", name);
}
void parse_options(int argc, char *argv[]) {
if (argc < 2) {
- fprintf(stderr, "Missing path to *.mstat data\n");
+ usage(argv[0]);
exit(1);
}
+
option.fields[0] = "rss";
option.fields[1] = "pss";
option.fields[2] = "swap";
@@ -236,6 +237,8 @@ int main(int argc, char *argv[]) {
size_t rec;
FILE *fp;
+ // Initialize options
+ memset(&option, 0, sizeof(option));
parse_options(argc, argv);
rec = 0;