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 | |
19 | namespace { |
20 | // perf top tool |
21 | static std::string overhead{"overhead" }; |
22 | static std::string sample{"sample" }; |
23 | static std::string period{"period" }; |
24 | static std::string dso{"dso" }; |
25 | static std::string symbol{"symbol" }; |
26 | static 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 | */ |
51 | static std::string processId{"processId" }; |
52 | static std::string user{"user" }; |
53 | static std::string priority{"priority" }; |
54 | static std::string nice{"nice" }; |
55 | static std::string virtualSize{"virtualSize" }; |
56 | static std::string residentSize{"residentSize" }; |
57 | static std::string sharable{"sharable" }; |
58 | static std::string status{"status" }; |
59 | static std::string cpuOverhead{"cpuOverhead" }; |
60 | static std::string memoryOverhead{"memoryOverhead" }; |
61 | static std::string timeCount{"timeCount" }; |
62 | static std::string command{"command" }; |
63 | |
64 | static std::string fd{"fd" }; |
65 | static std::string type{"type" }; |
66 | static std::string device{"device" }; |
67 | static std::string sizeOff{"sizeOff" }; |
68 | static std::string node{"node" }; |
69 | static std::string name{"name" }; |
70 | |
71 | static std::string netId{"netId" }; |
72 | static std::string state{"state" }; |
73 | static std::string recvQueue{"recvQueue" }; |
74 | static std::string sendQueue{"sendQueue" }; |
75 | static std::string localAddrPort{"localAddrPort" }; |
76 | static std::string peerAddrPort{"peerAddressPort" }; |
77 | static std::string usingProcesses{"usingProcesses" }; |
78 | |
79 | // device IO field |
80 | static std::string userId{"userId" }; |
81 | //static std::string processId{"processId"}; |
82 | static std::string kB_rd{"kB_rd" }; |
83 | static std::string kB_wd{"kB_wd" }; |
84 | static std::string kB_ccwr{"kB_ccwr" }; |
85 | static std::string iodelay{"iodelay" }; |
86 | //static std::string command{"command"}; |
87 | |
88 | |
89 | static std::string feild{"feild" }; |
90 | static std::string key{"key" }; |
91 | static std::string lines{"lines" }; |
92 | |
93 | static std::string package_name{"package_name" }; |
94 | static std::string system_default{"system default" }; |
95 | |
96 | template<class E> |
97 | const char * enumName() |
98 | { |
99 | return QMetaEnum::fromType<E>().name(); |
100 | } |
101 | |
102 | template<class Elem> |
103 | const char * enumElemName(Elem type) |
104 | { |
105 | return QMetaEnum::fromType<Elem>().valueToKey(type); |
106 | } |
107 | |
108 | struct QStr : QString |
109 | { |
110 | QStr(const std::string &src) : QString(QString::fromStdString(src)){} |
111 | }; |
112 | |
113 | struct 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 | |
130 | QString 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 | |
143 | struct 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 | |
226 | struct 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 | |
247 | class 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 | |
405 | Tools::Tools() |
406 | : d(new ToolsPrivate) |
407 | { |
408 | |
409 | } |
410 | |
411 | Tools::~Tools() |
412 | { |
413 | stopAll(); |
414 | if (d) { |
415 | if (d->textBrowser) { |
416 | delete d->textBrowser; |
417 | } |
418 | delete d; |
419 | } |
420 | } |
421 | |
422 | void 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 | |
431 | void 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 | |
441 | void 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 | |
457 | int Tools::attachPID() const |
458 | { |
459 | return d->attachPID; |
460 | } |
461 | |
462 | Json::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 | |
474 | bool 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 | |
560 | QRegularExpression 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 | |
627 | void 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 | |
1006 | void 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 | |
1040 | void 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 | |