Пример #1
0
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 = &mp; 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(&not_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(&not_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);
}
Пример #2
0
int
main(int argc, char **argv)
{
    int c, i;
    int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
    int count = 0, waittime = 0;
    char *memf = NULL, *nlistf = NULL;
    struct devstat_match *matches;
    struct itimerval alarmspec;
    int num_matches = 0;
    char errbuf[_POSIX2_LINE_MAX];
    kvm_t *kd = NULL;
    long generation;
    int num_devices_specified;
    int num_selected, num_selections;
    long select_generation;
    char **specified_devices;
    devstat_select_mode select_mode;
    float f;
    int havelast = 0;

    matches = NULL;
    maxshowdevs = 3;

    while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:xz?")) != -1) {
        switch(c) {
        case 'c':
            cflag++;
            count = atoi(optarg);
            if (count < 1)
                errx(1, "count %d is < 1", count);
            break;
        case 'C':
            Cflag++;
            break;
        case 'd':
            dflag++;
            break;
        case 'h':
            hflag++;
            break;
        case 'I':
            Iflag++;
            break;
        case 'K':
            Kflag++;
            break;
        case 'M':
            memf = optarg;
            break;
        case 'n':
            nflag++;
            maxshowdevs = atoi(optarg);
            if (maxshowdevs < 0)
                errx(1, "number of devices %d is < 0",
                     maxshowdevs);
            break;
        case 'N':
            nlistf = optarg;
            break;
        case 'o':
            oflag++;
            break;
        case 't':
            tflag++;
            if (devstat_buildmatch(optarg, &matches,
                                   &num_matches) != 0)
                errx(1, "%s", devstat_errbuf);
            break;
        case 'T':
            Tflag++;
            break;
        case 'w':
            wflag++;
            f = atof(optarg);
            waittime = f * 1000;
            if (waittime < 1)
                errx(1, "wait time is < 1ms");
            break;
        case 'x':
            xflag++;
            break;
        case 'z':
            zflag++;
            break;
        default:
            usage();
            exit(1);
            break;
        }
    }

    argc -= optind;
    argv += optind;

    if (nlistf != NULL || memf != NULL) {
        kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);

        if (kd == NULL)
            errx(1, "kvm_openfiles: %s", errbuf);

        if (kvm_nlist(kd, namelist) == -1)
            errx(1, "kvm_nlist: %s", kvm_geterr(kd));
    }

    /*
     * Make sure that the userland devstat version matches the kernel
     * devstat version.  If not, exit and print a message informing
     * the user of his mistake.
     */
    if (devstat_checkversion(kd) < 0)
        errx(1, "%s", devstat_errbuf);

    /*
     * Make sure Tflag and/or Cflag are set if dflag == 0.  If dflag is
     * greater than 0, they may be 0 or non-zero.
     */
    if (dflag == 0 && xflag == 0) {
        Cflag = 1;
        Tflag = 1;
    }

    /* find out how many devices we have */
    if ((num_devices = devstat_getnumdevs(kd)) < 0)
        err(1, "can't get number of devices");

    /*
     * Figure out how many devices we should display.
     */
    if (nflag == 0) {
        if (xflag > 0)
            maxshowdevs = num_devices;
        else if (oflag > 0) {
            if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
                maxshowdevs = 5;
            else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
                maxshowdevs = 5;
            else
                maxshowdevs = 4;
        } else {
            if ((dflag > 0) && (Cflag == 0))
                maxshowdevs = 4;
            else
                maxshowdevs = 3;
        }
    }

    cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
    if (cur.dinfo == NULL)
        err(1, "calloc failed");

    last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
    if (last.dinfo == NULL)
        err(1, "calloc failed");

    /*
     * Grab all the devices.  We don't look to see if the list has
     * changed here, since it almost certainly has.  We only look for
     * errors.
     */
    if (devstat_getdevs(kd, &cur) == -1)
        errx(1, "%s", devstat_errbuf);

    num_devices = cur.dinfo->numdevs;
    generation = cur.dinfo->generation;

    /*
     * If the user specified any devices on the command line, see if
     * they are in the list of devices we have now.
     */
    specified_devices = (char **)malloc(sizeof(char *));
    if (specified_devices == NULL)
        err(1, "malloc failed");

    for (num_devices_specified = 0; *argv; ++argv) {
        if (isdigit(**argv))
            break;
        num_devices_specified++;
        specified_devices = (char **)realloc(specified_devices,
                                             sizeof(char *) *
                                             num_devices_specified);
        if (specified_devices == NULL)
            err(1, "realloc failed");

        specified_devices[num_devices_specified - 1] = *argv;

    }
    if (nflag == 0 && maxshowdevs < num_devices_specified)
        maxshowdevs = num_devices_specified;

    dev_select = NULL;

    if ((num_devices_specified == 0) && (num_matches == 0))
        select_mode = DS_SELECT_ADD;
    else
        select_mode = DS_SELECT_ONLY;

    /*
     * At this point, selectdevs will almost surely indicate that the
     * device list has changed, so we don't look for return values of 0
     * or 1.  If we get back -1, though, there is an error.
     */
    if (devstat_selectdevs(&dev_select, &num_selected,
                           &num_selections, &select_generation, generation,
                           cur.dinfo->devices, num_devices, matches,
                           num_matches, specified_devices,
                           num_devices_specified, select_mode, maxshowdevs,
                           hflag) == -1)
        errx(1, "%s", devstat_errbuf);

    /*
     * Look for the traditional wait time and count arguments.
     */
    if (*argv) {
        f = atof(*argv);
        waittime = f * 1000;

        /* Let the user know he goofed, but keep going anyway */
        if (wflag != 0)
            warnx("discarding previous wait interval, using"
                  " %g instead", waittime / 1000.0);
        wflag++;

        if (*++argv) {
            count = atoi(*argv);
            if (cflag != 0)
                warnx("discarding previous count, using %d"
                      " instead", count);
            cflag++;
        } else
            count = -1;
    }

    /*
     * If the user specified a count, but not an interval, we default
     * to an interval of 1 second.
     */
    if ((wflag == 0) && (cflag > 0))
        waittime = 1 * 1000;

    /*
     * If the user specified a wait time, but not a count, we want to
     * go on ad infinitum.  This can be redundant if the user uses the
     * traditional method of specifying the wait, since in that case we
     * already set count = -1 above.  Oh well.
     */
    if ((wflag > 0) && (cflag == 0))
        count = -1;

    bzero(cur.cp_time, sizeof(cur.cp_time));
    cur.tk_nout = 0;
    cur.tk_nin = 0;

    /*
     * Set the snap time to the system boot time (ie: zero), so the
     * stats are calculated since system boot.
     */
    cur.snap_time = 0;

    /*
     * If the user stops the program (control-Z) and then resumes it,
     * print out the header again.
     */
    (void)signal(SIGCONT, needhdr);

    /*
     * If our standard output is a tty, then install a SIGWINCH handler
     * and set wresized so that our first iteration through the main
     * iostat loop will peek at the terminal's current rows to find out
     * how many lines can fit in a screenful of output.
     */
    if (isatty(fileno(stdout)) != 0) {
        wresized = 1;
        (void)signal(SIGWINCH, needresize);
    } else {
        wresized = 0;
        wrows = IOSTAT_DEFAULT_ROWS;
    }

    /*
     * Register a SIGINT handler so that we can print out final statistics
     * when we get that signal
     */
    (void)signal(SIGINT, needreturn);

    /*
     * Register a SIGALRM handler to implement sleeps if the user uses the
     * -c or -w options
     */
    (void)signal(SIGALRM, alarm_clock);
    alarmspec.it_interval.tv_sec = waittime / 1000;
    alarmspec.it_interval.tv_usec = 1000 * (waittime % 1000);
    alarmspec.it_value.tv_sec = waittime / 1000;
    alarmspec.it_value.tv_usec = 1000 * (waittime % 1000);
    setitimer(ITIMER_REAL, &alarmspec, NULL);

    for (headercount = 1;;) {
        struct devinfo *tmp_dinfo;
        long tmp;
        long double etime;
        sigset_t sigmask, oldsigmask;

        if (Tflag > 0) {
            if ((readvar(kd, "kern.tty_nin", X_TTY_NIN, &cur.tk_nin,
                         sizeof(cur.tk_nin)) != 0)
                    || (readvar(kd, "kern.tty_nout", X_TTY_NOUT,
                                &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
                Tflag = 0;
                warnx("disabling TTY statistics");
            }
        }

        if (Cflag > 0) {
            if (kd == NULL) {
                if (readvar(kd, "kern.cp_time", 0,
                            &cur.cp_time, sizeof(cur.cp_time)) != 0)
                    Cflag = 0;
            } else {
                if (kvm_getcptime(kd, cur.cp_time) < 0) {
                    warnx("kvm_getcptime: %s",
                          kvm_geterr(kd));
                    Cflag = 0;
                }
            }
            if (Cflag == 0)
                warnx("disabling CPU time statistics");
        }

        if (!--headercount) {
            phdr();
            if (wresized != 0)
                doresize();
            headercount = wrows;
        }

        tmp_dinfo = last.dinfo;
        last.dinfo = cur.dinfo;
        cur.dinfo = tmp_dinfo;

        last.snap_time = cur.snap_time;

        /*
         * Here what we want to do is refresh our device stats.
         * devstat_getdevs() returns 1 when the device list has changed.
         * If the device list has changed, we want to go through
         * the selection process again, in case a device that we
         * were previously displaying has gone away.
         */
        switch (devstat_getdevs(kd, &cur)) {
        case -1:
            errx(1, "%s", devstat_errbuf);
            break;
        case 1: {
            int retval;

            num_devices = cur.dinfo->numdevs;
            generation = cur.dinfo->generation;
            retval = devstat_selectdevs(&dev_select, &num_selected,
                                        &num_selections,
                                        &select_generation,
                                        generation,
                                        cur.dinfo->devices,
                                        num_devices, matches,
                                        num_matches,
                                        specified_devices,
                                        num_devices_specified,
                                        select_mode, maxshowdevs,
                                        hflag);
            switch(retval) {
            case -1:
                errx(1, "%s", devstat_errbuf);
                break;
            case 1:
                phdr();
                if (wresized != 0)
                    doresize();
                headercount = wrows;
                break;
            default:
                break;
            }
            break;
        }
        default:
            break;
        }

        /*
         * We only want to re-select devices if we're in 'top'
         * mode.  This is the only mode where the devices selected
         * could actually change.
         */
        if (hflag > 0) {
            int retval;
            retval = devstat_selectdevs(&dev_select, &num_selected,
                                        &num_selections,
                                        &select_generation,
                                        generation,
                                        cur.dinfo->devices,
                                        num_devices, matches,
                                        num_matches,
                                        specified_devices,
                                        num_devices_specified,
                                        select_mode, maxshowdevs,
                                        hflag);
            switch(retval) {
            case -1:
                errx(1,"%s", devstat_errbuf);
                break;
            case 1:
                phdr();
                if (wresized != 0)
                    doresize();
                headercount = wrows;
                break;
            default:
                break;
            }
        }

        if (Tflag > 0) {
            tmp = cur.tk_nin;
            cur.tk_nin -= last.tk_nin;
            last.tk_nin = tmp;
            tmp = cur.tk_nout;
            cur.tk_nout -= last.tk_nout;
            last.tk_nout = tmp;
        }

        etime = cur.snap_time - last.snap_time;

        if (etime == 0.0)
            etime = 1.0;

        for (i = 0; i < CPUSTATES; i++) {
            tmp = cur.cp_time[i];
            cur.cp_time[i] -= last.cp_time[i];
            last.cp_time[i] = tmp;
        }

        if (xflag == 0 && Tflag > 0)
            printf("%4.0Lf %5.0Lf", cur.tk_nin / etime,
                   cur.tk_nout / etime);

        devstats(hflag, etime, havelast);

        if (xflag == 0) {
            if (Cflag > 0)
                cpustats();

            printf("\n");
        }
        fflush(stdout);

        if ((count >= 0 && --count <= 0) || return_requested)
            break;

        /*
         * Use sigsuspend to safely sleep until either signal is
         * received
         */
        alarm_rang = 0;
        sigemptyset(&sigmask);
        sigaddset(&sigmask, SIGINT);
        sigaddset(&sigmask, SIGALRM);
        sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask);
        while (! (alarm_rang || return_requested) ) {
            sigsuspend(&oldsigmask);
        }
        sigprocmask(SIG_UNBLOCK, &sigmask, NULL);

        havelast = 1;
    }

    exit(0);
}
Пример #3
0
static void
dovmstat(unsigned int interval, int reps)
{
	struct vmtotal total;
	time_t uptime, halfuptime;
	struct devinfo *tmp_dinfo;
	size_t size;
	int ncpus, maxid;
	u_long cpumask;
	int rate_adj;

	uptime = getuptime() / 1000000000LL;
	halfuptime = uptime / 2;
	rate_adj = 1;
	ncpus = 1;
	maxid = 0;

	/*
	 * If the user stops the program (control-Z) and then resumes it,
	 * print out the header again.
	 */
	(void)signal(SIGCONT, needhdr);

	/*
	 * If our standard output is a tty, then install a SIGWINCH handler
	 * and set wresized so that our first iteration through the main
	 * vmstat loop will peek at the terminal's current rows to find out
	 * how many lines can fit in a screenful of output.
	 */
	if (isatty(fileno(stdout)) != 0) {
		wresized = 1;
		(void)signal(SIGWINCH, needresize);
	} else {
		wresized = 0;
		winlines = VMSTAT_DEFAULT_LINES;
	}

	if (kd != NULL) {
		if (namelist[X_STATHZ].n_type != 0 &&
		    namelist[X_STATHZ].n_value != 0)
			kread(X_STATHZ, &hz, sizeof(hz));
		if (!hz)
			kread(X_HZ, &hz, sizeof(hz));
	} else {
		struct clockinfo clockrate;

		size = sizeof(clockrate);
		mysysctl("kern.clockrate", &clockrate, &size, NULL, 0);
		if (size != sizeof(clockrate))
			errx(1, "clockrate size mismatch");
		hz = clockrate.hz;
	}

	if (Pflag) {
		ncpus = getcpuinfo(&cpumask, &maxid);
		size_cp_times = sizeof(long) * (maxid + 1) * CPUSTATES;
		cur_cp_times = calloc(1, size_cp_times);
		last_cp_times = calloc(1, size_cp_times);
	}
	for (hdrcnt = 1;;) {
		if (!--hdrcnt)
			printhdr(maxid, cpumask);
		if (kd != NULL) {
			if (kvm_getcptime(kd, cur.cp_time) < 0)
				errx(1, "kvm_getcptime: %s", kvm_geterr(kd));
		} else {
			size = sizeof(cur.cp_time);
			mysysctl("kern.cp_time", &cur.cp_time, &size, NULL, 0);
			if (size != sizeof(cur.cp_time))
				errx(1, "cp_time size mismatch");
		}
		if (Pflag) {
			size = size_cp_times;
			mysysctl("kern.cp_times", cur_cp_times, &size, NULL, 0);
			if (size != size_cp_times)
				errx(1, "cp_times mismatch");
		}

		tmp_dinfo = last.dinfo;
		last.dinfo = cur.dinfo;
		cur.dinfo = tmp_dinfo;
		last.snap_time = cur.snap_time;

		/*
		 * Here what we want to do is refresh our device stats.
		 * getdevs() returns 1 when the device list has changed.
		 * If the device list has changed, we want to go through
		 * the selection process again, in case a device that we
		 * were previously displaying has gone away.
		 */
		switch (devstat_getdevs(NULL, &cur)) {
		case -1:
			errx(1, "%s", devstat_errbuf);
			break;
		case 1: {
			int retval;

			num_devices = cur.dinfo->numdevs;
			generation = cur.dinfo->generation;

			retval = devstat_selectdevs(&dev_select, &num_selected,
					    &num_selections, &select_generation,
					    generation, cur.dinfo->devices,
					    num_devices, matches, num_matches,
					    specified_devices,
					    num_devices_specified, select_mode,
					    maxshowdevs, 0);
			switch (retval) {
			case -1:
				errx(1, "%s", devstat_errbuf);
				break;
			case 1:
				printhdr(maxid, cpumask);
				break;
			default:
				break;
			}
		}
		default:
			break;
		}

		fill_vmmeter(&sum);
		fill_vmtotal(&total);
		(void)printf("%1d %1d %1d",
		    total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
#define vmstat_pgtok(a) ((a) * (sum.v_page_size >> 10))
#define	rate(x)	(((x) * rate_adj + halfuptime) / uptime)	/* round */
		if (hflag) {
			printf("");
			prthuman(total.t_avm * (u_int64_t)sum.v_page_size, 5);
			printf(" ");
			prthuman(total.t_free * (u_int64_t)sum.v_page_size, 5);
			printf(" ");
			(void)printf("%5lu ",
			    (unsigned long)rate(sum.v_vm_faults -
			    osum.v_vm_faults));
		} else {
			printf(" %7d", vmstat_pgtok(total.t_avm));
			printf(" %7d ", vmstat_pgtok(total.t_free));
			(void)printf("%4lu ",
			    (unsigned long)rate(sum.v_vm_faults -
			    osum.v_vm_faults));
		}
		(void)printf("%3lu ",
		    (unsigned long)rate(sum.v_reactivated - osum.v_reactivated));
		(void)printf("%3lu ",
		    (unsigned long)rate(sum.v_swapin + sum.v_vnodein -
		    (osum.v_swapin + osum.v_vnodein)));
		(void)printf("%3lu ",
		    (unsigned long)rate(sum.v_swapout + sum.v_vnodeout -
		    (osum.v_swapout + osum.v_vnodeout)));
		(void)printf("%5lu ",
		    (unsigned long)rate(sum.v_tfree - osum.v_tfree));
		(void)printf("%4lu ",
		    (unsigned long)rate(sum.v_pdpages - osum.v_pdpages));
		devstats();
		(void)printf("%4lu %5lu %5lu",
		    (unsigned long)rate(sum.v_intr - osum.v_intr),
		    (unsigned long)rate(sum.v_syscall - osum.v_syscall),
		    (unsigned long)rate(sum.v_swtch - osum.v_swtch));
		if (Pflag)
			pcpustats(ncpus, cpumask, maxid);
		else
			cpustats();
		(void)printf("\n");
		(void)fflush(stdout);
		if (reps >= 0 && --reps <= 0)
			break;
		osum = sum;
		uptime = interval;
		rate_adj = 1000;
		/*
		 * We round upward to avoid losing low-frequency events
		 * (i.e., >= 1 per interval but < 1 per millisecond).
		 */
		if (interval != 1)
			halfuptime = (uptime + 1) / 2;
		else
			halfuptime = 0;
		(void)usleep(interval * 1000);
	}
}