sadp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1994 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/* sadp.c 1.14.1.12 of 8/9/89 */
/* sadp.c - For VAX and PDP11 machines,
disk profiler profiles rp06, rm05 and general disk drives.
It reads system buffer header pool, physical buffer header
pool and swap buffer header pool once every second,
to examine disk drive's I/O queue.
For 3b20s system, it profiles the regular disk drives,
it reads the circular output queue for each drive
once every second.
usage : sadp [-th][-d device[-drive]] s [n]
*/
#include <stdio.h>
#include <sys/sysmacros.h>
#include <nlist.h>
#include <string.h>
#include <fcntl.h>
#ifdef FIXME
#endif
#include <time.h>
#include <ctype.h>
#include <kvm.h>
/*
* These includes are for dealing with scsi targets.
*/
#include <sys/dditypes.h>
/* cylinder profiling */
#define BLANK ' '
#define BLOB '*'
#define TRACE '.'
#define BRK '='
#define FOOT '-'
#define CYLNO 1
#define SEEKD 2
#define CHUNK 16
#define CHUNKSHIFT 4
* the number of CHUNK cylinder
* chunks on a disk
*/
#define ERR_BAD_DEV "Device %s is not defined, valid devices are: "
#define ERR_BAD_UNIT \
"Invalid drive specified for device %s, valid drives are: "
#define ERR_NO_DEV "Please specify a device type, valid devices are: "
#define NDRIVE 10
#define MAX_HDISK_REP NDRIVE
#define X1_V 0
{"v"},
#define X1_BUF 1
{"buf"},
#define X1_PBUF 2
{"pbuf"},
#define X1_SDUNITS 3
{"sdunits"},
#ifdef FIXME
#define X1_DK_NDRIVE 4
{"dk_ndrive"},
#define X1_DK_BUSY 5
{"dk_busy"},
#define X1_DK_TIME 6
{"dk_time"},
#define X1_DK_SEEK 7
{"dk_seek"},
#define X1_DK_XFER 8
{"dk_xfer"},
#define X1_DK_WDS 9
{"dk_wds"},
#define X1_DK_BPS 10
{"dk_bps"},
#define X1_DK_READ 11
{"dk_read"},
#define X1_DK_IVEC 12
{"dk_ivec"},
#define X1_NUMSYMBOLS 13
#else
#define X1_NUMSYMBOLS 4
#endif
{0}
};
void do_disk_stats ();
void usage ();
void prthist ();
void pline ();
void cylhdr ();
void cylftr ();
void cylhist ();
void validate_device ();
void validate_drive ();
void init_geom ();
void bad_device ();
void read_devinfo_names ();
void fail ();
void init_disk ();
void safe_kvm_read ();
int debug = 1;
/*
* FETCH_XYZ naming convention:
* X = I for nlist index, A for actual address
* Y = V for regular variable, A for array variable
* Z = L if length explicitly specified
*/
int dk_ndrive;
int ndrives;
int all = 0; /*
* indicate whether all drives
* are implicitly specified
*/
char *cmdname = "sadp";
struct {
} dk;
int nonblk;
int index;
int index1;
unsigned temp1;
int s, n, ct;
static int ub = 8;
int sdist;
int m;
int dev;
int temp;
int f;
int i;
int dashf;
int dn;
struct HISTDATA {
long hdata[1];
};
char *nopt;
char empty[30];
char drive[30];
char *malloc();
int SCSI; /* SCSI */
int ALL;
long lseek();
long **dkcyl;
long **skcyl;
long *iocnt;
int cyl_no, prev_cyl_no;
int cyl_bk, prev_cyl_bk;
int max_cyl_no = 0;
int max_seek_dist = 0;
int argc;
char **argv;
{
unsigned sleep();
extern int optind;
extern char *optarg;
int c, j;
long curt;
long *skdist;
long *disk;
fail("sadp does not work yet -- no disk statistics in the kernel", 0);
switch (c) {
case 't':
tflg++;
break;
case 'h':
hflg++;
break;
case 'd':
/*
* Controller types can be arbitrary length.
*/
SCSI = 0;
errflg++;
break;
}
if (optarg[i] == ','){
if (n1 == i){
errflg++;
break;
}
if (getdrvn() != 0) {
errflg++;
break;
}
if (dashf != 0) {
errflg++;
break;
}
if (SCSI) /* SCSI */
Sdrvlist[j] = 1;
else
drvlist[j] = 1;
}
dashb = 0;
dashf = 0;
}
else
{
if (SCSI)
else
}
n1 = i+1;
} else {
if (optarg[i] == '-'){
if (dashf != 0) {
errflg++;
break;
}
if (getdrvn() != 0) {
errflg++;
break;
}
if (SCSI)
else
dashf = 1;
n1 = i+1;
} else {
if (i == dleng-1){
i++;
if (getdrvn() != 0) {
errflg++;
break;
}
if (dashf != 0)
if (SCSI)
Sdrvlist[j] = 1;
else
drvlist[j] = 1;
}
else
{
if (SCSI)
else
}
}
}
}
}
} else {
errflg++;
break;
}
all++;
if (SCSI)
ALL++;
else
for (i = 0; i < MAX_HDISK_REP; i++)
drvlist[i] = 1;
}
if (errflg)
break;
dflg++;
break;
case '?':
errflg++;
break;
}
if (errflg) {
usage();
}
/*
* If no frequency arguments present, exit.
*/
usage();
/*
* If a non-dash field is presented as an argument,
* check if it is a numerical arg.
*/
usage();
/*
* For frequency arguments, if only s is presented , set n to 1
*/
n = 1;
}
/*
* If both s and n are specified, check if
* arg n is numeric.
*/
usage();
}
if (s <= 0)
fail("bad value of s", 0);
if (n <= 0)
fail("bad value of n", 0);
ct = s;
/*
*/
== NULL)
/*
* Search name list to get offsets.
*/
}
/*
* Initialize buffers and get disk info.
*/
init_disk();
/*
* Make sure device and drive specified is legitimate.
*/
/*
* Get storage from memory for sysbuf pool and physical buf pool
*/
/*
* Determine the number of CHUNK cylinder chunks on the disk.
* This will be referenced as CHPERCYL.
*/
init_geom();
#ifdef FIXME
#endif
/*
* Get the list of scsi device pointers from kernel space.
*/
for (i = 0; i < SD_MAXUNIT; i++) {
}
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
/*
* Make sure that there is a scsi_device struct for
* the chosen device.
*/
if (!sdunits[k]) {
cmdname);
}
/*
* Read the scsi_device struct for the device.
*/
sizeof (struct scsi_device), "sdunits");
}
/*
* Get current I/O count for each drive.
*/
for (;;) {
s = ct;
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
for (j = 0; j < CHPERCYL; j++) {
dkcyl[i][j] = 0;
skcyl[i][j] = 0;
}
iocnt[i] = 0;
disk[i] = 0;
skdist[i] = 0;
i++;
}
/*
* If no drives are selected or illegal drive number
* is specified, exit.
*/
if (i == 0)
usage();
/*
* Get i/o count for each disk.
*/
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
i++;
}
for (;;) {
/*
* Take a snapshot of buffer header pool, swap
* buffer pool and physical buffer header.
*/
/*
* read system buffer header pool.
*/
/*
* Read physical buffer header pool.
*/
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
do_disk_stats (i, k);
i++;
}
/* TBD - get more samples */
if (--s)
sleep(1);
else {
/*
* At the end of sampling, get the present I/O
* count, and system name.
*/
/*
* Print the report, there are two parts:
* cylinder profile, seeking distance profile.
*/
printf("%s %s %s %s %s\n",
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
for (j = 0; j < CHPERCYL; j++) {
}
i++;
}
tflg = 1;
if (tflg){
printf("\nCYLINDER ACCESS PROFILE\n");
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
if (disk[i] != 0){
printf("\n%s-%d:\n",
device, k);
printf("Cylinders\tTransfers\n");
for (j = 0; j < CHPERCYL; j++) {
if (dkcyl[i][j] > 0)
printf("%3d - %3d\t%ld\n",
}
printf("\nSampled I/O = %ld, Actual I/O = %ld\n",
if (iocnt[i] > 0)
printf("Percentage of I/O sampled = %2.2f\n",
}
i++;
}
printf("\n\n\nSEEK DISTANCE PROFILE\n");
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
if (skdist[i] != 0){
printf("\n%s-%d:\n",
device, k);
printf("Seek Distance\tSeeks\n");
for (j = 0; j < CHPERCYL; j++)
if (skcyl[i][j] > 0){
if (j == 0)
printf(" 0\t%ld\n",
skcyl[i][j]);
else
printf("%3d - %3d\t%ld\n",
}
}
i++;
}
}
if (hflg){
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
if (disk[i] != 0) {
}
i++;
}
for (k = 0, i = 0; k < ub; k++) {
if (drvlist[k] == 0)
continue;
if (skdist[i] != 0){
}
i++;
}
}
break;
}
}
if (--n)
continue;
exit(0);
}
}
void
do_disk_stats (i, k)
{
#ifdef fixed
/*
* In each scsi_device struct there is a sd_private
* pointer to a specialised scsi_disk struct which
* describes the disk.
*/
do {
sizeof (struct scsi_disk), "sdunit");
/*
* The diskhd struct describing the active and waiting
* queues is embedded in the scsi_disk struct.
*/
/*
* The current SunOS sd.c driver uses the b_forw
* pointer for the currently active buffer, and the
* b_actf (av_forw) pointer for the waiting queue
* of buffers.
*/
/*
* Trace disk queue for I/O location, seek distance.
*/
continue;
} else {
}
"b_forw");
if (seek_dist < 0)
if (seek_bk < 0)
if (cyl_no > max_cyl_no) {
max_cyl_no = cyl_no;
}
if (seek_dist > max_seek_dist) {
}
}
#endif
}
/*
* Determine if the I/O is from system buffer pool,
* or swap buffer pool or physical buffer.
*/
int
testbuf()
{
(sizeof (struct buf));
nonblk = 1;
return (0);
}
/* TBD - Is it possible to access swap buffers on Sun? */
#ifndef sun
(sizeof (struct buf));
m = index;
nonblk = 2;
return (0);
}
#endif
return (-1);
}
return (0);
}
/*
* Verify the I/O, get the cylinder number.
*/
ckbits(x)
register struct buf *x;
{
register p;
for (p = 0; p < index; p++, x++)
continue;
return (0);
}
else
return (-1);
}
int
testdev()
{
goto endtest;
else {
goto endtest;
else {
if ((nonblk == 2) &&
} else {
return (-1);
}
}
}
return (0);
}
/*
* Get drive number routine.
*/
getdrvn()
{
extern char *optarg;
char *strcpy();
char *strncat();
return (-1);
if (SCSI) {
return (-1);
} else {
return (-1);
}
return (0);
}
void
usage()
{
exit(1);
}
char *ss;
{
kk = 0;
return (-1);
kk++;
}
return (0);
}
/*
* The following routines are obtained from iostat.
*
* Output Cylinder Histogram.
*/
void
long at;
{
register ii;
int maxrow;
long data;
long scale;
}
max = 0;
}
}
maxrow++;
/* determine scaling */
scale = 1;
if (max2) {
if (scale > 48)
scale = 48;
}
else
}
}
/*
* Print Histogram.
*/
void
register mrow;
{
long line;
line = 50;
/* handle overflow in scaling */
if (gmax > 51) {
line = 52;
} else if (gmax = 51)
line = 51;
while (line > 0) {
if ((line & 07) == 0) {
} else {
printf("\n |");
}
}
printf("\n 0%% -+");
line = -1;
}
/*
* Print Histogram Line.
*/
void
int mrow;
char dot;
{
register ii;
register char *lp;
char lbuff[132];
"pline(line 0x%x, array 0x%x, mrow 0x%x, dot 0x%x)\n",
else
else
*lp++ = 0;
}
/*
* Print Cylinder Profiling Headers.
*/
void
long total;
{
if (fflg)
printf("\014\n");
printf("\nCYLINDER ACCESS HISTOGRAM\n");
printf("\nSEEK DISTANCE HISTOGRAM\n");
printf("\n%s-%d:\n",
device, k);
printf("Total %s = %ld\n",
}
#define MAXCOL 80
/* Print Histogram Footers */
void
{
int i;
int chunk_mult = 1;
int col;
char digits[] = "0123456789";
int significant = 0;
else
printf("\n =<< ");
for (i = 0; i < 4; i++) {
}
}
if ((i / 1000) > 0) {
significant = 1;
}
significant = 1;
}
significant = 1;
}
if ((i == 0) || (significant) || ((i % 10) > 0)) {
}
if (i > CHUNK) {
chunk_mult = 2;
}
if (i > (3 * CHUNK)) {
chunk_mult = 4;
printf ("< ");
}
}
for (i = 0; i < 4; i++) {
}
printf ("\n");
}
void
{
int i;
if (dflg == 0) {
/*
* No device specified, so default to the first
* one if it is the only one, otherwise prompt
* user to enter one.
*/
for (i = 0; i < dk_ndrive; i++)
drvlist[i] = 1;
if (dk_ndrive > 1)
dev = 0;
} else {
/*
* Device was specified. Make sure it matches
* one that is configured in the system.
*/
for (i = 0; i < dk_ndrive; i++) {
break;
}
if (i == dk_ndrive)
dev = i;
}
}
void
{
int i, j, c;
/*
* For each controller number specified, make sure it exists
* in the configured device list.
*/
for (i = 0; i < dk_ndrive; i++) {
if (drvlist[i] == 0)
continue;
/*
* Since this controller number (i) was specified,
* find the corresponding entry (j) in the device list.
* If found, save the device list index in drvlist[].
*/
for (j = 0; j < dk_ndrive; j++) {
continue;
if (c == i) {
/*
* NOTE: saved value actual index+1
* as entries with 0 imply don't care.
*/
break;
}
}
/*
* If not found, output error, except if all drives
* were implied by only specifying controller type.
* In this case, flag it as don't care.
*/
if (j == dk_ndrive) {
if (all)
drvlist[i] = 0;
else
}
}
}
void
{
int i, fd;
/*
* When the new device naming convention is in effect, switch to it
*/
#ifdef NEW_DEVICE_NAMES
#define DEV_PREFIX "/dev/rdsk/"
#else
#define DEV_PREFIX "/dev/r"
#endif
for (i = 0; drvlist[i] == 0; i++);
}
/*
* dk_geom structure now has data, and the number
* of 8 cylinder chunks on the disk can now be
* referenced via the CHPERCYL macro. So allocate
* appropriate buffers based on this value.
*/
for (i = 0; i < dk_ndrive; i++) {
}
}
/*
* General routine for printing out an error message
*/
void
char *device;
char *errmsg;
{
int i, j;
int unique = 0;
char s[NAMESIZE];
char *msg;
/*
* Print usage statement if no device is specified.
*/
usage();
/*
* Compose a list of unique device controller types, or
* unit numbers for a specified controller type, from
* the complete device list.
*/
for (i = 0; i < dk_ndrive; i++) {
/*
* Get controller type or unit
*/
p = devnm[i];
continue;
p = p1;
p1++;
}
/*
* Have we already logged this one as unique?
* If not, then do so now.
*/
for (j = 0; j < unique; j++)
break;
if (j == unique)
}
/*
* Invalid device was specified. Compose message containing
* list of valid devices.
*/
}
/*
* Output the message and exit.
*/
}
/*
* Code below here was taken from the SunOS 5.0 iostat command.
*/
#ifdef FIXME
void
{
int i;
"dk_ivec");
for (i = 0; i < NDRIVE; i++) {
"dk_name");
}
}
}
#endif
void
{
#ifdef FIXME
int i;
for (i = 0; i < NDRIVE; i++) {
dr_select[i] = 0;
dk_bps[i] = 0;
}
/*
* The default device names: dk#
*/
for (i = 0; i < dk_ndrive; i++) {
}
/*
* Device names must be discovered in this program, and output
* with its io data via the "sa" structure.
*/
#else
return;
#endif
}
/*
* issue failure message and exit
*/
void
char *message;
int doperror;
{
if (doperror) {
}
exit(2);
}
void
unsigned long addr;
char *buf;
unsigned size;
{
int ret_code;
char errmsg[100];
if (addr == 0) {
}
}
}
/*
* code for debugging dumps
*/
#include <sys/tuneable.h>
int dump_iodev ();
{
dfprintf (stderr, "flags\tb_forw\tb_back\tav_forw\tav_back\tb_bcount\n0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t%d\n",
return (0);
}
char *str;
{
int i;
}
return (0);
}
{
return (0);
}
int *tbl;
int size;
{
int i;
for (i = 0; i < size/4; i++) {
}
return (0);
}