1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "tools.h"
6
7#include <QDebug>
8#include <QDir>
9#include <QMetaEnum>
10#include <QRegularExpression>
11#include <QCoreApplication>
12#include <QStandardPaths>
13#include <QTextBrowser>
14#include <QApplication>
15
16#include <iostream>
17#include <vector>
18
19namespace {
20// perf top tool
21static std::string overhead{"overhead"};
22static std::string sample{"sample"};
23static std::string period{"period"};
24static std::string dso{"dso"};
25static std::string symbol{"symbol"};
26static std::string srcline{"srcline"};
27
28/* top tool command out VIRT field :
29 * VIRT stands for the virtual size of a process,
30 * which is the sum of memory it is actually using,
31 * memory it has mapped into itself (for instance the video card’s RAM for the X server),
32 * files on disk that have been mapped into it (most notably shared libraries),
33 * and memory shared with other processes.
34 * VIRT represents how much memory the program is able to access at the present moment.
35 */
36
37/* top tool command out RES field :
38 * RES stands for the resident size,
39 * which is an accurate representation of how much actual physical memory a process is consuming.
40 * (This also corresponds directly to the %MEM column.) This will virtually always be less than the VIRT size,
41 * since most programs depend on the C library.
42 */
43
44/* top tool command out SHR field :
45 * SHR indicates how much of the VIRT size is actually sharable (memory or libraries).
46 * In the case of libraries, it does not necessarily mean that the entire library is resident.
47 * For example, if a program only uses a few functions in a library,
48 * the whole library is mapped and will be counted in VIRT and SHR,
49 * but only the parts of the library file containing the functions being used will actually be loaded in and be counted under RES.
50 */
51static std::string processId{"processId"};
52static std::string user{"user"};
53static std::string priority{"priority"};
54static std::string nice{"nice"};
55static std::string virtualSize{"virtualSize"};
56static std::string residentSize{"residentSize"};
57static std::string sharable{"sharable"};
58static std::string status{"status"};
59static std::string cpuOverhead{"cpuOverhead"};
60static std::string memoryOverhead{"memoryOverhead"};
61static std::string timeCount{"timeCount"};
62static std::string command{"command"};
63
64static std::string fd{"fd"};
65static std::string type{"type"};
66static std::string device{"device"};
67static std::string sizeOff{"sizeOff"};
68static std::string node{"node"};
69static std::string name{"name"};
70
71static std::string netId{"netId"};
72static std::string state{"state"};
73static std::string recvQueue{"recvQueue"};
74static std::string sendQueue{"sendQueue"};
75static std::string localAddrPort{"localAddrPort"};
76static std::string peerAddrPort{"peerAddressPort"};
77static std::string usingProcesses{"usingProcesses"};
78
79// device IO field
80static std::string userId{"userId"};
81//static std::string processId{"processId"};
82static std::string kB_rd{"kB_rd"};
83static std::string kB_wd{"kB_wd"};
84static std::string kB_ccwr{"kB_ccwr"};
85static std::string iodelay{"iodelay"};
86//static std::string command{"command"};
87
88
89static std::string feild{"feild"};
90static std::string key{"key"};
91static std::string lines{"lines"};
92
93static std::string package_name{"package_name"};
94static std::string system_default{"system default"};
95
96template<class E>
97const char * enumName()
98{
99 return QMetaEnum::fromType<E>().name();
100}
101
102template<class Elem>
103const char * enumElemName(Elem type)
104{
105 return QMetaEnum::fromType<Elem>().valueToKey(type);
106}
107
108struct QStr : QString
109{
110 QStr(const std::string &src) : QString(QString::fromStdString(src)){}
111};
112
113struct Add : QString, std::string
114{
115 Add(const QString &x, const QString &y)
116 : QString(QString::number(x.toULongLong() + y.toULongLong()))
117 , std::string(QString::toStdString())
118 {
119
120 }
121
122 Add(const std::string &x, const std::string &y)
123 : QString(QString::number(QStr(x).toLongLong() + QStr(y).toLongLong()))
124 , std::string(QString::toStdString())
125 {
126
127 }
128};
129
130QString format(const QString &src)
131{
132 QString temp = src;
133 temp.replace(QRegExp("[\\s]+"), " ");
134 while (temp.startsWith(" ")) {
135 temp.remove(0 , sizeof(" "));
136 }
137 while (temp.endsWith(" ")) {
138 temp.remove(temp.size() - 1, sizeof(" "));
139 }
140 return temp;
141};
142
143struct Template
144{
145 static QString scriptsPath()
146 {
147 QString cacheChildPath = QCoreApplication::applicationName();
148 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
149 dir.cdUp();
150 if (!dir.cd(cacheChildPath)) {
151 dir.mkdir(cacheChildPath);
152 dir.cd(cacheChildPath);
153 }
154
155 qInfo() << dir.path();
156 QString scriptChildPath = "scripts";
157 if (!dir.cd(scriptChildPath)) {
158 dir.mkdir(scriptChildPath);
159 dir.cd(scriptChildPath);
160 }
161 qInfo() << dir.path();
162 return dir.path();
163 }
164
165 static QString scriptsPathFrom(Tools::PerformanceType pt)
166 {
167 return scriptsPath() + QDir::separator() + enumElemName<Tools::PerformanceType>(pt) + "Lanuch.sh";
168 }
169
170 static void checkSocketScript()
171 {
172 QString filePath = scriptsPathFrom(Tools::PerformanceType::socket);
173 QFile file(filePath);
174 QByteArray readData;
175 if (file.open(QFile::ReadOnly | QFile::Text)) {
176 readData = file.readAll();
177 file.close();
178 } else {
179 qCritical() << "can't open socket script" << filePath;
180 }
181
182 if (readData.isEmpty() || socketScriptCode() != readData) {
183 QFile file(filePath);
184 if (file.open(QFile::Truncate | QFile::WriteOnly | QFile::Text)) {
185 file.write(socketScriptCode());
186 file.setPermissions(QFile::Permission::ExeUser |
187 QFile::Permission::ReadUser|
188 QFile::Permission::WriteUser);
189 file.close();
190 }
191 }
192 }
193
194 static QByteArray socketScriptCode(){
195#ifdef __linux__
196 return "#!/bin/bash\n"
197 "if [ -z $1 ]; then\n"
198 " echo \"can't attach pid null\">&2\n"
199 " exit -1\n"
200 "fi\n"
201 "\n"
202 "if [ -z $2 ]; then\n"
203 " echo \"can't parent pid null\">&2\n"
204 " exit -2\n"
205 "fi\n"
206 "\n"
207 "while [ true ];\n"
208 "do\n"
209 " if ps -p $1 >/dev/null; then\n"
210 " if ps -p $2 >/dev/null; then\n"
211 " ss -p|grep pid=$1;\n"
212 " else\n"
213 " echo \"can't found parent pid:'$2', add watch from parent\">&2\n"
214 " fi\n"
215 " else\n"
216 " echo \"can't found attach pid:'$1', process state no running\">&2\n"
217 " exit -1\n"
218 " fi\n"
219 " sleep 1;\n"
220 "done\n";
221#else
222#endif
223 }
224};
225
226struct JsonOp
227{
228
229 static void setField(const std::vector<std::string> &feild, Json::Value &jsonObj)
230 {
231 Json::Value feildArray(Json::arrayValue);
232 for (auto && val : feild) {
233 feildArray.append(Json::Value(val));
234 }
235 jsonObj[::feild] = feildArray;
236 }
237
238
239 static void setKey(const std::string &key, Json::Value &jsonObj)
240 {
241 jsonObj[::key] = key;
242 }
243};
244
245}
246
247class ToolsPrivate
248{
249 friend class Tools;
250 QHash<Tools::PerformanceType, QProcess*> toolsIns{};
251 QHash<Tools::PerformanceType, Json::Value> toolsCountCache{};
252 int attachPID{0};
253 QTextBrowser *textBrowser{nullptr};
254
255 ~ToolsPrivate()
256 {
257 qDeleteAll(toolsIns);
258 toolsCountCache.clear();
259 }
260
261 QTextBrowser *displayErrorWidget()
262 {
263 if (!textBrowser) {
264 textBrowser = new QTextBrowser();
265 textBrowser->setWindowFlag(Qt::Dialog);
266 textBrowser->setWindowTitle(QCoreApplication::applicationName() + " warning");
267 textBrowser->setMinimumSize(600,300);
268 textBrowser->hide();
269 qApp->setQuitOnLastWindowClosed(false);
270 }
271 return textBrowser;
272 }
273
274 void countFunction(const Json::Value &functionObj)
275 {
276 // no cache
277 if (!toolsCountCache.contains(Tools::PerformanceType::function)) {
278 toolsCountCache[Tools::PerformanceType::function] = functionObj;
279 } else { // has cache
280 // find array with cache
281 Json::Value cache = toolsCountCache[Tools::PerformanceType::function];
282 Json::Value cacheFeild = cache[::feild];
283 Json::Value cacheLines = cache[::lines];
284 Json::Value paramFeild = functionObj[::feild];
285 Json::Value paramLines = functionObj[::lines];
286 if (paramFeild.size() != cacheFeild.size()
287 && !cacheFeild.empty()) {
288 qCritical() << __FUNCTION__ << "error feild not's same: "
289 << "cache: " << QString::fromStdString(cacheFeild.toStyledString())
290 << "current: " << QString::fromStdString(paramFeild.toStyledString());
291
292 return;
293 }
294
295 int paramOverheadIdx = -1;
296 int paramSampleIdx = -1;
297 int paramPeriodIdx = -1;
298 int paramDsoIdx = -1;
299 int paramSymbolIdx = -1;
300 int paramSrclineIdx = -1;
301 int cacheOverheadIdx = -1;
302 int cacheSampleIdx = -1;
303 int cachePeriodIdx = -1;
304 int cacheDsoIdx = -1;
305 int cacheSymbolIdx = -1;
306 int cacheSrclineIdx = -1;
307
308 for (unsigned int idx = 0; idx < qMin(cacheFeild.size(), paramFeild.size()); idx ++) {
309 if (cacheFeild[idx] == ::overhead) {
310 cacheOverheadIdx = static_cast<int>(idx);
311 }
312 if (cacheFeild[idx] == ::sample) {
313 cacheSampleIdx = static_cast<int>(idx);
314 }
315 if (cacheFeild[idx] == ::period) {
316 cachePeriodIdx = static_cast<int>(idx);
317 }
318 if (cacheFeild[idx] == ::dso) {
319 cacheDsoIdx = static_cast<int>(idx);
320 }
321 if (cacheFeild[idx] == ::symbol) {
322 cacheSymbolIdx = static_cast<int>(idx);
323 }
324 if (cacheFeild[idx] == ::srcline) {
325 cacheSrclineIdx = static_cast<int>(idx);
326 }
327 if (paramFeild[idx] == ::overhead) {
328 paramOverheadIdx = static_cast<int>(idx);
329 }
330 if (paramFeild[idx] == ::sample) {
331 paramSampleIdx = static_cast<int>(idx);
332 }
333 if (paramFeild[idx] == ::period) {
334 paramPeriodIdx = static_cast<int>(idx);
335 }
336 if (paramFeild[idx] == ::dso) {
337 paramDsoIdx = static_cast<int>(idx);
338 }
339 if (paramFeild[idx] == ::symbol) {
340 paramSymbolIdx = static_cast<int>(idx);
341 }
342 if (paramFeild[idx] == ::srcline) {
343 paramSrclineIdx = static_cast<int>(idx);
344 }
345 }
346
347 for (unsigned int paramRow = 0; paramRow < paramLines.size(); paramRow ++) {
348 auto paramLine = paramLines[paramRow];
349 bool hasSame = false;
350 for (unsigned int cacheRow = 0; cacheRow < cacheLines.size(); cacheRow ++) {
351 auto cacheLine = cacheLines[cacheRow];
352 if (paramSymbolIdx > 0 && cacheSymbolIdx > 0) {
353 if (paramLine[paramSymbolIdx].asString() == cacheLine[cacheSymbolIdx].asString()) {
354 if (cacheOverheadIdx > 0 && paramOverheadIdx > 0)
355 cacheLine[cacheOverheadIdx] = paramLine[paramOverheadIdx].asString();
356 if (cacheSampleIdx > 0 && cacheSampleIdx > 0)
357 cacheLine[cacheSampleIdx] = Add(cacheLine[cacheSampleIdx].asString(), paramLine[paramSampleIdx].asString());
358 if (cachePeriodIdx > 0 && paramPeriodIdx > 0)
359 cacheLine[cachePeriodIdx] = Add(cacheLine[cachePeriodIdx].asString(), paramLine[paramPeriodIdx].asString());
360 if (cacheDsoIdx > 0 && paramDsoIdx > 0)
361 cacheLine[cacheDsoIdx] = paramLine[paramDsoIdx].asString();
362 if (cacheSymbolIdx > 0 && paramSymbolIdx > 0)
363 cacheLine[cacheSymbolIdx] = paramLine[paramSymbolIdx].asString();
364 if (cacheSrclineIdx > 0 && paramSrclineIdx > 0)
365 cacheLine[cacheSrclineIdx] = paramLine[paramSrclineIdx].asString();
366 cacheLines[cacheRow] = cacheLine;
367 hasSame = true;
368 break;
369 }
370 }
371 }
372 if (!hasSame) {
373 cacheLines.append(paramLine);
374 }
375 }
376
377 // rewrite overhead
378 long long countPeriod = 0;
379 for(unsigned int idx = 0; idx < cacheLines.size(); idx ++) {
380 Json::Value cacheLine = cacheLines[idx];
381 if (!cacheLine.empty()) {
382 countPeriod += QStr(cacheLine[cachePeriodIdx].asString()).toLongLong();
383 }
384 }
385 if (countPeriod > 0) {
386 for (unsigned int i = 0; i < cacheLines.size(); i++) {
387 Json::Value cacheLine = cacheLines[i];
388 if (!cacheLine.empty()) {
389 long long funcOnePeriod = QStr(cacheLine[cachePeriodIdx].asString()).toLongLong();
390 double onePeriod = double(funcOnePeriod)/ double(countPeriod);
391 // qInfo() << "funcOnePeriod" << funcOnePeriod << "countPeriod" << countPeriod;
392 cacheLine[cacheOverheadIdx] = QString(QString::number(onePeriod * 100, 10, 2)+ "%").toStdString();
393 // qInfo() << QStr(funcOne[::overhead].asString());
394 // qInfo() << "\n";
395 }
396 cacheLines[i] = cacheLine;
397 }
398 }
399 cache[::lines] = cacheLines;
400 toolsCountCache[Tools::PerformanceType::function] = cache;
401 }
402 }
403};
404
405Tools::Tools()
406 : d(new ToolsPrivate)
407{
408
409}
410
411Tools::~Tools()
412{
413 stopAll();
414 if (d) {
415 if (d->textBrowser) {
416 delete d->textBrowser;
417 }
418 delete d;
419 }
420}
421
422void Tools::startAll()
423{
424 for (auto ins : d->toolsIns) {
425 ins->start();
426 qInfo() << "start" << ins->program() << ins->arguments()
427 << "pid :" << ins->pid();
428 }
429}
430
431void Tools::stopAll()
432{
433 std::cout << __FUNCTION__ << std::endl;
434 for (auto ins : d->toolsIns) {
435 ins->kill();
436 ins->waitForFinished();
437 }
438 d->toolsCountCache.clear();
439}
440
441void Tools::setAttachPID(int pid)
442{
443 std::cout << __FUNCTION__ << ": " <<pid << std::endl;
444 d->attachPID = pid;
445
446 if (d->displayErrorWidget()) {
447 d->displayErrorWidget()->clear();
448 }
449
450 initTool(function);
451 initTool(global);
452 initTool(vfs);
453 initTool(socket);
454 initTool(deviceIO);
455}
456
457int Tools::attachPID() const
458{
459 return d->attachPID;
460}
461
462Json::Value Tools::data() const
463{
464 Json::Value result;
465 for (auto key : d->toolsCountCache.keys()) {
466 auto value = d->toolsCountCache.value(key);
467 if (!value.empty()) {
468 result[enumElemName(key)] = value;
469 }
470 }
471 return result;
472}
473
474bool Tools::initTool(Tools::PerformanceType pt)
475{
476 std::cout << __FUNCTION__ << " : "
477 << enumElemName<PerformanceType>(pt)
478 << std::endl;
479
480 if (d->attachPID <= 0) {
481 qCritical() << "unknown process from attach PID" << d->attachPID;
482 }
483
484 QProcess* process = new QProcess;
485 process->setEnvironment({"LINES=1000", "COLUMNS=1000"});
486 if (PerformanceType::function == pt) {
487 process->setProperty(package_name.c_str(), "perf-tools-unstable");
488 process->setProperty(::enumName<PerformanceType>(), function);
489 process->setProgram("/usr/bin/perf");
490 process->setArguments({"top",
491 "--delay", "1",
492 "--fields","overhead,sample,period,dso,symbol,srcline",
493 "--hide_kernel_symbols",
494 "--verbose",
495 "--stdio",
496 "-p", QString::number(d->attachPID)});
497 QDir tempDir(QDir::tempPath());
498 if (!tempDir.exists("perf_top_redirect_stdout")) {
499 qInfo() << "create redirect output from perf top";
500 QFile file(tempDir.filePath("perf_top_redirect_stdout"));
501 file.open(QFile::OpenModeFlag::NewOnly);
502 file.close();
503 }
504 process->setStandardInputFile(tempDir.filePath("perf_top_redirect_stdout"));
505 }
506 if (PerformanceType::global == pt) {
507 process->setProperty(package_name.c_str(), system_default.c_str());
508 process->setProperty(::enumName<PerformanceType>(), global);
509 process->setProgram("/usr/bin/top");
510 process->setArguments({"-b",
511 "-d","1",
512 "-p", QString::number(d->attachPID)});
513 }
514
515 if (PerformanceType::vfs == pt) {
516 process->setProperty(package_name.c_str(), system_default.c_str());
517 process->setProperty(::enumName<PerformanceType>(), vfs);
518 process->setProgram("/usr/bin/lsof");
519 process->setArguments({"-r", "1",
520 "-p", QString::number(d->attachPID)});
521 }
522
523 if (PerformanceType::socket == pt) {
524 Template::checkSocketScript();
525 process->setProperty(package_name.c_str(), system_default.c_str());
526 process->setProperty(::enumName<PerformanceType>(), socket);
527 process->setProgram(Template::scriptsPathFrom(socket));
528 process->setArguments({QString::number(d->attachPID), QString::number(QCoreApplication::applicationPid())});
529 }
530
531 if (PerformanceType::deviceIO == pt) {
532 process->setProperty(package_name.c_str(), "sysstat");
533 process->setProperty(::enumName<PerformanceType>(), deviceIO);
534 process->setProgram("/usr/bin/pidstat");
535 process->setArguments({"-d", "1",
536 "-p", QString::number(d->attachPID)});
537 }
538
539 QObject::connect(process, &QProcess::readyReadStandardOutput,
540 this, &Tools::readAllStandardOutput,
541 Qt::UniqueConnection);
542
543 QObject::connect(process, &QProcess::readyReadStandardError,
544 this, &Tools::readAllStandardError,
545 Qt::UniqueConnection);
546
547 QObject::connect(process, &QProcess::errorOccurred,
548 this, &Tools::errorOccurred,
549 Qt::UniqueConnection);
550
551 d->toolsIns.insert(pt, process);
552
553 if (!process->program().isEmpty()) {
554 return true;
555 }
556
557 return false;
558}
559
560QRegularExpression Tools::lineRegExpRule(Tools::PerformanceType pt)
561{
562 if (PerformanceType::global == pt)
563 return QRegularExpression(
564 QString("\\s?") +
565 "(?<"+ ::QStr(::processId) + ">[0-9]+)\\s+" +
566 "(?<"+ QStr(::user) + ">\\S+)\\s+" +
567 "(?<"+ QStr(::priority) + ">[0-9][0-9])\\s+" +
568 "(?<"+ QStr(::nice) + ">-?[0-9]+)\\s+" +
569 "(?<"+ QStr(::virtualSize) + ">[0-9]+\\.?[0-9]?+\\S?)\\s+" +
570 "(?<"+ QStr(::residentSize) + ">[0-9]+\\.?[0-9]?+\\S?)\\s+" +
571 "(?<"+ QStr(::sharable) + ">[0-9]+\\.?[0-9]?+\\S?)\\s+" +
572 "(?<"+ QStr(::status) + ">[A-Za-z])\\s+" +
573 "(?<"+ QStr(::cpuOverhead) + ">[0-9]{1,2}.[0-9]+)\\s+" +
574 "(?<"+ QStr(::memoryOverhead) + ">[0-9]{1,2}.[0-9]+)\\s+" +
575 "(?<"+ QStr(::timeCount) + ">[0-9]+:[0-9]+.[0-9]+)\\s+" +
576 "(?<"+ QStr(::command) + ">\\S+)");
577 if (PerformanceType::function == pt)
578 return QRegularExpression(
579 QString("\\s+") +
580 "(?<" + QStr(::overhead) + ">[0-9]+.[0-9]+%)\\s+" +
581 "(?<" + QStr(::sample) + ">[0-9]+)\\s+" +
582 "(?<" + QStr(::period) + ">[0-9]+)\\s+" +
583 "(?<" + QStr(::dso) + ">[\\S]+)\\s+" +
584 "(?<" + QStr(::symbol) + ">\\S+\\s+\\w\\s+\\S+\\s[_:\\w<>(),&@*\\s]+)\\s+" +
585 "(?<" + QStr(::srcline) + ">\\S+)"
586 );
587 if (PerformanceType::vfs == pt)
588 return QRegularExpression(
589 QString("\\s?") +
590 "(?<" + QStr(::command) + ">\\S+)\\s+" +
591 "(?<" + QStr(::processId) + ">[0-9]+)\\s+" +
592 "(?<" + QStr(::user) + ">\\S+)\\s+" +
593 "(?<" + QStr(::fd) + ">[a-z0-9]+)\\s+" +
594 "(?<" + QStr(::type) + ">[a-zA-z_]+)\\s+" +
595 "(?<" + QStr(::device) + ">[0-9]+\\,[0-9]+)\\s+" +
596 "(?<" + QStr(::sizeOff) + ">[0-9a-zA-Z]+)\\s+" +
597 "(?<" + QStr(::node) + ">[0-9]+)\\s+" +
598 "(?<" + QStr(::name) + ">\\S+)\\s?"
599 );
600 if (PerformanceType::socket == pt)
601 return QRegularExpression(
602 QString("\\s?") +
603 "(?<" + QStr(::netId) + ">\\S+)\\s+" +
604 "(?<" + QStr(::state) + ">\\S+)\\s+" +
605 "(?<" + QStr(::recvQueue) + ">[0-9]+)\\s+" +
606 "(?<" + QStr(::sendQueue) + ">[0-9]+)\\s+" +
607 "(?<" + QStr(::localAddrPort) + ">[\\S]+\\s?:?[\\S]+)\\s+" +
608 "(?<" + QStr(::peerAddrPort) + ">[\\S]+\\s?:?[\\S]+)\\s+" +
609 "(?<" + QStr(::usingProcesses) + ">users:\\(\\S+\\))?"
610 );
611
612 if (PerformanceType::deviceIO == pt)
613 return QRegularExpression(
614 QString("\\s?") +
615 "(?<" + QStr(::userId) + ">[0-9]+)\\s+" +
616 "(?<" + QStr(::processId) + ">[0-9]+)\\s+" +
617 "(?<" + QStr(::kB_rd) + ">[0-9]+.[0-9]+)\\s+" +
618 "(?<" + QStr(::kB_wd) + ">[0-9]+.[0-9]+)\\s+" +
619 "(?<" + QStr(::kB_ccwr) + ">[0-9]+.[0-9]+)\\s+" +
620 "(?<" + QStr(::iodelay) + ">[0-9]+)\\s+" +
621 "(?<" + QStr(::command) + ">\\S+)\\s?"
622 );
623
624 return QRegularExpression();
625}
626
627void Tools::readAllStandardOutput()
628{
629 QProcess *process = qobject_cast<QProcess*>(sender());
630 if (process) {
631 QVariant variant = process->property(::enumName<PerformanceType>());
632 if (!variant.isValid()) {
633 return;
634 }
635 if (variant.canConvert<PerformanceType>()) {
636 PerformanceType type = qvariant_cast<PerformanceType>(variant);
637 QString typeString = enumElemName<PerformanceType>(type);
638 QRegularExpression regExp = lineRegExpRule(type);
639
640 Json::Value globalObj(Json::objectValue);
641 JsonOp::setField({::processId, ::user, ::priority, ::nice,
642 ::virtualSize, ::residentSize, ::sharable, status,
643 ::cpuOverhead, ::memoryOverhead, ::timeCount, ::command}
644 , globalObj);
645
646 Json::Value functionObj(Json::objectValue);
647 JsonOp::setField({::overhead, ::sample, ::period,
648 ::dso, ::symbol, srcline}
649 , functionObj);
650 Json::Value functionLines = functionObj[::lines];
651
652 Json::Value vfsObj(Json::objectValue);
653 JsonOp::setField({::command, ::processId, ::user, ::fd,
654 ::type, ::device, ::sizeOff, ::node, ::name}
655 , vfsObj);
656
657 Json::Value socketObj(Json::objectValue);
658 JsonOp::setField({::netId, ::state, ::recvQueue, ::sendQueue,
659 ::localAddrPort, ::peerAddrPort, usingProcesses}
660 , socketObj);
661
662 Json::Value deviceIOObj(Json::objectValue);
663 JsonOp::setField({::userId, ::processId, ::kB_rd+"/s", ::kB_wd + "/s",
664 ::kB_ccwr + "/s", ::iodelay, ::command}
665 , deviceIOObj);
666
667 while (process->canReadLine()) {
668 auto lineData = process->readLine();
669 if (!lineData.isEmpty()) {
670 auto matchs = regExp.match(lineData);
671 if (matchs.hasMatch()) {
672
673 if (function == type) {
674
675 Json::Value functionFeild = functionObj[::feild];
676 if (!functionFeild.empty() && functionFeild.isArray()) {
677
678 auto overheadData = ::format(matchs.captured(QStr(::overhead)));
679 auto sampleData = ::format(matchs.captured(QStr(::sample)));
680 auto periodData = ::format(matchs.captured(QStr(::period)));
681 auto dsoData = ::format(matchs.captured(QStr(::dso)));
682 auto symbolData = ::format(matchs.captured(QStr(::symbol)));
683 auto srclineData = ::format(matchs.captured(QStr(::srcline)));
684 if (!overheadData.isEmpty() && !sampleData.isEmpty()
685 && !periodData.isEmpty() && !dsoData.isEmpty()
686 && !symbolData.isEmpty() && !srclineData.isEmpty()) {
687
688 Json::Value functionLine(Json::arrayValue);
689 functionLine.resize(functionFeild.size());
690 for (unsigned int feildIdx = 0; feildIdx < functionFeild.size(); feildIdx ++) {
691 auto feild = functionFeild[feildIdx].asString();
692 if (feild == ::overhead) {
693 functionLine[feildIdx] = overheadData.toStdString();
694 } else if (feild == ::sample) {
695 functionLine[feildIdx] = sampleData.toStdString();
696 } else if (feild == ::period) {
697 functionLine[feildIdx] = periodData.toStdString();
698 } else if (feild == ::dso) {
699 functionLine[feildIdx] = dsoData.toStdString();
700 } else if (feild == ::symbol) {
701 functionLine[feildIdx] = symbolData.toStdString();
702 } else if (feild == ::srcline) {
703 functionLine[feildIdx] = srclineData.toStdString();
704 } else {
705 qCritical() << "unknown feild from performance type : function";
706 }
707 }
708
709 int symbolIdx = -1;
710 int sampleIdx = -1;
711 int periodIdx = -1;
712 for (unsigned int idx = 0; idx < functionFeild.size(); idx++) {
713 if (functionFeild[idx].asString() == ::symbol) {
714 symbolIdx = static_cast<int>(idx);
715 } else if (functionFeild[idx].asString() == ::sample) {
716 sampleIdx = static_cast<int>(idx);
717 } else if (functionFeild[idx].asString() == ::period) {
718 periodIdx = static_cast<int>(idx);
719 } else {
720 // noting to do
721 }
722 }
723
724
725 bool hasSame = false;
726 if (symbolIdx >= 0) {
727 for (unsigned int lineIdx = 0; lineIdx < functionLines.size(); lineIdx ++) {
728 auto beforLine = functionLines[lineIdx];
729 if (!beforLine.empty() && beforLine[symbolIdx].asString() == symbolData.toStdString()) {
730 functionLine[sampleIdx] = Add(functionLine[sampleIdx].asString(), beforLine[sampleIdx].asString());
731 functionLine[periodIdx] = Add(functionLine[periodIdx].asString(), beforLine[periodIdx].asString());
732 functionLines[lineIdx] = functionLine;
733 hasSame = true;
734 break;
735 }
736 }
737 }
738
739 if (!hasSame || functionLines.empty()) {
740 functionLines.append(functionLine);
741 }
742
743 functionObj[::lines] = functionLines;
744 }
745 }
746 }
747
748 if (global == type) {
749 Json::Value feildArray = globalObj[::feild];
750 if (!feildArray.empty() && feildArray.isArray()) {
751 auto processIdData = ::format(matchs.captured(QStr(::processId)));
752 auto userData = ::format(matchs.captured(QStr(::user)));
753 auto priorityData = ::format(matchs.captured(QStr(::priority)));
754 auto niceData = ::format(matchs.captured(QStr(::nice)));
755 auto virtualSizeData = ::format(matchs.captured(QStr(::virtualSize)));
756 auto residentSizeData = ::format(matchs.captured(QStr(::residentSize)));
757 auto sharableData = ::format(matchs.captured(QStr(::sharable)));
758 auto statusData = ::format(matchs.captured(QStr(::status)));
759 auto cpuOverheadData = ::format(matchs.captured(QStr(::cpuOverhead)));
760 auto memoryOverheadData = ::format(matchs.captured(QStr(::memoryOverhead)));
761 auto timeCountData = ::format(matchs.captured(QStr(::timeCount)));
762 auto commandData = ::format(matchs.captured(QStr(::command)));
763 if (!processIdData.isEmpty() && !userData.isEmpty()
764 && !priorityData.isEmpty() && !niceData.isEmpty()
765 && !virtualSizeData.isEmpty() && !residentSizeData.isEmpty()
766 && !sharableData.isEmpty() && !statusData.isEmpty()
767 && !cpuOverheadData.isEmpty() && !memoryOverheadData.isEmpty()
768 && !timeCountData.isEmpty() && !commandData.isEmpty()) {
769
770 Json::Value globalLine(Json::arrayValue);
771 globalLine.resize(feildArray.size());
772 for (unsigned int idx = 0; idx < feildArray.size(); idx ++) {
773 auto feild = feildArray[idx];
774 if (feild == ::processId) {
775 globalLine[idx] = processIdData.toStdString();
776 } else if (feild == ::user) {
777 globalLine[idx] = userData.toStdString();
778 } else if (feild == ::priority) {
779 globalLine[idx] = priorityData.toStdString();
780 } else if (feild == ::nice) {
781 globalLine[idx] = niceData.toStdString();
782 } else if (feild == ::virtualSize) {
783 globalLine[idx] = virtualSizeData.toStdString();
784 } else if (feild == ::residentSize) {
785 globalLine[idx] = residentSizeData.toStdString();
786 } else if (feild == ::sharable) {
787 globalLine[idx] = sharableData.toStdString();
788 } else if (feild == ::status) {
789 globalLine[idx] = statusData.toStdString();
790 } else if (feild == ::cpuOverhead) {
791 globalLine[idx] = cpuOverheadData.toStdString() + "%";
792 } else if (feild == ::memoryOverhead) {
793 globalLine[idx] = memoryOverheadData.toStdString() + "%";
794 } else if (feild == ::timeCount) {
795 globalLine[idx] = timeCountData.toStdString();
796 } else if (feild == ::command) {
797 globalLine[idx] = commandData.toStdString();
798 } else {
799 qCritical() << "unknown feild from performance type : global";
800 }
801 }
802
803 Json::Value linesArray(Json::arrayValue);
804 linesArray.append(globalLine);
805 globalObj[::lines] = linesArray;
806 }
807 }
808 }
809
810 if (vfs == type) {
811 Json::Value feildArray = vfsObj[::feild];
812 if (!feildArray.empty() && feildArray.isArray()) {
813 auto commandData = ::format(matchs.captured(QStr(::command)));
814 auto processIdData = ::format(matchs.captured(QStr(::processId)));
815 auto userData = ::format(matchs.captured(QStr(::user)));
816 auto fdData = ::format(matchs.captured(QStr(::fd)));
817 auto typeData = ::format(matchs.captured(QStr(::type)));
818 auto deviceData = ::format(matchs.captured(QStr(::device)));
819 auto sizeOffData = ::format(matchs.captured(QStr(::sizeOff)));
820 auto nodeData = ::format(matchs.captured(QStr(::node)));
821 auto nameData = ::format(matchs.captured(QStr(::name)));
822 if (!commandData.isEmpty() && !processIdData.isEmpty()
823 && !userData.isEmpty() && !fdData.isEmpty()
824 && !typeData.isEmpty() && !deviceData.isEmpty()
825 && !sizeOffData.isEmpty() && !nodeData.isEmpty()
826 && !nameData.isEmpty()) {
827
828 Json::Value linesArray = vfsObj[::lines];
829 Json::Value vfsLine(Json::arrayValue);
830 vfsLine.resize(feildArray.size());
831 for (unsigned int idx = 0; idx < feildArray.size(); idx ++) {
832 auto feild = feildArray[idx];
833 if (feild == ::command) {
834 vfsLine[idx] = commandData.toStdString();
835 } else if (feild == ::processId) {
836 vfsLine[idx] = processIdData.toStdString();
837 } else if (feild == ::user) {
838 vfsLine[idx] = userData.toStdString();
839 } else if (feild == ::fd) {
840 vfsLine[idx] = fdData.toStdString();
841 } else if (feild == ::type) {
842 vfsLine[idx] = typeData.toStdString();
843 } else if (feild == ::device) {
844 vfsLine[idx] = deviceData.toStdString();
845 } else if (feild == ::sizeOff) {
846 vfsLine[idx] = sizeOffData.toStdString();
847 } else if (feild == ::node) {
848 vfsLine[idx] = nodeData.toStdString();
849 } else if (feild == ::name) {
850 vfsLine[idx] = nameData.toStdString();
851 } else {
852 qCritical() << "unknown feild from performance type : global";
853 }
854 }
855 linesArray.append(vfsLine);
856 vfsObj[::lines] = linesArray;
857 }
858 }
859 }
860
861 if (socket == type) {
862 Json::Value feildArray = socketObj[::feild];
863 if (!feildArray.empty() && feildArray.isArray()) {
864 auto netIdData = ::format(matchs.captured(QStr(::netId)));
865 auto stateData = ::format(matchs.captured(QStr(::state)));
866 auto recvQueueData = ::format(matchs.captured(QStr(::recvQueue)));
867 auto sendQueueData = ::format(matchs.captured(QStr(::sendQueue)));
868 auto localAddrPortData = ::format(matchs.captured(QStr(::localAddrPort)));
869 auto peerAddrPortData = ::format(matchs.captured(QStr(::peerAddrPort)));
870 auto usingProcessesData = ::format(matchs.captured(QStr(::usingProcesses)));
871 // maybe usingProcesses is empty, also to do.
872 if (!netIdData.isEmpty() && !stateData.isEmpty()
873 && !recvQueueData.isEmpty() && !sendQueueData.isEmpty()
874 && !localAddrPortData.isEmpty() && !peerAddrPortData.isEmpty())
875 {
876 Json::Value socketOne;
877 socketOne[::netId] = netIdData.toStdString();
878 socketOne[::state] = stateData.toStdString();
879 socketOne[::recvQueue] = recvQueueData.toStdString();
880 socketOne[::sendQueue] = sendQueueData.toStdString();
881 socketOne[::localAddrPort] = localAddrPortData.toStdString();
882 socketOne[::peerAddrPort] = peerAddrPortData.toStdString();
883 socketOne[::usingProcesses] = usingProcessesData.toStdString();
884
885 Json::Value linesArray = socketObj[::lines];
886 Json::Value socketLine(Json::arrayValue);
887 socketLine.resize(feildArray.size());
888 for (unsigned int idx = 0; idx < feildArray.size(); idx ++) {
889 auto feild = feildArray[idx];
890 if (feild == ::netId) {
891 socketLine[idx] = netIdData.toStdString();
892 } else if (feild == ::state) {
893 socketLine[idx] = stateData.toStdString();
894 } else if (feild == ::recvQueue) {
895 socketLine[idx] = recvQueueData.toStdString();
896 } else if (feild == ::sendQueue) {
897 socketLine[idx] = sendQueueData.toStdString();
898 } else if (feild == ::localAddrPort) {
899 socketLine[idx] = localAddrPortData.toStdString();
900 } else if (feild == ::peerAddrPort) {
901 socketLine[idx] = peerAddrPortData.toStdString();
902 } else if (feild == ::usingProcesses) {
903 socketLine[idx] = usingProcessesData.toStdString();
904 } else {
905 qCritical() << "unknown feild from performance type : global";
906 }
907 }
908 linesArray.append(socketLine);
909 socketObj[::lines] = linesArray;
910 }
911 }
912 }
913
914 if (deviceIO == type) {
915 Json::Value feildArray = deviceIOObj[::feild];
916 if (!feildArray.empty() && feildArray.isArray()) {
917 auto userIdData = ::format(matchs.captured(QStr(::userId)));
918 auto processIdData = ::format(matchs.captured(QStr(::processId)));
919 auto kB_rdData = ::format(matchs.captured(QStr(::kB_rd)));
920 auto kB_wdData = ::format(matchs.captured(QStr(::kB_wd)));
921 auto kB_ccwrData = ::format(matchs.captured(QStr(::kB_ccwr)));
922 auto iodelayData = ::format(matchs.captured(QStr(::iodelay)));
923 auto commandData = ::format(matchs.captured(QStr(::command)));
924 if (!userIdData.isEmpty() && !processIdData.isEmpty()
925 && !kB_rdData.isEmpty() && !kB_wdData.isEmpty()
926 && !kB_ccwrData.isEmpty() && !iodelayData.isEmpty()
927 && !commandData.isEmpty())
928 {
929 Json::Value linesArray;
930 Json::Value deviceIOLine(Json::arrayValue);
931 deviceIOLine.resize(feildArray.size());
932 for (unsigned int idx = 0; idx < feildArray.size(); idx ++) {
933 auto feild = feildArray[idx];
934 if (feild == ::userId) {
935 deviceIOLine[idx] = userIdData.toStdString();
936 } else if (feild == ::processId) {
937 deviceIOLine[idx] = processIdData.toStdString();
938 } else if (feild == ::kB_rd + "/s") {
939 deviceIOLine[idx] = kB_rdData.toStdString();
940 } else if (feild == ::kB_wd + "/s") {
941 deviceIOLine[idx] = kB_wdData.toStdString();
942 } else if (feild == ::kB_ccwr + "/s") {
943 deviceIOLine[idx] = kB_ccwrData.toStdString();
944 } else if (feild == ::iodelay) {
945 deviceIOLine[idx] = iodelayData.toStdString();
946 } else if (feild == ::command) {
947 deviceIOLine[idx] = commandData.toStdString();
948 } else {
949 qCritical() << "unknown feild from performance type : global";
950 }
951 }
952 linesArray.append(deviceIOLine);
953 deviceIOObj[::lines] = linesArray;
954 }
955 }
956 }
957 }
958 }
959 }
960
961 if (!functionObj[::lines].empty()) {
962 d->countFunction(functionObj); // count function information
963 }
964
965 if (!vfsObj[::lines].empty()) {
966 d->toolsCountCache[PerformanceType::vfs] = vfsObj;
967 }
968
969 if (!socketObj[::lines].empty()) {
970 d->toolsCountCache[PerformanceType::socket] = socketObj;
971 }
972
973 if (!deviceIOObj[::lines].empty()) {
974 d->toolsCountCache[PerformanceType::deviceIO] = deviceIOObj;
975 }
976
977 if (!globalObj[::lines].empty()) { // with global timer get count function information
978 Json::Value result;
979 d->toolsCountCache[PerformanceType::global] = globalObj;
980 Json::Value countfunctionCache = d->toolsCountCache[PerformanceType::function];
981 if (!countfunctionCache[::lines].empty()) {
982 d->toolsCountCache[PerformanceType::function] = countfunctionCache;
983 result[::enumElemName(PerformanceType::function)] = countfunctionCache;
984 }
985 Json::Value vfsCache = d->toolsCountCache[PerformanceType::vfs];
986 if (!vfsCache[::lines].empty()) {
987 result[::enumElemName(PerformanceType::vfs)] = vfsCache;
988 }
989 Json::Value socketCache = d->toolsCountCache[PerformanceType::socket];
990 if (!socketCache[::lines].empty()) {
991 result[::enumElemName(PerformanceType::socket)] = socketCache;
992 }
993 Json::Value deviceIOCache = d->toolsCountCache[PerformanceType::deviceIO];
994 if (!deviceIOCache[::lines].empty()) {
995 result[::enumElemName(PerformanceType::deviceIO)] = deviceIOCache;
996 }
997
998 result[::enumElemName(PerformanceType::global)] = globalObj;
999 qInfo() << qPrintable(QStr(result.toStyledString()));
1000 emit attachData(result);
1001 }
1002 }
1003 }
1004}
1005
1006void Tools::readAllStandardError()
1007{
1008 QProcess *process = qobject_cast<QProcess*>(sender());
1009 if (process) {
1010 QString errorOut = process->readAllStandardError().replace("\n", " ");
1011 // if (process->error() == QProcess::UnknownError) {
1012 // PerformanceType pt = qvariant_cast<PerformanceType>(process->property(::enumName<PerformanceType>()));
1013 // QString program = process->program();
1014 // QString package_name = process->property(::package_name.c_str()).toString();
1015
1016 // d->displayErrorWidget()->append(QStr(::enumName<PerformanceType>()) + ": " + ::enumElemName<PerformanceType>(pt));
1017 // d->displayErrorWidget()->append(QStr("program: ") + process->program() + process->arguments().join(" "));
1018 // d->displayErrorWidget()->append(QStr("errorString: ") + errorOut);
1019 // if (package_name.toStdString() != ::system_default) {
1020 // d->displayErrorWidget()->append(Tools::tr("suggestion: "
1021 // "please to install tool, the package-name \"%0\". "
1022 // "If you confirm that the current tool exists and still cannot be used normally, "
1023 // "the current operation does not support").arg(package_name));
1024 // } else {
1025 // d->displayErrorWidget()->append(QStr("suggestion: ")
1026 // + "Because the current program is a system application, "
1027 // "if it is not found, "
1028 // "it means that the item is unavailable");
1029 // }
1030 // d->displayErrorWidget()->append("\n");
1031 // d->displayErrorWidget()->show();
1032 // }
1033 qCritical() << qvariant_cast<PerformanceType>(process->property(::enumName<PerformanceType>()))
1034 << errorOut
1035 << process->error()
1036 << process->errorString();
1037 }
1038}
1039
1040void Tools::errorOccurred(QProcess::ProcessError error)
1041{
1042 QProcess *process = qobject_cast<QProcess*>(sender());
1043 if (process) {
1044
1045 PerformanceType pt = qvariant_cast<PerformanceType>(process->property(::enumName<PerformanceType>()));
1046 QString program = process->program();
1047 QString package_name = process->property(::package_name.c_str()).toString();
1048
1049 d->displayErrorWidget()->append(QStr(::enumName<PerformanceType>()) + ": " + ::enumElemName<PerformanceType>(pt));
1050 d->displayErrorWidget()->append(QStr("program: ") + process->program() + process->arguments().join(" "));
1051 d->displayErrorWidget()->append(QStr("errorString: ") + process->errorString());
1052 if (package_name.toStdString() != ::system_default) {
1053 d->displayErrorWidget()->append(Tools::tr("suggestion: "
1054 "please to install tool, the package-name \"%0\". "
1055 "If you confirm that the current tool exists and still cannot be used normally, "
1056 "the current operation does not support").arg(package_name));
1057 } else {
1058 d->displayErrorWidget()->append(QStr("suggestion: ")
1059 + "Because the current program is a system application, "
1060 "if it is not found, "
1061 "it means that the item is unavailable");
1062 }
1063 d->displayErrorWidget()->append("\n");
1064 d->displayErrorWidget()->show();
1065
1066 qCritical() << pt << error << process->errorString();
1067 }
1068}
1069