diff options
Diffstat (limited to 'gprofng/common/cpu_frequency.h')
-rw-r--r-- | gprofng/common/cpu_frequency.h | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/gprofng/common/cpu_frequency.h b/gprofng/common/cpu_frequency.h new file mode 100644 index 0000000..b46b54d --- /dev/null +++ b/gprofng/common/cpu_frequency.h @@ -0,0 +1,303 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _CPU_FREQUENCY_H +#define _CPU_FREQUENCY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <alloca.h> +#include <unistd.h> /* processor_info_t */ +#include <fcntl.h> + + typedef unsigned char uint8_t; + +#define MAXSTRLEN 1024 + /* + * This file provide the api to detect Intel CPU frequency variation features + */ + +#define COL_CPUFREQ_NONE 0x0000 +#define COL_CPUFREQ_SCALING 0x0001 +#define COL_CPUFREQ_TURBO 0x0002 + +#if defined(__i386__) || defined(__x86_64) + // XXXX This is a rough table to estimate frequency increment due to intel turbo boost. + // CPU with different stepping and different core number have different turbo increment. + // It is used internally here, and is not implemented on SPARC + + // YLM: one can use cputrack to estimate max turbo frequency + // example: for a cpu-bound app that runs for > 10 seconds, count cycles for 10 seconds: + // cputrack -T 10 -v -c cpu_clk_unhalted.thread_p a.out + + static int + get_max_turbo_freq (int model) + { + switch (model) + { + // Nehalem + case 30:// Core i7-870: 2/2/4/5 + return 2 * 133333; + case 26:// Xeon L5520: 1/1/1/2 + return 2 * 133333; + case 46:// Xeon E7540: 2 + return 2 * 133333; + // Westmere + case 37:// Core i5-520M: 2/4 + return 2 * 133333; + case 44:// Xeon E5620: 1/1/2/2 + return 2 * 133333; + case 47:// Xeon E7-2820: 1/1/1/2 + return 1 * 133333; + // Sandy Bridge + case 42:// Core i5-2500: 1/2/3/4 + return 3 * 100000; + // http://ark.intel.com/products/64584/Intel-Xeon-Processor-E5-2660-20M-Cache-2_20-GHz-8_00-GTs-Intel-QPI + case 45:// Xeon E5-2660 GenuineIntel 206D7 family 6 model 45 step 7 clock 2200 MHz + return 8 * 100000; + // Ivy Bridge + case 58:// Core i7-3770: 3/4/5/5 + return 4 * 100000; + case 62:// Xeon E5-2697: 3/3/3/3/3/3/3/4/5/6/7/8 + return 7 * 100000; + // Haswell + case 60: + return 789000; // empirically we see 3189 MHz - 2400 MHz + case 63: + return 1280000; // empirically we see 3580 MHz - 2300 MHz for single-threaded + // return 500000; // empirically we see 2800 MHz - 2300 MHz for large throughput + // Broadwell + // where are these values listed? + // maybe try https://en.wikipedia.org/wiki/Broadwell_%28microarchitecture%29#Server_processors + case 61: + return 400000; + case 71: + return 400000; + case 79: + return 950000; // empirically we see (3550-2600) MHz for single-threaded on x6-2a + case 85: + return 1600000; // X7: empirically see ~3.7GHz with single thread, baseline is 2.1Ghz Return 3,700,000-2,100,000 + case 31: // Nehalem? + case 28: // Atom + case 69: // Haswell + case 70: // Haswell + case 78: // Skylake + case 94: // Skylake + default: + return 0; + } + } +#endif + + /* + * parameter: mode, pointer to a 8bit mode indicator + * return: max cpu frequency in MHz + */ + //YXXX Updating this function? Check similar cut/paste code in: + // collctrl.cc::Coll_Ctrl() + // collector.c::log_header_write() + // cpu_frequency.h::get_cpu_frequency() + + static int + get_cpu_frequency (uint8_t *mode) + { + int ret_freq = 0; + if (mode != NULL) + *mode = COL_CPUFREQ_NONE; + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + int cpu = -1; +#if defined(__i386__) || defined(__x86_64) + int model = -1; + int family = -1; +#endif + while (fgets (temp, 1024, procf) != NULL) + { + if (strncmp (temp, "processor", strlen ("processor")) == 0) + { + char *val = strchr (temp, ':'); + cpu = val ? atoi (val + 1) : -1; + } +#if defined(__i386__) || defined(__x86_64) + else if (strncmp (temp, "model", strlen ("model")) == 0 + && strstr (temp, "name") == 0) + { + char *val = strchr (temp, ':'); + model = val ? atoi (val + 1) : -1; + } + else if (strncmp (temp, "cpu family", strlen ("cpu family")) == 0) + { + char *val = strchr (temp, ':'); + family = val ? atoi (val + 1) : -1; + } +#endif + else if (strncmp (temp, "cpu MHz", strlen ("cpu MHz")) == 0) + { + char *val = strchr (temp, ':'); + int mhz = val ? atoi (val + 1) : 0; /* reading it as int is fine */ + char scaling_freq_file[MAXSTRLEN + 1]; + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_driver", cpu); + int intel_pstate = 0; + int no_turbo = 0; + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *cpufreqd = fopen (scaling_freq_file, "r"); + if (cpufreqd != NULL) + { + if (fgets (temp, 1024, cpufreqd) != NULL + && strncmp (temp, "intel_pstate", sizeof ("intel_pstate") - 1) == 0) + intel_pstate = 1; + fclose (cpufreqd); + } + } + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/intel_pstate/no_turbo"); + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *pstatent = fopen (scaling_freq_file, "r"); + if (pstatent != NULL) + { + if (fgets (temp, 1024, pstatent) != NULL) + if (strncmp (temp, "1", sizeof ("1") - 1) == 0) + no_turbo = 1; + fclose (pstatent); + } + } + + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu); + int frequency_scaling = 0; + int turbo_mode = 0; + if (access (scaling_freq_file, R_OK) == 0) + { + FILE *cpufreqf = fopen (scaling_freq_file, "r"); + if (cpufreqf != NULL) + { + if (fgets (temp, 1024, cpufreqf) != NULL) + { + int ondemand = 0; + if (strncmp (temp, "ondemand", sizeof ("ondemand") - 1) == 0) + ondemand = 1; + int performance = 0; + if (strncmp (temp, "performance", sizeof ("performance") - 1) == 0) + performance = 1; + int powersave = 0; + if (strncmp (temp, "powersave", sizeof ("powersave") - 1) == 0) + powersave = 1; + if (intel_pstate || ondemand || performance) + { + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + if (access (scaling_freq_file, R_OK) == 0) + { + FILE * cpufreqf_max; + if ((cpufreqf_max = fopen (scaling_freq_file, "r")) != NULL) + { + if (fgets (temp, 1024, cpufreqf_max) != NULL) + { + int tmpmhz = atoi (temp); + snprintf (scaling_freq_file, sizeof (scaling_freq_file), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu); + if (intel_pstate) + { + frequency_scaling = 1; + turbo_mode = !no_turbo; + if (powersave) + // the system might have been relatively cold + // so we might do better with scaling_max_freq + mhz = (int) (((double) tmpmhz / 1000.0) + 0.5); + } + else if (access (scaling_freq_file, R_OK) == 0) + { + FILE * cpufreqf_ava; + if ((cpufreqf_ava = fopen (scaling_freq_file, "r")) != NULL) + { + if (fgets (temp, 1024, cpufreqf_ava) != NULL) + { + if (strchr (temp, ' ') != strrchr (temp, ' ') && ondemand) + frequency_scaling = 1; + if (tmpmhz > 1000) + { +#if defined(__i386__) || defined(__x86_64) + if (family == 6) + { + // test turbo mode + char non_turbo_max_freq[1024]; + snprintf (non_turbo_max_freq, sizeof (non_turbo_max_freq), + "%d", tmpmhz - 1000); + if (strstr (temp, non_turbo_max_freq)) + { + turbo_mode = 1; + tmpmhz = (tmpmhz - 1000) + get_max_turbo_freq (model); + } + } +#endif + } + } + fclose (cpufreqf_ava); + } + mhz = (int) (((double) tmpmhz / 1000.0) + 0.5); + } + } + fclose (cpufreqf_max); + } + } + } + } + fclose (cpufreqf); + } + } + if (mhz > ret_freq) + ret_freq = mhz; + if (frequency_scaling && mode != NULL) + *mode |= COL_CPUFREQ_SCALING; + if (turbo_mode && mode != NULL) + *mode |= COL_CPUFREQ_TURBO; + } + else if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' && + strncmp (strchr (temp + 1, 'C') ? strchr (temp + 1, 'C') : (temp + 4), "ClkTck", 6) == 0) + { // sparc-Linux + char *val = strchr (temp, ':'); + if (val) + { + unsigned long long freq; + sscanf (val + 2, "%llx", &freq); + int mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5); + if (mhz > ret_freq) + ret_freq = mhz; + } + } + } + fclose (procf); + } + return ret_freq; + } + +#ifdef __cplusplus +} +#endif + +#endif /*_CPU_FREQUENCY_H*/ |