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