/*
linuxsystem.cpp
Copyright 2006 John Poole.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "linuxsystem.h"
#include "platform.h"
#include <sstream>
#if !defined( PLATFORM_LINUX )
#error Wrong platform
#endif
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <regex.h>
#include <fstream>
#include <set>
#if defined( ARCH_PPC )
#include "model.h"
#endif
#if defined( ARCH_X86 )
struct Registers {
uint32 eax;
uint32 ebx;
uint32 ecx;
uint32 edx;
};
static Registers cpuID( uint32 level )
{
Registers r;
asm volatile("cpuid"
: "=a" (r.eax), "=b" (r.ebx), "=c" (r.ecx), "=d" (r.edx)
: "a" (level));
return r;
}
static std::string cpu()
{
std::string result;
Registers r; // r.eax, r.ebx, r.ecx, r.edx;
r = cpuID( 0x80000000 );
if( r.eax & 0x80000000 && r.eax >= 0x80000004 ) {
uint32 brand[13];
char * p;
r = cpuID( 0x80000002 );
brand[0] = r.eax;
brand[1] = r.ebx;
brand[2] = r.ecx;
brand[3] = r.edx;
r = cpuID( 0x80000003 );
brand[4] = r.eax;
brand[5] = r.ebx;
brand[6] = r.ecx;
brand[7] = r.edx;
r = cpuID( 0x80000004 );
brand[8] = r.eax;
brand[9] = r.ebx;
brand[10] = r.ecx;
brand[11] = r.edx;
brand[12] = 0;
p = ( char * )brand;
while( *p != 0 && *p == ' ' ) {
p++;
}
result = p;
}
return result;
}
static std::string cpuid()
{
std::ostringstream s;
Registers r;
uint32 family; //, extendedFamily;
uint32 model; //, extendedModel;
uint32 stepping;
uint32 vendor[4];
char * p;
r = cpuID( 1 );
family = ( r.eax & 0xf00 ) >> 8;
model = ( r.eax & 0xf0 ) >> 4;
stepping = r.eax & 0xf;
if( family == 0x06 || family == 0x0f ) {
if( family == 0x0f ) {
family += ( ( r.eax & 0xff00000 ) >> 20 );
}
model += ( ( r.eax & 0x000f0000 ) >> 12 );
}
//std::cout << "!" << std::endl;
r = cpuID( 0 );
vendor[0] = r.ebx;
vendor[1] = r.edx;
vendor[2] = r.ecx;
vendor[3] = 0;
p = ( char * )vendor;
s << p << " Family " << family << " Model " << model << " Stepping " << stepping;
return s.str();
}
static bool cpusse()
{
Registers r;
r = cpuID( 0x00000001 );
if( r.edx & 0x02000000 ) {
return true;
} else {
return false;
}
}
#else
static std::string cpu()
{
return "";
}
static std::string cpuid()
{
return "";
}
#endif
#define COMPILE_RE(v,r) if( regcomp( v, r, REG_ICASE ) ) { return false; }
bool LinuxSystem::parseCpuInfo()
{
std::ifstream cpuinfo( "/proc/cpuinfo" );
std::string buffer;
regmatch_t match[2];
std::set< int > physicalid;
regex_t processorRE;
regex_t physicalidRE;
regex_t mhzRE;
regex_t cacheRE;
regex_t clockRE;
regex_t cpuRE;
regex_t machineRE;
regex_t motherboardRE;
regex_t altivecRE;
if( !cpuinfo.is_open() ) {
return false;
}
COMPILE_RE( &processorRE, "processor[[:space:]]*: \\([[:digit:]]*\\)" );
COMPILE_RE( &physicalidRE, "physical id[[:space:]]*: \\([[:digit:]]*\\)" );
COMPILE_RE( &mhzRE, "cpu MHz[[:space:]]*: \\([[:digit:]]*\\)" );
COMPILE_RE( &cacheRE, "cache size[[:space:]]*: \\([[:digit:]]*\\)" );
COMPILE_RE( &clockRE, "clock[[:space:]]*: \\([[:digit:]]*\\)" );
COMPILE_RE( &cpuRE, "cpu[[:space:]]*: \\([^$]*\\)" );
COMPILE_RE( &machineRE, "machine[[:space:]]*: \\([^$]*\\)" );
COMPILE_RE( &motherboardRE, "motherboard[[:space:]]*: \\([^$]*\\)" );
COMPILE_RE( &altivecRE, "cpu[[:space:]]*: .* altivec supported" );
if( cpuinfo.is_open() ) {
while( getline( cpuinfo, buffer ) ) {
if( regexec( &processorRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_logicalCount += 1;
}
if( regexec( &physicalidRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
int id = atoi( buffer.substr( match[1].rm_so, match[1].rm_eo ).c_str() );
physicalid.insert( id );
}
if( regexec( &mhzRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_mhz = atoi( buffer.substr( match[1].rm_so, match[1].rm_eo ).c_str() );
}
if( regexec( &cacheRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_cacheSize = atoi( buffer.substr( match[1].rm_so, match[1].rm_eo ).c_str() );
}
if( regexec( &clockRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_mhz = atoi( buffer.substr( match[1].rm_so, match[1].rm_eo ).c_str() );
}
if( regexec( &cpuRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_cpu = buffer.substr( match[1].rm_so, match[1].rm_eo );
}
if( regexec( &machineRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_model = buffer.substr( match[1].rm_so, match[1].rm_eo );
}
if( regexec( &motherboardRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_motherboard =buffer.substr( match[1].rm_so, match[1].rm_eo );
}
if( regexec( &altivecRE, buffer.c_str(), 2, match, 0 ) == 0 ) {
m_altivec = true;
}
}
}
m_physicalCount = physicalid.size();
if( m_physicalCount == 0 ) {
m_physicalCount = 1;
}
return true;
}
LinuxSystem::LinuxSystem()
{
m_logicalCount = 0;
m_physicalCount = 0;
m_mhz = 0;
m_cacheSize = 0;
m_altivec = false;
if( !parseCpuInfo() ) {
m_logicalCount = 1;
m_physicalCount = 1;
}
}
LinuxSystem::~LinuxSystem()
{
}
static uint32 getL1Dcache()
{
uint32 result;
Registers r; // r.eax, r.ebx, r.ecx, r.edx;
r = cpuID( 0x80000000 );
if( r.eax & 0x80000000 && r.eax >= 0x80000005 ) {
r = cpuID( 0x80000005 );
result = 1024 * (r.ecx >> 24); // drop lower 24 bits, convert to bytes
}
return result;
}
static uint32 getL1Ccache()
{
uint32 result;
Registers r; // r.eax, r.ebx, r.ecx, r.edx;
r = cpuID( 0x80000000 );
if( r.eax & 0x80000000 && r.eax >= 0x80000005 ) {
r = cpuID( 0x80000005 );
result = 1024 * (r.edx >> 24); // drop lower 24 bits, convert to bytes
}
return result;
}
static uint32 getL2cache()
{
uint32 result;
Registers r; // r.eax, r.ebx, r.ecx, r.edx;
r = cpuID( 0x80000000 );
if( r.eax & 0x80000000 && r.eax >= 0x80000006 ) {
r = cpuID( 0x80000006 );
result = 1024 * (r.ecx >> 16); // drop lower 16 bits, convert to bytes
}
return result;
}
static uint32 getL3cache()
{
uint32 result;
Registers r; // r.eax, r.ebx, r.ecx, r.edx;
r = cpuID( 0x80000000 );
if( r.eax & 0x80000000 && r.eax >= 0x80000006 ) {
r = cpuID( 0x80000006 );
result = 524288 * (r.edx >> 18); // drop lower 18 bits, convert from 512KB chunks to bytes
}
return result;
}
std::string LinuxSystem::os()
{
std::ostringstream s;
struct utsname name;
uname( &name );
s << name.sysname << " " << name.release << " " << name.machine;
return s.str();
}
std::string LinuxSystem::model()
{
std::ostringstream s;
#if defined( ARCH_PPC )
if( m_model.length() ) {
return getModelName( m_model );
}
#endif
s << "Linux PC (" << cpu() << ")";
return s.str();
}
std::string LinuxSystem::motherboard()
{
if( m_motherboard.length() ) {
return m_motherboard;
}
return "Unknown Motherboard";
}
std::string LinuxSystem::cpu()
{
if( m_cpu.length() ) {
return m_cpu;
}
return ::cpu();
}
std::string LinuxSystem::cpuid()
{
if( m_cpu.length() ) {
return m_cpu;
}
return ::cpuid();
}
uint64 LinuxSystem::cpuLogicalCount()
{
return m_logicalCount;
}
uint64 LinuxSystem::cpuPhysicalCount()
{
return m_physicalCount;
}
uint64 LinuxSystem::cpuFrequency()
{
return m_mhz * 1000000;
}
uint64 LinuxSystem::cpuL1ICache()
{
uint64 result;
result = (uint64)getL1Ccache();
}
uint64 LinuxSystem::cpuL1DCache()
{
uint64 result;
result = (uint64)getL1Dcache();
}
uint64 LinuxSystem::cpuL2Cache()
{
uint64 result;
result = (uint64)getL2cache();
if (result)
return result;
return m_cacheSize * 1024;
}
uint64 LinuxSystem::cpuL3Cache()
{
uint64 result;
result = (uint64)getL3cache();
}
uint64 LinuxSystem::busFrequency()
{
return 0;
}
uint64 LinuxSystem::memorySize()
{
struct sysinfo si;
if( !sysinfo( &si ) ) {
return uint64( si.totalram ) * si.mem_unit;
}
return 0;
}
uint64 LinuxSystem::freeMemory()
{
struct sysinfo si;
if( !sysinfo( &si ) ) {
return uint64( si.freeram ) * si.mem_unit;
}
return 0;
}
uint64 LinuxSystem::simd()
{
#if defined( ARCH_X86 )
return cpusse();
#elif defined( ARCH_PPC )
return m_altivec;
#else
#error Unsupported architecture.
#endif
}