]> git.proxmox.com Git - mirror_ovs.git/blob - vswitchd/system-stats.c
lib: Move lib/poll-loop.h to include/openvswitch
[mirror_ovs.git] / vswitchd / system-stats.c
1 /* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc.
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
18 #include "system-stats.h"
19
20 #include <ctype.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #if HAVE_MNTENT_H
24 #include <mntent.h>
25 #endif
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 "openvswitch/dynamic-string.h"
37 #include "openvswitch/json.h"
38 #include "latch.h"
39 #include "openvswitch/ofpbuf.h"
40 #include "ovs-rcu.h"
41 #include "ovs-thread.h"
42 #include "openvswitch/poll-loop.h"
43 #include "openvswitch/shash.h"
44 #include "process.h"
45 #include "smap.h"
46 #include "timeval.h"
47 #include "openvswitch/vlog.h"
48
49 VLOG_DEFINE_THIS_MODULE(system_stats);
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
53 * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where
54 * this is possible. */
55 #ifdef __linux__
56 #define LINUX 1
57 #include <asm/param.h>
58 #else
59 #define LINUX 0
60 #endif
61
62 static void
63 get_cpu_cores(struct smap *stats)
64 {
65 long int n_cores = count_cpu_cores();
66 if (n_cores > 0) {
67 smap_add_format(stats, "cpu", "%ld", n_cores);
68 }
69 }
70
71 static void
72 get_load_average(struct smap *stats OVS_UNUSED)
73 {
74 #if HAVE_GETLOADAVG
75 double loadavg[3];
76
77 if (getloadavg(loadavg, 3) == 3) {
78 smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
79 loadavg[0], loadavg[1], loadavg[2]);
80 }
81 #endif
82 }
83
84 static void
85 get_memory_stats(struct smap *stats)
86 {
87 if (!LINUX) {
88 unsigned int pagesize = get_page_size();
89 #ifdef _SC_PHYS_PAGES
90 long int phys_pages = sysconf(_SC_PHYS_PAGES);
91 #else
92 long int phys_pages = 0;
93 #endif
94 #ifdef _SC_AVPHYS_PAGES
95 long int avphys_pages = sysconf(_SC_AVPHYS_PAGES);
96 #else
97 long int avphys_pages = 0;
98 #endif
99 int mem_total, mem_used;
100
101 #ifndef _WIN32
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);
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
115 smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
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) {
131 VLOG_WARN_ONCE("%s: open failed (%s)",
132 file_name, ovs_strerror(errno));
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
147 if (ovs_scan(line, "%15[^:]: %u", key, &value)) {
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;
160 smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
161 mem_total, mem_used, mem_cache, swap_total, swap_used);
162 }
163 }
164
165 static void
166 get_process_stats(struct smap *stats)
167 {
168 #ifndef _WIN32
169 struct dirent *de;
170 DIR *dir;
171
172 dir = opendir(ovs_rundir());
173 if (!dir) {
174 VLOG_ERR_ONCE("%s: open failed (%s)",
175 ovs_rundir(), ovs_strerror(errno));
176 return;
177 }
178
179 while ((de = readdir(dir)) != NULL) {
180 struct process_info pinfo;
181 char *file_name;
182 char *extension;
183 char *key;
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
197 file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name);
198 pid = read_pidfile(file_name);
199 free(file_name);
200 if (pid < 0) {
201 continue;
202 }
203
204 key = xasprintf("process_%.*s",
205 (int) (extension - de->d_name), de->d_name);
206 if (!smap_get(stats, key)) {
207 if (LINUX && get_process_info(pid, &pinfo)) {
208 smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld",
209 pinfo.vsz, pinfo.rss, pinfo.cputime,
210 pinfo.crashes, pinfo.booted, pinfo.uptime);
211 } else {
212 smap_add(stats, key, "");
213 }
214 }
215 free(key);
216 }
217
218 closedir(dir);
219 #endif /* _WIN32 */
220 }
221
222 static void
223 get_filesys_stats(struct smap *stats OVS_UNUSED)
224 {
225 #if HAVE_GETMNTENT_R && HAVE_STATVFS
226 static const char file_name[] = "/etc/mtab";
227 struct mntent mntent;
228 struct mntent *me;
229 char buf[4096];
230 FILE *stream;
231 struct ds s;
232
233 stream = setmntent(file_name, "r");
234 if (!stream) {
235 VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
236 return;
237 }
238
239 ds_init(&s);
240 while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) {
241 unsigned long long int total, free;
242 struct statvfs vfs;
243 char *p;
244
245 /* Skip non-local and read-only filesystems. */
246 if (strncmp(me->mnt_fsname, "/dev", 4)
247 || !strstr(me->mnt_opts, "rw")) {
248 continue;
249 }
250
251 /* Given the mount point we can stat the file system. */
252 if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) {
253 /* That's odd... */
254 continue;
255 }
256
257 /* Now format the data. */
258 if (s.length) {
259 ds_put_char(&s, ' ');
260 }
261 for (p = me->mnt_dir; *p != '\0'; p++) {
262 ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p);
263 }
264 total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024;
265 free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024;
266 ds_put_format(&s, ",%llu,%llu", total, total - free);
267 }
268 endmntent(stream);
269
270 if (s.length) {
271 smap_add(stats, "file_systems", ds_cstr(&s));
272 }
273 ds_destroy(&s);
274 #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */
275 }
276 \f
277 #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
278
279 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
280 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
281 static struct latch latch OVS_GUARDED_BY(mutex);
282 static bool enabled;
283 static bool started OVS_GUARDED_BY(mutex);
284 static struct smap *system_stats OVS_GUARDED_BY(mutex);
285
286 OVS_NO_RETURN static void *system_stats_thread_func(void *);
287 static void discard_stats(void);
288
289 /* Enables or disables system stats collection, according to 'enable'. */
290 void
291 system_stats_enable(bool enable)
292 {
293 if (enabled != enable) {
294 ovs_mutex_lock(&mutex);
295 if (enable) {
296 if (!started) {
297 ovs_thread_create("system_stats",
298 system_stats_thread_func, NULL);
299 latch_init(&latch);
300 started = true;
301 }
302 discard_stats();
303 xpthread_cond_signal(&cond);
304 }
305 enabled = enable;
306 ovs_mutex_unlock(&mutex);
307 }
308 }
309
310 /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
311 * milliseconds.
312 *
313 * When a new snapshot is available (which only occurs if system stats are
314 * enabled), returns it as an smap owned by the caller. The caller must use
315 * both smap_destroy() and free() to completely free the returned data.
316 *
317 * When no new snapshot is available, returns NULL. */
318 struct smap *
319 system_stats_run(void)
320 {
321 struct smap *stats = NULL;
322
323 ovs_mutex_lock(&mutex);
324 if (system_stats) {
325 latch_poll(&latch);
326
327 if (enabled) {
328 stats = system_stats;
329 system_stats = NULL;
330 } else {
331 discard_stats();
332 }
333 }
334 ovs_mutex_unlock(&mutex);
335
336 return stats;
337 }
338
339 /* Causes poll_block() to wake up when system_stats_run() needs to be
340 * called. */
341 void
342 system_stats_wait(void)
343 {
344 if (enabled) {
345 latch_wait(&latch);
346 }
347 }
348
349 static void
350 discard_stats(void) OVS_REQUIRES(mutex)
351 {
352 if (system_stats) {
353 smap_destroy(system_stats);
354 free(system_stats);
355 system_stats = NULL;
356 }
357 }
358
359 static void *
360 system_stats_thread_func(void *arg OVS_UNUSED)
361 {
362 pthread_detach(pthread_self());
363
364 for (;;) {
365 long long int next_refresh;
366 struct smap *stats;
367
368 ovs_mutex_lock(&mutex);
369 while (!enabled) {
370 /* The thread is sleeping, potentially for a long time, and it's
371 * not holding RCU protected references, so it makes sense to
372 * quiesce */
373 ovsrcu_quiesce_start();
374 ovs_mutex_cond_wait(&cond, &mutex);
375 ovsrcu_quiesce_end();
376 }
377 ovs_mutex_unlock(&mutex);
378
379 stats = xmalloc(sizeof *stats);
380 smap_init(stats);
381 get_cpu_cores(stats);
382 get_load_average(stats);
383 get_memory_stats(stats);
384 get_process_stats(stats);
385 get_filesys_stats(stats);
386
387 ovs_mutex_lock(&mutex);
388 discard_stats();
389 system_stats = stats;
390 latch_set(&latch);
391 ovs_mutex_unlock(&mutex);
392
393 next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
394 do {
395 poll_timer_wait_until(next_refresh);
396 poll_block();
397 } while (time_msec() < next_refresh);
398 }
399 }