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