ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/process_stats.c
Revision: 1.35
Committed: Sun Apr 4 15:56:53 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.34: +13 -0 lines
Log Message:
Add support for DragonFly BSD. Seems to be working correctly, and works
without needing any extra privileges. Some stuff comes from proc.h,
whilst some comes from thread.h.

A word of warning - the DragonFly folk seem to be changing a lot of stuff
to do with processes and threads, so it's likely this could break very
easily :-)

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.34 * $Id: process_stats.c,v 1.33 2004/04/04 11:28:46 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.33
211 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
212 tdb 1.33 kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL);
213     #else
214 tdb 1.29 kvmd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
215 tdb 1.33 #endif
216 tdb 1.26
217 tdb 1.29 if(kvmd == NULL) return NULL;
218 tdb 1.26
219 tdb 1.29 kp_stats = kvm_getprocs(kvmd, KERN_PROC_ALL, 0, &procs);
220 tdb 1.26
221 tdb 1.29 if (kp_stats == NULL || procs < 0) {
222 tdb 1.26 return NULL;
223     }
224    
225     for (i = 0; i < procs; i++) {
226 tdb 1.29 /* replace with something more sensible */
227 tdb 1.30 proc_state = realloc(proc_state,
228     (1+proc_state_size)*sizeof(proc_state_t));
229 tdb 1.29 if(proc_state == NULL ) {
230     return NULL;
231     }
232 tdb 1.26 proc_state_ptr = proc_state+proc_state_size;
233 tdb 1.30
234     #ifdef FREEBSD5
235     proc_state_ptr->process_name =
236     strdup(kp_stats[i].ki_comm);
237 tdb 1.35 #elif defined(DFBSD)
238     proc_state_ptr->process_name =
239     strdup(kp_stats[i].kp_thread.td_comm);
240 tdb 1.30 #else
241     proc_state_ptr->process_name =
242     strdup(kp_stats[i].kp_proc.p_comm);
243     #endif
244 tdb 1.29
245     args = kvm_getargv(kvmd, &(kp_stats[i]), 0);
246     if(args != NULL) {
247     alloc = 1;
248     proctitle = malloc(alloc);
249     if(proctitle == NULL) {
250     return NULL;
251     }
252     while(*args != NULL) {
253     if(strlen(proctitle) + strlen(*args) >= alloc) {
254     alloc = (alloc + strlen(*args)) << 1;
255     proctitle = realloc(proctitle, alloc);
256     if(proctitle == NULL) {
257     return NULL;
258     }
259     }
260     strncat(proctitle, *args, strlen(*args));
261     strncat(proctitle, " ", 1);
262     args++;
263     }
264     /* remove trailing space */
265     proctitle[strlen(proctitle)-1] = NULL;
266     proc_state_ptr->proctitle = proctitle;
267     }
268     else {
269 tdb 1.30 proc_state_ptr->proctitle =
270     malloc(strlen(proc_state_ptr->process_name)+4);
271 tdb 1.29 if(proc_state_ptr->proctitle == NULL) {
272     return NULL;
273     }
274 tdb 1.30 sprintf(proc_state_ptr->proctitle, " (%s)",
275     proc_state_ptr->process_name);
276 tdb 1.29 }
277 tdb 1.26
278 tdb 1.30 #ifdef FREEBSD5
279 tdb 1.26 proc_state_ptr->pid = kp_stats[i].ki_pid;
280     proc_state_ptr->parent = kp_stats[i].ki_ppid;
281     proc_state_ptr->pgid = kp_stats[i].ki_pgid;
282 tdb 1.30 #else
283     proc_state_ptr->pid = kp_stats[i].kp_proc.p_pid;
284     proc_state_ptr->parent = kp_stats[i].kp_eproc.e_ppid;
285     proc_state_ptr->pgid = kp_stats[i].kp_eproc.e_pgid;
286     #endif
287 tdb 1.26
288 tdb 1.30 #ifdef FREEBSD5
289 tdb 1.26 proc_state_ptr->uid = kp_stats[i].ki_ruid;
290     proc_state_ptr->euid = kp_stats[i].ki_uid;
291     proc_state_ptr->gid = kp_stats[i].ki_rgid;
292     proc_state_ptr->egid = kp_stats[i].ki_svgid;
293 tdb 1.35 #elif defined(DFBSD)
294     proc_state_ptr->uid = kp_stats[i].kp_eproc.e_ucred.cr_ruid;
295     proc_state_ptr->euid = kp_stats[i].kp_eproc.e_ucred.cr_svuid;
296     proc_state_ptr->gid = kp_stats[i].kp_eproc.e_ucred.cr_rgid;
297     proc_state_ptr->egid = kp_stats[i].kp_eproc.e_ucred.cr_svgid;
298 tdb 1.30 #else
299     proc_state_ptr->uid = kp_stats[i].kp_eproc.e_pcred.p_ruid;
300     proc_state_ptr->euid = kp_stats[i].kp_eproc.e_pcred.p_svuid;
301     proc_state_ptr->gid = kp_stats[i].kp_eproc.e_pcred.p_rgid;
302     proc_state_ptr->egid = kp_stats[i].kp_eproc.e_pcred.p_svgid;
303     #endif
304 tdb 1.26
305 tdb 1.30 #ifdef FREEBSD5
306 tdb 1.26 proc_state_ptr->proc_size = kp_stats[i].ki_size;
307     /* This is in pages */
308 tdb 1.30 proc_state_ptr->proc_resident =
309     kp_stats[i].ki_rssize * getpagesize();
310 tdb 1.28 /* This is in microseconds */
311 tdb 1.26 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
312 tdb 1.30 proc_state_ptr->cpu_percent =
313     ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
314 tdb 1.26 proc_state_ptr->nice = kp_stats[i].ki_nice;
315 tdb 1.30 #else
316     proc_state_ptr->proc_size =
317     kp_stats[i].kp_eproc.e_vm.vm_map.size;
318     /* This is in pages */
319     proc_state_ptr->proc_resident =
320     kp_stats[i].kp_eproc.e_vm.vm_rssize * getpagesize();
321     /* This is in microseconds */
322 tdb 1.34 #if defined(NETBSD) || defined(OPENBSD)
323 tdb 1.33 proc_state_ptr->time_spent =
324     kp_stats[i].kp_proc.p_rtime.tv_sec;
325 tdb 1.35 #elif defined(DFBSD)
326     proc_state_ptr->time_spent =
327     ( kp_stats[i].kp_thread.td_uticks +
328     kp_stats[i].kp_thread.td_sticks +
329     kp_stats[i].kp_thread.td_iticks ) / 1000000;
330 tdb 1.33 #else
331 tdb 1.30 proc_state_ptr->time_spent =
332     kp_stats[i].kp_proc.p_runtime / 1000000;
333 tdb 1.33 #endif
334 tdb 1.30 proc_state_ptr->cpu_percent =
335     ((double)kp_stats[i].kp_proc.p_pctcpu / FSCALE) * 100.0;
336     proc_state_ptr->nice = kp_stats[i].kp_proc.p_nice;
337     #endif
338 tdb 1.26
339 tdb 1.30 #ifdef FREEBSD5
340 tdb 1.26 switch (kp_stats[i].ki_stat) {
341 tdb 1.30 #else
342     switch (kp_stats[i].kp_proc.p_stat) {
343     #endif
344 tdb 1.26 case SIDL:
345     case SRUN:
346     #ifdef SONPROC
347     case SONPROC: /* NetBSD */
348     #endif
349     proc_state_ptr->state = RUNNING;
350     break;
351     case SSLEEP:
352     #ifdef SWAIT
353     case SWAIT: /* FreeBSD 5 */
354     #endif
355     #ifdef SLOCK
356     case SLOCK: /* FreeBSD 5 */
357     #endif
358     proc_state_ptr->state = SLEEPING;
359     break;
360     case SSTOP:
361     proc_state_ptr->state = STOPPED;
362     break;
363     case SZOMB:
364     #ifdef SDEAD
365     case SDEAD: /* OpenBSD & NetBSD */
366     #endif
367     proc_state_ptr->state = ZOMBIE;
368 tdb 1.31 break;
369     default:
370     proc_state_ptr->state = UNKNOWN;
371 tdb 1.26 break;
372     }
373     proc_state_size++;
374     }
375    
376     free(kp_stats);
377     #endif
378    
379 pajs 1.23 *ps = proc_state;
380     return proc_state_size;
381 tdb 1.27 }
382    
383     process_stat_t *get_process_stats() {
384     static process_stat_t process_stat;
385     proc_state_t *ps;
386     int ps_size, x;
387    
388 tdb 1.30 process_stat.sleeping = 0;
389     process_stat.running = 0;
390     process_stat.zombie = 0;
391     process_stat.stopped = 0;
392     process_stat.total = 0;
393 tdb 1.27
394     ps_size = get_proc_snapshot(&ps);
395 tdb 1.32
396     if(ps_size == NULL) {
397     return NULL;
398     }
399 tdb 1.27
400     for(x = 0; x < ps_size; x++) {
401     switch (ps->state) {
402 tdb 1.30 /* currently no mapping for UNKNOWN in process_stat_t */
403 tdb 1.27 case RUNNING:
404     process_stat.running++;
405     break;
406     case SLEEPING:
407     process_stat.sleeping++;
408     break;
409     case STOPPED:
410     process_stat.stopped++;
411     break;
412     case ZOMBIE:
413     process_stat.zombie++;
414     break;
415     }
416     ps++;
417     }
418    
419     process_stat.total = ps_size;
420    
421     return &process_stat;
422 pajs 1.23 }