Changed the cpu plugin to not use its headerfile anymore.
[collectd.git] / src / cpu.c
1 /**
2  * collectd - src/cpu.c
3  * Copyright (C) 2005  Florian octo Forster
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; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "plugin.h"
25 #include "common.h"
26
27 #ifdef HAVE_LIBKSTAT
28 # include <sys/sysinfo.h>
29 #endif /* HAVE_LIBKSTAT */
30
31 #ifdef HAVE_SYSCTLBYNAME
32 # ifdef HAVE_SYS_SYSCTL_H
33 #  include <sys/sysctl.h>
34 # endif
35
36 # ifdef HAVE_SYS_DKSTAT_H
37 #  include <sys/dkstat.h>
38 # endif
39
40 # if !defined(CP_USER) || !defined(CP_NICE) || !defined(CP_SYS) || !defined(CP_INTR) || !defined(CP_IDLE) || !defined(CPUSTATES)
41 #  define CP_USER   0
42 #  define CP_NICE   1
43 #  define CP_SYS    2
44 #  define CP_INTR   3
45 #  define CP_IDLE   4
46 #  define CPUSTATES 5
47 # endif
48 #endif /* HAVE_SYSCTLBYNAME */
49
50 #ifdef HAVE_LIBKSTAT
51 /* colleague tells me that Sun doesn't sell systems with more than 100 or so CPUs.. */
52 # define MAX_NUMCPU 256
53 extern kstat_ctl_t *kc;
54 static kstat_t *ksp[MAX_NUMCPU];
55 static int numcpu;
56 #endif /* HAVE_LIBKSTAT */
57
58 #ifdef HAVE_SYSCTLBYNAME
59 static int numcpu;
60 #endif /* HAVE_SYSCTLBYNAME */
61
62 #define MODULE_NAME "cpu"
63
64 static char *cpu_filename = "cpu-%s.rrd";
65
66 static char *ds_def[] =
67 {
68         "DS:user:COUNTER:25:0:100",
69         "DS:nice:COUNTER:25:0:100",
70         "DS:syst:COUNTER:25:0:100",
71         "DS:idle:COUNTER:25:0:100",
72         "DS:wait:COUNTER:25:0:100",
73         NULL
74 };
75 static int ds_num = 5;
76
77 void cpu_init (void)
78 {
79 #ifdef HAVE_LIBKSTAT
80         kstat_t *ksp_chain;
81
82         numcpu = 0;
83
84         if (kc == NULL)
85                 return;
86
87         /* Solaris doesn't count linear.. *sigh* */
88         for (numcpu = 0, ksp_chain = kc->kc_chain;
89                         (numcpu < MAX_NUMCPU) && (ksp_chain != NULL);
90                         ksp_chain = ksp_chain->ks_next)
91                 if (strncmp (ksp_chain->ks_module, "cpu_stat", 8) == 0)
92                         ksp[numcpu++] = ksp_chain;
93 /* #endif HAVE_LIBKSTAT */
94
95 #elif defined (HAVE_SYSCTLBYNAME)
96         size_t numcpu_size;
97
98         numcpu_size = sizeof (numcpu);
99
100         if (sysctlbyname ("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0)
101         {
102                 syslog (LOG_WARNING, "cpu: sysctlbyname: %s", strerror (errno));
103                 return;
104         }
105
106         if (numcpu != 1)
107                 syslog (LOG_NOTICE, "cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
108 #endif
109
110         return;
111 }
112
113 void cpu_write (char *host, char *inst, char *val)
114 {
115         char file[512];
116         int status;
117
118         status = snprintf (file, 512, cpu_filename, inst);
119         if (status < 1)
120                 return;
121         else if (status >= 512)
122                 return;
123
124         rrd_update_file (host, file, val, ds_def, ds_num);
125 }
126
127 #define BUFSIZE 512
128 static void cpu_submit (int cpu_num, unsigned long long user,
129                 unsigned long long nice, unsigned long long syst,
130                 unsigned long long idle, unsigned long long wait)
131 {
132         char buf[BUFSIZE];
133         char cpu[16];
134
135         if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu", (unsigned int) curtime,
136                                 user, nice, syst, idle, wait) >= BUFSIZE)
137                 return;
138         snprintf (cpu, 16, "%i", cpu_num);
139
140         plugin_submit (MODULE_NAME, cpu, buf);
141 }
142 #undef BUFSIZE
143
144 static void cpu_read (void)
145 {
146 #ifdef KERNEL_LINUX
147 #define BUFSIZE 1024
148         int cpu;
149         unsigned long long user, nice, syst, idle;
150         unsigned long long wait, intr, sitr; /* sitr == soft interrupt */
151         FILE *fh;
152         char buf[BUFSIZE];
153
154         char *fields[9];
155         int numfields;
156
157         if ((fh = fopen ("/proc/stat", "r")) == NULL)
158         {
159                 syslog (LOG_WARNING, "cpu: fopen: %s", strerror (errno));
160                 return;
161         }
162
163         while (fgets (buf, BUFSIZE, fh) != NULL)
164         {
165                 if (strncmp (buf, "cpu", 3))
166                         continue;
167                 if ((buf[3] < '0') || (buf[3] > '9'))
168                         continue;
169
170                 numfields = strsplit (buf, fields, 9);
171                 if (numfields < 5)
172                         continue;
173
174                 cpu = atoi (fields[0] + 3);
175                 user = atoll (fields[1]);
176                 nice = atoll (fields[2]);
177                 syst = atoll (fields[3]);
178                 idle = atoll (fields[4]);
179
180                 if (numfields >= 8)
181                 {
182                         wait = atoll (fields[5]);
183                         intr = atoll (fields[6]);
184                         sitr = atoll (fields[7]);
185
186                         /* I doubt anyone cares about the time spent in
187                          * interrupt handlers.. */
188                         syst += intr + sitr;
189                 }
190                 else
191                 {
192                         wait = 0LL;
193                 }
194
195                 cpu_submit (cpu, user, nice, syst, idle, wait);
196         }
197
198         fclose (fh);
199 #undef BUFSIZE
200 /* #endif defined(KERNEL_LINUX) */
201
202 #elif defined(HAVE_LIBKSTAT)
203         int cpu;
204         unsigned long long user, syst, idle, wait;
205         static cpu_stat_t cs;
206
207         if (kc == NULL)
208                 return;
209
210         for (cpu = 0; cpu < numcpu; cpu++)
211         {
212                 if (kstat_read (kc, ksp[cpu], &cs) == -1)
213                         continue; /* error message? */
214
215                 idle = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_IDLE];
216                 user = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_USER];
217                 syst = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_KERNEL];
218                 wait = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_WAIT];
219
220                 cpu_submit (ksp[cpu]->ks_instance,
221                                 user, 0LL, syst, idle, wait);
222         }
223 /* #endif defined(HAVE_LIBKSTAT) */
224
225 #elif defined (HAVE_SYSCTLBYNAME)
226         long cpuinfo[CPUSTATES];
227         size_t cpuinfo_size;
228
229         cpuinfo_size = sizeof (cpuinfo);
230
231         if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
232         {
233                 syslog (LOG_WARNING, "cpu: sysctlbyname: %s", strerror (errno));
234                 return;
235         }
236
237         cpuinfo[CP_SYS] += cpuinfo[CP_INTR];
238
239         /* FIXME: Instance is always `0' */
240         cpu_submit (0, cpuinfo[CP_USER], cpuinfo[CP_NICE], cpuinfo[CP_SYS], cpuinfo[CP_IDLE], 0LL);
241 #endif
242
243         return;
244 }
245
246 void module_register (void)
247 {
248         plugin_register (MODULE_NAME, cpu_init, cpu_read, cpu_write);
249 }
250
251 #undef MODULE_NAME