/* BEGIN_COMMON_COPYRIGHT_HEADER * (c)LGPL2+ ** ** SysStat is a Qt-based interface to system statistics ** ** Authors: ** Copyright (c) 2009 - 2012 Kuzma Shapran ** ** 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 ** ** END_COMMON_COPYRIGHT_HEADER */ #include #include "cpustat.h" #include "cpustat_p.h" namespace SysStat { CpuStatPrivate::CpuStatPrivate(CpuStat *parent) : BaseStatPrivate(parent) , mMonitoring(CpuStat::LoadAndFrequency) { mSource = defaultSource(); connect(mTimer, SIGNAL(timeout()), SLOT(timeout())); mUserHz = sysconf(_SC_CLK_TCK); updateSources(); } void CpuStatPrivate::addSource(const QString &source) { bool ok; uint min = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_min_freq").arg(source))).toUInt(&ok); if (ok) { uint max = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_max_freq").arg(source))).toUInt(&ok); if (ok) mBounds[source] = qMakePair(min, max); } } void CpuStatPrivate::updateSources() { mSources.clear(); foreach (const QString &row, readAllFile("/proc/stat").split(QChar('\n'), QString::SkipEmptyParts)) { QStringList tokens = row.split(QChar(' '), QString::SkipEmptyParts); if( (tokens.size() < 5) || (!tokens[0].startsWith("cpu")) ) continue; mSources.append(tokens[0]); } mBounds.clear(); bool ok; foreach (const QString &range, readAllFile("/sys/devices/system/cpu/online").split(QChar(','), QString::SkipEmptyParts)) { int dash = range.indexOf('-'); if (dash != -1) { uint min = range.leftRef(dash).toUInt(&ok); if (ok) { uint max = range.midRef(dash + 1).toUInt(&ok); if (ok) for (uint number = min; number <= max; ++number) addSource(QString("cpu%1").arg(number)); } } else { uint number = range.toUInt(&ok); if (ok) addSource(QString("cpu%1").arg(number)); } } } CpuStatPrivate::~CpuStatPrivate() { } void CpuStatPrivate::intervalChanged() { recalculateMinMax(); } void CpuStatPrivate::sourceChanged() { recalculateMinMax(); } void CpuStatPrivate::recalculateMinMax() { int cores = 1; if (mSource == "cpu") cores = mSources.size() - 1; mIntervalMin = static_cast(mTimer->interval()) / 1000 * static_cast(mUserHz) * static_cast(cores) / 1.25; // -25% mIntervalMax = static_cast(mTimer->interval()) / 1000 * static_cast(mUserHz) * static_cast(cores) * 1.25; // +25% } void CpuStatPrivate::timeout() { if ( (mMonitoring == CpuStat::LoadOnly) || (mMonitoring == CpuStat::LoadAndFrequency) ) { foreach (const QString &row, readAllFile("/proc/stat").split(QChar('\n'), QString::SkipEmptyParts)) { if (!row.startsWith("cpu")) continue; if (row.startsWith(mSource + " ")) { QStringList tokens = row.split(QChar(' '), QString::SkipEmptyParts); if (tokens.size() < 5) continue; Values current; current.user = tokens[1].toULongLong(); current.nice = tokens[2].toULongLong(); current.system = tokens[3].toULongLong(); current.idle = tokens[4].toULongLong(); current.other = 0; int m = tokens.size(); for (int i = 5; i < m; ++i) current.other += tokens[i].toULongLong(); current.sum(); float sumDelta = static_cast(current.total - mPrevious.total); if ((mPrevious.total != 0) && ((sumDelta < mIntervalMin) || (sumDelta > mIntervalMax))) { if (mMonitoring == CpuStat::LoadAndFrequency) emit update(0.0, 0.0, 0.0, 0.0, 0.0, 0); else emit update(0.0, 0.0, 0.0, 0.0); mPrevious.clear(); // make sure it won't keep crazy values. } else { if (mMonitoring == CpuStat::LoadAndFrequency) { float freqRate = 1.0; uint freq = 0; bool ok; if (mSource == "cpu") { uint count = 0; freqRate = 0.0; for (Bounds::ConstIterator I = mBounds.constBegin(); I != mBounds.constEnd(); ++I) { uint thisFreq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(I.key()))).toUInt(&ok); if (ok) { freq += thisFreq; freqRate += static_cast(thisFreq) / static_cast(I.value().second); ++count; } } if (!count) freqRate = 1.0; else { freq /= count; freqRate /= count; } } else { freq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(mSource))).toUInt(&ok); if (ok) { Bounds::ConstIterator I = mBounds.constFind(mSource); if (I != mBounds.constEnd()) freqRate = static_cast(freq) / static_cast(I.value().second); } } emit update( static_cast(current.user - mPrevious.user ) / sumDelta, static_cast(current.nice - mPrevious.nice ) / sumDelta, static_cast(current.system - mPrevious.system) / sumDelta, static_cast(current.other - mPrevious.other ) / sumDelta, freqRate, freq); } else { emit update( static_cast(current.user - mPrevious.user ) / sumDelta, static_cast(current.nice - mPrevious.nice ) / sumDelta, static_cast(current.system - mPrevious.system) / sumDelta, static_cast(current.other - mPrevious.other ) / sumDelta); } mPrevious = current; } } } } else { bool ok; uint freq = 0; if (mSource == "cpu") { uint count = 0; for (Bounds::ConstIterator I = mBounds.constBegin(); I != mBounds.constEnd(); ++I) { uint thisFreq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(I.key()))).toUInt(&ok); if (ok) { freq += thisFreq; ++count; } } if (count) { freq /= count; } } else { freq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(mSource))).toUInt(&ok); } emit update(freq); } } QString CpuStatPrivate::defaultSource() { return "cpu"; } CpuStatPrivate::Values::Values() : user(0) , nice(0) , system(0) , idle(0) , other(0) , total(0) { } void CpuStatPrivate::Values::sum() { total = user + nice + system + idle + other; } void CpuStatPrivate::Values::clear() { total = user = nice = system = idle = other = 0; } CpuStat::Monitoring CpuStatPrivate::monitoring() const { return mMonitoring; } void CpuStatPrivate::setMonitoring(CpuStat::Monitoring value) { mPrevious.clear(); mMonitoring = value; } CpuStat::CpuStat(QObject *parent) : BaseStat(parent) { impl = new CpuStatPrivate(this); baseimpl = impl; connect(impl, SIGNAL(update(float,float,float,float,float,uint)), this, SIGNAL(update(float,float,float,float,float,uint))); connect(impl, SIGNAL(update(float,float,float,float)), this, SIGNAL(update(float,float,float,float))); connect(impl, SIGNAL(update(uint)), this, SIGNAL(update(uint))); } CpuStat::~CpuStat() { } void CpuStat::updateSources() { dynamic_cast(impl)->updateSources(); } CpuStat::Monitoring CpuStat::monitoring() const { return impl->monitoring(); } void CpuStat::setMonitoring(CpuStat::Monitoring value) { if (impl->monitoring() != value) { impl->setMonitoring(value); emit monitoringChanged(value); } } }