Skip to content

Commit 42cf88b

Browse files
committed
CPUUsage: support display value per core
1 parent 6b008ed commit 42cf88b

File tree

9 files changed

+179
-61
lines changed

9 files changed

+179
-61
lines changed

src/detection/cpuusage/cpuusage.c

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,54 @@
44

55
#include <stdint.h>
66

7-
// We need to use uint64_t because sizeof(long) == 4 on Windows
8-
const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll);
9-
10-
static uint64_t inUseAll1, totalAll1;
7+
static FFlist cpuTimes1;
118

129
void ffPrepareCPUUsage(void)
1310
{
14-
ffGetCpuUsageInfo(&inUseAll1, &totalAll1);
11+
assert(cpuTimes1.elementSize == 0);
12+
ffListInit(&cpuTimes1, sizeof(FFCpuUsageInfo));
13+
ffGetCpuUsageInfo(&cpuTimes1);
1514
}
1615

17-
const char* ffGetCpuUsageResult(double* result)
16+
const char* ffGetCpuUsageResult(FFlist* result)
1817
{
1918
const char* error = NULL;
20-
if(inUseAll1 == 0 && totalAll1 == 0)
19+
if(cpuTimes1.elementSize == 0)
2120
{
22-
error = ffGetCpuUsageInfo(&inUseAll1, &totalAll1);
23-
if(error)
24-
return error;
21+
ffListInit(&cpuTimes1, sizeof(FFCpuUsageInfo));
22+
error = ffGetCpuUsageInfo(&cpuTimes1);
23+
if(error) return error;
2524
ffTimeSleep(200);
2625
}
2726

28-
while(true)
29-
{
30-
uint64_t inUseAll2, totalAll2;
31-
error = ffGetCpuUsageInfo(&inUseAll2, &totalAll2);
32-
if(error)
33-
return error;
27+
if(cpuTimes1.length == 0) return "No CPU cores found";
28+
29+
FF_LIST_AUTO_DESTROY cpuTimes2 = ffListCreate(sizeof(FFCpuUsageInfo));
3430

35-
if(inUseAll2 != inUseAll1)
31+
retry:
32+
error = ffGetCpuUsageInfo(&cpuTimes2);
33+
if(error) return error;
34+
if(cpuTimes1.length != cpuTimes2.length) return "Unexpected CPU usage result";
35+
36+
for (uint32_t i = 0; i < cpuTimes1.length; ++i)
37+
{
38+
FFCpuUsageInfo* cpuTime1 = ffListGet(&cpuTimes1, i);
39+
FFCpuUsageInfo* cpuTime2 = ffListGet(&cpuTimes2, i);
40+
if (cpuTime2->totalAll <= cpuTime1->totalAll)
3641
{
37-
*result = (double)(inUseAll2 - inUseAll1) / (double)(totalAll2 - totalAll1) * 100;
38-
inUseAll1 = inUseAll2;
39-
totalAll1 = totalAll2;
40-
return NULL;
41-
}
42-
else
42+
ffListClear(&cpuTimes2);
4343
ffTimeSleep(200);
44+
goto retry;
45+
}
46+
}
47+
48+
for (uint32_t i = 0; i < cpuTimes1.length; ++i)
49+
{
50+
FFCpuUsageInfo* cpuTime1 = ffListGet(&cpuTimes1, i);
51+
FFCpuUsageInfo* cpuTime2 = ffListGet(&cpuTimes2, i);
52+
*(double*) ffListAdd(result) = (double)(cpuTime2->inUseAll - cpuTime1->inUseAll) / (double)(cpuTime2->totalAll - cpuTime1->totalAll) * 100;
53+
cpuTime1->inUseAll = cpuTime2->inUseAll;
54+
cpuTime1->totalAll = cpuTime2->totalAll;
4455
}
56+
return NULL;
4557
}

src/detection/cpuusage/cpuusage.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
#ifndef FF_INCLUDED_detection_cpuusage_cpuusage
44
#define FF_INCLUDED_detection_cpuusage_cpuusage
55

6-
const char* ffGetCpuUsageResult(double* result);
6+
#include "fastfetch.h"
7+
8+
typedef struct FFCpuUsageInfo {
9+
uint64_t inUseAll;
10+
uint64_t totalAll;
11+
} FFCpuUsageInfo;
12+
const char* ffGetCpuUsageInfo(FFlist* cpuTimes);
13+
14+
const char* ffGetCpuUsageResult(FFlist* result); // list of double
715

816
#endif

src/detection/cpuusage/cpuusage_apple.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@
44
#include <mach/processor_info.h>
55
#include <mach/mach_host.h>
66

7-
const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
7+
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
88
{
99
natural_t numCPUs = 0U;
1010
processor_info_array_t cpuInfo;
1111
mach_msg_type_number_t numCpuInfo;
12-
*inUseAll = *totalAll = 0;
1312

1413
if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUs, &cpuInfo, &numCpuInfo) != KERN_SUCCESS)
1514
return "host_processor_info() failed";
1615
if (numCPUs * CPU_STATE_MAX != numCpuInfo)
1716
return "Unexpected host_processor_info() result";
1817

19-
for (natural_t i = 0U; i < numCPUs; ++i) {
18+
for (natural_t i = 0U; i < numCPUs; ++i)
19+
{
2020
integer_t inUse = cpuInfo[CPU_STATE_MAX * i + CPU_STATE_USER]
2121
+ cpuInfo[CPU_STATE_MAX * i + CPU_STATE_SYSTEM]
2222
+ cpuInfo[CPU_STATE_MAX * i + CPU_STATE_NICE];
2323
integer_t total = inUse + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_IDLE];
24-
*inUseAll += (uint64_t)inUse;
25-
*totalAll += (uint64_t)total;
24+
25+
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
26+
*info = (FFCpuUsageInfo) {
27+
.inUseAll = (uint64_t)inUse,
28+
.totalAll = (uint64_t)total,
29+
};
2630
}
2731
return NULL;
2832
}

src/detection/cpuusage/cpuusage_bsd.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
#include <sys/resource.h>
77
#include <stdlib.h>
88

9-
const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
9+
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
1010
{
1111
size_t neededLength = 0;
1212
if(sysctlbyname("kern.cp_times", NULL, &neededLength, NULL, 0) != 0)
1313
return "sysctlbyname(kern.cp_times, NULL) failed";
1414

15-
uint32_t coreCount = neededLength / (CPUSTATES * sizeof(uint64_t));
15+
uint32_t coreCount = neededLength / (CPUSTATES * (uint32_t) sizeof(uint64_t));
1616
assert(coreCount > 0);
1717

1818
FF_AUTO_FREE uint64_t (*cpTimes)[CPUSTATES] = malloc(neededLength);
@@ -24,8 +24,12 @@ const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
2424
uint64_t* cpTime = cpTimes[i];
2525
uint64_t inUse = cpTime[CP_USER] + cpTime[CP_NICE] + cpTime[CP_SYS];
2626
uint64_t total = cpTime[CP_INTR] + cpTime[CP_IDLE];
27-
*inUseAll += inUse;
28-
*totalAll += total;
27+
28+
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
29+
*info = (FFCpuUsageInfo) {
30+
.inUseAll = (uint64_t)inUse,
31+
.totalAll = (uint64_t)total,
32+
};
2933
}
3034

3135
return NULL;

src/detection/cpuusage/cpuusage_linux.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <stdio.h>
66
#include <inttypes.h>
77

8-
const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
8+
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
99
{
1010
FF_AUTO_CLOSE_FILE FILE* procStat = fopen("/proc/stat", "r");
1111
if(procStat == NULL)
@@ -20,16 +20,17 @@ const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
2020
if (fscanf(procStat, "cpu%*[^\n]\n") < 0)
2121
return "fscanf() first line failed";
2222

23-
*inUseAll = 0;
24-
*totalAll = 0;
25-
2623
uint64_t user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0;
2724
while (fscanf(procStat, "cpu%*d%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64, &user, &nice, &system, &idle, &iowait, &irq, &softirq) == 7)
2825
{
2926
uint64_t inUse = user + nice + system;
3027
uint64_t total = inUse + idle + iowait + irq + softirq;
31-
*inUseAll += inUse;
32-
*totalAll += total;
28+
29+
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
30+
*info = (FFCpuUsageInfo) {
31+
.inUseAll = (uint64_t)inUse,
32+
.totalAll = (uint64_t)total,
33+
};
3334
}
3435

3536
return NULL;

src/detection/cpuusage/cpuusage_windows.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <ntstatus.h>
77
#include <winternl.h>
88

9-
const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
9+
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
1010
{
1111
ULONG size = 0;
1212
if(NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
@@ -16,8 +16,6 @@ const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
1616
if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, pinfo, size, &size)))
1717
return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, size) failed";
1818

19-
*inUseAll = *totalAll = 0;
20-
2119
for (uint32_t i = 0; i < size / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); ++i)
2220
{
2321
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i;
@@ -28,9 +26,14 @@ const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll)
2826
coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart;
2927
coreInfo->KernelTime.QuadPart += dpcTime + interruptTime;
3028

31-
LONGLONG inUse = coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart;
32-
*inUseAll += (uint64_t)inUse;
33-
*totalAll += (uint64_t)(inUse + coreInfo->IdleTime.QuadPart);
29+
uint64_t inUse = (uint64_t) (coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart);
30+
uint64_t total = inUse + (uint64_t)coreInfo->IdleTime.QuadPart;
31+
32+
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
33+
*info = (FFCpuUsageInfo) {
34+
.inUseAll = (uint64_t)inUse,
35+
.totalAll = (uint64_t)total,
36+
};
3437
}
3538

3639
return NULL;

src/modules/cpuusage/cpuusage.c

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,81 @@
66
#include "util/stringUtils.h"
77

88
#define FF_CPUUSAGE_DISPLAY_NAME "CPU Usage"
9-
#define FF_CPUUSAGE_NUM_FORMAT_ARGS 1
9+
#define FF_CPUUSAGE_NUM_FORMAT_ARGS 5
1010

1111
void ffPrintCPUUsage(FFCPUUsageOptions* options)
1212
{
13-
double percentage = 0.0/0.0;
14-
const char* error = ffGetCpuUsageResult(&percentage);
13+
FF_LIST_AUTO_DESTROY percentages = ffListCreate(sizeof(double));
14+
const char* error = ffGetCpuUsageResult(&percentages);
1515

1616
if(error)
1717
{
1818
ffPrintError(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, "%s", error);
1919
return;
2020
}
2121

22+
double maxValue = -999, minValue = 999, sumValue = 0;
23+
uint32_t maxIndex = 999, minIndex = 999;
24+
25+
uint32_t index = 0;
26+
FF_LIST_FOR_EACH(double, percent, percentages)
27+
{
28+
sumValue += *percent;
29+
if (*percent > maxValue)
30+
{
31+
maxValue = *percent;
32+
maxIndex = index;
33+
}
34+
if (*percent < minValue)
35+
{
36+
minValue = *percent;
37+
minIndex = index;
38+
}
39+
++index;
40+
}
41+
double avgValue = sumValue / (double) percentages.length;
42+
2243
if(options->moduleArgs.outputFormat.length == 0)
2344
{
2445
ffPrintLogoAndKey(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
2546

2647
FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate();
27-
if(instance.config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT)
28-
ffAppendPercentBar(&str, percentage, 0, 50, 80);
29-
if(instance.config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT)
48+
if (options->displayType == FF_CPUUSAGE_DISPLAY_TYPE_DEFAULT)
3049
{
31-
if(str.length > 0)
32-
ffStrbufAppendC(&str, ' ');
33-
ffAppendPercentNum(&str, percentage, 50, 80, str.length > 0);
50+
if(instance.config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT)
51+
ffAppendPercentBar(&str, avgValue, 0, 50, 80);
52+
if(instance.config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT)
53+
{
54+
if(str.length > 0)
55+
ffStrbufAppendC(&str, ' ');
56+
ffAppendPercentNum(&str, avgValue, 50, 80, str.length > 0);
57+
}
58+
}
59+
else
60+
{
61+
FF_LIST_FOR_EACH(double, percent, percentages)
62+
{
63+
if(str.length > 0)
64+
ffStrbufAppendC(&str, ' ');
65+
ffAppendPercentNum(&str, *percent, 50, 80, false);
66+
}
3467
}
3568
ffStrbufPutTo(&str, stdout);
3669
}
3770
else
3871
{
39-
FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate();
40-
ffAppendPercentNum(&percentageStr, percentage, 50, 80, false);
72+
FF_STRBUF_AUTO_DESTROY avgStr = ffStrbufCreate();
73+
ffAppendPercentNum(&avgStr, avgValue, 50, 80, false);
74+
FF_STRBUF_AUTO_DESTROY minStr = ffStrbufCreate();
75+
ffAppendPercentNum(&minStr, minValue, 50, 80, false);
76+
FF_STRBUF_AUTO_DESTROY maxStr = ffStrbufCreate();
77+
ffAppendPercentNum(&maxStr, maxValue, 50, 80, false);
4178
ffPrintFormat(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_CPUUSAGE_NUM_FORMAT_ARGS, (FFformatarg[]){
42-
{FF_FORMAT_ARG_TYPE_STRBUF, &percentageStr}
79+
{FF_FORMAT_ARG_TYPE_STRBUF, &avgStr},
80+
{FF_FORMAT_ARG_TYPE_STRBUF, &maxStr},
81+
{FF_FORMAT_ARG_TYPE_UINT, &maxIndex},
82+
{FF_FORMAT_ARG_TYPE_STRBUF, &minStr},
83+
{FF_FORMAT_ARG_TYPE_UINT, &minIndex},
4384
});
4485
}
4586
}
@@ -57,6 +98,16 @@ bool ffParseCPUUsageCommandOptions(FFCPUUsageOptions* options, const char* key,
5798
if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs))
5899
return true;
59100

101+
if (ffStrEqualsIgnCase(subKey, "display-type"))
102+
{
103+
options->displayType = (FFCPUUsageDisplayType) ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
104+
{ "default", FF_CPUUSAGE_DISPLAY_TYPE_DEFAULT },
105+
{ "separate", FF_CPUUSAGE_DISPLAY_TYPE_SEPARATE },
106+
{},
107+
});
108+
return true;
109+
}
110+
60111
return false;
61112
}
62113

@@ -78,26 +129,49 @@ void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module)
78129
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
79130
continue;
80131

132+
if (ffStrEqualsIgnCase(key, "display-type"))
133+
{
134+
int value;
135+
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
136+
{ "default", FF_CPUUSAGE_DISPLAY_TYPE_DEFAULT },
137+
{ "separate", FF_CPUUSAGE_DISPLAY_TYPE_SEPARATE },
138+
{},
139+
});
140+
if (error)
141+
ffPrintErrorString(FF_CPUUSAGE_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", key, error);
142+
else
143+
options->displayType = (FFCPUUsageDisplayType) value;
144+
continue;
145+
}
146+
81147
ffPrintError(FF_CPUUSAGE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key);
82148
}
83149
}
84150

85151
void ffGenerateCPUUsageJson(FF_MAYBE_UNUSED FFCPUUsageOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
86152
{
87-
double percentage = 0.0/0.0;
88-
const char* error = ffGetCpuUsageResult(&percentage);
153+
FF_LIST_AUTO_DESTROY percentages = ffListCreate(sizeof(double));
154+
const char* error = ffGetCpuUsageResult(&percentages);
89155

90156
if(error)
91157
{
92158
yyjson_mut_obj_add_str(doc, module, "error", error);
93159
return;
94160
}
95-
yyjson_mut_obj_add_real(doc, module, "result", percentage);
161+
yyjson_mut_val* result = yyjson_mut_obj_add_arr(doc, module, "result");
162+
FF_LIST_FOR_EACH(double, percent, percentages)
163+
{
164+
yyjson_mut_arr_add_real(doc, result, *percent);
165+
}
96166
}
97167

98168
void ffPrintCPUUsageHelpFormat(void)
99169
{
100170
ffPrintModuleFormatHelp(FF_CPUUSAGE_MODULE_NAME, "{1}", FF_CPUUSAGE_NUM_FORMAT_ARGS, (const char* []) {
101-
"CPU usage (percentage)"
171+
"CPU usage (percentage, average)",
172+
"CPU usage (percentage, maximum)",
173+
"CPU core index of maximum usage",
174+
"CPU usage (percentage, minimum)",
175+
"CPU core index of minimum usage",
102176
});
103177
}

0 commit comments

Comments
 (0)