Last active
March 13, 2022 09:03
-
-
Save kala13x/104feda02854169135595bf30028cd22 to your computer and use it in GitHub Desktop.
Advanced system monitor with network, memory and CPU statistics in one window
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* @file libxutils/examples/xtop.c | |
* | |
* This source is part of "libxutils" project | |
* 2015-2022 Sun Dro ([email protected]) | |
* | |
* @brief Implementation of advanced system monitor based on the xUtils. | |
* Collect and monitor network, memory and CPU statistics in one window. | |
*/ | |
#include <xutils/xstd.h> | |
#include <xutils/xstr.h> | |
#include <xutils/xcli.h> | |
#include <xutils/xlog.h> | |
#include <xutils/xtop.h> | |
#include <xutils/addr.h> | |
#include <xutils/xver.h> | |
#include <xutils/xfs.h> | |
#define XTOP_VERSION_MAJ 0 | |
#define XTOP_VERSION_MIN 6 | |
#define XTOP_SORT_DISABLE 0 | |
#define XTOP_SORT_BUSY 1 | |
#define XTOP_SORT_FREE 2 | |
#define XTOP_SORT_NAME 3 | |
#define XTOP_SORT_LEN 4 | |
#define XTOP_CPU_HEADER " "\ | |
"CPU IDL "\ | |
"US KS "\ | |
"NI SI "\ | |
"HI IO "\ | |
"ST GT GN" | |
#define XTOP_IFACE_HEADER \ | |
"IFACE "\ | |
"RX "\ | |
"TX "\ | |
"SUM "\ | |
"MAC IP" | |
static int g_nInterrupted = 0; | |
extern char *optarg; | |
typedef struct xtop_args_ { | |
xbool_t bExcludeCPU; | |
size_t nIntervalU; | |
uint8_t nSort; | |
xpid_t nPID; | |
char sLink[XLINK_MAX]; | |
char sName[XNAME_MAX]; | |
} xtop_args_t; | |
void XTOPApp_SignalCallback(int sig) | |
{ | |
if (sig == 2) printf("\n"); | |
g_nInterrupted = 1; | |
} | |
static char *XTOPApp_WhiteSpace(const int nLength) | |
{ | |
static char sRetVal[XSTR_TINY]; | |
xstrnul(sRetVal); | |
int i = 0; | |
int nLen = XSTD_MIN(nLength, sizeof(sRetVal) - 1); | |
for (i = 0; i < nLen; i++) sRetVal[i] = ' '; | |
sRetVal[i] = '\0'; | |
return sRetVal; | |
} | |
void XTOPApp_DisplayUsage(const char *pName) | |
{ | |
int nLength = strlen(pName) + 6; | |
printf("==================================================================\n"); | |
printf("XTOP v%d.%d - (c) 2022 Sandro Kalatozishvili ([email protected])\n", | |
XTOP_VERSION_MAJ, XTOP_VERSION_MIN); | |
printf("==================================================================\n\n"); | |
printf("CPU usage bar: %s[%s%slow-priority/%s%snormal/%s%skernel/%s%svirtualized%s %sused%%%s%s]%s\n", | |
XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_BLUE, XSTR_FMT_RESET, XSTR_CLR_GREEN, XSTR_FMT_RESET, XSTR_CLR_RED, | |
XSTR_FMT_RESET, XSTR_CLR_CYAN, XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); | |
printf("Memory bar: %s[%s%sused/%s%sbuffers/%s%sshared/%s%scache%s %sused/total%s%s]%s\n", | |
XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_GREEN, XSTR_FMT_RESET, XSTR_CLR_BLUE, XSTR_FMT_RESET, XSTR_CLR_MAGENTA, | |
XSTR_FMT_RESET, XSTR_CLR_YELLOW, XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); | |
printf("Swap bar: %s[%s%sused/%s%scache%s %sused/total%s%s]%s\n\n", | |
XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_RED, XSTR_FMT_RESET, XSTR_CLR_YELLOW, | |
XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); | |
printf("Usage: %s [-i <name>] [-p <pid>] [-m <sec>]\n", pName); | |
printf(" %s [-r <link>] [-s <type>] [-c] [-h]\n\n", XTOPApp_WhiteSpace(nLength)); | |
printf("Options are:\n"); | |
printf(" %s-i%s <name> # Interface name to display on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-p%s <pid> # Track process CPU and memory usage\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-m%s <sec> # Monitoring interval seconds\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-r%s <link> # Remote endpoint link to monitor\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-s%s <type> # Sort result by selected type\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-c%s # Exclude additional CPU info \n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %s-h%s # Print version and usage\n\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf("Sort types:\n"); | |
printf(" %sb%s: Busy on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %sf%s: Free on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf(" %sn%s: Sort by name\n\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); | |
printf("Examples:\n"); | |
printf("1) %s -m 2 -s b -p 2274\n", pName); | |
printf("2) %s -s b -p 2274 -i enp4s0\n", pName); | |
printf("3) %s -r http://remote.srv/endpint/\n\n", pName); | |
} | |
uint8_t XTOPApp_GetSortType(const char *pArg) | |
{ | |
if (pArg == NULL) return XTOP_SORT_DISABLE; | |
else if (*pArg == 'b') return XTOP_SORT_BUSY; | |
else if (*pArg == 'f') return XTOP_SORT_FREE; | |
else if (*pArg == 'n') return XTOP_SORT_NAME; | |
return XTOP_SORT_DISABLE; | |
} | |
int XTOPApp_ParseArgs(xtop_args_t *pArgs, int argc, char *argv[]) | |
{ | |
pArgs->bExcludeCPU = XFALSE; | |
pArgs->nSort = XTOP_SORT_LEN; | |
xstrnul(pArgs->sLink); | |
xstrnul(pArgs->sName); | |
pArgs->nIntervalU = 0; | |
pArgs->nPID = 0; | |
int nChar = 0; | |
while ((nChar = getopt(argc, argv, "i:p:m:r:s:c1:h1")) != -1) | |
{ | |
switch (nChar) | |
{ | |
case 'i': | |
xstrncpy(pArgs->sName, sizeof(pArgs->sName), optarg); | |
break; | |
case 'r': | |
xstrncpy(pArgs->sLink, sizeof(pArgs->sLink), optarg); | |
break; | |
case 's': | |
pArgs->nSort = XTOPApp_GetSortType(optarg); | |
break; | |
case 'm': | |
pArgs->nIntervalU = atoi(optarg); | |
break; | |
case 'p': | |
pArgs->nPID = atoi(optarg); | |
break; | |
case 'c': | |
pArgs->bExcludeCPU = XTRUE; | |
break; | |
case 'h': | |
default: | |
return 0; | |
} | |
} | |
if (!pArgs->nIntervalU) pArgs->nIntervalU = XTOP_INTERVAL_USEC; | |
else pArgs->nIntervalU *= XTOP_INTERVAL_USEC; | |
return XTRUE; | |
} | |
int XTOPApp_CompareCPUs(const void *pData1, const void *pData2, void *pContext) | |
{ | |
xtop_args_t *pArgs = (xtop_args_t*)pContext; | |
xcpu_info_t *pInfo1 = (xcpu_info_t*)((xarray_data_t*)pData1)->pData; | |
xcpu_info_t *pInfo2 = (xcpu_info_t*)((xarray_data_t*)pData2)->pData; | |
return (pArgs->nSort == XTOP_SORT_BUSY) ? | |
(int)pInfo1->nIdleTime - (int)pInfo2->nIdleTime: | |
(int)pInfo2->nIdleTime - (int)pInfo1->nIdleTime; | |
} | |
int XTOPApp_CompareIFaces(const void *pData1, const void *pData2, void *pContext) | |
{ | |
xtop_args_t *pArgs = (xtop_args_t*)pContext; | |
xnet_iface_t *pIface1 = (xnet_iface_t*)((xarray_data_t*)pData1)->pData; | |
xnet_iface_t *pIface2 = (xnet_iface_t*)((xarray_data_t*)pData2)->pData; | |
if (pArgs->nSort == XTOP_SORT_LEN) return (int)strlen(pIface1->sName) - (int)strlen(pIface2->sName); | |
else if (pArgs->nSort == XTOP_SORT_NAME) return strcmp(pIface1->sName, pIface2->sName); | |
int nData1 = (int)pIface1->nBytesReceivedPerSec + (int)pIface1->nBytesSentPerSec; | |
int nData2 = (int)pIface2->nBytesReceivedPerSec + (int)pIface2->nBytesSentPerSec; | |
return (pArgs->nSort == XTOP_SORT_BUSY) ? nData2 - nData1 : nData1 - nData2; | |
} | |
int XTOPApp_FillCPUBar(xcli_bar_t *pBar, xcpu_info_t *pCore, char *pDst, size_t nSize) | |
{ | |
if (pDst == NULL || !nSize) return XSTDNON; | |
char sNormal[XLINE_MAX], sKernel[XLINE_MAX]; | |
char sVirt[XLINE_MAX], sLow[XLINE_MAX]; | |
/* Unpack raw percentage information */ | |
float fLow = XU32ToFloat(pCore->nUserSpaceNiced); | |
float fVirt = XU32ToFloat(pCore->nStealTime); | |
float fNormal = XU32ToFloat(pCore->nUserSpace); | |
float fKernel = XU32ToFloat(pCore->nKernelSpace); | |
fKernel += XU32ToFloat(pCore->nSoftInterrupts); | |
fKernel += XU32ToFloat(pCore->nHardInterrupts); | |
fKernel += XU32ToFloat(pCore->nIOWait); | |
/* Calculate percentage */ | |
size_t nMaxSize = pBar->nBarLength; | |
size_t nNormal = nMaxSize * (size_t)floor(fNormal) / 100; | |
size_t nKernel = nMaxSize * (size_t)floor(fKernel) / 100; | |
size_t nVirt = nMaxSize * (size_t)floor(fVirt) / 100; | |
size_t nLow = nMaxSize * (size_t)floor(fLow) / 100; | |
size_t nSum = nLow + nVirt + nNormal + nKernel; | |
/* Round the calculated results to improve bar fill accurracy */ | |
if (fNormal > 0. && !nNormal && nSum < nMaxSize) { nNormal++; nSum++; } | |
if (fKernel > 0. && !nKernel && nSum < nMaxSize) { nKernel++; nSum++; } | |
if (fVirt > 0. && !nVirt && nSum < nMaxSize) { nVirt++; nSum++; } | |
if (fLow > 0. && !nLow && nSum < nMaxSize) nLow++; | |
/* Fill partial results with the bar used character */ | |
xstrfill(sNormal, sizeof(sNormal), nNormal, pBar->cLoader); | |
xstrfill(sKernel, sizeof(sKernel), nKernel, pBar->cLoader); | |
xstrfill(sVirt, sizeof(sVirt), nVirt, pBar->cLoader); | |
xstrfill(sLow, sizeof(sLow), nLow, pBar->cLoader); | |
/* Create colorized line for CPU usage bar */ | |
return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s%s%s%s%s%s%s", | |
XSTR_CLR_BLUE, sLow, XSTR_FMT_RESET, | |
XSTR_CLR_GREEN, sNormal, XSTR_FMT_RESET, | |
XSTR_CLR_RED, sKernel, XSTR_FMT_RESET, | |
XSTR_CLR_CYAN, sVirt, XSTR_FMT_RESET); | |
} | |
XSTATUS XTOPApp_AddCPULoadBar(xcli_wind_t *pWin, xcli_bar_t *pBar, xcpu_stats_t *pCPU) | |
{ | |
char sFirst[XLINE_MAX], sSecond[XLINE_MAX], sUsed[XLINE_MAX]; | |
uint16_t i, nNext = pCPU->nCoreCount; | |
uint16_t nEdge = 0, nUsedCount = 0; | |
xstrnul(pBar->sSuffix); | |
xstrnul(sSecond); | |
xstrnul(sFirst); | |
XProgBar_UpdateWindowSize(pBar); | |
pBar->frameSize.nWinColumns /= 2; | |
for (i = 0; i < pCPU->nCoreCount; i++) | |
{ | |
xcpu_info_t *pCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, i); | |
if (pCore != NULL) | |
{ | |
if (nUsedCount >= pCPU->nCoreCount) break; | |
else if (nEdge && i == nEdge) continue; | |
nNext = i + pCPU->nCoreCount / 2; | |
if (!nEdge) nEdge = nNext; | |
nUsedCount++; | |
char sCore[XSTR_TINY]; | |
xstrnlcpyf(sCore, sizeof(sCore), 5, XSTR_SPACE_CHAR, "%d", pCore->nID); | |
xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", sCore); | |
pBar->fPercent = XU32ToFloat(pCore->nUserSpace) + XU32ToFloat(pCore->nUserSpaceNiced); | |
pBar->fPercent += XU32ToFloat(pCore->nKernelSpace) + XU32ToFloat(pCore->nSoftInterrupts); | |
pBar->fPercent += XU32ToFloat(pCore->nHardInterrupts) + XU32ToFloat(pCore->nIOWait); | |
pBar->fPercent += XU32ToFloat(pCore->nStealTime); | |
xstrnul(sUsed); | |
xbool_t bHidePct = XProgBar_CalculateBounds(pBar); | |
XTOPApp_FillCPUBar(pBar, pCore, sUsed, sizeof(sUsed)); | |
XProgBar_GetOutputAdv(pBar, sFirst, sizeof(sFirst), sUsed, bHidePct); | |
if (i == nNext || nNext >= pCPU->nCoreCount) | |
{ | |
xstrfill(sSecond, sizeof(sSecond), pBar->frameSize.nWinColumns, XSTR_SPACE_CHAR); | |
return XWindow_AddLineFmt(pWin, "%s%s", sFirst, sSecond); | |
} | |
xcpu_info_t *pSecondCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, nNext); | |
if (pSecondCore != NULL) | |
{ | |
xstrnlcpyf(sCore, sizeof(sCore), 5, XSTR_SPACE_CHAR, "%d", pSecondCore->nID); | |
xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", sCore); | |
pBar->fPercent = XU32ToFloat(pSecondCore->nUserSpace) + XU32ToFloat(pSecondCore->nUserSpaceNiced); | |
pBar->fPercent += XU32ToFloat(pSecondCore->nKernelSpace) + XU32ToFloat(pSecondCore->nSoftInterrupts); | |
pBar->fPercent += XU32ToFloat(pSecondCore->nHardInterrupts) + XU32ToFloat(pSecondCore->nIOWait); | |
pBar->fPercent += XU32ToFloat(pSecondCore->nStealTime); | |
xstrnul(sUsed); | |
xbool_t bHidePct = XProgBar_CalculateBounds(pBar); | |
XTOPApp_FillCPUBar(pBar, pSecondCore, sUsed, sizeof(sUsed)); | |
XProgBar_GetOutputAdv(pBar, sSecond, sizeof(sSecond), sUsed, bHidePct); | |
XWindow_AddLineFmt(pWin, "%s%s", sFirst, sSecond); | |
nUsedCount++; | |
} | |
} | |
} | |
return XSTDOK; | |
} | |
int XTOPApp_FillMemoryBar(xcli_bar_t *pBar, xmem_info_t *pMemInfo, char *pDst, size_t nSize) | |
{ | |
if (pDst == NULL || !nSize) return XSTDNON; | |
char sBuffers[XLINE_MAX], sUsed[XLINE_MAX]; | |
char sShared[XLINE_MAX], sCached[XLINE_MAX]; | |
size_t nMaxSize = pBar->nBarLength; | |
size_t nMaxUsed = pBar->nBarUsed; | |
size_t nTotalUsed = pMemInfo->nMemoryTotal - pMemInfo->nMemoryFree; | |
size_t nCached = pMemInfo->nMemoryCached - pMemInfo->nMemoryShared; | |
size_t nUsed = nTotalUsed - (pMemInfo->nBuffers + pMemInfo->nMemoryCached); | |
double fBuffers = nTotalUsed ? (double)100 / nTotalUsed * pMemInfo->nBuffers : 0.; | |
double fShared = nTotalUsed ? (double)100 / nTotalUsed * pMemInfo->nMemoryShared : 0.; | |
double fCached = nTotalUsed ? (double)100 / nTotalUsed * nCached : 0.; | |
double fUsed = nTotalUsed ? (double)100 / nTotalUsed * nUsed : 0.; | |
size_t nBuffersPct = nMaxUsed * (size_t)floor(fBuffers) / 100; | |
size_t nSharedPct = nMaxUsed * (size_t)floor(fShared) / 100; | |
size_t nCachedPct = nMaxUsed * (size_t)floor(fCached) / 100; | |
size_t nUsedPct = nMaxUsed * (size_t)floor(fUsed) / 100; | |
size_t nSum = nUsedPct + nSharedPct + nBuffersPct + nCachedPct; | |
/* Round the calculated results to improve bar fill accurracy */ | |
if (fBuffers > 0. && !nBuffersPct && nSum < nMaxSize) { nBuffersPct++; nSum++; } | |
if (fShared > 0. && !nSharedPct && nSum < nMaxSize) { nSharedPct++; nSum++; } | |
if (fCached > 0. && !nCachedPct && nSum < nMaxSize) { nCachedPct++; nSum++; } | |
if (fUsed > 0. && !nUsedPct && nSum < nMaxSize) nUsedPct++; | |
xstrfill(sBuffers, sizeof(sBuffers), nBuffersPct, pBar->cLoader); | |
xstrfill(sShared, sizeof(sShared), nSharedPct, pBar->cLoader); | |
xstrfill(sCached, sizeof(sCached), nCachedPct, pBar->cLoader); | |
xstrfill(sUsed, sizeof(sUsed), nUsedPct, pBar->cLoader); | |
return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s%s%s%s%s%s%s", | |
XSTR_CLR_GREEN, sUsed, XSTR_FMT_RESET, | |
XSTR_CLR_BLUE, sBuffers, XSTR_FMT_RESET, | |
XSTR_CLR_MAGENTA, sShared, XSTR_FMT_RESET, | |
XSTR_CLR_YELLOW, sCached, XSTR_FMT_RESET); | |
} | |
int XTOPApp_FillSwapBar(xcli_bar_t *pBar, xmem_info_t *pMemInfo, char *pDst, size_t nSize) | |
{ | |
if (pDst == NULL || !nSize) return XSTDNON; | |
char sUsed[XLINE_MAX], sCached[XLINE_MAX]; | |
size_t nMaxSize = pBar->nBarLength; | |
size_t nMaxUsed = pBar->nBarUsed; | |
/* Calculate swap and cache usage percents */ | |
size_t nSwapUsed = pMemInfo->nSwapTotal - pMemInfo->nSwapFree - pMemInfo->nSwapCached; | |
double fCached = nSwapUsed ? (double)100 / nSwapUsed * pMemInfo->nSwapCached : 0.; | |
double fUsed = nSwapUsed ? (double)100 / pMemInfo->nSwapTotal * nSwapUsed : 0.; | |
/* Calculate swap and cached percents in usage bar */ | |
size_t nCachedPct = nMaxUsed * (size_t)floor(fCached) / 100; | |
size_t nUsedPct = nMaxUsed * (size_t)floor(fUsed) / 100; | |
size_t nSum = nUsedPct + nCachedPct; | |
/* Round the calculated results to improve bar fill accurracy */ | |
if (fCached > 0. && !nCachedPct && nSum < nMaxSize) { nCachedPct++; nSum++; } | |
if (fUsed > 0. && !nUsedPct && nSum < nMaxSize) nUsedPct++; | |
/* Fill partial results with the bar used character */ | |
xstrfill(sCached, sizeof(sCached), nCachedPct, pBar->cLoader); | |
xstrfill(sUsed, sizeof(sUsed), nUsedPct, pBar->cLoader); | |
return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s", | |
XSTR_CLR_RED, sUsed, XSTR_FMT_RESET, | |
XSTR_CLR_YELLOW, sCached, XSTR_FMT_RESET); | |
} | |
XSTATUS XTOPApp_AddOverallBar(xcli_wind_t *pWin, xcli_bar_t *pBar, xmem_info_t *pMemInfo, xcpu_stats_t *pCPU) | |
{ | |
if (pMemInfo->nMemoryTotal < pMemInfo->nMemoryAvail) return XSTDNON; | |
char sLine[XLINE_MAX], sBuff[XSTR_TINY]; | |
char sUsed[XSTR_TINY], sCache[XSTR_TINY]; | |
/* Calculate memory usage percentage */ | |
size_t nTotalUsed = pMemInfo->nMemoryTotal - pMemInfo->nMemoryFree; | |
size_t nUsed = nTotalUsed - (pMemInfo->nBuffers + pMemInfo->nMemoryCached); | |
pBar->fPercent = nTotalUsed ? (double)100 / pMemInfo->nMemoryTotal * nTotalUsed : 0.; | |
/* Create memory usage bar */ | |
XKBToUnit(sUsed, sizeof(sUsed), nUsed, XTRUE); | |
XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nMemoryTotal, XTRUE); | |
xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", " Mem"); | |
xstrncpyf(pBar->sSuffix, sizeof(pBar->sSuffix), | |
"%s%s/%s%s", XSTR_FMT_DIM, sUsed, sBuff, XSTR_FMT_RESET); | |
xstrnul(sUsed); | |
xbool_t bHidePct = XProgBar_CalculateBounds(pBar); | |
XTOPApp_FillMemoryBar(pBar, pMemInfo, sUsed, sizeof(sUsed)); | |
XProgBar_GetOutputAdv(pBar, sLine, sizeof(sLine), sUsed, bHidePct); | |
/* Create and append memory usage info next to memory bar */ | |
XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nBuffers, XTRUE); | |
XKBToUnit(sUsed, sizeof(sUsed), pMemInfo->nMemoryShared, XTRUE); | |
XKBToUnit(sCache, sizeof(sCache), pMemInfo->nMemoryCached, XTRUE); | |
XWindow_AddLineFmt(pWin, "%s " | |
"%sBuff:%s %s, " | |
"%sShared:%s %s, " | |
"%sCached:%s %s", sLine, | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, sBuff, | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, sUsed, | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, sCache); | |
/* Calculate swap usage percentage */ | |
if (pMemInfo->nSwapTotal < pMemInfo->nSwapFree) return XSTDNON; | |
size_t nSwapUsed = pMemInfo->nSwapTotal - pMemInfo->nSwapFree - pMemInfo->nSwapCached; | |
pBar->fPercent = nSwapUsed ? (double)100 / pMemInfo->nSwapTotal * nSwapUsed : 0.; | |
/* Create swap usage bar */ | |
XKBToUnit(sUsed, sizeof(sUsed), nSwapUsed, XTRUE); | |
XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nSwapTotal, XTRUE); | |
xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", " Swp"); | |
xstrncpyf(pBar->sSuffix, sizeof(pBar->sSuffix), | |
"%s%s/%s%s", XSTR_FMT_DIM, sUsed, sBuff, XSTR_FMT_RESET); | |
xstrnul(sUsed); | |
bHidePct = XProgBar_CalculateBounds(pBar); | |
XTOPApp_FillSwapBar(pBar, pMemInfo, sUsed, sizeof(sUsed)); | |
XProgBar_GetOutputAdv(pBar, sLine, sizeof(sLine), sUsed, bHidePct); | |
XKBToUnit(sCache, sizeof(sCache), pMemInfo->nSwapCached, XTRUE); | |
XWindow_AddLineFmt(pWin, "%s " | |
"%sSwp Cached:%s %s, " | |
"%sLoad avg:%s %s%.2f%s %s%.2f%s %s%.2f%s", | |
sLine, XSTR_CLR_CYAN, XSTR_FMT_RESET, | |
sCache, XSTR_CLR_CYAN, XSTR_FMT_RESET, | |
XSTR_FMT_BOLD, XU32ToFloat(pCPU->nLoadAvg[0]), XSTR_FMT_RESET, | |
XSTR_CLR_LIGHT_CYAN, XU32ToFloat(pCPU->nLoadAvg[1]), XSTR_FMT_RESET, | |
XSTR_CLR_LIGHT_BLUE, XU32ToFloat(pCPU->nLoadAvg[2]), XSTR_FMT_RESET); | |
/* Create half-empry line for pretty print */ | |
XProgBar_UpdateWindowSize(pBar); pBar->frameSize.nWinColumns /= 2; | |
xstrfill(sLine, sizeof(sLine), pBar->frameSize.nWinColumns, XSTR_SPACE_CHAR); | |
/* Create and append process track info next to swap bar */ | |
XKBToUnit(sUsed, sizeof(sUsed), pMemInfo->nResidentMemory, XTRUE); | |
XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nVirtualMemory, XTRUE); | |
return XWindow_AddLineFmt(pWin, "%s%sRes:%s %s, %sVirt:%s %s, %sUS:%s %.2f, %sKS:%s %.2f", sLine, | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, sUsed, XSTR_CLR_CYAN, XSTR_FMT_RESET, sBuff, | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, XU32ToFloat(pCPU->usage.nUserSpaceUsage), | |
XSTR_CLR_CYAN, XSTR_FMT_RESET, XU32ToFloat(pCPU->usage.nKernelSpace)); | |
} | |
void XTOPApp_AddCPUInfoUnit(char *pLine, size_t nSize, double fPct, xbool_t bIdle) | |
{ | |
char sBuff[XSTR_TINY]; | |
const char *pColor; | |
if (bIdle) | |
{ | |
if (fPct > 50.) pColor = XSTR_CLR_GREEN; | |
else if (fPct <= 20.) pColor = XLOG_COLOR_RED; | |
else pColor = XLOG_COLOR_YELLOW; | |
} | |
else | |
{ | |
if (fPct < 50.) pColor = XSTR_CLR_NONE; | |
else if (fPct >= 80.) pColor = XLOG_COLOR_RED; | |
else pColor = XLOG_COLOR_YELLOW; | |
} | |
size_t nIdleLen = xstrnclr(sBuff, sizeof(sBuff), pColor, "%.2f", fPct); | |
nIdleLen -= xstrextra(sBuff, nIdleLen, 0, NULL, NULL); | |
if (nIdleLen < 8) | |
{ | |
char sEmpty[XSTR_TINY]; | |
xstrfill(sEmpty, sizeof(sEmpty), 8 - nIdleLen, XSTR_SPACE_CHAR); | |
xstrncat(pLine, nSize, "%s%s", sEmpty, sBuff); | |
} | |
} | |
XSTATUS XTOPApp_AddCPUInfo(xcli_wind_t *pWin, xcpu_info_t *pCore) | |
{ | |
char sLine[XLINE_MAX]; | |
char sCore[XSTR_TINY]; | |
if (pCore->nID >= 0) | |
{ | |
xstrnlcpyf(sCore, sizeof(sCore), 4, XSTR_SPACE_CHAR, "%d", pCore->nID); | |
xstrncpyf(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sCore, XSTR_FMT_RESET); | |
} | |
else | |
{ | |
xstrnlcpyf(sCore, sizeof(sCore), 4, XSTR_SPACE_CHAR, "%s", "s"); | |
xstrncpyf(sLine, sizeof(sLine), "%s%s%s%s", XSTR_FMT_BOLD, XSTR_FMT_ITALIC, sCore, XSTR_FMT_RESET); | |
} | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nIdleTime), XTRUE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nUserSpace), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nKernelSpace), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nUserSpaceNiced), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nSoftInterrupts), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nHardInterrupts), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nIOWait), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nStealTime), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nGuestTime), XFALSE); | |
XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nGuestNiced), XFALSE); | |
return XWindow_AddLineFmt(pWin, "%s", sLine); | |
} | |
XSTATUS XTOPApp_AddCPUExtra(xcli_wind_t *pWin, xtop_args_t *pArgs, xcli_bar_t *pBar, xmem_info_t *pMemInfo, xcpu_stats_t *pCPU) | |
{ | |
XWindow_AddAligned(pWin, XTOP_CPU_HEADER, XSTR_BACK_BLUE, XCLI_LEFT); | |
XSTATUS nStatus = XTOPApp_AddCPUInfo(pWin, &pCPU->sum); | |
if (nStatus <= 0) return nStatus; | |
if (pArgs->nSort && pCPU->nCoreCount && | |
pArgs->nSort != XTOP_SORT_NAME && | |
pArgs->nSort != XTOP_SORT_LEN) | |
XArray_Sort(&pCPU->cores, XTOPApp_CompareCPUs, pArgs); | |
uint16_t i; | |
for (i = 0; i < pCPU->nCoreCount; i++) | |
{ | |
xcpu_info_t *pCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, i); | |
if (pCore != NULL) nStatus = XTOPApp_AddCPUInfo(pWin, pCore); | |
} | |
return nStatus; | |
} | |
XSTATUS XTOPApp_AddInterface(xcli_wind_t *pWin, xtop_args_t *pArgs, xnet_iface_t *pIface, size_t nLength) | |
{ | |
char sLine[XLINE_MAX], sName[XSTR_TINY], sRound[XSTR_TINY], sData[XSTR_TINY]; | |
xstrnlcpyf(sName, sizeof(sName), nLength + 1, XSTR_SPACE_CHAR, "%s", pIface->sName); | |
xstrncpy(sLine, sizeof(sLine), sName); | |
XBytesToUnit(sData, sizeof(sData), pIface->nBytesReceivedPerSec, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
XBytesToUnit(sData, sizeof(sData), pIface->nBytesSentPerSec, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
uint64_t nSum = pIface->nBytesReceivedPerSec + pIface->nBytesSentPerSec; | |
XBytesToUnit(sData, sizeof(sData), nSum, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
xstrnlcpyf(sRound, sizeof(sRound), strlen(pIface->sHWAddr) + 8, XSTR_SPACE_CHAR, "%s", pIface->sHWAddr); | |
if (strncmp(pIface->sHWAddr, XNET_HWADDR_DEFAULT, 17)) xstrncat(sLine, sizeof(sLine), "%s", sRound); | |
else xstrncat(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sRound, XSTR_FMT_RESET); | |
xstrnlcpyf(sRound, sizeof(sRound), strlen(pIface->sIPAddr) + 8, XSTR_SPACE_CHAR, "%s", pIface->sIPAddr); | |
if (strncmp(pIface->sIPAddr, XNET_IPADDR_DEFAULT, 7)) xstrncat(sLine, sizeof(sLine), "%s", sRound); | |
else xstrncat(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sRound, XSTR_FMT_RESET); | |
return XWindow_AddLineFmt(pWin, "%s", sLine); | |
} | |
XSTATUS XTOPApp_AddNetworkInfo(xcli_wind_t *pWin, xtop_args_t *pArgs, xarray_t *pIfaces) | |
{ | |
if (pArgs->nSort) XArray_Sort(pIfaces, XTOPApp_CompareIFaces, pArgs); | |
size_t nTrackLen = strlen(pArgs->sName); | |
size_t i, nLength = 0; | |
int nTrackID = -1; | |
size_t nSumRX, nSumTX; | |
nSumRX = nSumTX = 0; | |
for (i = 0; i < pIfaces->nUsed; i++) | |
{ | |
xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, i); | |
if (pIface == NULL) continue; | |
nSumRX += pIface->nBytesReceivedPerSec; | |
nSumTX += pIface->nBytesSentPerSec; | |
if (xstrused(pIface->sName) == XTRUE && nTrackLen > 0 && nTrackID < 0 && | |
!strncmp(pArgs->sName, pIface->sName, nTrackLen)) nTrackID = (int)i; | |
size_t nNextLength = strlen(pIface->sName); | |
if (nNextLength > nLength) nLength = nNextLength; | |
} | |
char sLine[XLINE_MAX], sRound[XSTR_TINY], sData[XSTR_TINY]; | |
size_t nPreHdr = nLength > 4 ? nLength - 4 : nLength; | |
xstrfill(sLine, sizeof(sLine), nPreHdr, XSTR_SPACE_CHAR); | |
xstrncat(sLine, sizeof(sLine), "%s", XTOP_IFACE_HEADER); | |
XWindow_AddAligned(pWin, sLine, XSTR_BACK_BLUE, XCLI_LEFT); | |
if (nTrackID >= 0) | |
{ | |
xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, nTrackID); | |
if (pIface != NULL) XTOPApp_AddInterface(pWin, pArgs, pIface, nLength); | |
} | |
for (i = 0; i < pIfaces->nUsed; i++) | |
{ | |
if (nTrackID >= 0 && i == nTrackID) continue; | |
xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, i); | |
if (pIface != NULL) XTOPApp_AddInterface(pWin, pArgs, pIface, nLength); | |
} | |
xstrnlcpyf(sLine, sizeof(sLine), nLength + 1, XSTR_SPACE_CHAR, "%s", "total"); | |
XBytesToUnit(sData, sizeof(sData), nSumRX, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
XBytesToUnit(sData, sizeof(sData), nSumTX, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
XBytesToUnit(sData, sizeof(sData), nSumRX + nSumTX, XFALSE); | |
xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); | |
xstrncat(sLine, sizeof(sLine), "%s/s", sRound); | |
return XWindow_AddAligned(pWin, sLine, XSTR_CLR_LIGHT_CYAN, XCLI_LEFT); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
xlog_defaults(); | |
xtop_args_t args; | |
xtop_stats_t stats; | |
if (!XTOPApp_ParseArgs(&args, argc, argv)) | |
{ | |
XTOPApp_DisplayUsage(argv[0]); | |
return XSTDERR; | |
} | |
if (xstrused(args.sName)) | |
{ | |
char sIfcPath[XPATH_MAX]; | |
xstrncpyf(sIfcPath, sizeof(sIfcPath), "%s/%s", XSYS_CLASS_NET, args.sName); | |
if (!XPath_Exists(sIfcPath)) | |
{ | |
xloge("Interface not found: %s", args.sName); | |
return XSTDERR; | |
} | |
} | |
if (xstrused(args.sLink)) | |
{ | |
xlog("Remote endoint feature is coming soon..."); | |
return XSTDERR; | |
} | |
signal(SIGTERM, XTOPApp_SignalCallback); | |
signal(SIGINT, XTOPApp_SignalCallback); | |
int nStatus = XTop_StartMonitoring(&stats, args.nIntervalU, args.nPID); | |
if (nStatus < 0) | |
{ | |
xloge("PID not found: %d", args.nPID); | |
return XSTDERR; | |
} | |
else if (!nStatus) | |
{ | |
xloge("Failed to start monitoring thread: %d", errno); | |
return XSTDERR; | |
} | |
xcli_wind_t win; | |
XWindow_Init(&win); | |
XTop_WaitLoad(&stats, 1000); | |
xcli_bar_t bar; | |
XProgBar_GetDefaults(&bar); | |
bar.bInPercent = XTRUE; | |
bar.bInSuffix = XTRUE; | |
bar.cLoader = '|'; | |
while (!g_nInterrupted) | |
{ | |
XWindow_AddAligned(&win, "[XTOP]", XSTR_BACK_BLUE, XCLI_CENTER); | |
XWindow_AddEmptyLine(&win); | |
xcpu_stats_t cpuStats; | |
if (XTop_GetCPUStats(&stats, &cpuStats) > 0) | |
{ | |
xmem_info_t memInfo; | |
XTop_GetMemoryInfo(&stats, &memInfo); | |
XTOPApp_AddCPULoadBar(&win, &bar, &cpuStats); | |
XTOPApp_AddOverallBar(&win, &bar, &memInfo, &cpuStats); | |
if (!args.bExcludeCPU) | |
{ | |
XWindow_AddEmptyLine(&win); | |
XTOPApp_AddCPUExtra(&win, &args, &bar, &memInfo, &cpuStats); | |
} | |
XWindow_AddEmptyLine(&win); | |
XArray_Destroy(&cpuStats.cores); | |
} | |
xarray_t netIfaces; | |
if (XTop_GetNetworkStats(&stats, &netIfaces) > 0) | |
{ | |
XTOPApp_AddNetworkInfo(&win, &args, &netIfaces); | |
XArray_Destroy(&netIfaces); | |
} | |
XWindow_Flush(&win); | |
xusleep(args.nIntervalU); | |
} | |
XWindow_Destroy(&win); | |
XTop_StopMonitoring(&stats, 1000); | |
usleep(10000); // Make valgrind happy | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment