File indexing completed on 2024-04-28 05:31:38
0001 /* 0002 SPDX-FileCopyrightText: 2007 John Tapsell <tapsell@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "processes_atop_p.h" 0008 #include "atop_p.h" 0009 #include "process.h" 0010 #include "processcore_debug.h" 0011 0012 #include <zlib.h> 0013 0014 #include <QByteArray> 0015 #include <QFile> 0016 #include <QHash> 0017 #include <QTextStream> 0018 #include <QtEndian> 0019 0020 #include <QDebug> 0021 0022 namespace KSysGuard 0023 { 0024 class ProcessesATop::Private 0025 { 0026 public: 0027 Private(); 0028 ~Private(); 0029 QFile atopLog; 0030 bool ready; 0031 0032 bool loadDataForHistory(int index); 0033 bool loadHistoryFile(const QString &filename); 0034 0035 RawHeader rh; 0036 RawRecord rr; 0037 // SStat sstats; 0038 PStat *pstats; 0039 QList<long> pids; // This is a list of process pid's, in the exact same order as pstats 0040 QString lastError; 0041 0042 QList<long> historyOffsets; //< The file offset where each history is stored 0043 QList<QPair<QDateTime, uint>> historyTimes; //< The end time for each history record and its interval, probably in order from oldest to newest 0044 int currentlySelectedIndex; 0045 }; 0046 0047 ProcessesATop::Private::Private() 0048 : ready(false) 0049 , pstats(nullptr) 0050 , currentlySelectedIndex(-1) 0051 { 0052 } 0053 0054 ProcessesATop::Private::~Private() 0055 { 0056 } 0057 0058 QString ProcessesATop::historyFileName() const 0059 { 0060 return d->atopLog.fileName(); 0061 } 0062 0063 bool ProcessesATop::loadHistoryFile(const QString &filename) 0064 { 0065 return d->loadHistoryFile(filename); 0066 } 0067 0068 bool ProcessesATop::Private::loadHistoryFile(const QString &filename) 0069 { 0070 atopLog.setFileName(filename); 0071 ready = false; 0072 currentlySelectedIndex = -1; 0073 if (!atopLog.exists()) { 0074 lastError = QLatin1String("File ") + filename + QLatin1String(" does not exist"); 0075 return false; 0076 } 0077 0078 if (!atopLog.open(QIODevice::ReadOnly)) { 0079 lastError = QLatin1String("Could not open file ") + filename; 0080 return false; 0081 } 0082 0083 int sizeRead = atopLog.read((char *)(&rh), sizeof(RawHeader)); 0084 if (sizeRead != sizeof(RawHeader)) { 0085 lastError = QLatin1String("Could not read header from file ") + filename; 0086 return false; 0087 } 0088 if (rh.magic != ATOPLOGMAGIC) { 0089 lastError = QLatin1String("File ") + filename + QLatin1String(" does not contain raw atop/atopsar output (wrong magic number)"); 0090 return false; 0091 } 0092 if (/*rh.sstatlen != sizeof(SStat) ||*/ 0093 rh.pstatlen != sizeof(PStat) || rh.rawheadlen != sizeof(RawHeader) || rh.rawreclen != sizeof(RawRecord)) { 0094 lastError = QLatin1String("File ") + filename + QLatin1String(" has incompatible format"); 0095 if (rh.aversion & 0x8000) { 0096 lastError = QStringLiteral("(created by version %1.%2. This program understands the format written by version 1.23") 0097 .arg((rh.aversion >> 8) & 0x7f) 0098 .arg(rh.aversion & 0xff); 0099 } 0100 return false; 0101 } 0102 0103 /* Read the first data header */ 0104 int offset = atopLog.pos(); 0105 historyTimes.clear(); 0106 historyOffsets.clear(); 0107 while (!atopLog.atEnd() && atopLog.read((char *)(&rr), sizeof(RawRecord)) == sizeof(RawRecord)) { 0108 historyOffsets << offset; 0109 historyTimes << QPair<QDateTime, uint>(QDateTime::fromSecsSinceEpoch(rr.curtime), rr.interval); 0110 offset += sizeof(RawRecord) + rr.scomplen + rr.pcomplen; 0111 atopLog.seek(offset); 0112 } 0113 if (currentlySelectedIndex >= historyOffsets.size()) { 0114 currentlySelectedIndex = historyOffsets.size() - 1; 0115 } 0116 0117 ready = true; 0118 return true; 0119 } 0120 0121 bool ProcessesATop::Private::loadDataForHistory(int index) 0122 { 0123 delete[] pstats; 0124 pstats = nullptr; 0125 atopLog.seek(historyOffsets.at(index)); 0126 /*Read the first data header */ 0127 if (atopLog.read((char *)(&rr), sizeof(RawRecord)) != sizeof(RawRecord)) { 0128 lastError = QStringLiteral("Could not read data header"); 0129 return false; 0130 } 0131 0132 if (historyTimes.at(index).first != QDateTime::fromSecsSinceEpoch(rr.curtime) || historyTimes.at(index).second != rr.interval) { 0133 lastError = QStringLiteral("INTERNAL ERROR WITH loadDataForHistory"); 0134 ready = false; 0135 return false; 0136 } 0137 0138 atopLog.seek(atopLog.pos() + rr.scomplen); 0139 QByteArray processRecord; 0140 processRecord.resize(rr.pcomplen); 0141 // qToBigEndian( rr.pcomplen, (uchar*)processRecord.data() ); 0142 unsigned int dataRead = 0; 0143 do { 0144 int ret = atopLog.read(processRecord.data() + dataRead, rr.pcomplen - dataRead); 0145 if (ret == -1) { 0146 lastError = QStringLiteral("Stream interrupted while being read"); 0147 return false; 0148 } 0149 dataRead += ret; 0150 } while (dataRead < rr.pcomplen); 0151 Q_ASSERT(dataRead == rr.pcomplen); 0152 // Q_ASSERT( (index + 1 ==historyTimes.count()) || atopLog.pos() == historyTimes.at(index+1)); 0153 0154 pstats = new PStat[rr.nlist]; 0155 unsigned long uncompressedLength = sizeof(struct PStat) * rr.nlist; 0156 int ret = uncompress((Byte *)pstats, &uncompressedLength, (Byte *)processRecord.constData(), rr.pcomplen); 0157 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_NEED_DICT) { 0158 switch (ret) { 0159 case Z_MEM_ERROR: 0160 lastError = QStringLiteral("Could not uncompress record data due to lack of memory"); 0161 break; 0162 case Z_BUF_ERROR: 0163 lastError = QStringLiteral("Could not uncompress record data due to lack of room in buffer"); 0164 break; 0165 case Z_DATA_ERROR: 0166 lastError = QStringLiteral("Could not uncompress record data due to corrupted data"); 0167 break; 0168 default: 0169 lastError = QLatin1String("Could not uncompress record data due to unexpected error: ") + QString::number(ret); 0170 break; 0171 } 0172 delete[] pstats; 0173 pstats = nullptr; 0174 return false; 0175 } 0176 0177 pids.clear(); 0178 for (uint i = 0; i < rr.nlist; i++) { 0179 pids << pstats[i].gen.pid; 0180 } 0181 return true; 0182 } 0183 0184 ProcessesATop::ProcessesATop(bool loadDefaultFile) 0185 : d(new Private()) 0186 { 0187 if (loadDefaultFile) { 0188 loadHistoryFile(QStringLiteral("/var/log/atop.log")); 0189 } 0190 } 0191 0192 bool ProcessesATop::isHistoryAvailable() const 0193 { 0194 return d->ready; 0195 } 0196 0197 long ProcessesATop::getParentPid(long pid) 0198 { 0199 int index = d->pids.indexOf(pid); 0200 if (index < 0) { 0201 return 0; 0202 } 0203 return d->pstats[index].gen.ppid; 0204 } 0205 0206 bool ProcessesATop::updateProcessInfo(long pid, Process *process) 0207 { 0208 int index = d->pids.indexOf(pid); 0209 if (index < 0) { 0210 return false; 0211 } 0212 PStat &p = d->pstats[index]; 0213 process->setParentPid(p.gen.ppid); 0214 process->setUid(p.gen.ruid); 0215 process->setEuid(p.gen.ruid); 0216 process->setSuid(p.gen.ruid); 0217 process->setFsuid(p.gen.ruid); 0218 process->setGid(p.gen.rgid); 0219 process->setEgid(p.gen.rgid); 0220 process->setSgid(p.gen.rgid); 0221 process->setFsgid(p.gen.rgid); 0222 process->setTracerpid(-1); 0223 process->setNumThreads(p.gen.nthr); 0224 // process->setTty 0225 process->setUserTime(p.cpu.utime * 100 / d->rh.hertz); // check - divide by interval maybe? 0226 process->setSysTime(p.cpu.stime * 100 / d->rh.hertz); // check 0227 process->setUserUsage(process->userTime() / d->rr.interval); 0228 process->setSysUsage(process->sysTime() / d->rr.interval); 0229 process->setNiceLevel(p.cpu.nice); 0230 // process->setscheduler(p.cpu.policy); 0231 process->setVmSize(p.mem.vmem); 0232 process->setVmRSS(p.mem.rmem); 0233 process->vmSizeChange() = p.mem.vgrow; 0234 process->vmRSSChange() = p.mem.rgrow; 0235 process->setVmURSS(0); 0236 process->vmURSSChange() = 0; 0237 0238 /* Fill in name and command */ 0239 QString name = QString::fromUtf8(p.gen.name, qstrnlen(p.gen.name, PNAMLEN)); 0240 QString command = QString::fromUtf8(p.gen.cmdline, qstrnlen(p.gen.cmdline, CMDLEN)); 0241 // cmdline separates parameters with the NULL character 0242 if (!command.isEmpty()) { 0243 if (command.startsWith(name)) { 0244 int index = command.indexOf(QLatin1Char('\0')); 0245 name = command.left(index); 0246 } 0247 command.replace(QLatin1Char('\0'), QLatin1Char(' ')); 0248 } 0249 process->setName(name); 0250 process->setCommand(command); 0251 0252 /* Fill in state */ 0253 switch (p.gen.state) { 0254 case 'E': 0255 process->setStatus(Process::Ended); 0256 break; 0257 case 'R': 0258 process->setStatus(Process::Running); 0259 break; 0260 case 'S': 0261 process->setStatus(Process::Sleeping); 0262 break; 0263 case 'D': 0264 process->setStatus(Process::DiskSleep); 0265 break; 0266 case 'Z': 0267 process->setStatus(Process::Zombie); 0268 break; 0269 case 'T': 0270 process->setStatus(Process::Stopped); 0271 break; 0272 case 'W': 0273 process->setStatus(Process::Paging); 0274 break; 0275 default: 0276 process->setStatus(Process::OtherStatus); 0277 break; 0278 } 0279 0280 return true; 0281 } 0282 0283 QDateTime ProcessesATop::viewingTime() const 0284 { 0285 if (!d->ready) 0286 return QDateTime(); 0287 return d->historyTimes.at(d->currentlySelectedIndex).first; 0288 } 0289 0290 bool ProcessesATop::setViewingTime(const QDateTime &when) 0291 { 0292 QPair<QDateTime, uint> tmpWhen(when, 0); 0293 QList<QPair<QDateTime, uint>>::iterator i = std::upper_bound(d->historyTimes.begin(), d->historyTimes.end(), tmpWhen); 0294 0295 if (i->first == when || (i->first > when && i->first.addSecs(-i->second) <= when)) { 0296 // We found the time :) 0297 d->currentlySelectedIndex = i - d->historyTimes.begin(); 0298 bool success = d->loadDataForHistory(d->currentlySelectedIndex); 0299 if (!success) { 0300 qCWarning(LIBKSYSGUARD_PROCESSCORE) << d->lastError; 0301 } 0302 return success; 0303 } 0304 return false; 0305 } 0306 0307 QList<QPair<QDateTime, uint>> ProcessesATop::historiesAvailable() const 0308 { 0309 return d->historyTimes; 0310 } 0311 0312 QSet<long> ProcessesATop::getAllPids() 0313 { 0314 const QSet<long> pids(d->pids.cbegin(), d->pids.cend()); 0315 return pids; 0316 } 0317 0318 Processes::Error ProcessesATop::sendSignal(long pid, int sig) 0319 { 0320 Q_UNUSED(pid); 0321 Q_UNUSED(sig); 0322 0323 return Processes::NotSupported; 0324 } 0325 0326 Processes::Error ProcessesATop::setNiceness(long pid, int priority) 0327 { 0328 Q_UNUSED(pid); 0329 Q_UNUSED(priority); 0330 0331 return Processes::NotSupported; 0332 } 0333 0334 Processes::Error ProcessesATop::setScheduler(long pid, int priorityClass, int priority) 0335 { 0336 Q_UNUSED(pid); 0337 Q_UNUSED(priorityClass); 0338 Q_UNUSED(priority); 0339 0340 return Processes::NotSupported; 0341 } 0342 0343 Processes::Error ProcessesATop::setIoNiceness(long pid, int priorityClass, int priority) 0344 { 0345 Q_UNUSED(pid); 0346 Q_UNUSED(priorityClass); 0347 Q_UNUSED(priority); 0348 0349 return Processes::NotSupported; 0350 } 0351 0352 bool ProcessesATop::supportsIoNiceness() 0353 { 0354 return false; 0355 } 0356 0357 long long ProcessesATop::totalPhysicalMemory() 0358 { 0359 return 0; 0360 } 0361 0362 ProcessesATop::~ProcessesATop() 0363 { 0364 delete d; 0365 } 0366 0367 }