void get_iface_stats_libstatdisk (char verbose) { sg_disk_io_stats *disk_stats=NULL; int num_disk_stats,current_if_num=0,hidden_if=0; t_iface_speed_stats stats; /* local struct, used to calc total values */ t_iface_speed_stats tmp_if_stats; memset(&stats,0,(size_t)sizeof(t_iface_speed_stats)); /* init it */ disk_stats = sg_get_disk_io_stats(&num_disk_stats); if (disk_stats == NULL){ deinit(1, "libstatgrab error!\n"); } for (current_if_num=0;current_if_num<num_disk_stats;current_if_num++) { tmp_if_stats.bytes.in=disk_stats->read_bytes; tmp_if_stats.bytes.out=disk_stats->write_bytes; tmp_if_stats.packets.in=0; tmp_if_stats.packets.out=0; tmp_if_stats.errors.in=0; tmp_if_stats.errors.out=0; hidden_if = process_if_data (hidden_if, tmp_if_stats, &stats, disk_stats->disk_name, current_if_num, verbose,1); disk_stats++; } finish_iface_stats (verbose, stats, hidden_if,current_if_num); return; }
/* * Disk I/O statistics, see <tt>sg_get_disk_io_stats(3)</tt> manpage. */ static VALUE statgrab_disk_io_stats(VALUE self) { int entries, i; sg_disk_io_stats *stats; VALUE arr, info, time_at; if ((stats = sg_get_disk_io_stats(&entries)) == NULL) statgrab_handle_error(); arr = rb_ary_new(); for (i = 0; i < entries; i++) { info = rb_hash_new(); rb_hash_aset(info, ID2SYM(rb_intern("disk_name")), rb_str_new2(stats[i].disk_name)); rb_hash_aset(info, ID2SYM(rb_intern("read_bytes")), INT2NUM(stats[i].read_bytes/1024)); rb_hash_aset(info, ID2SYM(rb_intern("write_bytes")), INT2NUM(stats[i].write_bytes/1024)); rb_hash_aset(info, ID2SYM(rb_intern("systime")), INT2NUM(stats[i].systime)); time_at = rb_funcall(rb_cTime, rb_intern("at"), 1, INT2NUM(stats[i].systime)); rb_hash_aset(info, ID2SYM(rb_intern("time")), time_at); rb_ary_push(arr, info); } return arr; }
void populate_disk() { int n, i; sg_disk_io_stats *diskio; diskio = use_diffs ? sg_get_disk_io_stats_diff(&n) : sg_get_disk_io_stats(&n); if (diskio != NULL) { for (i = 0; i < n; i++) { const char *name = diskio[i].disk_name; add_stat(STRING, &diskio[i].disk_name, "disk", name, "disk_name", NULL); add_stat(BYTES, &diskio[i].read_bytes, "disk", name, "read_bytes", NULL); add_stat(BYTES, &diskio[i].write_bytes, "disk", name, "write_bytes", NULL); add_stat(TIME_T, &diskio[i].systime, "disk", name, "systime", NULL); } } }
sg_fs_stats *sg_get_fs_stats(int *entries){ VECTOR_DECLARE_STATIC(disk_stats, sg_fs_stats, 10, disk_stat_init, disk_stat_destroy); int num_disks=0; #if defined(LINUX) || defined (SOLARIS) || defined(CYGWIN) || defined(HPUX) FILE *f; #endif sg_fs_stats *disk_ptr; #ifdef SOLARIS struct mnttab mp; struct statvfs fs; #endif #if defined(LINUX) || defined(CYGWIN) || defined(HPUX) struct mntent *mp; struct statvfs fs; #endif #ifdef ALLBSD int nummnt; #ifdef HAVE_STATVFS struct statvfs *mp, **fs; #else struct statfs *mp, **fs; #endif #endif #ifdef WIN32 char lp_buf[MAX_PATH]; char volume_name_buf[BUFSIZE]; char filesys_name_buf[BUFSIZE]; char drive[4] = " :\\"; char *p; int drive_type; //@ lp_buf[0]='\0'; #endif #ifdef ALLBSD nummnt=getmntinfo(&mp, MNT_WAIT); if (nummnt<=0){ sg_set_error_with_errno(SG_ERROR_GETMNTINFO, NULL); return NULL; } for(fs = ∓ nummnt--; (*fs)++){ #endif #if defined(LINUX) || defined(CYGWIN) || defined(HPUX) #ifdef MNT_MNTTAB if ((f=setmntent(MNT_MNTTAB, "r" ))==NULL){ #else if ((f=setmntent("/etc/mtab", "r" ))==NULL){ #endif sg_set_error(SG_ERROR_SETMNTENT, NULL); return NULL; } while((mp=getmntent(f))){ if((statvfs(mp->mnt_dir, &fs)) !=0){ continue; } #endif #ifdef SOLARIS if ((f=fopen("/etc/mnttab", "r" ))==NULL){ sg_set_error_with_errno(SG_ERROR_OPEN, "/etc/mnttab"); return NULL; } while((getmntent(f, &mp)) == 0){ if ((statvfs(mp.mnt_mountp, &fs)) !=0){ continue; } #endif #ifdef WIN32 if (!(GetLogicalDriveStringsA(BUFSIZE-1, lp_buf))) { sg_set_error(SG_ERROR_GETMNTINFO, "GetLogicalDriveStrings"); return NULL; } p = lp_buf; do { // Copy drive letter to template string *drive = *p; // Only interested in harddrives. drive_type = GetDriveTypeA(drive); if(drive_type == DRIVE_FIXED) { #else if(is_valid_fs_type(SG_MP_FSTYPENAME(mp))){ #endif if (VECTOR_RESIZE(disk_stats, num_disks + 1) < 0) { return NULL; } disk_ptr=disk_stats+num_disks; #ifndef WIN32 /* Maybe make this char[bigenough] and do strncpy's and put a null in the end? * Downside is its a bit hungry for a lot of mounts, as MNT_MAX_SIZE would prob * be upwards of a k each */ if (sg_update_string(&disk_ptr->device_name, SG_MP_DEVNAME(mp)) < 0) { return NULL; } if (sg_update_string(&disk_ptr->fs_type, SG_MP_FSTYPENAME(mp)) < 0) { return NULL; } if (sg_update_string(&disk_ptr->mnt_point, SG_MP_MOUNTP(mp)) < 0) { return NULL; } disk_ptr->size = SG_FS_FRSIZE(fs) * SG_FS_BLOCKS(fs); disk_ptr->avail = SG_FS_FRSIZE(fs) * SG_FS_BAVAIL(fs); disk_ptr->used = (disk_ptr->size) - (SG_FS_FRSIZE(fs) * SG_FS_BFREE(fs)); disk_ptr->total_inodes = SG_FS_FILES(fs); disk_ptr->free_inodes = SG_FS_FFREE(fs); /* Linux, FreeBSD don't have a "available" inodes */ disk_ptr->used_inodes = disk_ptr->total_inodes - disk_ptr->free_inodes; disk_ptr->avail_inodes = SG_FS_FAVAIL(fs); disk_ptr->io_size = SG_FS_BSIZE(fs); disk_ptr->block_size = SG_FS_FRSIZE(fs); disk_ptr->total_blocks = SG_FS_BLOCKS(fs); disk_ptr->free_blocks = SG_FS_BFREE(fs); disk_ptr->avail_blocks = SG_FS_BAVAIL(fs); disk_ptr->used_blocks = disk_ptr->total_blocks - disk_ptr->free_blocks; #else if(!GetVolumeInformationA(drive, volume_name_buf, BUFSIZE, NULL, NULL, NULL, filesys_name_buf, BUFSIZE)) { sg_set_error_with_errno(SG_ERROR_DISKINFO, "GetVolumeInformation"); return NULL; } if (sg_update_string(&disk_ptr->device_name, volume_name_buf) < 0) { return NULL; } if (sg_update_string(&disk_ptr->fs_type, filesys_name_buf) < 0) { return NULL; } if (sg_update_string(&disk_ptr->mnt_point, drive) < 0) { return NULL; } if (!GetDiskFreeSpaceExA(drive, NULL, (PULARGE_INTEGER)&disk_ptr->size, (PULARGE_INTEGER)&disk_ptr->avail)) { sg_set_error_with_errno(SG_ERROR_DISKINFO, "GetDiskFreeSpaceEx"); return NULL; } disk_ptr->used = disk_ptr->size - disk_ptr->avail; disk_ptr->total_inodes = 0; disk_ptr->free_inodes = 0; disk_ptr->used_inodes = 0; disk_ptr->avail_inodes = 0; /* I dunno what to do with these... so have nothing */ disk_ptr->io_size = 0; disk_ptr->block_size = 0; disk_ptr->total_blocks = 0; disk_ptr->free_blocks = 0; disk_ptr->avail_blocks = 0; disk_ptr->used_blocks = 0; #endif num_disks++; } #ifdef WIN32 while(*p++); } while(*p); #else } #endif *entries=num_disks; /* If this fails, there is very little i can do about it, so I'll ignore it :) */ #if defined(LINUX) || defined(CYGWIN) || defined(HPUX) endmntent(f); #endif #if defined(SOLARIS) fclose(f); #endif return disk_stats; } int sg_fs_compare_device_name(const void *va, const void *vb) { const sg_fs_stats *a = (const sg_fs_stats *)va; const sg_fs_stats *b = (const sg_fs_stats *)vb; return strcmp(a->device_name, b->device_name); } int sg_fs_compare_mnt_point(const void *va, const void *vb) { const sg_fs_stats *a = (const sg_fs_stats *)va; const sg_fs_stats *b = (const sg_fs_stats *)vb; return strcmp(a->mnt_point, b->mnt_point); } static void diskio_stat_init(sg_disk_io_stats *d) { d->disk_name = NULL; } static void diskio_stat_destroy(sg_disk_io_stats *d) { free(d->disk_name); } VECTOR_DECLARE_STATIC(diskio_stats, sg_disk_io_stats, 10, diskio_stat_init, diskio_stat_destroy); #ifdef LINUX typedef struct { int major; int minor; } partition; #endif sg_disk_io_stats *sg_get_disk_io_stats(int *entries){ int num_diskio; #ifndef LINUX sg_disk_io_stats *diskio_stats_ptr; #endif #ifdef HPUX long long rbytes = 0, wbytes = 0; struct dirent *dinfo = NULL; struct stat lstatinfo; struct pst_diskinfo pstat_diskinfo[DISK_BATCH]; char fullpathbuf[1024] = {0}; dev_t diskid; DIR *dh = NULL; int diskidx = 0; int num, i; #endif #ifdef SOLARIS kstat_ctl_t *kc; kstat_t *ksp; kstat_io_t kios; #endif #ifdef LINUX FILE *f; char *line_ptr; int major, minor; int has_pp_stats = 1; VECTOR_DECLARE_STATIC(parts, partition, 16, NULL, NULL); int i, n; time_t now; const char *format; static regex_t not_part_re, part_re; static int re_compiled = 0; #endif #if defined(FREEBSD) || defined(DFBSD) static struct statinfo stats; static int stats_init = 0; int counter; struct device_selection *dev_sel = NULL; int n_selected, n_selections; long sel_gen; struct devstat *dev_ptr; #endif #ifdef NETBSD struct disk_sysctl *stats; #endif #ifdef OPENBSD int diskcount; char *disknames, *name, *bufpp; char **dk_name; struct diskstats *stats; #endif #ifdef NETBSD #define MIBSIZE 3 #endif #ifdef OPENBSD #define MIBSIZE 2 #endif #if defined(NETBSD) || defined(OPENBSD) int num_disks, i; int mib[MIBSIZE]; size_t size; #endif #ifdef WIN32 char *name; long long rbytes; long long wbytes; #endif num_diskio=0; #ifdef HPUX while (1) { num = pstat_getdisk(pstat_diskinfo, sizeof pstat_diskinfo[0], DISK_BATCH, diskidx); if (num == -1) { sg_set_error_with_errno(SG_ERROR_PSTAT, "pstat_getdisk"); return NULL; } else if (num == 0) { break; } for (i = 0; i < num; i++) { struct pst_diskinfo *di = &pstat_diskinfo[i]; /* Skip "disabled" disks. */ if (di->psd_status == 0) { continue; } /* We can't seperate the reads from the writes, we'll * just give the same to each. (This value is in * 64-byte chunks according to the pstat header file, * and can wrap to be negative.) */ rbytes = wbytes = ((unsigned long) di->psd_dkwds) * 64LL; /* Skip unused disks. */ if (rbytes == 0 && wbytes == 0) { continue; } if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) { return NULL; } diskio_stats_ptr = diskio_stats + num_diskio; diskio_stats_ptr->read_bytes = rbytes; diskio_stats_ptr->write_bytes = wbytes; diskio_stats_ptr->systime = time(NULL); num_diskio++; /* FIXME This should use a static cache, like the Linux * code below. */ if (diskio_stats_ptr->disk_name == NULL) { dh = opendir("/dev/dsk"); if (dh == NULL) { continue; } diskid = (di->psd_dev.psd_major << 24) | di->psd_dev.psd_minor; while (1) { dinfo = readdir(dh); if (dinfo == NULL) { break; } snprintf(fullpathbuf, sizeof(fullpathbuf), "/dev/dsk/%s", dinfo->d_name); if (lstat(fullpathbuf, &lstatinfo) < 0) { continue; } if (lstatinfo.st_rdev == diskid) { if (sg_update_string(&diskio_stats_ptr->disk_name, dinfo->d_name) < 0) { return NULL; } break; } } closedir(dh); if (diskio_stats_ptr->disk_name == NULL) { if (sg_update_string(&diskio_stats_ptr->disk_name, di->psd_hw_path.psh_name) < 0) { return NULL; } } } } diskidx = pstat_diskinfo[num - 1].psd_idx + 1; } #endif #ifdef OPENBSD mib[0] = CTL_HW; mib[1] = HW_DISKCOUNT; size = sizeof(diskcount); if (sysctl(mib, MIBSIZE, &diskcount, &size, NULL, 0) < 0) { sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKCOUNT"); return NULL; } mib[0] = CTL_HW; mib[1] = HW_DISKNAMES; if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) { sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES"); return NULL; } disknames = sg_malloc(size); if (disknames == NULL) { return NULL; } if (sysctl(mib, MIBSIZE, disknames, &size, NULL, 0) < 0) { sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKNAMES"); return NULL; } dk_name = sg_malloc(diskcount * sizeof(char *)); bufpp = disknames; for (i = 0; i < diskcount && (name = strsep(&bufpp, ",")) != NULL; i++) { dk_name[i] = name; } #endif #if defined(NETBSD) || defined(OPENBSD) mib[0] = CTL_HW; mib[1] = HW_DISKSTATS; #ifdef NETBSD mib[2] = sizeof(struct disk_sysctl); #endif if (sysctl(mib, MIBSIZE, NULL, &size, NULL, 0) < 0) { sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS"); return NULL; } #ifdef NETBSD num_disks = size / sizeof(struct disk_sysctl); #else num_disks = size / sizeof(struct diskstats); #endif stats = sg_malloc(size); if (stats == NULL) { return NULL; } if (sysctl(mib, MIBSIZE, stats, &size, NULL, 0) < 0) { sg_set_error_with_errno(SG_ERROR_SYSCTL, "CTL_HW.HW_DISKSTATS"); return NULL; } for (i = 0; i < num_disks; i++) { const char *name; u_int64_t rbytes, wbytes; #ifdef NETBSD #ifdef HAVE_DK_RBYTES rbytes = stats[i].dk_rbytes; wbytes = stats[i].dk_wbytes; #else /* Before 2.0, NetBSD merged reads and writes. */ rbytes = wbytes = stats[i].dk_bytes; #endif #else #ifdef HAVE_DS_RBYTES rbytes = stats[i].ds_rbytes; wbytes = stats[i].ds_wbytes; #else /* Before 3.5, OpenBSD merged reads and writes */ rbytes = wbytes = stats[i].ds_bytes; #endif #endif /* Don't keep stats for disks that have never been used. */ if (rbytes == 0 && wbytes == 0) { continue; } if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) { return NULL; } diskio_stats_ptr = diskio_stats + num_diskio; diskio_stats_ptr->read_bytes = rbytes; diskio_stats_ptr->write_bytes = wbytes; #ifdef NETBSD name = stats[i].dk_name; #else name = dk_name[i]; #endif if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) { return NULL; } diskio_stats_ptr->systime = time(NULL); num_diskio++; } free(stats); #ifdef OPENBSD free(dk_name); free(disknames); #endif #endif #if defined(FREEBSD) || defined(DFBSD) if (!stats_init) { stats.dinfo=sg_malloc(sizeof(struct devinfo)); if(stats.dinfo==NULL) return NULL; bzero(stats.dinfo, sizeof(struct devinfo)); stats_init = 1; } #ifdef FREEBSD5 if ((devstat_getdevs(NULL, &stats)) < 0) { /* FIXME devstat functions return a string error in devstat_errbuf */ sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL); return NULL; } /* Not aware of a get all devices, so i said 999. If we ever * find a machine with more than 999 disks, then i'll change * this number :) */ 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) { sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL); return NULL; } #else if ((getdevs(&stats)) < 0) { sg_set_error(SG_ERROR_DEVSTAT_GETDEVS, NULL); return NULL; } /* Not aware of a get all devices, so i said 999. If we ever * find a machine with more than 999 disks, then i'll change * this number :) */ 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) { sg_set_error(SG_ERROR_DEVSTAT_SELECTDEVS, NULL); return NULL; } #endif for(counter=0;counter<stats.dinfo->numdevs;counter++){ dev_ptr=&stats.dinfo->devices[dev_sel[counter].position]; /* Throw away devices that have done nothing, ever.. Eg "odd" * devices.. like mem, proc.. and also doesn't report floppy * drives etc unless they are doing stuff :) */ #ifdef FREEBSD5 if((dev_ptr->bytes[DEVSTAT_READ]==0) && (dev_ptr->bytes[DEVSTAT_WRITE]==0)) continue; #else if((dev_ptr->bytes_read==0) && (dev_ptr->bytes_written==0)) continue; #endif if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) { return NULL; } diskio_stats_ptr=diskio_stats+num_diskio; #ifdef FREEBSD5 diskio_stats_ptr->read_bytes=dev_ptr->bytes[DEVSTAT_READ]; diskio_stats_ptr->write_bytes=dev_ptr->bytes[DEVSTAT_WRITE]; #else diskio_stats_ptr->read_bytes=dev_ptr->bytes_read; diskio_stats_ptr->write_bytes=dev_ptr->bytes_written; #endif if(diskio_stats_ptr->disk_name!=NULL) free(diskio_stats_ptr->disk_name); if (asprintf((&diskio_stats_ptr->disk_name), "%s%d", dev_ptr->device_name, dev_ptr->unit_number) == -1) { sg_set_error_with_errno(SG_ERROR_ASPRINTF, NULL); return NULL; } diskio_stats_ptr->systime=time(NULL); num_diskio++; } free(dev_sel); #endif #ifdef SOLARIS if ((kc = kstat_open()) == NULL) { sg_set_error(SG_ERROR_KSTAT_OPEN, NULL); return NULL; } for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (!strcmp(ksp->ks_class, "disk")) { if(ksp->ks_type != KSTAT_TYPE_IO) continue; /* We dont want metadevices appearins as num_diskio */ if(strcmp(ksp->ks_module, "md")==0) continue; if((kstat_read(kc, ksp, &kios))==-1){ } if (VECTOR_RESIZE(diskio_stats, num_diskio + 1) < 0) { kstat_close(kc); return NULL; } diskio_stats_ptr=diskio_stats+num_diskio; diskio_stats_ptr->read_bytes=kios.nread; diskio_stats_ptr->write_bytes=kios.nwritten; if (sg_update_string(&diskio_stats_ptr->disk_name, sg_get_svr_from_bsd(ksp->ks_name)) < 0) { kstat_close(kc); return NULL; } diskio_stats_ptr->systime=time(NULL); num_diskio++; } } kstat_close(kc); #endif #ifdef LINUX num_diskio = 0; n = 0; /* Read /proc/partitions to find what devices exist. Recent 2.4 kernels have statistics in here too, so we can use those directly. 2.6 kernels have /proc/diskstats instead with almost (but not quite) the same format. */ f = fopen("/proc/diskstats", "r"); format = " %d %d %99s %*d %*d %lld %*d %*d %*d %lld"; if (f == NULL) { f = fopen("/proc/partitions", "r"); format = " %d %d %*d %99s %*d %*d %lld %*d %*d %*d %lld"; } if (f == NULL) goto out; now = time(NULL); if (!re_compiled) { if (regcomp(&part_re, "^(.*/)?[^/]*[0-9]$", REG_EXTENDED | REG_NOSUB) != 0) { sg_set_error(SG_ERROR_PARSE, NULL); goto out; } if (regcomp(¬_part_re, "^(.*/)?[^/0-9]+[0-9]+d[0-9]+$", REG_EXTENDED | REG_NOSUB) != 0) { sg_set_error(SG_ERROR_PARSE, NULL); goto out; } re_compiled = 1; } while ((line_ptr = sg_f_read_line(f, "")) != NULL) { char name[100]; long long rsect, wsect; int nr = sscanf(line_ptr, format, &major, &minor, name, &rsect, &wsect); if (nr < 3) continue; /* Skip device names ending in numbers, since they're partitions, unless they match the c0d0 pattern that some RAID devices use. */ /* FIXME: For 2.6+, we should probably be using sysfs to detect this... */ if ((regexec(&part_re, name, 0, NULL, 0) == 0) && (regexec(¬_part_re, name, 0, NULL, 0) != 0)) { continue; } if (nr < 5) { has_pp_stats = 0; rsect = 0; wsect = 0; } if (VECTOR_RESIZE(diskio_stats, n + 1) < 0) { goto out; } if (VECTOR_RESIZE(parts, n + 1) < 0) { goto out; } if (sg_update_string(&diskio_stats[n].disk_name, name) < 0) { goto out; } diskio_stats[n].read_bytes = rsect * 512; diskio_stats[n].write_bytes = wsect * 512; diskio_stats[n].systime = now; parts[n].major = major; parts[n].minor = minor; n++; } fclose(f); f = NULL; if (!has_pp_stats) { /* This is an older kernel where /proc/partitions doesn't contain stats. Read what we can from /proc/stat instead, and fill in the appropriate bits of the list allocated above. */ f = fopen("/proc/stat", "r"); if (f == NULL) goto out; now = time(NULL); line_ptr = sg_f_read_line(f, "disk_io:"); if (line_ptr == NULL) goto out; while((line_ptr=strchr(line_ptr, ' '))!=NULL){ long long rsect, wsect; if (*++line_ptr == '\0') break; if((sscanf(line_ptr, "(%d,%d):(%*d, %*d, %lld, %*d, %lld)", &major, &minor, &rsect, &wsect)) != 4) { continue; } /* Find the corresponding device from earlier. Just to add to the fun, "minor" is actually the disk number, not the device minor, so we need to figure out the real minor number based on the major! This list is not exhaustive; if you're running an older kernel you probably don't have fancy I2O hardware anyway... */ switch (major) { case 3: case 21: case 22: case 33: case 34: case 36: case 56: case 57: case 88: case 89: case 90: case 91: minor *= 64; break; case 9: case 43: break; default: minor *= 16; break; } for (i = 0; i < n; i++) { if (major == parts[i].major && minor == parts[i].minor) break; } if (i == n) continue; /* We read the number of blocks. Blocks are stored in 512 bytes */ diskio_stats[i].read_bytes = rsect * 512; diskio_stats[i].write_bytes = wsect * 512; diskio_stats[i].systime = now; } } num_diskio = n; out: if (f != NULL) fclose(f); #endif #ifdef CYGWIN sg_set_error(SG_ERROR_UNSUPPORTED, "Cygwin"); return NULL; #endif #ifdef WIN32 sg_set_error(SG_ERROR_NONE, NULL); while((name = get_diskio(num_diskio, &rbytes, &wbytes)) != NULL) { if (VECTOR_RESIZE(diskio_stats, num_diskio+1)) { return NULL; } diskio_stats_ptr = diskio_stats + num_diskio; if (sg_update_string(&diskio_stats_ptr->disk_name, name) < 0) { return NULL; } sg_update_string(&name, NULL); diskio_stats_ptr->read_bytes = rbytes; diskio_stats_ptr->write_bytes = wbytes; diskio_stats_ptr->systime = 0; num_diskio++; } #endif *entries=num_diskio; return diskio_stats; } sg_disk_io_stats *sg_get_disk_io_stats_diff(int *entries){ #ifndef WIN32 VECTOR_DECLARE_STATIC(diff, sg_disk_io_stats, 1, diskio_stat_init, diskio_stat_destroy); sg_disk_io_stats *src = NULL, *dest; int i, j, diff_count, new_count; if (diskio_stats == NULL) { /* No previous stats, so we can't calculate a difference. */ return sg_get_disk_io_stats(entries); } /* Resize the results array to match the previous stats. */ diff_count = VECTOR_SIZE(diskio_stats); if (VECTOR_RESIZE(diff, diff_count) < 0) { return NULL; } /* Copy the previous stats into the result. */ for (i = 0; i < diff_count; i++) { src = &diskio_stats[i]; dest = &diff[i]; if (sg_update_string(&dest->disk_name, src->disk_name) < 0) { return NULL; } dest->read_bytes = src->read_bytes; dest->write_bytes = src->write_bytes; dest->systime = src->systime; } /* Get a new set of stats. */ if (sg_get_disk_io_stats(&new_count) == NULL) { return NULL; } /* For each previous stat... */ for (i = 0; i < diff_count; i++) { dest = &diff[i]; /* ... find the corresponding new stat ... */ for (j = 0; j < new_count; j++) { /* Try the new stat in the same position first, since that's most likely to be it. */ src = &diskio_stats[(i + j) % new_count]; if (strcmp(src->disk_name, dest->disk_name) == 0) { break; } } if (j == new_count) { /* No match found. */ continue; } /* ... and subtract the previous stat from it to get the difference. */ dest->read_bytes = src->read_bytes - dest->read_bytes; dest->write_bytes = src->write_bytes - dest->write_bytes; dest->systime = src->systime - dest->systime; } *entries = diff_count; return diff; #else /* WIN32 */ return sg_get_disk_io_stats(entries); #endif } int sg_disk_io_compare_name(const void *va, const void *vb) { const sg_disk_io_stats *a = (const sg_disk_io_stats *)va; const sg_disk_io_stats *b = (const sg_disk_io_stats *)vb; return strcmp(a->disk_name, b->disk_name); }
static int disk_read (void) { #if HAVE_IOKIT_IOKITLIB_H io_registry_entry_t disk; io_registry_entry_t disk_child; io_iterator_t disk_list; CFDictionaryRef props_dict; CFDictionaryRef stats_dict; CFDictionaryRef child_dict; CFStringRef tmp_cf_string_ref; kern_return_t status; signed long long read_ops; signed long long read_byt; signed long long read_tme; signed long long write_ops; signed long long write_byt; signed long long write_tme; int disk_major; int disk_minor; char disk_name[DATA_MAX_NAME_LEN]; char disk_name_bsd[DATA_MAX_NAME_LEN]; /* Get the list of all disk objects. */ if (IOServiceGetMatchingServices (io_master_port, IOServiceMatching (kIOBlockStorageDriverClass), &disk_list) != kIOReturnSuccess) { ERROR ("disk plugin: IOServiceGetMatchingServices failed."); return (-1); } while ((disk = IOIteratorNext (disk_list)) != 0) { props_dict = NULL; stats_dict = NULL; child_dict = NULL; /* `disk_child' must be released */ if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child)) != kIOReturnSuccess) { /* This fails for example for DVD/CD drives.. */ DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status); IOObjectRelease (disk); continue; } /* We create `props_dict' => we need to release it later */ if (IORegistryEntryCreateCFProperties (disk, (CFMutableDictionaryRef *) &props_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) { ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed."); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } if (props_dict == NULL) { DEBUG ("IORegistryEntryCreateCFProperties (disk) failed."); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } /* tmp_cf_string_ref doesn't need to be released. */ tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict, CFSTR(kIOBSDNameKey)); if (!tmp_cf_string_ref) { DEBUG ("disk plugin: CFDictionaryGetValue(" "kIOBSDNameKey) failed."); CFRelease (props_dict); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ()); memset (disk_name_bsd, 0, sizeof (disk_name_bsd)); CFStringGetCString (tmp_cf_string_ref, disk_name_bsd, sizeof (disk_name_bsd), kCFStringEncodingUTF8); if (disk_name_bsd[0] == 0) { ERROR ("disk plugin: CFStringGetCString() failed."); CFRelease (props_dict); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } DEBUG ("disk plugin: disk_name_bsd = \"%s\"", disk_name_bsd); stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict, CFSTR (kIOBlockStorageDriverStatisticsKey)); if (stats_dict == NULL) { DEBUG ("disk plugin: CFDictionaryGetValue (" "%s) failed.", kIOBlockStorageDriverStatisticsKey); CFRelease (props_dict); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } if (IORegistryEntryCreateCFProperties (disk_child, (CFMutableDictionaryRef *) &child_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) { DEBUG ("disk plugin: IORegistryEntryCreateCFProperties (" "disk_child) failed."); IOObjectRelease (disk_child); CFRelease (props_dict); IOObjectRelease (disk); continue; } /* kIOBSDNameKey */ disk_major = (int) dict_get_value (child_dict, kIOBSDMajorKey); disk_minor = (int) dict_get_value (child_dict, kIOBSDMinorKey); read_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsReadsKey); read_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesReadKey); read_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalReadTimeKey); write_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsWritesKey); write_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesWrittenKey); /* This property describes the number of nanoseconds spent * performing writes since the block storage driver was * instantiated. It is one of the statistic entries listed * under the top-level kIOBlockStorageDriverStatisticsKey * property table. It has an OSNumber value. */ write_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey); if (use_bsd_name) sstrncpy (disk_name, disk_name_bsd, sizeof (disk_name)); else ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor); DEBUG ("disk plugin: disk_name = \"%s\"", disk_name); if ((read_byt != -1LL) || (write_byt != -1LL)) disk_submit (disk_name, "disk_octets", read_byt, write_byt); if ((read_ops != -1LL) || (write_ops != -1LL)) disk_submit (disk_name, "disk_ops", read_ops, write_ops); if ((read_tme != -1LL) || (write_tme != -1LL)) disk_submit (disk_name, "disk_time", read_tme / 1000, write_tme / 1000); CFRelease (child_dict); IOObjectRelease (disk_child); CFRelease (props_dict); IOObjectRelease (disk); } IOObjectRelease (disk_list); /* #endif HAVE_IOKIT_IOKITLIB_H */ #elif KERNEL_LINUX FILE *fh; char buffer[1024]; char *fields[32]; int numfields; int fieldshift = 0; int minor = 0; derive_t read_sectors = 0; derive_t write_sectors = 0; derive_t read_ops = 0; derive_t read_merged = 0; derive_t read_time = 0; derive_t write_ops = 0; derive_t write_merged = 0; derive_t write_time = 0; int is_disk = 0; diskstats_t *ds, *pre_ds; if ((fh = fopen ("/proc/diskstats", "r")) == NULL) { fh = fopen ("/proc/partitions", "r"); if (fh == NULL) { ERROR ("disk plugin: fopen (/proc/{diskstats,partitions}) failed."); return (-1); } /* Kernel is 2.4.* */ fieldshift = 1; } while (fgets (buffer, sizeof (buffer), fh) != NULL) { char *disk_name; numfields = strsplit (buffer, fields, 32); if ((numfields != (14 + fieldshift)) && (numfields != 7)) continue; minor = atoll (fields[1]); disk_name = fields[2 + fieldshift]; for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next) if (strcmp (disk_name, ds->name) == 0) break; if (ds == NULL) { if ((ds = (diskstats_t *) calloc (1, sizeof (diskstats_t))) == NULL) continue; if ((ds->name = strdup (disk_name)) == NULL) { free (ds); continue; } if (pre_ds == NULL) disklist = ds; else pre_ds->next = ds; } is_disk = 0; if (numfields == 7) { /* Kernel 2.6, Partition */ read_ops = atoll (fields[3]); read_sectors = atoll (fields[4]); write_ops = atoll (fields[5]); write_sectors = atoll (fields[6]); } else if (numfields == (14 + fieldshift)) { read_ops = atoll (fields[3 + fieldshift]); write_ops = atoll (fields[7 + fieldshift]); read_sectors = atoll (fields[5 + fieldshift]); write_sectors = atoll (fields[9 + fieldshift]); if ((fieldshift == 0) || (minor == 0)) { is_disk = 1; read_merged = atoll (fields[4 + fieldshift]); read_time = atoll (fields[6 + fieldshift]); write_merged = atoll (fields[8 + fieldshift]); write_time = atoll (fields[10+ fieldshift]); } } else { DEBUG ("numfields = %i; => unknown file format.", numfields); continue; } { derive_t diff_read_sectors; derive_t diff_write_sectors; /* If the counter wraps around, it's only 32 bits.. */ if (read_sectors < ds->read_sectors) diff_read_sectors = 1 + read_sectors + (UINT_MAX - ds->read_sectors); else diff_read_sectors = read_sectors - ds->read_sectors; if (write_sectors < ds->write_sectors) diff_write_sectors = 1 + write_sectors + (UINT_MAX - ds->write_sectors); else diff_write_sectors = write_sectors - ds->write_sectors; ds->read_bytes += 512 * diff_read_sectors; ds->write_bytes += 512 * diff_write_sectors; ds->read_sectors = read_sectors; ds->write_sectors = write_sectors; } /* Calculate the average time an io-op needs to complete */ if (is_disk) { derive_t diff_read_ops; derive_t diff_write_ops; derive_t diff_read_time; derive_t diff_write_time; if (read_ops < ds->read_ops) diff_read_ops = 1 + read_ops + (UINT_MAX - ds->read_ops); else diff_read_ops = read_ops - ds->read_ops; DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; " "ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";", disk_name, read_ops, ds->read_ops, diff_read_ops); if (write_ops < ds->write_ops) diff_write_ops = 1 + write_ops + (UINT_MAX - ds->write_ops); else diff_write_ops = write_ops - ds->write_ops; if (read_time < ds->read_time) diff_read_time = 1 + read_time + (UINT_MAX - ds->read_time); else diff_read_time = read_time - ds->read_time; if (write_time < ds->write_time) diff_write_time = 1 + write_time + (UINT_MAX - ds->write_time); else diff_write_time = write_time - ds->write_time; if (diff_read_ops != 0) ds->avg_read_time += disk_calc_time_incr ( diff_read_time, diff_read_ops); if (diff_write_ops != 0) ds->avg_write_time += disk_calc_time_incr ( diff_write_time, diff_write_ops); ds->read_ops = read_ops; ds->read_time = read_time; ds->write_ops = write_ops; ds->write_time = write_time; } /* if (is_disk) */ /* Don't write to the RRDs if we've just started.. */ ds->poll_count++; if (ds->poll_count <= 2) { DEBUG ("disk plugin: (ds->poll_count = %i) <= " "(min_poll_count = 2); => Not writing.", ds->poll_count); continue; } if ((read_ops == 0) && (write_ops == 0)) { DEBUG ("disk plugin: ((read_ops == 0) && " "(write_ops == 0)); => Not writing."); continue; } if ((ds->read_bytes != 0) || (ds->write_bytes != 0)) disk_submit (disk_name, "disk_octets", ds->read_bytes, ds->write_bytes); if ((ds->read_ops != 0) || (ds->write_ops != 0)) disk_submit (disk_name, "disk_ops", read_ops, write_ops); if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0)) disk_submit (disk_name, "disk_time", ds->avg_read_time, ds->avg_write_time); if (is_disk) { disk_submit (disk_name, "disk_merged", read_merged, write_merged); } /* if (is_disk) */ } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */ fclose (fh); /* #endif defined(KERNEL_LINUX) */ #elif HAVE_LIBKSTAT # if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME # define KIO_ROCTETS reads # define KIO_WOCTETS writes # define KIO_ROPS nreads # define KIO_WOPS nwrites # define KIO_RTIME rtime # define KIO_WTIME wtime # elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME # define KIO_ROCTETS nread # define KIO_WOCTETS nwritten # define KIO_ROPS reads # define KIO_WOPS writes # define KIO_RTIME rtime # define KIO_WTIME wtime # else # error "kstat_io_t does not have the required members" # endif static kstat_io_t kio; int i; if (kc == NULL) return (-1); for (i = 0; i < numdisk; i++) { if (kstat_read (kc, ksp[i], &kio) == -1) continue; if (strncmp (ksp[i]->ks_class, "disk", 4) == 0) { disk_submit (ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS, kio.KIO_WOCTETS); disk_submit (ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS); /* FIXME: Convert this to microseconds if necessary */ disk_submit (ksp[i]->ks_name, "disk_time", kio.KIO_RTIME, kio.KIO_WTIME); } else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0) { disk_submit (ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS, kio.KIO_WOCTETS); disk_submit (ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS); } } /* #endif defined(HAVE_LIBKSTAT) */ #elif defined(HAVE_LIBSTATGRAB) sg_disk_io_stats *ds; int disks, counter; char name[DATA_MAX_NAME_LEN]; if ((ds = sg_get_disk_io_stats(&disks)) == NULL) return (0); for (counter=0; counter < disks; counter++) { strncpy(name, ds->disk_name, sizeof(name)); name[sizeof(name)-1] = '\0'; /* strncpy doesn't terminate longer strings */ disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes); ds++; } /* #endif defined(HAVE_LIBSTATGRAB) */ #elif defined(HAVE_PERFSTAT) derive_t read_sectors; derive_t write_sectors; derive_t read_time; derive_t write_time; derive_t read_ops; derive_t write_ops; perfstat_id_t firstpath; int rnumdisk; int i; if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) { char errbuf[1024]; WARNING ("disk plugin: perfstat_disk: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } if (numdisk != pnumdisk || stat_disk==NULL) { if (stat_disk!=NULL) free(stat_disk); stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t)); } pnumdisk = numdisk; firstpath.name[0]='\0'; if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t), numdisk)) < 0) { char errbuf[1024]; WARNING ("disk plugin: perfstat_disk : %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } for (i = 0; i < rnumdisk; i++) { read_sectors = stat_disk[i].rblks*stat_disk[i].bsize; write_sectors = stat_disk[i].wblks*stat_disk[i].bsize; disk_submit (stat_disk[i].name, "disk_octets", read_sectors, write_sectors); read_ops = stat_disk[i].xrate; write_ops = stat_disk[i].xfers - stat_disk[i].xrate; disk_submit (stat_disk[i].name, "disk_ops", read_ops, write_ops); read_time = stat_disk[i].rserv; read_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0; write_time = stat_disk[i].wserv; write_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0; disk_submit (stat_disk[i].name, "disk_time", read_time, write_time); } #endif /* defined(HAVE_PERFSTAT) */ return (0); } /* int disk_read */
static int disk_read (void) { #if HAVE_IOKIT_IOKITLIB_H io_registry_entry_t disk; io_registry_entry_t disk_child; io_iterator_t disk_list; CFMutableDictionaryRef props_dict, child_dict; CFDictionaryRef stats_dict; CFStringRef tmp_cf_string_ref; kern_return_t status; signed long long read_ops, read_byt, read_tme; signed long long write_ops, write_byt, write_tme; int disk_major, disk_minor; char disk_name[DATA_MAX_NAME_LEN]; char child_disk_name_bsd[DATA_MAX_NAME_LEN], props_disk_name_bsd[DATA_MAX_NAME_LEN]; /* Get the list of all disk objects. */ if (IOServiceGetMatchingServices (io_master_port, IOServiceMatching (kIOBlockStorageDriverClass), &disk_list) != kIOReturnSuccess) { ERROR ("disk plugin: IOServiceGetMatchingServices failed."); return (-1); } while ((disk = IOIteratorNext (disk_list)) != 0) { props_dict = NULL; stats_dict = NULL; child_dict = NULL; /* get child of disk entry and corresponding property dictionary */ if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child)) != kIOReturnSuccess) { /* This fails for example for DVD/CD drives, which we want to ignore anyway */ DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status); IOObjectRelease (disk); continue; } if (IORegistryEntryCreateCFProperties (disk_child, (CFMutableDictionaryRef *) &child_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || child_dict == NULL) { ERROR ("disk plugin: IORegistryEntryCreateCFProperties (disk_child) failed."); IOObjectRelease (disk_child); IOObjectRelease (disk); continue; } /* extract name and major/minor numbers */ memset (child_disk_name_bsd, 0, sizeof (child_disk_name_bsd)); tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (child_dict, CFSTR(kIOBSDNameKey)); if (tmp_cf_string_ref) { assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ()); CFStringGetCString (tmp_cf_string_ref, child_disk_name_bsd, sizeof (child_disk_name_bsd), kCFStringEncodingUTF8); } disk_major = (int) dict_get_value (child_dict, kIOBSDMajorKey); disk_minor = (int) dict_get_value (child_dict, kIOBSDMinorKey); DEBUG ("disk plugin: child_disk_name_bsd=\"%s\" major=%d minor=%d", child_disk_name_bsd, disk_major, disk_minor); CFRelease (child_dict); IOObjectRelease (disk_child); /* get property dictionary of the disk entry itself */ if (IORegistryEntryCreateCFProperties (disk, (CFMutableDictionaryRef *) &props_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || props_dict == NULL) { ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed."); IOObjectRelease (disk); continue; } /* extract name and stats dictionary */ memset (props_disk_name_bsd, 0, sizeof (props_disk_name_bsd)); tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict, CFSTR(kIOBSDNameKey)); if (tmp_cf_string_ref) { assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ()); CFStringGetCString (tmp_cf_string_ref, props_disk_name_bsd, sizeof (props_disk_name_bsd), kCFStringEncodingUTF8); } stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict, CFSTR (kIOBlockStorageDriverStatisticsKey)); if (stats_dict == NULL) { ERROR ("disk plugin: CFDictionaryGetValue (%s) failed.", kIOBlockStorageDriverStatisticsKey); CFRelease (props_dict); IOObjectRelease (disk); continue; } DEBUG ("disk plugin: props_disk_name_bsd=\"%s\"", props_disk_name_bsd); /* choose name */ if (use_bsd_name) { if (child_disk_name_bsd[0] != 0) sstrncpy (disk_name, child_disk_name_bsd, sizeof (disk_name)); else if (props_disk_name_bsd[0] != 0) sstrncpy (disk_name, props_disk_name_bsd, sizeof (disk_name)); else { ERROR ("disk plugin: can't find bsd disk name."); ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor); } } else ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor); /* extract the stats */ read_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsReadsKey); read_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesReadKey); read_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalReadTimeKey); write_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsWritesKey); write_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesWrittenKey); write_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey); CFRelease (props_dict); IOObjectRelease (disk); /* and submit */ DEBUG ("disk plugin: disk_name = \"%s\"", disk_name); if ((read_byt != -1LL) || (write_byt != -1LL)) disk_submit (disk_name, "disk_octets", read_byt, write_byt); if ((read_ops != -1LL) || (write_ops != -1LL)) disk_submit (disk_name, "disk_ops", read_ops, write_ops); if ((read_tme != -1LL) || (write_tme != -1LL)) disk_submit (disk_name, "disk_time", read_tme / 1000, write_tme / 1000); } IOObjectRelease (disk_list); /* #endif HAVE_IOKIT_IOKITLIB_H */ #elif KERNEL_FREEBSD int retry, dirty; void *snap = NULL; struct devstat *snap_iter; struct gident *geom_id; const char *disk_name; long double read_time, write_time; for (retry = 0, dirty = 1; retry < 5 && dirty == 1; retry++) { if (snap != NULL) geom_stats_snapshot_free(snap); /* Get a fresh copy of stats snapshot */ snap = geom_stats_snapshot_get(); if (snap == NULL) { ERROR("disk plugin: geom_stats_snapshot_get() failed."); return (-1); } /* Check if we have dirty read from this snapshot */ dirty = 0; geom_stats_snapshot_reset(snap); while ((snap_iter = geom_stats_snapshot_next(snap)) != NULL) { if (snap_iter->id == NULL) continue; geom_id = geom_lookupid(&geom_tree, snap_iter->id); /* New device? refresh GEOM tree */ if (geom_id == NULL) { geom_deletetree(&geom_tree); if (geom_gettree(&geom_tree) != 0) { ERROR("disk plugin: geom_gettree() failed"); geom_stats_snapshot_free(snap); return (-1); } geom_id = geom_lookupid(&geom_tree, snap_iter->id); } /* * This should be rare: the device come right before we take the * snapshot and went away right after it. We will handle this * case later, so don't mark dirty but silently ignore it. */ if (geom_id == NULL) continue; /* Only collect PROVIDER data */ if (geom_id->lg_what != ISPROVIDER) continue; /* Only collect data when rank is 1 (physical devices) */ if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1) continue; /* Check if this is a dirty read quit for another try */ if (snap_iter->sequence0 != snap_iter->sequence1) { dirty = 1; break; } } } /* Reset iterator */ geom_stats_snapshot_reset(snap); for (;;) { snap_iter = geom_stats_snapshot_next(snap); if (snap_iter == NULL) break; if (snap_iter->id == NULL) continue; geom_id = geom_lookupid(&geom_tree, snap_iter->id); if (geom_id == NULL) continue; if (geom_id->lg_what != ISPROVIDER) continue; if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1) continue; /* Skip dirty reads, if present */ if (dirty && (snap_iter->sequence0 != snap_iter->sequence1)) continue; disk_name = ((struct gprovider *)geom_id->lg_ptr)->lg_name; if ((snap_iter->bytes[DEVSTAT_READ] != 0) || (snap_iter->bytes[DEVSTAT_WRITE] != 0)) { disk_submit(disk_name, "disk_octets", (derive_t)snap_iter->bytes[DEVSTAT_READ], (derive_t)snap_iter->bytes[DEVSTAT_WRITE]); } if ((snap_iter->operations[DEVSTAT_READ] != 0) || (snap_iter->operations[DEVSTAT_WRITE] != 0)) { disk_submit(disk_name, "disk_ops", (derive_t)snap_iter->operations[DEVSTAT_READ], (derive_t)snap_iter->operations[DEVSTAT_WRITE]); } read_time = devstat_compute_etime(&snap_iter->duration[DEVSTAT_READ], NULL); write_time = devstat_compute_etime(&snap_iter->duration[DEVSTAT_WRITE], NULL); if ((read_time != 0) || (write_time != 0)) { disk_submit (disk_name, "disk_time", (derive_t)(read_time*1000), (derive_t)(write_time*1000)); } } geom_stats_snapshot_free(snap); #elif KERNEL_LINUX FILE *fh; char buffer[1024]; char *fields[32]; int numfields; int fieldshift = 0; int minor = 0; derive_t read_sectors = 0; derive_t write_sectors = 0; derive_t read_ops = 0; derive_t read_merged = 0; derive_t read_time = 0; derive_t write_ops = 0; derive_t write_merged = 0; derive_t write_time = 0; gauge_t in_progress = NAN; derive_t io_time = 0; derive_t weighted_time = 0; int is_disk = 0; diskstats_t *ds, *pre_ds; if ((fh = fopen ("/proc/diskstats", "r")) == NULL) { fh = fopen ("/proc/partitions", "r"); if (fh == NULL) { ERROR ("disk plugin: fopen (/proc/{diskstats,partitions}) failed."); return (-1); } /* Kernel is 2.4.* */ fieldshift = 1; } #if HAVE_LIBUDEV handle_udev = udev_new(); #endif while (fgets (buffer, sizeof (buffer), fh) != NULL) { char *disk_name; char *output_name; numfields = strsplit (buffer, fields, 32); if ((numfields != (14 + fieldshift)) && (numfields != 7)) continue; minor = atoll (fields[1]); disk_name = fields[2 + fieldshift]; for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next) if (strcmp (disk_name, ds->name) == 0) break; if (ds == NULL) { if ((ds = (diskstats_t *) calloc (1, sizeof (diskstats_t))) == NULL) continue; if ((ds->name = strdup (disk_name)) == NULL) { free (ds); continue; } if (pre_ds == NULL) disklist = ds; else pre_ds->next = ds; } is_disk = 0; if (numfields == 7) { /* Kernel 2.6, Partition */ read_ops = atoll (fields[3]); read_sectors = atoll (fields[4]); write_ops = atoll (fields[5]); write_sectors = atoll (fields[6]); } else if (numfields == (14 + fieldshift)) { read_ops = atoll (fields[3 + fieldshift]); write_ops = atoll (fields[7 + fieldshift]); read_sectors = atoll (fields[5 + fieldshift]); write_sectors = atoll (fields[9 + fieldshift]); if ((fieldshift == 0) || (minor == 0)) { is_disk = 1; read_merged = atoll (fields[4 + fieldshift]); read_time = atoll (fields[6 + fieldshift]); write_merged = atoll (fields[8 + fieldshift]); write_time = atoll (fields[10+ fieldshift]); in_progress = atof (fields[11 + fieldshift]); io_time = atof (fields[12 + fieldshift]); weighted_time = atof (fields[13 + fieldshift]); } } else { DEBUG ("numfields = %i; => unknown file format.", numfields); continue; } { derive_t diff_read_sectors; derive_t diff_write_sectors; /* If the counter wraps around, it's only 32 bits.. */ if (read_sectors < ds->read_sectors) diff_read_sectors = 1 + read_sectors + (UINT_MAX - ds->read_sectors); else diff_read_sectors = read_sectors - ds->read_sectors; if (write_sectors < ds->write_sectors) diff_write_sectors = 1 + write_sectors + (UINT_MAX - ds->write_sectors); else diff_write_sectors = write_sectors - ds->write_sectors; ds->read_bytes += 512 * diff_read_sectors; ds->write_bytes += 512 * diff_write_sectors; ds->read_sectors = read_sectors; ds->write_sectors = write_sectors; } /* Calculate the average time an io-op needs to complete */ if (is_disk) { derive_t diff_read_ops; derive_t diff_write_ops; derive_t diff_read_time; derive_t diff_write_time; if (read_ops < ds->read_ops) diff_read_ops = 1 + read_ops + (UINT_MAX - ds->read_ops); else diff_read_ops = read_ops - ds->read_ops; DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; " "ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";", disk_name, read_ops, ds->read_ops, diff_read_ops); if (write_ops < ds->write_ops) diff_write_ops = 1 + write_ops + (UINT_MAX - ds->write_ops); else diff_write_ops = write_ops - ds->write_ops; if (read_time < ds->read_time) diff_read_time = 1 + read_time + (UINT_MAX - ds->read_time); else diff_read_time = read_time - ds->read_time; if (write_time < ds->write_time) diff_write_time = 1 + write_time + (UINT_MAX - ds->write_time); else diff_write_time = write_time - ds->write_time; if (diff_read_ops != 0) ds->avg_read_time += disk_calc_time_incr ( diff_read_time, diff_read_ops); if (diff_write_ops != 0) ds->avg_write_time += disk_calc_time_incr ( diff_write_time, diff_write_ops); ds->read_ops = read_ops; ds->read_time = read_time; ds->write_ops = write_ops; ds->write_time = write_time; } /* if (is_disk) */ /* Don't write to the RRDs if we've just started.. */ ds->poll_count++; if (ds->poll_count <= 2) { DEBUG ("disk plugin: (ds->poll_count = %i) <= " "(min_poll_count = 2); => Not writing.", ds->poll_count); continue; } if ((read_ops == 0) && (write_ops == 0)) { DEBUG ("disk plugin: ((read_ops == 0) && " "(write_ops == 0)); => Not writing."); continue; } output_name = disk_name; #if HAVE_LIBUDEV char *alt_name = disk_udev_attr_name (handle_udev, disk_name, conf_udev_name_attr); if (alt_name != NULL) output_name = alt_name; #endif if ((ds->read_bytes != 0) || (ds->write_bytes != 0)) disk_submit (output_name, "disk_octets", ds->read_bytes, ds->write_bytes); if ((ds->read_ops != 0) || (ds->write_ops != 0)) disk_submit (output_name, "disk_ops", read_ops, write_ops); if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0)) disk_submit (output_name, "disk_time", ds->avg_read_time, ds->avg_write_time); if (is_disk) { disk_submit (output_name, "disk_merged", read_merged, write_merged); submit_in_progress (output_name, in_progress); submit_io_time (output_name, io_time, weighted_time); } /* if (is_disk) */ #if HAVE_LIBUDEV /* release udev-based alternate name, if allocated */ sfree (alt_name); #endif } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */ #if HAVE_LIBUDEV udev_unref(handle_udev); #endif fclose (fh); /* #endif defined(KERNEL_LINUX) */ #elif HAVE_LIBKSTAT # if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME # define KIO_ROCTETS reads # define KIO_WOCTETS writes # define KIO_ROPS nreads # define KIO_WOPS nwrites # define KIO_RTIME rtime # define KIO_WTIME wtime # elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME # define KIO_ROCTETS nread # define KIO_WOCTETS nwritten # define KIO_ROPS reads # define KIO_WOPS writes # define KIO_RTIME rtime # define KIO_WTIME wtime # else # error "kstat_io_t does not have the required members" # endif static kstat_io_t kio; int i; if (kc == NULL) return (-1); for (i = 0; i < numdisk; i++) { if (kstat_read (kc, ksp[i], &kio) == -1) continue; if (strncmp (ksp[i]->ks_class, "disk", 4) == 0) { disk_submit (ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS, kio.KIO_WOCTETS); disk_submit (ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS); /* FIXME: Convert this to microseconds if necessary */ disk_submit (ksp[i]->ks_name, "disk_time", kio.KIO_RTIME, kio.KIO_WTIME); } else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0) { disk_submit (ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS, kio.KIO_WOCTETS); disk_submit (ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS); } } /* #endif defined(HAVE_LIBKSTAT) */ #elif defined(HAVE_LIBSTATGRAB) sg_disk_io_stats *ds; # if HAVE_LIBSTATGRAB_0_90 size_t disks; # else int disks; #endif int counter; char name[DATA_MAX_NAME_LEN]; if ((ds = sg_get_disk_io_stats(&disks)) == NULL) return (0); for (counter=0; counter < disks; counter++) { strncpy(name, ds->disk_name, sizeof(name)); name[sizeof(name)-1] = '\0'; /* strncpy doesn't terminate longer strings */ disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes); ds++; } /* #endif defined(HAVE_LIBSTATGRAB) */ #elif defined(HAVE_PERFSTAT) derive_t read_sectors; derive_t write_sectors; derive_t read_time; derive_t write_time; derive_t read_ops; derive_t write_ops; perfstat_id_t firstpath; int rnumdisk; int i; if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) { char errbuf[1024]; WARNING ("disk plugin: perfstat_disk: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } if (numdisk != pnumdisk || stat_disk==NULL) { if (stat_disk!=NULL) free(stat_disk); stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t)); } pnumdisk = numdisk; firstpath.name[0]='\0'; if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t), numdisk)) < 0) { char errbuf[1024]; WARNING ("disk plugin: perfstat_disk : %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } for (i = 0; i < rnumdisk; i++) { read_sectors = stat_disk[i].rblks*stat_disk[i].bsize; write_sectors = stat_disk[i].wblks*stat_disk[i].bsize; disk_submit (stat_disk[i].name, "disk_octets", read_sectors, write_sectors); read_ops = stat_disk[i].xrate; write_ops = stat_disk[i].xfers - stat_disk[i].xrate; disk_submit (stat_disk[i].name, "disk_ops", read_ops, write_ops); read_time = stat_disk[i].rserv; read_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0; write_time = stat_disk[i].wserv; write_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0; disk_submit (stat_disk[i].name, "disk_time", read_time, write_time); } #endif /* defined(HAVE_PERFSTAT) */ return (0); } /* int disk_read */