cpusage: add new package
authorThomas Huehn <thomas.huehn@hs-nordhausen.de>
Sat, 8 Oct 2022 13:11:17 +0000 (15:11 +0200)
committerNick Hainke <vincent@systemli.org>
Sat, 8 Oct 2022 13:34:04 +0000 (15:34 +0200)
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>
utils/cpusage/Makefile [new file with mode: 0644]
utils/cpusage/src/cpusage.c [new file with mode: 0644]

diff --git a/utils/cpusage/Makefile b/utils/cpusage/Makefile
new file mode 100644 (file)
index 0000000..f1f63ab
--- /dev/null
@@ -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))
diff --git a/utils/cpusage/src/cpusage.c b/utils/cpusage/src/cpusage.c
new file mode 100644 (file)
index 0000000..300fb62
--- /dev/null
@@ -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;
+}