Skip to content

Instantly share code, notes, and snippets.

@kala13x
Last active March 13, 2022 09:03
Show Gist options
  • Save kala13x/104feda02854169135595bf30028cd22 to your computer and use it in GitHub Desktop.
Save kala13x/104feda02854169135595bf30028cd22 to your computer and use it in GitHub Desktop.
Advanced system monitor with network, memory and CPU statistics in one window
/*!
* @file libxutils/examples/xtop.c
*
* This source is part of "libxutils" project
* 2015-2022 Sun Dro (f4tb0y@protonmail.com)
*
* @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 (f4tb0y@protonmail.com)\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