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