Skip to content

Commit 8b41cbd

Browse files
committed
CPU (Linux): refactor; fix cpu count detection
1 parent 7535ce8 commit 8b41cbd

File tree

4 files changed

+184
-19
lines changed

4 files changed

+184
-19
lines changed

src/detection/cpu/cpu_linux.c

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,13 @@ static void detectAndroid(FFCPUResult* cpu)
159159
#if __arm__ || __aarch64__
160160
#include "cpu_arm.h"
161161

162-
static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId)
162+
static void detectArmName(FFstrbuf* cpuinfo, FFCPUResult* cpu, uint32_t implId)
163163
{
164-
FF_AUTO_FREE char* line = NULL;
165-
rewind(cpuinfo);
164+
char* line = NULL;
166165
size_t len = 0;
167166
uint32_t lastPartId = UINT32_MAX;
168167
uint32_t num = 0;
169-
while(getline(&line, &len, cpuinfo) != -1)
168+
while(ffStrbufGetline(&line, &len, cpuinfo))
170169
{
171170
if (!ffStrStartsWith(line, "CPU part\t: ")) continue;
172171
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)
220219
#endif
221220

222221
static const char* parseCpuInfo(
223-
FF_MAYBE_UNUSED FILE* cpuinfo,
224-
FF_MAYBE_UNUSED FFCPUResult* cpu,
225-
FF_MAYBE_UNUSED FFstrbuf* cpuPhysicalId,
222+
FFstrbuf* cpuinfo,
223+
FFCPUResult* cpu,
226224
FF_MAYBE_UNUSED FFstrbuf* physicalCoresBuffer,
227225
FF_MAYBE_UNUSED FFstrbuf* cpuMHz,
228226
FF_MAYBE_UNUSED FFstrbuf* cpuIsa,
229227
FF_MAYBE_UNUSED FFstrbuf* cpuUarch,
230228
FF_MAYBE_UNUSED FFstrbuf* cpuImplementer)
231229
{
232-
FF_AUTO_FREE char* line = NULL;
230+
char* line = NULL;
233231
size_t len = 0;
234232

235-
while(getline(&line, &len, cpuinfo) != -1)
233+
while(ffStrbufGetline(&line, &len, cpuinfo))
236234
{
237235
//Stop after reasonable information is acquired
238236
if((*line == '\0' || *line == '\n')
@@ -248,9 +246,6 @@ static const char* parseCpuInfo(
248246
#if !(__arm__ || __aarch64__)
249247
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
250248
(cpu->vendor.length == 0 && ffParsePropLine(line, "vendor_id :", &cpu->vendor)) ||
251-
//Is it cheaper to just parse every physical id or to check if it's already set to the parsed value?
252-
(cpuPhysicalId->length == 0 && ffParsePropLine(line, "physical id:", cpuPhysicalId)) ||
253-
(cpuPhysicalId->length > 0 && ffParsePropLine(line, "physical id:", cpuPhysicalId)) ||
254249
(physicalCoresBuffer->length == 0 && ffParsePropLine(line, "cpu cores :", physicalCoresBuffer)) ||
255250
(cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) ||
256251
#endif
@@ -469,28 +464,45 @@ FF_MAYBE_UNUSED static void detectArmSoc(FFCPUResult* cpu)
469464
}
470465
}
471466

467+
FF_MAYBE_UNUSED static uint16_t getCPUCount(FFstrbuf* cpuinfo)
468+
{
469+
const char* p = cpuinfo->chars;
470+
uint64_t bits = 0;
471+
472+
while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), "\nphysical id\t:", strlen("\nphysical id\t:"))))
473+
{
474+
if (!p) break;
475+
p += strlen("\nphysical id\t:");
476+
char* pend;
477+
uint32_t id = (uint32_t) strtoul(p, &pend, 10);
478+
p = pend;
479+
bits |= 1 << id;
480+
}
481+
482+
return (uint16_t) __builtin_popcountll(bits);
483+
}
484+
472485
const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
473486
{
474-
FF_AUTO_CLOSE_FILE FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
475-
if(cpuinfo == NULL)
476-
return "fopen(\"/proc/cpuinfo\", \"r\") failed";
487+
FF_STRBUF_AUTO_DESTROY cpuinfo = ffStrbufCreateA(PROC_FILE_BUFFSIZ);
488+
if (!ffReadFileBuffer("/proc/cpuinfo", &cpuinfo) || cpuinfo.length == 0)
489+
return "ffReadFileBuffer(\"/proc/cpuinfo\") failed";
477490

478491
cpu->temperature = options->temp ? detectCPUTemp() : FF_CPU_TEMP_UNSET;
479492

480-
FF_STRBUF_AUTO_DESTROY cpuPhysicalId= ffStrbufCreate();
481493
FF_STRBUF_AUTO_DESTROY physicalCoresBuffer = ffStrbufCreate();
482494
FF_STRBUF_AUTO_DESTROY cpuMHz = ffStrbufCreate();
483495
FF_STRBUF_AUTO_DESTROY cpuIsa = ffStrbufCreate();
484496
FF_STRBUF_AUTO_DESTROY cpuUarch = ffStrbufCreate();
485497
FF_STRBUF_AUTO_DESTROY cpuImplementerStr = ffStrbufCreate();
486498

487-
const char* error = parseCpuInfo(cpuinfo, cpu, &cpuPhysicalId, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr);
499+
const char* error = parseCpuInfo(&cpuinfo, cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr);
488500
if (error) return error;
489501

490502
cpu->coresLogical = (uint16_t) get_nprocs_conf();
491503
cpu->coresOnline = (uint16_t) get_nprocs();
492504
cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, cpu->coresLogical);
493-
cpu->cpuCount = (uint16_t) ffStrbufToUInt(&cpuPhysicalId, 1) +1; //Assuming at least 1 CPU is present otherwise we wouldn't get this far
505+
cpu->cpuCount = getCPUCount(&cpuinfo);
494506

495507
// Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252
496508
ffCPUDetectSpeedByCpuid(cpu);
@@ -525,7 +537,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
525537
#endif
526538

527539
if (cpu->name.length == 0)
528-
detectArmName(cpuinfo, cpu, cpuImplementer);
540+
detectArmName(&cpuinfo, cpu, cpuImplementer);
529541
#endif
530542

531543
return NULL;

src/util/FFstrbuf.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,3 +531,47 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c)
531531
memset(&strbuf->chars[index], c, num);
532532
strbuf->length += num;
533533
}
534+
535+
/**
536+
* @brief Read a line from a FFstrbuf.
537+
*
538+
* @details Behaves like getline(3) but reads from a FFstrbuf.
539+
*
540+
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line.
541+
* Can be NULL for the first call.
542+
* @param[in,out] n The pointer to the size of the buffer of lineptr.
543+
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
544+
*
545+
* @return true if a line has been read, false if the end of the buffer has been reached.
546+
*/
547+
bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
548+
{
549+
assert(lineptr && n && buffer);
550+
assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0));
551+
assert(!*lineptr || (*lineptr >= buffer->chars && *lineptr <= buffer->chars + buffer->length));
552+
553+
const char* pBufferEnd = buffer->chars + buffer->length;
554+
if (!*lineptr)
555+
*lineptr = buffer->chars;
556+
else
557+
{
558+
*lineptr += *n;
559+
if (*lineptr >= pBufferEnd) // non-empty last line
560+
return false;
561+
**lineptr = '\n';
562+
++*lineptr;
563+
}
564+
if (*lineptr >= pBufferEnd) // empty last line
565+
return false;
566+
567+
size_t remaining = (size_t) (pBufferEnd - *lineptr);
568+
char* ending = memchr(*lineptr, '\n', remaining);
569+
if (ending)
570+
{
571+
*n = (size_t) (ending - *lineptr);
572+
*ending = '\0';
573+
}
574+
else
575+
*n = remaining;
576+
return true;
577+
}

src/util/FFstrbuf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV
9090
void ffStrbufUpperCase(FFstrbuf* strbuf);
9191
void ffStrbufLowerCase(FFstrbuf* strbuf);
9292

93+
bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer);
94+
9395
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate)
9496
{
9597
FFstrbuf strbuf;

tests/strbuf.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,113 @@ int main(void)
458458
VERIFY(strbuf2.allocated == 32);
459459
}
460460

461+
{
462+
int i = 0;
463+
char* lineptr = NULL;
464+
size_t n = 0;
465+
const char* text = "Processor\t: ARMv7\nprocessor\t: 0\nBogoMIPS\t: 38.00\n\nprocessor\t: 1\nBogoMIPS\t: 38.00";
466+
ffStrbufSetS(&strbuf, text);
467+
468+
while (ffStrbufGetline(&lineptr, &n, &strbuf))
469+
{
470+
++i;
471+
switch (i)
472+
{
473+
case 1:
474+
VERIFY(strcmp(lineptr, "Processor\t: ARMv7") == 0);
475+
VERIFY(n == strlen("Processor\t: ARMv7"));
476+
break;
477+
case 2:
478+
VERIFY(strcmp(lineptr, "processor\t: 0") == 0);
479+
VERIFY(n == strlen("processor\t: 0"));
480+
break;
481+
case 3:
482+
VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0);
483+
VERIFY(n == strlen("BogoMIPS\t: 38.00"));
484+
break;
485+
case 4:
486+
VERIFY(strcmp(lineptr, "") == 0);
487+
VERIFY(n == 0);
488+
break;
489+
case 5:
490+
VERIFY(strcmp(lineptr, "processor\t: 1") == 0);
491+
VERIFY(n == strlen("processor\t: 1"));
492+
break;
493+
case 6:
494+
VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0);
495+
VERIFY(n == strlen("BogoMIPS\t: 38.00"));
496+
break;
497+
default:
498+
VERIFY(false);
499+
break;
500+
}
501+
}
502+
VERIFY(ffStrbufEqualS(&strbuf, text));
503+
VERIFY(*lineptr == '\0');
504+
VERIFY(i == 6);
505+
506+
lineptr = NULL;
507+
n = 0;
508+
i = 0;
509+
text = "\n";
510+
ffStrbufSetS(&strbuf, text);
511+
while (ffStrbufGetline(&lineptr, &n, &strbuf))
512+
{
513+
++i;
514+
switch (i)
515+
{
516+
case 1:
517+
VERIFY(strcmp(lineptr, "") == 0);
518+
VERIFY(n == 0);
519+
break;
520+
default:
521+
VERIFY(false);
522+
break;
523+
}
524+
}
525+
VERIFY(ffStrbufEqualS(&strbuf, text));
526+
VERIFY(*lineptr == '\0');
527+
VERIFY(i == 1);
528+
529+
lineptr = NULL;
530+
n = 0;
531+
i = 0;
532+
text = "abcd";
533+
ffStrbufSetS(&strbuf, text);
534+
while (ffStrbufGetline(&lineptr, &n, &strbuf))
535+
{
536+
++i;
537+
switch (i)
538+
{
539+
case 1:
540+
VERIFY(strcmp(lineptr, "abcd") == 0);
541+
VERIFY(n == strlen("abcd"));
542+
break;
543+
default:
544+
VERIFY(false);
545+
break;
546+
}
547+
}
548+
VERIFY(ffStrbufEqualS(&strbuf, text));
549+
VERIFY(*lineptr == '\0');
550+
VERIFY(i == 1);
551+
552+
lineptr = NULL;
553+
n = 0;
554+
i = 0;
555+
text = "";
556+
ffStrbufSetS(&strbuf, text);
557+
while (ffStrbufGetline(&lineptr, &n, &strbuf))
558+
{
559+
++i;
560+
VERIFY(false);
561+
}
562+
563+
VERIFY(ffStrbufEqualS(&strbuf, text));
564+
VERIFY(*lineptr == '\0');
565+
VERIFY(i == 0);
566+
}
567+
461568
//Success
462569
puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
463570
}

0 commit comments

Comments
 (0)