ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.56
Committed: Sun Apr 4 22:18:38 2004 UTC (20 years, 1 month ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.55: +35 -81 lines
Log Message:
Make disk_stats use vectors.

(Note that this introduces some harmless strict aliasing warnings on GCC
3.3; I'll do something about that later.)

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: disk_stats.c,v 1.55 2004/04/04 21:38:02 ats Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include "statgrab.h"
33 #include "vector.h"
34
35 #ifdef SOLARIS
36 #include <sys/mnttab.h>
37 #include <sys/statvfs.h>
38 #include <kstat.h>
39 #define VALID_FS_TYPES {"ufs", "tmpfs"}
40 #endif
41
42 #if defined(LINUX) || defined(CYGWIN)
43 #include <mntent.h>
44 #include <sys/vfs.h>
45 #include "tools.h"
46 #endif
47
48 #ifdef LINUX
49 #define VALID_FS_TYPES {"adfs", "affs", "befs", "bfs", "efs", "ext2", \
50 "ext3", "vxfs", "hfs", "hfsplus", "hpfs", "jffs", \
51 "jffs2", "minix", "msdos", "ntfs", "qnx4", "ramfs", \
52 "rootfs", "reiserfs", "sysv", "v7", "udf", "ufs", \
53 "umsdos", "vfat", "xfs", "jfs"}
54 #endif
55
56 #ifdef CYGWIN
57 #define VALID_FS_TYPES {"user"}
58 #endif
59
60 #ifdef ALLBSD
61 #include <sys/param.h>
62 #include <sys/ucred.h>
63 #include <sys/mount.h>
64 #endif
65 #if defined(FREEBSD) || defined(DFBSD)
66 #include <sys/dkstat.h>
67 #include <devstat.h>
68 #define VALID_FS_TYPES {"hpfs", "msdosfs", "ntfs", "udf", "ext2fs", \
69 "ufs", "mfs"}
70 #endif
71 #if defined(NETBSD) || defined(OPENBSD)
72 #include <sys/param.h>
73 #include <sys/sysctl.h>
74 #include <sys/disk.h>
75 #define VALID_FS_TYPES {"ffs", "mfs", "msdos", "lfs", "adosfs", "ext2fs", \
76 "ntfs"}
77 #endif
78
79 char *copy_string(char *orig_ptr, const char *newtext){
80
81 /* Maybe free if not NULL, and strdup rather than realloc and strcpy? */
82 orig_ptr=realloc(orig_ptr, (1+strlen(newtext)));
83 if(orig_ptr==NULL){
84 return NULL;
85 }
86 strcpy(orig_ptr, newtext);
87
88 return orig_ptr;
89 }
90
91 static void disk_stat_init(disk_stat_t *d) {
92 d->device_name = NULL;
93 d->fs_type = NULL;
94 d->mnt_point = NULL;
95 }
96
97 static void disk_stat_destroy(disk_stat_t *d) {
98 free(d->device_name);
99 free(d->fs_type);
100 free(d->mnt_point);
101 }
102
103 int is_valid_fs_type(const char *type) {
104 const char *types[] = VALID_FS_TYPES;
105 int i;
106
107 for (i = 0; i < (sizeof types / sizeof *types); i++) {
108 if (strcmp(types[i], type) == 0) {
109 return 1;
110 }
111 }
112 return 0;
113 }
114
115 disk_stat_t *get_disk_stats(int *entries){
116 VECTOR_DECLARE_STATIC(disk_stats, disk_stat_t, 10,
117 disk_stat_init, disk_stat_destroy);
118
119 int valid_type;
120 int num_disks=0;
121 #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN)
122 FILE *f;
123 #endif
124
125 disk_stat_t *disk_ptr;
126
127 #ifdef SOLARIS
128 struct mnttab mp;
129 struct statvfs fs;
130 #endif
131 #if defined(LINUX) || defined(CYGWIN)
132 struct mntent *mp;
133 struct statfs fs;
134 #endif
135 #ifdef ALLBSD
136 int nummnt;
137 struct statfs *mp;
138 #endif
139
140 #ifdef ALLBSD
141 nummnt=getmntinfo(&mp , MNT_LOCAL);
142 if (nummnt<=0){
143 return NULL;
144 }
145 for(;nummnt--; mp++){
146 valid_type = is_valid_fs_type(mp->f_fstypename);
147 #endif
148
149 #if defined(LINUX) || defined(CYGWIN)
150 if ((f=setmntent("/etc/mtab", "r" ))==NULL){
151 return NULL;
152 }
153
154 while((mp=getmntent(f))){
155 if((statfs(mp->mnt_dir, &fs)) !=0){
156 continue;
157 }
158
159 valid_type = is_valid_fs_type(mp->mnt_type);
160 #endif
161
162 #ifdef SOLARIS
163 if ((f=fopen("/etc/mnttab", "r" ))==NULL){
164 return NULL;
165 }
166 while((getmntent(f, &mp)) == 0){
167 if ((statvfs(mp.mnt_mountp, &fs)) !=0){
168 continue;
169 }
170 valid_type = is_valid_fs_type(mp.mnt_fstype);
171 #endif
172
173 if(valid_type){
174 if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) {
175 return NULL;
176 }
177 disk_ptr=disk_stats+num_disks;
178
179 #ifdef ALLBSD
180 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp->f_mntfromname))==NULL){
181 return NULL;
182 }
183
184 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp->f_fstypename))==NULL){
185 return NULL;
186 }
187
188 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp->f_mntonname))==NULL){
189 return NULL;
190 }
191
192 disk_ptr->size = (long long)mp->f_bsize * (long long) mp->f_blocks;
193 disk_ptr->avail = (long long)mp->f_bsize * (long long) mp->f_bavail;
194 disk_ptr->used = (disk_ptr->size) - ((long long)mp->f_bsize * (long long)mp->f_bfree);
195
196 disk_ptr->total_inodes=(long long)mp->f_files;
197 disk_ptr->free_inodes=(long long)mp->f_ffree;
198 /* Freebsd doesn't have a "available" inodes */
199 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
200 #endif
201 #if defined(LINUX) || defined(CYGWIN)
202 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp->mnt_fsname))==NULL){
203 return NULL;
204 }
205
206 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp->mnt_type))==NULL){
207 return NULL;
208 }
209
210 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp->mnt_dir))==NULL){
211 return NULL;
212 }
213 disk_ptr->size = (long long)fs.f_bsize * (long long)fs.f_blocks;
214 disk_ptr->avail = (long long)fs.f_bsize * (long long)fs.f_bavail;
215 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_bsize * (long long)fs.f_bfree);
216
217 disk_ptr->total_inodes=(long long)fs.f_files;
218 disk_ptr->free_inodes=(long long)fs.f_ffree;
219 /* Linux doesn't have a "available" inodes */
220 disk_ptr->used_inodes=disk_ptr->total_inodes-disk_ptr->free_inodes;
221 #endif
222
223 #ifdef SOLARIS
224 /* Memory leak in event of realloc failing */
225 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end?
226 * Downside is its a bit hungry for a lot of mounts, as MNT_MAX_SIZE would prob
227 * be upwards of a k each
228 */
229 if((disk_ptr->device_name=copy_string(disk_ptr->device_name, mp.mnt_special))==NULL){
230 return NULL;
231 }
232
233 if((disk_ptr->fs_type=copy_string(disk_ptr->fs_type, mp.mnt_fstype))==NULL){
234 return NULL;
235 }
236
237 if((disk_ptr->mnt_point=copy_string(disk_ptr->mnt_point, mp.mnt_mountp))==NULL){
238 return NULL;
239 }
240
241 disk_ptr->size = (long long)fs.f_frsize * (long long)fs.f_blocks;
242 disk_ptr->avail = (long long)fs.f_frsize * (long long)fs.f_bavail;
243 disk_ptr->used = (disk_ptr->size) - ((long long)fs.f_frsize * (long long)fs.f_bfree);
244
245 disk_ptr->total_inodes=(long long)fs.f_files;
246 disk_ptr->used_inodes=disk_ptr->total_inodes - (long long)fs.f_ffree;
247 disk_ptr->free_inodes=(long long)fs.f_favail;
248 #endif
249 num_disks++;
250 }
251 }
252
253 *entries=num_disks;
254
255 /* If this fails, there is very little i can do about it, so
256 I'll ignore it :) */
257 #if defined(LINUX) || defined(CYGWIN)
258 endmntent(f);
259 #endif
260 #if defined(SOLARIS)
261 fclose(f);
262 #endif
263
264 return disk_stats;
265
266 }
267
268 static void diskio_stat_init(diskio_stat_t *d) {
269 d->disk_name = NULL;
270 }
271
272 static void diskio_stat_destroy(diskio_stat_t *d) {
273 free(d->disk_name);
274 }
275
276 VECTOR_DECLARE_STATIC(diskio_stats, diskio_stat_t, 10,
277 diskio_stat_init, diskio_stat_destroy);
278
279 #ifdef LINUX
280 typedef struct {
281 int major;
282 int minor;
283 } partition;
284 #endif
285
286 diskio_stat_t *get_diskio_stats(int *entries){
287 int num_diskio;
288 #ifndef LINUX
289 diskio_stat_t *diskio_stats_ptr;
290 #endif
291
292 #ifdef SOLARIS
293 kstat_ctl_t *kc;
294 kstat_t *ksp;
295 kstat_io_t kios;
296 #endif
297 #ifdef LINUX
298 FILE *f;
299 char *line_ptr;
300 int major, minor;
301 int has_pp_stats = 1;
302 static partition *parts = NULL;
303 static int alloc_parts = 0;
304 int i, n;
305 time_t now;
306 const char *format;
307 #endif
308 #if defined(FREEBSD) || defined(DFBSD)
309 static struct statinfo stats;
310 static int stats_init = 0;
311 int counter;
312 struct device_selection *dev_sel = NULL;
313 int n_selected, n_selections;
314 long sel_gen;
315 struct devstat *dev_ptr;
316 #endif
317 #ifdef NETBSD
318 struct disk_sysctl *stats;
319 #endif
320 #ifdef OPENBSD
321 int diskcount;
322 char *disknames, *name, *bufpp;
323 char **dk_name;
324 struct diskstats *stats;
325 #endif
326 #ifdef NETBSD
327 #define MIBSIZE 3
328 #endif
329 #ifdef OPENBSD
330 #define MIBSIZE 2
331 #endif
332 #if defined(NETBSD) || defined(OPENBSD)
333 int num_disks, i;
334 int mib[MIBSIZE];
335 size_t size;
336 #endif
337
338 num_diskio=0;
339
340 #ifdef OPENBSD
341 mib[0] = CTL_HW;
342 mib[1] = HW_DISKCOUNT;
343
344 size = sizeof(diskcount);
345 if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) {
346 return NULL;
347 }
348
349 mib[0] = CTL_HW;
350 mib[1] = HW_DISKNAMES;
351
352 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
353 return NULL;
354 }
355
356 disknames = malloc(size);
357 if (disknames == NULL) {
358 return NULL;
359 }
360
361 if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) {
362 return NULL;
363 }
364
365 dk_name = calloc(diskcount, sizeof(char *));
366 bufpp = disknames;
367 for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) {
368 dk_name[i] = name;
369 }
370 #endif
371
372 #if defined(NETBSD) || defined(OPENBSD)
373 mib[0] = CTL_HW;
374 mib[1] = HW_DISKSTATS;
375 #ifdef NETBSD
376 mib[2] = sizeof(struct disk_sysctl);
377 #endif
378
379 if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) {
380 return NULL;
381 }
382
383 #ifdef NETBSD
384 num_disks = size / sizeof(struct disk_sysctl);
385 #else
386 num_disks = size / sizeof(struct diskstats);
387 #endif
388
389 stats = malloc(size);
390 if (stats == NULL) {
391 return NULL;
392 }
393
394 if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) {
395 return NULL;
396 }
397
398 for (i = 0; i < num_disks; i++) {
399 u_int64_t rbytes, wbytes;
400
401 #ifdef NETBSD
402 #ifdef HAVE_DK_RBYTES
403 rbytes = stats[i].dk_rbytes;
404 wbytes = stats[i].dk_wbytes;
405 #else
406 /* Before 1.7, NetBSD merged reads and writes. */
407 rbytes = wbytes = stats[i].dk_bytes;
408 #endif
409 #else
410 rbytes = wbytes = stats[i].ds_bytes;
411 #endif
412
413 /* Don't keep stats for disks that have never been used. */
414 if (rbytes == 0 && wbytes == 0) {
415 continue;
416 }
417
418 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
419 return NULL;
420 }
421 diskio_stats_ptr = diskio_stats + num_diskio;
422
423 diskio_stats_ptr->read_bytes = rbytes;
424 diskio_stats_ptr->write_bytes = wbytes;
425 if (diskio_stats_ptr->disk_name != NULL) {
426 free(diskio_stats_ptr->disk_name);
427 }
428 #ifdef NETBSD
429 diskio_stats_ptr->disk_name = strdup(stats[i].dk_name);
430 #else
431 diskio_stats_ptr->disk_name = strdup(dk_name[i]);
432 #endif
433 diskio_stats_ptr->systime = time(NULL);
434
435 num_diskio++;
436 }
437
438 free(stats);
439 #ifdef OPENBSD
440 free(dk_name);
441 free(disknames);
442 #endif
443 #endif
444
445 #if defined(FREEBSD) || defined(DFBSD)
446 if (!stats_init) {
447 stats.dinfo=malloc(sizeof(struct devinfo));
448 if(stats.dinfo==NULL) return NULL;
449 bzero(stats.dinfo, sizeof(struct devinfo));
450 stats_init = 1;
451 }
452 #ifdef FREEBSD5
453 if ((devstat_getdevs(NULL, &stats)) < 0) return NULL;
454 /* Not aware of a get all devices, so i said 999. If we ever
455 * find a machine with more than 999 disks, then i'll change
456 * this number :)
457 */
458 if (devstat_selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) return NULL;
459 #else
460 if ((getdevs(&stats)) < 0) return NULL;
461 /* Not aware of a get all devices, so i said 999. If we ever
462 * find a machine with more than 999 disks, then i'll change
463 * this number :)
464 */
465 if (selectdevs(&dev_sel, &n_selected, &n_selections, &sel_gen, stats.dinfo->generation, stats.dinfo->devices, stats.dinfo->numdevs, NULL, 0, NULL, 0, DS_SELECT_ONLY, 999, 1) < 0) return NULL;
466 #endif
467
468 for(counter=0;counter<stats.dinfo->numdevs;counter++){
469 dev_ptr=&stats.dinfo->devices[dev_sel[counter].position];
470
471 /* Throw away devices that have done nothing, ever.. Eg "odd"
472 * devices.. like mem, proc.. and also doesn't report floppy
473 * drives etc unless they are doing stuff :)
474 */
475 #ifdef FREEBSD5
476 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue;
477 #else
478 if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue;
479 #endif
480
481 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
482 return NULL;
483 }
484 diskio_stats_ptr=diskio_stats+num_diskio;
485
486 #ifdef FREEBSD5
487 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ];
488 diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE];
489 #else
490 diskio_stats_ptr->read_bytes=dev_ptr->bytes_read;
491 diskio_stats_ptr->write_bytes=dev_ptr->bytes_written;
492 #endif
493 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
494 asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number);
495 diskio_stats_ptr->systime=time(NULL);
496
497 num_diskio++;
498 }
499 free(dev_sel);
500
501 #endif
502 #ifdef SOLARIS
503 if ((kc = kstat_open()) == NULL) {
504 return NULL;
505 }
506
507 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
508 if (!strcmp(ksp->ks_class, "disk")) {
509
510 if(ksp->ks_type != KSTAT_TYPE_IO) continue;
511 /* We dont want metadevices appearins as num_diskio */
512 if(strcmp(ksp->ks_module, "md")==0) continue;
513 if((kstat_read(kc, ksp, &kios))==-1){
514 }
515
516 if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) {
517 kstat_close(kc);
518 return NULL;
519 }
520 diskio_stats_ptr=diskio_stats+num_diskio;
521
522 diskio_stats_ptr->read_bytes=kios.nread;
523
524 diskio_stats_ptr->write_bytes=kios.nwritten;
525
526 if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name);
527
528 diskio_stats_ptr->disk_name=strdup((char *) get_svr_from_bsd(ksp->ks_name));
529 diskio_stats_ptr->systime=time(NULL);
530 num_diskio++;
531 }
532 }
533
534 kstat_close(kc);
535 #endif
536
537 #ifdef LINUX
538 num_diskio = 0;
539 n = 0;
540
541 /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels
542 have statistics in here too, so we can use those directly.
543 2.6 kernels have /proc/diskstats instead with almost (but not quite)
544 the same format. */
545
546 f = fopen("/proc/diskstats", "r");
547 format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld";
548 if (f == NULL) {
549 f = fopen("/proc/partitions", "r");
550 format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld";
551 }
552 if (f == NULL) goto out;
553 now = time(NULL);
554
555 while ((line_ptr = f_read_line(f, "")) != NULL) {
556 char name[100];
557 char *s;
558 long long rsect, wsect;
559
560 int nr = sscanf(line_ptr, format,
561 &major, &minor, name, &rsect, &wsect);
562 if (nr < 3) continue;
563
564 /* Skip device names ending in numbers, since they're
565 partitions. */
566 s = name;
567 while (*s != '\0') s++;
568 --s;
569 if (*s >= '0' && *s <= '9') continue;
570
571 if (nr < 5) {
572 has_pp_stats = 0;
573 rsect = 0;
574 wsect = 0;
575 }
576
577 if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) {
578 goto out;
579 }
580 if (n >= alloc_parts) {
581 alloc_parts += 16;
582 parts = realloc(parts, alloc_parts * sizeof *parts);
583 if (parts == NULL) {
584 alloc_parts = 0;
585 goto out;
586 }
587 }
588
589 if (diskio_stats[n].disk_name != NULL)
590 free(diskio_stats[n].disk_name);
591 diskio_stats[n].disk_name = strdup(name);
592 diskio_stats[n].read_bytes = rsect * 512;
593 diskio_stats[n].write_bytes = wsect * 512;
594 diskio_stats[n].systime = now;
595 parts[n].major = major;
596 parts[n].minor = minor;
597
598 n++;
599 }
600
601 fclose(f);
602 f = NULL;
603
604 if (!has_pp_stats) {
605 /* This is an older kernel where /proc/partitions doesn't
606 contain stats. Read what we can from /proc/stat instead, and
607 fill in the appropriate bits of the list allocated above. */
608
609 f = fopen("/proc/stat", "r");
610 if (f == NULL) goto out;
611 now = time(NULL);
612
613 line_ptr = f_read_line(f, "disk_io:");
614 if (line_ptr == NULL) goto out;
615
616 while((line_ptr=strchr(line_ptr, ' '))!=NULL){
617 long long rsect, wsect;
618
619 if (*++line_ptr == '\0') break;
620
621 if((sscanf(line_ptr,
622 "(%d,%d):(%*d, %*d, %lld, %*d, %lld)",
623 &major, &minor, &rsect, &wsect)) != 4) {
624 continue;
625 }
626
627 /* Find the corresponding device from earlier.
628 Just to add to the fun, "minor" is actually the disk
629 number, not the device minor, so we need to figure
630 out the real minor number based on the major!
631 This list is not exhaustive; if you're running
632 an older kernel you probably don't have fancy
633 I2O hardware anyway... */
634 switch (major) {
635 case 3:
636 case 21:
637 case 22:
638 case 33:
639 case 34:
640 case 36:
641 case 56:
642 case 57:
643 case 88:
644 case 89:
645 case 90:
646 case 91:
647 minor *= 64;
648 break;
649 case 9:
650 case 43:
651 break;
652 default:
653 minor *= 16;
654 break;
655 }
656 for (i = 0; i < n; i++) {
657 if (major == parts[i].major
658 && minor == parts[i].minor)
659 break;
660 }
661 if (i == n) continue;
662
663 /* We read the number of blocks. Blocks are stored in
664 512 bytes */
665 diskio_stats[i].read_bytes = rsect * 512;
666 diskio_stats[i].write_bytes = wsect * 512;
667 diskio_stats[i].systime = now;
668 }
669 }
670
671 num_diskio = n;
672 out:
673 if (f != NULL) fclose(f);
674 #endif
675
676 #ifdef CYGWIN
677 return NULL;
678 #endif
679
680 *entries=num_diskio;
681
682 return diskio_stats;
683 }
684
685 diskio_stat_t *get_diskio_stats_diff(int *entries){
686 VECTOR_DECLARE_STATIC(diff, diskio_stat_t, 1,
687 diskio_stat_init, diskio_stat_destroy);
688 diskio_stat_t *src = NULL, *dest;
689 int i, j, diff_count, new_count;
690
691 if (diskio_stats == NULL) {
692 /* No previous stats, so we can't calculate a difference. */
693 return get_diskio_stats(entries);
694 }
695
696 /* Resize the results array to match the previous stats. */
697 diff_count = VECTOR_SIZE(diskio_stats);
698 if (VECTOR_RESIZE(diff, diff_count) < 0) {
699 return NULL;
700 }
701
702 /* Copy the previous stats into the result. */
703 for (i = 0; i < diff_count; i++) {
704 src = &diskio_stats[i];
705 dest = &diff[i];
706
707 if (dest->disk_name != NULL) {
708 free(dest->disk_name);
709 }
710 dest->disk_name = strdup(src->disk_name);
711 dest->read_bytes = src->read_bytes;
712 dest->write_bytes = src->write_bytes;
713 dest->systime = src->systime;
714 }
715
716 /* Get a new set of stats. */
717 if (get_diskio_stats(&new_count) == NULL) {
718 return NULL;
719 }
720
721 /* For each previous stat... */
722 for (i = 0; i < diff_count; i++) {
723 dest = &diff[i];
724
725 /* ... find the corresponding new stat ... */
726 for (j = 0; j < new_count; j++) {
727 /* Try the new stat in the same position first,
728 since that's most likely to be it. */
729 src = &diskio_stats[(i + j) % new_count];
730 if (strcmp(src->disk_name, dest->disk_name) == 0) {
731 break;
732 }
733 }
734 if (j == new_count) {
735 /* No match found. */
736 continue;
737 }
738
739 /* ... and subtract the previous stat from it to get the
740 difference. */
741 dest->read_bytes = src->read_bytes - dest->read_bytes;
742 dest->write_bytes = src->write_bytes - dest->write_bytes;
743 dest->systime = src->systime - dest->systime;
744 }
745
746 *entries = diff_count;
747 return diff;
748 }
749