]> git.proxmox.com Git - mirror_ovs.git/blame - vswitchd/system-stats.c
worker: Delete library.
[mirror_ovs.git] / vswitchd / system-stats.c
CommitLineData
10a89ef0 1/* Copyright (c) 2010, 2012, 2013 Nicira, Inc.
ce887677
BP
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <config.h>
17
57c8677b
BP
18#include "system-stats.h"
19
ce887677
BP
20#include <ctype.h>
21#include <dirent.h>
22#include <errno.h>
23#if HAVE_MNTENT_H
24#include <mntent.h>
25#endif
ce887677
BP
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#if HAVE_SYS_STATVFS_H
30#include <sys/statvfs.h>
31#endif
32#include <unistd.h>
33
34#include "daemon.h"
35#include "dirs.h"
36#include "dynamic-string.h"
35a22d8c
BP
37#include "json.h"
38#include "ofpbuf.h"
39#include "poll-loop.h"
ce887677 40#include "shash.h"
57c8677b 41#include "smap.h"
ce887677
BP
42#include "timeval.h"
43#include "vlog.h"
44
d98e6007 45VLOG_DEFINE_THIS_MODULE(system_stats);
ce887677
BP
46
47/* #ifdefs make it a pain to maintain code: you have to try to build both ways.
48 * Thus, this file tries to compile as much of the code as possible regardless
361906b1
EJ
49 * of the target, by writing "if (LINUX_DATAPATH)" instead of "#ifdef
50 * __linux__" where this is possible. */
51#ifdef LINUX_DATAPATH
ce887677 52#include <asm/param.h>
ce887677 53#else
361906b1 54#define LINUX_DATAPATH 0
ce887677
BP
55#endif
56
57static void
57c8677b 58get_cpu_cores(struct smap *stats)
ce887677
BP
59{
60 long int n_cores = sysconf(_SC_NPROCESSORS_ONLN);
61 if (n_cores > 0) {
57c8677b 62 smap_add_format(stats, "cpu", "%ld", n_cores);
ce887677
BP
63 }
64}
65
66static void
57c8677b 67get_load_average(struct smap *stats OVS_UNUSED)
ce887677
BP
68{
69#if HAVE_GETLOADAVG
70 double loadavg[3];
71
72 if (getloadavg(loadavg, 3) == 3) {
57c8677b
BP
73 smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
74 loadavg[0], loadavg[1], loadavg[2]);
ce887677
BP
75 }
76#endif
77}
78
79static unsigned int
80get_page_size(void)
81{
82 static unsigned int cached;
83
84 if (!cached) {
85 long int value = sysconf(_SC_PAGESIZE);
86 if (value >= 0) {
87 cached = value;
88 }
89 }
90
91 return cached;
92}
93
94static void
57c8677b 95get_memory_stats(struct smap *stats)
ce887677 96{
361906b1 97 if (!LINUX_DATAPATH) {
ce887677 98 unsigned int pagesize = get_page_size();
a0e21f58 99#ifdef _SC_PHYS_PAGES
ce887677 100 long int phys_pages = sysconf(_SC_PHYS_PAGES);
a0e21f58
YT
101#else
102 long int phys_pages = 0;
103#endif
6ca00f6f 104#ifdef _SC_AVPHYS_PAGES
ce887677 105 long int avphys_pages = sysconf(_SC_AVPHYS_PAGES);
6ca00f6f
ETN
106#else
107 long int avphys_pages = 0;
108#endif
ce887677
BP
109 int mem_total, mem_used;
110
111 if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) {
112 return;
113 }
114
115 mem_total = phys_pages * (pagesize / 1024);
116 mem_used = (phys_pages - avphys_pages) * (pagesize / 1024);
57c8677b 117 smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
ce887677
BP
118 } else {
119 static const char file_name[] = "/proc/meminfo";
120 int mem_used, mem_cache, swap_used;
121 int mem_free = 0;
122 int buffers = 0;
123 int cached = 0;
124 int swap_free = 0;
125 int mem_total = 0;
126 int swap_total = 0;
127 struct shash dict;
128 char line[128];
129 FILE *stream;
130
131 stream = fopen(file_name, "r");
132 if (!stream) {
10a89ef0
BP
133 VLOG_WARN_ONCE("%s: open failed (%s)",
134 file_name, ovs_strerror(errno));
ce887677
BP
135 return;
136 }
137
138 shash_init(&dict);
139 shash_add(&dict, "MemTotal", &mem_total);
140 shash_add(&dict, "MemFree", &mem_free);
141 shash_add(&dict, "Buffers", &buffers);
142 shash_add(&dict, "Cached", &cached);
143 shash_add(&dict, "SwapTotal", &swap_total);
144 shash_add(&dict, "SwapFree", &swap_free);
145 while (fgets(line, sizeof line, stream)) {
146 char key[16];
147 int value;
148
149 if (sscanf(line, "%15[^:]: %u", key, &value) == 2) {
150 int *valuep = shash_find_data(&dict, key);
151 if (valuep) {
152 *valuep = value;
153 }
154 }
155 }
156 fclose(stream);
157 shash_destroy(&dict);
158
159 mem_used = mem_total - mem_free;
160 mem_cache = buffers + cached;
161 swap_used = swap_total - swap_free;
57c8677b
BP
162 smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
163 mem_total, mem_used, mem_cache, swap_total, swap_used);
ce887677
BP
164 }
165}
166
167/* Returns the time at which the system booted, as the number of milliseconds
168 * since the epoch, or 0 if the time of boot cannot be determined. */
169static long long int
170get_boot_time(void)
171{
172 static long long int cache_expiration = LLONG_MIN;
173 static long long int boot_time;
174
cb22974d 175 ovs_assert(LINUX_DATAPATH);
ce887677
BP
176
177 if (time_msec() >= cache_expiration) {
178 static const char stat_file[] = "/proc/stat";
179 char line[128];
180 FILE *stream;
181
182 cache_expiration = time_msec() + 5 * 1000;
183
184 stream = fopen(stat_file, "r");
185 if (!stream) {
10a89ef0
BP
186 VLOG_ERR_ONCE("%s: open failed (%s)",
187 stat_file, ovs_strerror(errno));
ce887677
BP
188 return boot_time;
189 }
190
191 while (fgets(line, sizeof line, stream)) {
192 long long int btime;
193 if (sscanf(line, "btime %lld", &btime) == 1) {
194 boot_time = btime * 1000;
195 goto done;
196 }
197 }
198 VLOG_ERR_ONCE("%s: btime not found", stat_file);
199 done:
200 fclose(stream);
201 }
202 return boot_time;
203}
204
205static unsigned long long int
206ticks_to_ms(unsigned long long int ticks)
207{
cb22974d 208 ovs_assert(LINUX_DATAPATH);
ce887677
BP
209
210#ifndef USER_HZ
211#define USER_HZ 100
212#endif
213
214#if USER_HZ == 100 /* Common case. */
215 return ticks * (1000 / USER_HZ);
216#else /* Alpha and some other architectures. */
217 double factor = 1000.0 / USER_HZ;
218 return ticks * factor + 0.5;
219#endif
220}
221
222struct raw_process_info {
223 unsigned long int vsz; /* Virtual size, in kB. */
224 unsigned long int rss; /* Resident set size, in kB. */
225 long long int uptime; /* ms since started. */
226 long long int cputime; /* ms of CPU used during 'uptime'. */
227 pid_t ppid; /* Parent. */
228 char name[18]; /* Name (surrounded by parentheses). */
229};
230
231static bool
232get_raw_process_info(pid_t pid, struct raw_process_info *raw)
233{
234 unsigned long long int vsize, rss, start_time, utime, stime;
235 long long int start_msec;
236 unsigned long ppid;
237 char file_name[128];
238 FILE *stream;
239 int n;
240
cb22974d 241 ovs_assert(LINUX_DATAPATH);
ce887677
BP
242
243 sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid);
244 stream = fopen(file_name, "r");
245 if (!stream) {
10a89ef0
BP
246 VLOG_ERR_ONCE("%s: open failed (%s)",
247 file_name, ovs_strerror(errno));
ce887677
BP
248 return false;
249 }
250
251 n = fscanf(stream,
252 "%*d " /* (1. pid) */
253 "%17s " /* 2. process name */
254 "%*c " /* (3. state) */
255 "%lu " /* 4. ppid */
256 "%*d " /* (5. pgid) */
257 "%*d " /* (6. sid) */
258 "%*d " /* (7. tty_nr) */
259 "%*d " /* (8. tty_pgrp) */
260 "%*u " /* (9. flags) */
261 "%*u " /* (10. min_flt) */
262 "%*u " /* (11. cmin_flt) */
263 "%*u " /* (12. maj_flt) */
264 "%*u " /* (13. cmaj_flt) */
265 "%llu " /* 14. utime */
266 "%llu " /* 15. stime */
267 "%*d " /* (16. cutime) */
268 "%*d " /* (17. cstime) */
269 "%*d " /* (18. priority) */
270 "%*d " /* (19. nice) */
271 "%*d " /* (20. num_threads) */
272 "%*d " /* (21. always 0) */
273 "%llu " /* 22. start_time */
274 "%llu " /* 23. vsize */
275 "%llu " /* 24. rss */
276#if 0
277 /* These are here for documentation but #if'd out to save
278 * actually parsing them from the stream for no benefit. */
279 "%*lu " /* (25. rsslim) */
280 "%*lu " /* (26. start_code) */
281 "%*lu " /* (27. end_code) */
282 "%*lu " /* (28. start_stack) */
283 "%*lu " /* (29. esp) */
284 "%*lu " /* (30. eip) */
285 "%*lu " /* (31. pending signals) */
286 "%*lu " /* (32. blocked signals) */
287 "%*lu " /* (33. ignored signals) */
288 "%*lu " /* (34. caught signals) */
289 "%*lu " /* (35. whcan) */
290 "%*lu " /* (36. always 0) */
291 "%*lu " /* (37. always 0) */
292 "%*d " /* (38. exit_signal) */
293 "%*d " /* (39. task_cpu) */
294 "%*u " /* (40. rt_priority) */
295 "%*u " /* (41. policy) */
296 "%*llu " /* (42. blkio_ticks) */
297 "%*lu " /* (43. gtime) */
298 "%*ld" /* (44. cgtime) */
299#endif
300 , raw->name, &ppid, &utime, &stime, &start_time, &vsize, &rss);
301 fclose(stream);
302 if (n != 7) {
303 VLOG_ERR_ONCE("%s: fscanf failed", file_name);
304 return false;
305 }
306
307 start_msec = get_boot_time() + ticks_to_ms(start_time);
308
309 raw->vsz = vsize / 1024;
310 raw->rss = rss * (getpagesize() / 1024);
311 raw->uptime = time_wall_msec() - start_msec;
312 raw->cputime = ticks_to_ms(utime + stime);
313 raw->ppid = ppid;
314
315 return true;
316}
317
318static int
319count_crashes(pid_t pid)
320{
321 char file_name[128];
322 const char *paren;
323 char line[128];
324 int crashes = 0;
325 FILE *stream;
326
cb22974d 327 ovs_assert(LINUX_DATAPATH);
ce887677
BP
328
329 sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid);
330 stream = fopen(file_name, "r");
331 if (!stream) {
10a89ef0 332 VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
ce887677
BP
333 goto exit;
334 }
335
336 if (!fgets(line, sizeof line, stream)) {
337 VLOG_WARN_ONCE("%s: read failed (%s)", file_name,
10a89ef0 338 feof(stream) ? "end of file" : ovs_strerror(errno));
ce887677
BP
339 goto exit_close;
340 }
341
342 paren = strchr(line, '(');
343 if (paren) {
344 int x;
345 if (sscanf(paren + 1, "%d", &x) == 1) {
346 crashes = x;
347 }
348 }
349
350exit_close:
351 fclose(stream);
352exit:
353 return crashes;
354}
355
356struct process_info {
357 unsigned long int vsz; /* Virtual size, in kB. */
358 unsigned long int rss; /* Resident set size, in kB. */
359 long long int booted; /* ms since monitor started. */
360 int crashes; /* # of crashes (usually 0). */
361 long long int uptime; /* ms since last (re)started by monitor. */
362 long long int cputime; /* ms of CPU used during 'uptime'. */
363};
364
365static bool
366get_process_info(pid_t pid, struct process_info *pinfo)
367{
368 struct raw_process_info child;
369
cb22974d 370 ovs_assert(LINUX_DATAPATH);
ce887677
BP
371 if (!get_raw_process_info(pid, &child)) {
372 return false;
373 }
374
375 pinfo->vsz = child.vsz;
376 pinfo->rss = child.rss;
377 pinfo->booted = child.uptime;
378 pinfo->crashes = 0;
379 pinfo->uptime = child.uptime;
380 pinfo->cputime = child.cputime;
381
382 if (child.ppid) {
383 struct raw_process_info parent;
384
385 get_raw_process_info(child.ppid, &parent);
386 if (!strcmp(child.name, parent.name)) {
387 pinfo->booted = parent.uptime;
388 pinfo->crashes = count_crashes(child.ppid);
389 }
390 }
391
392 return true;
393}
394
395static void
57c8677b 396get_process_stats(struct smap *stats)
ce887677
BP
397{
398 struct dirent *de;
399 DIR *dir;
400
b43c6fe2 401 dir = opendir(ovs_rundir());
ce887677 402 if (!dir) {
10a89ef0
BP
403 VLOG_ERR_ONCE("%s: open failed (%s)",
404 ovs_rundir(), ovs_strerror(errno));
ce887677
BP
405 return;
406 }
407
408 while ((de = readdir(dir)) != NULL) {
409 struct process_info pinfo;
ce887677
BP
410 char *file_name;
411 char *extension;
57c8677b 412 char *key;
ce887677
BP
413 pid_t pid;
414
415#ifdef _DIRENT_HAVE_D_TYPE
416 if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) {
417 continue;
418 }
419#endif
420
421 extension = strrchr(de->d_name, '.');
422 if (!extension || strcmp(extension, ".pid")) {
423 continue;
424 }
425
b43c6fe2 426 file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name);
ce887677
BP
427 pid = read_pidfile(file_name);
428 free(file_name);
8da7ca87 429 if (pid < 0) {
ce887677
BP
430 continue;
431 }
432
433 key = xasprintf("process_%.*s",
434 (int) (extension - de->d_name), de->d_name);
57c8677b 435 if (!smap_get(stats, key)) {
361906b1 436 if (LINUX_DATAPATH && get_process_info(pid, &pinfo)) {
57c8677b
BP
437 smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld",
438 pinfo.vsz, pinfo.rss, pinfo.cputime,
439 pinfo.crashes, pinfo.booted, pinfo.uptime);
440 } else {
441 smap_add(stats, key, "");
442 }
ce887677 443 }
57c8677b 444 free(key);
ce887677
BP
445 }
446
447 closedir(dir);
448}
449
450static void
57c8677b 451get_filesys_stats(struct smap *stats OVS_UNUSED)
ce887677 452{
ffb1cfd1 453#if HAVE_GETMNTENT_R && HAVE_STATVFS
ce887677 454 static const char file_name[] = "/etc/mtab";
ffb1cfd1 455 struct mntent mntent;
ce887677 456 struct mntent *me;
ffb1cfd1 457 char buf[4096];
ce887677
BP
458 FILE *stream;
459 struct ds s;
460
461 stream = setmntent(file_name, "r");
462 if (!stream) {
10a89ef0 463 VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
ce887677
BP
464 return;
465 }
466
467 ds_init(&s);
ffb1cfd1 468 while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) {
ce887677
BP
469 unsigned long long int total, free;
470 struct statvfs vfs;
471 char *p;
472
473 /* Skip non-local and read-only filesystems. */
474 if (strncmp(me->mnt_fsname, "/dev", 4)
475 || !strstr(me->mnt_opts, "rw")) {
476 continue;
477 }
478
479 /* Given the mount point we can stat the file system. */
480 if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) {
481 /* That's odd... */
482 continue;
483 }
484
485 /* Now format the data. */
486 if (s.length) {
487 ds_put_char(&s, ' ');
488 }
489 for (p = me->mnt_dir; *p != '\0'; p++) {
490 ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p);
491 }
492 total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024;
493 free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024;
494 ds_put_format(&s, ",%llu,%llu", total, total - free);
495 }
496 endmntent(stream);
497
498 if (s.length) {
57c8677b 499 smap_add(stats, "file_systems", ds_cstr(&s));
ce887677
BP
500 }
501 ds_destroy(&s);
ffb1cfd1 502#endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */
ce887677 503}
35a22d8c
BP
504\f
505#define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
ce887677 506
5a13f2f4
BP
507/* The next time to wake up, or LLONG_MAX if stats are disabled. */
508static long long int next_refresh = LLONG_MAX;
35a22d8c 509
5a13f2f4 510/* Enables or disables system stats collection, according to 'enable'. */
ce887677 511void
5a13f2f4 512system_stats_enable(bool enable)
35a22d8c 513{
5a13f2f4
BP
514 if (!enable) {
515 next_refresh = LLONG_MAX;
516 } else if (next_refresh == LLONG_MAX) {
517 next_refresh = time_msec();
35a22d8c
BP
518 }
519}
520
521/* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
522 * milliseconds.
523 *
524 * When a new snapshot is available (which only occurs if system stats are
525 * enabled), returns it as an smap owned by the caller. The caller must use
5a13f2f4 526 * both smap_destroy() and free() to completely free the returned data.
35a22d8c
BP
527 *
528 * When no new snapshot is available, returns NULL. */
529struct smap *
530system_stats_run(void)
ce887677 531{
5a13f2f4
BP
532 if (time_msec() >= next_refresh) {
533 struct smap *stats;
534
535 stats = xmalloc(sizeof *stats);
536 smap_init(stats);
537 get_cpu_cores(stats);
538 get_load_average(stats);
539 get_memory_stats(stats);
540 get_process_stats(stats);
541 get_filesys_stats(stats);
542
543 next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
544
545 return stats;
35a22d8c
BP
546 }
547
548 return NULL;
549}
550
551/* Causes poll_block() to wake up when system_stats_run() needs to be
552 * called. */
553void
554system_stats_wait(void)
555{
5a13f2f4 556 if (next_refresh != LLONG_MAX) {
35a22d8c 557 poll_timer_wait_until(next_refresh);
35a22d8c
BP
558 }
559}