ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/disk_stats.c
Revision: 1.66
Committed: Wed Apr 7 19:26:16 2004 UTC (20 years, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.65: +28 -5 lines
Log Message:
Error reporting for disk_stats.

File Contents

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