Top: adds option showType: <cpu|mem|disk>

This commit is contained in:
李通洲
2026-08-25 20:08:06 +08:00
parent b7d8c12015
commit 4344aa2ff1
17 changed files with 349 additions and 204 deletions
+31 -1
View File
@@ -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
},
+5 -2
View File
@@ -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
+6 -1
View File
@@ -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
View File
@@ -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;
}
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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) {
+17 -13
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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";
+9 -8
View File
@@ -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;
}
}
}
+1 -1
View File
@@ -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
+10 -7
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -2,7 +2,7 @@
#include "option.h"
void ffPrepareTopProcesses(void);
void ffPrepareTopProcesses(FFTopTypes showTypes);
bool ffPrintTop(FFTopOptions* options);
void ffInitTopOptions(FFTopOptions* options);