From 8b41cbd50833a557d9c6aee82bb6474baba96c35 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 26 Nov 2024 14:47:02 +0800 Subject: [PATCH] CPU (Linux): refactor; fix cpu count detection --- src/detection/cpu/cpu_linux.c | 50 ++++++++++------ src/util/FFstrbuf.c | 44 ++++++++++++++ src/util/FFstrbuf.h | 2 + tests/strbuf.c | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 19 deletions(-) diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 56493cf2a..5a306d473 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -159,14 +159,13 @@ static void detectAndroid(FFCPUResult* cpu) #if __arm__ || __aarch64__ #include "cpu_arm.h" -static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId) +static void detectArmName(FFstrbuf* cpuinfo, FFCPUResult* cpu, uint32_t implId) { - FF_AUTO_FREE char* line = NULL; - rewind(cpuinfo); + char* line = NULL; size_t len = 0; uint32_t lastPartId = UINT32_MAX; uint32_t num = 0; - while(getline(&line, &len, cpuinfo) != -1) + while(ffStrbufGetline(&line, &len, cpuinfo)) { if (!ffStrStartsWith(line, "CPU part\t: ")) continue; uint32_t partId = (uint32_t) strtoul(line + strlen("CPU part\t: "), NULL, 16); @@ -220,19 +219,18 @@ static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId) #endif static const char* parseCpuInfo( - FF_MAYBE_UNUSED FILE* cpuinfo, - FF_MAYBE_UNUSED FFCPUResult* cpu, - FF_MAYBE_UNUSED FFstrbuf* cpuPhysicalId, + FFstrbuf* cpuinfo, + FFCPUResult* cpu, FF_MAYBE_UNUSED FFstrbuf* physicalCoresBuffer, FF_MAYBE_UNUSED FFstrbuf* cpuMHz, FF_MAYBE_UNUSED FFstrbuf* cpuIsa, FF_MAYBE_UNUSED FFstrbuf* cpuUarch, FF_MAYBE_UNUSED FFstrbuf* cpuImplementer) { - FF_AUTO_FREE char* line = NULL; + char* line = NULL; size_t len = 0; - while(getline(&line, &len, cpuinfo) != -1) + while(ffStrbufGetline(&line, &len, cpuinfo)) { //Stop after reasonable information is acquired if((*line == '\0' || *line == '\n') @@ -248,9 +246,6 @@ static const char* parseCpuInfo( #if !(__arm__ || __aarch64__) (cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) || (cpu->vendor.length == 0 && ffParsePropLine(line, "vendor_id :", &cpu->vendor)) || - //Is it cheaper to just parse every physical id or to check if it's already set to the parsed value? - (cpuPhysicalId->length == 0 && ffParsePropLine(line, "physical id:", cpuPhysicalId)) || - (cpuPhysicalId->length > 0 && ffParsePropLine(line, "physical id:", cpuPhysicalId)) || (physicalCoresBuffer->length == 0 && ffParsePropLine(line, "cpu cores :", physicalCoresBuffer)) || (cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) || #endif @@ -469,28 +464,45 @@ FF_MAYBE_UNUSED static void detectArmSoc(FFCPUResult* cpu) } } +FF_MAYBE_UNUSED static uint16_t getCPUCount(FFstrbuf* cpuinfo) +{ + const char* p = cpuinfo->chars; + uint64_t bits = 0; + + while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), "\nphysical id\t:", strlen("\nphysical id\t:")))) + { + if (!p) break; + p += strlen("\nphysical id\t:"); + char* pend; + uint32_t id = (uint32_t) strtoul(p, &pend, 10); + p = pend; + bits |= 1 << id; + } + + return (uint16_t) __builtin_popcountll(bits); +} + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { - FF_AUTO_CLOSE_FILE FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if(cpuinfo == NULL) - return "fopen(\"/proc/cpuinfo\", \"r\") failed"; + FF_STRBUF_AUTO_DESTROY cpuinfo = ffStrbufCreateA(PROC_FILE_BUFFSIZ); + if (!ffReadFileBuffer("/proc/cpuinfo", &cpuinfo) || cpuinfo.length == 0) + return "ffReadFileBuffer(\"/proc/cpuinfo\") failed"; cpu->temperature = options->temp ? detectCPUTemp() : FF_CPU_TEMP_UNSET; - FF_STRBUF_AUTO_DESTROY cpuPhysicalId= ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY physicalCoresBuffer = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY cpuMHz = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY cpuIsa = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY cpuUarch = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY cpuImplementerStr = ffStrbufCreate(); - const char* error = parseCpuInfo(cpuinfo, cpu, &cpuPhysicalId, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr); + const char* error = parseCpuInfo(&cpuinfo, cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr); if (error) return error; cpu->coresLogical = (uint16_t) get_nprocs_conf(); cpu->coresOnline = (uint16_t) get_nprocs(); cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, cpu->coresLogical); - cpu->cpuCount = (uint16_t) ffStrbufToUInt(&cpuPhysicalId, 1) +1; //Assuming at least 1 CPU is present otherwise we wouldn't get this far + cpu->cpuCount = getCPUCount(&cpuinfo); // Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252 ffCPUDetectSpeedByCpuid(cpu); @@ -525,7 +537,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) #endif if (cpu->name.length == 0) - detectArmName(cpuinfo, cpu, cpuImplementer); + detectArmName(&cpuinfo, cpu, cpuImplementer); #endif return NULL; diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 68240f00e..f40572cef 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -531,3 +531,47 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c) memset(&strbuf->chars[index], c, num); strbuf->length += num; } + +/** + * @brief Read a line from a FFstrbuf. + * + * @details Behaves like getline(3) but reads from a FFstrbuf. + * + * @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line. + * Can be NULL for the first call. + * @param[in,out] n The pointer to the size of the buffer of lineptr. + * @param[in] buffer The buffer to read from. The buffer must not be a string literal. + * + * @return true if a line has been read, false if the end of the buffer has been reached. + */ +bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer) +{ + assert(lineptr && n && buffer); + assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0)); + assert(!*lineptr || (*lineptr >= buffer->chars && *lineptr <= buffer->chars + buffer->length)); + + const char* pBufferEnd = buffer->chars + buffer->length; + if (!*lineptr) + *lineptr = buffer->chars; + else + { + *lineptr += *n; + if (*lineptr >= pBufferEnd) // non-empty last line + return false; + **lineptr = '\n'; + ++*lineptr; + } + if (*lineptr >= pBufferEnd) // empty last line + return false; + + size_t remaining = (size_t) (pBufferEnd - *lineptr); + char* ending = memchr(*lineptr, '\n', remaining); + if (ending) + { + *n = (size_t) (ending - *lineptr); + *ending = '\0'; + } + else + *n = remaining; + return true; +} diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 2ac123352..cda2987f3 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -90,6 +90,8 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV void ffStrbufUpperCase(FFstrbuf* strbuf); void ffStrbufLowerCase(FFstrbuf* strbuf); +bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer); + FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) { FFstrbuf strbuf; diff --git a/tests/strbuf.c b/tests/strbuf.c index 92141d4b9..aad76da2f 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -458,6 +458,113 @@ int main(void) VERIFY(strbuf2.allocated == 32); } + { + int i = 0; + char* lineptr = NULL; + size_t n = 0; + const char* text = "Processor\t: ARMv7\nprocessor\t: 0\nBogoMIPS\t: 38.00\n\nprocessor\t: 1\nBogoMIPS\t: 38.00"; + ffStrbufSetS(&strbuf, text); + + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "Processor\t: ARMv7") == 0); + VERIFY(n == strlen("Processor\t: ARMv7")); + break; + case 2: + VERIFY(strcmp(lineptr, "processor\t: 0") == 0); + VERIFY(n == strlen("processor\t: 0")); + break; + case 3: + VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0); + VERIFY(n == strlen("BogoMIPS\t: 38.00")); + break; + case 4: + VERIFY(strcmp(lineptr, "") == 0); + VERIFY(n == 0); + break; + case 5: + VERIFY(strcmp(lineptr, "processor\t: 1") == 0); + VERIFY(n == strlen("processor\t: 1")); + break; + case 6: + VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0); + VERIFY(n == strlen("BogoMIPS\t: 38.00")); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 6); + + lineptr = NULL; + n = 0; + i = 0; + text = "\n"; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "") == 0); + VERIFY(n == 0); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 1); + + lineptr = NULL; + n = 0; + i = 0; + text = "abcd"; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "abcd") == 0); + VERIFY(n == strlen("abcd")); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 1); + + lineptr = NULL; + n = 0; + i = 0; + text = ""; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + VERIFY(false); + } + + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 0); + } + //Success puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); }