mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
76bdc7266a
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.
304 lines
9.2 KiB
C
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*/
|