f275af10e1a68edf5b0d0dbf13ed92b36baa752a
[lxde/libsysstat.git] / cpustat.cpp
1 /* BEGIN_COMMON_COPYRIGHT_HEADER
2 * (c)LGPL2+
3 **
4 ** SysStat is a Qt-based interface to system statistics
5 **
6 ** Authors:
7 ** Copyright (c) 2009 - 2012 Kuzma Shapran <Kuzma.Shapran@gmail.com>
8 **
9 ** This library is free software; you can redistribute it and/or
10 ** modify it under the terms of the GNU Lesser General Public
11 ** License as published by the Free Software Foundation; either
12 ** version 2.1 of the License, or (at your option) any later version.
13 **
14 ** This library is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 ** Lesser General Public License for more details.
18 **
19 ** You should have received a copy of the GNU Lesser General Public
20 ** License along with this library;
21 ** if not, write to the Free Software Foundation, Inc.,
22 ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 **
24 ** END_COMMON_COPYRIGHT_HEADER */
25
26
27 #include <unistd.h>
28
29 #include "cpustat.h"
30 #include "cpustat_p.h"
31
32
33 namespace SysStat {
34
35 CpuStatPrivate::CpuStatPrivate(CpuStat *parent)
36 : BaseStatPrivate(parent)
37 , mMonitoring(CpuStat::LoadAndFrequency)
38 {
39 mSource = defaultSource();
40
41 connect(mTimer, SIGNAL(timeout()), SLOT(timeout()));
42
43 mUserHz = sysconf(_SC_CLK_TCK);
44
45 updateSources();
46 }
47
48 void CpuStatPrivate::addSource(const QString &source)
49 {
50 bool ok;
51
52 uint min = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_min_freq").arg(source))).toUInt(&ok);
53 if (ok)
54 {
55 uint max = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_max_freq").arg(source))).toUInt(&ok);
56 if (ok)
57 mBounds[source] = qMakePair(min, max);
58 }
59 }
60
61 void CpuStatPrivate::updateSources()
62 {
63 mSources.clear();
64
65 foreach (const QString &row, readAllFile("/proc/stat").split(QChar('\n'), QString::SkipEmptyParts))
66 {
67 QStringList tokens = row.split(QChar(' '), QString::SkipEmptyParts);
68 if( (tokens.size() < 5)
69 || (!tokens[0].startsWith("cpu")) )
70 continue;
71
72 mSources.append(tokens[0]);
73 }
74
75 mBounds.clear();
76
77 bool ok;
78
79 foreach (const QString &range, readAllFile("/sys/devices/system/cpu/online").split(QChar(','), QString::SkipEmptyParts))
80 {
81 int dash = range.indexOf('-');
82 if (dash != -1)
83 {
84 uint min = range.leftRef(dash).toUInt(&ok);
85 if (ok)
86 {
87 uint max = range.midRef(dash + 1).toUInt(&ok);
88 if (ok)
89 for (uint number = min; number <= max; ++number)
90 addSource(QString("cpu%1").arg(number));
91 }
92 }
93 else
94 {
95 uint number = range.toUInt(&ok);
96 if (ok)
97 addSource(QString("cpu%1").arg(number));
98 }
99 }
100 }
101
102 CpuStatPrivate::~CpuStatPrivate()
103 {
104 }
105
106 void CpuStatPrivate::intervalChanged()
107 {
108 recalculateMinMax();
109 }
110
111 void CpuStatPrivate::sourceChanged()
112 {
113 recalculateMinMax();
114 }
115
116 void CpuStatPrivate::recalculateMinMax()
117 {
118 int cores = 1;
119 if (mSource == "cpu")
120 cores = mSources.size() - 1;
121
122 mIntervalMin = static_cast<float>(mTimer->interval()) / 1000 * static_cast<float>(mUserHz) * static_cast<float>(cores) / 1.25; // -25%
123 mIntervalMax = static_cast<float>(mTimer->interval()) / 1000 * static_cast<float>(mUserHz) * static_cast<float>(cores) * 1.25; // +25%
124 }
125
126 void CpuStatPrivate::timeout()
127 {
128 if ( (mMonitoring == CpuStat::LoadOnly)
129 || (mMonitoring == CpuStat::LoadAndFrequency) )
130 {
131 foreach (const QString &row, readAllFile("/proc/stat").split(QChar('\n'), QString::SkipEmptyParts))
132 {
133 if (!row.startsWith("cpu"))
134 continue;
135
136 if (row.startsWith(mSource + " "))
137 {
138 QStringList tokens = row.split(QChar(' '), QString::SkipEmptyParts);
139 if (tokens.size() < 5)
140 continue;
141
142 Values current;
143 current.user = tokens[1].toULongLong();
144 current.nice = tokens[2].toULongLong();
145 current.system = tokens[3].toULongLong();
146 current.idle = tokens[4].toULongLong();
147 current.other = 0;
148 int m = tokens.size();
149 for (int i = 5; i < m; ++i)
150 current.other += tokens[i].toULongLong();
151 current.sum();
152
153 float sumDelta = static_cast<float>(current.total - mPrevious.total);
154
155 if ((mPrevious.total != 0) && ((sumDelta < mIntervalMin) || (sumDelta > mIntervalMax)))
156 {
157 if (mMonitoring == CpuStat::LoadAndFrequency)
158 emit update(0.0, 0.0, 0.0, 0.0, 0.0, 0);
159 else
160 emit update(0.0, 0.0, 0.0, 0.0);
161
162 mPrevious.clear(); // make sure it won't keep crazy values.
163 }
164 else
165 {
166 if (mMonitoring == CpuStat::LoadAndFrequency)
167 {
168 float freqRate = 1.0;
169 uint freq = 0;
170
171 bool ok;
172
173 if (mSource == "cpu")
174 {
175 uint count = 0;
176 freqRate = 0.0;
177
178 for (Bounds::ConstIterator I = mBounds.constBegin(); I != mBounds.constEnd(); ++I)
179 {
180 uint thisFreq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(I.key()))).toUInt(&ok);
181
182 if (ok)
183 {
184 freq += thisFreq;
185 freqRate += static_cast<float>(thisFreq) / static_cast<float>(I.value().second);
186 ++count;
187 }
188 }
189 if (!count)
190 freqRate = 1.0;
191 else
192 {
193 freq /= count;
194 freqRate /= count;
195 }
196 }
197 else
198 {
199 freq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(mSource))).toUInt(&ok);
200
201 if (ok)
202 {
203 Bounds::ConstIterator I = mBounds.constFind(mSource);
204 if (I != mBounds.constEnd())
205 freqRate = static_cast<float>(freq) / static_cast<float>(I.value().second);
206 }
207 }
208
209 emit update(
210 static_cast<float>(current.user - mPrevious.user ) / sumDelta,
211 static_cast<float>(current.nice - mPrevious.nice ) / sumDelta,
212 static_cast<float>(current.system - mPrevious.system) / sumDelta,
213 static_cast<float>(current.other - mPrevious.other ) / sumDelta,
214 freqRate,
215 freq);
216 }
217 else
218 {
219 emit update(
220 static_cast<float>(current.user - mPrevious.user ) / sumDelta,
221 static_cast<float>(current.nice - mPrevious.nice ) / sumDelta,
222 static_cast<float>(current.system - mPrevious.system) / sumDelta,
223 static_cast<float>(current.other - mPrevious.other ) / sumDelta);
224 }
225
226 mPrevious = current;
227 }
228 }
229 }
230 }
231 else
232 {
233 bool ok;
234 uint freq = 0;
235
236 if (mSource == "cpu")
237 {
238 uint count = 0;
239
240 for (Bounds::ConstIterator I = mBounds.constBegin(); I != mBounds.constEnd(); ++I)
241 {
242 uint thisFreq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(I.key()))).toUInt(&ok);
243
244 if (ok)
245 {
246 freq += thisFreq;
247 ++count;
248 }
249 }
250 if (count)
251 {
252 freq /= count;
253 }
254 }
255 else
256 {
257 freq = readAllFile(qPrintable(QString("/sys/devices/system/cpu/%1/cpufreq/scaling_cur_freq").arg(mSource))).toUInt(&ok);
258 }
259 emit update(freq);
260 }
261 }
262
263 QString CpuStatPrivate::defaultSource()
264 {
265 return "cpu";
266 }
267
268 CpuStatPrivate::Values::Values()
269 : user(0)
270 , nice(0)
271 , system(0)
272 , idle(0)
273 , other(0)
274 , total(0)
275 {
276 }
277
278 void CpuStatPrivate::Values::sum()
279 {
280 total = user + nice + system + idle + other;
281 }
282
283 void CpuStatPrivate::Values::clear()
284 {
285 total = user = nice = system = idle = other = 0;
286 }
287
288 CpuStat::Monitoring CpuStatPrivate::monitoring() const
289 {
290 return mMonitoring;
291 }
292
293 void CpuStatPrivate::setMonitoring(CpuStat::Monitoring value)
294 {
295 mPrevious.clear();
296 mMonitoring = value;
297 }
298
299 CpuStat::CpuStat(QObject *parent)
300 : BaseStat(parent)
301 {
302 impl = new CpuStatPrivate(this);
303 baseimpl = impl;
304
305 connect(impl, SIGNAL(update(float,float,float,float,float,uint)), this, SIGNAL(update(float,float,float,float,float,uint)));
306 connect(impl, SIGNAL(update(float,float,float,float)), this, SIGNAL(update(float,float,float,float)));
307 connect(impl, SIGNAL(update(uint)), this, SIGNAL(update(uint)));
308 }
309
310 CpuStat::~CpuStat()
311 {
312 }
313
314 void CpuStat::updateSources()
315 {
316 dynamic_cast<CpuStatPrivate*>(impl)->updateSources();
317 }
318
319 CpuStat::Monitoring CpuStat::monitoring() const
320 {
321 return impl->monitoring();
322 }
323
324 void CpuStat::setMonitoring(CpuStat::Monitoring value)
325 {
326 if (impl->monitoring() != value)
327 {
328 impl->setMonitoring(value);
329 emit monitoringChanged(value);
330 }
331 }
332
333 }