mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-30 06:34:38 +02:00
318 lines
11 KiB
C
318 lines
11 KiB
C
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include "util.h"
|
|
#include "proc_table.h"
|
|
|
|
/*
|
|
* proc_table_get - get proc table
|
|
*
|
|
* Read
|
|
* /proc/<pid>/stat
|
|
* /proc/<pid>/task/<tid>/stat
|
|
* and fill in proc_table.
|
|
*
|
|
* We don't get a true snapshot, since things under /proc can change while
|
|
* reading through them. Each open/read needs to handle sudden disappearance.
|
|
*
|
|
* 'man proc' describes the contents of the stat files. Example contents of
|
|
* stat files below have line breaks inserted for readability.
|
|
*
|
|
* /proc/861/stat:
|
|
*
|
|
* 861 (telem_forwarder) S 1 861 861 0 -1 4194560 427 0 4 0 217 2685 0 0 20
|
|
* 0 4 0 10613 30613504 328 4294967295 32768 84056 2125479472 2125478440
|
|
* 1993544836 0 0 0 0 4294967295 0 0 17 0 0 0 0 0 0 118784 119528 25137152
|
|
* 2125479709 2125479725 2125479725 2125479907 0
|
|
*
|
|
* pid=861 utime=217 stime=2685
|
|
*
|
|
* /proc/861/task/@/stat:
|
|
*
|
|
* 861 (telem_forwarder) S 1 861 861 0 -1 4194560 408 0 4 0 0 13 0 0 20
|
|
* 0 4 0 10613 30613504 328 4294967295 32768 84056 2125479472 2125478440
|
|
* 1993544836 0 0 0 0 2147779492 0 0 17 0 0 0 0 0 0 118784 119528 25137152
|
|
* 2125479709 2125479725 2125479725 2125479907 0
|
|
*
|
|
* pid=861 utime=0 stime=13
|
|
*
|
|
* 878 (telem_forwarder) S 1 861 861 0 -1 4194624 4 0 0 0 193 2595 0 0 -40
|
|
* 0 4 0 10659 30613504 328 4294967295 32768 84056 2125479472 1992428328
|
|
* 1993700708 0 0 0 0 2148340764 0 0 -1 0 39 1 0 0 0 118784 119528 25137152
|
|
* 2125479709 2125479725 2125479725 2125479907 0
|
|
*
|
|
* pid=878 utime=193 stime=2595
|
|
*
|
|
* 879 (telem_forwarder) S 1 861 861 0 -1 4194368 5 0 0 0 14 49 0 0 -41
|
|
* 0 4 0 10659 30613504 328 4294967295 32768 84056 2125479472 1984040064
|
|
* 1995229292 0 0 0 0 2151581412 0 0 -1 0 40 1 0 0 0 118784 119528 25137152
|
|
* 2125479709 2125479725 2125479725 2125479907 0
|
|
*
|
|
* pid=879 utime=14 stime=49
|
|
*
|
|
* 880 (telem_forwarder) S 1 861 861 0 -1 4194368 10 0 0 0 12 23 0 0 -4
|
|
* 0 4 0 10659 30613504 328 4294967295 32768 84056 2125479472 1975655240
|
|
* 1993544836 0 0 0 0 2147779492 0 0 -1 0 3 1 0 0 0 118784 119528 25137152
|
|
* 2125479709 2125479725 2125479725 2125479907 0
|
|
*
|
|
* pid=880 utime=12 stime=23
|
|
*
|
|
* Oddly, the utimes and stimes of the tasks don't sum to the utime and stime
|
|
* of the overall process; from the above, the task sums are:
|
|
* utime=219 stime=2680
|
|
* compared to the process's:
|
|
* utime=217 stime=2685
|
|
*
|
|
* On success, proc_table is filled in with proc entries, proc_entries is set
|
|
* to the number of entries, and 0 is returned. On error, nonzero is returned.
|
|
*/
|
|
int proc_table_get(proc_entry_t *proc_table, int *proc_entries)
|
|
{
|
|
int status = 0;
|
|
int entries;
|
|
DIR *dp1;
|
|
struct dirent *de1;
|
|
DIR *dp2;
|
|
struct dirent *de2;
|
|
int pid;
|
|
int tid;
|
|
char *endptr;
|
|
char tasks_dir[32];
|
|
char file_name[48];
|
|
FILE *fp;
|
|
int match;
|
|
|
|
if (proc_table == NULL || proc_entries == NULL || *proc_entries == 0) {
|
|
fprintf(stderr, "proc_table_get: invalid arguments\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(proc_table, 0, *proc_entries * sizeof(proc_entry_t));
|
|
entries = 0;
|
|
|
|
dp1 = opendir("/proc");
|
|
if (dp1 == NULL) {
|
|
fprintf(stderr, "error opening directory /proc\n");
|
|
return -1;
|
|
}
|
|
|
|
/* loop through /proc/<pid>/ until the table fills up or we read all */
|
|
while ((entries < *proc_entries) && ((de1 = readdir(dp1)) != NULL)) {
|
|
/* quick check: ignore non-directory entries */
|
|
if (de1->d_type != DT_DIR)
|
|
continue;
|
|
/* it's a pid if it can be converted to an integer */
|
|
pid = strtol(de1->d_name, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
continue;
|
|
/* loop through process's tasks */
|
|
sprintf(tasks_dir, "/proc/%d/task", pid);
|
|
dp2 = opendir(tasks_dir);
|
|
if (dp2 == NULL) {
|
|
/* this happens if the process disappears between the time we find
|
|
/proc/<pid>/ and the time we try to open /proc/<pid>/task/ */
|
|
/*fprintf(stderr, "error opening directory %s\n", tasks_dir);*/
|
|
continue;
|
|
}
|
|
|
|
/* loop over all /proc/<pid>/task/<tid>/ */
|
|
while ((entries < *proc_entries) && ((de2 = readdir(dp2)) != NULL)) {
|
|
/* quick check: ignore non-directory entries */
|
|
if (de2->d_type != DT_DIR)
|
|
continue;
|
|
/* it's a tid if it can be converted to an integer */
|
|
tid = strtol(de2->d_name, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
continue;
|
|
/* read task's stat file */
|
|
sprintf(file_name, "/proc/%d/task/%d/stat", pid, tid);
|
|
fp = fopen(file_name, "r");
|
|
if (fp == NULL) {
|
|
/* this has not been observed, but presumably could happen if
|
|
the process or task disappears between the time we find
|
|
/proc/<pid>/ or /proc/<pid>/task/<tid>, and the time we try
|
|
to open /proc/<pid>/task/<tid>/stat */
|
|
/*fprintf(stderr, "error opening file %s\n", file_name);*/
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Format as specified by 'man proc':
|
|
* %d (%s) %c %d %d %d %d %d %u %lu
|
|
* %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld
|
|
* %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu
|
|
* %lu %lu %lu %lu %lu %lu %lu %d %d %u
|
|
* %u %llu %lu %ld %lu %lu %lu %lu %lu %lu
|
|
* %lu %d
|
|
*/
|
|
match = fscanf(fp, "%d %31s %*c %*d %*d %*d %*d %*d %*u %*u "
|
|
"%*u %*u %*u %lu %lu %*d %*d %ld %ld %*d "
|
|
"%*d %*u %*u %*d %*u %*u %*u %*u %*u %*u "
|
|
"%*u %*u %*u %*u %*u %*u %*u %*d %*d %*u "
|
|
"%*u %*u %*u %*d %*u %*u %*u %*u %*u %*u "
|
|
"%*u %*d",
|
|
&proc_table->pid, proc_table->comm, &proc_table->utime,
|
|
&proc_table->stime, &proc_table->priority, &proc_table->nice);
|
|
if (match == 6) {
|
|
proc_table++;
|
|
entries++;
|
|
} else {
|
|
/* fscanf fails to match for a process name with spaces (!),
|
|
e.g. "ci otg thread". It may also fail if the process or
|
|
task disappears (that has not been observed). */
|
|
/*fprintf(stderr, "error parsing line for %s (matched %d)\n",
|
|
file_name, match);*/
|
|
}
|
|
fclose(fp);
|
|
|
|
} /* while (de2...) */
|
|
|
|
closedir(dp2);
|
|
|
|
} /* while (de1...) */
|
|
|
|
closedir(dp1);
|
|
|
|
*proc_entries = entries;
|
|
|
|
return status;
|
|
|
|
} /* proc_table_get */
|
|
|
|
/*
|
|
* proc_table_diff - find diffs between two proc_tables
|
|
*
|
|
* For each pid that appears in both proc tables, create an output entry
|
|
* containing the difference between the corresponding utime and stime fields.
|
|
*
|
|
* Output table cannot be the same as either input table.
|
|
*
|
|
* Differences are computed as (table 2 value) - (table 1 value).
|
|
*/
|
|
int proc_table_diff(proc_entry_t *proc_table_1, int proc_entries_1, proc_entry_t *proc_table_2,
|
|
int proc_entries_2, proc_entry_t *proc_table_3, int *proc_entries_3)
|
|
{
|
|
int entry1;
|
|
int entry2;
|
|
int entry3;
|
|
int pid;
|
|
|
|
entry3 = 0;
|
|
|
|
/* proc_entries_1 or proc_entries_2 can be zero */
|
|
if (proc_table_1 == NULL || proc_table_2 == NULL || proc_table_3 == NULL ||
|
|
proc_table_3 == proc_table_1 || proc_table_3 == proc_table_2 || proc_entries_3 == NULL ||
|
|
*proc_entries_3 == 0) {
|
|
fprintf(stderr, "proc_table_diff: invalid arguments\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(proc_table_3, 0, *proc_entries_3 * sizeof(proc_entry_t));
|
|
|
|
/* loop through proc_table_1 */
|
|
for (entry1 = 0; entry1 < proc_entries_1; entry1++) {
|
|
pid = proc_table_1[entry1].pid;
|
|
|
|
/* see if there is a matching entry in proc_table_2 */
|
|
for (entry2 = 0; entry2 < proc_entries_2; entry2++)
|
|
if (proc_table_2[entry2].pid == pid)
|
|
break; /* match! */
|
|
|
|
/* was there a match? */
|
|
if (proc_table_2[entry2].pid != pid)
|
|
continue; /* no */
|
|
|
|
/* proc_table_1[entry1] and proc_table_2[entry2] are the same pid */
|
|
|
|
proc_table_3[entry3].pid = pid;
|
|
memcpy(proc_table_3[entry3].comm, proc_table_1[entry1].comm, COMM_MAX);
|
|
proc_table_3[entry3].utime = proc_table_2[entry2].utime - proc_table_1[entry1].utime;
|
|
proc_table_3[entry3].stime = proc_table_2[entry2].stime - proc_table_1[entry1].stime;
|
|
/* all other fields are left zero */
|
|
|
|
entry3++;
|
|
|
|
if (entry3 >= *proc_entries_3)
|
|
break;
|
|
|
|
} /* for (entry1...) */
|
|
|
|
*proc_entries_3 = entry3;
|
|
|
|
return 0;
|
|
|
|
} /* proc_table_diff */
|
|
|
|
/*
|
|
* proc_table_top - sort proc table by top CPU users
|
|
*
|
|
* The goal is to get this right the first time - no instabilities.
|
|
*
|
|
* We have around 70 entries, and most of those (80-90%) have zero CPU usage.
|
|
* An insertion sort is used: go through the array, and for each nonzero entry,
|
|
* insert it at its sorted position. This should result in an insertion for
|
|
* each entry with nonzero CPU usage; the zero entries can be skipped quickly
|
|
* and will end up at the bottom.
|
|
*/
|
|
void proc_table_top(proc_entry_t *proc_table, int proc_entries)
|
|
{
|
|
int i, j;
|
|
long unsigned sum1;
|
|
long unsigned sum2;
|
|
proc_entry_t p;
|
|
|
|
/* loop over all entries */
|
|
for (i = 0; i < proc_entries; i++) {
|
|
sum1 = proc_table[i].utime + proc_table[i].stime;
|
|
/* skip zero entries */
|
|
if (sum1 == 0)
|
|
continue;
|
|
/* All entries above this one are already sorted. Insert this one at
|
|
the sorted position (above its current position). If it should
|
|
stay where it is, then we'll go through the j loop without doing
|
|
the insertion. */
|
|
for (j = 0; j < i; j++) {
|
|
sum2 = proc_table[j].utime + proc_table[j].stime;
|
|
if (sum2 < sum1) {
|
|
/* insert i at j */
|
|
|
|
/* save entry at i */
|
|
p = proc_table[i]; /* structure copy */
|
|
|
|
/* move down, leaving empty slot at j, overwriting slot at i */
|
|
memmove(&proc_table[j + 1], &proc_table[j], (i - j) * sizeof(proc_entry_t));
|
|
|
|
/* copy saved entry to j */
|
|
proc_table[j] = p; /* structure copy */
|
|
|
|
break; /* for j */
|
|
}
|
|
|
|
} /* for (j...) */
|
|
|
|
} /* for (i...) */
|
|
|
|
} /* proc_table_top */
|
|
|
|
/*
|
|
* proc_table_dump - print proc table to stdout
|
|
*/
|
|
void proc_table_dump(const proc_entry_t *proc_table, int proc_entries)
|
|
{
|
|
int i;
|
|
|
|
printf("proc_table (%d entries):", proc_entries);
|
|
|
|
for (i = 0; i < proc_entries; i++)
|
|
printf("%6d %-20s %6lu %6lu %4ld %4ld\n", proc_table[i].pid, proc_table[i].comm,
|
|
proc_table[i].utime, proc_table[i].stime, proc_table[i].priority,
|
|
proc_table[i].nice);
|
|
|
|
} /* proc_table_dump */
|