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