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