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