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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 1234 _mdb_init(void) 1235 { 1236 return (&modinfo); 1237 } 1238