]> git.proxmox.com Git - mirror_ovs.git/blame - vswitchd/system-stats.c
Merge branch 'dpdk_merge' of https://github.com/istokes/ovs into HEAD
[mirror_ovs.git] / vswitchd / system-stats.c
CommitLineData
2f51a7eb 1/* Copyright (c) 2010, 2012, 2013, 2014 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"
3e8a2ad1 36#include "openvswitch/dynamic-string.h"
ee89ea7b 37#include "openvswitch/json.h"
59987a62 38#include "latch.h"
64c96779 39#include "openvswitch/ofpbuf.h"
5724fca4 40#include "ovs-rcu.h"
59987a62 41#include "ovs-thread.h"
fd016ae3 42#include "openvswitch/poll-loop.h"
ee89ea7b 43#include "openvswitch/shash.h"
ff1d2c16 44#include "process.h"
57c8677b 45#include "smap.h"
ce887677 46#include "timeval.h"
e6211adc 47#include "openvswitch/vlog.h"
ce887677 48
d98e6007 49VLOG_DEFINE_THIS_MODULE(system_stats);
ce887677
BP
50
51/* #ifdefs make it a pain to maintain code: you have to try to build both ways.
52 * Thus, this file tries to compile as much of the code as possible regardless
2f51a7eb
BP
53 * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where
54 * this is possible. */
55#ifdef __linux__
56#define LINUX 1
ce887677 57#include <asm/param.h>
ce887677 58#else
2f51a7eb 59#define LINUX 0
ce887677
BP
60#endif
61
62static void
57c8677b 63get_cpu_cores(struct smap *stats)
ce887677 64{
0122f6e6 65 long int n_cores = count_cpu_cores();
ce887677 66 if (n_cores > 0) {
57c8677b 67 smap_add_format(stats, "cpu", "%ld", n_cores);
ce887677
BP
68 }
69}
70
71static void
57c8677b 72get_load_average(struct smap *stats OVS_UNUSED)
ce887677
BP
73{
74#if HAVE_GETLOADAVG
75 double loadavg[3];
76
77 if (getloadavg(loadavg, 3) == 3) {
57c8677b
BP
78 smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
79 loadavg[0], loadavg[1], loadavg[2]);
ce887677
BP
80 }
81#endif
82}
83
ce887677 84static void
57c8677b 85get_memory_stats(struct smap *stats)
ce887677 86{
2f51a7eb 87 if (!LINUX) {
ce887677 88 unsigned int pagesize = get_page_size();
a0e21f58 89#ifdef _SC_PHYS_PAGES
ce887677 90 long int phys_pages = sysconf(_SC_PHYS_PAGES);
a0e21f58
YT
91#else
92 long int phys_pages = 0;
93#endif
6ca00f6f 94#ifdef _SC_AVPHYS_PAGES
ce887677 95 long int avphys_pages = sysconf(_SC_AVPHYS_PAGES);
6ca00f6f
ETN
96#else
97 long int avphys_pages = 0;
98#endif
ce887677
BP
99 int mem_total, mem_used;
100
6c04f67c 101#ifndef _WIN32
ce887677
BP
102 if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) {
103 return;
104 }
105
106 mem_total = phys_pages * (pagesize / 1024);
107 mem_used = (phys_pages - avphys_pages) * (pagesize / 1024);
6c04f67c
GS
108#else
109 MEMORYSTATUS memory_status;
110 GlobalMemoryStatus(&memory_status);
111
112 mem_total = memory_status.dwTotalPhys;
113 mem_used = memory_status.dwTotalPhys - memory_status.dwAvailPhys;
114#endif
57c8677b 115 smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
ce887677
BP
116 } else {
117 static const char file_name[] = "/proc/meminfo";
118 int mem_used, mem_cache, swap_used;
119 int mem_free = 0;
120 int buffers = 0;
121 int cached = 0;
122 int swap_free = 0;
123 int mem_total = 0;
124 int swap_total = 0;
125 struct shash dict;
126 char line[128];
127 FILE *stream;
128
129 stream = fopen(file_name, "r");
130 if (!stream) {
10a89ef0
BP
131 VLOG_WARN_ONCE("%s: open failed (%s)",
132 file_name, ovs_strerror(errno));
ce887677
BP
133 return;
134 }
135
136 shash_init(&dict);
137 shash_add(&dict, "MemTotal", &mem_total);
138 shash_add(&dict, "MemFree", &mem_free);
139 shash_add(&dict, "Buffers", &buffers);
140 shash_add(&dict, "Cached", &cached);
141 shash_add(&dict, "SwapTotal", &swap_total);
142 shash_add(&dict, "SwapFree", &swap_free);
143 while (fgets(line, sizeof line, stream)) {
144 char key[16];
145 int value;
146
c2c28dfd 147 if (ovs_scan(line, "%15[^:]: %u", key, &value)) {
ce887677
BP
148 int *valuep = shash_find_data(&dict, key);
149 if (valuep) {
150 *valuep = value;
151 }
152 }
153 }
154 fclose(stream);
155 shash_destroy(&dict);
156
157 mem_used = mem_total - mem_free;
158 mem_cache = buffers + cached;
159 swap_used = swap_total - swap_free;
57c8677b
BP
160 smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
161 mem_total, mem_used, mem_cache, swap_total, swap_used);
ce887677
BP
162 }
163}
164
ce887677 165static void
57c8677b 166get_process_stats(struct smap *stats)
ce887677 167{
6c04f67c 168#ifndef _WIN32
ce887677
BP
169 struct dirent *de;
170 DIR *dir;
171
b43c6fe2 172 dir = opendir(ovs_rundir());
ce887677 173 if (!dir) {
10a89ef0
BP
174 VLOG_ERR_ONCE("%s: open failed (%s)",
175 ovs_rundir(), ovs_strerror(errno));
ce887677
BP
176 return;
177 }
178
179 while ((de = readdir(dir)) != NULL) {
180 struct process_info pinfo;
ce887677
BP
181 char *file_name;
182 char *extension;
57c8677b 183 char *key;
ce887677
BP
184 pid_t pid;
185
186#ifdef _DIRENT_HAVE_D_TYPE
187 if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) {
188 continue;
189 }
190#endif
191
192 extension = strrchr(de->d_name, '.');
193 if (!extension || strcmp(extension, ".pid")) {
194 continue;
195 }
196
b43c6fe2 197 file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name);
ce887677
BP
198 pid = read_pidfile(file_name);
199 free(file_name);
8da7ca87 200 if (pid < 0) {
ce887677
BP
201 continue;
202 }
203
204 key = xasprintf("process_%.*s",
205 (int) (extension - de->d_name), de->d_name);
57c8677b 206 if (!smap_get(stats, key)) {
2f51a7eb 207 if (LINUX && get_process_info(pid, &pinfo)) {
99910ebb 208 smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld,%d",
57c8677b 209 pinfo.vsz, pinfo.rss, pinfo.cputime,
99910ebb
BB
210 pinfo.crashes, pinfo.booted, pinfo.uptime,
211 pinfo.core_id);
57c8677b
BP
212 } else {
213 smap_add(stats, key, "");
214 }
ce887677 215 }
57c8677b 216 free(key);
ce887677
BP
217 }
218
219 closedir(dir);
6c04f67c 220#endif /* _WIN32 */
ce887677
BP
221}
222
223static void
57c8677b 224get_filesys_stats(struct smap *stats OVS_UNUSED)
ce887677 225{
ffb1cfd1 226#if HAVE_GETMNTENT_R && HAVE_STATVFS
ce887677 227 static const char file_name[] = "/etc/mtab";
ffb1cfd1 228 struct mntent mntent;
ce887677 229 struct mntent *me;
ffb1cfd1 230 char buf[4096];
ce887677
BP
231 FILE *stream;
232 struct ds s;
233
234 stream = setmntent(file_name, "r");
235 if (!stream) {
10a89ef0 236 VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
ce887677
BP
237 return;
238 }
239
240 ds_init(&s);
ffb1cfd1 241 while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) {
ce887677
BP
242 unsigned long long int total, free;
243 struct statvfs vfs;
244 char *p;
245
246 /* Skip non-local and read-only filesystems. */
247 if (strncmp(me->mnt_fsname, "/dev", 4)
248 || !strstr(me->mnt_opts, "rw")) {
249 continue;
250 }
251
252 /* Given the mount point we can stat the file system. */
253 if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) {
254 /* That's odd... */
255 continue;
256 }
257
258 /* Now format the data. */
259 if (s.length) {
260 ds_put_char(&s, ' ');
261 }
262 for (p = me->mnt_dir; *p != '\0'; p++) {
263 ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p);
264 }
265 total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024;
266 free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024;
267 ds_put_format(&s, ",%llu,%llu", total, total - free);
268 }
269 endmntent(stream);
270
271 if (s.length) {
57c8677b 272 smap_add(stats, "file_systems", ds_cstr(&s));
ce887677
BP
273 }
274 ds_destroy(&s);
ffb1cfd1 275#endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */
ce887677 276}
35a22d8c
BP
277\f
278#define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
ce887677 279
834d6caf 280static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
59987a62 281static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
97be1538 282static struct latch latch OVS_GUARDED_BY(mutex);
59987a62 283static bool enabled;
97be1538
EJ
284static bool started OVS_GUARDED_BY(mutex);
285static struct smap *system_stats OVS_GUARDED_BY(mutex);
59987a62 286
cab50449 287OVS_NO_RETURN static void *system_stats_thread_func(void *);
59987a62 288static void discard_stats(void);
35a22d8c 289
5a13f2f4 290/* Enables or disables system stats collection, according to 'enable'. */
ce887677 291void
5a13f2f4 292system_stats_enable(bool enable)
35a22d8c 293{
59987a62 294 if (enabled != enable) {
97be1538 295 ovs_mutex_lock(&mutex);
59987a62
BP
296 if (enable) {
297 if (!started) {
8ba0a522
BP
298 ovs_thread_create("system_stats",
299 system_stats_thread_func, NULL);
59987a62
BP
300 latch_init(&latch);
301 started = true;
302 }
303 discard_stats();
304 xpthread_cond_signal(&cond);
305 }
306 enabled = enable;
97be1538 307 ovs_mutex_unlock(&mutex);
35a22d8c
BP
308 }
309}
310
311/* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
312 * milliseconds.
313 *
314 * When a new snapshot is available (which only occurs if system stats are
315 * enabled), returns it as an smap owned by the caller. The caller must use
5a13f2f4 316 * both smap_destroy() and free() to completely free the returned data.
35a22d8c
BP
317 *
318 * When no new snapshot is available, returns NULL. */
319struct smap *
320system_stats_run(void)
ce887677 321{
59987a62 322 struct smap *stats = NULL;
5a13f2f4 323
97be1538 324 ovs_mutex_lock(&mutex);
59987a62
BP
325 if (system_stats) {
326 latch_poll(&latch);
5a13f2f4 327
59987a62
BP
328 if (enabled) {
329 stats = system_stats;
330 system_stats = NULL;
331 } else {
332 discard_stats();
333 }
35a22d8c 334 }
97be1538 335 ovs_mutex_unlock(&mutex);
35a22d8c 336
59987a62 337 return stats;
35a22d8c
BP
338}
339
340/* Causes poll_block() to wake up when system_stats_run() needs to be
341 * called. */
342void
343system_stats_wait(void)
344{
59987a62
BP
345 if (enabled) {
346 latch_wait(&latch);
347 }
348}
349
350static void
344e21d4 351discard_stats(void) OVS_REQUIRES(mutex)
59987a62
BP
352{
353 if (system_stats) {
354 smap_destroy(system_stats);
355 free(system_stats);
356 system_stats = NULL;
357 }
358}
359
270f3286 360static void *
59987a62
BP
361system_stats_thread_func(void *arg OVS_UNUSED)
362{
363 pthread_detach(pthread_self());
364
365 for (;;) {
366 long long int next_refresh;
367 struct smap *stats;
368
97be1538 369 ovs_mutex_lock(&mutex);
59987a62 370 while (!enabled) {
5724fca4
DDP
371 /* The thread is sleeping, potentially for a long time, and it's
372 * not holding RCU protected references, so it makes sense to
373 * quiesce */
374 ovsrcu_quiesce_start();
97be1538 375 ovs_mutex_cond_wait(&cond, &mutex);
5724fca4 376 ovsrcu_quiesce_end();
59987a62 377 }
97be1538 378 ovs_mutex_unlock(&mutex);
59987a62
BP
379
380 stats = xmalloc(sizeof *stats);
381 smap_init(stats);
382 get_cpu_cores(stats);
383 get_load_average(stats);
384 get_memory_stats(stats);
385 get_process_stats(stats);
386 get_filesys_stats(stats);
387
97be1538 388 ovs_mutex_lock(&mutex);
59987a62
BP
389 discard_stats();
390 system_stats = stats;
391 latch_set(&latch);
97be1538 392 ovs_mutex_unlock(&mutex);
59987a62
BP
393
394 next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
395 do {
396 poll_timer_wait_until(next_refresh);
397 poll_block();
398 } while (time_msec() < next_refresh);
35a22d8c
BP
399 }
400}