mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-30 00:37:23 +01:00
Top: adds option showType: <cpu|mem|disk>
This commit is contained in:
+31
-1
@@ -4449,6 +4449,36 @@
|
||||
],
|
||||
"default": "cpu"
|
||||
},
|
||||
"showTypes": {
|
||||
"description": "Specify which resource usages are detected and displayed.\nIf memory is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped",
|
||||
"default": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"processes": {
|
||||
"type": "integer",
|
||||
"description": "Number of top processes to print",
|
||||
@@ -4457,7 +4487,7 @@
|
||||
},
|
||||
"waitTime": {
|
||||
"type": "integer",
|
||||
"description": "Wait time between process samples, in milliseconds",
|
||||
"description": "Wait time between process samples, in milliseconds. Ignored when showTypes contains \"memory\" only",
|
||||
"minimum": 1,
|
||||
"default": 500
|
||||
},
|
||||
|
||||
@@ -114,8 +114,11 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
#if !FF_MODULE_DISABLE_TOP
|
||||
case 'T':
|
||||
case 't':
|
||||
FF_IF_MODULE_MATCH(ffTopModuleInfo.name)
|
||||
ffPrepareTopProcesses();
|
||||
FF_IF_MODULE_MATCH(ffTopModuleInfo.name) {
|
||||
[[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options;
|
||||
ffInitTopOptions(&options);
|
||||
ffPrepareTopProcesses(options.showTypes);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -199,7 +199,12 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
|
||||
case 't':
|
||||
case 'T': {
|
||||
if (ffStrEqualsIgnCase(type, ffTopModuleInfo.name)) {
|
||||
ffPrepareTopProcesses();
|
||||
[[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options;
|
||||
ffInitTopOptions(&options);
|
||||
if (module) {
|
||||
ffTopModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPrepareTopProcesses(options.showTypes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+96
-52
@@ -4,15 +4,29 @@
|
||||
|
||||
static FFlist first;
|
||||
static double startTick;
|
||||
static FFTopTypes preparedShowTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY | FF_TOP_TYPE_DISK;
|
||||
|
||||
void ffPrepareTopProcesses(void) {
|
||||
if (startTick != 0) {
|
||||
void ffPrepareTopProcesses(FFTopTypes showTypes) {
|
||||
if ((showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0) {
|
||||
return; // Memory usage is instantaneous; no baseline snapshot is needed
|
||||
}
|
||||
|
||||
if (startTick != 0 && preparedShowTypes == showTypes) {
|
||||
return; // Already prepared
|
||||
}
|
||||
|
||||
if (startTick != 0) {
|
||||
// The set of requested types changed; discard the stale baseline
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, first) {
|
||||
ffStrbufDestroy(&item->name);
|
||||
}
|
||||
ffListDestroy(&first);
|
||||
}
|
||||
|
||||
ffListInit(&first);
|
||||
startTick = ffTimeGetTick();
|
||||
ffTopGetProcessSnapshot(&first);
|
||||
preparedShowTypes = showTypes;
|
||||
ffTopGetProcessSnapshot(&first, showTypes);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@@ -47,57 +61,92 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (startTick == 0) {
|
||||
ffPrepareTopProcesses();
|
||||
}
|
||||
// Memory usage is instantaneous; when neither CPU time nor disk IO counters
|
||||
// are requested, a single snapshot suffices and no sampling wait is needed.
|
||||
const bool sampleOnce = (options->showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0;
|
||||
|
||||
double elapsedTime = ffTimeGetTick() - startTick;
|
||||
if (elapsedTime < (double) options->waitTime) {
|
||||
ffTimeSleep(options->waitTime - (uint32_t) elapsedTime);
|
||||
}
|
||||
|
||||
if (first.length == 0) {
|
||||
return "No processes found";
|
||||
}
|
||||
|
||||
FF_LIST_AUTO_DESTROY second = ffListCreate();
|
||||
const char* error = ffTopGetProcessSnapshot(&second);
|
||||
const double elapsed = ffTimeGetTick() - startTick;
|
||||
|
||||
if (error || elapsed <= 0) {
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) {
|
||||
ffStrbufDestroy(&item->name);
|
||||
}
|
||||
return error ?: "Invalid process sampling interval";
|
||||
}
|
||||
|
||||
ffListInitA(result, sizeof(FFTopProcessResult), first.length < options->nProcesses ? first.length : options->nProcesses);
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, oldItem, first) {
|
||||
FFTopProcessSnapshot* newItem = nullptr;
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) {
|
||||
if (item->pid == oldItem->pid) {
|
||||
newItem = item;
|
||||
break;
|
||||
if (sampleOnce) {
|
||||
FF_LIST_AUTO_DESTROY snapshots = ffListCreate();
|
||||
const char* error = ffTopGetProcessSnapshot(&snapshots, options->showTypes);
|
||||
if (error) {
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, snapshots) {
|
||||
ffStrbufDestroy(&item->name);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
if (!newItem || newItem->startTime != oldItem->startTime || newItem->cpuTime < oldItem->cpuTime || newItem->bytesRead < oldItem->bytesRead || newItem->bytesWritten < oldItem->bytesWritten) {
|
||||
ffStrbufDestroy(&oldItem->name);
|
||||
continue;
|
||||
if (snapshots.length == 0) {
|
||||
return "No processes found";
|
||||
}
|
||||
|
||||
FFTopProcessResult* item = FF_LIST_ADD(FFTopProcessResult, *result);
|
||||
item->pid = newItem->pid;
|
||||
item->memBytes = newItem->memBytes;
|
||||
item->bytesRead = (newItem->bytesRead - oldItem->bytesRead) * 1000u / (uint64_t) elapsed;
|
||||
item->bytesWritten = (newItem->bytesWritten - oldItem->bytesWritten) * 1000u / (uint64_t) elapsed;
|
||||
item->cpuPercent = (double) (newItem->cpuTime - oldItem->cpuTime) / elapsed * 100.0;
|
||||
item->startTime = newItem->startTime;
|
||||
ffStrbufInitMove(&item->name, &oldItem->name);
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, snap, snapshots) {
|
||||
FFTopProcessResult* item = FF_LIST_ADD(FFTopProcessResult, *result);
|
||||
item->pid = snap->pid;
|
||||
item->memBytes = snap->memBytes;
|
||||
item->bytesRead = 0;
|
||||
item->bytesWritten = 0;
|
||||
item->cpuPercent = 0;
|
||||
item->startTime = snap->startTime;
|
||||
ffStrbufInitMove(&item->name, &snap->name);
|
||||
}
|
||||
} else {
|
||||
if (startTick == 0 || preparedShowTypes != options->showTypes) {
|
||||
ffPrepareTopProcesses(options->showTypes);
|
||||
}
|
||||
|
||||
double elapsedTime = ffTimeGetTick() - startTick;
|
||||
if (elapsedTime < (double) options->waitTime) {
|
||||
ffTimeSleep(options->waitTime - (uint32_t) elapsedTime);
|
||||
}
|
||||
|
||||
if (first.length == 0) {
|
||||
return "No processes found";
|
||||
}
|
||||
|
||||
ffListReserve(result, sizeof(FFTopProcessResult), first.length < options->nProcesses ? first.length : options->nProcesses);
|
||||
|
||||
FF_LIST_AUTO_DESTROY second = ffListCreateA(sizeof(FFTopProcessSnapshot), first.length);
|
||||
const char* error = ffTopGetProcessSnapshot(&second, options->showTypes);
|
||||
const double elapsed = ffTimeGetTick() - startTick;
|
||||
|
||||
if (error || elapsed <= 0) {
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) {
|
||||
ffStrbufDestroy(&item->name);
|
||||
}
|
||||
return error ?: "Invalid process sampling interval";
|
||||
}
|
||||
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, oldItem, first) {
|
||||
FFTopProcessSnapshot* newItem = nullptr;
|
||||
FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) {
|
||||
if (item->pid == oldItem->pid) {
|
||||
newItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!newItem || newItem->startTime != oldItem->startTime || newItem->cpuTime < oldItem->cpuTime || newItem->bytesRead < oldItem->bytesRead || newItem->bytesWritten < oldItem->bytesWritten) {
|
||||
ffStrbufDestroy(&oldItem->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
FFTopProcessResult* item = FF_LIST_ADD(FFTopProcessResult, *result);
|
||||
item->pid = newItem->pid;
|
||||
item->memBytes = newItem->memBytes;
|
||||
item->bytesRead = (newItem->bytesRead - oldItem->bytesRead) * 1000u / (uint64_t) elapsed;
|
||||
item->bytesWritten = (newItem->bytesWritten - oldItem->bytesWritten) * 1000u / (uint64_t) elapsed;
|
||||
item->cpuPercent = (double) (newItem->cpuTime - oldItem->cpuTime) / elapsed * 100.0;
|
||||
item->startTime = newItem->startTime;
|
||||
ffStrbufInitMove(&item->name, &oldItem->name);
|
||||
}
|
||||
|
||||
// Reuse `second` as the baseline of the next call
|
||||
ffListDestroy(&first);
|
||||
ffListInitMove(&first, &second);
|
||||
startTick = ffTimeGetTick();
|
||||
}
|
||||
|
||||
const void* compare = options->sort == FF_TOP_SORT_DISK_WRITE ? (void*) compareDiskWriteResults
|
||||
: options->sort == FF_TOP_SORT_DISK_READ ? (void*) compareDiskReadResults
|
||||
: options->sort == FF_TOP_SORT_MEMORY ? (void*) compareMemoryResults
|
||||
const void* compare = options->sort == FF_TOP_TYPE_DISK_WRITE ? (void*) compareDiskWriteResults
|
||||
: options->sort == FF_TOP_TYPE_DISK_READ ? (void*) compareDiskReadResults
|
||||
: options->sort == FF_TOP_TYPE_MEMORY ? (void*) compareMemoryResults
|
||||
: (void*) compareCpuResults;
|
||||
ffListSort(result, sizeof(FFTopProcessResult), (void*) compare);
|
||||
if (result->length > options->nProcesses) {
|
||||
@@ -108,10 +157,5 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
result->length = options->nProcesses;
|
||||
}
|
||||
|
||||
// Reuse `second` as the baseline of the next call
|
||||
ffListDestroy(&first);
|
||||
ffListInitMove(&first, &second);
|
||||
startTick = ffTimeGetTick();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
typedef struct FFTopProcessResult {
|
||||
FFstrbuf name;
|
||||
uint64_t startTime;
|
||||
double cpuPercent;
|
||||
uint64_t memBytes;
|
||||
uint64_t bytesRead;
|
||||
uint64_t bytesWritten;
|
||||
uint64_t startTime;
|
||||
uint32_t pid;
|
||||
} FFTopProcessResult;
|
||||
|
||||
@@ -24,4 +24,4 @@ typedef struct FFTopProcessSnapshot {
|
||||
} FFTopProcessSnapshot;
|
||||
|
||||
const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result);
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots);
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <libproc.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
|
||||
size_t length;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
|
||||
size_t length;
|
||||
|
||||
|
||||
+78
-78
@@ -1,78 +1,78 @@
|
||||
#include "top.h"
|
||||
|
||||
#include <hurd.h>
|
||||
#include <string.h>
|
||||
#include <ps.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
struct ps_context* context = nullptr;
|
||||
if (ps_context_create(getproc(), &context) != 0) {
|
||||
return "ps_context_create(getproc()) failed";
|
||||
}
|
||||
|
||||
const char* error = nullptr;
|
||||
struct proc_stat_list* list = nullptr;
|
||||
if (proc_stat_list_create(context, &list) != 0) {
|
||||
error = "proc_stat_list_create() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) {
|
||||
error = "proc_stat_list_add_all() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Flags that cannot be fetched for some process are simply left unset;
|
||||
// each field must be checked before use.
|
||||
if (proc_stat_list_set_flags(list, PSTAT_ARGS | PSTAT_STATE | PSTAT_TASK_BASIC) != 0) {
|
||||
error = "proc_stat_list_set_flags() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < list->num_procs; ++i) {
|
||||
struct proc_stat* stat = list->proc_stats[i];
|
||||
|
||||
if (!proc_stat_has(stat, PSTAT_PID | PSTAT_STATE)) {
|
||||
continue;
|
||||
}
|
||||
if (proc_stat_state(stat) & PSTAT_STATE_P_ZOMBIE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
|
||||
|
||||
// There is no kernel-side comm on the Hurd; use the first argument.
|
||||
ffStrbufInit(&item->name);
|
||||
if (proc_stat_has(stat, PSTAT_ARGS) && proc_stat_args(stat)) {
|
||||
size_t len = strnlen(proc_stat_args(stat), proc_stat_args_len(stat));
|
||||
ffStrbufAppendNS(&item->name, (uint32_t) len, proc_stat_args(stat));
|
||||
}
|
||||
if (item->name.length == 0) {
|
||||
ffStrbufAppendS(&item->name, "(unknown)");
|
||||
}
|
||||
|
||||
item->pid = (uint32_t) proc_stat_pid(stat);
|
||||
|
||||
if (proc_stat_has(stat, PSTAT_TASK_BASIC)) {
|
||||
const task_basic_info_t info = proc_stat_task_basic_info(stat);
|
||||
item->cpuTime = (uint64_t) info->user_time.seconds * 1000 +
|
||||
(uint64_t) info->user_time.microseconds / 1000 +
|
||||
(uint64_t) info->system_time.seconds * 1000 +
|
||||
(uint64_t) info->system_time.microseconds / 1000;
|
||||
item->memBytes = (uint64_t) info->resident_size;
|
||||
}
|
||||
|
||||
// Neither process start times nor storage io counters are exposed by
|
||||
// the Hurd; leave them zeroed.
|
||||
item->startTime = 0;
|
||||
item->bytesRead = 0;
|
||||
item->bytesWritten = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
if (list) {
|
||||
proc_stat_list_free(list);
|
||||
}
|
||||
ps_context_free(context);
|
||||
return error;
|
||||
}
|
||||
#include "top.h"
|
||||
|
||||
#include <hurd.h>
|
||||
#include <string.h>
|
||||
#include <ps.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
struct ps_context* context = nullptr;
|
||||
if (ps_context_create(getproc(), &context) != 0) {
|
||||
return "ps_context_create(getproc()) failed";
|
||||
}
|
||||
|
||||
const char* error = nullptr;
|
||||
struct proc_stat_list* list = nullptr;
|
||||
if (proc_stat_list_create(context, &list) != 0) {
|
||||
error = "proc_stat_list_create() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) {
|
||||
error = "proc_stat_list_add_all() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Flags that cannot be fetched for some process are simply left unset;
|
||||
// each field must be checked before use.
|
||||
if (proc_stat_list_set_flags(list, PSTAT_ARGS | PSTAT_STATE | PSTAT_TASK_BASIC) != 0) {
|
||||
error = "proc_stat_list_set_flags() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < list->num_procs; ++i) {
|
||||
struct proc_stat* stat = list->proc_stats[i];
|
||||
|
||||
if (!proc_stat_has(stat, PSTAT_PID | PSTAT_STATE)) {
|
||||
continue;
|
||||
}
|
||||
if (proc_stat_state(stat) & PSTAT_STATE_P_ZOMBIE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
|
||||
|
||||
// There is no kernel-side comm on the Hurd; use the first argument.
|
||||
ffStrbufInit(&item->name);
|
||||
if (proc_stat_has(stat, PSTAT_ARGS) && proc_stat_args(stat)) {
|
||||
size_t len = strnlen(proc_stat_args(stat), proc_stat_args_len(stat));
|
||||
ffStrbufAppendNS(&item->name, (uint32_t) len, proc_stat_args(stat));
|
||||
}
|
||||
if (item->name.length == 0) {
|
||||
ffStrbufAppendS(&item->name, "(unknown)");
|
||||
}
|
||||
|
||||
item->pid = (uint32_t) proc_stat_pid(stat);
|
||||
|
||||
if (proc_stat_has(stat, PSTAT_TASK_BASIC)) {
|
||||
const task_basic_info_t info = proc_stat_task_basic_info(stat);
|
||||
item->cpuTime = (uint64_t) info->user_time.seconds * 1000 +
|
||||
(uint64_t) info->user_time.microseconds / 1000 +
|
||||
(uint64_t) info->system_time.seconds * 1000 +
|
||||
(uint64_t) info->system_time.microseconds / 1000;
|
||||
item->memBytes = (uint64_t) info->resident_size;
|
||||
}
|
||||
|
||||
// Neither process start times nor storage io counters are exposed by
|
||||
// the Hurd; leave them zeroed.
|
||||
item->startTime = 0;
|
||||
item->bytesRead = 0;
|
||||
item->bytesWritten = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
if (list) {
|
||||
proc_stat_list_free(list);
|
||||
}
|
||||
ps_context_free(context);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <OS.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
int32 cookie = 0;
|
||||
team_info team;
|
||||
while (get_next_team_info(&cookie, &team) == B_OK) {
|
||||
|
||||
@@ -55,7 +55,7 @@ static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* r
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
const long ticks = sysconf(_SC_CLK_TCK);
|
||||
const long pageSize = instance.state.platform.sysinfo.pageSize;
|
||||
FF_DEBUG("Scanning /proc: clk_tck=%ld, pageSize=%ld", ticks, pageSize);
|
||||
@@ -98,20 +98,24 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
}
|
||||
statBuffer[statLength] = '\0';
|
||||
|
||||
ssize_t statmLength;
|
||||
if ((statmLength = ffReadFileDataRelative(subfd, "statm", sizeof(statmBuffer) - 1, statmBuffer)) < 0) {
|
||||
FF_DEBUG("Failed to read /proc/%s/statm: %s", entry->d_name, strerror(errno));
|
||||
continue;
|
||||
ssize_t statmLength = 0;
|
||||
if (showTypes & FF_TOP_TYPE_MEMORY) {
|
||||
if ((statmLength = ffReadFileDataRelative(subfd, "statm", sizeof(statmBuffer) - 1, statmBuffer)) < 0) {
|
||||
FF_DEBUG("Failed to read /proc/%s/statm: %s", entry->d_name, strerror(errno));
|
||||
statmLength = 0;
|
||||
}
|
||||
statmBuffer[statmLength] = '\0';
|
||||
}
|
||||
statmBuffer[statmLength] = '\0';
|
||||
|
||||
char ioBuffer[512];
|
||||
ssize_t ioLength;
|
||||
if ((ioLength = ffReadFileDataRelative(subfd, "io", sizeof(ioBuffer) - 1, ioBuffer)) < 0) {
|
||||
FF_DEBUG("Failed to read /proc/%s/io: %s", entry->d_name, strerror(errno));
|
||||
ioLength = 0;
|
||||
ssize_t ioLength = 0;
|
||||
if (showTypes & FF_TOP_TYPE_DISK) {
|
||||
if ((ioLength = ffReadFileDataRelative(subfd, "io", sizeof(ioBuffer) - 1, ioBuffer)) < 0) {
|
||||
FF_DEBUG("Failed to read /proc/%s/io: %s", entry->d_name, strerror(errno));
|
||||
ioLength = 0;
|
||||
}
|
||||
ioBuffer[ioLength] = '\0';
|
||||
}
|
||||
ioBuffer[ioLength] = '\0';
|
||||
|
||||
auto snapshot = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
|
||||
ffStrbufInit(&snapshot->name);
|
||||
@@ -121,8 +125,8 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t rssPages;
|
||||
if (__builtin_expect(sscanf(statmBuffer, "%*u %" SCNu64, &rssPages) != 1, false)) {
|
||||
uint64_t rssPages = 0;
|
||||
if (__builtin_expect((showTypes & FF_TOP_TYPE_MEMORY) && sscanf(statmBuffer, "%*u %" SCNu64, &rssPages) != 1, false)) {
|
||||
FF_DEBUG("Failed to parse statm of /proc/%lu", pid);
|
||||
ffStrbufDestroy(&snapshot->name);
|
||||
--snapshots->length;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX };
|
||||
size_t length;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||
if (!kd) {
|
||||
return "kvm_open() failed";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <procfs.h>
|
||||
#include <sys/param.h> // DEV_BSIZE
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/proc");
|
||||
if (!dirp) {
|
||||
return "opendir(\"/proc\") failed";
|
||||
@@ -52,13 +52,14 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
item->startTime = (uint64_t) psinfo.pr_start.tv_sec * 1000 +
|
||||
(uint64_t) psinfo.pr_start.tv_nsec / 1000000;
|
||||
|
||||
prusage_t usage;
|
||||
if (ffReadFileDataRelative(subfd, "usage", sizeof(usage), &usage) == (ssize_t) sizeof(usage)) {
|
||||
item->bytesRead = (uint64_t) usage.pr_inblk * DEV_BSIZE;
|
||||
item->bytesWritten = (uint64_t) usage.pr_oublk * DEV_BSIZE;
|
||||
} else {
|
||||
item->bytesRead = 0;
|
||||
item->bytesWritten = 0;
|
||||
item->bytesRead = 0;
|
||||
item->bytesWritten = 0;
|
||||
if (showTypes & FF_TOP_TYPE_DISK) {
|
||||
prusage_t usage;
|
||||
if (ffReadFileDataRelative(subfd, "usage", sizeof(usage), &usage) == (ssize_t) sizeof(usage)) {
|
||||
item->bytesRead = (uint64_t) usage.pr_inblk * DEV_BSIZE;
|
||||
item->bytesWritten = (uint64_t) usage.pr_oublk * DEV_BSIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr;
|
||||
|
||||
// Multiple attempts in case processes change while
|
||||
|
||||
@@ -3,16 +3,19 @@
|
||||
#include "common/option.h"
|
||||
#include "common/percent.h"
|
||||
|
||||
typedef enum FFTopSortType {
|
||||
FF_TOP_SORT_CPU = 0,
|
||||
FF_TOP_SORT_MEMORY,
|
||||
FF_TOP_SORT_DISK_READ,
|
||||
FF_TOP_SORT_DISK_WRITE,
|
||||
} FFTopSortType;
|
||||
typedef enum FFTopTypes: uint8_t {
|
||||
FF_TOP_TYPE_CPU = 1 << 0,
|
||||
FF_TOP_TYPE_MEMORY = 1 << 1,
|
||||
FF_TOP_TYPE_DISK_READ = 1 << 2,
|
||||
FF_TOP_TYPE_DISK_WRITE = 1 << 3,
|
||||
|
||||
FF_TOP_TYPE_DISK = FF_TOP_TYPE_DISK_READ | FF_TOP_TYPE_DISK_WRITE,
|
||||
} FFTopTypes;
|
||||
|
||||
typedef struct FFTopOptions {
|
||||
FFModuleArgs moduleArgs;
|
||||
FFTopSortType sort;
|
||||
FFTopTypes sort;
|
||||
FFTopTypes showTypes; // bitfields
|
||||
uint32_t nProcesses;
|
||||
uint32_t waitTime;
|
||||
FFPercentageModuleConfig percent;
|
||||
|
||||
+88
-33
@@ -11,19 +11,23 @@ static void printTopResult(FFTopOptions* options, uint32_t index, uint32_t total
|
||||
ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Top), total == 1 ? 0 : (uint8_t) (index + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate();
|
||||
ffStrbufAppendF(&output, "%s (%u) - ", process->name.chars, process->pid);
|
||||
if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT) {
|
||||
ffStrbufAppendS(&output, "CPU ");
|
||||
ffStrbufAppendF(&output, "%s (%u)", process->name.chars, process->pid);
|
||||
if ((options->showTypes & FF_TOP_TYPE_CPU) && (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)) {
|
||||
ffStrbufAppendS(&output, " - CPU ");
|
||||
ffPercentAppendNum(&output, process->cpuPercent, options->percent, false, &options->moduleArgs);
|
||||
}
|
||||
if (!(percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) {
|
||||
ffStrbufAppendS(&output, " - MEM ");
|
||||
ffSizeAppendNum(process->memBytes, &output);
|
||||
ffStrbufAppendS(&output, " - DSK ");
|
||||
ffSizeAppendNum(process->bytesRead, &output);
|
||||
ffStrbufAppendS(&output, "/s / ");
|
||||
ffSizeAppendNum(process->bytesWritten, &output);
|
||||
ffStrbufAppendS(&output, "/s");
|
||||
if (options->showTypes & FF_TOP_TYPE_MEMORY) {
|
||||
ffStrbufAppendS(&output, " - MEM ");
|
||||
ffSizeAppendNum(process->memBytes, &output);
|
||||
}
|
||||
if (options->showTypes & FF_TOP_TYPE_DISK) {
|
||||
ffStrbufAppendS(&output, " - DSK ");
|
||||
ffSizeAppendNum(process->bytesRead, &output);
|
||||
ffStrbufAppendS(&output, "/s / ");
|
||||
ffSizeAppendNum(process->bytesWritten, &output);
|
||||
ffStrbufAppendS(&output, "/s");
|
||||
}
|
||||
}
|
||||
ffStrbufPutTo(&output, stdout);
|
||||
} else {
|
||||
@@ -92,16 +96,54 @@ void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) {
|
||||
if (unsafe_yyjson_equals_str(key, "sort")) {
|
||||
int value;
|
||||
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){
|
||||
{ "cpu", FF_TOP_SORT_CPU },
|
||||
{ "memory", FF_TOP_SORT_MEMORY },
|
||||
{ "disk-read", FF_TOP_SORT_DISK_READ },
|
||||
{ "disk-write", FF_TOP_SORT_DISK_WRITE },
|
||||
{ "cpu", FF_TOP_TYPE_CPU },
|
||||
{ "memory", FF_TOP_TYPE_MEMORY },
|
||||
{ "disk-read", FF_TOP_TYPE_DISK_READ },
|
||||
{ "disk-write", FF_TOP_TYPE_DISK_WRITE },
|
||||
{},
|
||||
});
|
||||
if (error) {
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
|
||||
} else {
|
||||
options->sort = (FFTopSortType) value;
|
||||
options->sort = (FFTopTypes) value;
|
||||
}
|
||||
continue;
|
||||
} else if (unsafe_yyjson_equals_str(key, "showTypes")) {
|
||||
if (yyjson_is_arr(val)) {
|
||||
options->showTypes = 0;
|
||||
yyjson_val* item;
|
||||
size_t aidx, amax;
|
||||
yyjson_arr_foreach (val, aidx, amax, item) {
|
||||
int value;
|
||||
const char* error = ffJsonConfigParseEnum(item, &value, (FFKeyValuePair[]){
|
||||
{ "cpu", FF_TOP_TYPE_CPU },
|
||||
{ "memory", FF_TOP_TYPE_MEMORY },
|
||||
{ "disk", FF_TOP_TYPE_DISK },
|
||||
{},
|
||||
});
|
||||
if (error == nullptr) {
|
||||
options->showTypes |= (FFTopTypes) value;
|
||||
}
|
||||
if (error) {
|
||||
options->showTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY | FF_TOP_TYPE_DISK;
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int value;
|
||||
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){
|
||||
{ "cpu", FF_TOP_TYPE_CPU },
|
||||
{ "memory", FF_TOP_TYPE_MEMORY },
|
||||
{ "disk", FF_TOP_TYPE_DISK },
|
||||
{},
|
||||
});
|
||||
if (error == nullptr) {
|
||||
options->showTypes = (FFTopTypes) value;
|
||||
}
|
||||
if (error) {
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (unsafe_yyjson_equals_str(key, "processes")) {
|
||||
@@ -121,19 +163,25 @@ void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) {
|
||||
void ffGenerateTopJsonConfig(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
|
||||
ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", "top");
|
||||
switch (options->sort) {
|
||||
case FF_TOP_SORT_CPU:
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "cpu");
|
||||
break;
|
||||
case FF_TOP_SORT_MEMORY:
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "memory");
|
||||
break;
|
||||
case FF_TOP_SORT_DISK_READ:
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "disk-read");
|
||||
break;
|
||||
case FF_TOP_SORT_DISK_WRITE:
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "disk-write");
|
||||
break;
|
||||
yyjson_mut_val* showTypes = yyjson_mut_arr(doc);
|
||||
if (options->showTypes & FF_TOP_TYPE_CPU) {
|
||||
yyjson_mut_arr_add_str(doc, showTypes, "cpu");
|
||||
}
|
||||
if (options->showTypes & FF_TOP_TYPE_MEMORY) {
|
||||
yyjson_mut_arr_add_str(doc, showTypes, "memory");
|
||||
}
|
||||
if (options->showTypes & FF_TOP_TYPE_DISK) {
|
||||
yyjson_mut_arr_add_str(doc, showTypes, "disk");
|
||||
}
|
||||
yyjson_mut_obj_add_val(doc, module, "showTypes", showTypes);
|
||||
if (options->sort == FF_TOP_TYPE_CPU) {
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "cpu");
|
||||
} else if (options->sort == FF_TOP_TYPE_MEMORY) {
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "memory");
|
||||
} else if (options->sort == FF_TOP_TYPE_DISK_READ) {
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "disk-read");
|
||||
} else if (options->sort == FF_TOP_TYPE_DISK_WRITE) {
|
||||
yyjson_mut_obj_add_str(doc, module, "sort", "disk-write");
|
||||
}
|
||||
yyjson_mut_obj_add_uint(doc, module, "processes", options->nProcesses);
|
||||
yyjson_mut_obj_add_uint(doc, module, "waitTime", options->waitTime);
|
||||
@@ -153,10 +201,16 @@ bool ffGenerateTopJsonResult(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_
|
||||
yyjson_mut_val* item = yyjson_mut_arr_add_obj(doc, array);
|
||||
yyjson_mut_obj_add_strbuf(doc, item, "name", &process->name);
|
||||
yyjson_mut_obj_add_uint(doc, item, "pid", process->pid);
|
||||
yyjson_mut_obj_add_real(doc, item, "cpuPercent", process->cpuPercent);
|
||||
yyjson_mut_obj_add_uint(doc, item, "mem", process->memBytes);
|
||||
yyjson_mut_obj_add_uint(doc, item, "bytesRead", process->bytesRead);
|
||||
yyjson_mut_obj_add_uint(doc, item, "bytesWritten", process->bytesWritten);
|
||||
if (options->showTypes & FF_TOP_TYPE_CPU) {
|
||||
yyjson_mut_obj_add_real(doc, item, "cpuPercent", process->cpuPercent);
|
||||
}
|
||||
if (options->showTypes & FF_TOP_TYPE_MEMORY) {
|
||||
yyjson_mut_obj_add_uint(doc, item, "mem", process->memBytes);
|
||||
}
|
||||
if (options->showTypes & FF_TOP_TYPE_DISK) {
|
||||
yyjson_mut_obj_add_uint(doc, item, "bytesRead", process->bytesRead);
|
||||
yyjson_mut_obj_add_uint(doc, item, "bytesWritten", process->bytesWritten);
|
||||
}
|
||||
yyjson_mut_obj_add_uint(doc, item, "startTime", process->startTime);
|
||||
}
|
||||
|
||||
@@ -168,7 +222,8 @@ bool ffGenerateTopJsonResult(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_
|
||||
|
||||
void ffInitTopOptions(FFTopOptions* options) {
|
||||
ffOptionInitModuleArg(&options->moduleArgs, "");
|
||||
options->sort = FF_TOP_SORT_CPU;
|
||||
options->sort = FF_TOP_TYPE_CPU;
|
||||
options->showTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY | FF_TOP_TYPE_DISK;
|
||||
options->nProcesses = 5;
|
||||
options->waitTime = 500;
|
||||
options->compact = false;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "option.h"
|
||||
|
||||
void ffPrepareTopProcesses(void);
|
||||
void ffPrepareTopProcesses(FFTopTypes showTypes);
|
||||
|
||||
bool ffPrintTop(FFTopOptions* options);
|
||||
void ffInitTopOptions(FFTopOptions* options);
|
||||
|
||||
Reference in New Issue
Block a user