Home | History | Annotate | Line # | Download | only in common
      1 /* Copyright (C) 2021-2026 Free Software Foundation, Inc.
      2    Contributed by Oracle.
      3 
      4    This file is part of GNU Binutils.
      5 
      6    This program is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3, or (at your option)
      9    any later version.
     10 
     11    This program is distributed in the hope that it will be useful,
     12    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program; if not, write to the Free Software
     18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
     19    MA 02110-1301, USA.  */
     20 
     21 #ifndef	_CPU_FREQUENCY_H
     22 #define	_CPU_FREQUENCY_H
     23 
     24 #ifdef __cplusplus
     25 extern "C"
     26 {
     27 #endif
     28 
     29 #include <alloca.h>
     30 #include <unistd.h> /* processor_info_t	*/
     31 #include <fcntl.h>
     32 
     33   typedef unsigned char uint8_t;
     34 
     35 #define MAXSTRLEN               1024
     36   /*
     37    * This file provide the api to detect Intel CPU frequency variation features
     38    */
     39 
     40 #define COL_CPUFREQ_NONE        0x0000
     41 #define COL_CPUFREQ_SCALING     0x0001
     42 #define COL_CPUFREQ_TURBO       0x0002
     43 
     44 #if defined(__i386__) || defined(__x86_64__)
     45   // XXXX This is a rough table to estimate frequency increment due to intel turbo boost.
     46   // CPU with different stepping and different core number have different turbo increment.
     47   //  It is used internally here, and is not implemented on SPARC
     48 
     49   // YLM: one can use cputrack to estimate max turbo frequency
     50   // example: for a cpu-bound app that runs for > 10 seconds, count cycles for 10 seconds:
     51   //     cputrack -T 10 -v -c cpu_clk_unhalted.thread_p a.out
     52 
     53   static int
     54   get_max_turbo_freq (int model)
     55   {
     56     switch (model)
     57       {
     58 	// Nehalem
     59       case 30:// Core i7-870: 2/2/4/5
     60 	return 2 * 133333;
     61       case 26:// Xeon L5520: 1/1/1/2
     62 	return 2 * 133333;
     63       case 46:// Xeon E7540: 2
     64 	return 2 * 133333;
     65 	// Westmere
     66       case 37:// Core i5-520M: 2/4
     67 	return 2 * 133333;
     68       case 44:// Xeon E5620: 1/1/2/2
     69 	return 2 * 133333;
     70       case 47:// Xeon E7-2820: 1/1/1/2
     71 	return 1 * 133333;
     72 	// Sandy Bridge
     73       case 42:// Core i5-2500: 1/2/3/4
     74 	return 3 * 100000;
     75 	// http://ark.intel.com/products/64584/Intel-Xeon-Processor-E5-2660-20M-Cache-2_20-GHz-8_00-GTs-Intel-QPI
     76       case 45:// Xeon E5-2660 GenuineIntel 206D7 family 6 model 45 step 7 clock 2200 MHz
     77 	return 8 * 100000;
     78 	// Ivy Bridge
     79       case 58:// Core i7-3770: 3/4/5/5
     80 	return 4 * 100000;
     81       case 62:// Xeon E5-2697: 3/3/3/3/3/3/3/4/5/6/7/8
     82 	return 7 * 100000;
     83 	// Haswell
     84       case 60:
     85 	return 789000; // empirically we see 3189 MHz - 2400 MHz
     86       case 63:
     87 	return 1280000; // empirically we see 3580 MHz - 2300 MHz for single-threaded
     88 	//  return  500000;   // empirically we see 2800 MHz - 2300 MHz for large throughput
     89 	// Broadwell
     90 	// where are these values listed?
     91 	// maybe try https://en.wikipedia.org/wiki/Broadwell_%28microarchitecture%29#Server_processors
     92       case 61:
     93 	return 400000;
     94       case 71:
     95 	return 400000;
     96       case 79:
     97 	return 950000; // empirically we see (3550-2600) MHz for single-threaded on x6-2a
     98       case 85:
     99 	return 1600000; // X7: empirically see ~3.7GHz with single thread, baseline is 2.1Ghz  Return 3,700,000-2,100,000
    100       case 31: // Nehalem?
    101       case 28: // Atom
    102       case 69: // Haswell
    103       case 70: // Haswell
    104       case 78: // Skylake
    105       case 94: // Skylake
    106       default:
    107 	return 0;
    108       }
    109   }
    110 #endif
    111 
    112   /*
    113    * parameter: mode, pointer to a 8bit mode indicator
    114    * return: max cpu frequency in MHz
    115    */
    116   //YXXX Updating this function?  Check similar cut/paste code in:
    117   // collctrl.cc::Coll_Ctrl()
    118   // collector.c::log_header_write()
    119   // cpu_frequency.h::get_cpu_frequency()
    120 
    121   static int
    122   get_cpu_frequency (uint8_t *mode)
    123   {
    124     int ret_freq = 0;
    125     if (mode != NULL)
    126       *mode = COL_CPUFREQ_NONE;
    127     FILE *procf = fopen ("/proc/cpuinfo", "r");
    128     if (procf != NULL)
    129       {
    130 	char temp[1024];
    131 	int cpu = -1;
    132 #if defined(__i386__) || defined(__x86_64__)
    133 	int model = -1;
    134 	int family = -1;
    135 #endif
    136 	while (fgets (temp, 1024, procf) != NULL)
    137 	  {
    138 	    if (strncmp (temp, "processor", strlen ("processor")) == 0)
    139 	      {
    140 		char *val = strchr (temp, ':');
    141 		cpu = val ? atoi (val + 1) : -1;
    142 	      }
    143 #if defined(__i386__) || defined(__x86_64__)
    144 	    else if (strncmp (temp, "model", strlen ("model")) == 0
    145 		     && strstr (temp, "name") == 0)
    146 	      {
    147 		char *val = strchr (temp, ':');
    148 		model = val ? atoi (val + 1) : -1;
    149 	      }
    150 	    else if (strncmp (temp, "cpu family", strlen ("cpu family")) == 0)
    151 	      {
    152 		char *val = strchr (temp, ':');
    153 		family = val ? atoi (val + 1) : -1;
    154 	      }
    155 #endif
    156 	    else if (strncmp (temp, "cpu MHz", strlen ("cpu MHz")) == 0)
    157 	      {
    158 		char *val = strchr (temp, ':');
    159 		int mhz = val ? atoi (val + 1) : 0; /* reading it as int is fine */
    160 		char scaling_freq_file[MAXSTRLEN + 1];
    161 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
    162 			  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_driver", cpu);
    163 		int intel_pstate = 0;
    164 		int no_turbo = 0;
    165 		if (access (scaling_freq_file, R_OK) == 0)
    166 		  {
    167 		    FILE *cpufreqd = fopen (scaling_freq_file, "r");
    168 		    if (cpufreqd != NULL)
    169 		      {
    170 			if (fgets (temp, 1024, cpufreqd) != NULL
    171 			    && strncmp (temp, "intel_pstate", sizeof ("intel_pstate") - 1) == 0)
    172 			  intel_pstate = 1;
    173 			fclose (cpufreqd);
    174 		      }
    175 		  }
    176 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
    177 			  "/sys/devices/system/cpu/intel_pstate/no_turbo");
    178 		if (access (scaling_freq_file, R_OK) == 0)
    179 		  {
    180 		    FILE *pstatent = fopen (scaling_freq_file, "r");
    181 		    if (pstatent != NULL)
    182 		      {
    183 			if (fgets (temp, 1024, pstatent) != NULL)
    184 			  if (strncmp (temp, "1", sizeof ("1") - 1) == 0)
    185 			    no_turbo = 1;
    186 			fclose (pstatent);
    187 		      }
    188 		  }
    189 
    190 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
    191 			  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
    192 		int frequency_scaling = 0;
    193 		int turbo_mode = 0;
    194 		if (access (scaling_freq_file, R_OK) == 0)
    195 		  {
    196 		    FILE *cpufreqf = fopen (scaling_freq_file, "r");
    197 		    if (cpufreqf != NULL)
    198 		      {
    199 			if (fgets (temp, 1024, cpufreqf) != NULL)
    200 			  {
    201 			    int ondemand = 0;
    202 			    if (strncmp (temp, "ondemand", sizeof ("ondemand") - 1) == 0)
    203 			      ondemand = 1;
    204 			    int performance = 0;
    205 			    if (strncmp (temp, "performance", sizeof ("performance") - 1) == 0)
    206 			      performance = 1;
    207 			    int powersave = 0;
    208 			    if (strncmp (temp, "powersave", sizeof ("powersave") - 1) == 0)
    209 			      powersave = 1;
    210 			    if (intel_pstate || ondemand || performance)
    211 			      {
    212 				snprintf (scaling_freq_file, sizeof (scaling_freq_file),
    213 					  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
    214 				if (access (scaling_freq_file, R_OK) == 0)
    215 				  {
    216 				    FILE * cpufreqf_max;
    217 				    if ((cpufreqf_max = fopen (scaling_freq_file, "r")) != NULL)
    218 				      {
    219 					if (fgets (temp, 1024, cpufreqf_max) != NULL)
    220 					  {
    221 					    int tmpmhz = atoi (temp);
    222 					    snprintf (scaling_freq_file, sizeof (scaling_freq_file),
    223 						      "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu);
    224 					    if (intel_pstate)
    225 					      {
    226 						frequency_scaling = 1;
    227 						turbo_mode = !no_turbo;
    228 						if (powersave)
    229 						  // the system might have been relatively cold
    230 						  // so we might do better with scaling_max_freq
    231 						  mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
    232 					      }
    233 					    else if (access (scaling_freq_file, R_OK) == 0)
    234 					      {
    235 						FILE * cpufreqf_ava;
    236 						if ((cpufreqf_ava = fopen (scaling_freq_file, "r")) != NULL)
    237 						  {
    238 						    if (fgets (temp, 1024, cpufreqf_ava) != NULL)
    239 						      {
    240 							if (strchr (temp, ' ') != strrchr (temp, ' ') && ondemand)
    241 							  frequency_scaling = 1;
    242 							if (tmpmhz > 1000)
    243 							  {
    244 #if defined(__i386__) || defined(__x86_64__)
    245 							    if (family == 6)
    246 							      {
    247 							        // test turbo mode
    248 							        char non_turbo_max_freq[1024];
    249 							        snprintf (non_turbo_max_freq, sizeof (non_turbo_max_freq),
    250 							                  "%d", tmpmhz - 1000);
    251 							        if (strstr (temp, non_turbo_max_freq))
    252 							          {
    253 							            turbo_mode = 1;
    254 							            tmpmhz = (tmpmhz - 1000) + get_max_turbo_freq (model);
    255 							          }
    256 							      }
    257 #endif
    258 							  }
    259 						      }
    260 						    fclose (cpufreqf_ava);
    261 						  }
    262 						mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
    263 					      }
    264 					  }
    265 					fclose (cpufreqf_max);
    266 				      }
    267 				  }
    268 			      }
    269 			  }
    270 			fclose (cpufreqf);
    271 		      }
    272 		  }
    273 		if (mhz > ret_freq)
    274 		  ret_freq = mhz;
    275 		if (frequency_scaling && mode != NULL)
    276 		  *mode |= COL_CPUFREQ_SCALING;
    277 		if (turbo_mode && mode != NULL)
    278 		  *mode |= COL_CPUFREQ_TURBO;
    279 	      }
    280 	    else if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' &&
    281 		     strncmp (strchr (temp + 1, 'C') ? strchr (temp + 1, 'C') : (temp + 4), "ClkTck", 6) == 0)
    282 	      { // sparc-Linux
    283 		char *val = strchr (temp, ':');
    284 		if (val)
    285 		  {
    286 		    unsigned long long freq;
    287 		    sscanf (val + 2, "%llx", &freq);
    288 		    int mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5);
    289 		    if (mhz > ret_freq)
    290 		      ret_freq = mhz;
    291 		  }
    292 	      }
    293 	  }
    294 	fclose (procf);
    295       }
    296     return ret_freq;
    297   }
    298 
    299 #ifdef __cplusplus
    300 }
    301 #endif
    302 
    303 #endif  /*_CPU_FREQUENCY_H*/
    304