1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
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 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * NCA mdb module. Provides a collection of dcmds and walkers that
28 * operate on core NCA data structures. Dependencies on NCA internals
29 * are described in $SRC/uts/common/inet/nca/nca.h.
30 */
31
32 #include <mdb/mdb_modapi.h>
33 #include <mdb/mdb_ks.h>
34
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/sunddi.h>
38 #include <sys/processor.h>
39 #include <netinet/in.h>
40 #include <netinet/ip6.h> /* must come before common.h */
41 #include <inet/common.h> /* must come before led.h */
42 #include <inet/led.h> /* must come before ip.h */
43 #include <inet/ip.h> /* must come before tcp.h */
44 #include <inet/tcp.h> /* must come before nca/nca.h */
45 #include <inet/nca/nca.h>
46 #include <inet/nca/ncadoorhdr.h>
47
48 #define NCA_WALK_PLRU (void *)1
49 #define NCA_WALK_VLRU (void *)2
50 #define NCA_ADDR_WIDTH 11 /* kernel addresses *shouldn't* be wider */
51 #define YESNO(bool) ((bool) ? 'Y' : 'n')
52
53 /*
54 * Structure for assigning a name to a region of memory.
55 */
56 typedef struct {
57 const char *nm_name; /* name of region */
58 int nm_len; /* length to region */
59 uintptr_t nm_addr; /* starting address of region */
60 } namedmem_t;
61
62 /*
63 * Structure for giving a name to a constant.
64 */
65 typedef struct {
66 const char *const_name; /* name of constant */
67 int const_value; /* constant itself */
68 } constname_t;
69
70 /*
71 * Structure for mapping a bit to a name and a description. Instances
72 * of this datatype should always be arrays which decode bits in a
73 * number, and the index into the array should contain the description
74 * of a bit at position "index" in the number being decoded. The list
75 * must be terminated by an entry with a NULL `bit_name'.
76 */
77 typedef struct {
78 const char *bit_name; /* name of bit */
79 const char *bit_descr; /* description of bit's purpose */
80 } bitname_t;
81
82 /*
83 * Note: These should be defined in upside down order to their
84 * definitions in nca.h
85 * (Assumes that current ordering convention in nca.h will
86 * prevail for future additions)
87 */
88 static const bitname_t node_refs[] = {
89 { "REF_UNUSED", "0x00000001" },
90 { "REF_UNUSED", "0x00000002" },
91 { "REF_UNUSED", "0x00000004" },
92 { "REF_UNUSED", "0x00000008" },
93 { "REF_UNUSED", "0x00000010" },
94 { "REF_UNUSED", "0x00000020" },
95 { "REF_UNUSED", "0x00000040" },
96 { "REF_UNUSED", "0x00000080" },
97 { "REF_UNUSED", "0x00000100" },
98 { "REF_UNUSED", "0x00000200" },
99 { "REF_UNUSED", "0x00000400" },
100 { "REF_SEGMAP", "segmapped (PHYS|VIRT)" },
101 { "REF_NCAFS", "NCAfs required" },
102 { "REF_VNODE", "vnode hashed" },
103 { "REF_ERROR", "errored" },
104 { "REF_OWNED", "owned (won't be freed)" },
105 { "REF_UPCALL", "upcall not completed yet" },
106 { "REF_CTAG", "CTAG hashed" },
107 { "REF_PREEMPT", "processing preempted" },
108 { "REF_ONVLRU", "on virtual memory LRU list" },
109 { "REF_ONPLRU", "on physical memory LRU list" },
110 { "REF_MISS", "in miss processing" },
111 { "REF_NOLRU", "not safe for LRU reclaim" },
112 { "REF_RESP", "done parsing response header" },
113 { "REF_FILE", "reachable through filename hash" },
114 { "REF_SAFED", "not safe for use" },
115 { "REF_DONE", "done with miss processing" },
116 { "REF_KMEM", "content-backed via kmem_alloc()" },
117 { "REF_CKSUM", "checksum mapping in-use" },
118 { "REF_VIRT", "virtually mapped (data valid)" },
119 { "REF_PHYS", "physically mapped (pp valid)" },
120 { "REF_URI", "reachable through URI hash" },
121 { NULL }
122 };
123
124 static const bitname_t advise_types[] = {
125 { "ADVISE", "" },
126 { "ADVISE_REPLACE", "replace cached object with provided object" },
127 { "ADVISE_FLUSH", "flush cached object" },
128 { "ADVISE_TEMP", "return this object; keep cached object" },
129 { NULL }
130 };
131
132 /*
133 * Print `len' bytes of buffer `buf'. Handle nonprintable characters
134 * specially.
135 */
136 static void
printbuf(uint8_t * buf,size_t len)137 printbuf(uint8_t *buf, size_t len)
138 {
139 size_t i;
140
141 /*
142 * TODO: display octal form of unprintable characters in dim mode
143 * once mdb pager bug is fixed.
144 */
145 for (i = 0; i < len; i++)
146 mdb_printf(isgraph(buf[i]) ? "%c" : "\\%#o", buf[i]);
147
148 mdb_printf("\n");
149 }
150
151 /*
152 * Convert HTTP method operation `method' to a name.
153 */
154 static const char *
method2name(unsigned int method)155 method2name(unsigned int method)
156 {
157 unsigned int i;
158 static constname_t http_methods[] = {
159 { "NCA_UNKNOWN", NCA_UNKNOWN },
160 { "NCA_OPTIONS", NCA_OPTIONS },
161 { "NCA_GET", NCA_GET },
162 { "NCA_HEAD", NCA_HEAD },
163 { "NCA_POST", NCA_POST },
164 { "NCA_PUT", NCA_PUT },
165 { "NCA_DELETE", NCA_DELETE },
166 { "NCA_TRACE", NCA_TRACE },
167 { "NCA_RAW", NCA_RAW },
168 { NULL }
169 };
170
171 for (i = 0; http_methods[i].const_name != NULL; i++) {
172 if (method == http_methods[i].const_value)
173 return (http_methods[i].const_name);
174 }
175
176 return ("<unknown>");
177 }
178
179 /*
180 * Convert TCP state `state' to a name.
181 */
182 static const char *
state2name(int state)183 state2name(int state)
184 {
185 unsigned int i;
186 static constname_t tcp_states[] = {
187 { "CLOSED", TCPS_CLOSED },
188 { "IDLE", TCPS_IDLE },
189 { "BOUND", TCPS_BOUND },
190 { "LISTEN", TCPS_LISTEN },
191 { "SYN_SENT", TCPS_SYN_SENT },
192 { "SYN_RCVD", TCPS_SYN_RCVD },
193 { "ESTABLISHED", TCPS_ESTABLISHED },
194 { "CLOSE_WAIT", TCPS_CLOSE_WAIT },
195 { "FIN_WAIT1", TCPS_FIN_WAIT_1 },
196 { "FIN_WAIT2", TCPS_FIN_WAIT_2 },
197 { "CLOSING", TCPS_CLOSING },
198 { "LAST_ACK", TCPS_LAST_ACK },
199 { "TIME_WAIT", TCPS_TIME_WAIT },
200 { NULL }
201 };
202
203 for (i = 0; tcp_states[i].const_name != NULL; i++) {
204 if (state == tcp_states[i].const_value)
205 return (tcp_states[i].const_name);
206 }
207
208 return ("<unknown>");
209 }
210
211 /*
212 * Convert an nca_io2_t direct_type into a name.
213 */
214 static const char *
direct2name(unsigned int type)215 direct2name(unsigned int type)
216 {
217 unsigned int i;
218 static const constname_t direct_types[] = {
219 { "DIRECT_NONE", NCA_IO_DIRECT_NONE },
220 { "DIRECT_FILENAME", NCA_IO_DIRECT_FILENAME },
221 { "DIRECT_SHMSEG", NCA_IO_DIRECT_SHMSEG },
222 { "DIRECT_FILEDESC", NCA_IO_DIRECT_FILEDESC },
223 { "DIRECT_CTAG", NCA_IO_DIRECT_CTAG },
224 { "DIRECT_SPLICE", NCA_IO_DIRECT_SPLICE },
225 { "DIRECT_TEE", NCA_IO_DIRECT_TEE },
226 { "DIRECT_FILE_FD", NCA_IO_DIRECT_FILE_FD },
227 { NULL, 0 }
228 };
229
230 for (i = 0; direct_types[i].const_name != NULL; i++) {
231 if (type == direct_types[i].const_value)
232 return (direct_types[i].const_name);
233 }
234
235 return ("<unknown>");
236 }
237
238 /*
239 * Convert an nca_io2_t operation into a name.
240 */
241 static const char *
op2name(nca_op_t op)242 op2name(nca_op_t op)
243 {
244 unsigned int i;
245 static const constname_t op_types[] = {
246 { "http", http_op },
247 { "error", error_op },
248 { "error_retry", error_retry_op },
249 { "resource", resource_op },
250 { "timeout", timeout_op },
251 { "door_attach", door_attach_op },
252
253 { "log", log_op },
254 { "log_ok", log_ok_op },
255 { "log_error", log_error_op },
256 { "log_op_fiov", log_op_fiov },
257
258 { NULL, 0 }
259 };
260
261 for (i = 0; op_types[i].const_name != NULL; i++) {
262 if (op == op_types[i].const_value)
263 return (op_types[i].const_name);
264 }
265
266 return ("<unknown>");
267 }
268
269 /*
270 * Convert from ticks to milliseconds.
271 */
272 static uint64_t
tick2msec(uint64_t tick)273 tick2msec(uint64_t tick)
274 {
275 static int tick_per_msec;
276 static int msec_per_tick;
277 static int once;
278
279 if (once == 0) {
280 if (mdb_readvar(&tick_per_msec, "tick_per_msec") == -1) {
281 mdb_warn("cannot read symbol tick_per_msec");
282 return (0);
283 }
284 if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) {
285 mdb_warn("cannot read symbol msec_per_tick");
286 return (0);
287 }
288 once++;
289 }
290
291 return (tick_per_msec ? tick / tick_per_msec : tick * msec_per_tick);
292 }
293
294 /*
295 * Print the core fields in an nca_io2_t. With the "-v" argument,
296 * provide more verbose output. With the "-p" argument, print payload
297 * information.
298 */
299 static int
nca_io2(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)300 nca_io2(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
301 {
302 unsigned int i;
303 unsigned int payload_len;
304 uint64_t payload_output_max = 0;
305 unsigned int verbose = FALSE;
306 const int IO2_ADVDELT = NCA_ADDR_WIDTH + 1;
307 boolean_t arm;
308 nca_io2_t io2;
309 uint8_t *buf;
310 namedmem_t area[3];
311
312 if (!(flags & DCMD_ADDRSPEC))
313 return (DCMD_USAGE);
314
315 if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose,
316 'p', MDB_OPT_UINT64, &payload_output_max, NULL) != argc)
317 return (DCMD_USAGE);
318
319 if (!DCMD_HDRSPEC(flags) && verbose)
320 mdb_printf("\n\n");
321
322 if (DCMD_HDRSPEC(flags) || verbose) {
323 mdb_printf("%<u>%-*s %2s %4s %8s %*s %8s %16s %-12s%</u>\n",
324 NCA_ADDR_WIDTH, "ADDR", "AV", "MFNP", "TID",
325 NCA_ADDR_WIDTH, "CONN", "CONN_TAG", "CACHE_TAG",
326 "OPERATION");
327 }
328
329 if (mdb_vread(&io2, sizeof (nca_io2_t), addr) == -1) {
330 mdb_warn("cannot read nca_io2_t at %p", addr);
331 return (DCMD_ERR);
332 }
333
334 if (io2.version != NCA_HTTP_VERSION2)
335 mdb_warn("nca_io2_t at %p has incorrect version `%u'\n", addr,
336 io2.version);
337
338 mdb_printf("%0*p %02x %c%c%c%c %08x %0*llx %08x %016llx %s\n",
339 NCA_ADDR_WIDTH, addr, io2.advisory, YESNO(io2.more),
340 YESNO(io2.first), YESNO(io2.nocache), YESNO(io2.preempt),
341 (uint32_t)io2.tid, NCA_ADDR_WIDTH, io2.cid, io2.tag, io2.ctag,
342 op2name(io2.op));
343
344 if (verbose) {
345 arm = B_TRUE;
346 for (i = 0; advise_types[i].bit_name != NULL; i++) {
347 if ((io2.advisory & (1 << i)) == 0)
348 continue;
349
350 if (arm) {
351 mdb_printf("%*s|\n", IO2_ADVDELT, "");
352 mdb_printf("%*s+--> ", IO2_ADVDELT, "");
353 arm = B_FALSE;
354 } else
355 mdb_printf("%*s ", IO2_ADVDELT, "");
356
357 mdb_printf("%-15s %s\n", advise_types[i].bit_name,
358 advise_types[i].bit_descr);
359 }
360 }
361
362 payload_len = io2.data_len + io2.direct_len + io2.trailer_len;
363
364 if (payload_output_max == 0 || payload_len == 0)
365 return (DCMD_OK);
366
367 mdb_inc_indent(4);
368 mdb_printf("\n%u byte payload consists of:\n", payload_len);
369 mdb_inc_indent(4);
370
371 buf = mdb_alloc(payload_output_max, UM_SLEEP);
372
373 area[0].nm_name = "data";
374 area[0].nm_addr = addr + io2.data;
375 area[0].nm_len = io2.data_len;
376
377 area[1].nm_name = direct2name(io2.direct_type);
378 area[1].nm_addr = addr + io2.direct;
379 area[1].nm_len = io2.direct_len;
380
381 area[2].nm_name = "trailer";
382 area[2].nm_addr = addr + io2.trailer;
383 area[2].nm_len = io2.trailer_len;
384
385 for (i = 0; i < sizeof (area) / sizeof (area[0]); i++) {
386 if (area[i].nm_len <= 0)
387 continue;
388
389 mdb_printf("%d byte %s area at %p (", area[i].nm_len,
390 area[i].nm_name, area[i].nm_addr);
391
392 if (area[i].nm_len > payload_output_max) {
393 mdb_printf("first");
394 area[i].nm_len = (int)payload_output_max;
395 } else
396 mdb_printf("all");
397
398 mdb_printf(" %u bytes follow):\n", area[i].nm_len);
399 if (mdb_vread(buf, area[i].nm_len, area[i].nm_addr) == -1)
400 mdb_warn("cannot read %s area at %p", area[i].nm_name,
401 area[i].nm_addr);
402 else {
403 mdb_inc_indent(4);
404 printbuf(buf, area[i].nm_len);
405 mdb_dec_indent(4);
406 }
407 }
408 mdb_dec_indent(4);
409 mdb_dec_indent(4);
410
411 mdb_free(buf, payload_output_max);
412
413 return (DCMD_OK);
414 }
415
416 static void
nca_io2_help(void)417 nca_io2_help(void)
418 {
419 mdb_printf("Print the core information for a given NCA nca_io2_t.\n");
420 mdb_printf("Options:\n");
421 mdb_printf("\t-p N\tshow up to N bytes of payload information from\n");
422 mdb_printf("\t\teach payload area\n");
423 mdb_printf("\t\t(reminder: default radix is %<b>hex%</b>)\n");
424 mdb_printf("\t-v\tbe verbose (more descriptive)\n");
425 }
426
427 /*
428 * Print the core fields for one or all NCA timers. If no address is
429 * specified, all NCA timers are printed; otherwise the specified timer
430 * list is printed. With the "-e" argument, the "encapsulated" pointer
431 * for each te_t in a given tb_t is shown in parentheses.
432 */
433 static int
nca_timer(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)434 nca_timer(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
435 {
436 unsigned int show_encap = FALSE;
437 void *tb_addr, *te_addr;
438 clock_t lbolt, first_exec = 0;
439 ti_t ti;
440 tb_t tb;
441 te_t te;
442
443 if (!(flags & DCMD_ADDRSPEC)) {
444 if (mdb_walk_dcmd("nca_timer", "nca_timer", argc, argv) == -1) {
445 mdb_warn("cannot walk timer list");
446 return (DCMD_ERR);
447 }
448 return (DCMD_OK);
449 }
450
451 if (mdb_getopts(argc, argv, 'e', MDB_OPT_SETBITS, TRUE, &show_encap,
452 NULL) != argc)
453 return (DCMD_USAGE);
454
455 if (DCMD_HDRSPEC(flags)) {
456 mdb_printf("%<u>%-*s %-*s %-55s%</u>\n", NCA_ADDR_WIDTH, "TI",
457 NCA_ADDR_WIDTH, "SQUEUE", "FIRELIST +MSEC");
458 }
459
460 if (mdb_vread(&ti, sizeof (ti_t), addr) == -1) {
461 mdb_warn("cannot read ti_t at %p", addr);
462 return (DCMD_ERR);
463 }
464
465 if ((lbolt = (clock_t)mdb_get_lbolt()) == -1)
466 return (DCMD_ERR);
467
468 mdb_printf("%0*p %0*p", NCA_ADDR_WIDTH, addr, NCA_ADDR_WIDTH, ti.ep);
469 mdb_inc_indent(24);
470 for (tb_addr = ti.head; tb_addr != NULL; tb_addr = tb.next) {
471 if (mdb_vread(&tb, sizeof (tb_t), (uintptr_t)tb_addr) == -1) {
472 mdb_warn("cannot read tb_t at %p", tb_addr);
473 return (DCMD_ERR);
474 }
475 if (first_exec == 0) {
476 mdb_printf(" %ld", tick2msec(tb.exec - lbolt));
477 first_exec = tb.exec;
478 } else
479 mdb_printf(" %+lld", tick2msec(tb.exec - first_exec));
480
481 if (!show_encap || tb.head == NULL)
482 continue;
483
484 mdb_printf("(");
485 for (te_addr = tb.head; te_addr != NULL; te_addr = te.next) {
486 if (mdb_vread(&te, sizeof (te_t), (uintptr_t)te_addr)
487 == -1) {
488 mdb_warn("cannot read te_t at %p", te_addr);
489 return (DCMD_ERR);
490 }
491 mdb_printf("%0p%s", te.ep, te.next == NULL ? "" : " ");
492 }
493 mdb_printf(")");
494 }
495 mdb_printf("\n");
496 mdb_dec_indent(24);
497
498 return (DCMD_OK);
499 }
500
501 static void
nca_timer_help(void)502 nca_timer_help(void)
503 {
504 mdb_printf("Print the core information for one or all NCA timer\n");
505 mdb_printf("lists. If no timer list is given, then all timer lists\n");
506 mdb_printf("are shown. For each timer list, the list of timers to\n");
507 mdb_printf("fire on that list are shown, the first in absolute\n");
508 mdb_printf("ticks and the rest in ticks relative to the first.\n\n");
509 mdb_printf("Options:\n");
510 mdb_printf("\t-e\tshow the encapsulating pointer for each event ");
511 mdb_printf("at each fire time\n");
512 }
513
514 /*
515 * Print the core fields in an NCA node_t. With the "-r" argument,
516 * provide additional information about the request; with "-v",
517 * provide more verbose output.
518 */
519 static int
nca_node(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)520 nca_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
521 {
522 unsigned int i, max;
523 unsigned int verbose = FALSE;
524 unsigned int request = FALSE;
525 const int NODE_REFDELT = NCA_ADDR_WIDTH + 4 + 2;
526 boolean_t arm;
527 node_t node;
528 char *buf;
529 namedmem_t hdr[4];
530
531 if (!(flags & DCMD_ADDRSPEC))
532 return (DCMD_USAGE);
533
534 if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose,
535 'r', MDB_OPT_SETBITS, TRUE, &request, 'p', NULL) != argc)
536 return (DCMD_USAGE);
537
538 if (!DCMD_HDRSPEC(flags) && verbose)
539 mdb_printf("\n\n");
540
541 if (DCMD_HDRSPEC(flags) || verbose) {
542 mdb_printf("%<u>%-*s %4s %5s %8s %-*s %-*s %-*s %-*s%</u>\n",
543 NCA_ADDR_WIDTH, "ADDR", "REF", "STATE", "DATASIZE",
544 NCA_ADDR_WIDTH, "SQUEUE", NCA_ADDR_WIDTH, "REQUEST",
545 NCA_ADDR_WIDTH, "PLRUN", NCA_ADDR_WIDTH, "VLRUN");
546 }
547
548 if (mdb_vread(&node, sizeof (node_t), addr) == -1) {
549 mdb_warn("cannot read node_t at %p", addr);
550 return (DCMD_ERR);
551 }
552
553 mdb_printf("%0*p %4d %05x %8d %0*p %0*p %0*p %0*p\n",
554 NCA_ADDR_WIDTH, addr, node.cnt, node.ref,
555 node.datasz, NCA_ADDR_WIDTH, node.sqp, NCA_ADDR_WIDTH,
556 node.req, NCA_ADDR_WIDTH, node.plrunn, NCA_ADDR_WIDTH, node.vlrunn);
557
558 if (verbose) {
559 arm = B_TRUE;
560 for (i = 0; node_refs[i].bit_name != NULL; i++) {
561 if ((node.ref & (1 << i)) == 0)
562 continue;
563
564 if (arm) {
565 mdb_printf("%*s|\n", NODE_REFDELT, "");
566 mdb_printf("%*s+--> ", NODE_REFDELT, "");
567 arm = B_FALSE;
568 } else
569 mdb_printf("%*s ", NODE_REFDELT, "");
570
571 mdb_printf("%-12s %s\n", node_refs[i].bit_name,
572 node_refs[i].bit_descr);
573 }
574 }
575
576 if (!request || node.req == NULL)
577 return (DCMD_OK);
578
579 mdb_inc_indent(4);
580 mdb_printf("\n%u byte HTTP/%u.%u %s request (%u bytes in header, "
581 "%u in content)\n", node.reqsz, node.version >> 16,
582 node.version & 0xff, method2name(node.method), node.reqhdrsz,
583 node.reqcontl);
584
585 hdr[0].nm_name = "URI";
586 hdr[0].nm_addr = (uintptr_t)node.path;
587 hdr[0].nm_len = node.pathsz;
588
589 hdr[1].nm_name = "Accept";
590 hdr[1].nm_addr = (uintptr_t)node.reqaccept;
591 hdr[1].nm_len = node.reqacceptsz;
592
593 hdr[2].nm_name = "Accept-Language";
594 hdr[2].nm_addr = (uintptr_t)node.reqacceptl;
595 hdr[2].nm_len = node.reqacceptlsz;
596
597 hdr[3].nm_name = "Host";
598 hdr[3].nm_addr = (uintptr_t)node.reqhost;
599 hdr[3].nm_len = node.reqhostsz;
600
601 /*
602 * A little optimization. Allocate all of the necessary memory here,
603 * so we don't have to allocate on each loop iteration.
604 */
605
606 max = node.reqhdrsz;
607 for (i = 0; i < 4; i++)
608 max = MAX(max, hdr[i].nm_len);
609 max++;
610
611 buf = mdb_alloc(max, UM_SLEEP);
612
613 mdb_inc_indent(4);
614 for (i = 0; i < sizeof (hdr) / sizeof (hdr[0]); i++) {
615 if (hdr[i].nm_len <= 0)
616 continue;
617
618 if (mdb_vread(buf, hdr[i].nm_len, hdr[i].nm_addr) == -1) {
619 mdb_warn("cannot read \"%s\" header field at %p",
620 hdr[i].nm_name, hdr[i].nm_addr);
621 continue;
622 }
623 buf[hdr[i].nm_len] = '\0';
624
625 mdb_printf("%s: ", hdr[i].nm_name);
626 mdb_inc_indent(4);
627 mdb_printf("%s\n", buf);
628 mdb_dec_indent(4);
629 }
630
631 if (node.reqhdrsz > 0 && verbose) {
632 if (mdb_vread(buf, node.reqhdrsz, (uintptr_t)node.reqhdr) == -1)
633 mdb_warn("cannot read header at %p", node.reqhdr);
634 else {
635 mdb_printf("Raw header: ");
636 mdb_inc_indent(4);
637 printbuf((uint8_t *)buf, node.reqhdrsz);
638 mdb_dec_indent(4);
639 }
640 }
641 mdb_dec_indent(4);
642 mdb_dec_indent(4);
643
644 mdb_free(buf, max);
645
646 return (DCMD_OK);
647 }
648
649 static void
nca_node_help(void)650 nca_node_help(void)
651 {
652 mdb_printf("Print the core information for a given NCA node_t.\n\n");
653 mdb_printf("Options:\n");
654 mdb_printf("\t-r\tdisplay HTTP request information\n");
655 mdb_printf("\t-v\tbe verbose (more descriptive)\n");
656 }
657
658 /*
659 * Print the core fields in an NCA nca_conn_t. With the "-t" argument, skip
660 * all nca_conn_t's that are in the TIME_WAIT state. With the "-x" argument,
661 * show the xmit data.
662 */
663 static int
nca_conn(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)664 nca_conn(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
665 {
666 unsigned int i;
667 nca_conn_t conn;
668 unsigned int show_timewait = TRUE;
669 unsigned int show_xmit = FALSE;
670
671 if (!(flags & DCMD_ADDRSPEC))
672 return (DCMD_USAGE);
673
674 if (mdb_getopts(argc, argv, 'x', MDB_OPT_SETBITS, TRUE, &show_xmit,
675 't', MDB_OPT_CLRBITS, TRUE, &show_timewait, NULL) != argc)
676 return (DCMD_USAGE);
677
678 if (DCMD_HDRSPEC(flags)) {
679 mdb_printf("%<u>%-*s %3s %8s %15s %15s %-*s %-10s%</u>\n",
680 NCA_ADDR_WIDTH, "ADDR", "REF", "CREATE", "LOCAL_ADDR",
681 "REMOTE_ADDR", NCA_ADDR_WIDTH, "NODE", "STATE");
682 }
683
684 if (mdb_vread(&conn, sizeof (nca_conn_t), addr) == -1) {
685 mdb_warn("cannot read nca_conn_t at %p", addr);
686 return (DCMD_ERR);
687 }
688
689 if (!show_timewait && conn.tcp_state == TCPS_TIME_WAIT)
690 return (DCMD_OK);
691
692 mdb_printf("%0*p %3d %8lx %15I %15I %0*p %s\n", NCA_ADDR_WIDTH, addr,
693 conn.ref, conn.create, conn.laddr, conn.faddr, NCA_ADDR_WIDTH,
694 conn.req_np, state2name(conn.tcp_state));
695
696 if (show_xmit) {
697 mdb_inc_indent(4);
698
699 for (i = 0; i < TCP_XMIT_MAX_IX; i++) {
700 mdb_printf("xmit[%d]\n", i);
701 mdb_printf("\tref pointer\t\t%p\n", conn.xmit[i].np);
702 mdb_printf("\tdata pointer\t\t%p\n", conn.xmit[i].dp);
703 mdb_printf("\tcksum array\t\t%p\n", conn.xmit[i].cp);
704 mdb_printf("\tremaining xmit data\t%d\n",
705 conn.xmit[i].sz);
706 mdb_printf("\tref to node_t\t\t%p\n",
707 conn.xmit[i].refed);
708 mdb_printf("\tremaining segment data\t%d\n",
709 conn.xmit[i].dsz);
710 mdb_printf("\tvirtual pointer\t\t%p\n",
711 conn.xmit[i].dvp);
712 }
713
714 mdb_dec_indent(4);
715 }
716
717 return (DCMD_OK);
718 }
719
720 static void
nca_conn_help(void)721 nca_conn_help(void)
722 {
723 mdb_printf("Print the core information for a given NCA "
724 "nca_conn_t.\n\n");
725 mdb_printf("Options:\n");
726 mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
727 mdb_printf("\t-x\tshow TCP XMIT information\n");
728 }
729
730 /*
731 * Print the core TCP-related fields in an NCA nca_conn_t. With the "-t"
732 * argument, skips all nca_conn_t's that are in the TIME_WAIT state.
733 */
734 static int
nca_tcpconn(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)735 nca_tcpconn(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
736 {
737 nca_conn_t conn;
738 unsigned int show_timewait = TRUE;
739
740 if (!(flags & DCMD_ADDRSPEC))
741 return (DCMD_USAGE);
742
743 if (mdb_getopts(argc, argv, 't', MDB_OPT_CLRBITS, TRUE, &show_timewait,
744 NULL) != argc)
745 return (DCMD_USAGE);
746
747 if (DCMD_HDRSPEC(flags)) {
748 mdb_printf("%<u>%-*s %21s %5s %8s %5s %8s %5s %-9s%</u>\n",
749 NCA_ADDR_WIDTH, "ADDR", "REMOTE_ADDR", "SWIND", "SUNASEQ",
750 "SNSEQ", "RACKSEQ", "RNSEQ", "STATE");
751 }
752
753 if (mdb_vread(&conn, sizeof (nca_conn_t), addr) == -1) {
754 mdb_warn("cannot read nca_conn_t at %p", addr);
755 return (DCMD_ERR);
756 }
757
758 if (!show_timewait && conn.tcp_state == TCPS_TIME_WAIT)
759 return (DCMD_OK);
760
761 mdb_nhconvert(&conn.conn_fport, &conn.conn_fport, sizeof (in_port_t));
762
763 mdb_printf("%0*p %15I:%05hu %5u %08x %+5d %08x %+5d %-9s\n",
764 NCA_ADDR_WIDTH, addr, conn.faddr, conn.conn_fport, conn.tcp_swnd,
765 conn.tcp_suna, conn.tcp_snxt - conn.tcp_suna, conn.tcp_rack,
766 conn.tcp_rnxt - conn.tcp_rack, state2name(conn.tcp_state));
767
768 return (DCMD_OK);
769 }
770
771 static void
nca_tcpconn_help(void)772 nca_tcpconn_help(void)
773 {
774 mdb_printf("Print the core TCP-related information for a given ");
775 mdb_printf("NCA nca_conn_t.\n\n");
776 mdb_printf("Options:\n");
777 mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
778 }
779
780 /*
781 * Initialize a walk for the NCA connection fanout table. Note that
782 * local walks are not supported since they're more trouble than
783 * they're worth.
784 */
785 static int
nca_connf_walk_init(mdb_walk_state_t * wsp)786 nca_connf_walk_init(mdb_walk_state_t *wsp)
787 {
788 int fanout_size;
789
790 if (wsp->walk_addr != 0) {
791 mdb_warn("nca_connf_walk does not support local walks\n");
792 return (WALK_DONE);
793 }
794
795 if (mdb_readvar(&wsp->walk_addr, "nca_conn_fanout") == -1) {
796 mdb_warn("cannot read symbol nca_conn_fanout");
797 return (WALK_ERR);
798 }
799
800 if (mdb_readvar(&fanout_size, "nca_conn_fanout_size") == -1) {
801 mdb_warn("cannot read symbol nca_conn_fanout_size");
802 return (WALK_ERR);
803 }
804
805 wsp->walk_data = (void *)(uintptr_t)fanout_size;
806
807 return (WALK_NEXT);
808 }
809
810 /*
811 * Walk the NCA connection fanout table; `wsp->walk_data' is used to keep
812 * track of the number of indicies that are left to walk so we know when
813 * to stop.
814 */
815 static int
nca_connf_walk_step(mdb_walk_state_t * wsp)816 nca_connf_walk_step(mdb_walk_state_t *wsp)
817 {
818 connf_t connf;
819 nca_conn_t conn;
820 int status;
821 intptr_t i = (intptr_t)wsp->walk_data;
822
823 if (i-- <= 0)
824 return (WALK_DONE);
825
826 if (mdb_vread(&connf, sizeof (connf_t), wsp->walk_addr) == -1) {
827 mdb_warn("cannot read connf_t at %p", wsp->walk_addr);
828 return (WALK_ERR);
829 }
830
831 /*
832 * No point in walking the fanout if there are no
833 * connections in it.
834 */
835 if (connf.head != NULL) {
836 /*
837 * Point to the nca_conn_t instead of the connf_t so that output
838 * can be piped to ::nca_conn dcmd.
839 */
840 if (mdb_vread(&conn, sizeof (nca_conn_t),
841 (uintptr_t)connf.head) == -1) {
842 mdb_warn("cannot read nca_conn_t at %p", connf.head);
843 return (WALK_ERR);
844 }
845 status = wsp->walk_callback((uintptr_t)connf.head, &conn,
846 wsp->walk_cbdata);
847 } else {
848 status = WALK_NEXT;
849 }
850
851 wsp->walk_data = (void *)i;
852 wsp->walk_addr += sizeof (connf_t);
853
854 return (status);
855 }
856
857 /*
858 * Initialize a walk for the NCA node fanout tables. Note that local
859 * walks are not supported since they're more trouble than they're
860 * worth.
861 */
862 static int
nca_nodef_walk_init(mdb_walk_state_t * wsp)863 nca_nodef_walk_init(mdb_walk_state_t *wsp)
864 {
865 char varname[256];
866 uint32_t size;
867
868 if (wsp->walk_addr != 0) {
869 mdb_warn("nca_nodef_walk does not support local walks\n");
870 return (WALK_DONE);
871 }
872
873 if (mdb_readvar(&wsp->walk_addr, wsp->walk_arg) == -1) {
874 mdb_warn("cannot read symbol %s", wsp->walk_arg);
875 return (WALK_ERR);
876 }
877
878 mdb_snprintf(varname, sizeof (varname), "%s_sz", wsp->walk_arg);
879
880 if (mdb_readvar(&size, varname) == -1) {
881 mdb_warn("cannot read symbol %s", varname);
882 return (WALK_ERR);
883 }
884
885 wsp->walk_data = (void *)(uintptr_t)size;
886
887 return (WALK_NEXT);
888 }
889
890 /*
891 * Walk the NCA node fanout table; `wsp->walk_data' is used to keep
892 * track of the number of indicies that are left to walk so we know
893 * when to stop.
894 */
895 static int
nca_nodef_walk_step(mdb_walk_state_t * wsp)896 nca_nodef_walk_step(mdb_walk_state_t *wsp)
897 {
898 nodef_t nodef;
899 node_t node;
900 int status;
901 intptr_t i = (intptr_t)wsp->walk_data;
902
903 if (i-- <= 0)
904 return (WALK_DONE);
905
906 if (mdb_vread(&nodef, sizeof (nodef_t), wsp->walk_addr) == -1) {
907 mdb_warn("cannot read nodef_t at %p", wsp->walk_addr);
908 return (WALK_ERR);
909 }
910
911 status = wsp->walk_callback(wsp->walk_addr, &nodef, wsp->walk_cbdata);
912
913 wsp->walk_data = (void *)i;
914 wsp->walk_addr += sizeof (nodef_t);
915
916 if (nodef.head != NULL) {
917 /*
918 * Point to the node_t instead of the nodef_t so that output
919 * can be piped to ::nca_node dcmd.
920 */
921 if (mdb_vread(&node, sizeof (node),
922 (uintptr_t)nodef.head) == -1) {
923 mdb_warn("cannot read node_t at %p", nodef.head);
924 return (WALK_ERR);
925 }
926
927 status = wsp->walk_callback((uintptr_t)nodef.head,
928 &node, wsp->walk_cbdata);
929 } else {
930 status = WALK_NEXT;
931 }
932
933 return (status);
934 }
935
936 /*
937 * Initialize a walk for the NCA CPU table. Note that local walks
938 * are not supported since they're more trouble than they're worth.
939 */
940 static int
nca_cpu_walk_init(mdb_walk_state_t * wsp)941 nca_cpu_walk_init(mdb_walk_state_t *wsp)
942 {
943 int ncpus;
944
945 if (wsp->walk_addr != 0) {
946 mdb_warn("nca_cpu_walk does not support local walks\n");
947 return (WALK_DONE);
948 }
949
950 if (mdb_readvar(&wsp->walk_addr, "nca_gv") == -1) {
951 mdb_warn("cannot read symbol nca_gv");
952 return (WALK_ERR);
953 }
954
955 if (mdb_readvar(&ncpus, "ncpus") == -1) {
956 mdb_warn("cannot read symbol ncpus");
957 return (WALK_ERR);
958 }
959 wsp->walk_data = (void *)(uintptr_t)ncpus;
960
961 return (WALK_NEXT);
962 }
963
964 /*
965 * Walk the NCA CPU table; `wsp->walk_data' is used to keep track of the
966 * number of CPUs that are left to walk so we know when to stop.
967 */
968 static int
nca_cpu_walk_step(mdb_walk_state_t * wsp)969 nca_cpu_walk_step(mdb_walk_state_t *wsp)
970 {
971 nca_cpu_t cpu;
972 int status;
973 intptr_t curcpu = (intptr_t)wsp->walk_data;
974
975 if (curcpu-- <= 0)
976 return (WALK_DONE);
977
978 if (mdb_vread(&cpu, sizeof (nca_cpu_t), wsp->walk_addr) == -1) {
979 mdb_warn("cannot read nca_cpu_t at %p", wsp->walk_addr);
980 return (WALK_ERR);
981 }
982
983 status = wsp->walk_callback(wsp->walk_addr, &cpu, wsp->walk_cbdata);
984
985 wsp->walk_data = (void *)curcpu;
986 wsp->walk_addr += sizeof (nca_cpu_t);
987
988 return (status);
989 }
990
991 /*
992 * Initialize a walk for the NCA timer list. Note that local walks
993 * are not supported since this walk is layered on top of "nca_cpu"
994 * which doesn't support them (and they're not too useful here anyway).
995 */
996 static int
nca_timer_walk_init(mdb_walk_state_t * wsp)997 nca_timer_walk_init(mdb_walk_state_t *wsp)
998 {
999 if (wsp->walk_addr != 0) {
1000 mdb_warn("nca_timer_walk does not support local walks\n");
1001 return (WALK_DONE);
1002 }
1003
1004 if (mdb_layered_walk("nca_cpu", wsp) == -1) {
1005 mdb_warn("cannot walk nca_cpu");
1006 return (WALK_ERR);
1007 }
1008
1009 return (WALK_NEXT);
1010 }
1011
1012 /*
1013 * Walk the NCA timer list; done as a layered walk on top of "nca_cpu".
1014 */
1015 static int
nca_timer_walk_step(mdb_walk_state_t * wsp)1016 nca_timer_walk_step(mdb_walk_state_t *wsp)
1017 {
1018 const nca_cpu_t *nca_cpu = wsp->walk_layer;
1019 ti_t ti;
1020
1021 /*
1022 * Just skip CPUs that don't have any timers running.
1023 */
1024 if (nca_cpu->tcp_ti == NULL)
1025 return (WALK_NEXT);
1026
1027 if (mdb_vread(&ti, sizeof (ti_t), (uintptr_t)nca_cpu->tcp_ti) == -1) {
1028 mdb_warn("cannot read ti_t at %p", nca_cpu->tcp_ti);
1029 return (WALK_ERR);
1030 }
1031
1032 return (wsp->walk_callback((uintptr_t)nca_cpu->tcp_ti, &ti,
1033 wsp->walk_cbdata));
1034 }
1035
1036 /*
1037 * Initialize a walk for NCA node LRUs; the type of LRU to walk should
1038 * be specified through `wsp->walk_arg'. If no starting location for
1039 * the walk is given, `wsp->walk_addr' is set to the head of the
1040 * appropriate LRU.
1041 */
1042 static int
nca_node_lru_walk_init(mdb_walk_state_t * wsp)1043 nca_node_lru_walk_init(mdb_walk_state_t *wsp)
1044 {
1045 GElf_Sym sym;
1046 lru_t lru;
1047
1048 if (wsp->walk_addr != 0)
1049 return (WALK_NEXT);
1050
1051 /*
1052 * We do this instead of mdb_readvar() so that we catch changes
1053 * in the size of the lru_t structure.
1054 */
1055 if (mdb_lookup_by_name("nca_lru", &sym) == -1) {
1056 mdb_warn("cannot lookup symbol nca_lru");
1057 return (WALK_ERR);
1058 }
1059
1060 if (sym.st_size != sizeof (lru)) {
1061 mdb_warn("nca_lru object size mismatch\n");
1062 return (WALK_ERR);
1063 }
1064
1065 if (mdb_vread(&lru, sym.st_size, (uintptr_t)sym.st_value) == -1) {
1066 mdb_warn("cannot read nca_lru at %p", sym.st_value);
1067 return (WALK_ERR);
1068 }
1069
1070 if (wsp->walk_arg == NCA_WALK_PLRU)
1071 wsp->walk_addr = (uintptr_t)lru.phead;
1072 else
1073 wsp->walk_addr = (uintptr_t)lru.vhead;
1074
1075 return (WALK_NEXT);
1076 }
1077
1078 /*
1079 * Walk the NCA node LRUs; the type of LRU to walk should be specified
1080 * through `wsp->walk_arg'.
1081 */
1082 static int
nca_node_lru_walk_step(mdb_walk_state_t * wsp)1083 nca_node_lru_walk_step(mdb_walk_state_t *wsp)
1084 {
1085 node_t node;
1086 int status;
1087
1088 if (wsp->walk_addr == 0)
1089 return (WALK_DONE);
1090
1091 if (mdb_vread(&node, sizeof (node_t), wsp->walk_addr) == -1) {
1092 mdb_warn("cannot read node_t at %p", wsp->walk_addr);
1093 return (WALK_ERR);
1094 }
1095
1096 status = wsp->walk_callback(wsp->walk_addr, &node, wsp->walk_cbdata);
1097
1098 if (wsp->walk_arg == NCA_WALK_PLRU)
1099 wsp->walk_addr = (uintptr_t)node.plrunn;
1100 else
1101 wsp->walk_addr = (uintptr_t)node.vlrunn;
1102
1103 return (status);
1104 }
1105
1106 /*
1107 * Walk the NCA node structures; follows node_t next pointers from a
1108 * given offset, specified through `wsp->walk_arg'.
1109 */
1110 static int
nca_node_walk_step(mdb_walk_state_t * wsp)1111 nca_node_walk_step(mdb_walk_state_t *wsp)
1112 {
1113 node_t node;
1114 int status;
1115
1116 if (wsp->walk_addr == 0) {
1117 mdb_warn("nca_node_walk does not support global walks\n");
1118 return (WALK_DONE);
1119 }
1120
1121 if (mdb_vread(&node, sizeof (node_t), wsp->walk_addr) == -1) {
1122 mdb_warn("cannot read node_t at %p", wsp->walk_addr);
1123 return (WALK_ERR);
1124 }
1125
1126 status = wsp->walk_callback(wsp->walk_addr, &node, wsp->walk_cbdata);
1127 if (status != WALK_NEXT)
1128 return (status);
1129
1130 /* LINTED */
1131 wsp->walk_addr = *(uintptr_t *)((caddr_t)&node +
1132 (uint_t)(uintptr_t)wsp->walk_arg);
1133
1134 if (wsp->walk_addr == 0)
1135 return (WALK_DONE);
1136
1137 return (WALK_NEXT);
1138 }
1139
1140 /*
1141 * Walk the NCA connection structures; follows nca_conn_t next pointers
1142 * from a given offset, specified through `wsp->walk_arg'.
1143 */
1144 static int
nca_conn_walk_step(mdb_walk_state_t * wsp)1145 nca_conn_walk_step(mdb_walk_state_t *wsp)
1146 {
1147 nca_conn_t conn;
1148 int status;
1149
1150 if (wsp->walk_addr == 0) {
1151 mdb_warn("nca_conn_walk does not support global walks\n");
1152 return (WALK_DONE);
1153 }
1154
1155 if (mdb_vread(&conn, sizeof (nca_conn_t), wsp->walk_addr) == -1) {
1156 mdb_warn("cannot read nca_conn_t at %p", wsp->walk_addr);
1157 return (WALK_ERR);
1158 }
1159
1160 status = wsp->walk_callback(wsp->walk_addr, &conn, wsp->walk_cbdata);
1161 if (status != WALK_NEXT)
1162 return (status);
1163
1164 /* LINTED */
1165 wsp->walk_addr = *(uintptr_t *)((caddr_t)&conn +
1166 (uint_t)(uintptr_t)wsp->walk_arg);
1167
1168 if (wsp->walk_addr == 0)
1169 return (WALK_DONE);
1170
1171 return (WALK_NEXT);
1172 }
1173
1174 static const mdb_dcmd_t dcmds[] = {
1175 { "nca_conn", ":[-tx]", "print core NCA nca_conn_t info", nca_conn,
1176 nca_conn_help },
1177 { "nca_tcpconn", ":[-t]", "print TCP NCA nca_conn_t info",
1178 nca_tcpconn, nca_tcpconn_help },
1179 { "nca_io2", ":[-pv]", "print core NCA io2_t info", nca_io2,
1180 nca_io2_help },
1181 { "nca_node", ":[-rv]", "print core NCA node_t info", nca_node,
1182 nca_node_help },
1183 { "nca_timer", "?[-e]", "print core NCA timer info", nca_timer,
1184 nca_timer_help },
1185 { NULL }
1186 };
1187
1188 static const mdb_walker_t walkers[] = {
1189 { "nca_conn_hash", "walk the NCA connection hash chain", 0,
1190 nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, hashnext) },
1191 { "nca_conn_bind", "walk the NCA connection bind chain", 0,
1192 nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, bindnext) },
1193 { "nca_conn_miss", "walk the NCA connection miss chain", 0,
1194 nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, nodenext) },
1195 { "nca_conn_tw", "walk the NCA connection TIME_WAIT chain", 0,
1196 nca_conn_walk_step, 0, (void *)OFFSETOF(nca_conn_t, twnext) },
1197
1198 { "nca_node_file", "walk the NCA node file chain", 0,
1199 nca_node_walk_step, 0, (void *)OFFSETOF(node_t, filenext) },
1200 { "nca_node_hash", "walk the NCA node hash chain", 0,
1201 nca_node_walk_step, 0, (void *)OFFSETOF(node_t, hashnext) },
1202 { "nca_node_chunk", "walk the NCA node chunk chain", 0,
1203 nca_node_walk_step, 0, (void *)OFFSETOF(node_t, next) },
1204 { "nca_node_ctag", "walk the NCA node ctag chain", 0,
1205 nca_node_walk_step, 0, (void *)OFFSETOF(node_t, ctagnext) },
1206
1207 { "nca_node_plru", "walk the NCA node physical LRU chain",
1208 nca_node_lru_walk_init, nca_node_lru_walk_step, 0, NCA_WALK_PLRU },
1209 { "nca_node_vlru", "walk the NCA node virtual LRU chain",
1210 nca_node_lru_walk_init, nca_node_lru_walk_step, 0, NCA_WALK_VLRU },
1211
1212 { "nca_uri_hash", "walk the NCA URI node hash table",
1213 nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncaurihash" },
1214 { "nca_file_hash", "walk the NCA file node hash table",
1215 nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncafilehash" },
1216 { "nca_ctag_hash", "walk the NCA ctag node hash table",
1217 nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncactaghash" },
1218 { "nca_vnode_hash", "walk the NCA vnode node hash table",
1219 nca_nodef_walk_init, nca_nodef_walk_step, 0, "ncavnodehash" },
1220
1221 { "nca_cpu", "walk the NCA CPU table",
1222 nca_cpu_walk_init, nca_cpu_walk_step },
1223 { "nca_timer", "walk the NCA timer table",
1224 nca_timer_walk_init, nca_timer_walk_step },
1225 { "nca_connf", "walk the NCA connection fanout",
1226 nca_connf_walk_init, nca_connf_walk_step },
1227
1228 { NULL }
1229 };
1230
1231 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1232
1233 const mdb_modinfo_t *
_mdb_init(void)1234 _mdb_init(void)
1235 {
1236 return (&modinfo);
1237 }
1238