1 /*
2  * Copyright (c) 2001-2012 Willem Dijkstra
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 /*
32  * Get process statistics from kernel and return them in symon_buf as
33  *
34  * number of processes : ticks_user : ticks_system : ticks_interrupt :
35  * cpuseconds : procsizes : resident segment sizes
36  *
37  */
38 
39 #include "conf.h"
40 
41 #include <sys/param.h>
42 #include <sys/sysctl.h>
43 
44 #include <limits.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "error.h"
49 #include "symon.h"
50 #include "xmalloc.h"
51 
52 #define pagetob(size) (((u_int32_t)size) << proc_pageshift)
53 
54 #ifdef HAS_KERN_PROC2
55 #define KINFO_NEWAPI
56 #define KINFO_MIB KERN_PROC2
57 #define KINFO_STRUCT kinfo_proc2
58 #else
59 #ifndef HAS_KERN_KPPROC
60 #define KINFO_NEWAPI
61 #define KINFO_MIB KERN_PROC
62 #define KINFO_STRUCT kinfo_proc
63 #else
64 #undef KINFO_NEWAPI
65 #define KINFO_STRUCT kinfo_proc
66 #endif
67 #endif
68 
69 /* Globals for this module start with proc_ */
70 static struct KINFO_STRUCT *proc_ps = NULL;
71 static int proc_max = 0;
72 static int proc_cur = 0;
73 static int proc_stathz = 0;
74 static int proc_pageshift;
75 static int proc_pagesize;
76 
77 /* get scale factor cpu percentage counter */
78 #define FIXED_PCTCPU FSCALE
79 typedef long pctcpu;
80 #define pctdouble(p) ((double)(p) / FIXED_PCTCPU)
81 
82 
83 void
gets_proc()84 gets_proc()
85 {
86     int mib[6];
87     int procs;
88     size_t size;
89 
90     /* how much memory is needed */
91     mib[0] = CTL_KERN;
92     mib[1] = KERN_NPROCS;
93     size = sizeof(procs);
94     if (sysctl(mib, 2, &procs, &size, NULL, 0) < 0) {
95         fatal("%s:%d: sysctl failed: can't get kern.nproc",
96               __FILE__, __LINE__);
97     }
98 
99 #ifdef KINFO_NEWAPI
100     /* increase buffers if necessary */
101     if (procs > proc_max) {
102         proc_max = (procs * 5) / 4;
103 
104         if (proc_max > SYMON_MAX_DOBJECTS) {
105             fatal("%s:%d: dynamic object limit (%d) exceeded for kinfo_proc structures",
106                   __FILE__, __LINE__, SYMON_MAX_DOBJECTS);
107         }
108 
109         proc_ps = xrealloc(proc_ps, proc_max * sizeof(struct KINFO_STRUCT));
110     }
111 
112     /* read data in anger */
113     mib[0] = CTL_KERN;
114     mib[1] = KINFO_MIB;
115     mib[2] = KERN_PROC_KTHREAD;
116     mib[3] = 0;
117     mib[4] = sizeof(struct KINFO_STRUCT);
118     mib[5] = proc_max;
119     size = proc_max * sizeof(struct KINFO_STRUCT);
120     if (sysctl(mib, 6, proc_ps, &size, NULL, 0) < 0) {
121         warning("proc probe cannot get processes");
122         proc_cur = 0;
123         return;
124     }
125 
126     if (size % sizeof(struct KINFO_STRUCT) != 0) {
127         warning("proc size mismatch: got %d bytes, not dividable by sizeof(kinfo_proc) %d",
128                 size, sizeof(struct KINFO_STRUCT));
129         proc_cur = 0;
130     } else {
131         proc_cur = size / sizeof(struct KINFO_STRUCT);
132     }
133 #else
134     /* increase buffers if necessary */
135     if (procs > proc_max) {
136         proc_max = (procs * 5) / 4;
137 
138         if (proc_max > SYMON_MAX_DOBJECTS) {
139             fatal("%s:%d: dynamic object limit (%d) exceeded for kinfo_proc structures",
140                   __FILE__, __LINE__, SYMON_MAX_DOBJECTS);
141         }
142 
143         proc_ps = xrealloc(proc_ps, proc_max * sizeof(struct kinfo_proc));
144     }
145 
146     /* read data in anger */
147     mib[0] = CTL_KERN;
148     mib[1] = KERN_PROC;
149     mib[2] = KERN_PROC_KTHREAD;
150     size = proc_max * sizeof(struct kinfo_proc);
151     if (sysctl(mib, 3, proc_ps, &size, NULL, 0) < 0) {
152         warning("proc probe cannot get processes");
153         proc_cur = 0;
154         return;
155     }
156 
157     if (size % sizeof(struct kinfo_proc) != 0) {
158         warning("proc size mismatch: got %d bytes, not dividable by sizeof(kinfo_proc) %d",
159                 size, sizeof(struct kinfo_proc));
160         proc_cur = 0;
161     } else {
162         proc_cur = size / sizeof(struct kinfo_proc);
163     }
164 #endif
165 }
166 
167 void
privinit_proc()168 privinit_proc()
169 {
170     /* EMPTY */
171 }
172 
173 void
init_proc(struct stream * st)174 init_proc(struct stream *st)
175 {
176     int mib[2] = {CTL_KERN, KERN_CLOCKRATE};
177     struct clockinfo cinf;
178     size_t size = sizeof(cinf);
179 
180     /* get clockrate */
181     if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1) {
182         fatal("%s:%d: could not get clockrate", __FILE__, __LINE__);
183     }
184 
185     proc_stathz = cinf.stathz;
186 
187     /* get pagesize */
188     proc_pagesize = sysconf(_SC_PAGESIZE);
189     proc_pageshift = 0;
190     while (proc_pagesize > 1) {
191         proc_pageshift++;
192         proc_pagesize >>= 1;
193     }
194 
195     info("started module proc(%.200s)", st->arg);
196 }
197 
198 int
get_proc(char * symon_buf,int maxlen,struct stream * st)199 get_proc(char *symon_buf, int maxlen, struct stream *st)
200 {
201     int i;
202     struct KINFO_STRUCT *pp;
203     u_quad_t  cpu_ticks = 0;
204     u_quad_t  cpu_uticks = 0;
205     u_quad_t  cpu_iticks = 0;
206     u_quad_t  cpu_sticks =0;
207     u_int32_t cpu_secs = 0;
208     double    cpu_pct = 0;
209     double    cpu_pcti = 0;
210     u_int32_t mem_procsize = 0;
211     u_int32_t mem_rss = 0;
212     int n = 0;
213 
214     for (pp = proc_ps, i = 0; i < proc_cur; pp++, i++) {
215 #ifdef KINFO_NEWAPI
216          if (strncmp(st->arg, pp->p_comm, strlen(st->arg)) == 0) {
217              /* cpu time - accumulated */
218              cpu_uticks += pp->p_uticks;  /* user */
219              cpu_sticks += pp->p_sticks;  /* sys  */
220              cpu_iticks += pp->p_iticks;  /* int  */
221              /* cpu time - percentage since last measurement */
222              cpu_pct = pctdouble(pp->p_pctcpu) * 100.0;
223              cpu_pcti += cpu_pct;
224              /* memory size - shared pages are counted multiple times */
225              mem_procsize += pagetob(pp->p_vm_tsize + /* text pages */
226                                      pp->p_vm_dsize + /* data */
227                                      pp->p_vm_ssize); /* stack */
228              mem_rss += pagetob(pp->p_vm_rssize);     /* rss  */
229 #else
230          if (strncmp(st->arg, pp->kp_proc.p_comm, strlen(st->arg)) == 0) {
231              /* cpu time - accumulated */
232              cpu_uticks += pp->kp_proc.p_uticks;  /* user */
233              cpu_sticks += pp->kp_proc.p_sticks;  /* sys  */
234              cpu_iticks += pp->kp_proc.p_iticks;  /* int  */
235              /* cpu time - percentage since last measurement */
236              cpu_pct = pctdouble(pp->kp_proc.p_pctcpu) * 100.0;
237              cpu_pcti += cpu_pct;
238              /* memory size - shared pages are counted multiple times */
239              mem_procsize += pagetob(pp->kp_eproc.e_vm.vm_tsize + /* text pages */
240                                      pp->kp_eproc.e_vm.vm_dsize + /* data */
241                                      pp->kp_eproc.e_vm.vm_ssize); /* stack */
242              mem_rss += pagetob(pp->kp_eproc.e_vm.vm_rssize);     /* rss  */
243 #endif
244              n++;
245          }
246     }
247 
248     /* calc total cpu_secs spent */
249     cpu_ticks = cpu_uticks + cpu_sticks + cpu_iticks;
250     cpu_secs = cpu_ticks / proc_stathz;
251 
252     return snpack(symon_buf, maxlen, st->arg, MT_PROC,
253                   n,
254                   cpu_uticks, cpu_sticks, cpu_iticks, cpu_secs, cpu_pcti,
255                   mem_procsize, mem_rss );
256 }
257