ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/process_stats.c
Revision: 1.36
Committed: Sun Apr 4 16:12:59 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.35: +22 -10 lines
Log Message:
Revert to using sysctl instead of kvm to get process stats. This should be
more compatible with NetBSD and OpenBSD. Still need to use kvm to get the
command line args, but this seems unavoidable.

At this stage we don't bail if kvm fails (eg. not enough privs), instead
the command line args will just be set to NULL. Seems a fair compromise.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: process_stats.c,v 1.35 2004/04/04 15:56:53 tdb Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "statgrab.h"
29 #if defined(SOLARIS) || defined(LINUX)
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <string.h>
35 #endif
36
37 #ifdef SOLARIS
38 #include <procfs.h>
39 #include <limits.h>
40 #define PROC_LOCATION "/proc"
41 #define MAX_FILE_LENGTH PATH_MAX
42 #endif
43 #ifdef LINUX
44 #include <limits.h>
45 #include <unistd.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #define PROC_LOCATION "/proc"
49 #define MAX_FILE_LENGTH PATH_MAX
50 #endif
51 #ifdef ALLBSD
52 #include <stdlib.h>
53 #include <sys/param.h>
54 #include <sys/sysctl.h>
55 #if defined(FREEBSD) || defined(DFBSD)
56 #include <sys/user.h>
57 #else
58 #include <sys/proc.h>
59 #endif
60 #include <string.h>
61 #include <paths.h>
62 #include <fcntl.h>
63 #include <limits.h>
64 #include <kvm.h>
65 #endif
66
67 int get_proc_snapshot(proc_state_t **ps){
68 proc_state_t *proc_state = NULL;
69 proc_state_t *proc_state_ptr;
70 int proc_state_size = 0;
71 #ifdef ALLBSD
72 int mib[3];
73 size_t size;
74 struct kinfo_proc *kp_stats;
75 int procs, i, alloc;
76 static kvm_t *kvmd;
77 char **args;
78 char *proctitle;
79 #endif
80 #if defined(SOLARIS) || defined(LINUX)
81 DIR *proc_dir;
82 struct dirent *dir_entry;
83 char filename[MAX_FILE_LENGTH];
84 FILE *f;
85 #ifdef SOLARIS
86 psinfo_t process_info;
87 #endif
88 #ifdef LINUX
89 char s;
90 /* If someone has a executable of 4k filename length, they deserve to get it truncated :) */
91 char ps_name[4096];
92 char *ptr;
93 static char *psargs = NULL;
94 static int psarg_size = 0;
95 unsigned long stime, utime;
96 int x;
97 int fn;
98 int toread;
99 ssize_t size;
100 int t_read;
101 #endif
102
103 if((proc_dir=opendir(PROC_LOCATION))==NULL){
104 return -1;
105 }
106
107 while((dir_entry=readdir(proc_dir))!=NULL){
108 if(atoi(dir_entry->d_name) == 0) continue;
109
110 #ifdef SOLARIS
111 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
112 #endif
113 #ifdef LINUX
114 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
115 #endif
116 if((f=fopen(filename, "r"))==NULL){
117 /* Open failed.. Process since vanished, or the path was too long.
118 * Ah well, move onwards to the next one */
119 continue;
120 }
121 #ifdef SOLARIS
122 fread(&process_info, sizeof(psinfo_t), 1, f);
123 #endif
124
125 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
126 proc_state_ptr = proc_state+proc_state_size;
127 #ifdef SOLARIS
128 proc_state_ptr->pid = process_info.pr_pid;
129 proc_state_ptr->parent = process_info.pr_ppid;
130 proc_state_ptr->pgid = process_info.pr_pgid;
131 proc_state_ptr->uid = process_info.pr_uid;
132 proc_state_ptr->euid = process_info.pr_euid;
133 proc_state_ptr->gid = process_info.pr_gid;
134 proc_state_ptr->egid = process_info.pr_egid;
135 proc_state_ptr->proc_size = (process_info.pr_size) * 1024;
136 proc_state_ptr->proc_resident = (process_info.pr_rssize) * 1024;
137 proc_state_ptr->time_spent = process_info.pr_time.tv_sec;
138 proc_state_ptr->cpu_percent = (process_info.pr_pctcpu * 100.0) / 0x8000;
139 proc_state_ptr->process_name = strdup(process_info.pr_fname);
140 proc_state_ptr->proctitle = strdup(process_info.pr_psargs);
141
142 if(process_info.pr_lwp.pr_state==1) proc_state_ptr->state = SLEEPING;
143 if(process_info.pr_lwp.pr_state==2) proc_state_ptr->state = RUNNING;
144 if(process_info.pr_lwp.pr_state==3) proc_state_ptr->state = ZOMBIE;
145 if(process_info.pr_lwp.pr_state==4) proc_state_ptr->state = STOPPED;
146 if(process_info.pr_lwp.pr_state==6) proc_state_ptr->state = RUNNING;
147 #endif
148 #ifdef LINUX
149 x = fscanf(f, "%d %4096s %c %d %d %*d %*d %*d %*lu %*lu %*lu %*lu %*lu %lu %lu %*ld %*ld %*ld %d %*ld %*ld %*lu %llu %llu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*d %*d\n", &(proc_state_ptr->pid), ps_name, &s, &(proc_state_ptr->parent), &(proc_state_ptr->pgid), &utime, &stime, &(proc_state_ptr->nice), &(proc_state_ptr->proc_size), &(proc_state_ptr->proc_resident));
150 proc_state_ptr->proc_resident = proc_state_ptr->proc_resident * getpagesize();
151 if(s == 'S') proc_state_ptr->state = SLEEPING;
152 if(s == 'R') proc_state_ptr->state = RUNNING;
153 if(s == 'Z') proc_state_ptr->state = ZOMBIE;
154 if(s == 'T') proc_state_ptr->state = STOPPED;
155 if(s == 'D') proc_state_ptr->state = STOPPED;
156
157 /* pa_name[0] should = '(' */
158 ptr = strchr(&ps_name[1], ')');
159 if(ptr !=NULL) *ptr='\0';
160 proc_state_ptr->process_name = strdup(&ps_name[1]);
161
162 /* Need to do cpu */
163
164
165 /* proctitle */
166 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/cmdline", dir_entry->d_name);
167
168 if((fn=open(filename, O_RDONLY)) == -1){
169 /* Open failed.. Process since vanished, or the path was too long.
170 * Ah well, move onwards to the next one */
171 continue;
172 }
173 #define PSARG_START_SIZE 128
174 if(psargs == NULL){
175 psargs = malloc(PSARG_START_SIZE);
176 psarg_size = PSARG_START_SIZE;
177 }
178 ptr = psargs;
179 t_read = 0;
180 toread = psarg_size;
181 while((size = read(fn, ptr, toread)) == toread){
182 psargs = realloc(psargs, (psarg_size + PSARG_START_SIZE));
183 ptr = psargs+psarg_size;
184 t_read = psarg_size;
185 psarg_size+=PSARG_START_SIZE;
186 toread = PSARG_START_SIZE;
187 }
188 if(size != -1) t_read+=size;
189
190 ptr = psargs;
191 for(x=0; x<t_read; x++){
192 if (*ptr == '\0') *ptr = ' ';
193 ptr++;
194 }
195 /* for safety sake */
196 psargs[t_read] = '\0';
197
198 proc_state_ptr->proctitle = strdup(psargs);
199
200 #endif
201
202 proc_state_size++;
203
204 fclose(f);
205 }
206 closedir(proc_dir);
207 #endif
208
209 #ifdef ALLBSD
210 mib[0] = CTL_KERN;
211 mib[1] = KERN_PROC;
212 mib[2] = KERN_PROC_ALL;
213
214 if(sysctl(mib, 3, NULL, &size, NULL, 0) < 0) {
215 return NULL;
216 }
217
218 procs = size / sizeof(struct kinfo_proc);
219
220 kp_stats = malloc(size);
221 if(kp_stats == NULL) {
222 return NULL;
223 }
224
225 if(sysctl(mib, 3, kp_stats, &size, NULL, 0) < 0) {
226 free(kp_stats);
227 return NULL;
228 }
229
230 #if defined(NETBSD) || defined(OPENBSD)
231 kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL);
232 #else
233 kvmd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
234 #endif
235
236 for (i = 0; i < procs; i++) {
237 /* replace with something more sensible */
238 proc_state = realloc(proc_state,
239 (1+proc_state_size)*sizeof(proc_state_t));
240 if(proc_state == NULL ) {
241 return NULL;
242 }
243 proc_state_ptr = proc_state+proc_state_size;
244
245 #ifdef FREEBSD5
246 proc_state_ptr->process_name =
247 strdup(kp_stats[i].ki_comm);
248 #elif defined(DFBSD)
249 proc_state_ptr->process_name =
250 strdup(kp_stats[i].kp_thread.td_comm);
251 #else
252 proc_state_ptr->process_name =
253 strdup(kp_stats[i].kp_proc.p_comm);
254 #endif
255
256 args = kvm_getargv(kvmd, &(kp_stats[i]), 0);
257 if(args != NULL) {
258 alloc = 1;
259 proctitle = malloc(alloc);
260 if(proctitle == NULL) {
261 return NULL;
262 }
263 while(*args != NULL) {
264 if(strlen(proctitle) + strlen(*args) >= alloc) {
265 alloc = (alloc + strlen(*args)) << 1;
266 proctitle = realloc(proctitle, alloc);
267 if(proctitle == NULL) {
268 return NULL;
269 }
270 }
271 strncat(proctitle, *args, strlen(*args));
272 strncat(proctitle, " ", 1);
273 args++;
274 }
275 /* remove trailing space */
276 proctitle[strlen(proctitle)-1] = NULL;
277 proc_state_ptr->proctitle = proctitle;
278 }
279 else {
280 /* Should probably be returning NULL here */
281 proc_state_ptr->proctitle =
282 malloc(strlen(proc_state_ptr->process_name)+4);
283 if(proc_state_ptr->proctitle == NULL) {
284 return NULL;
285 }
286 sprintf(proc_state_ptr->proctitle, " (%s)",
287 proc_state_ptr->process_name);
288 }
289
290 #ifdef FREEBSD5
291 proc_state_ptr->pid = kp_stats[i].ki_pid;
292 proc_state_ptr->parent = kp_stats[i].ki_ppid;
293 proc_state_ptr->pgid = kp_stats[i].ki_pgid;
294 #else
295 proc_state_ptr->pid = kp_stats[i].kp_proc.p_pid;
296 proc_state_ptr->parent = kp_stats[i].kp_eproc.e_ppid;
297 proc_state_ptr->pgid = kp_stats[i].kp_eproc.e_pgid;
298 #endif
299
300 #ifdef FREEBSD5
301 proc_state_ptr->uid = kp_stats[i].ki_ruid;
302 proc_state_ptr->euid = kp_stats[i].ki_uid;
303 proc_state_ptr->gid = kp_stats[i].ki_rgid;
304 proc_state_ptr->egid = kp_stats[i].ki_svgid;
305 #elif defined(DFBSD)
306 proc_state_ptr->uid = kp_stats[i].kp_eproc.e_ucred.cr_ruid;
307 proc_state_ptr->euid = kp_stats[i].kp_eproc.e_ucred.cr_svuid;
308 proc_state_ptr->gid = kp_stats[i].kp_eproc.e_ucred.cr_rgid;
309 proc_state_ptr->egid = kp_stats[i].kp_eproc.e_ucred.cr_svgid;
310 #else
311 proc_state_ptr->uid = kp_stats[i].kp_eproc.e_pcred.p_ruid;
312 proc_state_ptr->euid = kp_stats[i].kp_eproc.e_pcred.p_svuid;
313 proc_state_ptr->gid = kp_stats[i].kp_eproc.e_pcred.p_rgid;
314 proc_state_ptr->egid = kp_stats[i].kp_eproc.e_pcred.p_svgid;
315 #endif
316
317 #ifdef FREEBSD5
318 proc_state_ptr->proc_size = kp_stats[i].ki_size;
319 /* This is in pages */
320 proc_state_ptr->proc_resident =
321 kp_stats[i].ki_rssize * getpagesize();
322 /* This is in microseconds */
323 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
324 proc_state_ptr->cpu_percent =
325 ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
326 proc_state_ptr->nice = kp_stats[i].ki_nice;
327 #else
328 proc_state_ptr->proc_size =
329 kp_stats[i].kp_eproc.e_vm.vm_map.size;
330 /* This is in pages */
331 proc_state_ptr->proc_resident =
332 kp_stats[i].kp_eproc.e_vm.vm_rssize * getpagesize();
333 #if defined(NETBSD) || defined(OPENBSD)
334 proc_state_ptr->time_spent =
335 kp_stats[i].kp_proc.p_rtime.tv_sec;
336 #elif defined(DFBSD)
337 proc_state_ptr->time_spent =
338 ( kp_stats[i].kp_thread.td_uticks +
339 kp_stats[i].kp_thread.td_sticks +
340 kp_stats[i].kp_thread.td_iticks ) / 1000000;
341 #else
342 /* This is in microseconds */
343 proc_state_ptr->time_spent =
344 kp_stats[i].kp_proc.p_runtime / 1000000;
345 #endif
346 proc_state_ptr->cpu_percent =
347 ((double)kp_stats[i].kp_proc.p_pctcpu / FSCALE) * 100.0;
348 proc_state_ptr->nice = kp_stats[i].kp_proc.p_nice;
349 #endif
350
351 #ifdef FREEBSD5
352 switch (kp_stats[i].ki_stat) {
353 #else
354 switch (kp_stats[i].kp_proc.p_stat) {
355 #endif
356 case SIDL:
357 case SRUN:
358 #ifdef SONPROC
359 case SONPROC: /* NetBSD */
360 #endif
361 proc_state_ptr->state = RUNNING;
362 break;
363 case SSLEEP:
364 #ifdef SWAIT
365 case SWAIT: /* FreeBSD 5 */
366 #endif
367 #ifdef SLOCK
368 case SLOCK: /* FreeBSD 5 */
369 #endif
370 proc_state_ptr->state = SLEEPING;
371 break;
372 case SSTOP:
373 proc_state_ptr->state = STOPPED;
374 break;
375 case SZOMB:
376 #ifdef SDEAD
377 case SDEAD: /* OpenBSD & NetBSD */
378 #endif
379 proc_state_ptr->state = ZOMBIE;
380 break;
381 default:
382 proc_state_ptr->state = UNKNOWN;
383 break;
384 }
385 proc_state_size++;
386 }
387
388 free(kp_stats);
389 #endif
390
391 *ps = proc_state;
392 return proc_state_size;
393 }
394
395 process_stat_t *get_process_stats() {
396 static process_stat_t process_stat;
397 proc_state_t *ps;
398 int ps_size, x;
399
400 process_stat.sleeping = 0;
401 process_stat.running = 0;
402 process_stat.zombie = 0;
403 process_stat.stopped = 0;
404 process_stat.total = 0;
405
406 ps_size = get_proc_snapshot(&ps);
407
408 if(ps_size == NULL) {
409 return NULL;
410 }
411
412 for(x = 0; x < ps_size; x++) {
413 switch (ps->state) {
414 /* currently no mapping for UNKNOWN in process_stat_t */
415 case RUNNING:
416 process_stat.running++;
417 break;
418 case SLEEPING:
419 process_stat.sleeping++;
420 break;
421 case STOPPED:
422 process_stat.stopped++;
423 break;
424 case ZOMBIE:
425 process_stat.zombie++;
426 break;
427 }
428 ps++;
429 }
430
431 process_stat.total = ps_size;
432
433 return &process_stat;
434 }