Merge pull request #3329 from efuss/fix-3311
[collectd.git] / src / zone.c
1 /**
2  * collectd - src/zone.c
3  * Copyright (C) 2011       Mathijs Mohlmann
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Mathijs Mohlmann
20  *   Dagobert Michelsen (forward-porting)
21  **/
22
23 #if HAVE_CONFIG_H
24 #include "config.h"
25 #undef HAVE_CONFIG_H
26 #endif
27 /* avoid procfs.h error "Cannot use procfs in the large file compilation
28  * environment" */
29 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
30 #undef _FILE_OFFSET_BITS
31 #undef _LARGEFILE64_SOURCE
32 #endif
33
34 #include "collectd.h"
35
36 #include "plugin.h"
37 #include "utils/common/common.h"
38
39 #include <procfs.h>
40 #include <zone.h>
41
42 #include "utils/avltree/avltree.h"
43
44 #define MAX_PROCFS_PATH 40
45 #define FRC2PCT(pp) (((float)(pp)) / 0x8000 * 100)
46
47 typedef struct zone_stats {
48   ushort_t pctcpu;
49   ushort_t pctmem;
50 } zone_stats_t;
51
52 static int zone_compare(const void *a, const void *b) {
53   if (*(const zoneid_t *)a == *(const zoneid_t *)b)
54     return 0;
55   if (*(const zoneid_t *)a < *(const zoneid_t *)b)
56     return -1;
57   return 1;
58 }
59
60 static int zone_read_procfile(char const *pidstr, char const *name, void *buf,
61                               size_t bufsize) {
62   int fd;
63
64   char procfile[MAX_PROCFS_PATH];
65   (void)snprintf(procfile, sizeof(procfile), "/proc/%s/%s", pidstr, name);
66   if ((fd = open(procfile, O_RDONLY)) == -1) {
67     return 1;
68   }
69
70   if (sread(fd, buf, bufsize) != 0) {
71     ERROR("zone plugin: Reading \"%s\" failed: %s", procfile, STRERRNO);
72     close(fd);
73     return 1;
74   }
75
76   close(fd);
77   return 0;
78 }
79
80 static int zone_submit_value(char *zone, gauge_t value) {
81   value_list_t vl = VALUE_LIST_INIT;
82   value_t values[1];
83
84   values[0].gauge = value;
85
86   vl.values = values;
87   vl.values_len = 1; /*STATIC_ARRAY_SIZE (values);*/
88   sstrncpy(vl.plugin, "zone", sizeof(vl.plugin));
89   sstrncpy(vl.type, "percent", sizeof(vl.type));
90   sstrncpy(vl.type_instance, zone, sizeof(vl.type_instance));
91
92   return plugin_dispatch_values(&vl);
93 }
94
95 static zone_stats_t *zone_find_stats(c_avl_tree_t *tree, zoneid_t zoneid) {
96   zone_stats_t *ret = NULL;
97   zoneid_t *key = NULL;
98
99   if (c_avl_get(tree, (void **)&zoneid, (void **)&ret)) {
100     if (!(ret = malloc(sizeof(*ret)))) {
101       WARNING("zone plugin: no memory");
102       return NULL;
103     }
104     if (!(key = malloc(sizeof(*key)))) {
105       WARNING("zone plugin: no memory");
106       free(ret);
107       return NULL;
108     }
109     *key = zoneid;
110     if (c_avl_insert(tree, key, ret)) {
111       WARNING("zone plugin: error inserting into tree");
112       return NULL;
113     }
114   }
115   return ret;
116 }
117
118 static void zone_submit_values(c_avl_tree_t *tree) {
119   char zonename[ZONENAME_MAX];
120   zoneid_t *zoneid = NULL;
121   zone_stats_t *stats = NULL;
122
123   while (c_avl_pick(tree, (void **)&zoneid, (void **)&stats) == 0) {
124     if (getzonenamebyid(*zoneid, zonename, sizeof(zonename)) == -1) {
125       WARNING("zone plugin: error retrieving zonename");
126     } else {
127       zone_submit_value(zonename, (gauge_t)FRC2PCT(stats->pctcpu));
128     }
129     free(stats);
130     free(zoneid);
131   }
132   c_avl_destroy(tree);
133 }
134
135 static c_avl_tree_t *zone_scandir(DIR *procdir) {
136   pid_t pid;
137   dirent_t *direntp;
138   psinfo_t psinfo;
139   c_avl_tree_t *tree;
140   zone_stats_t *stats;
141
142   if (!(tree = c_avl_create(zone_compare))) {
143     WARNING("zone plugin: Failed to create tree");
144     return NULL;
145   }
146
147   rewinddir(procdir);
148   while ((direntp = readdir(procdir))) {
149     char const *pidstr = direntp->d_name;
150     if (pidstr[0] == '.') /* skip "." and ".."  */
151       continue;
152
153     pid = atoi(pidstr);
154     if (pid == 0 || pid == 2 || pid == 3)
155       continue; /* skip sched, pageout and fsflush */
156
157     if (zone_read_procfile(pidstr, "psinfo", &psinfo, sizeof(psinfo_t)) != 0)
158       continue;
159
160     stats = zone_find_stats(tree, psinfo.pr_zoneid);
161     if (stats) {
162       stats->pctcpu += psinfo.pr_pctcpu;
163       stats->pctmem += psinfo.pr_pctmem;
164     }
165   }
166   return tree;
167 }
168
169 static int zone_read(void) {
170   DIR *procdir;
171   c_avl_tree_t *tree;
172
173   if ((procdir = opendir("/proc")) == NULL) {
174     ERROR("zone plugin: cannot open /proc directory\n");
175     return -1;
176   }
177
178   tree = zone_scandir(procdir);
179   closedir(procdir);
180   if (tree == NULL) {
181     return -1;
182   }
183   zone_submit_values(tree); /* this also frees tree */
184   return 0;
185 }
186
187 void module_register(void) {
188   plugin_register_read("zone", zone_read);
189 } /* void module_register */