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