genunix.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <mdb/mdb_param.h>
#include <mdb/mdb_modapi.h>
#include <sys/priocntl.h>
#include <sys/flock_impl.h>
#include <sys/kmem_impl.h>
#include <sys/vmem_impl.h>
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/taskq_impl.h>
#include <sys/errorq_impl.h>
#include <sys/cred_impl.h>
#include <regex.h>
#include <sys/port_impl.h>
#include "contract.h"
#include "cpupart_mdb.h"
#include "devinfo.h"
#include "leaky.h"
#include "lgrp.h"
#include "list.h"
#include "log.h"
#include "kgrep.h"
#include "kmem.h"
#include "bio.h"
#include "streams.h"
#include "cyclic.h"
#include "findstack.h"
#include "ndievents.h"
#include "mmd.h"
#include "net.h"
#include "nvpair.h"
#include "ctxop.h"
#include "tsd.h"
#include "thread.h"
#include "memory.h"
#include "sobj.h"
#include "sysevent.h"
#include "rctl.h"
#include "typegraph.h"
#include "ldi.h"
#include "vfs.h"
#include "zone.h"
#include "modhash.h"
/*
* Surely this is defined somewhere...
*/
#define NINTR 16
#ifndef STACK_BIAS
#define STACK_BIAS 0
#endif
static char
{
switch (state) {
case SSLEEP: return ('S');
case SRUN: return ('R');
case SZOMB: return ('Z');
case SIDL: return ('I');
case SONPROC: return ('O');
case SSTOP: return ('T');
default: return ('?');
}
}
#define PS_PRTTHREADS 0x1
#define PS_PRTLWPS 0x2
#define PS_PSARGS 0x4
#define PS_TASKS 0x8
#define PS_PROJECTS 0x10
#define PS_ZONES 0x20
static int
{
static const mdb_bitmask_t t_state_bits[] = {
{ NULL, 0, 0 }
};
if (prt_flags & PS_PRTTHREADS)
if (prt_flags & PS_PRTLWPS)
return (WALK_NEXT);
}
int
{
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk 'proc'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%1s %6s %6s %6s %6s ",
"S", "PID", "PPID", "PGID", "SID");
if (prt_flags & PS_PROJECTS)
mdb_printf("%6s %10s %?s %s%</u>\n",
"UID", "FLAGS", "ADDR", "NAME");
}
if (prt_flags & PS_PROJECTS)
mdb_printf("%c %6d %6d %6d %6d ",
if (prt_flags & PS_PROJECTS)
mdb_printf("%6d 0x%08x %0?p %s\n",
return (DCMD_OK);
}
#define PG_NEWEST 0x0001
#define PG_OLDEST 0x0002
#define PG_PIPE_OUT 0x0004
typedef struct pgrep_data {
const char *pg_pat;
#ifndef _KMDB
#endif
} pgrep_data_t;
/*ARGSUSED*/
static int
{
#ifndef _KMDB
#endif
/*
* kmdb doesn't have access to the reg* functions, so we fall back
* to strstr.
*/
#ifdef _KMDB
return (WALK_NEXT);
#else
return (WALK_NEXT);
#endif
}
} else {
}
}
} else {
mdb_warn("can't invoke 'ps'");
return (WALK_DONE);
}
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
int i;
#ifndef _KMDB
int err;
#endif
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
NULL);
argc -= i;
argv += i;
if (argc != 1)
return (DCMD_USAGE);
/*
* -n and -o are mutually exclusive.
*/
return (DCMD_USAGE);
return (DCMD_USAGE);
if (flags & DCMD_PIPE_OUT)
if (DCMD_HDRSPEC(flags))
else
#ifndef _KMDB
char *buf;
return (DCMD_ERR);
}
#endif
mdb_warn("can't walk 'proc'");
return (DCMD_ERR);
}
} else {
0, NULL) != 0) {
mdb_warn("can't invoke 'ps'");
return (DCMD_ERR);
}
}
}
return (DCMD_OK);
}
int
{
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk task_cache");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%?s %6s %6s %6s %6s %10s%</u>\n",
"ADDR", "TASKID", "PROJID", "ZONEID", "REFCNT", "FLAGS");
}
return (DCMD_ERR);
}
return (DCMD_ERR);
}
mdb_printf("%0?p %6d %6d %6d %6u 0x%08x\n",
return (DCMD_OK);
}
int
{
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk projects");
return (DCMD_ERR);
}
return (DCMD_OK);
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%?s %6s %6s %6s%</u>\n",
"ADDR", "PROJID", "ZONEID", "REFCNT");
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
int co_kfanout;
int co_id;
int i, j, k;
const char *lbolt_sym;
return (DCMD_USAGE);
if (mdb_prop_postmortem)
lbolt_sym = "panic_lbolt";
else
lbolt_sym = "lbolt";
return (DCMD_ERR);
}
mdb_warn("failed to read callout_fanout");
return (DCMD_ERR);
}
mdb_warn("failed to read callout_table");
return (DCMD_ERR);
}
mdb_printf("%<u>%-24s %-?s %-?s %-?s%</u>\n",
"FUNCTION", "ARGUMENT", "ID", "TIME");
for (i = 0; i < CALLOUT_NTYPES; i++) {
for (j = 0; j < co_kfanout; j++) {
co_id = CALLOUT_TABLE(i, j);
mdb_warn("failed to read table at %p",
continue;
}
for (k = 0; k < CALLOUT_BUCKETS; k++) {
sizeof (co_callout),
mdb_printf("%-24a %0?p %0?lx %?lx "
}
}
}
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
long num_classes, i;
char class_name[PC_CLNMSZ];
mdb_warn("failed to find symbol sclass\n");
return (DCMD_ERR);
}
mdb_warn("failed to read sclass");
return (DCMD_ERR);
}
"INIT FCN", "CLASS FCN");
for (i = 0; i < num_classes; i++) {
}
return (DCMD_OK);
}
int
{
char buf[MAXPATHLEN];
return (DCMD_USAGE);
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("expected explicit vnode_t address before ::\n");
return (DCMD_USAGE);
}
mdb_warn("failed to read rootdir");
return (DCMD_ERR);
}
return (DCMD_ERR);
if (*buf == '\0') {
mdb_printf("??\n");
return (DCMD_OK);
}
mdb_printf("\n");
return (DCMD_OK);
}
int
{
return (WALK_NEXT);
}
int
{
int status;
mdb_warn("couldn't read lock_descriptor_t at %p\n",
return (WALK_ERR);
}
return (WALK_ERR);
return (WALK_DONE);
return (status);
}
int
{
mdb_warn("failed to find symbol 'lock_graph'\n");
return (WALK_ERR);
}
return (WALK_NEXT);
}
typedef struct lg_walk_data {
void *data;
/*
* We can't use ::walk lock_descriptor directly, because the head of each graph
* is really a dummy lock. Rather than trying to dynamically determine if this
* is a dummy node or not, we just filter out the initial element of the
* list.
*/
static int
{
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
return (WALK_ERR);
}
return (WALK_NEXT);
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* The space available for the path corresponding to the locked vnode depends
* on whether we are printing 32- or 64-bit addresses.
*/
#ifdef _LP64
#define LM_VNPATHLEN 20
#else
#define LM_VNPATHLEN 30
#endif
/*ARGSUSED*/
static int
{
char buf[LM_VNPATHLEN];
proc_t p;
mdb_printf("%-?p %2s %04x %6d %-16s %-?p ",
sizeof (buf));
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (DCMD_HDRSPEC(flags))
mdb_printf("%<u>%-?s %2s %4s %6s %-16s %-?s %s%</u>\n",
"ADDR", "TP", "FLAG", "PID", "COMM", "VNODE", "PATH");
}
/*ARGSUSED*/
int
{
struct seg s;
if (argc != 0)
return (DCMD_USAGE);
mdb_printf("%<u>%?s %?s %?s %?s %s%</u>\n",
"SEG", "BASE", "SIZE", "DATA", "OPS");
}
return (DCMD_ERR);
}
mdb_printf("%?p %?p %?lx %?p %a\n",
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
(*nres)++;
return (WALK_NEXT);
}
static int
{
struct segvn_data svn;
int nres = 0;
goto drive_on;
}
/*
* We've got an amp for this segment; walk through
* the amp, and determine mappings.
*/
char buf[29];
} else
mdb_printf(" [ anon ]");
}
mdb_printf("\n");
return (WALK_NEXT);
}
static int
{
struct segvn_data svn;
} else {
mdb_printf(" [ anon ]");
}
}
mdb_printf("\n");
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_USAGE);
return (DCMD_ERR);
}
else
if (quick) {
mdb_printf("VNODE\n");
} else {
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
typedef struct anon_walk_data {
int aw_nlevone;
int aw_levone_ndx;
int aw_levtwo_ndx;
int
{
mdb_warn("anon walk doesn't support global walks\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_ERR);
}
} else {
aw->aw_nlevone =
}
aw->aw_levone_ndx = 0;
aw->aw_levtwo_ndx = 0;
aw->aw_levone_ndx++;
mdb_warn("corrupt anon; couldn't"
"find ptr to lev two map");
goto out;
}
}
}
out:
return (0);
}
int
{
int status;
/*
* Once we've walked through level one, we're done.
*/
return (WALK_DONE);
aw->aw_levone_ndx++;
} else {
aw->aw_levtwo_ndx++;
aw->aw_levtwo_ndx = 0;
do {
aw->aw_levone_ndx++;
return (WALK_DONE);
sizeof (uintptr_t),
}
}
} else
goto again;
return (status);
}
void
{
}
/*ARGSUSED*/
int
{
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
return (WALK_ERR);
}
if (t == NULL)
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
mdb_warn("can't proc walk");
return (DCMD_ERR);
}
return (DCMD_OK);
}
typedef struct datafmt {
char *hdr1;
char *hdr2;
char *dashes;
char *fmt;
} datafmt_t;
{ "cache ", "name ",
"-------------------------", "%-25s " },
{ " buf", " size", "------", "%6u " },
{ " buf", "in use", "------", "%6u " },
{ " buf", " total", "------", "%6u " },
{ " memory", " in use", "---------", "%9u " },
{ " alloc", " succeed", "---------", "%9u " },
{ "alloc", " fail", "-----", "%5u " },
};
{ "vmem ", "name ",
"-------------------------", "%-*s " },
{ " memory", " in use", "---------", "%9llu " },
{ " memory", " total", "----------", "%10llu " },
{ " memory", " import", "---------", "%9llu " },
{ " alloc", " succeed", "---------", "%9llu " },
{ "alloc", " fail", "-----", "%5llu " },
};
/*ARGSUSED*/
static int
{
if (ccp->cc_prounds > 0)
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
return (WALK_NEXT);
}
typedef struct kmastat_vmem {
struct kmastat_vmem *kv_next;
int kv_meminuse;
int kv_alloc;
int kv_fail;
static int
{
int magsize;
goto out;
}
out:
mdb_printf("\n");
return (WALK_NEXT);
}
static int
{
return (WALK_NEXT);
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
int ident = 0;
ident = 0;
break;
}
}
mdb_printf("\n");
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (argc != 0)
return (DCMD_USAGE);
mdb_printf("\n");
mdb_printf("\n");
mdb_printf("\n");
mdb_warn("can't walk 'kmem_cache'");
return (DCMD_ERR);
}
mdb_printf("\n");
mdb_warn("can't walk 'vmem'");
return (DCMD_ERR);
}
mdb_printf("\n");
mdb_printf("\n");
mdb_printf("\n");
mdb_printf("\n");
mdb_printf("\n");
mdb_warn("can't walk 'vmem'");
return (DCMD_ERR);
}
mdb_printf("\n");
return (DCMD_OK);
}
/*
* Our ::kgrep callback scans the entire kernel VA space (kas). kas is made
* up of a set of 'struct seg's. We could just scan each seg en masse, but
* unfortunately, a few of the segs are both large and sparse, so we could
* spend quite a bit of time scanning VAs which have no backing pages.
*
* So for the few very sparse segs, we skip the segment itself, and scan
* the allocated vmem_segs in the vmem arena which manages that part of kas.
* Currently, we do this for:
*
* SEG VMEM ARENA
* kvseg heap_arena
* kvseg32 heap32_arena
* kvseg_core heap_core_arena
*
* In addition, we skip the segkpm segment in its entirety, since it is very
* sparse, and contains no new kernel data.
*/
typedef struct kgrep_walk_data {
void *kg_cbdata;
static int
{
return (WALK_NEXT);
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
}
static int
{
return (WALK_NEXT);
if (mdb_pwalk("vmem_alloc",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
if (mdb_get_state() == MDB_STATE_RUNNING) {
mdb_warn("kgrep can only be run on a system "
"dump or under kmdb; see dumpadm(1M)\n");
return (DCMD_ERR);
}
mdb_warn("failed to locate 'kas' symbol\n");
return (DCMD_ERR);
}
mdb_warn("failed to locate 'kvseg' symbol\n");
return (DCMD_ERR);
}
mdb_warn("failed to locate 'kvseg32' symbol\n");
return (DCMD_ERR);
}
mdb_warn("failed to locate 'kvseg_core' symbol\n");
return (DCMD_ERR);
}
mdb_warn("failed to locate 'segkpm_ops' symbol\n");
return (DCMD_ERR);
}
mdb_warn("failed to walk kas segments");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
kgrep_subr_pagesize(void)
{
return (PAGESIZE);
}
typedef struct file_walk_data {
int fw_flistsz;
int fw_ndx;
int fw_nofiles;
int
{
proc_t p;
mdb_warn("file walk doesn't support global walks\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_DONE);
}
mdb_warn("failed to read file array at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
goto again;
}
int
{
return (WALK_DONE);
else
}
void
{
}
int
{
mdb_warn("port walk doesn't support global walks\n");
return (WALK_ERR);
}
mdb_warn("couldn't walk 'file'");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
return (WALK_ERR);
}
return (WALK_NEXT);
return (WALK_ERR);
}
}
typedef struct portev_walk_data {
int
{
mdb_warn("portev walk doesn't support global walks\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_ERR);
}
mdb_warn("input address (%p) does not point to an event port",
return (WALK_ERR);
}
return (WALK_DONE);
}
return (WALK_NEXT);
}
int
{
struct port_kevent ev;
return (WALK_DONE);
return (WALK_DONE);
}
}
void
{
}
typedef struct proc_walk_data {
int pw_depth;
int pw_max;
int
{
mdb_warn("failed to read 'practive'");
return (WALK_ERR);
}
}
mdb_warn("failed to read 'nproc'");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int status;
return (WALK_DONE);
}
goto sib;
}
return (status);
mdb_warn("proc %p has invalid p_child %p; skipping\n",
goto sib;
}
mdb_warn("depth %d exceeds max depth; try again\n",
return (WALK_DONE);
}
return (WALK_NEXT);
}
sib:
/*
* We know that p0 has no siblings, and if another starting proc
* was given, we don't want to walk its siblings anyway.
*/
return (WALK_DONE);
mdb_warn("proc %p has invalid p_sibling %p; skipping\n",
}
return (WALK_NEXT);
}
return (WALK_DONE);
}
return (WALK_NEXT);
}
void
{
}
int
{
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int status;
return (WALK_DONE);
}
return (WALK_DONE);
return (status);
}
int
{
mdb_warn("failed to read 'proj0p'");
return (WALK_ERR);
}
}
return (WALK_NEXT);
}
int
{
int status;
return (WALK_DONE);
}
return (status);
return (WALK_DONE);
return (WALK_NEXT);
}
static int
{
wsp->walk_cbdata));
}
struct aw_info {
void *aw_buff; /* buffer to hold the tree's data structure */
};
/*
* common code used to find the addr of the the leftmost child below
* an AVL node
*/
static uintptr_t
{
for (;;) {
return ((uintptr_t)-1L);
}
break;
}
return (addr);
}
/*
* initialize a forward walk thru an avl tree.
*/
int
{
/*
* allocate the AVL walk data
*/
/*
* get an mdb copy of the avl_tree_t being walked
*/
goto error;
}
mdb_warn("invalid avl_tree_t at %p, avl_size:%d, avl_offset:%d",
goto error;
}
/*
* allocate a buffer to hold the mdb copy of tree's structs
* "node" always points at the avl_node_t field inside the struct
*/
/*
* get the first avl_node_t address, use same algorithm
* as avl_start() -- leftmost child in tree from root
*/
return (WALK_NEXT);
}
goto error;
return (WALK_NEXT);
return (WALK_ERR);
}
/*
* At each step, visit (callback) the current node, then move to the next
* in the AVL tree. Uses the same algorithm as avl_walk().
*/
int
{
int status;
int was_child;
/*
* don't walk past the end of the tree!
*/
return (WALK_DONE);
/*
* must read the current node for the call back to use
*/
return (WALK_ERR);
}
/*
* do the call back
*/
return (status);
/*
* move to the next node....
* note we read in new nodes, so the pointer to the buffer is fixed
*/
/*
* if the node has a right child then go to it and then all the way
* thru as many left children as possible
*/
return (WALK_ERR);
/*
* othewise return to parent nodes, stopping if we ever return from
* a left child
*/
} else {
for (;;) {
break;
if (was_child == 0) /* stop on return from left child */
break;
return (WALK_ERR);
}
}
}
return (WALK_NEXT);
}
/*
* Release the memory allocated for the walk
*/
void
{
return;
}
int
{
mdb_warn("seg walk must begin at struct as *\n");
return (WALK_ERR);
}
/*
* this is really just a wrapper to AVL tree walk
*/
return (avl_walk_init(wsp));
}
static int
cpu_walk_cmp(const void *l, const void *r)
{
return (-1);
return (1);
return (0);
}
typedef struct cpu_walk {
int cw_ndx;
} cpu_walk_t;
int
{
cpu_walk_t *cw;
int max_ncpus, i = 0;
mdb_warn("failed to read 'max_ncpus'");
return (WALK_ERR);
}
mdb_warn("failed to read 'panicstr'");
return (WALK_ERR);
}
mdb_warn("failed to find 'panic_cpu'");
return (WALK_ERR);
}
mdb_warn("failed to read 'panic_cpu'");
return (WALK_ERR);
}
}
/*
* Unfortunately, there is no platform-independent way to walk
* CPUs in ID order. We therefore loop through in cpu_next order,
* building an array of CPU pointers which will subsequently be
* sorted.
*/
mdb_warn("failed to read 'cpu_list'");
return (WALK_ERR);
}
do {
return (WALK_ERR);
}
} else {
}
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
return (WALK_DONE);
}
}
typedef struct cpuinfo_data {
char cid_print_head;
char cid_print_thr;
char cid_print_ithr;
char cid_print_flags;
int
{
cpu_t c;
int id;
return (WALK_NEXT);
return (WALK_NEXT);
}
mdb_warn("CPU %p has id (%d) greater than NCPU (%d)\n",
return (WALK_NEXT);
}
mdb_warn("thread %p has pil (%d) greater than %d\n",
return (WALK_NEXT);
}
mdb_warn("CPU %d has multiple threads at pil %d (at least "
return (WALK_NEXT);
}
return (WALK_NEXT);
}
#define CPUINFO_IDWIDTH 3
#define CPUINFO_FLAGWIDTH 9
#ifdef _LP64
#define CPUINFO_CPUWIDTH 11
#define CPUINFO_TWIDTH 11
#else
#define CPUINFO_CPUWIDTH 8
#define CPUINFO_TWIDTH 8
#endif
#define CPUINFO_ITHRDELT 4
int
{
kthread_t t;
proc_t p;
char **flagbuf;
const char *flags[] = {
"RUNNING", "READY", "QUIESCED", "EXISTS",
"ENABLE", "OFFLINE", "POWEROFF", "FROZEN",
};
return (WALK_NEXT);
/*
* Set cid_cpu to -1 to indicate that we found a matching CPU.
*/
}
if (cid->cid_print_head) {
mdb_printf("%3s %-*s %3s %4s %4s %3s %4s %5s %-6s %-*s %s\n",
"PROC");
}
return (WALK_ERR);
}
mdb_printf("%3d %0*p %3x %4d %4d ",
} else {
}
if (cpu->cpu_last_swtch) {
return (WALK_ERR);
}
} else {
}
mdb_printf(" (idle)\n");
mdb_printf(" -\n");
else {
} else {
mdb_printf(" ?\n");
}
}
if (cid->cid_print_flags) {
int first = 1, i, j, k;
char *s;
continue;
if (first) {
flagbuf[nflaglines++] = s;
}
for (k = strlen(s); k < CPUINFO_THRDELT; k++)
s[k] = ' ';
s[k] = '\0';
flagbuf[nflaglines++] = s;
first = 0;
}
}
if (cid->cid_print_ithr) {
for (i = NINTR - 1; i >= 0; i--) {
continue;
if (!found_one) {
CPUINFO_ITHRDELT, "");
mdb_printf("%c%*s+--> %3s %s\n",
"", "PIL", "THREAD");
}
mdb_warn("failed to read kthread_t at %p",
iaddr);
return (WALK_ERR);
}
mdb_printf("%c%*s %3d %0*p\n",
}
if (mdb_vread(&t, sizeof (t),
mdb_warn("failed to read kthread_t at %p",
pinned);
return (WALK_ERR);
}
if (mdb_vread(&p, sizeof (p),
mdb_warn("failed to read proc_t at %p",
t.t_procp);
return (WALK_ERR);
}
mdb_printf("%c%*s %3s %0*p %s\n",
}
}
return (WALK_ERR);
}
mdb_printf("|\n");
for (i = npri - 1; i >= 0; i--) {
mdb_warn("failed to read kthread_t "
"at %p", taddr);
return (WALK_ERR);
}
if (mdb_vread(&p, sizeof (p),
mdb_warn("failed to read proc_t at %p",
t.t_procp);
return (WALK_ERR);
}
}
}
}
while (flagline < nflaglines)
if (cid->cid_print_head)
mdb_printf("\n");
return (rval);
}
int
{
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
if (verbose) {
}
if (cid.cid_print_ithr) {
int i;
for (i = 0; i < NCPU; i++)
&cid) == -1) {
mdb_warn("couldn't walk thread");
return (DCMD_ERR);
}
}
mdb_warn("failed to find panic_lbolt");
return (DCMD_ERR);
}
mdb_warn("failed to read panic_lbolt");
return (DCMD_ERR);
}
if (lbolt == 0) {
mdb_warn("failed to find lbolt");
return (DCMD_ERR);
}
}
mdb_warn("can't walk cpus");
return (DCMD_ERR);
}
/*
* We didn't find this CPU when we walked through the CPUs
* (i.e. the address specified doesn't show up in the "cpu"
* walk). However, the specified address may still correspond
* to a valid cpu_t (for example, if the specified address is
* the actual panicking cpu_t and not the cached panic_cpu).
* Point is: even if we didn't find it, we still want to try
* to print the specified address as a cpu_t.
*/
mdb_warn("%p is neither a valid CPU ID nor a "
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
int i;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_OK);
}
/*
* Grumble, grumble.
*/
int
{
long smd_hashmsk;
int hash;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
mdb_warn("failed to read smd_hashmsk");
return (DCMD_ERR);
}
mdb_warn("failed to read smd_hash");
return (DCMD_ERR);
}
mdb_warn("failed to read smd_hash");
return (DCMD_ERR);
}
mdb_warn("failed to read segkmap");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
if (argc != 0) {
else
}
mdb_warn("couldn't read smap at %p",
return (DCMD_ERR);
}
do {
return (DCMD_ERR);
}
mdb_printf("vnode %p, offs %p is smap %p, vaddr %p\n",
return (DCMD_OK);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
struct segmap_data sd;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
mdb_warn("failed to read segkmap");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
int
{
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
mdb_warn("failed to walk proc");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
int ident = 0;
}
mdb_inc_indent(ident);
mdb_dec_indent(ident);
return (WALK_NEXT);
}
void
{
proc_t p;
return;
}
}
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
else
mdb_warn("couldn't walk 'proc'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
int fdnum;
proc_t p;
if ((flags & DCMD_ADDRSPEC) == 0) {
mdb_warn("fd doesn't give global information\n");
return (DCMD_ERR);
}
if (argc != 1)
return (DCMD_USAGE);
else
return (DCMD_ERR);
}
mdb_warn("process %p only has %d files open.\n",
return (DCMD_ERR);
}
mdb_warn("couldn't read uf_entry_t at %p",
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
}
return (DCMD_OK);
}
static char *sysfile_cmd[] = {
"exclude:",
"include:",
"forceload:",
"rootdev:",
"rootfs:",
"swapdev:",
"swapfs:",
"moddir:",
"set",
"unknown",
};
/*ARGSUSED*/
static int
{
return (WALK_DONE);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
{
char var[256];
char modname[256];
char val[256];
char strval[256];
void *straddr;
mdb_warn("failed to read sysparam_hd");
return (DCMD_ERR);
}
mdb_warn("failed to read mod_sysfile_arena");
return (DCMD_ERR);
}
var[0] = '\0';
val[0] = '\0';
modname[0] = '\0';
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_ERR);
}
/*
* Is this an int or a string? We determine this
* by checking whether straddr is contained in
* mod_sysfile_arena. If so, the walker will set
* straddr to NULL.
*/
mdb_pwalk("vmem_seg",
(uintptr_t)mod_sysfile_arena) == 0 &&
strval);
} else {
}
}
}
return (DCMD_OK);
}
/*
* Dump a taskq_ent_t given its address.
*/
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("expected explicit taskq_ent_t address before ::\n");
return (DCMD_USAGE);
}
return (DCMD_ERR);
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
"ENTRY", "ARG", "FUNCTION");
}
}
return (DCMD_OK);
}
/*
* Given the address of the (taskq_t) task queue head, walk the queue listing
* the address of every taskq_ent_t.
*/
int
{
mdb_warn("start address required\n");
return (WALK_ERR);
}
/*
* Save the address of the list head entry. This terminates the list.
*/
/*
* Read in taskq head, set walk_addr to point to first taskq_ent_t.
*/
-1) {
mdb_warn("failed to read taskq list head at %p",
}
/*
* Check for null list (next=head)
*/
return (WALK_DONE);
}
return (WALK_NEXT);
}
int
{
int status;
-1) {
return (DCMD_ERR);
}
wsp->walk_cbdata);
/* Check if we're at the last element (next=head) */
return (WALK_DONE);
}
return (status);
}
int
{
return (WALK_DONE);
} else
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
if (argc != 1)
return (DCMD_USAGE);
mdb_warn("failed to walk thread");
return (DCMD_ERR);
}
return (DCMD_OK);
}
static int
{
mdb_warn("failed to read errorq_list");
return (WALK_ERR);
}
return (WALK_NEXT);
}
static int
{
return (WALK_DONE);
return (WALK_ERR);
}
}
typedef struct eqd_walk_data {
void *eqd_buf;
/*
* In order to walk the list of pending error queue elements, we push the
* addresses of the corresponding data buffers in to the eqd_stack array.
* The error lists are in reverse chronological order when iterating using
* eqe_prev, so we then pop things off the top in eqd_walk_step so that the
* walker client gets addresses in order from oldest error to newest error.
*/
static void
{
break;
}
mdb_warn("errorq is overfull -- more than %lu "
break;
}
}
}
static int
{
ulong_t i;
return (WALK_ERR);
}
return (WALK_ERR);
}
/*
* The newest elements in the queue are on the pending list, so we
* push those on to our stack first.
*/
/*
* If eq_ptail is set, it may point to a subset of the errors on the
* pending list in the event a casptr() failed; if ptail's data is
* already in our stack, NULL out eq_ptail and ignore it.
*/
break;
}
}
}
/*
* If eq_phead is set, it has the processing list in order from oldest
* to newest. Use this to recompute eq_ptail as best we can and then
* we nicely fall into eqd_push_list() of eq_ptail below.
*/
/*
* The oldest elements in the queue are on the processing list, subject
* to machinations in the if-clauses above. Push any such elements.
*/
return (WALK_NEXT);
}
static int
{
return (WALK_DONE);
return (WALK_ERR);
}
}
static void
{
}
static int
{
int i;
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("can't walk 'errorq'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
argc -= i;
argv += i;
if (argc != 0)
return (DCMD_USAGE);
mdb_printf("%<u>%-11s %-16s %1s %1s %1s ",
"ADDR", "NAME", "S", "V", "N");
if (!opt_v) {
mdb_printf("%7s %7s %7s%</u>\n",
"ACCEPT", "DROP", "LOG");
} else {
mdb_printf("%5s %6s %6s %3s %16s%</u>\n",
"KSTAT", "QLEN", "SIZE", "IPL", "FUNC");
}
}
return (DCMD_ERR);
}
if (!opt_v) {
mdb_printf("%7llu %7llu %7llu\n",
} else {
mdb_printf("%5s %6lu %6lu %3u %a\n",
mdb_printf("%38s\n%41s"
"%12s %llu\n"
"%53s %llu\n"
"%53s %llu\n"
"%53s %llu\n"
"%53s %llu\n"
"%53s %llu\n"
"%53s %llu\n"
"%53s %llu\n\n",
"|", "+-> ",
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
void *panicbuf;
int i, n;
if (!mdb_prop_postmortem) {
mdb_warn("panicinfo can only be run on a system "
"dump; see dumpadm(1M)\n");
return (DCMD_ERR);
}
return (DCMD_USAGE);
mdb_warn("failed to read 'panic_cpu'");
else
mdb_warn("failed to read 'panic_thread'");
else
mdb_warn("failed to read 'panicbuf'");
return (DCMD_ERR);
}
sizeof (panic_nv_t))) / sizeof (panic_nv_t);
for (i = 0; i < n; i++)
mdb_printf("%16s %?llx\n",
return (DCMD_OK);
}
static const mdb_dcmd_t dcmds[] = {
/* from genunix.c */
{ "binding_hash_entry", ":", "print driver names hash table entry",
{ "did2thread", "? kt_did", "find kernel thread for this id",
did2thread },
{ "pgrep", "[-n | -o] pattern", "pattern match against all processes",
pgrep },
{ "sysevent", "?[-sv]", "print sysevent pending or sent queue",
sysevent},
{ "sysevent_channel", "?", "print sysevent channel database",
{ "sysevent_class_list", ":", "print sysevent class list",
{ "sysevent_subclass_list", ":",
"print sysevent subclass list", sysevent_subclass_list},
{ "whereopen", ":", "given a vnode, dumps procs which have it open",
whereopen },
/* from zone.c */
/* from bio.c */
/* from contract.c */
/* from cpupart.c */
/* from cyclic.c */
/* from devinfo.c */
{ "devbindings", "?[-qs] [device-name | major-num]",
"print devinfo nodes bound to device-name or major-num",
devinfo_help },
{ "devinfo_audit", ":[-v]", "devinfo configuration audit record",
{ "devinfo_audit_log", "?[-v]", "system wide devinfo configuration log",
{ "devinfo_audit_node", ":[-v]", "devinfo node configuration history",
{ "devinfo2driver", ":", "find driver name for this devinfo node",
{ "dev2major", "?<dev_t>", "convert dev_t to a major number",
dev2major },
{ "dev2minor", "?<dev_t>", "convert dev_t to a minor number",
dev2minor },
{ "devt", "?<dev_t>", "display a dev_t's major and minor numbers",
devt },
{ "major2name", "?<major-num>", "convert major number to dev name",
major2name },
{ "minornodes", ":", "given a devinfo node, print its minor nodes",
minornodes },
{ "modctl2devinfo", ":", "given a modctl, list its devinfos",
{ "name2major", "<dev-name>", "convert dev name to major number",
name2major },
{ "softstate", ":<instance>", "retrieve soft-state pointer",
softstate },
{ "devinfo_fm", ":", "devinfo fault managment configuration",
devinfo_fm },
{ "devinfo_fmce", ":", "devinfo fault managment cache entry",
/* from findstack.c */
/* from kmem.c */
{ "allocdby", ":", "given a thread, print its allocated buffers",
allocdby },
{ "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
{ "kmalog", "?[ fail | slab ]",
"display kmem transaction log and stack traces", kmalog },
{ "kmausers", "?[-ef] [cache ...]", "current medium and large users "
{ "kmem_verify", "?", "check integrity of kmem-managed memory",
kmem_verify },
{ "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
"[-m minsize] [-M maxsize] [-t thread] [-T type]",
whatis_help },
{ "whatthread", ":[-v]", "print threads whose stack contains the "
"given address", whatthread },
/* from ldi.c */
{ "ldi_handle", "?[-i]", "display a layered driver handle",
/* from leaky.c + leaky_subr.c */
{ "findleaks", FINDLEAKS_USAGE,
"search for potential kernel memory leaks", findleaks,
/* from lgrp.c */
/* from log.c */
/* from memory.c */
/* from mmd.c */
{ "multidata", ":[-sv]", "display a summarized multidata_t",
multidata },
{ "pattbl", ":", "display a summarized multidata attribute table",
pattbl },
{ "pattr2multidata", ":", "print multidata pointer from pattr_t",
{ "pdesc2slab", ":", "print pdesc slab pointer from pdesc_t",
pdesc2slab },
{ "slab2multidata", ":", "print multidata pointer from pdesc_slab_t",
/* from modhash.c */
{ "modhash", "?[-ceht] [-k key] [-v val] [-i index]",
"display information about one or all mod_hash structures",
modhash, modhash_help },
{ "modent", ":[-k | -v | -t type]",
"display information about a mod_hash_entry", modent,
modent_help },
/* from net.c */
{ "mi", ":[-p] [-d | -m]", "filter and display MI object or payload",
mi },
{ "netstat", "[-av] [-f inet | inet6 | unix] [-P tcp | udp]",
"show network statistics", netstat },
{ "sonode", "?[-f inet | inet6 | unix | #] "
"[-t stream | dgram | raw | #] [-p #]",
"filter and display sonode", sonode },
/* from nvpair.c */
nvpair_print },
/* from rctl.c */
{ "rctl_dict", "?", "print systemwide default rctl definitions",
rctl_dict },
{ "rctl_list", ":[handle]", "print rctls for the given proc",
rctl_list },
{ "rctl", ":[handle]", "print a rctl_t, only if it matches the handle",
rctl },
{ "rctl_validate", ":[-v] [-n #]", "test resource control value "
"sequence", rctl_validate },
/* from sobj.c */
mutex_help },
/* from stream.c */
{ "mblk", ":[-q|v] [-f|F flag] [-t|T type] [-l|L|B len] [-d dbaddr]",
{ "mblk2dblk", ":", "convert mblk_t address to dblk_t address",
mblk2dblk },
{ "queue", ":[-q|v] [-m mod] [-f flag] [-F flag] [-s syncq_addr]",
{ "stdata", ":[-q|v] [-f flag] [-F flag]",
{ "syncq", ":[-q|v] [-f flag] [-F flag] [-t type] [-T type]",
/* from thread.c */
thread_help },
{ "threadlist", "?[-v [count]]",
"display threads and associated C stack traces", threadlist,
/* from tsd.c */
/*
* typegraph does not work under kmdb, as it requires too much memory
* for its internal data structures.
*/
#ifndef _KMDB
/* from typegraph.c */
{ "findfalse", "?[-v]", "find potentially falsely shared structures",
findfalse },
#endif
/* from vfs.c */
pfiles_help },
{ NULL }
};
static const mdb_walker_t walkers[] = {
/* from genunix.c */
{ "avl", "given any avl_tree_t *, forward walk all entries in tree",
{ "anon", "given an amp, list of anon structures",
{ "errorq", "walk list of system error queues",
{ "errorq_data", "walk pending error queue data buffers",
{ "allfile", "given a proc pointer, list all file pointers",
{ "file", "given a proc pointer, list of open file pointers",
{ "lock_descriptor", "walk lock_descriptor_t structures",
{ "lock_graph", "walk lock graph",
{ "port", "given a proc pointer, list of created event ports",
{ "portev", "given a port pointer, list of events in the queue",
{ "proc", "list of active proc_t structures",
{ "projects", "walk a list of kernel projects",
{ "seg", "given an as, list of segments",
{ "sysevent_pend", "walk sysevent pending queue",
{ "sysevent_channel", "walk sysevent channel subscriptions",
{ "sysevent_class_list", "walk sysevent subscription's class list",
{ "sysevent_subclass_list",
"walk sysevent subscription's subclass list",
{ "task", "given a task pointer, walk its processes",
{ "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list",
/* from zone.c */
{ "zone", "walk a list of kernel zones",
{ "zsd", "walk list of zsd entries for a zone",
/* from bio.c */
{ "buf", "walk the bio buf hash",
/* from contract.c */
{ "contract", "walk all contracts, or those of the specified type",
{ "ct_event", "walk events on a contract event queue",
{ "ct_listener", "walk contract event queue listeners",
/* from cpupart.c */
{ "cpupart_cpulist", "given an cpupart_t, walk cpus in partition",
NULL },
{ "cpupart_walk", "walk the set of cpu partitions",
/* from ctxop.c */
{ "ctxop", "walk list of context ops on a thread",
/* from cyclic.c */
{ "cyccpu", "walk per-CPU cyc_cpu structures",
{ "cycomni", "for an omnipresent cyclic, walk cyc_omni_cpu list",
{ "cyctrace", "walk cyclic trace buffer",
/* from devinfo.c */
{ "binding_hash", "walk all entries in binding hash table",
{ "devinfo", "walk devinfo tree or subtree",
{ "devinfo_audit_log", "walk devinfo audit system-wide log",
{ "devinfo_audit_node", "walk per-devinfo audit history",
{ "devinfo_children", "walk children of devinfo node",
{ "devinfo_parents", "walk ancestors of devinfo node",
{ "devinfo_siblings", "walk siblings of devinfo node",
{ "devi_next", "walk devinfo list",
{ "devnames", "walk devnames array",
{ "minornode", "given a devinfo node, walk minor nodes",
{ "softstate",
"given an i_ddi_soft_state*, list all in-use driver stateps",
{ "softstate_all",
"given an i_ddi_soft_state*, list all driver stateps",
{ "devinfo_fmc",
"walk a fault management handle cache active list",
/* from kmem.c */
{ "allocdby", "given a thread, walk its allocated bufctls",
{ "bufctl", "walk a kmem cache's bufctls",
{ "bufctl_history", "walk the available history of a bufctl",
{ "freedby", "given a thread, walk its freed bufctls",
{ "freectl", "walk a kmem cache's free bufctls",
{ "freectl_constructed", "walk a kmem cache's constructed free bufctls",
{ "freemem", "walk a kmem cache's free memory",
{ "freemem_constructed", "walk a kmem cache's constructed free memory",
{ "kmem", "walk a kmem cache",
{ "kmem_cpu_cache", "given a kmem cache, walk its per-CPU caches",
{ "kmem_hash", "given a kmem cache, walk its allocated hash table",
{ "kmem_log", "walk the kmem transaction log",
{ "kmem_slab", "given a kmem cache, walk its slabs",
{ "kmem_slab_partial",
"given a kmem cache, walk its partially allocated slabs (min 1)",
{ "vmem", "walk vmem structures in pre-fix, depth-first order",
{ "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
{ "vmem_free", "given a vmem_t, walk its free vmem_segs",
{ "vmem_postfix", "walk vmem structures in post-fix, depth-first order",
{ "vmem_seg", "given a vmem_t, walk all of its vmem_segs",
{ "vmem_span", "given a vmem_t, walk its spanning vmem_segs",
/* from ldi.c */
{ "ldi_handle", "walk the layered driver handle hash",
{ "ldi_ident", "walk the layered driver identifier hash",
/* from leaky.c + leaky_subr.c */
{ "leak", "given a leaked bufctl or vmem_seg, find leaks w/ same "
"stack trace",
{ "leakbuf", "given a leaked bufctl or vmem_seg, walk buffers for "
"leaks w/ same stack trace",
/* from lgrp.c */
{ "lgrp_cpulist", "given an lgrp, walk cpus",
NULL },
{ "lgrptbl", "walk the lgrp table",
/* from list.c */
{ "list", "walk a linked list",
/* from memory.c */
{ "page", "walk all pages, or those from the specified vnode",
{ "memlist", "walk specified memlist",
{ "swapinfo", "walk swapinfo structures",
/* from mmd.c */
{ "pdesc", "walk pdesc_t structures",
{ "pdesc_slab", "walk pdesc_slab_t structures",
/* from modhash.c */
{ "modent", "walk list of entries in a given mod_hash",
{ "modchain", "walk list of entries in a given mod_hash_entry",
/* from net.c */
{ "ar", "walk ar_t structures using MI",
{ "icmp", "walk ICMP control structures using MI",
{ "ill", "walk ill_t structures using MI",
{ "mi", "given a MI_O, walk the MI",
{ "sonode", "given a sonode, walk its children",
{ "udp", "walk UDP connections using MI",
/* from nvpair.c */
/* from rctl.c */
{ "rctl_dict_list", "walk all rctl_dict_entry_t's from rctl_lists",
{ "rctl_val", "given a rctl_t, walk all rctl_val entries associated",
/* from sobj.c */
{ "blocked", "walk threads blocked on a given sobj",
{ "wchan", "given a wchan, list of blocked threads",
/* from stream.c */
{ "b_cont", "walk mblk_t list using b_cont",
{ "b_next", "walk mblk_t list using b_next",
{ "qlink", "walk queue_t list using q_link",
{ "qnext", "walk queue_t list using q_next",
{ "strftblk", "given a dblk_t, walk STREAMS flow trace event list",
{ "readq", "walk read queue side of stdata",
{ "writeq", "walk write queue side of stdata",
/* from thread.c */
{ "deathrow", "walk threads on both lwp_ and thread_deathrow",
{ "cpu_dispq", "given a cpu_t, walk threads in dispatcher queues",
{ "cpupart_dispq",
"given a cpupart_t, walk threads in dispatcher queues",
{ "lwp_deathrow", "walk lwp_deathrow",
{ "thread", "global or per-process kthread_t structures",
{ "thread_deathrow", "walk threads on thread_deathrow",
/* from tsd.c */
{ "tsd", "walk list of thread-specific data",
/*
* typegraph does not work under kmdb, as it requires too much memory
* for its internal data structures.
*/
#ifndef _KMDB
/* from typegraph.c */
{ "typeconflict", "walk buffers with conflicting type inferences",
{ "typeunknown", "walk buffers with unknown types",
#endif
/* from vfs.c */
{ "vfs", "walk file system list",
{ NULL }
};
const mdb_modinfo_t *
_mdb_init(void)
{
mdb_warn("failed to read 'top_devinfo'");
return (NULL);
}
if (findstack_init() != DCMD_OK)
return (NULL);
kmem_init();
return (&modinfo);
}
void
_mdb_fini(void)
{
/*
* Force ::findleaks to let go any cached memory
*/
leaky_cleanup(1);
}