xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/common/cpu_frequency.h (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1*cb63e24eSchristos /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
24f645668Schristos    Contributed by Oracle.
34f645668Schristos 
44f645668Schristos    This file is part of GNU Binutils.
54f645668Schristos 
64f645668Schristos    This program is free software; you can redistribute it and/or modify
74f645668Schristos    it under the terms of the GNU General Public License as published by
84f645668Schristos    the Free Software Foundation; either version 3, or (at your option)
94f645668Schristos    any later version.
104f645668Schristos 
114f645668Schristos    This program is distributed in the hope that it will be useful,
124f645668Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
134f645668Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144f645668Schristos    GNU General Public License for more details.
154f645668Schristos 
164f645668Schristos    You should have received a copy of the GNU General Public License
174f645668Schristos    along with this program; if not, write to the Free Software
184f645668Schristos    Foundation, 51 Franklin Street - Fifth Floor, Boston,
194f645668Schristos    MA 02110-1301, USA.  */
204f645668Schristos 
214f645668Schristos #ifndef	_CPU_FREQUENCY_H
224f645668Schristos #define	_CPU_FREQUENCY_H
234f645668Schristos 
244f645668Schristos #ifdef __cplusplus
254f645668Schristos extern "C"
264f645668Schristos {
274f645668Schristos #endif
284f645668Schristos 
294f645668Schristos #include <alloca.h>
304f645668Schristos #include <unistd.h> /* processor_info_t	*/
314f645668Schristos #include <fcntl.h>
324f645668Schristos 
334f645668Schristos   typedef unsigned char uint8_t;
344f645668Schristos 
354f645668Schristos #define MAXSTRLEN               1024
364f645668Schristos   /*
374f645668Schristos    * This file provide the api to detect Intel CPU frequency variation features
384f645668Schristos    */
394f645668Schristos 
404f645668Schristos #define COL_CPUFREQ_NONE        0x0000
414f645668Schristos #define COL_CPUFREQ_SCALING     0x0001
424f645668Schristos #define COL_CPUFREQ_TURBO       0x0002
434f645668Schristos 
444f645668Schristos #if defined(__i386__) || defined(__x86_64)
454f645668Schristos   // XXXX This is a rough table to estimate frequency increment due to intel turbo boost.
464f645668Schristos   // CPU with different stepping and different core number have different turbo increment.
474f645668Schristos   //  It is used internally here, and is not implemented on SPARC
484f645668Schristos 
494f645668Schristos   // YLM: one can use cputrack to estimate max turbo frequency
504f645668Schristos   // example: for a cpu-bound app that runs for > 10 seconds, count cycles for 10 seconds:
514f645668Schristos   //     cputrack -T 10 -v -c cpu_clk_unhalted.thread_p a.out
524f645668Schristos 
534f645668Schristos   static int
get_max_turbo_freq(int model)544f645668Schristos   get_max_turbo_freq (int model)
554f645668Schristos   {
564f645668Schristos     switch (model)
574f645668Schristos       {
584f645668Schristos 	// Nehalem
594f645668Schristos       case 30:// Core i7-870: 2/2/4/5
604f645668Schristos 	return 2 * 133333;
614f645668Schristos       case 26:// Xeon L5520: 1/1/1/2
624f645668Schristos 	return 2 * 133333;
634f645668Schristos       case 46:// Xeon E7540: 2
644f645668Schristos 	return 2 * 133333;
654f645668Schristos 	// Westmere
664f645668Schristos       case 37:// Core i5-520M: 2/4
674f645668Schristos 	return 2 * 133333;
684f645668Schristos       case 44:// Xeon E5620: 1/1/2/2
694f645668Schristos 	return 2 * 133333;
704f645668Schristos       case 47:// Xeon E7-2820: 1/1/1/2
714f645668Schristos 	return 1 * 133333;
724f645668Schristos 	// Sandy Bridge
734f645668Schristos       case 42:// Core i5-2500: 1/2/3/4
744f645668Schristos 	return 3 * 100000;
754f645668Schristos 	// http://ark.intel.com/products/64584/Intel-Xeon-Processor-E5-2660-20M-Cache-2_20-GHz-8_00-GTs-Intel-QPI
764f645668Schristos       case 45:// Xeon E5-2660 GenuineIntel 206D7 family 6 model 45 step 7 clock 2200 MHz
774f645668Schristos 	return 8 * 100000;
784f645668Schristos 	// Ivy Bridge
794f645668Schristos       case 58:// Core i7-3770: 3/4/5/5
804f645668Schristos 	return 4 * 100000;
814f645668Schristos       case 62:// Xeon E5-2697: 3/3/3/3/3/3/3/4/5/6/7/8
824f645668Schristos 	return 7 * 100000;
834f645668Schristos 	// Haswell
844f645668Schristos       case 60:
854f645668Schristos 	return 789000; // empirically we see 3189 MHz - 2400 MHz
864f645668Schristos       case 63:
874f645668Schristos 	return 1280000; // empirically we see 3580 MHz - 2300 MHz for single-threaded
884f645668Schristos 	//  return  500000;   // empirically we see 2800 MHz - 2300 MHz for large throughput
894f645668Schristos 	// Broadwell
904f645668Schristos 	// where are these values listed?
914f645668Schristos 	// maybe try https://en.wikipedia.org/wiki/Broadwell_%28microarchitecture%29#Server_processors
924f645668Schristos       case 61:
934f645668Schristos 	return 400000;
944f645668Schristos       case 71:
954f645668Schristos 	return 400000;
964f645668Schristos       case 79:
974f645668Schristos 	return 950000; // empirically we see (3550-2600) MHz for single-threaded on x6-2a
984f645668Schristos       case 85:
994f645668Schristos 	return 1600000; // X7: empirically see ~3.7GHz with single thread, baseline is 2.1Ghz  Return 3,700,000-2,100,000
1004f645668Schristos       case 31: // Nehalem?
1014f645668Schristos       case 28: // Atom
1024f645668Schristos       case 69: // Haswell
1034f645668Schristos       case 70: // Haswell
1044f645668Schristos       case 78: // Skylake
1054f645668Schristos       case 94: // Skylake
1064f645668Schristos       default:
1074f645668Schristos 	return 0;
1084f645668Schristos       }
1094f645668Schristos   }
1104f645668Schristos #endif
1114f645668Schristos 
1124f645668Schristos   /*
1134f645668Schristos    * parameter: mode, pointer to a 8bit mode indicator
1144f645668Schristos    * return: max cpu frequency in MHz
1154f645668Schristos    */
1164f645668Schristos   //YXXX Updating this function?  Check similar cut/paste code in:
1174f645668Schristos   // collctrl.cc::Coll_Ctrl()
1184f645668Schristos   // collector.c::log_header_write()
1194f645668Schristos   // cpu_frequency.h::get_cpu_frequency()
1204f645668Schristos 
1214f645668Schristos   static int
get_cpu_frequency(uint8_t * mode)1224f645668Schristos   get_cpu_frequency (uint8_t *mode)
1234f645668Schristos   {
1244f645668Schristos     int ret_freq = 0;
1254f645668Schristos     if (mode != NULL)
1264f645668Schristos       *mode = COL_CPUFREQ_NONE;
1274f645668Schristos     FILE *procf = fopen ("/proc/cpuinfo", "r");
1284f645668Schristos     if (procf != NULL)
1294f645668Schristos       {
1304f645668Schristos 	char temp[1024];
1314f645668Schristos 	int cpu = -1;
1324f645668Schristos #if defined(__i386__) || defined(__x86_64)
1334f645668Schristos 	int model = -1;
1344f645668Schristos 	int family = -1;
1354f645668Schristos #endif
1364f645668Schristos 	while (fgets (temp, 1024, procf) != NULL)
1374f645668Schristos 	  {
1384f645668Schristos 	    if (strncmp (temp, "processor", strlen ("processor")) == 0)
1394f645668Schristos 	      {
1404f645668Schristos 		char *val = strchr (temp, ':');
1414f645668Schristos 		cpu = val ? atoi (val + 1) : -1;
1424f645668Schristos 	      }
1434f645668Schristos #if defined(__i386__) || defined(__x86_64)
1444f645668Schristos 	    else if (strncmp (temp, "model", strlen ("model")) == 0
1454f645668Schristos 		     && strstr (temp, "name") == 0)
1464f645668Schristos 	      {
1474f645668Schristos 		char *val = strchr (temp, ':');
1484f645668Schristos 		model = val ? atoi (val + 1) : -1;
1494f645668Schristos 	      }
1504f645668Schristos 	    else if (strncmp (temp, "cpu family", strlen ("cpu family")) == 0)
1514f645668Schristos 	      {
1524f645668Schristos 		char *val = strchr (temp, ':');
1534f645668Schristos 		family = val ? atoi (val + 1) : -1;
1544f645668Schristos 	      }
1554f645668Schristos #endif
1564f645668Schristos 	    else if (strncmp (temp, "cpu MHz", strlen ("cpu MHz")) == 0)
1574f645668Schristos 	      {
1584f645668Schristos 		char *val = strchr (temp, ':');
1594f645668Schristos 		int mhz = val ? atoi (val + 1) : 0; /* reading it as int is fine */
1604f645668Schristos 		char scaling_freq_file[MAXSTRLEN + 1];
1614f645668Schristos 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
1624f645668Schristos 			  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_driver", cpu);
1634f645668Schristos 		int intel_pstate = 0;
1644f645668Schristos 		int no_turbo = 0;
1654f645668Schristos 		if (access (scaling_freq_file, R_OK) == 0)
1664f645668Schristos 		  {
1674f645668Schristos 		    FILE *cpufreqd = fopen (scaling_freq_file, "r");
1684f645668Schristos 		    if (cpufreqd != NULL)
1694f645668Schristos 		      {
1704f645668Schristos 			if (fgets (temp, 1024, cpufreqd) != NULL
1714f645668Schristos 			    && strncmp (temp, "intel_pstate", sizeof ("intel_pstate") - 1) == 0)
1724f645668Schristos 			  intel_pstate = 1;
1734f645668Schristos 			fclose (cpufreqd);
1744f645668Schristos 		      }
1754f645668Schristos 		  }
1764f645668Schristos 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
1774f645668Schristos 			  "/sys/devices/system/cpu/intel_pstate/no_turbo");
1784f645668Schristos 		if (access (scaling_freq_file, R_OK) == 0)
1794f645668Schristos 		  {
1804f645668Schristos 		    FILE *pstatent = fopen (scaling_freq_file, "r");
1814f645668Schristos 		    if (pstatent != NULL)
1824f645668Schristos 		      {
1834f645668Schristos 			if (fgets (temp, 1024, pstatent) != NULL)
1844f645668Schristos 			  if (strncmp (temp, "1", sizeof ("1") - 1) == 0)
1854f645668Schristos 			    no_turbo = 1;
1864f645668Schristos 			fclose (pstatent);
1874f645668Schristos 		      }
1884f645668Schristos 		  }
1894f645668Schristos 
1904f645668Schristos 		snprintf (scaling_freq_file, sizeof (scaling_freq_file),
1914f645668Schristos 			  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
1924f645668Schristos 		int frequency_scaling = 0;
1934f645668Schristos 		int turbo_mode = 0;
1944f645668Schristos 		if (access (scaling_freq_file, R_OK) == 0)
1954f645668Schristos 		  {
1964f645668Schristos 		    FILE *cpufreqf = fopen (scaling_freq_file, "r");
1974f645668Schristos 		    if (cpufreqf != NULL)
1984f645668Schristos 		      {
1994f645668Schristos 			if (fgets (temp, 1024, cpufreqf) != NULL)
2004f645668Schristos 			  {
2014f645668Schristos 			    int ondemand = 0;
2024f645668Schristos 			    if (strncmp (temp, "ondemand", sizeof ("ondemand") - 1) == 0)
2034f645668Schristos 			      ondemand = 1;
2044f645668Schristos 			    int performance = 0;
2054f645668Schristos 			    if (strncmp (temp, "performance", sizeof ("performance") - 1) == 0)
2064f645668Schristos 			      performance = 1;
2074f645668Schristos 			    int powersave = 0;
2084f645668Schristos 			    if (strncmp (temp, "powersave", sizeof ("powersave") - 1) == 0)
2094f645668Schristos 			      powersave = 1;
2104f645668Schristos 			    if (intel_pstate || ondemand || performance)
2114f645668Schristos 			      {
2124f645668Schristos 				snprintf (scaling_freq_file, sizeof (scaling_freq_file),
2134f645668Schristos 					  "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
2144f645668Schristos 				if (access (scaling_freq_file, R_OK) == 0)
2154f645668Schristos 				  {
2164f645668Schristos 				    FILE * cpufreqf_max;
2174f645668Schristos 				    if ((cpufreqf_max = fopen (scaling_freq_file, "r")) != NULL)
2184f645668Schristos 				      {
2194f645668Schristos 					if (fgets (temp, 1024, cpufreqf_max) != NULL)
2204f645668Schristos 					  {
2214f645668Schristos 					    int tmpmhz = atoi (temp);
2224f645668Schristos 					    snprintf (scaling_freq_file, sizeof (scaling_freq_file),
2234f645668Schristos 						      "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu);
2244f645668Schristos 					    if (intel_pstate)
2254f645668Schristos 					      {
2264f645668Schristos 						frequency_scaling = 1;
2274f645668Schristos 						turbo_mode = !no_turbo;
2284f645668Schristos 						if (powersave)
2294f645668Schristos 						  // the system might have been relatively cold
2304f645668Schristos 						  // so we might do better with scaling_max_freq
2314f645668Schristos 						  mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
2324f645668Schristos 					      }
2334f645668Schristos 					    else if (access (scaling_freq_file, R_OK) == 0)
2344f645668Schristos 					      {
2354f645668Schristos 						FILE * cpufreqf_ava;
2364f645668Schristos 						if ((cpufreqf_ava = fopen (scaling_freq_file, "r")) != NULL)
2374f645668Schristos 						  {
2384f645668Schristos 						    if (fgets (temp, 1024, cpufreqf_ava) != NULL)
2394f645668Schristos 						      {
2404f645668Schristos 							if (strchr (temp, ' ') != strrchr (temp, ' ') && ondemand)
2414f645668Schristos 							  frequency_scaling = 1;
2424f645668Schristos 							if (tmpmhz > 1000)
2434f645668Schristos 							  {
2444f645668Schristos #if defined(__i386__) || defined(__x86_64)
2454f645668Schristos 							    if (family == 6)
2464f645668Schristos 							      {
2474f645668Schristos 							        // test turbo mode
2484f645668Schristos 							        char non_turbo_max_freq[1024];
2494f645668Schristos 							        snprintf (non_turbo_max_freq, sizeof (non_turbo_max_freq),
2504f645668Schristos 							                  "%d", tmpmhz - 1000);
2514f645668Schristos 							        if (strstr (temp, non_turbo_max_freq))
2524f645668Schristos 							          {
2534f645668Schristos 							            turbo_mode = 1;
2544f645668Schristos 							            tmpmhz = (tmpmhz - 1000) + get_max_turbo_freq (model);
2554f645668Schristos 							          }
2564f645668Schristos 							      }
2574f645668Schristos #endif
2584f645668Schristos 							  }
2594f645668Schristos 						      }
2604f645668Schristos 						    fclose (cpufreqf_ava);
2614f645668Schristos 						  }
2624f645668Schristos 						mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
2634f645668Schristos 					      }
2644f645668Schristos 					  }
2654f645668Schristos 					fclose (cpufreqf_max);
2664f645668Schristos 				      }
2674f645668Schristos 				  }
2684f645668Schristos 			      }
2694f645668Schristos 			  }
2704f645668Schristos 			fclose (cpufreqf);
2714f645668Schristos 		      }
2724f645668Schristos 		  }
2734f645668Schristos 		if (mhz > ret_freq)
2744f645668Schristos 		  ret_freq = mhz;
2754f645668Schristos 		if (frequency_scaling && mode != NULL)
2764f645668Schristos 		  *mode |= COL_CPUFREQ_SCALING;
2774f645668Schristos 		if (turbo_mode && mode != NULL)
2784f645668Schristos 		  *mode |= COL_CPUFREQ_TURBO;
2794f645668Schristos 	      }
2804f645668Schristos 	    else if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' &&
2814f645668Schristos 		     strncmp (strchr (temp + 1, 'C') ? strchr (temp + 1, 'C') : (temp + 4), "ClkTck", 6) == 0)
2824f645668Schristos 	      { // sparc-Linux
2834f645668Schristos 		char *val = strchr (temp, ':');
2844f645668Schristos 		if (val)
2854f645668Schristos 		  {
2864f645668Schristos 		    unsigned long long freq;
2874f645668Schristos 		    sscanf (val + 2, "%llx", &freq);
2884f645668Schristos 		    int mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5);
2894f645668Schristos 		    if (mhz > ret_freq)
2904f645668Schristos 		      ret_freq = mhz;
2914f645668Schristos 		  }
2924f645668Schristos 	      }
2934f645668Schristos 	  }
2944f645668Schristos 	fclose (procf);
2954f645668Schristos       }
2964f645668Schristos     return ret_freq;
2974f645668Schristos   }
2984f645668Schristos 
2994f645668Schristos #ifdef __cplusplus
3004f645668Schristos }
3014f645668Schristos #endif
3024f645668Schristos 
3034f645668Schristos #endif  /*_CPU_FREQUENCY_H*/
304