]>
Commit | Line | Data |
---|---|---|
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 | 49 | VLOG_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 | ||
62 | static void | |
57c8677b | 63 | get_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 | ||
71 | static void | |
57c8677b | 72 | get_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 | 84 | static void |
57c8677b | 85 | get_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 | 165 | static void |
57c8677b | 166 | get_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 | ||
223 | static void | |
57c8677b | 224 | get_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 | 280 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; |
59987a62 | 281 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
97be1538 | 282 | static struct latch latch OVS_GUARDED_BY(mutex); |
59987a62 | 283 | static bool enabled; |
97be1538 EJ |
284 | static bool started OVS_GUARDED_BY(mutex); |
285 | static struct smap *system_stats OVS_GUARDED_BY(mutex); | |
59987a62 | 286 | |
cab50449 | 287 | OVS_NO_RETURN static void *system_stats_thread_func(void *); |
59987a62 | 288 | static void discard_stats(void); |
35a22d8c | 289 | |
5a13f2f4 | 290 | /* Enables or disables system stats collection, according to 'enable'. */ |
ce887677 | 291 | void |
5a13f2f4 | 292 | system_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. */ | |
319 | struct smap * | |
320 | system_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. */ | |
342 | void | |
343 | system_stats_wait(void) | |
344 | { | |
59987a62 BP |
345 | if (enabled) { |
346 | latch_wait(&latch); | |
347 | } | |
348 | } | |
349 | ||
350 | static void | |
344e21d4 | 351 | discard_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 | 360 | static void * |
59987a62 BP |
361 | system_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 | } |