/source/ArmarXCore/core/system/ProcessWatcher.cpp
C++ | 420 lines | 316 code | 80 blank | 24 comment | 26 complexity | 68fd08549a846e560adbe153d428891e MD5 | raw file
- /*
- * This file is part of ArmarX.
- *
- * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
- *
- * ArmarX is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * ArmarX 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 <http://www.gnu.org/licenses/>.
- *
- * @package ArmarX::
- * @author Mirko Waechter ( mirko.waechter at kit dot edu)
- * @date 2013
- * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
- * GNU General Public License
- */
- #include <IceUtil/Handle.h> // for HandleBase
- #include <boost/algorithm/string/split.hpp> // for split
- #include <boost/algorithm/string/trim.hpp> // for trim
- #include <boost/foreach.hpp> // for auto_any_base, etc
- #include <boost/iterator/iterator_facade.hpp> // for operator!=
- #include <boost/lexical_cast.hpp> // for lexical_cast
- #include <boost/move/utility.hpp> // for move
- #include <boost/mpl/bool.hpp> // for bool_
- #include <boost/mpl/bool_fwd.hpp> // for false_
- #include <boost/range/iterator_range_core.hpp> // for operator==
- #include <boost/smart_ptr/shared_ptr.hpp> // for shared_ptr
- #include <boost/token_functions.hpp> // for char_separator
- #include <boost/token_iterator.hpp> // for token_iterator
- #include <boost/tokenizer.hpp> // for tokenizer
- #include <boost/typeof/typeof.hpp> // for BOOST_AUTO
- #include <boost/utility/swap.hpp> // for swap
- #include <ctype.h> // for isspace
- #include <ext/alloc_traits.h>
- #include <stdlib.h> // for atoi
- #include <algorithm> // for max, swap
- #include <functional> // for equal_to
- #include <memory> // for allocator, operator==
- #include <sstream> // for operator<<, ifstream, etc
- #include <utility> // for pair
- #include <fstream>
- #include "ArmarXCore/core/logging/LogSender.h" // for LogSender
- #include "ArmarXCore/core/logging/Logging.h" // for ARMARX_WARNING_S
- #include "ArmarXCore/core/services/tasks/PeriodicTask.h"
- #include "ArmarXCore/core/system/../services/profiler/Profiler.h"
- #include "ArmarXCore/interface/core/ThreadingIceBase.h" // for upCast
- #include "ProcessWatcher.h"
- #ifndef _WIN32
- #include <sys/utsname.h> // for uname, utsname
- #ifdef __APPLE__
- #include <cstdlib>
- #else
- #include <malloc.h> // for mallinfo
- #endif
- #include <time.h> // for NULL
- #include <unistd.h> // for sysconf, _SC_CLK_TCK
- #endif
- using namespace armarx;
- ProcessWatcher::ProcessWatcher(Profiler::ProfilerPtr profiler) :
- profiler(profiler)
- {
- long pid = LogSender::getProcessId();
- Hertz = sysconf(_SC_CLK_TCK);
- std::stringstream ss;
- ss << "/proc/" << pid << "/stat";
- processCpuDataFilename = ss.str();
- processCpuUsage.processId = pid;
- cpuUsageFromProcFile();
- }
- void ProcessWatcher::start()
- {
- if (!watcherTask)
- {
- watcherTask = new PeriodicTask<ProcessWatcher>(this, &ProcessWatcher::watch, 300, false);
- }
- watcherTask->start();
- }
- void ProcessWatcher::stop()
- {
- watcherTask->stop();
- watcherTask = nullptr;
- }
- void ProcessWatcher::addThread(int processId, int threadId)
- {
- ScopedLock lock(threadWatchListMutex);
- ThreadUsage usage;
- usage.processId = processId;
- usage.threadId = threadId;
- usage.lastJiffies = GetThreadJiffies(processId, threadId);
- usage.lastUpdate = IceUtil::Time::now();
- usage.load = 0;
- threadWatchList.insert(usage);
- }
- void ProcessWatcher::addThread(int threadId)
- {
- addThread(LogSender::getProcessId(), threadId);
- }
- void ProcessWatcher::watch()
- {
- runThreadWatchList();
- cpuUsageFromProcFile();
- getMemoryUsage();
- reportCpuUsage();
- reportMemoryUsage();
- }
- void ProcessWatcher::runThreadWatchList()
- {
- ScopedLock lock(threadWatchListMutex);
- boost::unordered_set<ThreadUsage> newset;
- boost::unordered_set<ThreadUsage>::iterator it = threadWatchList.begin();
- for (; it != threadWatchList.end(); it++)
- {
- ThreadUsage usage = *it;
- IceUtil::Time queryTime = IceUtil::Time::now();
- int curJiffies = GetThreadJiffies(usage.processId, usage.threadId);
- if (GetHertz() != -1)
- {
- usage.load = (curJiffies - usage.lastJiffies) / ((queryTime - usage.lastUpdate).toSecondsDouble() * GetHertz());
- }
- else
- {
- usage.load = 0;
- }
- usage.lastJiffies = curJiffies;
- usage.lastUpdate = queryTime;
- newset.insert(usage);
- }
- threadWatchList.swap(newset);
- }
- #if !defined(_WIN32) && !defined(__APPLE__)
- int ProcessWatcher::GetHertz()
- {
- static Mutex mutex;
- ScopedLock lock(mutex);
- static int value = -2;
- if (value == -2) // init value
- {
- static std::string _release;
- if (_release.empty())
- {
- struct utsname utsinfo;
- uname(&utsinfo);
- _release = utsinfo.release;
- }
- std::string fileName = "/boot/config-" + _release;
- std::ifstream configFile(fileName.c_str());
- // procFile.open(fileName, ios::out);
- if (!configFile.is_open())
- {
- ARMARX_WARNING_S << "Cannot open " << fileName << " for reading the cpu hertz value";
- value = -1;
- }
- else
- {
- value = -1;
- std::string line;
- const std::string searchString = "CONFIG_HZ=";
- while (getline(configFile, line))
- {
- std::size_t pos = line.find(searchString);
- if (pos != std::string::npos)
- {
- std::string hzString = line.substr(pos + searchString.length());
- value = atoi(hzString.c_str());
- break;
- }
- }
- }
- }
- return value;
- }
- void ProcessWatcher::cpuUsageFromProcFile()
- {
- std::string line;
- std::ifstream file(processCpuDataFilename);
- if (!file.is_open())
- {
- ARMARX_WARNING_S << "File " << processCpuDataFilename << " does not exist";
- }
- std::getline(file, line);
- file.close();
- std::vector<std::string> pidElements;
- boost::split(pidElements, line, ::isspace);
- int currentUtime = boost::lexical_cast<int>(pidElements.at(eUTime));
- int currentStime = boost::lexical_cast<int>(pidElements.at(eSTime));
- int currentCUtime = boost::lexical_cast<int>(pidElements.at(eCUTIME));
- int currentCStime = boost::lexical_cast<int>(pidElements.at(eCSTIME));
- {
- ScopedLock guard{processCpuUsageMutex};
- IceUtil::Time queryTime = IceUtil::Time::now();
- processCpuUsage.proc_total_time = 100 * (double)((currentStime + currentCStime - processCpuUsage.lastStime - processCpuUsage.lastCStime) + (currentUtime + currentCUtime - processCpuUsage.lastCUtime - processCpuUsage.lastUtime)) / ((queryTime - processCpuUsage.lastUpdate).toSecondsDouble() * Hertz);
- processCpuUsage.lastUpdate = queryTime;
- processCpuUsage.lastUtime = currentUtime;
- processCpuUsage.lastStime = currentStime;
- processCpuUsage.lastCUtime = currentCUtime;
- processCpuUsage.lastCStime = currentCStime;
- }
- }
- void ProcessWatcher::getMemoryUsage()
- {
- struct mallinfo _mallinfo;
- _mallinfo = mallinfo();
- {
- ScopedLock guard{processMemoryUsageMutex};
- memoryUsage.fastbinBlocks = _mallinfo.fsmblks;
- memoryUsage.totalAllocatedSpace = _mallinfo.uordblks;
- memoryUsage.totalFreeSpace = _mallinfo.fordblks;
- }
- }
- std::map<int, int> ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
- {
- std::map<int, int> resultMap;
- BOOST_FOREACH(int threadId, threadIds)
- {
- resultMap[threadId] = 0;
- std::stringstream fileName;
- fileName << "/proc/" << threadId << "/task/" << threadId << "/stat";
- std::ifstream statFile(fileName.str().c_str());
- if (!statFile.is_open())
- {
- ARMARX_WARNING_S << "Cannot open " << fileName.str() << " for reading the hertz value";
- resultMap[threadId] = 0;
- continue;
- }
- std::string line;
- while (getline(statFile, line))
- {
- boost::algorithm::trim(line);
- using namespace boost;
- char_separator<char> sep(" ");
- tokenizer< char_separator<char> > tokens(line, sep);
- std::vector<std::string> stringVec;
- stringVec.assign(tokens.begin(), tokens.end());
- int userTimeJiffies = atoi(stringVec.at(13).c_str());
- int kernelTimeJiffies = atoi(stringVec.at(14).c_str());
- resultMap[threadId] = userTimeJiffies + kernelTimeJiffies;
- }
- }
- return resultMap;
- }
- #else
- int ProcessWatcher::GetHertz()
- {
- return -1;
- }
- std::map<int, int> ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
- {
- return std::map<int, int>();
- }
- void ProcessWatcher::cpuUsageFromProcFile()
- {
- }
- void ProcessWatcher::getMemoryUsage()
- {
- }
- #endif
- bool ThreadUsage::operator <(const ThreadUsage& rhs) const
- {
- if (processId < rhs.processId)
- {
- return true;
- }
- if (threadId < rhs.threadId)
- {
- return true;
- }
- return false;
- }
- bool ThreadUsage::operator ==(const ThreadUsage& rhs) const
- {
- if (processId == rhs.processId && threadId == rhs.threadId)
- {
- return true;
- }
- return false;
- }
- int ProcessWatcher::GetThreadJiffies(int processId, int threadId)
- {
- std::map<int, int> result = GetThreadListJiffies(processId, std::vector<int>(1, threadId));
- std::map<int, int>::iterator it = result.find(threadId);
- if (it == result.end())
- {
- return 0;
- }
- return it->second;
- }
- void ProcessWatcher::reportCpuUsage()
- {
- ScopedLock guard{processCpuUsageMutex};
- profiler->logProcessCpuUsage(processCpuUsage.proc_total_time);
- }
- void ProcessWatcher::reportMemoryUsage()
- {
- ScopedLock guard{processMemoryUsageMutex};
- profiler->logProcessMemoryUsage(memoryUsage.totalFreeSpace);
- }
- double ProcessWatcher::getThreadLoad(int threadId)
- {
- return getThreadLoad(LogSender::getProcessId(), threadId);
- }
- double ProcessWatcher::getThreadLoad(int processId, int threadId)
- {
- ThreadUsage query;
- query.processId = processId;
- query.threadId = threadId;
- ScopedLock lock(threadWatchListMutex);
- BOOST_AUTO(it, threadWatchList.find(query));
- if (it != threadWatchList.end())
- {
- return it->load * 100;
- }
- else
- {
- return 0.0;
- }
- }
- armarx::CpuUsage ProcessWatcher::getProcessCpuUsage()
- {
- ScopedLock guard{processCpuUsageMutex};
- return processCpuUsage;
- }
- void ProcessWatcher::removeThread(int processId, int threadId)
- {
- ThreadUsage query;
- query.processId = processId;
- query.threadId = threadId;
- ScopedLock lock(threadWatchListMutex);
- threadWatchList.erase(query);
- }
- void ProcessWatcher::removeThread(int threadId)
- {
- removeThread(LogSender::getProcessId(), threadId);
- }