/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* NCA mdb module. Provides a collection of dcmds and walkers that
* operate on core NCA data structures. Dependencies on NCA internals
*/
#include <mdb/mdb_modapi.h>
#include <ctype.h>
#include <sys/processor.h>
/*
* Structure for assigning a name to a region of memory.
*/
typedef struct {
} namedmem_t;
/*
* Structure for giving a name to a constant.
*/
typedef struct {
} constname_t;
/*
* Structure for mapping a bit to a name and a description. Instances
* of this datatype should always be arrays which decode bits in a
* number, and the index into the array should contain the description
* of a bit at position "index" in the number being decoded. The list
* must be terminated by an entry with a NULL `bit_name'.
*/
typedef struct {
} bitname_t;
/*
* Note: These should be defined in upside down order to their
* definitions in nca.h
* (Assumes that current ordering convention in nca.h will
* prevail for future additions)
*/
{ "REF_UNUSED", "0x00000001" },
{ "REF_UNUSED", "0x00000002" },
{ "REF_UNUSED", "0x00000004" },
{ "REF_UNUSED", "0x00000008" },
{ "REF_UNUSED", "0x00000010" },
{ "REF_UNUSED", "0x00000020" },
{ "REF_UNUSED", "0x00000040" },
{ "REF_UNUSED", "0x00000080" },
{ "REF_UNUSED", "0x00000100" },
{ "REF_UNUSED", "0x00000200" },
{ "REF_UNUSED", "0x00000400" },
{ "REF_SEGMAP", "segmapped (PHYS|VIRT)" },
{ "REF_NCAFS", "NCAfs required" },
{ "REF_VNODE", "vnode hashed" },
{ "REF_ERROR", "errored" },
{ "REF_OWNED", "owned (won't be freed)" },
{ "REF_UPCALL", "upcall not completed yet" },
{ "REF_CTAG", "CTAG hashed" },
{ "REF_PREEMPT", "processing preempted" },
{ "REF_ONVLRU", "on virtual memory LRU list" },
{ "REF_ONPLRU", "on physical memory LRU list" },
{ "REF_MISS", "in miss processing" },
{ "REF_NOLRU", "not safe for LRU reclaim" },
{ "REF_RESP", "done parsing response header" },
{ "REF_FILE", "reachable through filename hash" },
{ "REF_SAFED", "not safe for use" },
{ "REF_DONE", "done with miss processing" },
{ "REF_KMEM", "content-backed via kmem_alloc()" },
{ "REF_CKSUM", "checksum mapping in-use" },
{ "REF_VIRT", "virtually mapped (data valid)" },
{ "REF_PHYS", "physically mapped (pp valid)" },
{ "REF_URI", "reachable through URI hash" },
{ NULL }
};
{ "ADVISE", "" },
{ "ADVISE_REPLACE", "replace cached object with provided object" },
{ "ADVISE_FLUSH", "flush cached object" },
{ "ADVISE_TEMP", "return this object; keep cached object" },
{ NULL }
};
/*
* Print `len' bytes of buffer `buf'. Handle nonprintable characters
* specially.
*/
static void
{
size_t i;
/*
* TODO: display octal form of unprintable characters in dim mode
* once mdb pager bug is fixed.
*/
for (i = 0; i < len; i++)
mdb_printf("\n");
}
/*
* Convert HTTP method operation `method' to a name.
*/
static const char *
{
unsigned int i;
{ "NCA_UNKNOWN", NCA_UNKNOWN },
{ "NCA_OPTIONS", NCA_OPTIONS },
{ "NCA_GET", NCA_GET },
{ "NCA_HEAD", NCA_HEAD },
{ "NCA_POST", NCA_POST },
{ "NCA_PUT", NCA_PUT },
{ "NCA_DELETE", NCA_DELETE },
{ "NCA_TRACE", NCA_TRACE },
{ "NCA_RAW", NCA_RAW },
{ NULL }
};
return (http_methods[i].const_name);
}
return ("<unknown>");
}
/*
* Convert TCP state `state' to a name.
*/
static const char *
{
unsigned int i;
{ "CLOSED", TCPS_CLOSED },
{ "IDLE", TCPS_IDLE },
{ "BOUND", TCPS_BOUND },
{ "LISTEN", TCPS_LISTEN },
{ "SYN_SENT", TCPS_SYN_SENT },
{ "SYN_RCVD", TCPS_SYN_RCVD },
{ "ESTABLISHED", TCPS_ESTABLISHED },
{ "CLOSE_WAIT", TCPS_CLOSE_WAIT },
{ "FIN_WAIT1", TCPS_FIN_WAIT_1 },
{ "FIN_WAIT2", TCPS_FIN_WAIT_2 },
{ "CLOSING", TCPS_CLOSING },
{ "LAST_ACK", TCPS_LAST_ACK },
{ "TIME_WAIT", TCPS_TIME_WAIT },
{ NULL }
};
return (tcp_states[i].const_name);
}
return ("<unknown>");
}
/*
* Convert an nca_io2_t direct_type into a name.
*/
static const char *
{
unsigned int i;
{ "DIRECT_NONE", NCA_IO_DIRECT_NONE },
{ "DIRECT_FILENAME", NCA_IO_DIRECT_FILENAME },
{ "DIRECT_SHMSEG", NCA_IO_DIRECT_SHMSEG },
{ "DIRECT_FILEDESC", NCA_IO_DIRECT_FILEDESC },
{ "DIRECT_CTAG", NCA_IO_DIRECT_CTAG },
{ "DIRECT_SPLICE", NCA_IO_DIRECT_SPLICE },
{ "DIRECT_TEE", NCA_IO_DIRECT_TEE },
{ "DIRECT_FILE_FD", NCA_IO_DIRECT_FILE_FD },
{ NULL, 0 }
};
return (direct_types[i].const_name);
}
return ("<unknown>");
}
/*
* Convert an nca_io2_t operation into a name.
*/
static const char *
{
unsigned int i;
{ "http", http_op },
{ "error", error_op },
{ "error_retry", error_retry_op },
{ "resource", resource_op },
{ "timeout", timeout_op },
{ "door_attach", door_attach_op },
{ "log", log_op },
{ "log_ok", log_ok_op },
{ "log_error", log_error_op },
{ "log_op_fiov", log_op_fiov },
{ NULL, 0 }
};
return (op_types[i].const_name);
}
return ("<unknown>");
}
/*
* Convert from ticks to milliseconds.
*/
static uint64_t
{
static int tick_per_msec;
static int msec_per_tick;
static int once;
if (once == 0) {
mdb_warn("cannot read symbol tick_per_msec");
return (0);
}
mdb_warn("cannot read symbol msec_per_tick");
return (0);
}
once++;
}
}
/*
* Print the core fields in an nca_io2_t. With the "-v" argument,
* provide more verbose output. With the "-p" argument, print payload
* information.
*/
static int
{
unsigned int i;
unsigned int payload_len;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
mdb_printf("\n\n");
mdb_printf("%<u>%-*s %2s %4s %8s %*s %8s %16s %-12s%</u>\n",
"OPERATION");
}
return (DCMD_ERR);
}
mdb_printf("%0*p %02x %c%c%c%c %08x %0*llx %08x %016llx %s\n",
if (verbose) {
continue;
if (arm) {
} else
advise_types[i].bit_descr);
}
}
if (payload_output_max == 0 || payload_len == 0)
return (DCMD_OK);
mdb_inc_indent(4);
mdb_inc_indent(4);
continue;
mdb_printf("first");
} else
mdb_printf("all");
else {
mdb_inc_indent(4);
mdb_dec_indent(4);
}
}
mdb_dec_indent(4);
mdb_dec_indent(4);
return (DCMD_OK);
}
static void
nca_io2_help(void)
{
mdb_printf("Print the core information for a given NCA nca_io2_t.\n");
mdb_printf("Options:\n");
mdb_printf("\t-p N\tshow up to N bytes of payload information from\n");
mdb_printf("\t\teach payload area\n");
mdb_printf("\t\t(reminder: default radix is %<b>hex%</b>)\n");
mdb_printf("\t-v\tbe verbose (more descriptive)\n");
}
/*
* Print the core fields for one or all NCA timers. If no address is
* specified, all NCA timers are printed; otherwise the specified timer
* list is printed. With the "-e" argument, the "encapsulated" pointer
* for each te_t in a given tb_t is shown in parentheses.
*/
static int
{
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("cannot walk timer list");
return (DCMD_ERR);
}
return (DCMD_OK);
}
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
}
return (DCMD_ERR);
}
return (DCMD_ERR);
mdb_inc_indent(24);
return (DCMD_ERR);
}
if (first_exec == 0) {
} else
continue;
mdb_printf("(");
== -1) {
return (DCMD_ERR);
}
}
mdb_printf(")");
}
mdb_printf("\n");
mdb_dec_indent(24);
return (DCMD_OK);
}
static void
nca_timer_help(void)
{
mdb_printf("Print the core information for one or all NCA timer\n");
mdb_printf("lists. If no timer list is given, then all timer lists\n");
mdb_printf("are shown. For each timer list, the list of timers to\n");
mdb_printf("fire on that list are shown, the first in absolute\n");
mdb_printf("ticks and the rest in ticks relative to the first.\n\n");
mdb_printf("Options:\n");
mdb_printf("\t-e\tshow the encapsulating pointer for each event ");
mdb_printf("at each fire time\n");
}
/*
* Print the core fields in an NCA node_t. With the "-r" argument,
* provide additional information about the request; with "-v",
* provide more verbose output.
*/
static int
{
unsigned int i, max;
char *buf;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
mdb_printf("\n\n");
mdb_printf("%<u>%-*s %4s %5s %8s %-*s %-*s %-*s %-*s%</u>\n",
}
return (DCMD_ERR);
}
mdb_printf("%0*p %4d %05x %8d %0*p %0*p %0*p %0*p\n",
if (verbose) {
continue;
if (arm) {
} else
}
}
return (DCMD_OK);
mdb_inc_indent(4);
mdb_printf("\n%u byte HTTP/%u.%u %s request (%u bytes in header, "
/*
* A little optimization. Allocate all of the necessary memory here,
* so we don't have to allocate on each loop iteration.
*/
for (i = 0; i < 4; i++)
max++;
mdb_inc_indent(4);
continue;
mdb_warn("cannot read \"%s\" header field at %p",
continue;
}
mdb_inc_indent(4);
mdb_dec_indent(4);
}
else {
mdb_printf("Raw header: ");
mdb_inc_indent(4);
mdb_dec_indent(4);
}
}
mdb_dec_indent(4);
mdb_dec_indent(4);
return (DCMD_OK);
}
static void
nca_node_help(void)
{
mdb_printf("Print the core information for a given NCA node_t.\n\n");
mdb_printf("Options:\n");
mdb_printf("\t-r\tdisplay HTTP request information\n");
mdb_printf("\t-v\tbe verbose (more descriptive)\n");
}
/*
* Print the core fields in an NCA nca_conn_t. With the "-t" argument, skip
* all nca_conn_t's that are in the TIME_WAIT state. With the "-x" argument,
* show the xmit data.
*/
static int
{
unsigned int i;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%-*s %3s %8s %15s %15s %-*s %-10s%</u>\n",
}
return (DCMD_ERR);
}
return (DCMD_OK);
if (show_xmit) {
mdb_inc_indent(4);
for (i = 0; i < TCP_XMIT_MAX_IX; i++) {
mdb_printf("xmit[%d]\n", i);
mdb_printf("\tremaining xmit data\t%d\n",
mdb_printf("\tref to node_t\t\t%p\n",
mdb_printf("\tremaining segment data\t%d\n",
mdb_printf("\tvirtual pointer\t\t%p\n",
}
mdb_dec_indent(4);
}
return (DCMD_OK);
}
static void
nca_conn_help(void)
{
mdb_printf("Print the core information for a given NCA "
"nca_conn_t.\n\n");
mdb_printf("Options:\n");
mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
mdb_printf("\t-x\tshow TCP XMIT information\n");
}
/*
* Print the core TCP-related fields in an NCA nca_conn_t. With the "-t"
* argument, skips all nca_conn_t's that are in the TIME_WAIT state.
*/
static int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%-*s %21s %5s %8s %5s %8s %5s %-9s%</u>\n",
"SNSEQ", "RACKSEQ", "RNSEQ", "STATE");
}
return (DCMD_ERR);
}
return (DCMD_OK);
mdb_printf("%0*p %15I:%05hu %5u %08x %+5d %08x %+5d %-9s\n",
return (DCMD_OK);
}
static void
nca_tcpconn_help(void)
{
mdb_printf("Print the core TCP-related information for a given ");
mdb_printf("NCA nca_conn_t.\n\n");
mdb_printf("Options:\n");
mdb_printf("\t-t\tskip connections in the TIME_WAIT state\n");
}
/*
* Initialize a walk for the NCA connection fanout table. Note that
* local walks are not supported since they're more trouble than
* they're worth.
*/
static int
{
int fanout_size;
mdb_warn("nca_connf_walk does not support local walks\n");
return (WALK_DONE);
}
mdb_warn("cannot read symbol nca_conn_fanout");
return (WALK_ERR);
}
mdb_warn("cannot read symbol nca_conn_fanout_size");
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* Walk the NCA connection fanout table; `wsp->walk_data' is used to keep
* track of the number of indicies that are left to walk so we know when
* to stop.
*/
static int
{
int status;
if (i-- <= 0)
return (WALK_DONE);
return (WALK_ERR);
}
/*
* No point in walking the fanout if there are no
* connections in it.
*/
/*
* Point to the nca_conn_t instead of the connf_t so that output
* can be piped to ::nca_conn dcmd.
*/
return (WALK_ERR);
}
wsp->walk_cbdata);
} else {
}
return (status);
}
/*
* Initialize a walk for the NCA node fanout tables. Note that local
* walks are not supported since they're more trouble than they're
* worth.
*/
static int
{
mdb_warn("nca_nodef_walk does not support local walks\n");
return (WALK_DONE);
}
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* Walk the NCA node fanout table; `wsp->walk_data' is used to keep
* track of the number of indicies that are left to walk so we know
* when to stop.
*/
static int
{
int status;
if (i-- <= 0)
return (WALK_DONE);
return (WALK_ERR);
}
/*
* Point to the node_t instead of the nodef_t so that output
* can be piped to ::nca_node dcmd.
*/
return (WALK_ERR);
}
} else {
}
return (status);
}
/*
* Initialize a walk for the NCA CPU table. Note that local walks
* are not supported since they're more trouble than they're worth.
*/
static int
{
int ncpus;
mdb_warn("nca_cpu_walk does not support local walks\n");
return (WALK_DONE);
}
mdb_warn("cannot read symbol nca_gv");
return (WALK_ERR);
}
mdb_warn("cannot read symbol ncpus");
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* Walk the NCA CPU table; `wsp->walk_data' is used to keep track of the
* number of CPUs that are left to walk so we know when to stop.
*/
static int
{
int status;
if (curcpu-- <= 0)
return (WALK_DONE);
return (WALK_ERR);
}
return (status);
}
/*
* Initialize a walk for the NCA timer list. Note that local walks
* are not supported since this walk is layered on top of "nca_cpu"
* which doesn't support them (and they're not too useful here anyway).
*/
static int
{
mdb_warn("nca_timer_walk does not support local walks\n");
return (WALK_DONE);
}
mdb_warn("cannot walk nca_cpu");
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* Walk the NCA timer list; done as a layered walk on top of "nca_cpu".
*/
static int
{
/*
* Just skip CPUs that don't have any timers running.
*/
return (WALK_NEXT);
return (WALK_ERR);
}
wsp->walk_cbdata));
}
/*
* Initialize a walk for NCA node LRUs; the type of LRU to walk should
* be specified through `wsp->walk_arg'. If no starting location for
* the walk is given, `wsp->walk_addr' is set to the head of the
* appropriate LRU.
*/
static int
{
return (WALK_NEXT);
/*
* We do this instead of mdb_readvar() so that we catch changes
* in the size of the lru_t structure.
*/
mdb_warn("cannot lookup symbol nca_lru");
return (WALK_ERR);
}
mdb_warn("nca_lru object size mismatch\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
else
return (WALK_NEXT);
}
/*
* Walk the NCA node LRUs; the type of LRU to walk should be specified
* through `wsp->walk_arg'.
*/
static int
{
int status;
return (WALK_DONE);
return (WALK_ERR);
}
else
return (status);
}
/*
* Walk the NCA node structures; follows node_t next pointers from a
* given offset, specified through `wsp->walk_arg'.
*/
static int
{
int status;
mdb_warn("nca_node_walk does not support global walks\n");
return (WALK_DONE);
}
return (WALK_ERR);
}
return (status);
/* LINTED */
return (WALK_DONE);
return (WALK_NEXT);
}
/*
* Walk the NCA connection structures; follows nca_conn_t next pointers
* from a given offset, specified through `wsp->walk_arg'.
*/
static int
{
int status;
mdb_warn("nca_conn_walk does not support global walks\n");
return (WALK_DONE);
}
return (WALK_ERR);
}
return (status);
/* LINTED */
return (WALK_DONE);
return (WALK_NEXT);
}
{ "nca_tcpconn", ":[-t]", "print TCP NCA nca_conn_t info",
nca_io2_help },
{ NULL }
};
{ "nca_conn_hash", "walk the NCA connection hash chain", 0,
{ "nca_conn_bind", "walk the NCA connection bind chain", 0,
{ "nca_conn_miss", "walk the NCA connection miss chain", 0,
{ "nca_conn_tw", "walk the NCA connection TIME_WAIT chain", 0,
{ "nca_node_file", "walk the NCA node file chain", 0,
{ "nca_node_hash", "walk the NCA node hash chain", 0,
{ "nca_node_chunk", "walk the NCA node chunk chain", 0,
{ "nca_node_ctag", "walk the NCA node ctag chain", 0,
{ "nca_node_plru", "walk the NCA node physical LRU chain",
{ "nca_node_vlru", "walk the NCA node virtual LRU chain",
{ "nca_uri_hash", "walk the NCA URI node hash table",
{ "nca_file_hash", "walk the NCA file node hash table",
{ "nca_ctag_hash", "walk the NCA ctag node hash table",
{ "nca_vnode_hash", "walk the NCA vnode node hash table",
{ "nca_cpu", "walk the NCA CPU table",
{ "nca_timer", "walk the NCA timer table",
{ "nca_connf", "walk the NCA connection fanout",
{ NULL }
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}