mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-09 06:25:16 +00:00
Keep the default zero CPU value when the procfs load file cannot be read, and trim only the newline that was actually present. This avoids parsing uninitialized stack data in fillcpu and avoids writing before the showinfo CPU buffer when fgets returns no data. Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com>
655 lines
16 KiB
C
655 lines
16 KiB
C
/****************************************************************************
|
|
* 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 Types
|
|
****************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
if (fgets(buf, 8, fp) == NULL)
|
|
{
|
|
snprintf(buf, 8, "%.1f%%", 0.0);
|
|
fclose(fp);
|
|
return;
|
|
}
|
|
|
|
/* sscanf(buf, "%f", &cpu); */
|
|
|
|
buf[strcspn(buf, "\n")] = '\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;
|
|
}
|