1  /* Copyright (C) 2021-2023 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*/