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