PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/source/ArmarXCore/core/system/ProcessWatcher.cpp

https://gitlab.com/mkroehnert/ArmarXCore
C++ | 420 lines | 316 code | 80 blank | 24 comment | 26 complexity | 68fd08549a846e560adbe153d428891e MD5 | raw file
  1. /*
  2. * This file is part of ArmarX.
  3. *
  4. * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
  5. *
  6. * ArmarX is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. *
  10. * ArmarX is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * @package ArmarX::
  19. * @author Mirko Waechter ( mirko.waechter at kit dot edu)
  20. * @date 2013
  21. * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
  22. * GNU General Public License
  23. */
  24. #include <IceUtil/Handle.h> // for HandleBase
  25. #include <boost/algorithm/string/split.hpp> // for split
  26. #include <boost/algorithm/string/trim.hpp> // for trim
  27. #include <boost/foreach.hpp> // for auto_any_base, etc
  28. #include <boost/iterator/iterator_facade.hpp> // for operator!=
  29. #include <boost/lexical_cast.hpp> // for lexical_cast
  30. #include <boost/move/utility.hpp> // for move
  31. #include <boost/mpl/bool.hpp> // for bool_
  32. #include <boost/mpl/bool_fwd.hpp> // for false_
  33. #include <boost/range/iterator_range_core.hpp> // for operator==
  34. #include <boost/smart_ptr/shared_ptr.hpp> // for shared_ptr
  35. #include <boost/token_functions.hpp> // for char_separator
  36. #include <boost/token_iterator.hpp> // for token_iterator
  37. #include <boost/tokenizer.hpp> // for tokenizer
  38. #include <boost/typeof/typeof.hpp> // for BOOST_AUTO
  39. #include <boost/utility/swap.hpp> // for swap
  40. #include <ctype.h> // for isspace
  41. #include <ext/alloc_traits.h>
  42. #include <stdlib.h> // for atoi
  43. #include <algorithm> // for max, swap
  44. #include <functional> // for equal_to
  45. #include <memory> // for allocator, operator==
  46. #include <sstream> // for operator<<, ifstream, etc
  47. #include <utility> // for pair
  48. #include <fstream>
  49. #include "ArmarXCore/core/logging/LogSender.h" // for LogSender
  50. #include "ArmarXCore/core/logging/Logging.h" // for ARMARX_WARNING_S
  51. #include "ArmarXCore/core/services/tasks/PeriodicTask.h"
  52. #include "ArmarXCore/core/system/../services/profiler/Profiler.h"
  53. #include "ArmarXCore/interface/core/ThreadingIceBase.h" // for upCast
  54. #include "ProcessWatcher.h"
  55. #ifndef _WIN32
  56. #include <sys/utsname.h> // for uname, utsname
  57. #ifdef __APPLE__
  58. #include <cstdlib>
  59. #else
  60. #include <malloc.h> // for mallinfo
  61. #endif
  62. #include <time.h> // for NULL
  63. #include <unistd.h> // for sysconf, _SC_CLK_TCK
  64. #endif
  65. using namespace armarx;
  66. ProcessWatcher::ProcessWatcher(Profiler::ProfilerPtr profiler) :
  67. profiler(profiler)
  68. {
  69. long pid = LogSender::getProcessId();
  70. Hertz = sysconf(_SC_CLK_TCK);
  71. std::stringstream ss;
  72. ss << "/proc/" << pid << "/stat";
  73. processCpuDataFilename = ss.str();
  74. processCpuUsage.processId = pid;
  75. cpuUsageFromProcFile();
  76. }
  77. void ProcessWatcher::start()
  78. {
  79. if (!watcherTask)
  80. {
  81. watcherTask = new PeriodicTask<ProcessWatcher>(this, &ProcessWatcher::watch, 300, false);
  82. }
  83. watcherTask->start();
  84. }
  85. void ProcessWatcher::stop()
  86. {
  87. watcherTask->stop();
  88. watcherTask = nullptr;
  89. }
  90. void ProcessWatcher::addThread(int processId, int threadId)
  91. {
  92. ScopedLock lock(threadWatchListMutex);
  93. ThreadUsage usage;
  94. usage.processId = processId;
  95. usage.threadId = threadId;
  96. usage.lastJiffies = GetThreadJiffies(processId, threadId);
  97. usage.lastUpdate = IceUtil::Time::now();
  98. usage.load = 0;
  99. threadWatchList.insert(usage);
  100. }
  101. void ProcessWatcher::addThread(int threadId)
  102. {
  103. addThread(LogSender::getProcessId(), threadId);
  104. }
  105. void ProcessWatcher::watch()
  106. {
  107. runThreadWatchList();
  108. cpuUsageFromProcFile();
  109. getMemoryUsage();
  110. reportCpuUsage();
  111. reportMemoryUsage();
  112. }
  113. void ProcessWatcher::runThreadWatchList()
  114. {
  115. ScopedLock lock(threadWatchListMutex);
  116. boost::unordered_set<ThreadUsage> newset;
  117. boost::unordered_set<ThreadUsage>::iterator it = threadWatchList.begin();
  118. for (; it != threadWatchList.end(); it++)
  119. {
  120. ThreadUsage usage = *it;
  121. IceUtil::Time queryTime = IceUtil::Time::now();
  122. int curJiffies = GetThreadJiffies(usage.processId, usage.threadId);
  123. if (GetHertz() != -1)
  124. {
  125. usage.load = (curJiffies - usage.lastJiffies) / ((queryTime - usage.lastUpdate).toSecondsDouble() * GetHertz());
  126. }
  127. else
  128. {
  129. usage.load = 0;
  130. }
  131. usage.lastJiffies = curJiffies;
  132. usage.lastUpdate = queryTime;
  133. newset.insert(usage);
  134. }
  135. threadWatchList.swap(newset);
  136. }
  137. #if !defined(_WIN32) && !defined(__APPLE__)
  138. int ProcessWatcher::GetHertz()
  139. {
  140. static Mutex mutex;
  141. ScopedLock lock(mutex);
  142. static int value = -2;
  143. if (value == -2) // init value
  144. {
  145. static std::string _release;
  146. if (_release.empty())
  147. {
  148. struct utsname utsinfo;
  149. uname(&utsinfo);
  150. _release = utsinfo.release;
  151. }
  152. std::string fileName = "/boot/config-" + _release;
  153. std::ifstream configFile(fileName.c_str());
  154. // procFile.open(fileName, ios::out);
  155. if (!configFile.is_open())
  156. {
  157. ARMARX_WARNING_S << "Cannot open " << fileName << " for reading the cpu hertz value";
  158. value = -1;
  159. }
  160. else
  161. {
  162. value = -1;
  163. std::string line;
  164. const std::string searchString = "CONFIG_HZ=";
  165. while (getline(configFile, line))
  166. {
  167. std::size_t pos = line.find(searchString);
  168. if (pos != std::string::npos)
  169. {
  170. std::string hzString = line.substr(pos + searchString.length());
  171. value = atoi(hzString.c_str());
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. return value;
  178. }
  179. void ProcessWatcher::cpuUsageFromProcFile()
  180. {
  181. std::string line;
  182. std::ifstream file(processCpuDataFilename);
  183. if (!file.is_open())
  184. {
  185. ARMARX_WARNING_S << "File " << processCpuDataFilename << " does not exist";
  186. }
  187. std::getline(file, line);
  188. file.close();
  189. std::vector<std::string> pidElements;
  190. boost::split(pidElements, line, ::isspace);
  191. int currentUtime = boost::lexical_cast<int>(pidElements.at(eUTime));
  192. int currentStime = boost::lexical_cast<int>(pidElements.at(eSTime));
  193. int currentCUtime = boost::lexical_cast<int>(pidElements.at(eCUTIME));
  194. int currentCStime = boost::lexical_cast<int>(pidElements.at(eCSTIME));
  195. {
  196. ScopedLock guard{processCpuUsageMutex};
  197. IceUtil::Time queryTime = IceUtil::Time::now();
  198. processCpuUsage.proc_total_time = 100 * (double)((currentStime + currentCStime - processCpuUsage.lastStime - processCpuUsage.lastCStime) + (currentUtime + currentCUtime - processCpuUsage.lastCUtime - processCpuUsage.lastUtime)) / ((queryTime - processCpuUsage.lastUpdate).toSecondsDouble() * Hertz);
  199. processCpuUsage.lastUpdate = queryTime;
  200. processCpuUsage.lastUtime = currentUtime;
  201. processCpuUsage.lastStime = currentStime;
  202. processCpuUsage.lastCUtime = currentCUtime;
  203. processCpuUsage.lastCStime = currentCStime;
  204. }
  205. }
  206. void ProcessWatcher::getMemoryUsage()
  207. {
  208. struct mallinfo _mallinfo;
  209. _mallinfo = mallinfo();
  210. {
  211. ScopedLock guard{processMemoryUsageMutex};
  212. memoryUsage.fastbinBlocks = _mallinfo.fsmblks;
  213. memoryUsage.totalAllocatedSpace = _mallinfo.uordblks;
  214. memoryUsage.totalFreeSpace = _mallinfo.fordblks;
  215. }
  216. }
  217. std::map<int, int> ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
  218. {
  219. std::map<int, int> resultMap;
  220. BOOST_FOREACH(int threadId, threadIds)
  221. {
  222. resultMap[threadId] = 0;
  223. std::stringstream fileName;
  224. fileName << "/proc/" << threadId << "/task/" << threadId << "/stat";
  225. std::ifstream statFile(fileName.str().c_str());
  226. if (!statFile.is_open())
  227. {
  228. ARMARX_WARNING_S << "Cannot open " << fileName.str() << " for reading the hertz value";
  229. resultMap[threadId] = 0;
  230. continue;
  231. }
  232. std::string line;
  233. while (getline(statFile, line))
  234. {
  235. boost::algorithm::trim(line);
  236. using namespace boost;
  237. char_separator<char> sep(" ");
  238. tokenizer< char_separator<char> > tokens(line, sep);
  239. std::vector<std::string> stringVec;
  240. stringVec.assign(tokens.begin(), tokens.end());
  241. int userTimeJiffies = atoi(stringVec.at(13).c_str());
  242. int kernelTimeJiffies = atoi(stringVec.at(14).c_str());
  243. resultMap[threadId] = userTimeJiffies + kernelTimeJiffies;
  244. }
  245. }
  246. return resultMap;
  247. }
  248. #else
  249. int ProcessWatcher::GetHertz()
  250. {
  251. return -1;
  252. }
  253. std::map<int, int> ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
  254. {
  255. return std::map<int, int>();
  256. }
  257. void ProcessWatcher::cpuUsageFromProcFile()
  258. {
  259. }
  260. void ProcessWatcher::getMemoryUsage()
  261. {
  262. }
  263. #endif
  264. bool ThreadUsage::operator <(const ThreadUsage& rhs) const
  265. {
  266. if (processId < rhs.processId)
  267. {
  268. return true;
  269. }
  270. if (threadId < rhs.threadId)
  271. {
  272. return true;
  273. }
  274. return false;
  275. }
  276. bool ThreadUsage::operator ==(const ThreadUsage& rhs) const
  277. {
  278. if (processId == rhs.processId && threadId == rhs.threadId)
  279. {
  280. return true;
  281. }
  282. return false;
  283. }
  284. int ProcessWatcher::GetThreadJiffies(int processId, int threadId)
  285. {
  286. std::map<int, int> result = GetThreadListJiffies(processId, std::vector<int>(1, threadId));
  287. std::map<int, int>::iterator it = result.find(threadId);
  288. if (it == result.end())
  289. {
  290. return 0;
  291. }
  292. return it->second;
  293. }
  294. void ProcessWatcher::reportCpuUsage()
  295. {
  296. ScopedLock guard{processCpuUsageMutex};
  297. profiler->logProcessCpuUsage(processCpuUsage.proc_total_time);
  298. }
  299. void ProcessWatcher::reportMemoryUsage()
  300. {
  301. ScopedLock guard{processMemoryUsageMutex};
  302. profiler->logProcessMemoryUsage(memoryUsage.totalFreeSpace);
  303. }
  304. double ProcessWatcher::getThreadLoad(int threadId)
  305. {
  306. return getThreadLoad(LogSender::getProcessId(), threadId);
  307. }
  308. double ProcessWatcher::getThreadLoad(int processId, int threadId)
  309. {
  310. ThreadUsage query;
  311. query.processId = processId;
  312. query.threadId = threadId;
  313. ScopedLock lock(threadWatchListMutex);
  314. BOOST_AUTO(it, threadWatchList.find(query));
  315. if (it != threadWatchList.end())
  316. {
  317. return it->load * 100;
  318. }
  319. else
  320. {
  321. return 0.0;
  322. }
  323. }
  324. armarx::CpuUsage ProcessWatcher::getProcessCpuUsage()
  325. {
  326. ScopedLock guard{processCpuUsageMutex};
  327. return processCpuUsage;
  328. }
  329. void ProcessWatcher::removeThread(int processId, int threadId)
  330. {
  331. ThreadUsage query;
  332. query.processId = processId;
  333. query.threadId = threadId;
  334. ScopedLock lock(threadWatchListMutex);
  335. threadWatchList.erase(query);
  336. }
  337. void ProcessWatcher::removeThread(int threadId)
  338. {
  339. removeThread(LogSender::getProcessId(), threadId);
  340. }