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

# User Rev Content
1 tdb 1.17 /*
2 pajs 1.1 * i-scream central monitoring system
3 tdb 1.8 * http://www.i-scream.org
4 tdb 1.17 * Copyright (C) 2000-2004 i-scream
5 pajs 1.1 *
6 tdb 1.17 * 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 pajs 1.1 *
11 tdb 1.17 * This library is distributed in the hope that it will be useful,
12 pajs 1.1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 tdb 1.17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     * Lesser General Public License for more details.
15 pajs 1.1 *
16 tdb 1.17 * 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 tdb 1.18 *
21 tdb 1.36 * $Id: process_stats.c,v 1.35 2004/04/04 15:56:53 tdb Exp $
22 pajs 1.1 */
23    
24     #ifdef HAVE_CONFIG_H
25     #include "config.h"
26     #endif
27    
28 pajs 1.6 #include "statgrab.h"
29     #if defined(SOLARIS) || defined(LINUX)
30 pajs 1.1 #include <stdio.h>
31     #include <stdlib.h>
32 pajs 1.5 #include <sys/types.h>
33     #include <dirent.h>
34     #include <string.h>
35 pajs 1.6 #endif
36 pajs 1.1
37     #ifdef SOLARIS
38     #include <procfs.h>
39     #include <limits.h>
40 pajs 1.5 #define PROC_LOCATION "/proc"
41     #define MAX_FILE_LENGTH PATH_MAX
42     #endif
43     #ifdef LINUX
44 ats 1.16 #include <limits.h>
45 pajs 1.25 #include <unistd.h>
46     #include <sys/stat.h>
47     #include <fcntl.h>
48 pajs 1.1 #define PROC_LOCATION "/proc"
49     #define MAX_FILE_LENGTH PATH_MAX
50     #endif
51 ats 1.13 #ifdef ALLBSD
52 tdb 1.19 #include <stdlib.h>
53 pajs 1.6 #include <sys/param.h>
54     #include <sys/sysctl.h>
55 tdb 1.22 #if defined(FREEBSD) || defined(DFBSD)
56     #include <sys/user.h>
57     #else
58 tdb 1.19 #include <sys/proc.h>
59 ats 1.13 #endif
60 tdb 1.29 #include <string.h>
61     #include <paths.h>
62     #include <fcntl.h>
63     #include <limits.h>
64     #include <kvm.h>
65 pajs 1.6 #endif
66 pajs 1.1
67 pajs 1.23 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 tdb 1.26 #ifdef ALLBSD
72     int mib[3];
73     size_t size;
74     struct kinfo_proc *kp_stats;
75 tdb 1.29 int procs, i, alloc;
76     static kvm_t *kvmd;
77     char **args;
78     char *proctitle;
79 tdb 1.26 #endif
80 pajs 1.24 #if defined(SOLARIS) || defined(LINUX)
81 pajs 1.23 DIR *proc_dir;
82     struct dirent *dir_entry;
83     char filename[MAX_FILE_LENGTH];
84     FILE *f;
85 pajs 1.24 #ifdef SOLARIS
86 pajs 1.5 psinfo_t process_info;
87 pajs 1.24 #endif
88 pajs 1.25 #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 pajs 1.1
103 pajs 1.23 if((proc_dir=opendir(PROC_LOCATION))==NULL){
104 pajs 1.25 return -1;
105 pajs 1.23 }
106    
107     while((dir_entry=readdir(proc_dir))!=NULL){
108     if(atoi(dir_entry->d_name) == 0) continue;
109    
110 pajs 1.24 #ifdef SOLARIS
111 pajs 1.23 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
112 pajs 1.24 #endif
113 pajs 1.25 #ifdef LINUX
114     snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
115     #endif
116 pajs 1.23 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 pajs 1.24 #ifdef SOLARIS
122 pajs 1.23 fread(&process_info, sizeof(psinfo_t), 1, f);
123 pajs 1.25 #endif
124 pajs 1.1
125 pajs 1.23 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
126     proc_state_ptr = proc_state+proc_state_size;
127 pajs 1.25 #ifdef SOLARIS
128 pajs 1.23 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 pajs 1.24
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 pajs 1.25 #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 pajs 1.24 #endif
201 pajs 1.23
202     proc_state_size++;
203 pajs 1.1
204 pajs 1.23 fclose(f);
205     }
206     closedir(proc_dir);
207 tdb 1.26 #endif
208    
209     #ifdef ALLBSD
210 tdb 1.36 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 tdb 1.33
230 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
231 tdb 1.33 kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL);
232     #else
233 tdb 1.29 kvmd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
234 tdb 1.33 #endif
235 tdb 1.26
236     for (i = 0; i < procs; i++) {
237 tdb 1.29 /* replace with something more sensible */
238 tdb 1.30 proc_state = realloc(proc_state,
239     (1+proc_state_size)*sizeof(proc_state_t));
240 tdb 1.29 if(proc_state == NULL ) {
241     return NULL;
242     }
243 tdb 1.26 proc_state_ptr = proc_state+proc_state_size;
244 tdb 1.30
245     #ifdef FREEBSD5
246     proc_state_ptr->process_name =
247     strdup(kp_stats[i].ki_comm);
248 tdb 1.35 #elif defined(DFBSD)
249     proc_state_ptr->process_name =
250     strdup(kp_stats[i].kp_thread.td_comm);
251 tdb 1.30 #else
252     proc_state_ptr->process_name =
253     strdup(kp_stats[i].kp_proc.p_comm);
254     #endif
255 tdb 1.29
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 tdb 1.36 /* Should probably be returning NULL here */
281 tdb 1.30 proc_state_ptr->proctitle =
282     malloc(strlen(proc_state_ptr->process_name)+4);
283 tdb 1.29 if(proc_state_ptr->proctitle == NULL) {
284     return NULL;
285     }
286 tdb 1.30 sprintf(proc_state_ptr->proctitle, " (%s)",
287     proc_state_ptr->process_name);
288 tdb 1.29 }
289 tdb 1.26
290 tdb 1.30 #ifdef FREEBSD5
291 tdb 1.26 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 tdb 1.30 #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 tdb 1.26
300 tdb 1.30 #ifdef FREEBSD5
301 tdb 1.26 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 tdb 1.35 #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 tdb 1.30 #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 tdb 1.26
317 tdb 1.30 #ifdef FREEBSD5
318 tdb 1.26 proc_state_ptr->proc_size = kp_stats[i].ki_size;
319     /* This is in pages */
320 tdb 1.30 proc_state_ptr->proc_resident =
321     kp_stats[i].ki_rssize * getpagesize();
322 tdb 1.28 /* This is in microseconds */
323 tdb 1.26 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
324 tdb 1.30 proc_state_ptr->cpu_percent =
325     ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
326 tdb 1.26 proc_state_ptr->nice = kp_stats[i].ki_nice;
327 tdb 1.30 #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 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
334 tdb 1.33 proc_state_ptr->time_spent =
335     kp_stats[i].kp_proc.p_rtime.tv_sec;
336 tdb 1.35 #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 tdb 1.33 #else
342 tdb 1.36 /* This is in microseconds */
343 tdb 1.30 proc_state_ptr->time_spent =
344     kp_stats[i].kp_proc.p_runtime / 1000000;
345 tdb 1.33 #endif
346 tdb 1.30 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 tdb 1.26
351 tdb 1.30 #ifdef FREEBSD5
352 tdb 1.26 switch (kp_stats[i].ki_stat) {
353 tdb 1.30 #else
354     switch (kp_stats[i].kp_proc.p_stat) {
355     #endif
356 tdb 1.26 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 tdb 1.31 break;
381     default:
382     proc_state_ptr->state = UNKNOWN;
383 tdb 1.26 break;
384     }
385     proc_state_size++;
386     }
387    
388     free(kp_stats);
389     #endif
390    
391 pajs 1.23 *ps = proc_state;
392     return proc_state_size;
393 tdb 1.27 }
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 tdb 1.30 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 tdb 1.27
406     ps_size = get_proc_snapshot(&ps);
407 tdb 1.32
408     if(ps_size == NULL) {
409     return NULL;
410     }
411 tdb 1.27
412     for(x = 0; x < ps_size; x++) {
413     switch (ps->state) {
414 tdb 1.30 /* currently no mapping for UNKNOWN in process_stat_t */
415 tdb 1.27 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 pajs 1.23 }