/* PIP - Platform Independent Primitives Process resource monitor Copyright (C) 2017 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "pisystemmonitor.h" #include "pisysteminfo.h" #include "piprocess.h" #include "pitime_win.h" #ifdef WINDOWS # include # include #endif PRIVATE_DEFINITION_START(PISystemMonitor) #ifndef WINDOWS llong cpu_u_cur, cpu_u_prev, cpu_s_cur, cpu_s_prev; #else HANDLE hProc; PROCESS_MEMORY_COUNTERS mem_cnt; PISystemTime tm_kernel, tm_user; PITimeMeasurer tm; #endif PRIVATE_DEFINITION_END(PISystemMonitor) PISystemMonitor::PISystemMonitor(): PIThread() { self_ = false; pID_ = cycle = 0; cpu_count = PISystemInfo::instance()->processorsCount; #ifndef WINDOWS # ifdef QNX page_size = 4096; # else page_size = getpagesize(); # endif #else PRIVATE->hProc = 0; PRIVATE->mem_cnt.cb = sizeof(PRIVATE->mem_cnt); #endif setName("system_monitor"); } PISystemMonitor::~PISystemMonitor() { stop(); } PISystemMonitor::ProcessStats::ProcessStats() { ID = parent_ID = group_ID = session_ID = priority = threads = 0; physical_memsize = resident_memsize = share_memsize = virtual_memsize = data_memsize = 0; cpu_load_user = cpu_load_system = 0.f; } bool PISystemMonitor::startOnProcess(int pID, int interval_ms) { stop(); self_ = false; pID_ = pID; #ifndef WINDOWS file.open("/proc/" + PIString::fromNumber(pID_) + "/stat", PIIODevice::ReadOnly); filem.open("/proc/" + PIString::fromNumber(pID_) + "/statm", PIIODevice::ReadOnly); if (!file.isOpened()) { piCoutObj << "Can`t find process with ID = " << pID_ << "!"; return false; } cycle = -1; #else PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_); if (PRIVATE->hProc == 0) { piCoutObj << "Can`t open process with ID = " << pID_ << "," << errorString(); return false; } PRIVATE->tm.reset(); #endif return start(interval_ms); } bool PISystemMonitor::startOnSelf(int interval_ms) { bool ret = startOnProcess(PIProcess::currentPID(), interval_ms); self_ = true; return ret; } PIVector PISystemMonitor::threadsStatistic() const { mutex_.lock(); PIVector ret = cur_tm.values(); mutex_.unlock(); return ret; } void PISystemMonitor::stop() { PIThread::stop(); #ifdef WINDOWS if (PRIVATE->hProc != 0) { CloseHandle(PRIVATE->hProc); PRIVATE->hProc = 0; } #endif } void PISystemMonitor::run() { #ifndef WINDOWS file.seekToBegin(); PIString str(file.readAll(true)); int si = str.find('(') + 1, fi = 0, cc = 1; for (int i = si; i < str.size_s(); ++i) { if (str[i] == '(') cc++; if (str[i] == ')') cc--; if (cc <= 0) { fi = i; break; } } stat.exec_name = str.mid(si, fi - si); str.cutMid(si - 1, fi - si + 3); PIStringList sl = str.split(" "); if (sl.size_s() < 19) return; stat.ID = sl[0].toInt(); stat.state = sl[1]; stat.parent_ID = sl[2].toInt(); stat.group_ID = sl[3].toInt(); stat.session_ID = sl[4].toInt(); if (cycle < 0) { PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur = sl[12].toLLong(); PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur = sl[13].toLLong(); } cycle++; //if (cycle >= 4) { PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur; PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur; PRIVATE->cpu_u_cur = sl[12].toLLong(); PRIVATE->cpu_s_cur = sl[13].toLLong(); stat.cpu_load_system = PRIVATE->cpu_s_cur - PRIVATE->cpu_s_prev; stat.cpu_load_user = PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev; stat.cpu_load_system /= cpu_count; stat.cpu_load_user /= cpu_count; cycle = 0; //} stat.priority = sl[16].toInt(); stat.threads = sl[18].toInt(); filem.seekToBegin(); str = filem.readAll(true); sl = str.split(" "); if (sl.size_s() < 6) return; stat.virtual_memsize = sl[0].toLong() * page_size; stat.resident_memsize = sl[1].toLong() * page_size; stat.share_memsize = sl[2].toLong() * page_size; stat.data_memsize = sl[5].toLong() * page_size; stat.physical_memsize = stat.resident_memsize - stat.share_memsize; #else stat.ID = pID_; // HMODULE hMod; // DWORD cbNeeded; if (GetProcessMemoryInfo(PRIVATE->hProc, &PRIVATE->mem_cnt, sizeof(PRIVATE->mem_cnt)) != 0) { stat.physical_memsize = PRIVATE->mem_cnt.WorkingSetSize; } stat.priority = GetPriorityClass(PRIVATE->hProc); HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pID_); int thcnt = 0; if (snap != 0) { THREADENTRY32 thread; thread.dwSize = sizeof(THREADENTRY32); if (Thread32First(snap, &thread) == TRUE) { if (thread.th32OwnerProcessID == DWORD(pID_)) ++thcnt; while (Thread32Next(snap, &thread) == TRUE) { if (thread.th32OwnerProcessID == DWORD(pID_)) ++thcnt; } } stat.threads = thcnt; CloseHandle(snap); } FILETIME ft0, ft1, ft_kernel, ft_user; double el_s = PRIVATE->tm.elapsed_s() * cpu_count / 100.; if (GetProcessTimes(PRIVATE->hProc, &ft0, &ft1, &ft_kernel, &ft_user) != 0) { PISystemTime tm_kernel_c = FILETIME2PISystemTime(ft_kernel); PISystemTime tm_user_c = FILETIME2PISystemTime(ft_user); if (cycle < 0) { PRIVATE->tm_kernel = tm_kernel_c; PRIVATE->tm_user = tm_user_c; } //cycle++; cycle = 0; if (el_s <= 0.) { stat.cpu_load_system = 0.f; stat.cpu_load_user = 0.f; } else { stat.cpu_load_system = (tm_kernel_c - PRIVATE->tm_kernel).toSeconds() / el_s; stat.cpu_load_user = (tm_user_c - PRIVATE->tm_user).toSeconds() / el_s; } PRIVATE->tm_kernel = tm_kernel_c; PRIVATE->tm_user = tm_user_c; } else { stat.cpu_load_system = 0.f; stat.cpu_load_user = 0.f; } PRIVATE->tm.reset(); #endif stat.cpu_load_system = piClampf(stat.cpu_load_system, 0.f, 100.f); stat.cpu_load_user = piClampf(stat.cpu_load_user, 0.f, 100.f); last_tm = cur_tm; gatherThreadsStats(); //PISystemTime dt = PISystemTime::fromMilliseconds(delay_); for (PIMap::iterator i = cur_tm.begin(); i != cur_tm.end(); ++i) { if (!last_tm.contains(i.key())) continue; ThreadStats & ts_new(i.value()); ThreadStats & ts_old(last_tm[i.key()]); ts_new.cpu_load_kernel = calcThreadUsage(ts_new.kernel_time, ts_old.kernel_time); ts_new.cpu_load_user = calcThreadUsage(ts_new.user_time, ts_old.user_time); } lock(); cur_ts = cur_tm.values(); unlock(); makeStrings(); } void PISystemMonitor::makeStrings() { stat.physical_memsize_readable.setReadableSize(stat.physical_memsize); stat.resident_memsize_readable.setReadableSize(stat.resident_memsize); stat.share_memsize_readable.setReadableSize(stat.share_memsize); stat.virtual_memsize_readable.setReadableSize(stat.virtual_memsize); stat.data_memsize_readable.setReadableSize(stat.data_memsize); } void PISystemMonitor::gatherThreadsStats() { cur_ts.clear(); cur_tm.clear(); if (!self_) return; __PIThreadCollection * pitc = __PIThreadCollection::instance(); pitc->lock(); PIVector tv = pitc->threads(); piForeachC (PIThread * t, tv) { ThreadStats ts; ts.name = t->name(); #ifdef WINDOWS FILETIME times[4]; PISystemTime ct = PISystemTime::current(); if (GetThreadTimes(t->handle(), &(times[0]), &(times[1]), &(times[2]), &(times[3])) == 0) { piCout << "[PISystemMonitor] threadsInfo():: GetThreadTimes() error:" << errorString(); continue; } ts.created = FILETIME2PIDateTime(times[0]); ts.work_time = ct - ts.created.toSystemTime(); ts.kernel_time = FILETIME2PISystemTime(times[2]); ts.user_time = FILETIME2PISystemTime(times[3]); #endif cur_ts << ts; cur_tm[t] = ts; } pitc->unlock(); } float PISystemMonitor::calcThreadUsage(PISystemTime & t_new, PISystemTime & t_old) { if (delay_ <= 0) return -1.; return piClampf(100. * ((t_new - t_old).toMilliseconds() / delay_), 0.f, 100.f);}