binutils-gdb/gprofng/common/cpu_frequency.h
Alan Modra 76bdc7266a Update year range in gprofng copyright notices
This adds 'Innovative Computing Labs' as an external author to
update-copyright.py, to cover the copyright notice in
gprofng/common/opteron_pcbe.c, and uses that plus another external
author 'Oracle and' to update gprofng copyright dates.  I'm not going
to commit 'Oracle and' as an accepted author, but that covers the
string "Copyright (c) 2006, 2012, Oracle and/or its affiliates. All
rights reserved." found in gprofng/testsuite/gprofng.display/jsynprog
files.
2023-01-01 23:26:30 +10:30

304 lines
9.2 KiB
C

/* Copyright (C) 2021-2023 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*/