Remoto - VFS: cpuUsage.cpp Source File
Remoto - VFS
cpuUsage.cpp
Go to the documentation of this file.
1 
2 #include <QSysInfo>
3 #include <QProcess>
4 #include <QThread>
5 
6 #include "VFS.h"
7 #include "VFS_icons.h"
8 #include "utilities/rutils.h"
9 
10 #include "cpuUsage.h"
11 
27 , _processors(1)
28 {
29  _kernel = QSysInfo::kernelType().toLower();
30  _processors = QThread::idealThreadCount();
31 
32  //VFS::LOG( "CPU usage kernel: "+_kernel, 0, "cpuusage");
33 
34  if (_kernel == "linux")
35  {
36  _scale = 100.0;
37 
38  //https://stackoverflow.com/questions/890894/portable-way-to-find-out-if-a-command-exists-c-c
39  if (system("which sar > /dev/null 2>&1"))
40  VFS::WARN("For linux, the 'sar' command is used for cpuUsage. This command is missing on this system.", 0, "cpuusage");
41 
42  if (system("which bc > /dev/null 2>&1"))
43  VFS::WARN("For linux, the 'bc' command is used for cpuUsage. This command is missing on this system.", 0, "cpuusage");
44  }
45  else if (_kernel == "darwin")
46  {
47  _scale = _processors * 100.0;
48  }
49  else //for windoze, etc
50  {
51  _scale = 100.0;
52  }
53 }
54 
56 {
57 }
58 
64 {
65  startTimer(1000);
66 }
67 
74 {
75  return false;
76 }
77 
83 QByteArray cpuUsage::icon()
84 {
85  return "data:image/svg+xml;utf8,<svg class=\"menuIcon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><g id=\"vfs\"><path d=\"M3,25.95a1,1,0,0,1-.55-1.84l6.16-4A1,1,0,0,1,9.48,20l5.38,2.11,2.51-9.21A1,1,0,0,1,19,12.47l6.29,6.15L28.68,4.87a1,1,0,1,1,1.94.47L26.85,20.8a1,1,0,0,1-1.67.48l-6.33-6.19L16.5,23.73a1,1,0,0,1-.52.63,1,1,0,0,1-.81,0L9.23,22.07,3.51,25.79A1,1,0,0,1,3,25.95Z\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"18.38\" cy=\"13.71\" r=\"1.66\" transform=\"translate(-4.31 17.01) rotate(-45)\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"29.65\" cy=\"5.11\" r=\"1.66\" transform=\"translate(5.08 22.46) rotate(-45)\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"25.96\" cy=\"20.23\" r=\"1.66\" transform=\"translate(-6.7 24.29) rotate(-45)\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"15.78\" cy=\"23.52\" r=\"1.66\" transform=\"translate(-12.01 18.05) rotate(-45)\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"2.6\" cy=\"24.95\" r=\"1.66\" transform=\"translate(-16.88 9.14) rotate(-45)\"/><circle fill=\"rgb(27,186,217)\" class=\"iconColor\" cx=\"9.09\" cy=\"21.3\" r=\"1.66\" transform=\"translate(-12.4 12.67) rotate(-45)\"/></g></svg>";
86  // return VFS_icons::get("graph");
87 }
88 
90 {
91  QJsonObject a;
92  a["type"] = "openLayout";
93 
94  r->_metadata["icon"] = QString(icon());
95  r->_metadata["action"] = a;
96 
97  QJsonObject l = rutils::jsonResource(":/admin/templates/basicLayout.json");
98  l["icon"] = "@icon@";
99  QJsonObject t;
100  t["openLayout"] = l;
101 
102  r->_metadata["template"] = t;
103  r->_metadata["where"] = "nearest";
104  r->_metadata["type"] = "cpuUsage:cpuGraph.js";
105  r->_success = true;
106 }
107 
109 {
110  QJsonObject c;
111  for (int i=0;i<_cpuSamples.length();i++)
112  c[QString::number(_cpuSamples[i].first)] = _cpuSamples[i].second;
113 
114  QJsonObject m;
115  for (int i=0;i<_ramSamples.length();i++)
116  m[QString::number(_ramSamples[i].first)] = _ramSamples[i].second;
117 
118  QJsonObject q;
119  for (int i=0;i<_reqSamples.length();i++)
120  q[QString::number(_reqSamples[i].first)] = _reqSamples[i].second;
121 
122  QJsonObject d;
123  d["cpu"] = c;
124  d["ram"] = m;
125  d["req"] = q;
126  d["max"] = MAX_SAMPLES;
127 
128  r->_data.setObject(d);
129  r->_success = true;
130  //VFS_application::read(r);
131 }
132 
141 QString cpuUsage::code(QString nodename, QString libname, QString &error)
142 {
143  if (libname == "cpuGraph.js")
144  return rutils::resourceContents(":/admin/html/cpuGraph.js");
145 
146  if (libname == "cpuGraph.css")
147  return rutils::resourceContents(":/admin/html/cpuGraph.css");
148 
149  return VFS_application::code(nodename,libname,error);
150 }
151 
157 void cpuUsage::timerEvent(QTimerEvent *event)
158 {
159  Q_UNUSED(event);
160 
161  //printf( "Timer ID: %d\n", event->timerId() );
162 
163  QDateTime now = QDateTime::currentDateTime();
164  qint64 t = now.toMSecsSinceEpoch();
165 
166  getCPUSample(t);
167  getRAMSample(t);
168  getREQSample(t);
169 
170  QJsonObject _diff;
171  _diff["uptime"] = VFS::uptimeString(false);
172  _diff["cpu"] = _cpu_diff;
173  _diff["ram"] = _ram_diff;
174  _diff["req"] = _req_diff;
175 
176  VFS_request *d = createRequest( VFS_request::diff, "", "server", QJsonDocument(_diff) );
177  emit diff(this,d);
178  delete d;
179 
180  _cpu_diff = QJsonObject();
181  _ram_diff = QJsonObject();
182  _req_diff = QJsonObject();
183 }
184 
192 void cpuUsage::getCPUSample(quint64 t)
193 {
194  double p = runCommand("cpuusage").toDouble() / _scale;
195 
196  _cpuSamples << QPair<quint64,double> (t,p);
197 
198  _cpu_diff[QString::number(t)] = p;
199 
200  while ( _cpuSamples.length() > MAX_SAMPLES )
201  _cpuSamples.removeFirst();
202 }
203 
211 void cpuUsage::getRAMSample(quint64 t)
212 {
213  double p = runCommand("ramusage").toDouble();
214  //printf("ram: %f\n",p);
215 
216  _ramSamples << QPair<quint64,double> (t,p);
217 
218  _ram_diff[QString::number(t)] = p;
219 
220  while ( _ramSamples.length() > MAX_SAMPLES )
221  _ramSamples.removeFirst();
222 }
223 
224 
232 void cpuUsage::getREQSample(quint64 t)
233 {
234  quint32 s = VFS_request::getSample();
235  quint32 r = VFS_request::_refcount;
236 
237  _reqSamples << QPair<quint64,quint32> (t,s);
238 
239  _req_diff[QString::number(t)] = (qint64) (s+r);
240  //_req_diff[QString::number(t)] = (qint64) (r);
241 
242  while ( _reqSamples.length() > MAX_SAMPLES )
243  _reqSamples.removeFirst();
244 
245  //fixme: this should always report 0 for r
246  //printf("ref: %d %ld\n",s,r);
247 
248  //if (r > 20)
249  // VFS::WARN( QString("The outstanding VFS_request::_refcount is more than 20 (%1). Suspect a memory leak somehwere in a plugin due to VFS_requests failing to delete.\nUse valgrind to debug this, as well as set the #define DEBUG_DANGLING_REQUESTS in VFS_node.h to true. Ideally this value would be 0.").arg(r) );
250 }
251 
252 
261 QString cpuUsage::runCommand(QString which)
262 {
263  // https://stackoverflow.com/questions/30855440/how-to-get-cpu-utilization-in-in-terminal-mac
264  // `ps -A -o %cpu | awk '{s+=$1} END {print s "%"}'`
265 
266  // https://stackoverflow.com/questions/6481005/how-to-obtain-the-number-of-cpus-cores-in-linux-from-the-command-line
267  // `grep -c ^processor /proc/cpuinfo`
268 
269  // https://stackoverflow.com/questions/8122277/getting-memory-information-with-qt
270  // https://www.cyberciti.biz/faq/unix-command-to-find-cpu-utilization/
271 
272  QString command = "";
273 
274  if (_kernel == "linux")
275  {
276  //if (which == "cpucount")
277  // command = "grep -c ^processor /proc/cpuinfo";
278 
279  if (which == "cpuusage")
280  //command = "ps -A -o %cpu | awk '{s+=$1} END {print s \"\"}'";
281  command = "sar -u 1 1 | grep verage | perl -pe \"s/\\s+/+/g\" | cut -f 3,5 -d '+' | bc -l";
282  //command = "ps -A -o %cpu | awk '{s+=$1} END {print s}'";
283 
284 
285  if (which == "ramusage")
286  // free -b | grep Mem | perl -pe "s/\s+/ /g" | cut -f 2,4 -d " " | perl -pe "s/\s+/ \/ /" | perl -pe "s/(.*)/scale=20; 1-1\/(\1)/" | bc
287  command = "free -b | grep Mem | perl -pe \"s/\\s+/ /g\" | cut -f 2,4 -d \" \" | perl -pe \"s/\\s+/ \\/ /\" | perl -pe \"s/(.*)/scale=20; 1-1\\/(\\1)/\" | bc -l";
288  }
289 
290  if (_kernel == "darwin")
291  {
292  if (which == "cpuusage")
293  //command = "ps -A -o %cpu | awk '{s+=$1} END {print s \"\"}'";
294  command = "ps -A -o %cpu | awk '{s+=$1} END {print s}'";
295  //command = "bash -c 'ps -A -o %cpu'";
296 
297  if (which == "ramusage")
298  { // sysctl hw.physmem hw.memsize | cut -f 2 -d " " | perl -pe "s/\n/ /" | perl -pe "s/(\d+)[^\d]*(\d+)/\1 \/ \2\n/" | bc -l
299  //command = "sysctl hw.physmem hw.memsize | cut -f 2 -d \" \" | perl -pe \"s/\\n/ /\" | perl -pe \"s/(\\d+)[^\\d]*(\\d+)/scale=20; \\1 \\/ \\2\\n/\" | bc -l";
300  //command = "memory_pressure | grep percentage | perl -pe \"s/([^\\d]*)(\\d+)([^\\d]*)/scale=20; \2 \\/ 100\\n/\" | bc -l";
301 
302  // memory_pressure | grep percentage | perl -pe "s/([^\d]*)(\d+)([^\d]*)/scale=20; \2 \/ 100.0\n/" | bc -l
303  command = "memory_pressure | grep percentage | perl -pe \"s/([^\\d]*)(\\d+)([^\\d]*)/scale=20; \\2 \\/ 100\\n/\" | bc -l";
304  }
305  }
306 
307  if (!command.isEmpty())
308  {
309  QProcess process;
310  process.start("bash", QStringList() << "-c" << command);
311  process.waitForFinished(5000); // will wait for 5 seconds to finish
312 
313  QString stdout = process.readAllStandardOutput();
314  QString stderr = process.readAllStandardError();
315 
316  //printf("stdout: %s\n",qUtf8Printable(stdout));
317  //printf("stderr: %s\n",qUtf8Printable(stderr));
318 
319  return stdout;
320  }
321 
322  VFS::ERROR( QString("Unsupported OS or selector: %1 %2. Can't return command.").arg(_kernel).arg(which) );
323 
324  return "";
325 }
A common base class for all things that want to act like an application or include ACL support.
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
Definition: VFS_node.cpp:1913
static QString code(QString nodename, QString libname, QString &error)
Fetch code or any other resource from a node.
Definition: VFS_node.cpp:1038
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
The base class for all requests between nodes.
Definition: VFS_node.h:54
@ diff
send a diff (7)
Definition: VFS_node.h:71
static quint32 getSample()
Get the current sample count and clear the counter.
Definition: VFS_node.cpp:235
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
static long _refcount
A reference counter for VFS_request instances, used for debugging to ensure all instances are properl...
Definition: VFS_node.h:122
QJsonObject _metadata
the request payload
Definition: VFS_node.h:101
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static QString uptimeString(bool ms=true)
Get the uptime of this VFS instance as a string.
Definition: VFS.cpp:443
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
QJsonObject _cpu_diff
The CPU diff.
Definition: cpuUsage.h:50
int _processors
The number of processors available on this machine.
Definition: cpuUsage.h:43
virtual QByteArray icon()
Return the icon.
Definition: cpuUsage.cpp:83
void getRAMSample(quint64 t)
Get a RAM sample.
Definition: cpuUsage.cpp:211
QList< QPair< quint64, double > > _ramSamples
The RAM samples recorded.
Definition: cpuUsage.h:47
virtual void metadata(VFS_request *r)
Fetch the metadata of this node.
Definition: cpuUsage.cpp:89
virtual void initialize()
Start the sample timer.
Definition: cpuUsage.cpp:63
QJsonObject _req_diff
The VFS_request sample diff.
Definition: cpuUsage.h:52
float _scale
The amount to scale processor values.
Definition: cpuUsage.h:44
virtual ~cpuUsage()
Definition: cpuUsage.cpp:55
void getREQSample(quint64 t)
Take a sample of the current VFS_request::_refcount.
Definition: cpuUsage.cpp:232
static QString code(QString nodename, QString libname, QString &error)
Fetch code from this node.
Definition: cpuUsage.cpp:141
QString runCommand(QString which)
Execute a shell command to gather sample data.
Definition: cpuUsage.cpp:261
QList< QPair< quint64, quint16 > > _reqSamples
The VFS_request count sample recorded.
Definition: cpuUsage.h:48
bool isContainer()
This is always false.
Definition: cpuUsage.cpp:73
virtual void read(VFS_request *r)
Return the data contents of this node, or if it's a container call ls()
Definition: cpuUsage.cpp:108
Q_INVOKABLE cpuUsage()
The cpuUsage constructor.
Definition: cpuUsage.cpp:25
QString _kernel
The kernel string reported by Qt.
Definition: cpuUsage.h:42
QList< QPair< quint64, double > > _cpuSamples
The CPU samples recorded.
Definition: cpuUsage.h:46
void timerEvent(QTimerEvent *event)
Gather the CPU and RAM samples.
Definition: cpuUsage.cpp:157
void getCPUSample(quint64 t)
Get a CPU sample.
Definition: cpuUsage.cpp:192
QJsonObject _ram_diff
The RAM diff.
Definition: cpuUsage.h:51
setter error
Set the error value of this widget.
#define MAX_SAMPLES
Definition: cpuUsage.h:10
QJsonObject jsonResource(QString resource, bool *ok=nullptr)
Fetch the contents of a Qt resource as a QJsonObject.
Definition: rutils.cpp:90
QByteArray resourceContents(QString resource, bool *ok=nullptr, bool squashHash=false)
Fetch the contents of a Qt resource file.
Definition: rutils.cpp:53