Fawkes API  Fawkes Development Version
procrrd_thread.cpp
1 
2 /***************************************************************************
3  * procrrd_thread.cpp - Fawkes Proc RRD Thread
4  *
5  * Created: Mon Dec 17 12:54:00 2012
6  * Copyright 2012 Bastian Klingen
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "procrrd_thread.h"
24 
25 #include <utils/time/wait.h>
26 #include <stdio.h>
27 #include <dirent.h>
28 #include <string.h>
29 
30 using namespace fawkes;
31 
32 #define PROC_CONF_PREFIX "/plugins/procrrd/processes/"
33 
34 /** @class ProcRRDThread "procrrd_thread.h"
35  * Proc RRD Thread.
36  * This thread queries performance data from system every few seconds and
37  * writes it to RRD databases. The sampling rate can be set via the config.
38  *
39  * @author Bastian Klingen
40  */
41 
42 /** Constructor. */
44  : Thread("ProcRRDThread", Thread::OPMODE_CONTINUOUS),
45  ConfigurationChangeHandler(PROC_CONF_PREFIX)
46 {
48 }
49 
50 
51 /** Destructor. */
53 {
54 }
55 
56 
57 void
59 {
60  __samplerate = 10;
61  try {
62  __samplerate = config->get_uint("/plugins/procrrd/samplerate");
63  } catch (Exception &e) {}
64 
65  __timewait = new TimeWait(clock, __samplerate * 1000000);
66  __netinterface = "wlan0";
67  try {
68  __netinterface = config->get_string("/plugins/procrrd/netinterface");
69  } catch (Exception &e) {}
70 
71  __lastcpu = new unsigned long int[11];
72  get_cpu(__lastcpu);
73 
74  __net_recv_graph = NULL;
75  __net_trans_graph = NULL;
76  std::vector<RRDDataSource> rrds;
77  // /proc/net/dev
78  // use data for net_interface
79  // Receive bytes
80  rrds.push_back(RRDDataSource("net_recv_bytes", RRDDataSource::COUNTER));
81  // Receive packets
82  rrds.push_back(RRDDataSource("net_recv_packets", RRDDataSource::COUNTER));
83  // Receive errs
84  rrds.push_back(RRDDataSource("net_recv_errors", RRDDataSource::COUNTER));
85  // Transmit bytes
86  rrds.push_back(RRDDataSource("net_trans_bytes", RRDDataSource::COUNTER));
87  // Transmit packets
88  rrds.push_back(RRDDataSource("net_trans_packets", RRDDataSource::COUNTER));
89  // Transmit errs
90  rrds.push_back(RRDDataSource("net_trans_errors", RRDDataSource::COUNTER));
91  __net_rrd = new RRDDefinition("network", rrds);
92 
93  try {
94  rrd_manager->add_rrd(__net_rrd);
95  } catch (Exception &e) {
96  finalize();
97  throw;
98  }
99 
100  std::vector<RRDGraphDataDefinition> defs;
101  std::vector<RRDGraphElement *> els;
102 
103  defs.push_back(RRDGraphDataDefinition("net_recv_bytes", RRDArchive::AVERAGE,
104  __net_rrd));
105  defs.push_back(RRDGraphDataDefinition("net_recv_packets", RRDArchive::AVERAGE,
106  __net_rrd));
107  defs.push_back(RRDGraphDataDefinition("net_recv_errors", RRDArchive::AVERAGE,
108  __net_rrd));
109 
110  els.push_back(new RRDGraphLine("net_recv_bytes", 1, "006400", "Bytes"));
111  els.push_back(new RRDGraphGPrint("net_recv_bytes", RRDArchive::LAST,
112  " Current\\:%8.2lf %s"));
113  els.push_back(new RRDGraphGPrint("net_recv_bytes", RRDArchive::AVERAGE,
114  "Average\\:%8.2lf %s"));
115  els.push_back(new RRDGraphGPrint("net_recv_bytes", RRDArchive::MAX,
116  "Maximum\\:%8.2lf %s\\n"));
117 
118  els.push_back(new RRDGraphLine("net_recv_packets", 1, "808000", "Packets"));
119  els.push_back(new RRDGraphGPrint("net_recv_packets", RRDArchive::LAST,
120  "Current\\:%8.2lf %s"));
121  els.push_back(new RRDGraphGPrint("net_recv_packets", RRDArchive::AVERAGE,
122  "Average\\:%8.2lf %s"));
123  els.push_back(new RRDGraphGPrint("net_recv_packets", RRDArchive::MAX,
124  "Maximum\\:%8.2lf %s\\n"));
125 
126  els.push_back(new RRDGraphLine("net_recv_errors", 1, "FF0000", "Errors"));
127  els.push_back(new RRDGraphGPrint("net_recv_errors", RRDArchive::LAST,
128  " Current\\:%8.2lf %s"));
129  els.push_back(new RRDGraphGPrint("net_recv_errors", RRDArchive::AVERAGE,
130  "Average\\:%8.2lf %s"));
131  els.push_back(new RRDGraphGPrint("net_recv_errors", RRDArchive::MAX,
132  "Maximum\\:%8.2lf %s\\n"));
133 
134  __net_recv_graph = new RRDGraphDefinition("network_recv", __net_rrd,
135  "Network Receive", "",
136  defs, els);
137 
138  defs.clear();
139  els.clear();
140 
141  defs.push_back(RRDGraphDataDefinition("net_trans_bytes", RRDArchive::AVERAGE,
142  __net_rrd));
143  defs.push_back(RRDGraphDataDefinition("net_trans_packets", RRDArchive::AVERAGE,
144  __net_rrd));
145  defs.push_back(RRDGraphDataDefinition("net_trans_errors", RRDArchive::AVERAGE,
146  __net_rrd));
147 
148  els.push_back(new RRDGraphLine("net_trans_bytes", 1, "006400", "Bytes"));
149  els.push_back(new RRDGraphGPrint("net_trans_bytes", RRDArchive::LAST,
150  " Current\\:%8.2lf %s"));
151  els.push_back(new RRDGraphGPrint("net_trans_bytes", RRDArchive::AVERAGE,
152  "Average\\:%8.2lf %s"));
153  els.push_back(new RRDGraphGPrint("net_trans_bytes", RRDArchive::MAX,
154  "Maximum\\:%8.2lf %s\\n"));
155 
156  els.push_back(new RRDGraphLine("net_trans_packets", 1, "808000", "Packets"));
157  els.push_back(new RRDGraphGPrint("net_trans_packets", RRDArchive::LAST,
158  "Current\\:%8.2lf %s"));
159  els.push_back(new RRDGraphGPrint("net_trans_packets", RRDArchive::AVERAGE,
160  "Average\\:%8.2lf %s"));
161  els.push_back(new RRDGraphGPrint("net_trans_packets", RRDArchive::MAX,
162  "Maximum\\:%8.2lf %s\\n"));
163 
164  els.push_back(new RRDGraphLine("net_trans_errors", 1, "FF0000", "Errors"));
165  els.push_back(new RRDGraphGPrint("net_trans_errors", RRDArchive::LAST,
166  " Current\\:%8.2lf %s"));
167  els.push_back(new RRDGraphGPrint("net_trans_errors", RRDArchive::AVERAGE,
168  "Average\\:%8.2lf %s"));
169  els.push_back(new RRDGraphGPrint("net_trans_errors", RRDArchive::MAX,
170  "Maximum\\:%8.2lf %s\\n"));
171 
172  __net_trans_graph = new RRDGraphDefinition("network_trans", __net_rrd,
173  "Network Transmit", "",
174  defs, els);
175 
176  try {
177  rrd_manager->add_graph(__net_recv_graph);
178  rrd_manager->add_graph(__net_trans_graph);
179  } catch (Exception &e) {
180  finalize();
181  throw;
182  }
183 
184  std::string procprefix = PROC_CONF_PREFIX;
185 
186  try {
187  std::string selfid = get_process_id("fawkes");
188  add_process((procprefix+"fawkes").c_str(), selfid, "fawkes");
189  } catch (Exception &e) {
190  logger->log_warn(name(), "Failed to add process: Fawkes, exception follows");
191  logger->log_warn(name(), e);
192  finalize();
193  throw;
194  }
195 
196  Configuration::ValueIterator *i = config->search(procprefix.c_str());
197  while (i->next()) {
198 
199  if (! i->is_string()) {
200  logger->log_warn(name(), "Entry %s is not a string, but of type %s, "
201  "ignoring", i->path(), i->type());
202  continue;
203  }
204 
205 
206  std::string name = i->get_string();
207  try {
208  std::string pid = get_process_id(name.c_str());
209  add_process(i->path(), pid, name);
210  } catch (Exception &e) {
211  logger->log_warn(this->name(), "Failed to add process: %s, exception follows", name.c_str());
212  logger->log_warn(this->name(), e);
213  finalize();
214  throw;
215  }
216  }
217 
218  std::string p;
219  ProcessMap::iterator pi = __processes.begin();
220  p = pi->second.name;
221  ++pi;
222  for (; pi != __processes.end(); ++pi) {
223  p += ", "+pi->second.name;
224  }
225 
226 
227 
228  logger->log_info(name(), "ProcRRD logging network interface %s and processes %s with a samplerate of %lu second(s)",
229  __netinterface.c_str(), p.c_str(), __samplerate);
230 
231  config->add_change_handler(this);
232 }
233 
234 
235 void
237 {
238  config->rem_change_handler(this);
239  delete __timewait;
240 
241  rrd_manager->remove_rrd(__net_rrd);
242 
243  for (ProcessMap::iterator i = __processes.begin(); i != __processes.end(); ++i) {
244  ProcessInfo &info = i->second;
245  rrd_manager->remove_rrd(info.rrd);
246  delete info.cpu_graph;
247  delete info.mem_graph;
248  delete info.io_read_graph;
249  delete info.io_write_graph;
250  delete info.rrd;
251  }
252  __processes.clear();
253 
254  delete __net_recv_graph;
255  delete __net_trans_graph;
256  delete __net_rrd;
257 }
258 
259 void
260 ProcRRDThread::add_process(const char *path, std::string pid, std::string name)
261 {
262  if (__processes.find(path) != __processes.end()) {
263  throw Exception("Process stats for config %s already monitored", path);
264  }
265  if (pid == "") {
266  throw Exception("No PID set.");
267  }
268 
269  ProcessInfo info;
270  info.last_cpu = new unsigned long int[3];
271  FILE *file;
272  file = fopen(("/proc/"+pid+"/stat").c_str(), "r");
273  if(file) {
274  // /proc/PID/stat
275  // 14th (utime %lu) and 15th (stime %lu) value
276  // number of jiffies that the process has executed in user mode and kernel mode
277  fscanf(file, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d %*u %*u %*u %*d", info.last_cpu+1, info.last_cpu+2);
278  // process cpu = utime + stime
279  info.last_cpu[0] = info.last_cpu[1] + info.last_cpu[2];
280  fclose(file);
281  }
282 
283  std::vector<RRDDataSource> rrds;
284  // totalcpu = sum of /proc/stat line 1
285  // usage% = 100 * process cpu / totalcpu
286  rrds.push_back(RRDDataSource("cpu_usage", RRDDataSource::GAUGE));
287  // Threads: number of currently running threads
288  rrds.push_back(RRDDataSource("cpu_threads", RRDDataSource::GAUGE));
289  // /proc/PID/status
290  // VmPeak: maximum virtual memory space used by the process, in kB (1024 bytes)
291  rrds.push_back(RRDDataSource("mem_maxvirt", RRDDataSource::GAUGE));
292  // VmSize: current virtual memory space used by the process, in kB (1024 bytes)
293  rrds.push_back(RRDDataSource("mem_curvirt", RRDDataSource::GAUGE));
294  // VmRss: amount of memory that have been mapped into the process' address space, or its resident set size, in kB (1024 bytes)
295  rrds.push_back(RRDDataSource("mem_rss", RRDDataSource::GAUGE));
296 
297  // /proc/PID/io
298  // rchar: number of bytes the process read, using any read-like system call (from files, pipes, tty...).
299  rrds.push_back(RRDDataSource("io_rchar", RRDDataSource::COUNTER));
300  // wchar: number of bytes the process wrote using any write-like system call.
301  rrds.push_back(RRDDataSource("io_wchar", RRDDataSource::COUNTER));
302  // syscr: number of read-like system call invocations that the process performed.
303  rrds.push_back(RRDDataSource("io_syscr", RRDDataSource::COUNTER));
304  // syscw: number of write-like system call invocations that the process performed.
305  rrds.push_back(RRDDataSource("io_syscw", RRDDataSource::COUNTER));
306  // read_bytes: number of bytes the process directly read from disk.
307  rrds.push_back(RRDDataSource("io_read_bytes", RRDDataSource::COUNTER));
308  // write_bytes: number of bytes the process originally dirtied in the page-cache (assuming they will go to disk later).
309  rrds.push_back(RRDDataSource("io_write_bytes", RRDDataSource::COUNTER));
310  // cancelled_write_bytes: number of bytes the process "un-dirtied" - e.g. using an "ftruncate" call that truncated pages from the page-cache.
311  rrds.push_back(RRDDataSource("io_cancelled_write", RRDDataSource::COUNTER));
312 
313  info.pid = pid;
314  info.name = name;
315 
316  info.rrd_name = info.name + "_" + info.pid;
317  size_t pos = 0;
318  size_t at;
319  while((at = info.rrd_name.find_first_of(" .-", pos)) != std::string::npos) {
320  info.rrd_name.replace(at, 1, "_");
321  pos = pos + 1;
322  }
323 
324  info.rrd = new RRDDefinition(info.rrd_name.c_str(), rrds);
325 
326  std::vector<RRDGraphDataDefinition> defs;
327  std::vector<RRDGraphElement *> els;
328 
329  defs.push_back(RRDGraphDataDefinition("cpu_usage", RRDArchive::AVERAGE,
330  info.rrd));
331  defs.push_back(RRDGraphDataDefinition("cpu_threads", RRDArchive::AVERAGE,
332  info.rrd));
333 
334  els.push_back(new RRDGraphLine("cpu_usage", 1, "FF0000", "Usage %"));
335  els.push_back(new RRDGraphGPrint("cpu_usage", RRDArchive::LAST,
336  "Current\\:%8.2lf %s"));
337  els.push_back(new RRDGraphGPrint("cpu_usage", RRDArchive::AVERAGE,
338  "Average\\:%8.2lf %s"));
339  els.push_back(new RRDGraphGPrint("cpu_usage", RRDArchive::MAX,
340  "Maximum\\:%8.2lf %s\\n"));
341 
342  els.push_back(new RRDGraphLine("cpu_threads", 1, "008800", "Threads"));
343  els.push_back(new RRDGraphGPrint("cpu_threads", RRDArchive::LAST,
344  "Current\\:%8.2lf %s"));
345  els.push_back(new RRDGraphGPrint("cpu_threads", RRDArchive::AVERAGE,
346  "Average\\:%8.2lf %s"));
347  els.push_back(new RRDGraphGPrint("cpu_threads", RRDArchive::MAX,
348  "Maximum\\:%8.2lf %s\\n"));
349 
350  std::string g1name = info.rrd_name + "_cpu";
351  std::string g1title = std::string("CPU Usage in % and Number Threads for ") + info.name + "(" + pid + ")";
352  info.cpu_graph = new RRDGraphDefinition(g1name.c_str(), info.rrd,
353  g1title.c_str(), "", defs, els);
354 
355 
356  defs.clear(); els.clear();
357  defs.push_back(RRDGraphDataDefinition("mem_maxvirt_kb", RRDArchive::AVERAGE,
358  info.rrd, "mem_maxvirt"));
359  defs.push_back(RRDGraphDataDefinition("mem_curvirt_kb", RRDArchive::AVERAGE,
360  info.rrd, "mem_curvirt"));
361  defs.push_back(RRDGraphDataDefinition("mem_rss_kb", RRDArchive::AVERAGE,
362  info.rrd, "mem_rss"));
363  defs.push_back(RRDGraphDataDefinition("mem_maxvirt", "mem_maxvirt_kb,1024,*"));
364  defs.push_back(RRDGraphDataDefinition("mem_curvirt", "mem_curvirt_kb,1024,*"));
365  defs.push_back(RRDGraphDataDefinition("mem_rss", "mem_rss_kb,1024,*"));
366 
367  els.push_back(new RRDGraphArea("mem_maxvirt", "3B7AD9", "VmPeak"));
368  els.push_back(new RRDGraphGPrint("mem_maxvirt", RRDArchive::LAST,
369  "Current\\:%8.2lf %s"));
370  els.push_back(new RRDGraphGPrint("mem_maxvirt", RRDArchive::AVERAGE,
371  "Average\\:%8.2lf %s"));
372  els.push_back(new RRDGraphGPrint("mem_maxvirt", RRDArchive::MAX,
373  "Maximum\\:%8.2lf %s\\n"));
374 
375  els.push_back(new RRDGraphArea("mem_curvirt", "6FD1BF", "VmSize"));
376  els.push_back(new RRDGraphGPrint("mem_curvirt", RRDArchive::LAST,
377  "Current\\:%8.2lf %s"));
378  els.push_back(new RRDGraphGPrint("mem_curvirt", RRDArchive::AVERAGE,
379  "Average\\:%8.2lf %s"));
380  els.push_back(new RRDGraphGPrint("mem_curvirt", RRDArchive::MAX,
381  "Maximum\\:%8.2lf %s\\n"));
382 
383  els.push_back(new RRDGraphArea("mem_rss", "0E6E5C", "VmRSS"));
384  els.push_back(new RRDGraphGPrint("mem_rss", RRDArchive::LAST,
385  " Current\\:%8.2lf %s"));
386  els.push_back(new RRDGraphGPrint("mem_rss", RRDArchive::AVERAGE,
387  "Average\\:%8.2lf %s"));
388  els.push_back(new RRDGraphGPrint("mem_rss", RRDArchive::MAX,
389  "Maximum\\:%8.2lf %s\\n"));
390 
391  std::string g2name = info.rrd_name + "_memory";
392  std::string g2title = std::string("Memory Usage for ") + info.name + "(" + pid + ")";
393  info.mem_graph = new RRDGraphDefinition(g2name.c_str(), info.rrd,
394  g2title.c_str(), "Bytes", defs, els);
395 
396 
397  defs.clear();
398  els.clear();
399  defs.push_back(RRDGraphDataDefinition("io_rchar", RRDArchive::AVERAGE,
400  info.rrd));
401  defs.push_back(RRDGraphDataDefinition("io_syscr", RRDArchive::AVERAGE,
402  info.rrd));
403  defs.push_back(RRDGraphDataDefinition("io_read_bytes", RRDArchive::AVERAGE,
404  info.rrd));
405 
406  els.push_back(new RRDGraphLine("io_rchar", 1, "556B2F", "rchar"));
407  els.push_back(new RRDGraphGPrint("io_rchar", RRDArchive::LAST,
408  "Current\\:%8.2lf %s"));
409  els.push_back(new RRDGraphGPrint("io_rchar", RRDArchive::AVERAGE,
410  "Average\\:%8.2lf %s"));
411  els.push_back(new RRDGraphGPrint("io_rchar", RRDArchive::MAX,
412  "Maximum\\:%8.2lf %s\\n"));
413 
414  els.push_back(new RRDGraphLine("io_syscr", 1, "9ACD32", "syscr"));
415  els.push_back(new RRDGraphGPrint("io_syscr", RRDArchive::LAST,
416  "Current\\:%8.2lf %s"));
417  els.push_back(new RRDGraphGPrint("io_syscr", RRDArchive::AVERAGE,
418  "Average\\:%8.2lf %s"));
419  els.push_back(new RRDGraphGPrint("io_syscr", RRDArchive::MAX,
420  "Maximum\\:%8.2lf %s\\n"));
421 
422  els.push_back(new RRDGraphLine("io_read_bytes", 1, "98FB98", "bytes"));
423  els.push_back(new RRDGraphGPrint("io_read_bytes", RRDArchive::LAST,
424  "Current\\:%8.2lf %s"));
425  els.push_back(new RRDGraphGPrint("io_read_bytes", RRDArchive::AVERAGE,
426  "Average\\:%8.2lf %s"));
427  els.push_back(new RRDGraphGPrint("io_read_bytes", RRDArchive::MAX,
428  "Maximum\\:%8.2lf %s\\n"));
429 
430  std::string g3name = info.rrd_name + "_io_read";
431  std::string g3title = std::string("IO Read Operations for ") + info.name + "(" + pid + ")";
432  info.io_read_graph = new RRDGraphDefinition(g3name.c_str(), info.rrd,
433  g3title.c_str(), "Bytes", defs, els);
434 
435  defs.clear();
436  els.clear();
437  defs.push_back(RRDGraphDataDefinition("io_wchar", RRDArchive::AVERAGE,
438  info.rrd));
439  defs.push_back(RRDGraphDataDefinition("io_syscw", RRDArchive::AVERAGE,
440  info.rrd));
441  defs.push_back(RRDGraphDataDefinition("io_write_bytes", RRDArchive::AVERAGE,
442  info.rrd));
443  defs.push_back(RRDGraphDataDefinition("io_cancelled_write", RRDArchive::AVERAGE,
444  info.rrd));
445 
446  els.push_back(new RRDGraphLine("io_wchar", 1, "800000", "wchar"));
447  els.push_back(new RRDGraphGPrint("io_wchar", RRDArchive::LAST,
448  " Current\\:%8.2lf %s"));
449  els.push_back(new RRDGraphGPrint("io_wchar", RRDArchive::AVERAGE,
450  "Average\\:%8.2lf %s"));
451  els.push_back(new RRDGraphGPrint("io_wchar", RRDArchive::MAX,
452  "Maximum\\:%8.2lf %s\\n"));
453 
454  els.push_back(new RRDGraphLine("io_syscw", 1, "FF0000", "syscw"));
455  els.push_back(new RRDGraphGPrint("io_syscw", RRDArchive::LAST,
456  " Current\\:%8.2lf %s"));
457  els.push_back(new RRDGraphGPrint("io_syscw", RRDArchive::AVERAGE,
458  "Average\\:%8.2lf %s"));
459  els.push_back(new RRDGraphGPrint("io_syscw", RRDArchive::MAX,
460  "Maximum\\:%8.2lf %s\\n"));
461 
462  els.push_back(new RRDGraphLine("io_write_bytes", 1, "FF8C00", "bytes"));
463  els.push_back(new RRDGraphGPrint("io_write_bytes", RRDArchive::LAST,
464  " Current\\:%8.2lf %s"));
465  els.push_back(new RRDGraphGPrint("io_write_bytes", RRDArchive::AVERAGE,
466  "Average\\:%8.2lf %s"));
467  els.push_back(new RRDGraphGPrint("io_write_bytes", RRDArchive::MAX,
468  "Maximum\\:%8.2lf %s\\n"));
469 
470  els.push_back(new RRDGraphLine("io_cancelled_write", 1, "FFA07A", "cancelled"));
471  els.push_back(new RRDGraphGPrint("io_cancelled_write", RRDArchive::LAST,
472  "Current\\:%8.2lf %s"));
473  els.push_back(new RRDGraphGPrint("io_cancelled_write", RRDArchive::AVERAGE,
474  "Average\\:%8.2lf %s"));
475  els.push_back(new RRDGraphGPrint("io_cancelled_write", RRDArchive::MAX,
476  "Maximum\\:%8.2lf %s\\n"));
477 
478  std::string g4name = info.rrd_name + "_io_write";
479  std::string g4title = std::string("IO Write Operations for ") + info.name + "(" + pid + ")";
480  info.io_write_graph = new RRDGraphDefinition(g4name.c_str(), info.rrd,
481  g4title.c_str(), "Bytes", defs, els);
482  rrd_manager->add_rrd(info.rrd);
483  try {
484  rrd_manager->add_graph(info.cpu_graph);
485  rrd_manager->add_graph(info.mem_graph);
486  rrd_manager->add_graph(info.io_read_graph);
487  rrd_manager->add_graph(info.io_write_graph);
488 
489  __processes[path] = info;
490  logger->log_info(this->name(), "Started monitoring process: %s (PID: %s)",
491  info.name.c_str(), info.pid.c_str());
492  } catch (Exception &e) {
493  rrd_manager->remove_rrd(info.rrd);
494  delete info.cpu_graph;
495  delete info.mem_graph;
496  delete info.io_read_graph;
497  delete info.io_write_graph;
498  delete info.rrd;
499  throw;
500  }
501 }
502 
503 
504 void
505 ProcRRDThread::remove_process(const char *path)
506 {
507  if (__processes.find(path) != __processes.end()) {
508  ProcessInfo &info = __processes[path];
509  rrd_manager->remove_rrd(info.rrd);
510  delete info.cpu_graph;
511  delete info.mem_graph;
512  delete info.io_read_graph;
513  delete info.io_write_graph;
514  delete info.rrd;
515 
516  logger->log_info(name(), "Stopped monitoring process: %s (PID: %s)",
517  info.name.c_str(), info.pid.c_str());
518  __processes.erase(path);
519  }
520 }
521 
522 std::string
523 ProcRRDThread::get_process_id(const char *process)
524 {
525  DIR *dir_p;
526  FILE *file;
527  struct dirent *dir_entry_p;
528  std::string result;
529  result="";
530 
531  // Open /proc/ directory
532  dir_p = opendir("/proc/");
533  // Reading /proc/ entries
534  while(NULL != (dir_entry_p = readdir(dir_p))) {
535  // Checking for numbered directories
536  if (strspn(dir_entry_p->d_name, "0123456789") == strlen(dir_entry_p->d_name)) {
537  std::string f = "/proc/" + std::string(dir_entry_p->d_name) + "/status";
538  // open /proc/PID/status
539  file = fopen(f.c_str(), "r");
540  char name[256];
541  if(file) {
542  fscanf(file, "Name: %s", name);
543  fclose(file);
544  }
545  if(strcmp(process, name) == 0) {
546  result = std::string(dir_entry_p->d_name);
547  closedir(dir_p);
548  return result;
549  }
550  }
551  }
552  closedir(dir_p);
553 
554  return result;
555 }
556 
557 void
558 ProcRRDThread::get_cpu(unsigned long int* cpus)
559 {
560  FILE *file;
561  file = fopen("/proc/stat", "r");
562  int i = 0;
563  i = fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", cpus+1, cpus+2, cpus+3, cpus+4, cpus+5, cpus+6, cpus+7, cpus+8, cpus+9, cpus+10);
564  cpus[0] = 0;
565  for(int j=1; j<=i; j++)
566  cpus[0] += cpus[j];
567 
568  fclose(file);
569 }
570 
571 void
573 {
574  __timewait->mark_start();
575  FILE *file;
576  file = fopen("/proc/net/dev", "r");
577  unsigned long long int recv_bytes, recv_packets, recv_errors, trans_bytes, trans_packets, trans_errors;
578  recv_bytes = recv_packets = recv_errors = trans_bytes = trans_packets = trans_errors = 0;
579  char line[1024];
580  while (file && fgets(line, 1024, file) != NULL){
581  // sscanf the device to get rid of leading whitespaces
582  char dev[100];
583  sscanf(line, "%s", dev);
584 
585  if (strncmp(dev, (__netinterface+":").c_str(), __netinterface.length()+1) == 0){
586  sscanf(line, "%*s %llu %llu %llu %*u %*u %*u %*u %*u %llu %llu %llu %*u %*u %*u %*u %*u", &recv_bytes, &recv_packets, &recv_errors, &trans_bytes, &trans_packets, &trans_errors);
587  break;
588  }
589  }
590  fclose(file);
591  try {
592  rrd_manager->add_data("network", "N:%llu:%llu:%llu:%llu:%llu:%llu", recv_bytes, recv_packets, recv_errors, trans_bytes, trans_packets, trans_errors);
593  } catch (Exception &e) {
594  logger->log_warn(name(), "Failed to update network RRD, exception follows");
595  logger->log_warn(name(), e);
596  }
597 
598  unsigned long int *curcpu = new unsigned long int[11];
599  get_cpu(curcpu);
600 
601  for (ProcessMap::iterator i = __processes.begin(); i != __processes.end(); ++i) {
602  file = fopen(("/proc/"+i->second.pid+"/stat").c_str(), "r");
603  unsigned long int cpu[3];
604  if(file) {
605  fscanf(file, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", cpu+1, cpu+2);
606  cpu[0] = cpu[1] + cpu[2];
607  fclose(file);
608  }
609  file = fopen(("/proc/"+i->second.pid+"/status").c_str(), "r");
610  unsigned long long int vmpeak, vmsize, vmrss;
611  long int threads = 0;
612  vmpeak = vmsize = vmrss = 0;
613  char line[128];
614  while (file && fgets(line, 128, file) != NULL){
615  if (strncmp(line, "VmPeak:", 7) == 0){
616  sscanf(line, "VmPeak: %llu", &vmpeak);
617  }
618  else if (strncmp(line, "VmSize:", 7) == 0){
619  sscanf(line, "VmSize: %llu", &vmsize);
620  }
621  else if (strncmp(line, "VmRSS:", 6) == 0){
622  sscanf(line, "VmRSS: %llu", &vmrss);
623  }
624  else if (strncmp(line, "Threads:", 8) == 0){
625  sscanf(line, "Threads: %ld", &threads);
626  break;
627  }
628  }
629  fclose(file);
630  file = fopen(("/proc/"+i->second.pid+"/io").c_str(), "r");
631  unsigned long long int rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes;
632  rchar = wchar = syscr = syscw = read_bytes = write_bytes = cancelled_write_bytes = 0;
633  char line2[128];
634  while (file && fgets(line2, 128, file) != NULL){
635  if (strncmp(line2, "rchar:", 6) == 0){
636  sscanf(line2, "rchar: %llu", &rchar);
637  }
638  else if (strncmp(line2, "wchar:", 6) == 0){
639  sscanf(line2, "wchar: %llu", &wchar);
640  }
641  else if (strncmp(line2, "syscr:", 6) == 0){
642  sscanf(line2, "syscr: %llu", &syscr);
643  }
644  else if (strncmp(line2, "syscw:", 6) == 0){
645  sscanf(line2, "syscw: %llu", &syscw);
646  }
647  else if (strncmp(line2, "read_bytes:", 11) == 0){
648  sscanf(line2, "read_bytes: %llu", &read_bytes);
649  }
650  else if (strncmp(line2, "write_bytes:", 12) == 0){
651  sscanf(line2, "write_bytes: %llu", &write_bytes);
652  }
653  else if (strncmp(line2, "cancelled_write_bytes:", 22) == 0){
654  sscanf(line2, "cancelled_write_bytes: %llu", &cancelled_write_bytes);
655  break;
656  }
657  }
658  if(file)
659  fclose(file);
660 
661  if(curcpu[0] < __lastcpu[0] || curcpu[1] < __lastcpu[1] || curcpu[2] < __lastcpu[2] || curcpu[3] < __lastcpu[3] || curcpu[4] < __lastcpu[4] || curcpu[5] < __lastcpu[5] || curcpu[6] < __lastcpu[6] || curcpu[7] < __lastcpu[7] || curcpu[8] < __lastcpu[8] || curcpu[9] < __lastcpu[9] || curcpu[10] < __lastcpu[10] || cpu[0] < i->second.last_cpu[0] || cpu[1] < i->second.last_cpu[1] || cpu[2] < i->second.last_cpu[2]) {
662  // value rollover for atleast one value, ignore cpu data
663  try {
664  rrd_manager->add_data(i->second.rrd_name.c_str(), "N:U:%ld:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu", threads, vmpeak, vmsize, vmrss, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes);
665  } catch (Exception &e) {
666  logger->log_warn(name(), "Failed to update %s RRD, exception follows", i->second.rrd_name.c_str());
667  logger->log_warn(name(), e);
668  }
669  }
670  else {
671  double cpuuse = 100.0 * (double)(cpu[0] - i->second.last_cpu[0]) / (double)(curcpu[0] - __lastcpu[0]);
672  try {
673  rrd_manager->add_data(i->second.rrd_name.c_str(), "N:%f:%ld:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu", cpuuse, threads, vmpeak, vmsize, vmrss, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes);
674  } catch (Exception &e) {
675  logger->log_warn(name(), "Failed to update %s RRD, exception follows", i->second.rrd_name.c_str());
676  logger->log_warn(name(), e);
677  }
678  }
679 
680  i->second.last_cpu[0] = cpu[0];
681  }
682 
683  __lastcpu = curcpu;
684 
685  __timewait->wait_systime();
686 }
687 
688 void
689 ProcRRDThread::config_tag_changed(const char *new_tag)
690 {
691  // ignored
692 }
693 
694 
695 void
696 ProcRRDThread::config_value_changed(const Configuration::ValueIterator *v)
697 {
698  if (v->is_string()) {
699  remove_process(v->path());
700  std::string name = v->get_string();
701  std::string pid = get_process_id(name.c_str());
702  add_process(v->path(), pid, name);
703  } else {
704  logger->log_warn(name(), "Non-string value at %s, ignoring", v->path());
705  }
706 }
707 
708 void
709 ProcRRDThread::config_comment_changed(const Configuration::ValueIterator *v)
710 {
711 }
712 
713 void
714 ProcRRDThread::config_value_erased(const char *path)
715 {
716 
717  remove_process(path);
718 }
virtual void loop()
Code to execute in the thread.
RRDManager * rrd_manager
Manager class to access RRD features.
Definition: rrd.h:45
virtual void add_rrd(RRDDefinition *rrd_def)=0
Add RRD.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
virtual const char * type() const =0
Type of value.
Print graph area.
Fawkes library namespace.
Interface for configuration change handling.
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
virtual bool next()=0
Check if there is another element and advance to this if possible.
virtual void add_graph(RRDGraphDefinition *rrd_graph_def)=0
Add graph.
Thread class encapsulation of pthreads.
Definition: thread.h:42
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
Definition: thread.cpp:727
virtual void add_data(const char *rrd_name, const char *format,...)=0
Add data.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:44
void wait_systime()
Wait until minimum loop time has been reached in real time.
Definition: wait.cpp:100
virtual void finalize()
Finalize the thread.
Clock * clock
By means of this member access to the clock is given.
Definition: clock.h:45
Print graph line.
virtual bool is_string() const =0
Check if current value is a string.
Represent data definition in graph arguments.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Class representing a graph definition.
virtual ~ProcRRDThread()
Destructor.
virtual void rem_change_handler(ConfigurationChangeHandler *h)
Remove a configuration change handler.
Definition: config.cpp:564
Class to represent a RRD data source.
virtual std::string get_string() const =0
Get string value.
const char * name() const
Get name of thread.
Definition: thread.h:95
virtual void remove_rrd(RRDDefinition *rrd_def)=0
Remove RRD.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual const char * path() const =0
Path of value.
void mark_start()
Mark start of loop.
Definition: wait.cpp:70
Iterator interface to iterate over config values.
Definition: config.h:72
ProcRRDThread()
Constructor.
virtual void add_change_handler(ConfigurationChangeHandler *h)
Add a configuration change handler.
Definition: config.cpp:547
Print string inside graph.
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
virtual void init()
Initialize the thread.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:44
Time wait utility.
Definition: wait.h:32
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.