stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
meem | ca9327a | 2008-05-08 16:56:26 -0700 | [diff] [blame^] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 7 | * |
| 8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| 9 | * or http://www.opensolaris.org/os/licensing. |
| 10 | * See the License for the specific language governing permissions |
| 11 | * and limitations under the License. |
| 12 | * |
| 13 | * When distributing Covered Code, include this CDDL HEADER in each |
| 14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| 15 | * If applicable, add the following below this CDDL HEADER, with the |
| 16 | * fields enclosed by brackets "[]" replaced with your own identifying |
| 17 | * information: Portions Copyright [yyyy] [name of copyright owner] |
| 18 | * |
| 19 | * CDDL HEADER END |
| 20 | */ |
| 21 | /* |
meem | ca9327a | 2008-05-08 16:56:26 -0700 | [diff] [blame^] | 22 | * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 23 | * Use is subject to license terms. |
| 24 | */ |
| 25 | |
| 26 | #pragma ident "%Z%%M% %I% %E% SMI" /* SVr3.2H */ |
| 27 | |
| 28 | /* |
| 29 | * Dump STREAMS module. Could be used anywhere on a stream to |
| 30 | * print all message headers and data on to the console. |
| 31 | */ |
| 32 | |
| 33 | #include <sys/types.h> |
| 34 | #include <sys/param.h> |
| 35 | #include <sys/systm.h> |
| 36 | #include <sys/stream.h> |
| 37 | #include <sys/stropts.h> |
| 38 | #include <sys/errno.h> |
| 39 | #include <sys/cmn_err.h> |
| 40 | #include <sys/ddi.h> |
| 41 | #include <sys/strsun.h> |
| 42 | |
| 43 | #include <sys/conf.h> |
| 44 | #include <sys/modctl.h> |
| 45 | |
| 46 | static char hdr[100]; /* current message header */ |
| 47 | static char hdrpad[100]; /* pad of same length as hdr[] */ |
| 48 | |
| 49 | /* |
| 50 | * Raw buffer dumping routine. Displays the contents of the first message in |
| 51 | * message chain `mp', using the "traditional" dump format. |
| 52 | * |
| 53 | * For instance, "Hello STREAMS, panicked lately?" would be displayed as: |
| 54 | * |
| 55 | * RD 30001dbb240 M_DATA 48656C6C 6F205354 5245414D 532C2070 Hello STREAMS, p |
| 56 | * 616E6963 6B656420 6C617465 6C793F anicked lately? |
| 57 | * |
| 58 | * If the character being displayed is not printable, a '.' is shown. |
| 59 | */ |
| 60 | |
| 61 | #define DEDUMP_HEXPERBLK 4 |
| 62 | #define DEDUMP_HEXLEN (sizeof ("11223344") * 4) |
| 63 | #define DEDUMP_ASCLEN (sizeof ("0123456789ABCDEF") - 1) |
| 64 | |
| 65 | static void |
| 66 | dedump_raw(mblk_t *mp) |
| 67 | { |
| 68 | char hex[DEDUMP_HEXLEN + 1], asc[DEDUMP_ASCLEN + 1]; |
| 69 | int hexi = 0, asci = 0, i = 0; |
| 70 | uchar_t c; |
| 71 | char *hdrp = hdr; |
| 72 | |
| 73 | hex[DEDUMP_HEXLEN] = '\0'; |
| 74 | |
| 75 | for (;;) { |
| 76 | if (i == MBLKL(mp) || (i != 0 && (i % DEDUMP_ASCLEN) == 0)) { |
| 77 | /* |
| 78 | * We're either out of data or we've filled a complete |
| 79 | * line. In either case, print out what we've got -- |
| 80 | * but first NUL-terminate asc[] and pad out hex[] |
| 81 | * with spaces. |
| 82 | */ |
| 83 | asc[asci] = '\0'; |
| 84 | (void) memset(hex + hexi, ' ', DEDUMP_HEXLEN - hexi); |
| 85 | (void) printf("%s %s %s\n", hdrp, hex, asc); |
| 86 | |
| 87 | /* |
| 88 | * If we're out of data, bail. Otherwise, reset asci |
| 89 | * and hexi for another lap around. Also, set hdrp to |
| 90 | * the pad since we only want to show the header once. |
| 91 | */ |
| 92 | if (i == MBLKL(mp)) |
| 93 | break; |
| 94 | asci = 0; |
| 95 | hexi = 0; |
| 96 | hdrp = hdrpad; |
| 97 | } |
| 98 | |
| 99 | c = mp->b_rptr[i++]; |
| 100 | |
| 101 | hexi += snprintf(hex + hexi, 3, "%02X", c); |
| 102 | if ((i % DEDUMP_HEXPERBLK) == 0) |
| 103 | hex[hexi++] = ' '; |
| 104 | asc[asci++] = (c >= 32 && c <= 126) ? c : '.'; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | static void |
| 109 | dedump_char(mblk_t *mp) |
| 110 | { |
| 111 | (void) printf("%s 0x%x\n", hdr, *(uchar_t *)mp->b_rptr); |
| 112 | } |
| 113 | |
| 114 | static void |
| 115 | dedump_int(mblk_t *mp) |
| 116 | { |
| 117 | (void) printf("%s %d\n", hdr, *(int *)mp->b_rptr); |
| 118 | } |
| 119 | |
| 120 | static void |
| 121 | dedump_ssize(mblk_t *mp) |
| 122 | { |
| 123 | (void) printf("%s %ld\n", hdr, *(ssize_t *)mp->b_rptr); |
| 124 | } |
| 125 | |
| 126 | static void |
meem | ca9327a | 2008-05-08 16:56:26 -0700 | [diff] [blame^] | 127 | dedump_cmdblk(mblk_t *mp) |
| 128 | { |
| 129 | struct cmdblk *cbp = (struct cmdblk *)mp->b_rptr; |
| 130 | |
| 131 | (void) printf("%s cmd %x cred %p len %u error %d\n", hdr, cbp->cb_cmd, |
| 132 | (void *)cbp->cb_cr, cbp->cb_len, cbp->cb_error); |
| 133 | } |
| 134 | |
| 135 | static void |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 136 | dedump_iocblk(mblk_t *mp) |
| 137 | { |
| 138 | struct iocblk *ic = (struct iocblk *)mp->b_rptr; |
| 139 | |
| 140 | (void) printf("%s cmd %x cred %p id %u flag %x count %ld rval %d " |
| 141 | "err %d\n", hdr, ic->ioc_cmd, (void *)ic->ioc_cr, ic->ioc_id, |
| 142 | ic->ioc_flag, ic->ioc_count, ic->ioc_rval, ic->ioc_error); |
| 143 | } |
| 144 | |
| 145 | static void |
| 146 | dedump_stroptions(mblk_t *mp) |
| 147 | { |
| 148 | struct stroptions *so = (struct stroptions *)mp->b_rptr; |
| 149 | |
| 150 | (void) printf("%s flag %x readopt %d wroff %u\n", hdr, |
| 151 | so->so_flags, so->so_readopt, so->so_wroff); |
| 152 | |
| 153 | (void) printf("%s minpsz %ld maxpsz %ld hiwat %lu lowat %lu\n", hdrpad, |
| 154 | so->so_minpsz, so->so_maxpsz, so->so_hiwat, so->so_lowat); |
| 155 | |
| 156 | (void) printf("%s band %u erropt %u maxblk %ld copyopt %u\n", hdrpad, |
| 157 | so->so_band, so->so_erropt, so->so_maxblk, so->so_copyopt); |
| 158 | } |
| 159 | |
| 160 | static void |
| 161 | dedump_copyreq(mblk_t *mp) |
| 162 | { |
| 163 | struct copyreq *cq = (struct copyreq *)mp->b_rptr; |
| 164 | |
| 165 | (void) printf("%s cmd %x cred %p id %u flag %x priv %p addr %p size " |
| 166 | "%lu\n", hdr, cq->cq_cmd, (void *)cq->cq_cr, cq->cq_id, cq->cq_flag, |
| 167 | (void *)cq->cq_private, (void *)cq->cq_addr, cq->cq_size); |
| 168 | } |
| 169 | |
| 170 | static void |
| 171 | dedump_copyresp(mblk_t *mp) |
| 172 | { |
| 173 | struct copyresp *cp = (struct copyresp *)mp->b_rptr; |
| 174 | |
| 175 | (void) printf("%s cmd %x cred %p id %u flag %x priv %p rval %p\n", hdr, |
| 176 | cp->cp_cmd, (void *)cp->cp_cr, cp->cp_id, cp->cp_flag, |
| 177 | (void *)cp->cp_private, (void *)cp->cp_rval); |
| 178 | } |
| 179 | |
| 180 | typedef struct msgfmt { |
| 181 | uchar_t m_type; |
| 182 | char m_desc[15]; |
| 183 | void (*m_print)(mblk_t *); |
| 184 | } msgfmt_t; |
| 185 | |
| 186 | static msgfmt_t msgfmt[256] = { |
| 187 | { M_DATA, "M_DATA ", dedump_raw }, |
| 188 | { M_PROTO, "M_PROTO ", dedump_raw }, |
| 189 | { M_BREAK, "M_BREAK ", dedump_raw }, |
| 190 | { M_PASSFP, "M_PASSFP ", dedump_raw }, |
| 191 | { M_EVENT, "M_EVENT ", dedump_raw }, |
| 192 | { M_SIG, "M_SIG ", dedump_char }, |
| 193 | { M_DELAY, "M_DELAY ", dedump_int }, |
| 194 | { M_CTL, "M_CTL ", dedump_raw }, |
| 195 | { M_IOCTL, "M_IOCTL ", dedump_iocblk }, |
| 196 | { M_SETOPTS, "M_SETOPTS ", dedump_stroptions }, |
| 197 | { M_RSE, "M_RSE ", dedump_raw }, |
| 198 | { M_IOCACK, "M_IOCACK ", dedump_iocblk }, |
| 199 | { M_IOCNAK, "M_IOCNAK ", dedump_iocblk }, |
| 200 | { M_PCPROTO, "M_PCPROTO ", dedump_raw }, |
| 201 | { M_PCSIG, "M_PCSIG ", dedump_char }, |
| 202 | { M_READ, "M_READ ", dedump_ssize }, |
| 203 | { M_FLUSH, "M_FLUSH ", dedump_char }, |
| 204 | { M_STOP, "M_STOP ", dedump_raw }, |
| 205 | { M_START, "M_START ", dedump_raw }, |
| 206 | { M_HANGUP, "M_HANGUP ", dedump_raw }, |
| 207 | { M_ERROR, "M_ERROR ", dedump_char }, |
| 208 | { M_COPYIN, "M_COPYIN ", dedump_copyreq }, |
| 209 | { M_COPYOUT, "M_COPYOUT ", dedump_copyreq }, |
| 210 | { M_IOCDATA, "M_IOCDATA ", dedump_copyresp }, |
| 211 | { M_PCRSE, "M_PCRSE ", dedump_raw }, |
| 212 | { M_STOPI, "M_STOPI ", dedump_raw }, |
| 213 | { M_STARTI, "M_STARTI ", dedump_raw }, |
| 214 | { M_PCEVENT, "M_PCEVENT ", dedump_raw }, |
| 215 | { M_UNHANGUP, "M_UNHANGUP", dedump_raw }, |
meem | ca9327a | 2008-05-08 16:56:26 -0700 | [diff] [blame^] | 216 | { M_CMD, "M_CMD ", dedump_cmdblk }, |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 217 | }; |
| 218 | |
| 219 | /*ARGSUSED1*/ |
| 220 | static int |
| 221 | dedumpopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp) |
| 222 | { |
| 223 | if (!sflag) |
| 224 | return (ENXIO); |
| 225 | |
| 226 | if (q->q_ptr) |
| 227 | return (0); /* already attached */ |
| 228 | |
| 229 | qprocson(q); |
| 230 | return (0); |
| 231 | } |
| 232 | |
| 233 | /*ARGSUSED1*/ |
| 234 | static int |
| 235 | dedumpclose(queue_t *q, int flag, cred_t *crp) |
| 236 | { |
| 237 | qprocsoff(q); |
| 238 | return (0); |
| 239 | } |
| 240 | |
| 241 | /* |
| 242 | * Common put procedure for upstream and downstream. |
| 243 | */ |
| 244 | static int |
| 245 | dedumpput(queue_t *q, mblk_t *mp) |
| 246 | { |
| 247 | unsigned char type = DB_TYPE(mp); |
| 248 | ssize_t hdrlen; |
| 249 | |
| 250 | hdrlen = snprintf(hdr, sizeof (hdr), "%s %p %10s ", |
| 251 | (q->q_flag & QREADR) ? "RD" : "WR", (void *)q, msgfmt[type].m_desc); |
| 252 | |
| 253 | hdrpad[hdrlen] = '\0'; |
| 254 | msgfmt[type].m_print(mp); |
| 255 | hdrpad[hdrlen] = ' '; |
| 256 | |
| 257 | putnext(q, mp); |
| 258 | return (0); |
| 259 | } |
| 260 | |
| 261 | struct module_info dedump_minfo = { |
| 262 | 0xaaa, "dedump", 0, INFPSZ, 0, 0 |
| 263 | }; |
| 264 | |
| 265 | struct qinit dedumprinit = { |
| 266 | dedumpput, NULL, dedumpopen, dedumpclose, NULL, &dedump_minfo, NULL |
| 267 | }; |
| 268 | |
| 269 | struct qinit dedumpwinit = { |
| 270 | dedumpput, NULL, NULL, NULL, NULL, &dedump_minfo, NULL |
| 271 | }; |
| 272 | |
| 273 | struct streamtab dedumpinfo = { |
| 274 | &dedumprinit, &dedumpwinit, NULL, NULL, |
| 275 | }; |
| 276 | |
| 277 | static struct fmodsw fsw = { |
| 278 | "dedump", |
| 279 | &dedumpinfo, |
| 280 | D_MP | D_MTPERMOD /* just to serialize printfs */ |
| 281 | }; |
| 282 | |
| 283 | static struct modlstrmod modlstrmod = { |
| 284 | &mod_strmodops, "dump streams module", &fsw |
| 285 | }; |
| 286 | |
| 287 | static struct modlinkage modlinkage = { |
| 288 | MODREV_1, &modlstrmod, NULL |
| 289 | }; |
| 290 | |
| 291 | int |
| 292 | _init(void) |
| 293 | { |
| 294 | int i; |
| 295 | msgfmt_t mf; |
| 296 | |
| 297 | /* |
| 298 | * Sort msgfmt[] so that msgfmt[n] describes message type n. |
| 299 | */ |
| 300 | for (i = 255; i != 0; i--) { |
| 301 | mf = msgfmt[i]; |
| 302 | msgfmt[i].m_type = i; |
| 303 | (void) sprintf(msgfmt[i].m_desc, "M_BOGUS_0x%x", i); |
| 304 | msgfmt[i].m_print = dedump_raw; |
| 305 | if (mf.m_desc[0] != 0) |
| 306 | msgfmt[mf.m_type] = mf; |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | * Fill hdrpad[] with as many spaces as will fit. |
| 311 | */ |
| 312 | (void) memset(hdrpad, ' ', sizeof (hdrpad) - 1); |
| 313 | hdrpad[sizeof (hdrpad) - 1] = '\0'; |
| 314 | |
| 315 | return (mod_install(&modlinkage)); |
| 316 | } |
| 317 | |
| 318 | int |
| 319 | _fini(void) |
| 320 | { |
| 321 | return (mod_remove(&modlinkage)); |
| 322 | } |
| 323 | |
| 324 | int |
| 325 | _info(struct modinfo *modinfop) |
| 326 | { |
| 327 | return (mod_info(&modlinkage, modinfop)); |
| 328 | } |