v07i116: nlist -- display and monitor kernel variables and data structures
Brandon S. Allbery - comp.sources.misc
allbery at uunet.UU.NET
Sun Aug 6 12:17:28 AEST 1989
Posting-number: Volume 7, Issue 116
Submitted-by: chris at cetia.UUCP (Chris Bertin)
Archive-name: nlist
[Amazing. It looks like this should be easy to adapt to almost any flavor
of Unix; it comes with SunOS 3.5, 4.3BSD, and System V R2 and R3 templates.
Horray for portable programming! ++bsa]
This is 'nlist'. There is a README file and manual that explain what it does,
as well as a few examples of the things you can do with it. I tried posting
it to comp.sources.unix, but nothing seems to be going on with that group any
more (the last posting I received from comp.sources.unix is from the first
of July...). I am not even getting replies to my mail to Rich Salz.
Anyway, if you have any questions, please email them to me.
Chris
---------------------------------- cut here -------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# README
# MANIFEST
# nlist.8
# nlist.c
# Makefile
# chars.sun
# config.sh
# dnlc.sun
# file.sun
# file.sys53
# minfo.sys5
# mount.sun
# mount.sys53
# syserr.sys5
# sysinfo1.sys5
# sysinfo2.sys5
# sysinfo3.sys5
# sysinfo4.sys5
# sysinfo5.sys5
# sar-s
# This archive created: Wed Aug 2 17:52:40 1989
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
-- N L I S T --
To make 'nlist', type 'sh config.sh'. I have built it on SYSV.2, SYSV.3,
SunOs3.5, BSD4.3 and Mach, so I hope it will compile and run without
problems on your system.
There are examples for Sun machines in *.sun and examples for System V
machines in *.sys5*. BSD4.3 systems should be able to run some of the Sun
scripts. If these scripts don't work on your machine, it means
the structures are not quite identical to the ones I have here. It should
be pretty easy to fix them, once you've read the manual. If symbols are not
found, try adding or removing the leading symbol name underscore.
If you have any comments, you can Email them to me.
Chris Bertin | -- CETIA -- 150, Av Marcelin Berthelot, Z.I. Toulon-Est
+33(94)212005 | 83088 Toulon Cedex, France
| inria!cetia!chris
SHAR_EOF
fi
if test -f 'MANIFEST'
then
echo shar: "will not over-write existing file 'MANIFEST'"
else
cat << \SHAR_EOF > 'MANIFEST'
README
MANIFEST
nlist.8
nlist.c
Makefile
chars.sun
config.sh
dnlc.sun
file.sun
file.sys53
minfo.sys5
mount.sun
mount.sys53
syserr.sys5
sysinfo1.sys5
sysinfo2.sys5
sysinfo3.sys5
sysinfo4.sys5
sysinfo5.sys5
sar-s
SHAR_EOF
fi
if test -f 'nlist.8'
then
echo shar: "will not over-write existing file 'nlist.8'"
else
cat << \SHAR_EOF > 'nlist.8'
.TH NLIST 8 LOCAL
.SH NAME
.ad b
nlist \- display and monitor kernel variables and data structures
.SH SYNOPSIS
.B nlist
[-aptux] [-f format] [-h header] [-l loops] [-m mem]
.if n \{\
.br
.ti 2c\}
[-n namelist] [-s sleep] [-D debug] symbol|formula ...
.SH DESCRIPTION
.I Nlist
reads kernel variables and displays them. It can be used to monitor variables
or to display arrays of data structures or linked lists. It is also a full
report generator, with a built-in calculator. It is very useful for kernel
debugging (monitoring of debug variables, for example) and can even provide
a user-configurable replacement for utilities like
.I sar(1),
on SYSV systems.
See the EXAMPLE section below.
.PP
The toggle options are:
.TP 12
.I -a
Auto increment the address by the size of the previous read. This is used
to step through a structure with a minimum amount of address calculations.
The auto-increment option is disabled if the cell contains any address
calculation. See the SYMBOLS, FORMULAS and EXAMPLES sections.
.TP
.I -p
Prepend the '0x' prefix, if the display type is in hexadecimal, or the '0'
prefix if it is in octal. Essentially the '#' option of printf.
.TP
.I -t
Print the time in the first column (ala 'sar').
.TP
.I -u
Print unique lines only (excluding the time, if the -t flag is present).
.TP
.I -x
Normally,
.B nlist
exits if a value doesn't fit in the print format, or if a symbol is not
found in the name list. If this option is present,
.B nlist
will carry on silently. See the
.B format
option below. Also, if this flag is set and if a read failure occurs,
the output will be a string of question marks (?).
.PP
The other options are:
.TP 12
.I -f format
The argument is a simplified printf format: a number followed by a valid
format type. See the FORMATS section below.
.TP
.I -h header
If the argument is zero (0), no title will be printed. If it is one (1),
a title is printed on the first line only. If it is any other value, the
title will be reprinted every ${LINES} (24 by default).
.TP
.I -l loops
Normally,
.B nlist
loops forever. The argument is the number of lines that will
be displayed, excluding the headers.
.TP
.I -m memdev
Read kernel memory from
.B memdev
instead of the default
.B /dev/kmem.
.TP
.I -n namelist
Read the namelist from
.B namelist
instead of
.B /unix
or
.B /vmunix.
.TP
.I -s sleep
The argument overrides the default one (1) second pause between line output.
.TP
.I -D debug
This field is a level of debug. One (1) prints information about the read's,
two (2) prints the control structure before it is analyzed, and
three (3) prints it after it has been analyzed. See source code for details.
.PP
The arguments following these options are kernel symbols or formulas. They
will be refered to as
.I cells
in this manual.
.SH SYMBOLS
A symbol is a valid name found in the kernel namelist. A read size may be
supplied after the symbol name, following the special character
.B '|'.
If the print format is not a string (see FORMATS below), only sizes
of 1, 2 and 4 may be given, meaning the symbol to be read is 1, 2 or 4 bytes
big. The default is 4.
.PP
Symbols may be also followed by a positive or negative offset indicated by
.I +value
or
.I -value.
.PP
They may also be followed by a positive or negative loop increment, following
the special character
.B '>':
.I >value,
or
.I >+value,
or
.I >-value.
.PP
Loop incrementing is done for each line, after all the arguments have been
evaluated. It differs from the 'auto-increment' option which will increment
the address of each cell with the read size of the preceding one, before the
read is done.
.PP
These values may be given in any order.
.PP
If a symbol name is followed by an exclamation mark ('!'), the read will
be done once only and the cell won't be printed.
.SH FORMULAS
.B Nlist
has a built-in stacking (reverse Polish) calculator. Arithmetic
operations may be performed on any current value, preceding value or address
of any field (cell). The first non-option argument is cell one (1).
.PP
The special characters are:
.TP 6
.I '#'
means the current value of a cell. That value is the value read from memory
or whatever may have been placed there by a
calculation.
.TP
.I '^'
means the value of a cell on the previous pass.
.TP
.I '&'
means the address of a cell. The address is the memory location from which
data is read.
.PP
The cell identification may be absolute or relative and the special
character '.' means the current cell. For example, '&.' is the read-address
for the current cell, '#1' is the current value of cell 1, '^+3' is
the value of the third cell to the right, on the previous pass, and '#-2' is
the current value of the second cell on the left.
.PP
The valid operations are: +, -, * or / for add, subtract, multiply or divide,
respectively. The operands may be integers, current cell values, previous
cell values or read-addresses.
.PP
Additionally, values or addresses may be initialized or recalculated on every
pass, using the
.B '!'
and the
.B '=',
special symbols respectively. For example:
.PP
.ti 10
.B "#. = &-1 #-2 +"
.PP
will set the current value to the sum of the read-address of the preceding
cell and the current value of the second cell on the left.
.PP
.ti 10
.B "^+1 ! 1"
.PP
will initialize the previous value of the cell on the right to one (1).
Note that initialization cells are not printed.
.PP
If the
.B -x
flag is set, divisions by zero (0) return an error instead of causing
.B nlist
to exit.
.PP
Floating point exceptions, except divisions by zero caught by the
calculator, are always fatal.
.SH CONDITIONAL EVALUATION
If a question mark ('?') is found it the argument, it indicates that
the read address for the current cell has been calculated by another cell
and that the read should take place only if the read-address is non-zero.
Following cells will be skipped as well and the value displayed will be a
string of dashes ('-'). Reading will resume on the next cell that contains
a real symbol, or on the next line, which ever comes first.
.SH FORMATS
The global print format is set to hexadecimal, 8 characters wide, by default.
It may be modified globally using the
.I -h
flag, or for each cell. The syntax of a format cell is: the special character
.B '%',
optionally followed by a positive value and followed by a identifier.
.PP
Formats supported are: %d, %u, %o, %x, %f (2 decimals), %s and %t. %t is
time format, meaning the value is printed using ctime(3).
.PP
The optional value is the width of the cell. If an overflow occurs,
.B nlist
will exit, unless the
.B -x
flag is set, in which case a string of asterisks (*) will be displayed.
.PP
If the format is decimal and if the result of a calculation is not an integer,
floating point format will be used automatically.
.SH HEADERS
Each cell may supply its own header, after the special character
.B ':'.
If the header contains characters special to
.B nlist,
it must be quoted. If no header is supplied, the argument is printed.
.SH EXAMPLES
Here are now a few examples that will show most of the things
that can be done with
.B nlist:
.PP
Example 1
.PP
This command works on BSD and V7 systems that keep track of the
total number of characters input and output, in '_tk_nin' and '_tk_nout',
respectively.
.PP
.nf
nlist -t -f 9d -s 2 \\
_tk_nin \\
_tk_nout \\
'#. = #1 #2 + : Total' \\
'#1 100 * #3 / : "% Input"' \\
'#2 100 * #3 / : "% Output"'
.fi
.PP
Description, argument by argument:
.PP
The time will be printed at the start of each line (-t option), the print
mode will be decimal, 9 characters wide, for all the cells (-f 9d),
and the program will run continuously with a line of output
every 2 seconds (-s 2).
.PP
The value of '_tk_nin' will be read into cell 1, the value of '_tk_nout' into
cell 2, the sum of these 2 values will be placed in the current value
of cell 3.
.PP
Cells 4 and 5 will print out the percentages of inputs and outputs
respectively.
.PP
The result will be something like:
.PP
.TS
rrrrrr.
16:18:39 _tk_nin _tk_nout Total % Input % Output
16:18:39 19160 994283 1013443 1.89 98.11
16:18:41 19160 994343 1013503 1.89 98.11
16:18:43 19160 994403 1013563 1.89 98.11
16:18:45 19160 994463 1013623 1.89 98.11
.../...
.TE
.PP
Example 2
.PP
This command works on a Sun 3, with a release 3.5EXPORT. If the
mount structure hasn't changed on newer releases, it will work as well. It
should also work on generic 4.3 BSD systems.
.PP
This example uses different print formats, different read sizes,
loop increments and cell auto-increments.
.PP
.nf
nlist -pax -s 0 -f 8x -l 4 \\
'^. 1 + : " #" %2d' \\
'_mounttab>28 : Vfsp' \\
' |1 : Maj %3o' \\
' |1 : Min %3o' \\
' : Devvp' \\
' : Bufp' \\
' : Qinod' \\
' : Qflags |2' \\
' : Btime %5d' \\
' : Ftime %5d'
.fi
.PP
Description, argument by argument:
.PP
Turn the 'prefix' option (-p), turn on the 'auto-increment' option (-a),
continue if an overflow occurs (-x), don't pause between line displays (-s 0),
use hexadecimal, 8 characters wide as a default print mode (-f 8x), and
print out 4 lines (-l 4).
.PP
The next argument, on the next line, will just print a header by taking the
value of the current cell on the previous pass (uninitialized, zero) and
adding one (1) to it. Its header will be ' #' and its print format 2 decimal
characters.
.PP
The next argument will cause the variable '_mounttab' to be read in and to
be displayed with the 'Vfsp' title. When all the arguments will be processed,
the address will be incremented by 28 (the size of the mount table
structure). The read-address will be incremented by 4 for the next read,
because auto-increment is turned on and because the read size is 4
bytes (default).
.PP
The next 2 arguments will read 1 byte each (half a 'dev_t' type, on BSD),
and display them as 'Maj' and 'Min', in octal, 3 characters wide.
.PP
The next 3 arguments are pointers, 4 bytes big. These cells are basically
empty cells with a title (':').
.PP
The following argument is a short and the last 2 are longs, displayed in
decimal, 6 characters wide.
.PP
The output will look something like this:
.PP
.if t \{\
.TS
rrrrrrrrrr.
# m_vfsp maj min devvp bufp qinod qflags btime ftime
1 0x110b50 03 0 0x110afc 0x10213c 0 0 0 0
2 0x104400 07 0 0x1044f4 0x10125c 0 0 0 0
3 0x104424 03 03 0x110fc8 0x102208 0 0 0 0
4 0x104594 03 07 0x1044cc 0x1013f4 0 0 0 0
.TE\}
.if n \{\
.nf
# m_vfsp maj min devvp bufp qinod qflags btime ftime
1 0x110b50 03 0 0x110afc 0x10213c 0 0 0 0
2 0x104400 07 0 0x1044f4 0x10125c 0 0 0 0
3 0x104424 03 03 0x110fc8 0x102208 0 0 0 0
4 0x104594 03 07 0x1044cc 0x1013f4 0 0 0 0
.fi\}
.PP
Example 3
.PP
This command will dump the 'dnlc' cache on a Sun. It uses the string output
format and the addressing feature needed to follow linked lists. It should
work without too many modifications on generic 4.3 systems.
.PP
.nf
nlist -ax -h1 -s 0 -f6x -l 10 \\
'_ncache : Hshnxt' \\
' : Hshprv' \\
' : Lrunxt' \\
' : Lruprv' \\
' : Vp' \\
' : Dp' \\
'|1 : Len %3d' \\
'|15 : Name %15s' \\ <== 8
' : Ucred' \\
'@&1 = #1' <== 10
.fi
.PP
Description of arguments not yet explained.
.PP
The argument on the 8th line displays a string of
characters, 15 bytes long.
.PP
The last (10th) argument shows how one can follow a linked list. The '@'
means that the cell won't be printed, '&1 = #1' sets
the address of the first cell to the value of the first cell.
.PP
Here is the result:
.PP
.if t \{\
.TS
rrrrrrrrr
rrrrrrrlr.
Hshnxt Hshprv Lrunxt Lruprv Vp Dp Len Name Ucred
a02e0 0 a051c a0e38 a1db0 a1ac4 0 ......\\.......\\ a1518
a1360 a051c a1728 a0fc4 92b6c 9242e 3 usrxcvsub.c0or0 0
a1ddc a02e0 a1964 a190c 92834 90668 11 usr.MC68010nd.o 0
a16d0 a1360 a138c a0de0 95070 92b6c 5 spool.heds40.os 0
a17d8 a1ddc a117c a1200 96962 92766 3 ucbf.hub.cld.av 0
a0a9c a16d0 a0860 a0390 93c52 97be4 7 nlist.oa1.c.old 0
a10cc a17d8 a030c a1d00 95a18 92dd6 2 ldrace.h.c30rc1 0
a0cd8 a0a9c a1bcc a093c 92c3a 95476 6 time.hh0p at svc.2 0
.../...
.TE\}
.if n \{\
.nf
Hshnxt Hshprv Lrunxt Lruprv Vp Dp Len Name Ucred
a02e0 0 a051c a0e38 a1db0 a1ac4 0 ......\\.......\\ a1518
a1360 a051c a1728 a0fc4 92b6c 9242e 3 usrxcvsub.c0or0 0
a1ddc a02e0 a1964 a190c 92834 90668 11 usr.MC68010nd.o 0
a16d0 a1360 a138c a0de0 95070 92b6c 5 spool.heds40.os 0
a17d8 a1ddc a117c a1200 96962 92766 3 ucbf.hub.cld.av 0
a0a9c a16d0 a0860 a0390 93c52 97be4 7 nlist.oa1.c.old 0
a10cc a17d8 a030c a1d00 95a18 92dd6 2 ldrace.h.c30rc1 0
a0cd8 a0a9c a1bcc a093c 92c3a 95476 6 time.hh0p at svc.2 0
.../...
.fi\}
.PP
Example 4
.PP
The next command will show you how to use indirection. On BSD systems, the
variable '_file' is not the beginning of the file table, but a pointer to
the table. It will also show you how to follow a pointer inside a structure,
in this case, the 'ucred' pointer.
.PP
.nf
nlist -a -s 0 -f7x -h 1 -l 10 \\
'^. 1 + : " #" %3d' \\
'_file!' \\ <== 3
'&+1 ! #-1' \\ <== 4
'>26 : flag %4o' \\ <== 5
'|2 : type %4x' \\
'|2 : count %6d' \\
'|2 : msgcount %6d' \\
' : fops' \\
' : fdata' \\
' : offset %6x' \\
' : cred' \\
'&. = #-1 |2 : Crref %5d' \\ <== 13
'|2 : Uid %4d' \\
'|2 : Gid %4d'
.fi
.PP
Description of arguments not yet explained:
.PP
Line 3: The variable '_file' is read but the cell is not displayed because
of the trailing '!'.
.PP
Line 4: Set the read-address of the cell on the right to the value read in the
preceding cell.
.PP
Line 5: This cell's read-address has been initialized by the preceding cell.
On the next pass, it will be incremented by 26 bytes (the size of the file
structure).
.PP
Line 13: Here, we follow the the 'ucred' pointer and display the first
element from that structure. The next 2 cell will auto-increment from
that new address.
.PP
Here is the file table:
.PP
.if t \{\
.TS
rrrrrrrrrrrr.
# flag type count msgcou fops fdata offset cred Crref Uid Gid
1 1 1 0 0 7a0f8 970a0 9e2 1047d0 5 110 100
2 1 1 0 0 7a0f8 93ebc 29b 1047d0 5 110 100
3 1 1 0 0 7a0f8 2183ac 1047d6 1047d0 5 110 100
4 1 1 0 0 7a0f8 94c6a 80a7c 1047d0 5 110 100
5 1 1 0 0 7a0f8 94c6a 8a6a4 1047d0 5 110 100
6 3 2 0 0 799fc 0 0 110b24 97 0 0
7 2 1 0 0 7a0f8 93ebc 29b 1047d0 5 110 100
.../...
.TE\}
.if n \{\
.nf
# flag type count msgcou fops fdata offset cred Crref Uid Gid
1 1 1 0 0 7a0f8 970a0 9e2 1047d0 5 110 100
2 1 1 0 0 7a0f8 93ebc 29b 1047d0 5 110 100
3 1 1 0 0 7a0f8 2183ac 1047d6 1047d0 5 110 100
4 1 1 0 0 7a0f8 94c6a 80a7c 1047d0 5 110 100
5 1 1 0 0 7a0f8 94c6a 8a6a4 1047d0 5 110 100
6 3 2 0 0 799fc 0 0 110b24 97 0 0
7 2 1 0 0 7a0f8 93ebc 29b 1047d0 5 110 100
.../...
.fi\}
.PP
Example 5:
.PP
Dump the mount table on a SYSV.3 88K Motorola system ('dev_t' is a long on
that system):
.PP
.nf
nlist -ap -s 0 -f8x -l 4 \\
'^. 1 +: #%2d' \\
'_mount |2 >36 : Flags %6o' \\
'|2 : Type %4d' \\
' : Bsize %5o' \\
' : Dev %6o' \\
' : Bufp' \\
' : Inop' \\
' : Mountp' \\
'|2 : Rflags' \\
' : Namep' \\
' : Bcount %6d'
.fi
.PP
Example 6:
.PP
This will do what 'sar -s' does, on SYSV:
.PP
.nf
echo "`uname -a` `date +%D`\\n"
nlist -xat -f7d -s 1 -h1 \\
'@sysinfo' \\
'@' \\
'@' \\
'@' \\
'@#. = #1 ^1 -' \\
'@#. = #2 ^2 -' \\
'@#. = #3 ^3 -' \\
'@#. = #4 ^4 -' \\
'@#-1 #-2 #-3 #-4 +++' \\
'#6 100 * #-1 /: "%usr"' \\
'#7 100 * #-2 /: "%sys"' \\
'#8 100 * #-3 /: "%wio"' \\
'#5 100 * #-4 /: "%idle"'
.fi
.PP
Finally, an example of the neat things you can do with
.B nlist:
.PP
.nf
nlist -h 1 -f 12u -l 31 -s 0 '^+1 ! 1' '^2 2 *:"Powers of 2"'
.fi
.SH FILES
/unix or /vmunix name list
.br
/dev/kmem kernel memory
.SH BUGS
Conditional evaluation is very basic.
.PP
The syntax is not exactly user-friendly.
.SH AUTHOR
Chris Bertin.
SHAR_EOF
fi
if test -f 'nlist.c'
then
echo shar: "will not over-write existing file 'nlist.c'"
else
cat << \SHAR_EOF > 'nlist.c'
#ifndef lint
static char ID[] = "@(#)nlist.c 2.1 Copyright (C) 1989 Chris Bertin 89/07/31";
#endif
/*
* nlist -- display and monitor kernel variables. Dump kernel structures.
*
* This program is free for redistribution as long as this header
* remains unchanged.
*
* Copyleft Chris Bertin, July 1989
*
*/
#include <stdio.h>
#ifdef V7
#include <a.out.h>
#else
#include <nlist.h>
#endif /* V7 */
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif /* SYSV */
#include <ctype.h>
#include <memory.h>
#include <time.h>
#include <sys/types.h>
#include <sys/signal.h>
#ifdef SYSV
#include <sys/var.h>
#endif
#define NLISTBUG /* see comment in fixnlist() */
#ifndef SYSV
#define DEF_NLIST "/vmunix" /* default namelist */
#else
#define DEF_NLIST "/unix"
#endif
#define DEF_KMEM "/dev/kmem" /* default memory device */
#define DEF_SLEEP 1 /* default pause between displays */
#define DEF_WIDTH 8 /* default print width */
#define DEF_LOOPS -1 /* default number of loops (infinite) */
#define DEF_RDSIZE 4 /* default read size */
/* misc return values */
#define R_OK 0 /* OK */
#define R_NOPRINT 1 /* value was computed but don't print */
#define R_FLOAT 2 /* print value in float format */
#define R_SKIP 3 /* skip this entry */
#define R_ERR -1 /* print error value */
/* misc */
#define CDEPTH 4 /* max number of push for formulas */
#define NDEC 2 /* number of decimals for float */
#define NSPACES 1 /* number of spaces between fields */
/* header special characters */
#define H_NOPRINT '@' /* don't print if first character */
#define H_POST ':' /* real header start character */
#define H_SET '=' /* set field to value */
#define H_INIT '!' /* initialize field to value */
#define H_SIZE '|' /* read size (real symbols only) */
#define H_FMT '%' /* field print width */
#define H_INCR '>' /* increment address on next loop */
#define H_NONZERO '?' /* quit if this address is 0 */
/* field identifiers in formulas. (For example: #1: value of field1, */
/* ^+1: previous value of next field, &.: nlist address in current field) */
#define H_CURVAL '#' /* current value in field number */
#define H_PREVAL '^' /* previous value in field number */
#define H_ADDR '&' /* the nlist address of the field */
#define H_CURFIELD '.' /* current field */
/* values for -h flag */
#define HD_NONE 0 /* don't print header */
#define HD_ONCE 1 /* print header once only */
#define HD_PAGE 2 /* print header every $LINES lines */
typedef float val_t;
typedef struct nlist nl_t;
typedef struct field {
nl_t *f_nl; /* nlist entry pointer */
char *f_arg; /* original argument */
char *f_form; /* formula or symbol */
char *f_post; /* title */
char *f_fmt; /* print format (dec, oct, hex...) */
char *f_buf; /* read buffer for string printf's */
short f_flags; /* features */
short f_width; /* print width */
short f_rdsize; /* read size */
off_t f_offset; /* address offset */
off_t f_incr; /* address increment */
val_t f_curval; /* current value */
val_t f_preval; /* previous value */
} fld_t;
/* f_flags bits */
#define F_TITLE 0x1 /* Title is present */
#define F_SYM 0x2 /* valid symbol */
#define F_SET 0x4 /* set value */
#define F_INIT 0x8 /* set value once only */
#define F_DONE 0x10 /* init done */
#define F_QUOTED 0x20 /* header is quoted. Don't clean it */
#define F_NONZERO 0x40 /* quit if address is 0 */
#define F_TIME 0x80 /* print the value as date/time */
#define F_READOK 0x100 /* OK to read size other that 1,2,4 */
extern char *optarg, *getenv(), *parsearg(), *alloc(), *calloc();
extern char *prfmt(), *spaces(), *cleanbuf();
extern char *sys_errlist[];
extern int errno, optind;
extern double ceil();
/* formats */
char decfmt[] = "%*d"; /* decimal print mode */
char unsfmt[] = "%*u"; /* unsigned decimal print mode */
char octfmt[] = "%*o"; /* octal print mode */
char hexfmt[] = "%*x"; /* hex print mode */
char flofmt[] = "%*.*f"; /* floating point format */
char strfmt[] = "%*.*s"; /* string print format */
#ifndef V7
char extoctfmt[] = "%#*o"; /* hex with '0' prefix */
char exthexfmt[] = "%#*x"; /* octal with '0x' prefix */
#endif
#define DEF_FMT hexfmt /* default print format */
val_t calc[CDEPTH]; /* calculator stack */
int cdepth; /* calculator stack depth */
int tim, uniq, fmtprefix, debug, autoincr; /* flags with default 0 */
int strict = 1; /* flags with default 1 */
char *buf, *hdr; /* line and header buffers */
char *pname; /* argv[0], for error messages */
char *kmemf = DEF_KMEM;
char *nlistf = DEF_NLIST;
char *fmt = DEF_FMT;
int slp = DEF_SLEEP;
int width = DEF_WIDTH;
int nloops = DEF_LOOPS;
int header = HD_PAGE;
main (argc, argv)
char **argv;
{
register int mem, i;
int nsymbols, maxw, prtime;
register fld_t *baseflp, *flp;
nl_t *nl;
char *p;
int onfpe();
pname = *argv;
while ((i = getopt(argc, argv, "aD:f:h:l:m:n:ps:tux")) != -1) {
switch (i) {
case 'a':
autoincr++;
break;
case 'D':
debug = stoi(&optarg, "debug level", 0, 0);;
break;
case 'f':
fmt = prfmt(&optarg, &width, &prtime, 0, optarg);
break;
case 'h':
header = stoi(&optarg, "header value", 1, 0);
break;
case 'l':
nloops = stoi(&optarg, "number of loops", 0, 0);
break;
case 'm':
kmemf = optarg;
break;
case 'n':
nlistf = optarg;
break;
case 'p':
fmtprefix++;
break;
case 's':
slp = stoi(&optarg, "sleep time", 0, 0);
break;
case 't':
tim++;
break;
case 'u':
uniq++;
break;
case 'x':
strict = 0;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc <= 0)
usage();
if ((mem = open (kmemf, 0)) < 0)
perrexit (kmemf);
baseflp = (fld_t *) alloc((int) (argc * sizeof (fld_t)));
nl = (nl_t *) alloc((int) ((argc + 2) * sizeof (nl_t)));
for (flp = baseflp, maxw = nsymbols = 0, i = 0; i < argc; i++, flp++) {
p = parsearg(flp, *(argv + i));
if (flp->f_fmt != NULL)
maxw += flp->f_width + NSPACES;
if (flp->f_flags & F_SYM) {
flp->f_nl = nl + nsymbols;
nsymbols++;
#ifdef V7
(void) strncpy(flp->f_nl->n_name, p, SYMLENGTH);
#else
flp->f_nl->n_name = p;
#endif
continue;
}
if (autoincr && i > 0 && flp->f_incr == 0 && *p == '\0') {
sprintf(p = alloc(32), "%c%c%c%c-1 %d +", H_ADDR,
H_CURFIELD, H_SET, H_ADDR, (flp - 1)->f_rdsize);
flp->f_flags |= F_SET;
}
flp->f_nl = nl + (argc - i + nsymbols + 1);
flp->f_form = p;
}
if (maxw == 0) {
fprintf(stderr, "%s: nothing to print\n", pname);
exit (1);
}
if (nsymbols) {
donlist(nl, mem, nsymbols);
for (i = 0, flp = baseflp; i < argc; i++, flp++)
if (flp->f_offset && flp->f_nl->n_value)
flp->f_nl->n_value += flp->f_offset;
}
if (header != HD_NONE) {
hdr = alloc(maxw);
for (p = hdr, i = 0, flp = baseflp; i < argc; i++, flp++) {
if (flp->f_fmt == NULL)
continue;
sprintf(p, "%s%*.*s", spaces(NSPACES), flp->f_width,
flp->f_width, flp->f_post? flp->f_post: flp->f_arg);
p += flp->f_width + NSPACES;
}
}
(void) signal(SIGFPE, onfpe);
for (;;) {
if (process(baseflp, argc, mem, maxw, prtime)) {
outputline();
if (--nloops == 0)
break;
}
(void) sleep((unsigned) slp);
}
return (0);
}
/*
* Process the arguments once. Return 1 if 'buf' is to be printed.
*/
process(baseflp, max, mem, maxw, prtime)
register fld_t *baseflp;
register int max, mem, maxw;
{
register int i;
register char *p;
register fld_t *flp;
static char *lastbuf, *tmp;
if (buf == NULL) {
buf = alloc(maxw);
lastbuf = alloc(maxw);
tmp = alloc(BUFSIZ);
}
for (flp = baseflp, buf[0] = '\0', i = 0; i < max; i++, flp++) {
tmp[0] = '\0';
switch (analyze(baseflp, flp, max, mem)) {
case R_NOPRINT:
continue;
case R_SKIP:
sprintf(tmp, strfmt, flp->f_width, flp->f_width,
"--------------------");
break;
case R_FLOAT:
sprintf(tmp, flofmt, flp->f_width, NDEC, flp->f_curval);
break;
case R_OK:
p = flp->f_fmt;
if (p == strfmt) {
if (prtime || (flp->f_flags & F_TIME)) {
time_t t = (time_t) flp->f_curval;
(void) strncpy(flp->f_buf, ctime(&t),
flp->f_width);
}
sprintf(tmp, strfmt, flp->f_width, flp->f_width,
cleanbuf(flp->f_buf, flp->f_width));
break;
}
#ifndef V7
if (fmtprefix && (long) flp->f_curval) {
if (flp->f_fmt == hexfmt)
p = exthexfmt;
else if (flp->f_fmt == octfmt)
p = extoctfmt;
}
#endif
sprintf(tmp, p, flp->f_width, (long) flp->f_curval);
break;
case R_ERR:
if (strict) {
outputline();
fprintf(stderr, "%s: symbol '%s' not found\n",
pname, flp->f_nl->n_name);
exit (1);
}
sprintf(tmp, strfmt, flp->f_width, flp->f_width,
"????????????????????");
break;
}
if (strlen(tmp) > flp->f_width) {
if (strict) {
outputline();
fprintf(stderr, "%s: print overflow\n", pname);
exit (1);
}
sprintf(tmp, strfmt, flp->f_width, flp->f_width,
"********************");
}
(void) strcat(buf, spaces(NSPACES));
(void) strcat(buf, tmp);
}
for (i = 0, flp = baseflp; i < max; i++, flp++) {
flp->f_preval = flp->f_curval;
if (flp->f_incr && flp->f_nl->n_value)
flp->f_nl->n_value += flp->f_incr;
}
if (uniq) {
if (strcmp(buf, lastbuf) == NULL)
return (0);
(void) strcpy(lastbuf, buf);
}
return(1);
}
/*
* Print output buffer, adding title if needed. Exit if err is set.
*/
outputline()
{
static int lines, nlines;
char *p;
if (buf == NULL || *buf == '\0')
return;
if (header != HD_NONE) {
if (nlines == 0)
lines = nlines = (p = getenv("LINES"))? atoi(p): 24;
if (lines == nlines) {
printline(hdr);
lines = 2;
}
lines++;
if (header == HD_ONCE)
header = HD_NONE;
}
printline(buf);
}
/*
* Print a line, with the current time if -t flag present.
*/
printline(line)
char *line;
{
time_t t;
register struct tm *tm;
if (tim) {
(void) time(&t);
tm = localtime(&t);
printf("%2d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
}
printf("%s\n", line);
fflush(stdout);
}
/*
* Parse an argument and initialize the control structure. Return a pointer
* to a symbol without any trailers, if the argument is a symbol name.
*/
char *
parsearg(flp, str)
register fld_t *flp;
char *str;
{
char *p;
register char *pp, *xp;
register char c;
int endsym, w, prtime;
flp->f_rdsize = DEF_RDSIZE;
flp->f_width = width;
flp->f_fmt = fmt;
(void) skipspaces(&str);
if (*str == H_NOPRINT) {
flp->f_fmt = NULL;
str++;
(void) skipspaces(&str);
}
if (isalpha(*str) || *str == '_')
flp->f_flags |= F_SYM;
flp->f_arg = str;
(void) strcpy(pp = alloc(strlen(str) + 1), str);
for (endsym = 0, p = pp; *p; ) {
(void) skipspaces(&p);
xp = p;
switch (*p++) {
case H_POST:
endsym++;
(void) skipspaces(&p);
if ((c = *p) != '\'' && c != '"') {
flp->f_post = p;
break;
}
flp->f_post = ++p;
flp->f_flags |= F_QUOTED;
while (*p != c)
if (*p++ == '\0') {
fprintf(stderr,
"%s: missing quote in header -- %s\n",
pname, pp);
exit (1);
}
*p++ = '\0';
break;
case H_FMT:
endsym++;
flp->f_fmt = prfmt(&p, &w, &prtime, 1, flp->f_arg);
if (w)
flp->f_width = w;
if (flp->f_fmt == strfmt) {
flp->f_buf = alloc(flp->f_width + 1);
if (prtime)
flp->f_flags |= F_TIME;
else
flp->f_flags |= F_READOK;
}
break;
case H_SIZE:
endsym++;
flp->f_rdsize = stoi(&p, "read size", 0, 1);
break;
case H_INCR:
endsym++;
flp->f_incr = stoi(&p, "increment value", 1, 1);
break;
case H_SET:
flp->f_flags |= F_SET;
break;
case H_INIT:
*(p - 1) = (flp->f_flags & F_SYM)? '\0': H_SET;
flp->f_flags |= (F_SET|F_INIT);
flp->f_fmt = NULL;
break;
case '+':
case '-':
if (flp->f_flags & F_SYM)
endsym++;
--p;
flp->f_offset = (val_t) stoi(&p, "offset", 1, 1);
break;
case H_NONZERO:
endsym++;
flp->f_flags |= F_NONZERO;
break;
default:
continue;
}
if (! endsym)
continue;
*xp = '\0';
}
if (flp->f_buf == NULL)
flp->f_buf = alloc(flp->f_rdsize);
if ((flp->f_flags & F_QUOTED) == 0)
cleanspaces(flp->f_post);
cleanspaces(pp);
cleanspaces(flp->f_arg);
return (pp);
}
/*
* Handle symbols, formulas, reads, calculator, etc...
*/
analyze(baseflp, flp, max, mem)
register fld_t *baseflp, *flp;
register int max, mem;
{
fld_t *nfp;
char *p, *form, *fmtp;
register int i;
register val_t *vp;
int prev, addr, seta;
static int skiptonext;
if (flp == baseflp)
skiptonext = 0;
if (debug > 1)
showfl(flp, flp - baseflp, "start analyze");
if (flp->f_flags & F_DONE)
return (R_NOPRINT);
if (flp->f_flags & F_INIT)
flp->f_flags |= F_DONE;
prev = addr = seta = 0;
fmtp = flp->f_fmt;
p = form = flp->f_form;
if (flp->f_flags & F_SYM) {
skiptonext = 0;
if (flp->f_nl->n_value == 0)
return (R_ERR);
flp->f_curval = (val_t) doread(flp->f_nl->n_name, kmemf, mem,
flp->f_nl->n_value, flp->f_buf, flp->f_rdsize,
(flp->f_flags & F_READOK));
return ((fmtp == flofmt)? R_FLOAT: (fmtp? R_OK: R_NOPRINT));
}
for (i = 0; i < CDEPTH; i++)
calc[i] = 0;
cdepth = 0;
vp = &flp->f_curval;
if (flp->f_flags & F_SET) {
nfp = baseflp + fn(baseflp, flp, &p, &prev, &seta, max, 0) - 1;
if (seta == 0) {
flp = nfp;
vp = prev? &flp->f_preval: &flp->f_curval;
}
(void) skipspaces(&p);
if (*p++ != H_SET)
badf(form, --p, "invalid 'set' format");
}
*vp = 0;
while (*p) {
if (skipspaces(&p))
continue;
if (i = isdigit(*p))
*vp = stoi(&p, "formula", 1, 1);
else if (i = fn(baseflp, flp, &p, &prev, &addr, max, 1)) {
if (addr)
*vp = (val_t) (baseflp + i - 1)->f_nl->n_value;
else
*vp = (prev? (baseflp + i - 1)->f_preval:
(baseflp + i - 1)->f_curval);
}
if (i) {
if (push(*vp) == R_ERR)
badf(form, p, "stack overflow");
*vp = 0;
continue;
}
switch (*p++) {
case '+':
calc[1] = calc[1] + calc[0];
break;
case '-':
calc[1] = calc[1] - calc[0];
break;
case '*':
calc[1] = calc[1] * calc[0];
break;
case '/':
if (calc[0] != 0)
calc[1] = calc[1] / calc[0];
else if (strict)
badf(form, --p, "zero divide");
else
return (R_ERR);
break;
default:
badf(form, --p, "invalid operand");
}
*vp = 0;
if (pop() == R_ERR)
badf(form, --p, "stack empty");
}
if (*vp == 0)
*vp = calc[0];
if (seta)
nfp->f_nl->n_value = (long) *vp;
if (debug > 2)
showfl(flp, flp - baseflp, "end analyze");
if ((flp->f_flags & F_NONZERO) && flp->f_nl->n_value == 0)
skiptonext++;
if (skiptonext)
return (fmtp? R_SKIP: R_NOPRINT);
if (flp->f_nl->n_value)
flp->f_curval = (val_t) doread(flp->f_nl->n_name, kmemf, mem,
flp->f_nl->n_value, flp->f_buf, flp->f_rdsize,
(flp->f_flags & F_READOK));
if (fmtp == flofmt || (fmtp == decfmt &&
(val_t) ceil((double) flp->f_curval) != flp->f_curval))
return (R_FLOAT);
return (fmtp? R_OK: R_NOPRINT);
}
/*
* Return field number. Set 'prev' and 'addr' flags, advance formula pointer.
*/
fn(baseflp, flp, p, prev, addr, max, errok)
register fld_t *baseflp, *flp;
register char **p;
int *prev, *addr;
{
register int id, plus;
if (((*addr = (**p == H_ADDR)) == 0) &&
((*prev = (**p == H_PREVAL)) == 0) && **p != H_CURVAL) {
if (errok)
return (0);
badf(flp->f_form, *p, "not a field identifier");
}
(*p)++;
if (**p == H_CURFIELD) {
(*p)++;
return ((flp - baseflp) + 1);
}
plus = (**p == '+');
if ((id = stoi(p, "field number", 1, 1)) < 0 || (plus && id > 0))
id = (int) (flp - baseflp) + 1 + id;
if (id <= 0 || id > max) {
char *pp;
sprintf(pp = alloc((id / 10) + 2), "%d", id);
badf(flp->f_form, pp, "invalid field number");
}
return (id);
}
/*
* Convert a string into a positive or negative int. Handles hex and octal.
* Advance pointer.
*/
stoi(p, what, negok, errok)
char *what;
register char **p;
{
int n, newval, neg, base;
neg = 0;
base = 10;
(void) skipspaces(p);
if (**p == '+' || (negok && (neg = (**p == '-'))))
(*p)++;
if (! strncmp(*p, "0x", 2)) {
base = 16;
(*p) += 2;
}
else if (**p == '0') {
base = 8;
(*p)++;
}
for (newval = 0; **p; (*p)++) {
if ((n = conv(*p, base, what, errok)) < 0)
break;
newval = (newval * base) + n;
}
(void) skipspaces(p);
return (neg? -newval: newval);
}
/*
* Convert an ascii character into a digit.
*/
conv(p, base, what, errok)
char *p, *what;
{
register char c;
if (isdigit(c = *p))
return (c - '0');
if (isupper(c))
c = tolower(c);
if (base == 16 && c >= 'a' && c <= 'f')
return (10 + c - 'a');
if (errok)
return (-1);
fprintf(stderr, "%s: illegal %s (%s)\n", pname, what, p);
usage();
/* NOTREACHED */
}
/*
* Advance pointer to next non-space character. Return 1 pointer changed.
*/
skipspaces(p)
register char **p;
{
if (! isspace(**p))
return (0);
while (isspace(**p))
(*p)++;
return (1);
}
/*
* Remove trailing spaces from string.
*/
cleanspaces(str)
register char *str;
{
if (str == NULL || *str == '\0')
return;
while (*str)
str++;
--str;
while (isspace(*str))
*str-- = '\0';
}
/*
* Replace unprintable characters in a buffer by dots ('.').
*/
char *
cleanbuf(p, n)
register char *p;
{
register char *pp;
for (pp = p; --n >= 0; pp++)
if (! isprint(*pp))
*pp = '.';
return (p);
}
/*
* Handle a format string. Return a format pointer and the width in 'width'.
* Advance pointer.
*/
char *
prfmt(p, w, prtime, errok, str)
char **p;
char *str;
int *w, *prtime;
{
*prtime = 0;
if ((*w = stoi(p, "print width", 0, 1)) == 0)
*w = DEF_WIDTH;
switch (**p) {
case 'd':
return (decfmt);
case 'f':
return (flofmt);
case 'o':
return (octfmt);
case 't':
*prtime = 1;
/* fall thru */
case 's':
return (strfmt);
case 'u':
return (unsfmt);
case 'x':
return (hexfmt);
}
if (! isalpha(**p) && errok) {
(*p)++;
return (DEF_FMT);
}
fprintf(stderr, "%s: invalid print format in '%s' (%c)\n", pname,
str, **p);
outputline();
exit (1);
/* NOTREACHED */
}
/*
* Push argument onto calculator stack.
*/
push(n)
register val_t n;
{
register int i;
if (cdepth++ == CDEPTH)
return (R_ERR);
for (i = cdepth; --i >= 0; )
calc[i] = i? calc[i - 1]: n;
return (R_OK);
}
/*
* Pop one argument from calculator stack.
*/
pop()
{
register int i;
if (--cdepth <= 0)
return (R_ERR);
for (i = 1; i <= cdepth; i++)
if ((calc[i - 1] = calc[i]) == 0)
break;
return (R_OK);
}
/*
* Do 'nlist' and validate name list. Validation is very basic. Note that
* one extra 'nlist' entry was allocated to handle the additional symbol.
*/
donlist(nl, mem, max)
register nl_t *nl;
{
char *p;
int nproc, size;
#ifdef SYSV
struct var v;
#ifdef UNDERSCORESYM
(nl + max)->n_name = "_v";
#else
(nl + max)->n_name = "v";
#endif
p = (char *) &v;
size = sizeof (v);
#else /* SYSV */
#ifdef V7
strncpy((nl + max)->n_name, "_nproc", SYMLENGTH);
#else
#ifdef UNDERSCORESYM
(nl + max)->n_name = "_nproc";
#else
(nl + max)->n_name = "nproc";
#endif
#endif /* V7 */
p = (char *) &nproc;
size = sizeof nproc;
#endif /* SYSV */
if (nlist (nlistf, nl) < 0) {
fprintf(stderr, "%s: nlist(%s) failed -- ", pname, nlistf);
perrexit("");
}
if ((nl + max)->n_value == 0) {
fprintf(stderr, "%s: symbol '%s' not found in %s\n", pname,
(nl + max)->n_name, nlistf);
exit (1);
}
(void) doread((nl + max)->n_name, kmemf, mem,
(off_t) (nl + max)->n_value, p, size, 1);
#ifdef SYSV
nproc = v.v_proc;
#endif
if (nproc > 4096 || nproc < 3) {
fprintf(stderr, "Namelist out of date\n");
exit (1);
}
#ifdef NLISTBUG
fixnlist(nl, max);
#endif
}
#ifdef NLISTBUG
/*
* If a symbol is more than once in the list, all but the first one will have
* an n_value of zero. This normally doesn't matter, but 'nlist' can ask for
* symbol+4 and symbol+8, for example, in which case the second one fails.
* This is a bug present in all the nlist(3) I have tried.
*/
fixnlist(nl, max)
register nl_t *nl;
register int max;
{
register nl_t *np, *npp;
for (np = nl; np < nl + (max - 1) ; np++)
for (npp = np + 1; npp < nl + max; npp++)
if (! strcmp(npp->n_name, np->n_name)) {
npp->n_value = np->n_value;
break;
}
}
#endif /* NLISTBUG */
/*
* Generic read routine. If read size is 1, 2, 4 bytes, return that value,
* else return 0. If okdiff is 0, don't allow read sizes != 1, 2, 4.
*/
doread(what, from, mem, offset, into, size, okdiff)
char *what, *from, *into;
off_t offset;
{
if (debug)
showread(what, from, offset, size, -1);
(void) lseek(mem, offset, 0);
if (read(mem, into, (unsigned) size) != size) {
showread(what, from, offset, size, errno);
if (strict) {
outputline();
exit (1);
}
}
switch (size) {
case sizeof (char):
return ((int) *into);
case sizeof (short):
return ((int) *(short *) into);
case sizeof (int):
return ((int) *(int *) into);
default:
if (okdiff)
return (0);
outputline();
fprintf(stderr, "%s: Invalid read size: %d\n", pname, size);
exit (1);
}
/* NOTREACHED */
}
/*
* Debug and error routine. Show read arguments.
*/
showread(what, from, offset, size, err)
char *what, *from;
off_t offset;
{
fprintf(stderr, "%s: read ", pname);
if (what)
fprintf(stderr, "'%s', ", what);
fprintf(stderr, "%d bytes @ offset 0x%x in %s", size, offset, from);
if (err >= 0)
fprintf(stderr, " -- %s", sys_errlist[err]);
fprintf(stderr, "\n");
}
/*
* Debug routine. Show the whole control structure.
*/
showfl(flp, n, str)
register fld_t *flp;
char *str;
{
fprintf(stderr, " %s: fld %d, nl %x, buf '%s', name '%s', addr %x\n",
str, n, flp->f_nl, flp->f_buf, flp->f_nl->n_name,
flp->f_nl->n_value);
fprintf(stderr, " arg '%s', form '%s', post '%s', fmt '%s'\n",
flp->f_arg, flp->f_form, flp->f_post, flp->f_fmt);
fprintf(stderr, " f 0x%x, w %d, rd %d, o %d, i %d, cur %d, pre %d\n",
flp->f_flags, flp->f_width, flp->f_rdsize, flp->f_offset,
flp->f_incr, (long) flp->f_curval, (long) flp->f_preval);
}
/*
* Allocate 'n' bytes on the heap.
*/
char *
alloc(n)
{
char *a;
if ((a = calloc((unsigned) n, (unsigned) 1)) == 0)
perrexit("calloc");
return (a);
}
/*
* Build an array of spaces, return the address.
*/
char *
spaces(n)
{
static char *sp;
register char *p;
if (sp == NULL) {
sp = alloc(n + 1);
for (p = sp; --n >= 0; p++)
*p = ' ';
}
return (sp);
}
badf(form, msg1, msg2)
char *form, *msg1, *msg2;
{
outputline();
fprintf(stderr, "%s: invalid formula '%s'", pname, form);
if (msg1 && *msg1)
fprintf(stderr, " (%s)", msg1);
if (msg2 && *msg2)
fprintf(stderr, " -- %s", msg2);
fprintf(stderr, "\n");
exit (1);
}
perrexit(str)
char *str;
{
perror(str);
exit (1);
}
/*ARGSUSED*/
onfpe(n)
int n;
{
outputline();
fprintf(stderr, "%s: Floating poing exception\n", pname);
exit (1);
}
usage()
{
fprintf(stderr, "Usage: %s [-aptux] [-f format] [-h header] [-l loops] [-m mem]\n\t[-n namelist] [-s sleep] [-D debug] symbol|formula...\n", pname);
exit (1);
}
SHAR_EOF
fi
if test -f 'Makefile'
then
echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Makefile for nlist
all: nlist man
nlist: nlist.o
$(CC) $(LDFLAGS) -o $@ $@.o -lm
clean:
rm -f core *.o
clobber: clean
rm -f nlist
man:
tbl < nlist.8 | nroff -man > nlist.man
SHAR_EOF
fi
if test -f 'chars.sun'
then
echo shar: "will not over-write existing file 'chars.sun'"
else
cat << \SHAR_EOF > 'chars.sun'
#! /bin/sh
echo "Showing character I/O statistics"
nlist -t -s 2 -f9d \
'_tk_nin : "Chars in"' \
'_tk_nout : "Chars out"' \
'#. = #1 #2 + : Total' \
'#1 100 * #3 / : "% Input"' \
'#2 100 * #3 / : "% Output"'
SHAR_EOF
fi
if test -f 'config.sh'
then
echo shar: "will not over-write existing file 'config.sh'"
else
cat << \SHAR_EOF > 'config.sh'
#! /bin/sh
echo "Checking configuration and building 'nlist'"
CFLAGS="-O"
if [ -f /unix ]; then
echo "This looks like a System 5 system"
CFLAGS="${CFLAGS} -DSYSV"
NAMELIST=/unix
getmain() grep -c '_main[ ]'
elif [ -f /vmunix ]; then
echo "This looks like a BSD system"
NAMELIST=/vmunix
getmain() grep -c '_main$'
else
echo "No /unix and no /vmunix. What system are you?" >&2
exit 1
fi
if [ `nm ${NAMELIST} | getmain` -gt 0 ]; then
CFLAGS="${CFLAGS} -DUNDERSCORESYM"
MSG=prepends
else
MSG="doesn't prepend"
fi
echo "It looks like the loader ${MSG} an underscore to symbol names"
echo "If this is not right, 'nlist' may fail. Build it manually"
echo ""
echo "Making 'nlist'"
make nlist CFLAGS="${CFLAGS}"
SHAR_EOF
fi
if test -f 'dnlc.sun'
then
echo shar: "will not over-write existing file 'dnlc.sun'"
else
cat << \SHAR_EOF > 'dnlc.sun'
#! /bin/sh
echo "Dumping 100 entries of the dnlc (ncache) structures,
following the 'hash_next' pointer"
nlist -ax -h1 -s 0 -f6x -l 10 \
'_ncache : Hshnxt' \
' : Hshprv' \
' : Lrunxt' \
' : Lruprv' \
' : Vp' \
' : Dp' \
'|1 : Len%3d' \
'|15 : Name%15s' \
' : Ucred' \
'@&1 = #1'
SHAR_EOF
fi
if test -f 'file.sun'
then
echo shar: "will not over-write existing file 'file.sun'"
else
cat << \SHAR_EOF > 'file.sun'
#! /bin/sh
echo "Dumping 10 entries of the file table. Note that _file is the ADDRESS of
the file table and not the file table itself"
# description (lines following the 'nlist' line)
# line 1: header
# line 2: read '_file' once
# line 3: set address of next field to content of previous field
# line 4: read that and increment by 26 next time
# line 5: read rest of structure with auto increment
nlist -a -s 0 -f7x -h 1 -l 10 \
'^. 1 + :" #" %3d' \
'_file!' \
'&+1!#-1' \
'>26 : flag %4o' \
'|2 : type %6x' \
'|2 : count %6d' \
'|2 : msgcount %6d' \
' : fops' \
' : fdata' \
' : offset %6x' \
' : cred' \
'&. = #-1 |2 : Crref %5d' \
'|2 : Uid %4u' \
'|2 : Gid %4u'
SHAR_EOF
fi
if test -f 'file.sys53'
then
echo shar: "will not over-write existing file 'file.sys53'"
else
cat << \SHAR_EOF > 'file.sys53'
#! /bin/sh
echo "Dumping 50 entries of the file table"
nlist -ap -s 0 -fd -h 2 -l 50 \
'^. 1 +: #%3d' \
'_file>12|1:Flag%4o' \
'&.=&-1 2+|2:Count%5u' \
':"Inode"%10x' \
':"Offset"%10d'
SHAR_EOF
fi
if test -f 'minfo.sys5'
then
echo shar: "will not over-write existing file 'minfo.sys5'"
else
cat << \SHAR_EOF > 'minfo.sys5'
#! /bin/sh
echo "Dumping the 'minfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f8d -s 2 \
'minfo : FreeM %5d' \
' : FreeS %5d' \
' : Vflt' \
' : Demd' \
' : Swap %5d' \
' : Cache' \
' : File' \
' : Pfault' \
' : CopyWr' \
' : Steal' \
' : Freed' \
' : UmodS' \
' : UmodF'
SHAR_EOF
fi
if test -f 'mount.sun'
then
echo shar: "will not over-write existing file 'mount.sun'"
else
cat << \SHAR_EOF > 'mount.sun'
#! /bin/sh
echo "Dumping 10 entries of the mount table"
nlist -p -aa -s 0 -f 10x -l 10 \
'^. 1 + : " #"%2d' \
'_mounttab>28 : Vfsp' \
' |1 : Maj %3o' \
' |1 : Min %3o' \
' : Devvp' \
' : Bufp' \
' : Qinod' \
' : Qflags |2' \
' : Btime %6d' \
' : Ftime %6d'
SHAR_EOF
fi
if test -f 'mount.sys53'
then
echo shar: "will not over-write existing file 'mount.sys53'"
else
cat << \SHAR_EOF > 'mount.sys53'
SHAR_EOF
fi
if test -f 'syserr.sys5'
then
echo shar: "will not over-write existing file 'syserr.sys5'"
else
cat << \SHAR_EOF > 'syserr.sys5'
#! /bin/sh
echo "Dumping the 'syserr' structure. Unique lines only, every 2 seconds"
nlist -atu -f7d -s 2 \
'syserr :InoOvf' \
' :FileOvf' \
' :TextOvf' \
' :ProcOvf' \
' :Silo' \
' :CrdRds' \
' :Alerts' \
' :Faults' \
' :Timeouts'
SHAR_EOF
fi
if test -f 'sysinfo1.sys5'
then
echo shar: "will not over-write existing file 'sysinfo1.sys5'"
else
cat << \SHAR_EOF > 'sysinfo1.sys5'
#! /bin/sh
echo "Dumping part of the 'sysinfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f8d -s 2 \
'sysinfo :CPU_IDLE' \
' :CPU_USER' \
' :CPU_KERN' \
' :CPU_WAIT' \
' :WAIT_IO' \
' :WAIT_SWP' \
' :WAIT_PIO'
SHAR_EOF
fi
if test -f 'sysinfo2.sys5'
then
echo shar: "will not over-write existing file 'sysinfo2.sys5'"
else
cat << \SHAR_EOF > 'sysinfo2.sys5'
#! /bin/sh
echo "Dumping part of the 'sysinfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f8d -s 2 \
'sysinfo+28 :Breads' \
' :Bwrites' \
' :Lreads' \
' :Lwrites' \
' :Phreads' \
' :Phwrites' \
' :Swapin' \
' :Swapout' \
' :Bswapin' \
' :Bswapout'
SHAR_EOF
fi
if test -f 'sysinfo3.sys5'
then
echo shar: "will not over-write existing file 'sysinfo3.sys5'"
else
cat << \SHAR_EOF > 'sysinfo3.sys5'
#! /bin/sh
echo "Dumping part of the 'sysinfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f8d -s 2 \
'sysinfo+68 :PSwtches' \
' :Syscall' \
' :SReads' \
' :SWrites' \
' :SForks %6d' \
' :SExecs %6d' \
' :RunQ' \
' :RunOcc' \
' :SwpQ %5d' \
' :SwpOcc %5d'
SHAR_EOF
fi
if test -f 'sysinfo4.sys5'
then
echo shar: "will not over-write existing file 'sysinfo4.sys5'"
else
cat << \SHAR_EOF > 'sysinfo4.sys5'
#! /bin/sh
echo "Dumping part of the 'sysinfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f9d -s 2 \
'sysinfo+108 :Iget' \
' :Namei' \
' :Dirblks' \
' :Readch' \
' :Writech' \
'#-2 100 * #-1 #-2 + / : "% Reads"
SHAR_EOF
fi
if test -f 'sysinfo5.sys5'
then
echo shar: "will not over-write existing file 'sysinfo5.sys5'"
else
cat << \SHAR_EOF > 'sysinfo5.sys5'
#! /bin/sh
echo "Dumping part of the 'sysinfo' structure. Unique lines only, every 2 seconds"
nlist -atu -f8d -s 2 \
'sysinfo+128 :RcvInts' \
' :XmtInts' \
' :MdmInts' \
' :RawCh' \
' :CanCh' \
' :OutCh' \
' :Msg %4d' \
' :Sema %4d'
SHAR_EOF
fi
if test -f 'sar-s'
then
echo shar: "will not over-write existing file 'sar-s'"
else
cat << \SHAR_EOF > 'sar-s'
#! /bin/sh
LOOPS=${1:-100}
echo "This will do what 'sar -s 1 1 ${LOOPS}' does"
echo "`uname -a` `date +%D`\n"
nlist -xat -f7u -s 1 -h1 -l ${LOOPS} \
'@sysinfo' \
'@' \
'@' \
'@' \
'@#1 ^1 -' \
'@#2 ^2 -' \
'@#3 ^3 -' \
'@#4 ^4 -' \
'@#-1 #-2 #-3 #-4 +++' \
'#6 100 * #-1 /: "%usr"' \
'#7 100 * #-2 /: "%sys"' \
'#8 100 * #-3 /: "%wio"' \
'#5 100 * #-4 /: "%idle"' | \
awk 'BEGIN {l=0; u=0; s=0; w=0; i=0} \
{printf "%8s %7s %7s %7s %7s\n", $1, $2, $3, $4, $5} \
$2 != "%usr" {l += 1; u += $2; s += $3; w += $4; i += $5} \
END {printf "\nAverage %7d %7d %7d %7d\n", u/l, s/l, w/l, i/l}'
SHAR_EOF
fi
exit 0
# End of shell archive
---------------------------------- cut here -------------------------------
--
Chris Bertin | -- CETIA -- 150, Av Marcelin Berthelot, Z.I. Toulon-Est
+33(94)212005 | 83088 Toulon Cedex, France
| inria!cetia!chris
More information about the Comp.sources.misc
mailing list