ProcessWatcher.cpp
Go to the documentation of this 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
25#include "ProcessWatcher.h"
26
27#include <ctype.h> // for isspace
28
29#include <fstream>
30#include <memory> // for allocator, operator==
31#include <sstream> // for operator<<, ifstream, etc
32#include <unordered_set>
33
34#include <sys/utsname.h> // for uname, utsname
35#include <unistd.h> // for sysconf, _SC_CLK_TCK
36
37#include <IceUtil/Handle.h> // for HandleBase
38
39#include <SimoxUtility/algorithm/string/string_tools.h>
40
41#include "ArmarXCore/core/logging/LogSender.h" // for LogSender
42#include "ArmarXCore/core/logging/Logging.h" // for ARMARX_WARNING_S
45#include "ArmarXCore/interface/core/ThreadingIceBase.h" // for upCast
46
48#include <ext/alloc_traits.h>
49#include <malloc.h> // for mallinfo
50
51namespace armarx
52{
54 {
55 long pid = LogSender::getProcessId();
56 Hertz = sysconf(_SC_CLK_TCK);
57 std::stringstream ss;
58 ss << "/proc/" << pid << "/stat";
59 processCpuDataFilename = ss.str();
60 processCpuUsage.processId = pid;
61 cpuUsageFromProcFile();
62 }
63
64 void
66 {
67 if (!watcherTask)
68 {
69 watcherTask =
70 new PeriodicTask<ProcessWatcher>(this, &ProcessWatcher::watch, 300, false);
71 }
72
73 watcherTask->start();
74 }
75
76 void
78 {
79 watcherTask->stop();
80 watcherTask = nullptr;
81 }
82
83 void
84 ProcessWatcher::addThread(int processId, int threadId)
85 {
87 std::unique_lock lock(threadWatchListMutex);
88 ThreadUsage usage;
89 usage.processId = processId;
90 usage.threadId = threadId;
91 usage.lastJiffies = GetThreadJiffies(processId, threadId);
92 usage.lastUpdate = IceUtil::Time::now();
93 usage.load = 0;
94 threadWatchList.insert(usage);
95 }
96
97 void
99 {
101 }
102
103 void
104 ProcessWatcher::watch()
105 {
107 runThreadWatchList();
108 cpuUsageFromProcFile();
109 getMemoryUsage();
112 }
113
114 void
115 ProcessWatcher::runThreadWatchList()
116 {
118 std::unique_lock lock(threadWatchListMutex);
119 std::unordered_set<ThreadUsage> newset;
120 std::unordered_set<ThreadUsage>::iterator it = threadWatchList.begin();
121
122 for (; it != threadWatchList.end(); it++)
123 {
125 ThreadUsage usage = *it;
126 IceUtil::Time queryTime = IceUtil::Time::now();
127 int curJiffies = GetThreadJiffies(usage.processId, usage.threadId);
128
129 if (GetHertz() != -1)
130 {
131 usage.load = (curJiffies - usage.lastJiffies) /
132 ((queryTime - usage.lastUpdate).toSecondsDouble() * GetHertz());
133 }
134 else
135 {
136 usage.load = 0;
137 }
138
139 usage.lastJiffies = curJiffies;
140 usage.lastUpdate = queryTime;
141 newset.insert(usage);
142 }
143
144 threadWatchList.swap(newset);
145 }
146
147
148#if !defined(_WIN32) && !defined(__APPLE__)
149 int
151 {
153 static std::mutex mutex;
154 std::unique_lock lock(mutex);
155 static int value = -2;
156
157 if (value == -2) // init value
158 {
160 static std::string _release;
161
162 if (_release.empty())
163 {
164 struct utsname utsinfo;
165 uname(&utsinfo);
166 _release = utsinfo.release;
167 }
168
169 std::string fileName = "/boot/config-" + _release;
170 std::ifstream configFile(fileName.c_str());
171
173 // procFile.open(fileName, ios::out);
174 if (!configFile.is_open())
175 {
176 ARMARX_WARNING_S << "Cannot open " << fileName
177 << " for reading the cpu hertz value";
178 value = -1;
179 }
180 else
181 {
182 value = -1;
183 std::string line;
184 const std::string searchString = "CONFIG_HZ=";
185
186 while (getline(configFile, line))
187 {
189 std::size_t pos = line.find(searchString);
190
191 if (pos != std::string::npos)
192 {
193 std::string hzString = line.substr(pos + searchString.length());
194 value = std::stoi(hzString);
195 break;
196 }
197 }
198 }
199 }
200
201 return value;
202 }
203
204 void
205 ProcessWatcher::cpuUsageFromProcFile()
206 {
208 std::string line;
209
210 std::ifstream file(processCpuDataFilename);
211
212 if (!file.is_open())
213 {
214 ARMARX_WARNING_S << "File " << processCpuDataFilename << " does not exist";
215 }
216
217
218 std::getline(file, line);
219 file.close();
220
221 std::vector<std::string> pidElements = simox::alg::split(line, "\t ");
222
223 int currentUtime = std::stoi(pidElements.at(eUTime));
224 int currentStime = std::stoi(pidElements.at(eSTime));
225
226 int currentCUtime = std::stoi(pidElements.at(eCUTIME));
227 int currentCStime = std::stoi(pidElements.at(eCSTIME));
228 {
230 std::unique_lock guard{processCpuUsageMutex};
231
232 IceUtil::Time queryTime = IceUtil::Time::now();
233
234 processCpuUsage.proc_total_time =
235 100 *
236 (double)((currentStime + currentCStime - processCpuUsage.lastStime -
237 processCpuUsage.lastCStime) +
238 (currentUtime + currentCUtime - processCpuUsage.lastCUtime -
239 processCpuUsage.lastUtime)) /
240 ((queryTime - processCpuUsage.lastUpdate).toSecondsDouble() * Hertz);
241
242 processCpuUsage.lastUpdate = queryTime;
243 processCpuUsage.lastUtime = currentUtime;
244 processCpuUsage.lastStime = currentStime;
245
246 processCpuUsage.lastCUtime = currentCUtime;
247 processCpuUsage.lastCStime = currentCStime;
248 }
249 }
250
251 void
252 ProcessWatcher::getMemoryUsage()
253 {
255 struct mallinfo _mallinfo;
256
257 _mallinfo = mallinfo();
258
259 {
260 std::unique_lock guard{processMemoryUsageMutex};
261 memoryUsage.fastbinBlocks = _mallinfo.fsmblks;
262 memoryUsage.totalAllocatedSpace = _mallinfo.uordblks;
263 memoryUsage.totalFreeSpace = _mallinfo.fordblks;
264 }
265 }
266
267 std::map<int, int>
268 ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
269 {
271 std::map<int, int> resultMap;
272 for (int threadId : threadIds)
273 {
275 resultMap[threadId] = 0;
276 std::stringstream fileName;
277 fileName << "/proc/" << threadId << "/task/" << threadId << "/stat";
278 std::ifstream statFile(fileName.str().c_str());
279
280 if (!statFile.is_open())
281 {
282 ARMARX_WARNING_S << "Cannot open " << fileName.str()
283 << " for reading the hertz value";
284 resultMap[threadId] = 0;
285 continue;
286 }
287
288 std::string line;
289
290 while (getline(statFile, line))
291 {
293 simox::alg::trim(line);
294
295 std::vector<std::string> stringVec = simox::alg::split(line, " ");
296
297 ARMARX_CHECK_LESS(14, stringVec.size());
298 int userTimeJiffies = atoi(stringVec.at(13).c_str());
299 int kernelTimeJiffies = atoi(stringVec.at(14).c_str());
300
301 resultMap[threadId] = userTimeJiffies + kernelTimeJiffies;
302 }
303 }
304
305 return resultMap;
306 }
307#else
308
309 int
311 {
312 return -1;
313 }
314
315 std::map<int, int>
316 ProcessWatcher::GetThreadListJiffies(int processId, std::vector<int> threadIds)
317 {
318 return std::map<int, int>();
319 }
320
321 void
322 ProcessWatcher::cpuUsageFromProcFile()
323 {
324 }
325
326 void
327 ProcessWatcher::getMemoryUsage()
328 {
329 }
330
331#endif
332
333 bool
334 ThreadUsage::operator<(const ThreadUsage& rhs) const
335 {
336 if (processId < rhs.processId)
337 {
338 return true;
339 }
340
341 if (threadId < rhs.threadId)
342 {
343 return true;
344 }
345
346 return false;
347 }
348
349 bool
351 {
352 if (processId == rhs.processId && threadId == rhs.threadId)
353 {
354 return true;
355 }
356
357 return false;
358 }
359
360 int
361 ProcessWatcher::GetThreadJiffies(int processId, int threadId)
362 {
364 std::map<int, int> result = GetThreadListJiffies(processId, std::vector<int>(1, threadId));
365 std::map<int, int>::iterator it = result.find(threadId);
366
367 if (it == result.end())
368 {
369 return 0;
370 }
371
372 return it->second;
373 }
374
375 void
377 {
378 std::unique_lock guard{processCpuUsageMutex};
379 profiler->logProcessCpuUsage(processCpuUsage.proc_total_time);
380 }
381
382 void
384 {
385 std::unique_lock guard{processMemoryUsageMutex};
386 profiler->logProcessMemoryUsage(memoryUsage.totalFreeSpace);
387 }
388
389 double
391 {
392 return getThreadLoad(LogSender::getProcessId(), threadId);
393 }
394
395 double
396 ProcessWatcher::getThreadLoad(int processId, int threadId)
397 {
399 ThreadUsage query;
400 query.processId = processId;
401 query.threadId = threadId;
402 std::unique_lock lock(threadWatchListMutex);
403 auto it = threadWatchList.find(query);
404
406 if (it != threadWatchList.end())
407 {
408 return it->load * 100;
409 }
410 else
411 {
412 return 0.0;
413 }
414 }
415
418 {
419 std::unique_lock guard{processCpuUsageMutex};
420 return processCpuUsage;
421 }
422
423 void
424 ProcessWatcher::removeThread(int processId, int threadId)
425 {
427 ThreadUsage query;
428 query.processId = processId;
429 query.threadId = threadId;
430 std::unique_lock lock(threadWatchListMutex);
431 threadWatchList.erase(query);
432 }
433
434 void
436 {
438 }
439} // namespace armarx
static long getProcessId()
The periodic task executes one thread method repeatedly using the time period specified in the constr...
ProcessWatcher(Profiler::ProfilerPtr profiler)
void removeThread(int processId, int threadId)
static std::map< int, int > GetThreadListJiffies(int processId, std::vector< int > threadIds)
static int GetThreadJiffies(int processId, int threadId)
armarx::CpuUsage getProcessCpuUsage()
double getThreadLoad(int processId, int threadId)
void addThread(int processId, int threadId)
#define ARMARX_CHECK_LESS(lhs, rhs)
This macro evaluates whether lhs is less (<) than rhs and if it turns out to be false it will throw a...
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
std::shared_ptr< Profiler > ProfilerPtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool operator==(const ThreadUsage &rhs) const
bool operator<(const ThreadUsage &rhs) const
IceUtil::Time lastUpdate
#define ARMARX_TRACE
Definition trace.h:77