system/resmonitor/showinfo.c (554 lines of code) (raw):

/**************************************************************************** * apps/system/resmonitor/showinfo.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include <malloc.h> #include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/statfs.h> #include <sys/time.h> #include <syslog.h> #include <time.h> #include <unistd.h> /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define PATH "/proc" #define CPULOAD "cpuload" #define HEAP "heap" #define STACK "stack" #define LOADAVG "loadavg" #define GROUP "group/status" #define FILELEN 100 /**************************************************************************** * Private Type Declarations ****************************************************************************/ struct t_info { unsigned long mem_total; unsigned long mem_used; unsigned long mem_maxused; unsigned long mem_free; unsigned long mem_largest; unsigned long mem_nused; unsigned long mem_nfree; char total_cpu[8]; }; struct p_info { unsigned long stack_used; unsigned long heap_used; char cpu[8]; }; /**************************************************************************** * Private Data ****************************************************************************/ static int go = 1; static int p_head = 1; static int d_head = 1; /**************************************************************************** * Private Functions ****************************************************************************/ static void handler(int sig) { go = 0; } static void show_usages(void) { syslog(LOG_WARNING, "Usage: CMD [-i <interval>] [-p <pid>] [-a] [-d <dir>] [-l " "<logpath>] [-k <logpath>]\n" "\t\t-i: set refresh interval (s), default 1s\n" "\t\t-p: set pid to monitor, show stack, head and cpu used by pid\n" "\t\t-a: work only if -p is set, show stack, head and cpu used by " "pid group\n" "\t\t-d: print the diskinfo of the dir\n" "\t\t-l: the memory and cpu info output to logpath\n" "\t\t-k: the diskinfo output to logpath\n"); exit(1); } static void get_cpu(int pid, char *buf) { char filepath[FILELEN]; int ret; if (pid <= 0) { ret = snprintf(filepath, FILELEN, "%s/%s", PATH, CPULOAD); } else { ret = snprintf(filepath, FILELEN, "%s/%d/%s", PATH, pid, LOADAVG); } if (ret < 0) { /* syslog(LOG_ERR, "snprintf error\n"); */ snprintf(buf, 8, "%.1f%%", 0.0); return; } FILE *fp = fopen(filepath, "r"); if (!fp) { snprintf(buf, 8, "%.1f%%", 0.0); return; } fgets(buf, 8, fp); /* sscanf(buf, "%f", &cpu); */ buf[strlen(buf) - 1] = '\0'; fclose(fp); } static void add_cpu(char *a, char *b, char *buf) { if (a == NULL || b == NULL) { return; } float fa = 0; float fb = 0; sscanf(a, "%f", &fa); sscanf(b, "%f", &fb); float sum = fa + fb; int ret = snprintf(buf, 8, "%.1f%%", sum); if (ret < 0) { syslog(LOG_ERR, "add_cpu error\n"); } } static unsigned long get_heap(int pid) { unsigned long heap = 0; char filepath[FILELEN]; int ret = snprintf(filepath, FILELEN, "%s/%d/%s", PATH, pid, HEAP); if (ret < 0) { return heap; } FILE *fp = fopen(filepath, "r"); if (!fp) { return heap; } char buf[25]; while (fgets(buf, 25, fp) != NULL) { if (strstr(buf, "AllocSize")) { int idx = strcspn(buf, "0123456789"); heap = strtoul(&buf[idx], NULL, 10); break; } } fclose(fp); return heap; } static unsigned long get_stack(int pid) { unsigned long stack = 0; char filepath[FILELEN]; int ret = snprintf(filepath, FILELEN, "%s/%d/%s", PATH, pid, STACK); if (ret < 0) { return stack; } FILE *fp = fopen(filepath, "r"); if (!fp) { return stack; } char buf[25]; while (fgets(buf, 25, fp) != NULL) { if (strstr(buf, "StackUsed")) { int idx = strcspn(buf, "0123456789"); stack = strtoul(&buf[idx], NULL, 10); break; } } fclose(fp); return stack; } static int get_total_info(struct t_info *t_info) { memset(t_info, 0, sizeof(struct t_info)); struct mallinfo g_alloc_info = mallinfo(); t_info->mem_total = g_alloc_info.arena; t_info->mem_used = g_alloc_info.uordblks; t_info->mem_free = g_alloc_info.fordblks; t_info->mem_largest = g_alloc_info.mxordblk; t_info->mem_nused = g_alloc_info.aordblks; t_info->mem_nfree = g_alloc_info.ordblks; t_info->mem_maxused = g_alloc_info.usmblks; get_cpu(0, t_info->total_cpu); return 0; } static int get_pid_info(struct p_info *p_info, int pid, bool all) { memset(p_info, 0, sizeof(struct p_info)); if (!all) { get_cpu(pid, p_info->cpu); p_info->heap_used = get_heap(pid); p_info->stack_used = get_stack(pid); } else { char filepath[FILELEN]; int ret = snprintf(filepath, FILELEN, "%s/%d/%s", PATH, pid, GROUP); if (ret < 0) { return -1; } FILE *fp = fopen(filepath, "r"); if (!fp) { return -1; } char buf[100]; while (fgets(buf, 100, fp) != NULL) { if (strstr(buf, "Member IDs")) { int idx = strcspn(buf, ":"); char *p = strtok(&buf[idx], " "); while ((p = strtok(NULL, " ")) != NULL) { int gpid = strtoul(p, NULL, 10); char gcpu[8] = "0.0%%"; if (gpid > 0) { get_cpu(gpid, gcpu); } add_cpu(p_info->cpu, gcpu, p_info->cpu); p_info->heap_used += get_heap(gpid); p_info->stack_used += get_stack(gpid); } break; } } fclose(fp); } return 0; } static void print_result(struct t_info *t_info, struct p_info *p_info, char *logpath) { if (strlen(logpath) != 0) { time_t rawtime = 0; struct tm info; char date[30]; time(&rawtime); localtime_r(&rawtime, &info); strftime(date, 30, "[%Y-%m-%d %H:%M:%S] ", &info); FILE *f; if ((f = fopen(logpath, "a+")) != NULL) { if (p_head == 1) { p_head = 0; if (p_info == NULL) { fprintf(f, "%22s%13s%11s%11s%11s%11s%7s%7s%7s\n", "", "total", "used", "free", "maxused", "largest", "nused", "nfree", "cpu"); } else { fprintf(f, "%22s%13s%11s%11s%11s%11s%7s%7s%7s%7s%11s%7s\n", "", "total", "used", "free", "maxused", "largest", "nused", "nfree", "cpu", "pstack", "pheap", "pcpu"); } } if (p_info == NULL) { fprintf(f, "%-22s%13lu%11lu%11lu%11lu%11lu%7lu%7lu%7s\n", date, t_info->mem_total, t_info->mem_used, t_info->mem_free, t_info->mem_maxused, t_info->mem_largest, t_info->mem_nused, t_info->mem_nfree, t_info->total_cpu); } else { fprintf( f, "%-22s%13lu%11lu%11lu%11lu%11lu%7lu%7lu%7s%7lu%11lu%7s\n", date, t_info->mem_total, t_info->mem_used, t_info->mem_free, t_info->mem_maxused, t_info->mem_largest, t_info->mem_nused, t_info->mem_nfree, t_info->total_cpu, p_info->stack_used, p_info->heap_used, p_info->cpu); } fclose(f); } else { syslog(LOG_ERR, "fopen logpath %s error \n", logpath); } return; } if (p_info == NULL) { syslog(LOG_INFO, "%11s%11s%11s%11s%11s%7s%7s%7s\n", "total", "used", "free", "maxused", "largest", "nused", "nfree", "cpu"); syslog(LOG_INFO, "%11lu%11lu%11lu%11lu%11lu%7lu%7lu%7s\n\n", t_info->mem_total, t_info->mem_used, t_info->mem_free, t_info->mem_maxused, t_info->mem_largest, t_info->mem_nused, t_info->mem_nfree, t_info->total_cpu); } else { syslog(LOG_INFO, "%11s%11s%11s%11s%11s%7s%7s%7s%7s%11s%7s\n", "total", "used", "free", "maxused", "largest", "nused", "nfree", "cpu", "pstack", "pheap", "pcpu"); syslog(LOG_INFO, "%11lu%11lu%11lu%11lu%11lu%7lu%7lu%7s%7lu%11lu%7s\n\n", t_info->mem_total, t_info->mem_used, t_info->mem_free, t_info->mem_maxused, t_info->mem_largest, t_info->mem_nused, t_info->mem_nfree, t_info->total_cpu, p_info->stack_used, p_info->heap_used, p_info->cpu); } } static void transfer(uintmax_t value, char *res, int len) { memset(res, 0, len); if (value > 1024 * 1024) { int ret = snprintf(res, len, "%ju", value / 1024 / 1024); if (ret < 0) { return; } strcat(res, "M"); } else if (value > 1024) { int ret = snprintf(res, len, "%ju", value / 1024); if (ret < 0) { return; } strcat(res, "K"); } else { int ret = snprintf(res, len, "%ju", value); if (ret < 0) { return; } strcat(res, "B"); } } static void print_diskinfo(char *path, char *logpath) { struct statfs diskinfo; int ret = statfs(path, &diskinfo); if (ret != 0) { syslog(LOG_ERR, "statfs fail!\n"); return; } char c_tsize[20]; char c_asize[20]; char c_bsize[20]; char c_fsize[20]; uintmax_t bsize = (uintmax_t)diskinfo.f_bsize; uintmax_t bavail = (uintmax_t)diskinfo.f_bavail; uintmax_t blocks = (uintmax_t)diskinfo.f_blocks; uintmax_t bfree = (uintmax_t)diskinfo.f_bfree; transfer(bsize * blocks, c_tsize, 20); transfer(bsize * bavail, c_asize, 20); transfer(bsize * bfree, c_fsize, 20); transfer(bsize, c_bsize, 20); if (strlen(logpath) != 0) { time_t rawtime = 0; struct tm info; char date[30]; time(&rawtime); localtime_r(&rawtime, &info); strftime(date, 30, "[%Y-%m-%d %H:%M:%S] ", &info); FILE *f; if ((f = fopen(logpath, "a+")) != NULL) { if (d_head == 1) { d_head = 0; fprintf(f, "%22s%11s%11s%11s%11s%11s\n", "", "dir", "total size", "free size", "avail size", "block size"); } fprintf(f, "%-22s%11s%11s%11s%11s%11s\n", date, path, c_tsize, c_fsize, c_asize, c_bsize); fclose(f); } else { syslog(LOG_ERR, "fopen logpath %s error \n", logpath); } } else { syslog(LOG_INFO, "%11s%11s%11s%11s%11s\n", "dir", "total size", "free size", "avail size", "block size"); syslog(LOG_INFO, "%11s%11s%11s%11s%11s\n\n", path, c_tsize, c_fsize, c_asize, c_bsize); } } int main(int argc, char *argv[]) { if (argc == 1) { show_usages(); } char filepath[FILELEN]; char logpath_l[FILELEN]; char logpath_k[FILELEN]; struct timeval sleeptime; struct t_info total_info; struct p_info pid_info; int interval = 1; int pid = -1; bool all = false; int ret; int o; memset(filepath, 0, FILELEN); memset(logpath_l, 0, FILELEN); memset(logpath_k, 0, FILELEN); go = 1; p_head = 1; d_head = 1; while ((o = getopt(argc, argv, "i:p:ad:l:k:")) != EOF) { switch (o) { case 'i': interval = atoi(optarg); break; case 'p': pid = atoi(optarg); break; case 'a': all = true; break; case 'd': snprintf(filepath, FILELEN, "%s", optarg); break; case 'l': snprintf(logpath_l, FILELEN, "%s", optarg); break; case 'k': snprintf(logpath_k, FILELEN, "%s", optarg); break; default: show_usages(); break; } } signal(SIGINT, handler); signal(SIGKILL, handler); while (go) { ret = get_total_info(&total_info); if (ret < 0) { syslog(LOG_ERR, "get total info fail\n"); break; } if (pid > 0) { ret = get_pid_info(&pid_info, pid, all); if (ret < 0) { syslog(LOG_ERR, "get pid info fail\n"); break; } } if (all || pid > 0) { print_result(&total_info, &pid_info, logpath_l); } else { print_result(&total_info, NULL, logpath_l); } if (strlen(filepath) != 0) { print_diskinfo(filepath, logpath_k); } sleeptime.tv_sec = interval; sleeptime.tv_usec = 0; select(0, NULL, NULL, NULL, &sleeptime); } syslog(LOG_INFO, "program complete!\n"); return 0; }