1 /* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "system-stats.h"
29 #if HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
36 #include "openvswitch/dynamic-string.h"
37 #include "openvswitch/json.h"
39 #include "openvswitch/ofpbuf.h"
41 #include "ovs-thread.h"
42 #include "openvswitch/poll-loop.h"
43 #include "openvswitch/shash.h"
47 #include "openvswitch/vlog.h"
49 VLOG_DEFINE_THIS_MODULE(system_stats
);
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. */
57 #include <asm/param.h>
63 get_cpu_cores(struct smap
*stats
)
65 long int n_cores
= count_cpu_cores();
67 smap_add_format(stats
, "cpu", "%ld", n_cores
);
72 get_load_average(struct smap
*stats OVS_UNUSED
)
77 if (getloadavg(loadavg
, 3) == 3) {
78 smap_add_format(stats
, "load_average", "%.2f,%.2f,%.2f",
79 loadavg
[0], loadavg
[1], loadavg
[2]);
85 get_memory_stats(struct smap
*stats
)
88 unsigned int pagesize
= get_page_size();
90 long int phys_pages
= sysconf(_SC_PHYS_PAGES
);
92 long int phys_pages
= 0;
94 #ifdef _SC_AVPHYS_PAGES
95 long int avphys_pages
= sysconf(_SC_AVPHYS_PAGES
);
97 long int avphys_pages
= 0;
99 int mem_total
, mem_used
;
102 if (pagesize
<= 0 || phys_pages
<= 0 || avphys_pages
<= 0) {
106 mem_total
= phys_pages
* (pagesize
/ 1024);
107 mem_used
= (phys_pages
- avphys_pages
) * (pagesize
/ 1024);
109 MEMORYSTATUS memory_status
;
110 GlobalMemoryStatus(&memory_status
);
112 mem_total
= memory_status
.dwTotalPhys
;
113 mem_used
= memory_status
.dwTotalPhys
- memory_status
.dwAvailPhys
;
115 smap_add_format(stats
, "memory", "%d,%d", mem_total
, mem_used
);
117 static const char file_name
[] = "/proc/meminfo";
118 int mem_used
, mem_cache
, swap_used
;
129 stream
= fopen(file_name
, "r");
131 VLOG_WARN_ONCE("%s: open failed (%s)",
132 file_name
, ovs_strerror(errno
));
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
)) {
147 if (ovs_scan(line
, "%15[^:]: %u", key
, &value
)) {
148 int *valuep
= shash_find_data(&dict
, key
);
155 shash_destroy(&dict
);
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
);
166 get_process_stats(struct smap
*stats
)
172 dir
= opendir(ovs_rundir());
174 VLOG_ERR_ONCE("%s: open failed (%s)",
175 ovs_rundir(), ovs_strerror(errno
));
179 while ((de
= readdir(dir
)) != NULL
) {
180 struct process_info pinfo
;
186 #ifdef _DIRENT_HAVE_D_TYPE
187 if (de
->d_type
!= DT_UNKNOWN
&& de
->d_type
!= DT_REG
) {
192 extension
= strrchr(de
->d_name
, '.');
193 if (!extension
|| strcmp(extension
, ".pid")) {
197 file_name
= xasprintf("%s/%s", ovs_rundir(), de
->d_name
);
198 pid
= read_pidfile(file_name
);
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
);
212 smap_add(stats
, key
, "");
223 get_filesys_stats(struct smap
*stats OVS_UNUSED
)
225 #if HAVE_GETMNTENT_R && HAVE_STATVFS
226 static const char file_name
[] = "/etc/mtab";
227 struct mntent mntent
;
233 stream
= setmntent(file_name
, "r");
235 VLOG_ERR_ONCE("%s: open failed (%s)", file_name
, ovs_strerror(errno
));
240 while ((me
= getmntent_r(stream
, &mntent
, buf
, sizeof buf
)) != NULL
) {
241 unsigned long long int total
, free
;
245 /* Skip non-local and read-only filesystems. */
246 if (strncmp(me
->mnt_fsname
, "/dev", 4)
247 || !strstr(me
->mnt_opts
, "rw")) {
251 /* Given the mount point we can stat the file system. */
252 if (statvfs(me
->mnt_dir
, &vfs
) && vfs
.f_flag
& ST_RDONLY
) {
257 /* Now format the data. */
259 ds_put_char(&s
, ' ');
261 for (p
= me
->mnt_dir
; *p
!= '\0'; p
++) {
262 ds_put_char(&s
, *p
== ' ' || *p
== ',' ? '_' : *p
);
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
);
271 smap_add(stats
, "file_systems", ds_cstr(&s
));
274 #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */
277 #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
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
);
283 static bool started
OVS_GUARDED_BY(mutex
);
284 static struct smap
*system_stats
OVS_GUARDED_BY(mutex
);
286 OVS_NO_RETURN
static void *system_stats_thread_func(void *);
287 static void discard_stats(void);
289 /* Enables or disables system stats collection, according to 'enable'. */
291 system_stats_enable(bool enable
)
293 if (enabled
!= enable
) {
294 ovs_mutex_lock(&mutex
);
297 ovs_thread_create("system_stats",
298 system_stats_thread_func
, NULL
);
303 xpthread_cond_signal(&cond
);
306 ovs_mutex_unlock(&mutex
);
310 /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
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.
317 * When no new snapshot is available, returns NULL. */
319 system_stats_run(void)
321 struct smap
*stats
= NULL
;
323 ovs_mutex_lock(&mutex
);
328 stats
= system_stats
;
334 ovs_mutex_unlock(&mutex
);
339 /* Causes poll_block() to wake up when system_stats_run() needs to be
342 system_stats_wait(void)
350 discard_stats(void) OVS_REQUIRES(mutex
)
353 smap_destroy(system_stats
);
360 system_stats_thread_func(void *arg OVS_UNUSED
)
362 pthread_detach(pthread_self());
365 long long int next_refresh
;
368 ovs_mutex_lock(&mutex
);
370 /* The thread is sleeping, potentially for a long time, and it's
371 * not holding RCU protected references, so it makes sense to
373 ovsrcu_quiesce_start();
374 ovs_mutex_cond_wait(&cond
, &mutex
);
375 ovsrcu_quiesce_end();
377 ovs_mutex_unlock(&mutex
);
379 stats
= xmalloc(sizeof *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
);
387 ovs_mutex_lock(&mutex
);
389 system_stats
= stats
;
391 ovs_mutex_unlock(&mutex
);
393 next_refresh
= time_msec() + SYSTEM_STATS_INTERVAL
;
395 poll_timer_wait_until(next_refresh
);
397 } while (time_msec() < next_refresh
);