cpusage: add new package

This PR adds the new package "cpusage" to OpenWrt packages.
Cpusage is a small utility that prints cpu usage per second as output.

Signed-off-by: Thomas Huehn <thomas.huehn@hs-nordhausen.de>
This commit is contained in:
Thomas Huehn 2022-10-08 15:11:17 +02:00 committed by Nick Hainke
parent 193ffa68c1
commit e3fe484aae
2 changed files with 382 additions and 0 deletions

39
utils/cpusage/Makefile Normal file
View File

@ -0,0 +1,39 @@
# SPDX-Identifier-License: GPL-2.0-only
#
# Copyright (C) 2005 Fabian Schneider,
# 2010 Florian Sesser,
# 2022 Thomas Hühn
include $(TOPDIR)/rules.mk
PKG_NAME:=cpusage
PKG_VERSION:=$(AUTORELEASE)
PKG_MAINTAINER:=Thomas Hühn <thomas.huehn@hs-nordhausen.de>
include $(INCLUDE_DIR)/package.mk
define Package/cpusage
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Outputs CPU usage statistics once per second
endef
define Package/cpusage/description
CPUsage outputs CPU usage statistics once per second.
Optionally writes CSV output (see '-o' option).
Originally written by Fabian Schneider (TUM, TUB) in 2005.
Timestamp and CSV-compliance by Florian Sesser (TUM), 2010.
Refreshed by Thomas Hühn in 2022.
endef
define Build/Compile
$(TARGET_CC) $(TARGET_CFLAGS) \
-o $(PKG_BUILD_DIR)/cpusage $(PKG_BUILD_DIR)/cpusage.c
endef
define Package/cpusage/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/cpusage $(1)/usr/bin/
endef
$(eval $(call BuildPackage,cpusage))

343
utils/cpusage/src/cpusage.c Normal file
View File

@ -0,0 +1,343 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define CPUSTATES 7
#define IDLEI 3
/* long names:
* user - nice - system - idle - iowait - irq - soft irq */
char *cpustatenames[] = {"user", "nice", "system", "idle",
"iowait", "irq", "softirq", NULL};
#define LIMIT 95
static const char usage[] =
"\n usage: cpusage [ -hos ] [ -a | -l limit | -o ] [ -c CPU ]\n";
char *appname;
static float cpu_perc[CPUSTATES];
static float cpu_max[CPUSTATES];
static float cpu_min[CPUSTATES];
int cpunum; /* -1 all, 0-n CPU/Core 0-n */
int output;
int breakloop;
/* returns 1-n yielding the number of CPU's/Cores */
int getNumCPU() {
char buffer[32768];
int fd, len, i;
char *test;
fd = open("/proc/stat", O_RDONLY);
if (fd <= 0)
fprintf(stderr, "%s: cannot open /proc/stat \n", appname);
len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
buffer[len] = '\0';
i = 0;
test = strstr(buffer, "cpu");
if (test != NULL) {
test += sizeof("cpu");
test = strstr(test, "cpu");
}
while (test != NULL) {
test += sizeof("cpu");
/* fprintf(stderr, "%s: DEBUG: %s\n", appname, test); */
i++;
test = strstr(test, "cpu");
}
return i;
}
void getSysinfo(unsigned long *ptr, size_t size) {
char buffer[4096];
char match[100];
char *start;
int fd, len, j;
for (j = 0; j < size; j++)
ptr[j] = 0;
fd = open("/proc/stat", O_RDONLY);
if (fd <= 0)
fprintf(stderr, "%s: cannot open /proc/stat\n", appname);
len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
buffer[len] = '\0';
strcpy(match, "cpu ");
start = buffer;
if (cpunum != -1) {
sprintf(match, "cpu%d ", cpunum);
start = strstr(buffer, match);
}
strcat(match, "%ld %ld %ld %ld %ld %ld %ld");
if (sscanf(start, match, &ptr[0], &ptr[1], &ptr[2], &ptr[3], &ptr[4], &ptr[5],
&ptr[6]) != 7) {
fprintf(stderr, "%s: wrong /proc/stat format\n", appname);
}
}
long perc(int cpustates, long *cp_time, long *cp_old, long *cp_diff) {
int i = 0;
long total = 0;
for (i = 0; i < cpustates; i++) {
cp_diff[i] = cp_time[i] - cp_old[i];
total += cp_diff[i];
}
for (i = 0; i < cpustates; i++) {
cpu_perc[i] = ((float)cp_diff[i] * 100.0 / total);
/* new max ? */
if (cpu_perc[i] > cpu_max[i])
cpu_max[i] = cpu_perc[i];
/* new min ? */
if (cpu_perc[i] < cpu_min[i])
cpu_min[i] = cpu_perc[i];
}
return total;
}
void print_perc(float *perc, const char *head) {
int i;
time_t Zeitstempel;
struct tm *now;
/* human readable */
if ((output == 0) && (head != ""))
printf("%s: ", head);
/* machine readable */
if ((output == 1) && (head != ""))
printf("%s;", head);
/* timestamp */
time(&Zeitstempel);
now = localtime(&Zeitstempel);
if (output == 0)
printf("timestamp: %04d-%02d-%02d %02d.%02d.%02d, ", now->tm_year + 1900,
now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min,
now->tm_sec);
else
printf("%04d-%02d-%02d;%02d:%02d:%02d;", now->tm_year + 1900,
now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min,
now->tm_sec);
if (output == 0)
printf("%s: %5.1f%%, ", cpustatenames[0], perc[0]);
else
printf("%.1f", perc[0]);
/* print out calculated information in percentages */
for (i = 1; i < CPUSTATES; i++) {
if (output == 0)
printf("%s: %5.1f%%, ", cpustatenames[i], perc[i]);
else
printf(";%.1f", perc[i]);
}
printf("\n");
}
/* to catch Strg+C when looping */
void loop_term_handler(int signum) { breakloop = 1; }
int main(int argc, char **argv) {
appname = argv[0];
int i, c, limit;
int runonce; /* run just once and exit */
int avg; /* is avg measurement allready running */
int avg_run; /* did we allready had an avg measurement */
static long cp_time1[CPUSTATES];
static long cp_time2[CPUSTATES];
static long cp_avg_start[CPUSTATES];
static long cp_avg_stop[CPUSTATES];
static long cp_diff[CPUSTATES];
struct sigaction sigold, signew;
long *old = cp_time2;
long *new = cp_time1;
long total;
limit = LIMIT;
output = 0; /* 0: human readable; 1: machine readable */
runonce = 0; /* 0: run continuesly; 1: run once */
cpunum = -1; /* -1: all CPUs/Cores, 0-n: special CPU/Core */
/* reading commandline options */
while (1) {
c = getopt(argc, argv, "saohl:c:");
if (c == -1) {
break;
}
switch (c) {
/*run once and exit */
case 's':
runonce = 1;
break;
/* use avg from begin to end -> same as "-l 100" */
case 'a':
limit = 100;
break;
case 'o':
output = 1; /* machine readable */
// header for CSV output
printf("date;time;user;nice;system;idle;iowait;irq;softirq\n");
break;
/* print usage */
case 'h':
fprintf(stderr, "%s: %s", appname, usage);
exit(0);
break;
/* set limit */
case 'l':
if (!(sscanf(optarg, "%d", &limit) == 1)) {
fprintf(stderr, "%s: option for -l should be integer (is %s)\n",
appname, optarg);
exit(1);
}
break;
/* select CPU/Core */
case 'c':
if (!(sscanf(optarg, "%d", &cpunum) == 1)) {
fprintf(stderr, "%s: option for -c should be integer (is %s)\n",
appname, optarg);
exit(1);
}
break;
}
}
if (cpunum != -1) {
int numcpu = getNumCPU();
if (cpunum < numcpu) {
printf("-- Selected CPU %d\n", cpunum);
} else {
if (numcpu == 1) {
fprintf(stderr, "%s: CPU %d not available (found %d CPU: [0])\n",
appname, cpunum, numcpu);
} else {
fprintf(stderr,
"%s: CPU %d not available (found %d CPU's: [0]-[%d])\n ",
appname, cpunum, numcpu, numcpu - 1);
}
exit(1);
}
}
breakloop = 0;
for (i = 0; i < CPUSTATES; i++) {
cpu_max[i] = 0;
cpu_min[i] = 100;
}
/* get information */
getSysinfo((unsigned long *)new, CPUSTATES);
/* catch Strg+C when capturing to call pcap_breakloop() */
memset(&signew, 0, sizeof(signew));
signew.sa_handler = loop_term_handler;
if (sigaction(SIGINT, &signew, &sigold) < 0) {
fprintf(stderr, "Could not set signal handler -> exiting");
}
avg = 0;
avg_run = 0;
if (runonce) {
breakloop = 1;
}
while (1) {
usleep(1000000);
if (new == cp_time1) {
new = cp_time2;
old = cp_time1;
} else {
new = cp_time1;
old = cp_time2;
}
/* get information again */
getSysinfo((unsigned long *)new, CPUSTATES);
/* convert cp_time counts to percentages */
total = perc(CPUSTATES, new, old, cp_diff);
/* check for avg measurement start */
if (!avg_run && !avg && (cpu_perc[IDLEI] <= limit)) {
avg = 1;
for (i = 0; i < CPUSTATES; i++)
cp_avg_start[i] = new[i];
}
/* check for avg measurement stop */
if (!avg_run && avg && (cpu_perc[IDLEI] > limit)) {
avg = 0;
for (i = 0; i < CPUSTATES; i++)
cp_avg_stop[i] = new[i];
avg_run = 1;
}
print_perc(cpu_perc, "");
if (breakloop) {
if (avg) {
avg = 0;
for (i = 0; i < CPUSTATES; i++)
cp_avg_stop[i] = new[i];
}
break;
}
}
/* Set default behaviour when loop is done */
if (sigaction(SIGINT, &sigold, &signew) < 0) {
fprintf(stderr, "%s: Could not restore signal handler -> exiting", appname);
}
if (!runonce && output == 0) {
// print avg only when not making a one-shot msg and
// when not writing CSV output
printf("---Summary----\n");
print_perc(cpu_min, "Min");
print_perc(cpu_max, "Max");
perc(CPUSTATES, cp_avg_start, cp_avg_stop, cp_diff);
print_perc(cpu_perc, "Avg");
}
return 0;
}