| 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 | |