]>
Commit | Line | Data |
---|---|---|
fd67aa11 | 1 | /* Copyright (C) 2021-2024 Free Software Foundation, Inc. |
bb368aad VM |
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*/ |