2021-02-18 22:25:36 +01:00
#include "fastfetch.h"
2021-04-09 14:07:05 +02:00
#include <string.h>
2022-06-15 16:14:46 +02:00
#include <errno.h>
2021-04-09 14:07:05 +02:00
2021-04-15 18:59:26 +02:00
#define FF_CPU_MODULE_NAME "CPU"
2022-06-15 16:14:46 +02:00
#define FF_CPU_NUM_FORMAT_ARGS 15
// TODO: possibly add a config option for this
char * FF_CPU_THERMAL_ZONES [] = {
"x86_pkg_temp" , // x86 CPU package temperature
"cpu-thermal" // Raspberry Pi CPU temperature (possibly other ARM platforms aswell)
};
2021-04-15 18:59:26 +02:00
2021-06-16 11:47:32 +02:00
static double parseHz ( FFstrbuf * content )
2021-03-28 13:02:40 +02:00
{
2021-06-16 11:28:33 +02:00
if ( content -> length == 0 )
2021-03-28 13:02:40 +02:00
return 0 ;
2021-06-16 11:47:32 +02:00
double herz ;
if ( sscanf ( content -> chars , "%lf" , & herz ) != 1 )
2021-03-28 13:02:40 +02:00
return 0 ;
2021-06-16 11:47:32 +02:00
return herz ;
2021-03-28 13:02:40 +02:00
}
2021-06-16 11:47:32 +02:00
static double getGhz ( const char * policyFile , const char * cpuFile )
2021-06-16 11:28:33 +02:00
{
FFstrbuf content ;
ffStrbufInit ( & content );
2021-06-16 11:47:32 +02:00
ffGetFileContent ( policyFile , & content );
if ( content . length == 0 )
ffGetFileContent ( cpuFile , & content );
double herz = parseHz ( & content );
2021-06-16 11:28:33 +02:00
ffStrbufDestroy ( & content );
2021-06-16 11:47:32 +02:00
herz /= 1000.0 ; //to MHz
return herz / 1000.0 ; //to GHz
2021-06-16 11:28:33 +02:00
}
2021-02-27 18:30:05 +01:00
void ffPrintCPU ( FFinstance * instance )
2021-02-18 22:25:36 +01:00
{
2021-04-15 18:59:26 +02:00
if ( ffPrintFromCache ( instance , FF_CPU_MODULE_NAME , & instance -> config . cpuKey , & instance -> config . cpuFormat , FF_CPU_NUM_FORMAT_ARGS ))
2021-02-22 16:52:51 +01:00
return ;
2022-05-23 20:01:18 +02:00
FILE * cpuinfo = fopen ( "/proc/cpuinfo" , "r" );
2021-04-03 21:51:50 +02:00
if ( cpuinfo == NULL )
{
2022-05-23 20:01:18 +02:00
ffPrintError ( instance , FF_CPU_MODULE_NAME , 0 , & instance -> config . cpuKey , & instance -> config . cpuFormat , FF_CPU_NUM_FORMAT_ARGS , "fopen( \" ""/proc/cpuinfo \" , \" r \" ) == NULL" );
2021-04-03 21:51:50 +02:00
return ;
}
2021-06-16 11:28:33 +02:00
FFstrbuf name ;
2021-11-17 10:59:31 +01:00
ffStrbufInitA ( & name , 64 );
2021-06-16 11:28:33 +02:00
FFstrbuf vendor ;
2021-11-17 10:59:31 +01:00
ffStrbufInitA ( & vendor , 64 );
2021-06-16 11:28:33 +02:00
FFstrbuf physicalCoresString ;
ffStrbufInit ( & physicalCoresString );
FFstrbuf procGhzString ;
ffStrbufInit ( & procGhzString );
2021-03-27 18:14:19 +01:00
2021-03-28 13:02:40 +02:00
char * line = NULL ;
size_t len = 0 ;
while ( getline ( & line , & len , cpuinfo ) != - 1 )
2021-02-19 16:23:41 +01:00
{
2021-03-28 13:02:40 +02:00
//Stop after the first CPU
2021-10-20 06:04:55 +00:00
if ( name . length > 0 && ( * line == '\0' || * line == '\n' ))
2021-03-28 13:02:40 +02:00
break ;
2021-10-20 06:04:55 +00:00
( void )(
2022-04-08 15:50:49 +02:00
ffParsePropLine ( line , "model name :" , & name ) ||
ffParsePropLine ( line , "vendor_id :" , & vendor ) ||
ffParsePropLine ( line , "cpu cores :" , & physicalCoresString ) ||
ffParsePropLine ( line , "cpu MHz :" , & procGhzString ) ||
( name . length == 0 && ffParsePropLine ( line , "Hardware :" , & name )) //For Android devices
2021-10-20 06:04:55 +00:00
);
2021-02-19 16:23:41 +01:00
}
2021-02-18 22:25:36 +01:00
2021-03-28 13:02:40 +02:00
if ( line != NULL )
free ( line );
fclose ( cpuinfo );
2021-06-16 11:47:32 +02:00
double procGhz = parseHz ( & procGhzString ) / 1000.0 ; //to GHz
2021-06-16 11:28:33 +02:00
ffStrbufDestroy ( & procGhzString );
2021-06-16 11:47:32 +02:00
double biosLimit = getGhz ( "/sys/devices/system/cpu/cpufreq/policy0/bios_limit" , "/sys/devices/system/cpu/cpu0/cpufreq/bios_limit" );
double scalingMaxFreq = getGhz ( "/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq" , "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" );
double scalingMinFreq = getGhz ( "/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq" , "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq" );
double infoMaxFreq = getGhz ( "/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq" , "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" );
double infoMinFreq = getGhz ( "/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_min_freq" , "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq" );
2021-02-18 22:25:36 +01:00
2021-03-28 13:02:40 +02:00
int numProcsOnline = get_nprocs ();
int numProcsAvailable = get_nprocs_conf ();
2021-03-19 20:57:07 +01:00
2021-06-16 11:28:33 +02:00
int physicalCores = 1 ;
sscanf ( physicalCoresString . chars , "%i" , & physicalCores );
ffStrbufDestroy ( & physicalCoresString );
2021-04-03 21:51:50 +02:00
//The current get_nprocs* returns 1 on failure. It also makes no sense to have a (1) as count
int numProcs = numProcsOnline ;
if ( numProcs <= 1 )
numProcs = numProcsAvailable ;
if ( numProcs <= 1 )
numProcs = physicalCores ;
2022-06-15 16:14:46 +02:00
double cpuPackageTemp = __DBL_MAX__ ;
// enumerate thermal zones until we either find the x86_pkg_temp zone or we run out of zones.
// NOTE: This should generally run just a couple times (~10 iterations), but maybe we should put this behind allowSlowOperations?
for ( int tzIndex = 0 ;; tzIndex ++ )
{
char path [ 64 ];
snprintf ( path , sizeof ( path ), "/sys/class/thermal/thermal_zone%d/type" , tzIndex );
FFstrbuf fileContents ;
ffStrbufInit ( & fileContents );
if ( ! ffGetFileContent ( path , & fileContents ))
{
// this termal zone does not exist
ffStrbufDestroy ( & fileContents );
break ;
}
bool isTargetTZ = false ;
for ( size_t i = 0 ; i < sizeof ( FF_CPU_THERMAL_ZONES ) / sizeof ( FF_CPU_THERMAL_ZONES [ 0 ]); i ++ )
{
if ( strncmp ( fileContents . chars , FF_CPU_THERMAL_ZONES [ i ], fileContents . length ) == 0 ) {
isTargetTZ = true ;
break ;
}
}
if ( isTargetTZ )
{
snprintf ( path , sizeof ( path ), "/sys/class/thermal/thermal_zone%d/temp" , tzIndex );
ffStrbufInit ( & fileContents );
if ( ! ffGetFileContent ( path , & fileContents ))
{
ffStrbufDestroy ( & fileContents );
break ;
}
// The temperature is encoded in millicelsius, meaning "49000" is 49C
// NOTE: for strtol errno MUST be manually reset (see manpage)
errno = 0 ;
long sensorValue = strtol ( fileContents . chars , NULL , 10 );
if ( ! errno )
cpuPackageTemp = ( float ) sensorValue / 1000.0f ; // convert to celsius
ffStrbufDestroy ( & fileContents );
break ;
}
ffStrbufDestroy ( & fileContents );
}
if ( cpuPackageTemp == __DBL_MAX__ )
{
ffPrintError ( instance , FF_CPU_MODULE_NAME , 0 , & instance -> config . cpuKey , & instance -> config . cpuFormat , FF_CPU_NUM_FORMAT_ARGS , "Could not find a CPU thermal zone" );
}
2021-04-03 21:51:50 +02:00
double ghz = biosLimit ;
if ( ghz == 0 )
ghz = scalingMaxFreq ;
if ( ghz == 0 )
ghz = infoMaxFreq ;
2021-06-16 11:28:33 +02:00
if ( ghz == 0 )
ghz = procGhz ;
2021-04-03 21:51:50 +02:00
if ( ghz == 0 )
ghz = scalingMinFreq ;
if ( ghz == 0 )
ghz = infoMinFreq ;
if (
2021-06-16 11:28:33 +02:00
name . length == 0 &&
vendor . length == 0 &&
2021-04-03 21:51:50 +02:00
numProcs <= 1 &&
ghz <= 0
) {
2021-06-16 11:28:33 +02:00
ffStrbufDestroy ( & name );
ffStrbufDestroy ( & vendor );
2021-04-15 18:59:26 +02:00
ffPrintError ( instance , FF_CPU_MODULE_NAME , 0 , & instance -> config . cpuKey , & instance -> config . cpuFormat , FF_CPU_NUM_FORMAT_ARGS , "No CPU info found in /proc/cpuinfo" );
2021-04-03 21:51:50 +02:00
return ;
}
FFstrbuf namePretty ;
ffStrbufInitA ( & namePretty , 64 );
2021-06-16 11:28:33 +02:00
ffStrbufAppend ( & namePretty , & name );
2021-04-17 14:37:32 +02:00
const char * removeStrings [] = {
"(R)" , "(r)" , "(TM)" , "(tm)" ,
2021-05-02 21:45:47 +02:00
" CPU" , " FPU" , " APU" , " Processor" ,
2021-04-17 14:37:32 +02:00
" Dual-Core" , " Quad-Core" , " Six-Core" , " Eight-Core" , " Ten-Core" ,
" 2-Core" , " 4-Core" , " 6-Core" , " 8-Core" , " 10-Core" , " 12-Core" , " 14-Core" , " 16-Core"
};
ffStrbufRemoveStringsA ( & namePretty , sizeof ( removeStrings ) / sizeof ( removeStrings [ 0 ]), removeStrings );
2021-04-03 21:51:50 +02:00
ffStrbufSubstrBeforeFirstC ( & namePretty , '@' ); //Cut the speed output in the name as we append our own
ffStrbufTrimRight ( & namePretty , ' ' ); //If we removed the @ in previous step there was most likely a space before it
2021-04-03 12:14:25 +02:00
FFstrbuf cpu ;
ffStrbufInitA ( & cpu , 128 );
2021-03-19 20:57:07 +01:00
2021-04-15 18:59:26 +02:00
if ( namePretty . length > 0 )
ffStrbufAppend ( & cpu , & namePretty );
2021-06-16 11:28:33 +02:00
else if ( name . length > 0 )
ffStrbufAppend ( & cpu , & name );
else if ( vendor . length > 0 )
2021-03-19 20:57:07 +01:00
{
2021-06-16 11:28:33 +02:00
ffStrbufAppend ( & cpu , & vendor );
2022-01-09 16:51:57 +01:00
ffStrbufAppendS ( & cpu , " CPU" );
2021-03-19 20:57:07 +01:00
}
else
2022-01-09 16:51:57 +01:00
ffStrbufAppendS ( & cpu , "CPU" );
2021-04-15 18:59:26 +02:00
if ( numProcs > 1 )
ffStrbufAppendF ( & cpu , " (%i)" , numProcs );
if ( ghz > 0 )
ffStrbufAppendF ( & cpu , " @ %.9gGHz" , ghz );
2022-06-15 16:14:46 +02:00
if ( cpuPackageTemp != __DBL_MAX__ )
ffStrbufAppendF ( & cpu , " (%g°C)" , cpuPackageTemp );
2021-04-15 18:59:26 +02:00
2022-04-08 15:50:49 +02:00
ffPrintAndWriteToCache ( instance , FF_CPU_MODULE_NAME , & instance -> config . cpuKey , & cpu , & instance -> config . cpuFormat , FF_CPU_NUM_FORMAT_ARGS , ( FFformatarg []){
2021-06-16 11:28:33 +02:00
{ FF_FORMAT_ARG_TYPE_STRBUF , & name },
2021-04-15 18:59:26 +02:00
{ FF_FORMAT_ARG_TYPE_STRBUF , & namePretty },
2021-06-16 11:28:33 +02:00
{ FF_FORMAT_ARG_TYPE_STRBUF , & vendor },
2021-04-15 18:59:26 +02:00
{ FF_FORMAT_ARG_TYPE_INT , & numProcsOnline },
{ FF_FORMAT_ARG_TYPE_INT , & numProcsAvailable },
{ FF_FORMAT_ARG_TYPE_INT , & physicalCores },
{ FF_FORMAT_ARG_TYPE_INT , & numProcs },
2022-06-15 16:14:46 +02:00
{ FF_FORMAT_ARG_TYPE_DOUBLE , & cpuPackageTemp },
2021-06-16 11:28:33 +02:00
{ FF_FORMAT_ARG_TYPE_DOUBLE , & biosLimit },
2021-04-15 18:59:26 +02:00
{ FF_FORMAT_ARG_TYPE_DOUBLE , & scalingMaxFreq },
{ FF_FORMAT_ARG_TYPE_DOUBLE , & scalingMinFreq },
{ FF_FORMAT_ARG_TYPE_DOUBLE , & infoMaxFreq },
{ FF_FORMAT_ARG_TYPE_DOUBLE , & infoMinFreq },
2021-06-16 11:28:33 +02:00
{ FF_FORMAT_ARG_TYPE_DOUBLE , & procGhz },
2021-04-15 18:59:26 +02:00
{ FF_FORMAT_ARG_TYPE_DOUBLE , & ghz }
});
2021-03-27 18:14:19 +01:00
2021-04-03 21:51:50 +02:00
ffStrbufDestroy ( & cpu );
2021-06-16 11:28:33 +02:00
ffStrbufDestroy ( & namePretty );
ffStrbufDestroy ( & name );
ffStrbufDestroy ( & vendor );
2021-03-27 18:14:19 +01:00
}