ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/process_stats.c
Revision: 1.42
Committed: Sun Apr 4 22:09:32 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.41: +3 -3 lines
Log Message:
Add an get_kvm2 function to open kvm with slightly different args. This
set of args doesn't seem to need elevated privileges, but in my last test
wouldn't perform the functions required in swap_stats.

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.42 * $Id: process_stats.c,v 1.41 2004/04/04 21:54:49 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 tdb 1.41 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
65 tdb 1.29 #include <kvm.h>
66 pajs 1.6 #endif
67 tdb 1.41 #include <unistd.h>
68     #endif
69 pajs 1.1
70 pajs 1.23 int get_proc_snapshot(proc_state_t **ps){
71     proc_state_t *proc_state = NULL;
72     proc_state_t *proc_state_ptr;
73     int proc_state_size = 0;
74 tdb 1.26 #ifdef ALLBSD
75 tdb 1.37 int mib[4];
76 tdb 1.26 size_t size;
77     struct kinfo_proc *kp_stats;
78 tdb 1.41 int procs, i;
79 tdb 1.37 char *proctitle;
80 tdb 1.41 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
81 tdb 1.42 kvm_t *kvmd;
82 tdb 1.41 char **args;
83     int alloc;
84     #else
85 tdb 1.37 long buflen;
86     char *p;
87     #endif
88 tdb 1.26 #endif
89 pajs 1.24 #if defined(SOLARIS) || defined(LINUX)
90 pajs 1.23 DIR *proc_dir;
91     struct dirent *dir_entry;
92     char filename[MAX_FILE_LENGTH];
93     FILE *f;
94 pajs 1.24 #ifdef SOLARIS
95 pajs 1.5 psinfo_t process_info;
96 pajs 1.24 #endif
97 pajs 1.25 #ifdef LINUX
98     char s;
99     /* If someone has a executable of 4k filename length, they deserve to get it truncated :) */
100     char ps_name[4096];
101     char *ptr;
102     static char *psargs = NULL;
103     static int psarg_size = 0;
104     unsigned long stime, utime;
105     int x;
106     int fn;
107     int toread;
108     ssize_t size;
109     int t_read;
110     #endif
111 pajs 1.1
112 pajs 1.23 if((proc_dir=opendir(PROC_LOCATION))==NULL){
113 pajs 1.25 return -1;
114 pajs 1.23 }
115    
116     while((dir_entry=readdir(proc_dir))!=NULL){
117     if(atoi(dir_entry->d_name) == 0) continue;
118    
119 pajs 1.24 #ifdef SOLARIS
120 pajs 1.23 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
121 pajs 1.24 #endif
122 pajs 1.25 #ifdef LINUX
123     snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
124     #endif
125 pajs 1.23 if((f=fopen(filename, "r"))==NULL){
126     /* Open failed.. Process since vanished, or the path was too long.
127     * Ah well, move onwards to the next one */
128     continue;
129     }
130 pajs 1.24 #ifdef SOLARIS
131 pajs 1.23 fread(&process_info, sizeof(psinfo_t), 1, f);
132 pajs 1.25 #endif
133 pajs 1.1
134 pajs 1.23 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
135     proc_state_ptr = proc_state+proc_state_size;
136 pajs 1.25 #ifdef SOLARIS
137 pajs 1.23 proc_state_ptr->pid = process_info.pr_pid;
138     proc_state_ptr->parent = process_info.pr_ppid;
139     proc_state_ptr->pgid = process_info.pr_pgid;
140     proc_state_ptr->uid = process_info.pr_uid;
141     proc_state_ptr->euid = process_info.pr_euid;
142     proc_state_ptr->gid = process_info.pr_gid;
143     proc_state_ptr->egid = process_info.pr_egid;
144     proc_state_ptr->proc_size = (process_info.pr_size) * 1024;
145     proc_state_ptr->proc_resident = (process_info.pr_rssize) * 1024;
146     proc_state_ptr->time_spent = process_info.pr_time.tv_sec;
147     proc_state_ptr->cpu_percent = (process_info.pr_pctcpu * 100.0) / 0x8000;
148     proc_state_ptr->process_name = strdup(process_info.pr_fname);
149     proc_state_ptr->proctitle = strdup(process_info.pr_psargs);
150 pajs 1.24
151     if(process_info.pr_lwp.pr_state==1) proc_state_ptr->state = SLEEPING;
152     if(process_info.pr_lwp.pr_state==2) proc_state_ptr->state = RUNNING;
153     if(process_info.pr_lwp.pr_state==3) proc_state_ptr->state = ZOMBIE;
154     if(process_info.pr_lwp.pr_state==4) proc_state_ptr->state = STOPPED;
155     if(process_info.pr_lwp.pr_state==6) proc_state_ptr->state = RUNNING;
156 pajs 1.25 #endif
157     #ifdef LINUX
158 ats 1.38 x = fscanf(f, "%d %4096s %c %d %d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu %*d %*d %*d %d %*d %*d %*u %llu %llu %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*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));
159 pajs 1.25 proc_state_ptr->proc_resident = proc_state_ptr->proc_resident * getpagesize();
160     if(s == 'S') proc_state_ptr->state = SLEEPING;
161     if(s == 'R') proc_state_ptr->state = RUNNING;
162     if(s == 'Z') proc_state_ptr->state = ZOMBIE;
163     if(s == 'T') proc_state_ptr->state = STOPPED;
164     if(s == 'D') proc_state_ptr->state = STOPPED;
165    
166     /* pa_name[0] should = '(' */
167     ptr = strchr(&ps_name[1], ')');
168     if(ptr !=NULL) *ptr='\0';
169     proc_state_ptr->process_name = strdup(&ps_name[1]);
170    
171     /* Need to do cpu */
172    
173    
174     /* proctitle */
175     snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/cmdline", dir_entry->d_name);
176    
177     if((fn=open(filename, O_RDONLY)) == -1){
178     /* Open failed.. Process since vanished, or the path was too long.
179     * Ah well, move onwards to the next one */
180     continue;
181     }
182     #define PSARG_START_SIZE 128
183     if(psargs == NULL){
184     psargs = malloc(PSARG_START_SIZE);
185     psarg_size = PSARG_START_SIZE;
186     }
187     ptr = psargs;
188     t_read = 0;
189     toread = psarg_size;
190     while((size = read(fn, ptr, toread)) == toread){
191     psargs = realloc(psargs, (psarg_size + PSARG_START_SIZE));
192     ptr = psargs+psarg_size;
193     t_read = psarg_size;
194     psarg_size+=PSARG_START_SIZE;
195     toread = PSARG_START_SIZE;
196     }
197     if(size != -1) t_read+=size;
198    
199     ptr = psargs;
200     for(x=0; x<t_read; x++){
201     if (*ptr == '\0') *ptr = ' ';
202     ptr++;
203     }
204     /* for safety sake */
205     psargs[t_read] = '\0';
206    
207     proc_state_ptr->proctitle = strdup(psargs);
208    
209 pajs 1.24 #endif
210 pajs 1.23
211     proc_state_size++;
212 pajs 1.1
213 pajs 1.23 fclose(f);
214     }
215     closedir(proc_dir);
216 tdb 1.26 #endif
217    
218     #ifdef ALLBSD
219 tdb 1.36 mib[0] = CTL_KERN;
220     mib[1] = KERN_PROC;
221     mib[2] = KERN_PROC_ALL;
222    
223     if(sysctl(mib, 3, NULL, &size, NULL, 0) < 0) {
224     return NULL;
225     }
226    
227     procs = size / sizeof(struct kinfo_proc);
228    
229     kp_stats = malloc(size);
230     if(kp_stats == NULL) {
231     return NULL;
232     }
233    
234     if(sysctl(mib, 3, kp_stats, &size, NULL, 0) < 0) {
235     free(kp_stats);
236     return NULL;
237     }
238 tdb 1.33
239 tdb 1.37 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
240 tdb 1.42 kvmd = get_kvm2();
241 tdb 1.33 #endif
242 tdb 1.26
243     for (i = 0; i < procs; i++) {
244 tdb 1.29 /* replace with something more sensible */
245 tdb 1.30 proc_state = realloc(proc_state,
246     (1+proc_state_size)*sizeof(proc_state_t));
247 tdb 1.29 if(proc_state == NULL ) {
248     return NULL;
249     }
250 tdb 1.26 proc_state_ptr = proc_state+proc_state_size;
251 tdb 1.30
252     #ifdef FREEBSD5
253     proc_state_ptr->process_name =
254     strdup(kp_stats[i].ki_comm);
255 tdb 1.35 #elif defined(DFBSD)
256     proc_state_ptr->process_name =
257     strdup(kp_stats[i].kp_thread.td_comm);
258 tdb 1.30 #else
259     proc_state_ptr->process_name =
260     strdup(kp_stats[i].kp_proc.p_comm);
261     #endif
262 tdb 1.29
263 tdb 1.37 #if defined(FREEBSD5) || defined(NETBSD) || defined(OPENBSD)
264     size = sizeof(buflen);
265    
266     #ifdef FREEBSD5
267     if(sysctlbyname("kern.ps_arg_cache_limit", &buflen, &size, NULL, 0) < 0) {
268     return NULL;
269     }
270     #else
271     mib[1] = KERN_ARGMAX;
272    
273     if(sysctl(mib, 2, &buflen, &size, NULL, 0) < 0) {
274     return NULL;
275     }
276     #endif
277    
278     proctitle = malloc(buflen);
279     if(proctitle == NULL) {
280     return NULL;
281     }
282    
283     size = buflen;
284    
285     #ifdef FREEBSD5
286     mib[2] = KERN_PROC_ARGS;
287     mib[3] = kp_stats[i].ki_pid;
288     #else
289     mib[1] = KERN_PROC_ARGS;
290     mib[2] = kp_stats[i].kp_proc.p_pid;
291     mib[3] = KERN_PROC_ARGV;
292     #endif
293    
294     if(sysctl(mib, 4, proctitle, &size, NULL, 0) < 0) {
295     free(proctitle);
296     proc_state_ptr->proctitle = NULL;
297     }
298     else if(size > 0) {
299     proc_state_ptr->proctitle = malloc(size);
300     if(proc_state_ptr->proctitle == NULL) {
301 tdb 1.29 return NULL;
302     }
303 tdb 1.37 p = proctitle;
304     proc_state_ptr->proctitle[0] = NULL;
305     do {
306     strncat(proc_state_ptr->proctitle, p, strlen(p));
307     strncat(proc_state_ptr->proctitle, " ", 1);
308     p += strlen(p) + 1;
309     } while (p < proctitle + size);
310     free(proctitle);
311     proc_state_ptr->proctitle[strlen(proc_state_ptr->proctitle)-1] = NULL;
312     }
313     else {
314     free(proctitle);
315     proc_state_ptr->proctitle = NULL;
316     }
317     #else
318     if(kvmd != NULL) {
319     args = kvm_getargv(kvmd, &(kp_stats[i]), 0);
320     if(args != NULL) {
321     alloc = 1;
322     proctitle = malloc(alloc);
323     if(proctitle == NULL) {
324     return NULL;
325     }
326     while(*args != NULL) {
327     if(strlen(proctitle) + strlen(*args) >= alloc) {
328     alloc = (alloc + strlen(*args)) << 1;
329     proctitle = realloc(proctitle, alloc);
330     if(proctitle == NULL) {
331     return NULL;
332     }
333 tdb 1.29 }
334 tdb 1.37 strncat(proctitle, *args, strlen(*args));
335     strncat(proctitle, " ", 1);
336     args++;
337 tdb 1.29 }
338 tdb 1.37 /* remove trailing space */
339     proctitle[strlen(proctitle)-1] = NULL;
340     proc_state_ptr->proctitle = proctitle;
341     }
342     else {
343     proc_state_ptr->proctitle = NULL;
344 tdb 1.29 }
345     }
346     else {
347 tdb 1.37 proc_state_ptr->proctitle = NULL;
348 tdb 1.29 }
349 tdb 1.37 #endif
350 tdb 1.26
351 tdb 1.30 #ifdef FREEBSD5
352 tdb 1.26 proc_state_ptr->pid = kp_stats[i].ki_pid;
353     proc_state_ptr->parent = kp_stats[i].ki_ppid;
354     proc_state_ptr->pgid = kp_stats[i].ki_pgid;
355 tdb 1.30 #else
356     proc_state_ptr->pid = kp_stats[i].kp_proc.p_pid;
357     proc_state_ptr->parent = kp_stats[i].kp_eproc.e_ppid;
358     proc_state_ptr->pgid = kp_stats[i].kp_eproc.e_pgid;
359     #endif
360 tdb 1.26
361 tdb 1.30 #ifdef FREEBSD5
362 tdb 1.26 proc_state_ptr->uid = kp_stats[i].ki_ruid;
363     proc_state_ptr->euid = kp_stats[i].ki_uid;
364     proc_state_ptr->gid = kp_stats[i].ki_rgid;
365     proc_state_ptr->egid = kp_stats[i].ki_svgid;
366 tdb 1.35 #elif defined(DFBSD)
367     proc_state_ptr->uid = kp_stats[i].kp_eproc.e_ucred.cr_ruid;
368     proc_state_ptr->euid = kp_stats[i].kp_eproc.e_ucred.cr_svuid;
369     proc_state_ptr->gid = kp_stats[i].kp_eproc.e_ucred.cr_rgid;
370     proc_state_ptr->egid = kp_stats[i].kp_eproc.e_ucred.cr_svgid;
371 tdb 1.30 #else
372     proc_state_ptr->uid = kp_stats[i].kp_eproc.e_pcred.p_ruid;
373     proc_state_ptr->euid = kp_stats[i].kp_eproc.e_pcred.p_svuid;
374     proc_state_ptr->gid = kp_stats[i].kp_eproc.e_pcred.p_rgid;
375     proc_state_ptr->egid = kp_stats[i].kp_eproc.e_pcred.p_svgid;
376     #endif
377 tdb 1.26
378 tdb 1.30 #ifdef FREEBSD5
379 tdb 1.26 proc_state_ptr->proc_size = kp_stats[i].ki_size;
380     /* This is in pages */
381 tdb 1.30 proc_state_ptr->proc_resident =
382     kp_stats[i].ki_rssize * getpagesize();
383 tdb 1.28 /* This is in microseconds */
384 tdb 1.26 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
385 tdb 1.30 proc_state_ptr->cpu_percent =
386     ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
387 tdb 1.26 proc_state_ptr->nice = kp_stats[i].ki_nice;
388 tdb 1.30 #else
389     proc_state_ptr->proc_size =
390     kp_stats[i].kp_eproc.e_vm.vm_map.size;
391     /* This is in pages */
392     proc_state_ptr->proc_resident =
393     kp_stats[i].kp_eproc.e_vm.vm_rssize * getpagesize();
394 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
395 tdb 1.33 proc_state_ptr->time_spent =
396     kp_stats[i].kp_proc.p_rtime.tv_sec;
397 tdb 1.35 #elif defined(DFBSD)
398     proc_state_ptr->time_spent =
399     ( kp_stats[i].kp_thread.td_uticks +
400     kp_stats[i].kp_thread.td_sticks +
401     kp_stats[i].kp_thread.td_iticks ) / 1000000;
402 tdb 1.33 #else
403 tdb 1.36 /* This is in microseconds */
404 tdb 1.30 proc_state_ptr->time_spent =
405     kp_stats[i].kp_proc.p_runtime / 1000000;
406 tdb 1.33 #endif
407 tdb 1.30 proc_state_ptr->cpu_percent =
408     ((double)kp_stats[i].kp_proc.p_pctcpu / FSCALE) * 100.0;
409     proc_state_ptr->nice = kp_stats[i].kp_proc.p_nice;
410     #endif
411 tdb 1.26
412 tdb 1.30 #ifdef FREEBSD5
413 tdb 1.26 switch (kp_stats[i].ki_stat) {
414 tdb 1.30 #else
415     switch (kp_stats[i].kp_proc.p_stat) {
416     #endif
417 tdb 1.26 case SIDL:
418     case SRUN:
419     #ifdef SONPROC
420     case SONPROC: /* NetBSD */
421     #endif
422     proc_state_ptr->state = RUNNING;
423     break;
424     case SSLEEP:
425     #ifdef SWAIT
426     case SWAIT: /* FreeBSD 5 */
427     #endif
428     #ifdef SLOCK
429     case SLOCK: /* FreeBSD 5 */
430     #endif
431     proc_state_ptr->state = SLEEPING;
432     break;
433     case SSTOP:
434     proc_state_ptr->state = STOPPED;
435     break;
436     case SZOMB:
437     #ifdef SDEAD
438     case SDEAD: /* OpenBSD & NetBSD */
439     #endif
440     proc_state_ptr->state = ZOMBIE;
441 tdb 1.31 break;
442     default:
443     proc_state_ptr->state = UNKNOWN;
444 tdb 1.26 break;
445     }
446     proc_state_size++;
447     }
448    
449     free(kp_stats);
450     #endif
451    
452 pajs 1.23 *ps = proc_state;
453     return proc_state_size;
454 tdb 1.27 }
455    
456     process_stat_t *get_process_stats() {
457     static process_stat_t process_stat;
458     proc_state_t *ps;
459     int ps_size, x;
460    
461 tdb 1.30 process_stat.sleeping = 0;
462     process_stat.running = 0;
463     process_stat.zombie = 0;
464     process_stat.stopped = 0;
465     process_stat.total = 0;
466 tdb 1.27
467     ps_size = get_proc_snapshot(&ps);
468 tdb 1.32
469 ats 1.39 if(ps_size == 0) {
470 tdb 1.32 return NULL;
471     }
472 tdb 1.27
473     for(x = 0; x < ps_size; x++) {
474     switch (ps->state) {
475     case RUNNING:
476     process_stat.running++;
477     break;
478     case SLEEPING:
479     process_stat.sleeping++;
480     break;
481     case STOPPED:
482     process_stat.stopped++;
483     break;
484     case ZOMBIE:
485     process_stat.zombie++;
486 ats 1.40 break;
487     default:
488     /* currently no mapping for UNKNOWN in process_stat_t */
489 tdb 1.27 break;
490     }
491     ps++;
492     }
493    
494     process_stat.total = ps_size;
495    
496     return &process_stat;
497 pajs 1.23 }