1/* Copyright (C) 2021-2024 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
25extern "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