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

# 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.41 2004/04/04 21:54:49 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 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
65 #include <kvm.h>
66 #endif
67 #include <unistd.h>
68 #endif
69
70 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 #ifdef ALLBSD
75 int mib[4];
76 size_t size;
77 struct kinfo_proc *kp_stats;
78 int procs, i;
79 char *proctitle;
80 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
81 kvm_t *kvmd;
82 char **args;
83 int alloc;
84 #else
85 long buflen;
86 char *p;
87 #endif
88 #endif
89 #if defined(SOLARIS) || defined(LINUX)
90 DIR *proc_dir;
91 struct dirent *dir_entry;
92 char filename[MAX_FILE_LENGTH];
93 FILE *f;
94 #ifdef SOLARIS
95 psinfo_t process_info;
96 #endif
97 #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
112 if((proc_dir=opendir(PROC_LOCATION))==NULL){
113 return -1;
114 }
115
116 while((dir_entry=readdir(proc_dir))!=NULL){
117 if(atoi(dir_entry->d_name) == 0) continue;
118
119 #ifdef SOLARIS
120 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/psinfo", dir_entry->d_name);
121 #endif
122 #ifdef LINUX
123 snprintf(filename, MAX_FILE_LENGTH, "/proc/%s/stat", dir_entry->d_name);
124 #endif
125 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 #ifdef SOLARIS
131 fread(&process_info, sizeof(psinfo_t), 1, f);
132 #endif
133
134 proc_state = realloc(proc_state, (1+proc_state_size)*sizeof(proc_state_t));
135 proc_state_ptr = proc_state+proc_state_size;
136 #ifdef SOLARIS
137 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
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 #endif
157 #ifdef LINUX
158 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 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 #endif
210
211 proc_state_size++;
212
213 fclose(f);
214 }
215 closedir(proc_dir);
216 #endif
217
218 #ifdef ALLBSD
219 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
239 #if (defined(FREEBSD) && !defined(FREEBSD5)) || defined(DFBSD)
240 kvmd = get_kvm2();
241 #endif
242
243 for (i = 0; i < procs; i++) {
244 /* replace with something more sensible */
245 proc_state = realloc(proc_state,
246 (1+proc_state_size)*sizeof(proc_state_t));
247 if(proc_state == NULL ) {
248 return NULL;
249 }
250 proc_state_ptr = proc_state+proc_state_size;
251
252 #ifdef FREEBSD5
253 proc_state_ptr->process_name =
254 strdup(kp_stats[i].ki_comm);
255 #elif defined(DFBSD)
256 proc_state_ptr->process_name =
257 strdup(kp_stats[i].kp_thread.td_comm);
258 #else
259 proc_state_ptr->process_name =
260 strdup(kp_stats[i].kp_proc.p_comm);
261 #endif
262
263 #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 return NULL;
302 }
303 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 }
334 strncat(proctitle, *args, strlen(*args));
335 strncat(proctitle, " ", 1);
336 args++;
337 }
338 /* 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 }
345 }
346 else {
347 proc_state_ptr->proctitle = NULL;
348 }
349 #endif
350
351 #ifdef FREEBSD5
352 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 #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
361 #ifdef FREEBSD5
362 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 #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 #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
378 #ifdef FREEBSD5
379 proc_state_ptr->proc_size = kp_stats[i].ki_size;
380 /* This is in pages */
381 proc_state_ptr->proc_resident =
382 kp_stats[i].ki_rssize * getpagesize();
383 /* This is in microseconds */
384 proc_state_ptr->time_spent = kp_stats[i].ki_runtime / 1000000;
385 proc_state_ptr->cpu_percent =
386 ((double)kp_stats[i].ki_pctcpu / FSCALE) * 100.0;
387 proc_state_ptr->nice = kp_stats[i].ki_nice;
388 #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 #if defined(NETBSD) || defined(OPENBSD)
395 proc_state_ptr->time_spent =
396 kp_stats[i].kp_proc.p_rtime.tv_sec;
397 #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 #else
403 /* This is in microseconds */
404 proc_state_ptr->time_spent =
405 kp_stats[i].kp_proc.p_runtime / 1000000;
406 #endif
407 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
412 #ifdef FREEBSD5
413 switch (kp_stats[i].ki_stat) {
414 #else
415 switch (kp_stats[i].kp_proc.p_stat) {
416 #endif
417 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 break;
442 default:
443 proc_state_ptr->state = UNKNOWN;
444 break;
445 }
446 proc_state_size++;
447 }
448
449 free(kp_stats);
450 #endif
451
452 *ps = proc_state;
453 return proc_state_size;
454 }
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 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
467 ps_size = get_proc_snapshot(&ps);
468
469 if(ps_size == 0) {
470 return NULL;
471 }
472
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 break;
487 default:
488 /* currently no mapping for UNKNOWN in process_stat_t */
489 break;
490 }
491 ps++;
492 }
493
494 process_stat.total = ps_size;
495
496 return &process_stat;
497 }